Fixes and SQL auto Update

- fixed removegentask command
- added autostop of generation task after a certain amout of chunks or at the world border
- added SqlUpdateManager to check  if tables need to be created or updated
pull/4/head
Trivernis 5 years ago
parent 29720e8451
commit ecf7f10f52

@ -5,6 +5,7 @@ import net.trivernis.chunkmaster.commands.CommandListGenTasks
import net.trivernis.chunkmaster.commands.CommandRemoveGenTask import net.trivernis.chunkmaster.commands.CommandRemoveGenTask
import net.trivernis.chunkmaster.lib.GenerationManager import net.trivernis.chunkmaster.lib.GenerationManager
import net.trivernis.chunkmaster.lib.Spiral import net.trivernis.chunkmaster.lib.Spiral
import net.trivernis.chunkmaster.lib.SqlUpdateManager
import org.bukkit.plugin.java.JavaPlugin import org.bukkit.plugin.java.JavaPlugin
import org.bukkit.scheduler.BukkitTask import org.bukkit.scheduler.BukkitTask
import java.lang.Exception import java.lang.Exception
@ -66,20 +67,11 @@ class Chunkmaster: JavaPlugin() {
Class.forName("org.sqlite.JDBC") Class.forName("org.sqlite.JDBC")
sqliteConnection = DriverManager.getConnection("jdbc:sqlite:${dataFolder.absolutePath}/chunkmaster.db") sqliteConnection = DriverManager.getConnection("jdbc:sqlite:${dataFolder.absolutePath}/chunkmaster.db")
logger.info("Database connection established.") logger.info("Database connection established.")
val createTableStatement = sqliteConnection.prepareStatement("""
CREATE TABLE IF NOT EXISTS generation_tasks ( val updateManager = SqlUpdateManager(sqliteConnection, this)
id integer PRIMARY KEY AUTOINCREMENT, updateManager.checkUpdate()
center_x integer NOT NULL DEFAULT 0, updateManager.performUpdate()
center_z integer NOT NULL DEFAULT 0, logger.info("Database fully initialized.")
last_x integer NOT NULL DEFAULT 0,
last_z integer NOT NULL DEFAULT 0,
world text UNIQUE NOT NULL DEFAULT 'world',
autostart integer DEFAULT 1
);
""".trimIndent())
createTableStatement.execute()
createTableStatement.close()
logger.info("Database tables created.")
} catch(e: Exception) { } catch(e: Exception) {
logger.warning("Failed to init database: ${e.message}") logger.warning("Failed to init database: ${e.message}")
} }

