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.lib.GenerationManager
import net.trivernis.chunkmaster.lib.Spiral
import net.trivernis.chunkmaster.lib.SqlUpdateManager
import org.bukkit.plugin.java.JavaPlugin
import org.bukkit.scheduler.BukkitTask
import java.lang.Exception
@ -66,20 +67,11 @@ class Chunkmaster: JavaPlugin() {
Class.forName("org.sqlite.JDBC")
sqliteConnection = DriverManager.getConnection("jdbc:sqlite:${dataFolder.absolutePath}/chunkmaster.db")
logger.info("Database connection established.")
val createTableStatement = sqliteConnection.prepareStatement("""
CREATE TABLE IF NOT EXISTS generation_tasks (
id integer PRIMARY KEY AUTOINCREMENT,
center_x integer NOT NULL DEFAULT 0,
center_z integer NOT NULL DEFAULT 0,
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.")
val updateManager = SqlUpdateManager(sqliteConnection, this)
updateManager.checkUpdate()
updateManager.performUpdate()
logger.info("Database fully initialized.")
} catch(e: Exception) {
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 {
if (sender is Player) {
return if (command.testPermission(sender)) {
chunkmaster.generationManager.addTask(sender.world)
sender.sendMessage("Added generation task for world \"${sender.world.name}\"")
var stopAfter = -1
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
} else {
sender.sendMessage("You do not have permission.")
true
}
} else {
return if (args.size == 1) {
return if (args.size >= 1) {
val world = sender.server.getWorld(args[0])
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
} else {
sender.sendMessage("World \"${args[0]}\" not found")

@ -6,9 +6,12 @@ import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender
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 {
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.")
true
} else {

@ -12,24 +12,24 @@ import java.lang.NullPointerException
class GenerationManager(private val chunkmaster: Chunkmaster, private val server: Server) {
val tasks: HashSet<TaskEntry> = HashSet()
get() = field
/**
* Adds a generation task
*/
fun addTask(world: World): Int {
fun addTask(world: World, stopAfter: Int = -1): Int {
val centerChunk = world.getChunkAt(world.spawnLocation)
val generationTask = GenerationTask(chunkmaster, world, centerChunk, centerChunk)
val task = server.scheduler.runTaskTimer(chunkmaster, generationTask, 10, 2)
val insertStatement = chunkmaster.sqliteConnection.prepareStatement("""
INSERT INTO generation_tasks (center_x, center_z, last_x, last_z, world)
values (?, ?, ?, ?, ?)
INSERT INTO generation_tasks (center_x, center_z, last_x, last_z, world, stop_after)
values (?, ?, ?, ?, ?, ?)
""")
insertStatement.setInt(1, centerChunk.x)
insertStatement.setInt(2, centerChunk.z)
insertStatement.setInt(3, centerChunk.x)
insertStatement.setInt(4, centerChunk.z)
insertStatement.setString(5, world.name)
insertStatement.setInt(6, stopAfter)
insertStatement.execute()
val getIdStatement = chunkmaster.sqliteConnection.prepareStatement("""
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
*/
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}\"")
val generationTask = GenerationTask(chunkmaster, world, center, last)
val generationTask = GenerationTask(chunkmaster, world, center, last, stopAfter)
val task = server.scheduler.runTaskTimer(chunkmaster, generationTask, 10, 2)
tasks.add(TaskEntry(id, task, generationTask))
}
@ -66,7 +66,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
tasks.remove(taskEntry)
}
val setAutostart = chunkmaster.sqliteConnection.prepareStatement("""
UPDATE TABLE generation_tasks SET autostart = 0 WHERE id = ?
DELETE FROM generation_tasks WHERE id = ?
""".trimIndent())
setAutostart.setInt(1, id)
setAutostart.execute()
@ -117,8 +117,9 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
val world = server.getWorld(res.getString("world"))
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 stopAfter = res.getInt("stop_after")
if (this.tasks.find {it.id == id} == null) {
resumeTask(world, center, last, id)
resumeTask(world, center, last, id, stopAfter)
}
}
} catch (error: NullPointerException) {
@ -150,6 +151,11 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
updateStatement.setInt(3, task.id)
updateStatement.execute()
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) {
server.consoleSender.sendMessage("Exception when saving task progress ${error.message}")
}

@ -7,17 +7,22 @@ import org.bukkit.scheduler.BukkitRunnable
import org.bukkit.scheduler.BukkitTask
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 loadedChunks: HashSet<Chunk> = HashSet()
var count = 0
get() = field
private set
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
* it hasn't been generated already.
* After 10 chunks have been generated, they will all be unloaded and saved.
*/
override fun run() {
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 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)) {
chunk.load(true)
loadedChunks.add(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'
commands:
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
usage: /generate [world]
usage: /generate [world] [stopAfter]
listgentasks:
description: Lists all generation tasks
permission: chunkmaster.listgentasks

Loading…
Cancel
Save