Changes to sql structure

- changed SqliteUpdateManager to SqliteManager
- added methods to execute sql with values and callback to the SqliteManager
- added auto close of statements
- switched to establishing a connection only when sql should be executed
pull/15/head
Trivernis 5 years ago
parent b09b96d95c
commit 8b63643f7a

@ -3,7 +3,7 @@ package net.trivernis.chunkmaster
import io.papermc.lib.PaperLib import io.papermc.lib.PaperLib
import net.trivernis.chunkmaster.commands.* import net.trivernis.chunkmaster.commands.*
import net.trivernis.chunkmaster.lib.generation.GenerationManager import net.trivernis.chunkmaster.lib.generation.GenerationManager
import net.trivernis.chunkmaster.lib.SqlUpdateManager import net.trivernis.chunkmaster.lib.SqliteManager
import org.bstats.bukkit.Metrics import org.bstats.bukkit.Metrics
import org.bukkit.plugin.java.JavaPlugin import org.bukkit.plugin.java.JavaPlugin
import org.bukkit.scheduler.BukkitTask import org.bukkit.scheduler.BukkitTask
@ -12,8 +12,7 @@ import java.sql.Connection
import java.sql.DriverManager import java.sql.DriverManager
class Chunkmaster: JavaPlugin() { class Chunkmaster: JavaPlugin() {
lateinit var sqliteConnection: Connection lateinit var sqliteManager: SqliteManager
var dbname: String? = null
lateinit var generationManager: GenerationManager lateinit var generationManager: GenerationManager
private lateinit var tpsTask: BukkitTask private lateinit var tpsTask: BukkitTask
var mspt = 50L // keep track of the milliseconds per tick var mspt = 50L // keep track of the milliseconds per tick
@ -51,7 +50,6 @@ class Chunkmaster: JavaPlugin() {
override fun onDisable() { override fun onDisable() {
logger.info("Stopping all generation tasks...") logger.info("Stopping all generation tasks...")
generationManager.stopAll() generationManager.stopAll()
sqliteConnection.close()
} }
/** /**
@ -76,13 +74,8 @@ class Chunkmaster: JavaPlugin() {
private fun initDatabase() { private fun initDatabase() {
logger.info("Initializing Database...") logger.info("Initializing Database...")
try { try {
Class.forName("org.sqlite.JDBC") this.sqliteManager = SqliteManager( this)
sqliteConnection = DriverManager.getConnection("jdbc:sqlite:${dataFolder.absolutePath}/chunkmaster.db") sqliteManager.init()
logger.info("Database connection established.")
val updateManager = SqlUpdateManager(sqliteConnection, this)
updateManager.checkUpdate()
updateManager.performUpdate()
logger.info("Database fully initialized.") logger.info("Database fully initialized.")
} catch(e: Exception) { } catch(e: Exception) {
logger.warning("Failed to init database: ${e.message}") logger.warning("Failed to init database: ${e.message}")

@ -2,10 +2,14 @@ package net.trivernis.chunkmaster.lib
import net.trivernis.chunkmaster.Chunkmaster import net.trivernis.chunkmaster.Chunkmaster
import org.apache.commons.lang.exception.ExceptionUtils import org.apache.commons.lang.exception.ExceptionUtils
import org.sqlite.SQLiteConnection
import java.lang.Exception import java.lang.Exception
import java.sql.Connection import java.sql.Connection
import java.sql.DriverManager
import java.sql.PreparedStatement
import java.sql.ResultSet
class SqlUpdateManager(private val connnection: Connection, private val chunkmaster: Chunkmaster) { class SqliteManager(private val chunkmaster: Chunkmaster) {
private val tables = listOf( private val tables = listOf(
Pair( Pair(
"generation_tasks", "generation_tasks",
@ -23,11 +27,33 @@ class SqlUpdateManager(private val connnection: Connection, private val chunkmas
private val needUpdate = HashSet<Pair<String, Pair<String, String>>>() private val needUpdate = HashSet<Pair<String, Pair<String, String>>>()
private val needCreation = HashSet<String>() private val needCreation = HashSet<String>()
/**
* Returns the connection to the database
*/
fun getConnection(): Connection? {
try {
Class.forName("org.sqlite.JDBC")
return DriverManager.getConnection("jdbc:sqlite:${chunkmaster.dataFolder.absolutePath}/chunkmaster.db")
} catch (e: Exception) {
chunkmaster.logger.severe("Could not get database connection.")
chunkmaster.logger.severe(e.message)
}
return null
}
/**
* Checks for and performs an update
*/
fun init() {
this.checkUpdate()
this.performUpdate()
}
/** /**
* Checks which tables need an update or creation. * Checks which tables need an update or creation.
*/ */
fun checkUpdate() { private fun checkUpdate() {
val meta = connnection.metaData val meta = getConnection()!!.metaData
for (table in tables) { for (table in tables) {
val resTables = meta.getTables(null, null, table.first, null) val resTables = meta.getTables(null, null, table.first, null)
@ -38,6 +64,7 @@ class SqlUpdateManager(private val connnection: Connection, private val chunkmas
if (!resColumn.next()) { if (!resColumn.next()) {
needUpdate.add(Pair(table.first, column)) needUpdate.add(Pair(table.first, column))
} }
resColumn.close()
} }
} else { } else {
needCreation.add(table.first) needCreation.add(table.first)
@ -46,10 +73,32 @@ class SqlUpdateManager(private val connnection: Connection, private val chunkmas
} }
} }
/**
* Executes a sql statement on the database.
*/
fun executeStatement(sql: String, values: HashMap<Int, Any>, callback: ((ResultSet) -> Unit)?) {
val connection = getConnection()
if (connection != null) {
val statement = connection.prepareStatement(sql)
for (parameterValue in values) {
statement.setObject(parameterValue.key, parameterValue.value)
}
statement.execute()
val res = statement.resultSet
if (callback != null) {
callback(res)
}
statement.close()
connection.close()
} else {
chunkmaster.logger.severe("Could not execute sql $sql. No database connection established.")
}
}
/** /**
* Creates or updates tables that needed an update. * Creates or updates tables that needed an update.
*/ */
fun performUpdate() { private fun performUpdate() {
for (table in needCreation) { for (table in needCreation) {
try { try {
var tableDef = "CREATE TABLE IF NOT EXISTS $table (" var tableDef = "CREATE TABLE IF NOT EXISTS $table ("
@ -59,21 +108,17 @@ class SqlUpdateManager(private val connnection: Connection, private val chunkmas
} }
tableDef = tableDef.substringBeforeLast(",") + ");" tableDef = tableDef.substringBeforeLast(",") + ");"
chunkmaster.logger.info("Creating table $table with definition $tableDef") chunkmaster.logger.info("Creating table $table with definition $tableDef")
val stmt = connnection.prepareStatement(tableDef) executeStatement(tableDef, HashMap(), null)
stmt.execute() } catch (e: Exception) {
stmt.close()
} catch (err: Exception) {
chunkmaster.logger.severe("Error creating table $table.") chunkmaster.logger.severe("Error creating table $table.")
chunkmaster.logger.severe(err.message) chunkmaster.logger.severe(e.message)
chunkmaster.logger.info(ExceptionUtils.getStackTrace(err)); chunkmaster.logger.info(ExceptionUtils.getStackTrace(e))
} }
} }
for (table in needUpdate) { for (table in needUpdate) {
val updateSql = "ALTER TABLE ${table.first} ADD COLUMN ${table.second.first} ${table.second.second}" val updateSql = "ALTER TABLE ${table.first} ADD COLUMN ${table.second.first} ${table.second.second}"
try { try {
val stmt = connnection.prepareStatement(updateSql) executeStatement(updateSql, HashMap(), null)
stmt.execute()
stmt.close()
chunkmaster.logger.info("Updated table ${table.first} with sql $updateSql") chunkmaster.logger.info("Updated table ${table.first} with sql $updateSql")
} catch (e: Exception) { } catch (e: Exception) {
chunkmaster.logger.severe("Failed to update table ${table.first} with sql $updateSql") chunkmaster.logger.severe("Failed to update table ${table.first} with sql $updateSql")

@ -2,11 +2,8 @@ package net.trivernis.chunkmaster.lib.generation
import io.papermc.lib.PaperLib import io.papermc.lib.PaperLib
import net.trivernis.chunkmaster.Chunkmaster import net.trivernis.chunkmaster.Chunkmaster
import org.bukkit.Chunk
import org.bukkit.Server import org.bukkit.Server
import org.bukkit.World import org.bukkit.World
import java.lang.Exception
import java.lang.NullPointerException
class GenerationManager(private val chunkmaster: Chunkmaster, private val server: Server) { class GenerationManager(private val chunkmaster: Chunkmaster, private val server: Server) {
@ -31,35 +28,37 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
val centerChunk = ChunkCoordinates(world.spawnLocation.chunk.x, world.spawnLocation.chunk.z) val centerChunk = ChunkCoordinates(world.spawnLocation.chunk.x, world.spawnLocation.chunk.z)
val generationTask = createGenerationTask(world, centerChunk, centerChunk, stopAfter) val generationTask = createGenerationTask(world, centerChunk, centerChunk, stopAfter)
val insertStatement = chunkmaster.sqliteConnection.prepareStatement( chunkmaster.sqliteManager.executeStatement(
""" """
INSERT INTO generation_tasks (center_x, center_z, last_x, last_z, world, stop_after) INSERT INTO generation_tasks (center_x, center_z, last_x, last_z, world, stop_after)
values (?, ?, ?, ?, ?, ?) values (?, ?, ?, ?, ?, ?)
""" """,
HashMap(
mapOf(
1 to centerChunk.x,
2 to centerChunk.z,
3 to centerChunk.x,
4 to centerChunk.z,
5 to world.name,
6 to stopAfter
)
),
null
) )
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( var id = 0
chunkmaster.sqliteManager.executeStatement(
""" """
SELECT id FROM generation_tasks ORDER BY id DESC LIMIT 1 SELECT id FROM generation_tasks ORDER BY id DESC LIMIT 1
""".trimIndent() """.trimIndent(),
) HashMap()
getIdStatement.execute() ) {
val result = getIdStatement.resultSet it.next()
result.next() id = it.getInt("id")
val id: Int = result.getInt("id") }
insertStatement.close()
getIdStatement.close()
generationTask.onEndReached { generationTask.onEndReached {
chunkmaster.logger.info("Task #${id} finished after ${generationTask.count} chunks.") chunkmaster.logger.info("Task #${id} finished after ${it.count} chunks.")
removeTask(id) removeTask(id)
} }
@ -115,14 +114,13 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
} }
if (taskEntry != null) { if (taskEntry != null) {
taskEntry.cancel() taskEntry.cancel()
val deleteTask = chunkmaster.sqliteConnection.prepareStatement( chunkmaster.sqliteManager.executeStatement(
""" """
DELETE FROM generation_tasks WHERE id = ?; DELETE FROM generation_tasks WHERE id = ?;
""".trimIndent() """.trimIndent(),
HashMap(mapOf(1 to taskEntry.id)),
null
) )
deleteTask.setInt(1, taskEntry.id)
deleteTask.execute()
deleteTask.close()
if (taskEntry is RunningTaskEntry) { if (taskEntry is RunningTaskEntry) {
if (taskEntry.task.isCancelled) { if (taskEntry.task.isCancelled) {
@ -173,24 +171,24 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
* Starts all generation tasks. * Starts all generation tasks.
*/ */
fun startAll() { fun startAll() {
val savedTasksStatement = chunkmaster.sqliteConnection.prepareStatement("SELECT * FROM generation_tasks") chunkmaster.sqliteManager.executeStatement("SELECT * FROM generation_tasks", HashMap()) {
savedTasksStatement.execute() val res = it
val res = savedTasksStatement.resultSet while (res.next()) {
while (res.next()) { try {
try { val id = res.getInt("id")
val id = res.getInt("id") val world = server.getWorld(res.getString("world"))
val world = server.getWorld(res.getString("world")) val center = ChunkCoordinates(res.getInt("center_x"), res.getInt("center_z"))
val center = ChunkCoordinates(res.getInt("center_x"), res.getInt("center_z")) val last = ChunkCoordinates(res.getInt("last_x"), res.getInt("last_z"))
val last = ChunkCoordinates(res.getInt("last_x"), res.getInt("last_z")) val stopAfter = res.getInt("stop_after")
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, stopAfter)
resumeTask(world!!, center, last, id, stopAfter) }
} catch (error: NullPointerException) {
chunkmaster.logger.severe("Failed to load Task ${res.getInt("id")}.")
} }
} catch (error: NullPointerException) {
chunkmaster.logger.severe("Failed to load Task ${res.getInt("id")}.")
} }
} }
savedTasksStatement.close()
if (tasks.isNotEmpty()) { if (tasks.isNotEmpty()) {
chunkmaster.logger.info("${tasks.size} saved tasks loaded.") chunkmaster.logger.info("${tasks.size} saved tasks loaded.")
} }
@ -226,22 +224,21 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
chunkmaster.logger.info( chunkmaster.logger.info(
"""Task #${task.id} running for "${genTask.world.name}". """Task #${task.id} running for "${genTask.world.name}".
|Progress ${task.generationTask.count} chunks |Progress ${task.generationTask.count} chunks
|${if (task.generationTask.stopAfter > 0) "(${"%.2f".format((task.generationTask.count.toDouble() / |${if (task.generationTask.stopAfter > 0) "(${"%.2f".format(
task.generationTask.stopAfter.toDouble()) * 100)}%)" else ""}. (task.generationTask.count.toDouble() /
task.generationTask.stopAfter.toDouble()) * 100
)}%)" else ""}.
| Speed: ${"%.1f".format(task.generationSpeed)} chunks/sec, | Speed: ${"%.1f".format(task.generationSpeed)} chunks/sec,
|Last Chunk: ${genTask.lastChunk.x}, ${genTask.lastChunk.z}""".trimMargin("|").replace('\n', ' ') |Last Chunk: ${genTask.lastChunk.x}, ${genTask.lastChunk.z}""".trimMargin("|").replace('\n', ' ')
) )
val updateStatement = chunkmaster.sqliteConnection.prepareStatement( chunkmaster.sqliteManager.executeStatement(
""" """
UPDATE generation_tasks SET last_x = ?, last_z = ? UPDATE generation_tasks SET last_x = ?, last_z = ?
WHERE id = ? WHERE id = ?
""".trimIndent() """.trimIndent(),
HashMap(mapOf(1 to genTask.lastChunk.x, 2 to genTask.lastChunk.z, 3 to task.id)),
null
) )
updateStatement.setInt(1, genTask.lastChunk.x)
updateStatement.setInt(2, genTask.lastChunk.z)
updateStatement.setInt(3, task.id)
updateStatement.execute()
updateStatement.close()
} catch (error: Exception) { } catch (error: Exception) {
chunkmaster.logger.warning("Exception when saving task progress ${error.message}") chunkmaster.logger.warning("Exception when saving task progress ${error.message}")
} }

@ -25,7 +25,7 @@ abstract class GenerationTask(plugin: Chunkmaster, centerChunk: ChunkCoordinates
protected val maxLoadedChunks = plugin.config.getInt("generation.max-loaded-chunks") protected val maxLoadedChunks = plugin.config.getInt("generation.max-loaded-chunks")
protected val chunksPerStep = plugin.config.getInt("generation.chunks-per-step") protected val chunksPerStep = plugin.config.getInt("generation.chunks-per-step")
protected var endReachedCallback: (() -> Unit)? = null protected var endReachedCallback: ((GenerationTask) -> Unit)? = null
private set private set
abstract override fun run() abstract override fun run()
@ -58,7 +58,7 @@ abstract class GenerationTask(plugin: Chunkmaster, centerChunk: ChunkCoordinates
/** /**
* Registers end reached callback * Registers end reached callback
*/ */
fun onEndReached(cb: () -> Unit) { fun onEndReached(cb: (GenerationTask) -> Unit) {
endReachedCallback = cb endReachedCallback = cb
} }
} }

@ -38,7 +38,7 @@ class GenerationTaskPaper(
} else if (pendingChunks.size < maxPendingChunks) { // if more than 10 chunks are pending, wait. } else if (pendingChunks.size < maxPendingChunks) { // if more than 10 chunks are pending, wait.
if (borderReached()) { if (borderReached()) {
endReached = true endReached = true
endReachedCallback?.invoke() endReachedCallback?.invoke(this)
return return
} }

@ -33,7 +33,7 @@ class GenerationTaskSpigot(
} else { } else {
if (borderReached()) { if (borderReached()) {
endReached = true endReached = true
endReachedCallback?.invoke() endReachedCallback?.invoke(this)
return return
} }

Loading…
Cancel
Save