@ -13,18 +13,32 @@ class CommandGenerate(private val chunkmaster: Chunkmaster): CommandExecutor {
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean { override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
if (sender is Player) { if (sender is Player) {
return if (command.testPermission(sender)) { return if (command.testPermission(sender)) {
chunkmaster.generationManager.addTask(sender.world) var stopAfter = -1
sender.sendMessage("Added generation task for world \"${sender.world.name}\"")
if (args.isNotEmpty() && args[0].toIntOrNull() != null) {
stopAfter = args[0].toInt()
}
chunkmaster.generationManager.addTask(sender.world, stopAfter)
sender.sendMessage("Added generation task for world \"${sender.world.name}\"" +
"Stopping after $stopAfter chunks (-1 for world border).")
true true
} else { } else {
sender.sendMessage("You do not have permission.") sender.sendMessage("You do not have permission.")
true true
} }
} else { } else {
return if (args.size == 1) { return if (args.size >= 1) {
val world = sender.server.getWorld(args[0]) val world = sender.server.getWorld(args[0])
if (world != null) { if (world != null) {
chunkmaster.generationManager.addTask(world) var stopAfter = -1
if (args.size >=2 && args[1].toIntOrNull() != null) {
stopAfter = args[1].toInt()
}
chunkmaster.generationManager.addTask(world, stopAfter)
sender.sendMessage("Added generation task for world \"${world.name}\"" +
"Stopping after $stopAfter chunks (-1 for world border).")
true true
} else { } else {
sender.sendMessage("World \"${args[0]}\" not found") sender.sendMessage("World \"${args[0]}\" not found")

@ -6,9 +6,12 @@ import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender import org.bukkit.command.CommandSender
class CommandRemoveGenTask(private val chunkmaster: Chunkmaster): CommandExecutor { class CommandRemoveGenTask(private val chunkmaster: Chunkmaster): CommandExecutor {
/**
* Stops the specified generation task and removes it from the autostart.
*/
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean { override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
return if (command.testPermission(sender) && args.size == 1) { return if (command.testPermission(sender) && args.size == 1) {
chunkmaster.generationManager.removeTask(args[1].toInt()) chunkmaster.generationManager.removeTask(args[0].toInt())
sender.sendMessage("Task ${args[1].toInt()} canceled.") sender.sendMessage("Task ${args[1].toInt()} canceled.")
true true
} else { } else {

@ -12,24 +12,24 @@ import java.lang.NullPointerException
class GenerationManager(private val chunkmaster: Chunkmaster, private val server: Server) { class GenerationManager(private val chunkmaster: Chunkmaster, private val server: Server) {
val tasks: HashSet<TaskEntry> = HashSet() val tasks: HashSet<TaskEntry> = HashSet()
get() = field
/** /**
* Adds a generation task * Adds a generation task
*/ */
fun addTask(world: World): Int { fun addTask(world: World, stopAfter: Int = -1): Int {
val centerChunk = world.getChunkAt(world.spawnLocation) val centerChunk = world.getChunkAt(world.spawnLocation)
val generationTask = GenerationTask(chunkmaster, world, centerChunk, centerChunk) val generationTask = GenerationTask(chunkmaster, world, centerChunk, centerChunk)
val task = server.scheduler.runTaskTimer(chunkmaster, generationTask, 10, 2) val task = server.scheduler.runTaskTimer(chunkmaster, generationTask, 10, 2)
val insertStatement = chunkmaster.sqliteConnection.prepareStatement(""" val insertStatement = chunkmaster.sqliteConnection.prepareStatement("""
INSERT INTO generation_tasks (center_x, center_z, last_x, last_z, world) INSERT INTO generation_tasks (center_x, center_z, last_x, last_z, world, stop_after)
values (?, ?, ?, ?, ?) values (?, ?, ?, ?, ?, ?)
""") """)
insertStatement.setInt(1, centerChunk.x) insertStatement.setInt(1, centerChunk.x)
insertStatement.setInt(2, centerChunk.z) insertStatement.setInt(2, centerChunk.z)
insertStatement.setInt(3, centerChunk.x) insertStatement.setInt(3, centerChunk.x)
insertStatement.setInt(4, centerChunk.z) insertStatement.setInt(4, centerChunk.z)
insertStatement.setString(5, world.name) insertStatement.setString(5, world.name)
insertStatement.setInt(6, stopAfter)
insertStatement.execute() insertStatement.execute()
val getIdStatement = chunkmaster.sqliteConnection.prepareStatement(""" val getIdStatement = chunkmaster.sqliteConnection.prepareStatement("""
SELECT id FROM generation_tasks ORDER BY id DESC LIMIT 1 SELECT id FROM generation_tasks ORDER BY id DESC LIMIT 1
@ -47,9 +47,9 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
/** /**
* Resumes a generation task * Resumes a generation task
*/ */
private fun resumeTask(world: World, center: Chunk, last: Chunk, id: Int) { private fun resumeTask(world: World, center: Chunk, last: Chunk, id: Int, stopAfter: Int = -1) {
chunkmaster.logger.info("Resuming chunk generation task for world \"${world.name}\"") chunkmaster.logger.info("Resuming chunk generation task for world \"${world.name}\"")
val generationTask = GenerationTask(chunkmaster, world, center, last) val generationTask = GenerationTask(chunkmaster, world, center, last, stopAfter)
val task = server.scheduler.runTaskTimer(chunkmaster, generationTask, 10, 2) val task = server.scheduler.runTaskTimer(chunkmaster, generationTask, 10, 2)
tasks.add(TaskEntry(id, task, generationTask)) tasks.add(TaskEntry(id, task, generationTask))
} }
@ -66,7 +66,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
tasks.remove(taskEntry) tasks.remove(taskEntry)
} }
val setAutostart = chunkmaster.sqliteConnection.prepareStatement(""" val setAutostart = chunkmaster.sqliteConnection.prepareStatement("""
UPDATE TABLE generation_tasks SET autostart = 0 WHERE id = ? DELETE FROM generation_tasks WHERE id = ?
""".trimIndent()) """.trimIndent())
setAutostart.setInt(1, id) setAutostart.setInt(1, id)
setAutostart.execute() setAutostart.execute()
@ -117,8 +117,9 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
val world = server.getWorld(res.getString("world")) val world = server.getWorld(res.getString("world"))
val center = world!!.getChunkAt(res.getInt("center_x"), res.getInt("center_z")) val center = world!!.getChunkAt(res.getInt("center_x"), res.getInt("center_z"))
val last = world.getChunkAt(res.getInt("last_x"), res.getInt("last_z")) val last = world.getChunkAt(res.getInt("last_x"), res.getInt("last_z"))
val stopAfter = res.getInt("stop_after")
if (this.tasks.find {it.id == id} == null) { if (this.tasks.find {it.id == id} == null) {
resumeTask(world, center, last, id) resumeTask(world, center, last, id, stopAfter)
} }
} }
} catch (error: NullPointerException) { } catch (error: NullPointerException) {
@ -150,6 +151,11 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
updateStatement.setInt(3, task.id) updateStatement.setInt(3, task.id)
updateStatement.execute() updateStatement.execute()
updateStatement.close() updateStatement.close()
if (genTask.endReached) { // remove the task if it is finished after the progress has been saved
server.consoleSender.sendMessage("Task #${task.id} finished after ${genTask.count} chunks.")
removeTask(task.id)
}
} catch (error: Exception) { } catch (error: Exception) {
server.consoleSender.sendMessage("Exception when saving task progress ${error.message}") server.consoleSender.sendMessage("Exception when saving task progress ${error.message}")
} }

@ -7,17 +7,22 @@ import org.bukkit.scheduler.BukkitRunnable
import org.bukkit.scheduler.BukkitTask import org.bukkit.scheduler.BukkitTask
class GenerationTask(private val plugin: Chunkmaster, val world: World, class GenerationTask(private val plugin: Chunkmaster, val world: World,
private val centerChunk: Chunk, val startChunk: Chunk): Runnable { private val centerChunk: Chunk, private val startChunk: Chunk,
val stopAfter: Int = -1): Runnable {
private val spiral: Spiral = Spiral(Pair(centerChunk.x, centerChunk.z), Pair(startChunk.x, startChunk.z)) private val spiral: Spiral = Spiral(Pair(centerChunk.x, centerChunk.z), Pair(startChunk.x, startChunk.z))
private val loadedChunks: HashSet<Chunk> = HashSet() private val loadedChunks: HashSet<Chunk> = HashSet()
var count = 0 var count = 0
get() = field private set
var lastChunk: Chunk = startChunk var lastChunk: Chunk = startChunk
get() = field private set
var endReached: Boolean = false
private set
/** /**
* Runs the generation task. Every Iteration the next chunk will be generated if * Runs the generation task. Every Iteration the next chunk will be generated if
* it hasn't been generated already. * it hasn't been generated already.
* After 10 chunks have been generated, they will all be unloaded and saved.
*/ */
override fun run() { override fun run() {
if (plugin.mspt < 500L) { // pause when tps < 2 if (plugin.mspt < 500L) { // pause when tps < 2
@ -31,12 +36,17 @@ class GenerationTask(private val plugin: Chunkmaster, val world: World,
val nextChunkCoords = spiral.next() val nextChunkCoords = spiral.next()
val chunk = world.getChunkAt(nextChunkCoords.first, nextChunkCoords.second) val chunk = world.getChunkAt(nextChunkCoords.first, nextChunkCoords.second)
if (!world.worldBorder.isInside(chunk.getBlock(8, 0, 8).location) || (stopAfter in 1..count)) {
endReached = true
return
}
if (!world.isChunkGenerated(chunk.x, chunk.z)) { if (!world.isChunkGenerated(chunk.x, chunk.z)) {
chunk.load(true) chunk.load(true)
loadedChunks.add(chunk) loadedChunks.add(chunk)
} }
lastChunk = chunk lastChunk = chunk
count++ count = spiral.count // set the count to the more accurate spiral count
} }
} }
} }

@ -0,0 +1,80 @@
package net.trivernis.chunkmaster.lib
import net.trivernis.chunkmaster.Chunkmaster
import java.lang.Exception
import java.sql.Connection
class SqlUpdateManager(private val connnection: Connection, private val chunkmaster: Chunkmaster) {
private val tables = listOf(
Pair(
"generation_tasks",
listOf(
Pair("id", "integer PRIMARY KEY AUTOINCREMENT"),
Pair("center_x", "integer NOT NULL DEFAULT 0"),
Pair("center_z", "integer NOT NULL DEFAULT 0"),
Pair("last_x", "integer NOT NULL DEFAULT 0"),
Pair("last_z", "integer NOT NULL DEFAULT 0"),
Pair("world", "text UNIQUE NOT NULL DEFAULT 'world'"),
Pair("stop_after", "integer DEFAULT -1"),
Pair("autostart", "integer DEFAULT 1")
)
)
)
private val needUpdate = HashSet<Pair<String, Pair<String, String>>>()
private val needCreation = HashSet<String>()
/**
* Checks which tables need an update or creation.
*/
fun checkUpdate() {
val meta = connnection.metaData
for (table in tables) {
val resTables = meta.getTables(null, null, table.first, null)
if (resTables.next()) { // table exists
for (column in table.second) {
val resColumn = meta.getColumns(null, null, table.first, column.first)
if (!resColumn.next()) {
needUpdate.add(Pair(table.first, column))
}
}
} else {
needCreation.add(table.first)
}
}
}
/**
* Creates or updates tables that needed an update.
*/
fun performUpdate() {
for (table in needCreation) {
try {
var tableDef = "CREATE TABLE IF NOT EXISTS $table ("
for (column in tables.find{it.first == table}!!.second) {
tableDef += "${column.first} ${column.second},"
}
tableDef = tableDef.substringBeforeLast(",") + ");"
chunkmaster.logger.info("Creating table $table with definition $tableDef")
val stmt = connnection.prepareStatement(tableDef)
stmt.execute()
stmt.close()
} catch (err: Exception) {
chunkmaster.logger.severe("Error creating table $table.");
}
}
for (table in needUpdate) {
val updateSql = "ALTER TABLE ${table.first} ADD COLUMN ${table.second.first} ${table.second.second}"
try {
val stmt = connnection.prepareStatement(updateSql)
stmt.execute()
stmt.close()
chunkmaster.logger.info("Updated table ${table.first} with sql $updateSql")
} catch (e: Exception) {
chunkmaster.logger.severe("Failed to update table ${table.first} with sql $updateSql")
}
}
}
}

@ -7,9 +7,13 @@ website: trivernis.net
api-version: '1.14' api-version: '1.14'
commands: commands:
generate: generate:
description: Generates chunks starting from the world spawnpoint description: >
Generates chunks starting from the world spawnpoint.
StopAfter indicates the number of chunks to generate
(-1 meaning to stop at the world border). If a player
executes the command, the world argument will be ignored.
permission: chumkmaster.generate permission: chumkmaster.generate
usage: /generate [world] usage: /generate [world] [stopAfter]
listgentasks: listgentasks:
description: Lists all generation tasks description: Lists all generation tasks
permission: chunkmaster.listgentasks permission: chunkmaster.listgentasks

Loading…
Cancel
Save