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

@ -2,10 +2,14 @@ package net.trivernis.chunkmaster.lib
import net.trivernis.chunkmaster.Chunkmaster
import org.apache.commons.lang.exception.ExceptionUtils
import org.sqlite.SQLiteConnection
import java.lang.Exception
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(
Pair(
"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 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.
*/
fun checkUpdate() {
val meta = connnection.metaData
private fun checkUpdate() {
val meta = getConnection()!!.metaData
for (table in tables) {
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()) {
needUpdate.add(Pair(table.first, column))
}
resColumn.close()
}
} else {
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.
*/
fun performUpdate() {
private fun performUpdate() {
for (table in needCreation) {
try {
var tableDef = "CREATE TABLE IF NOT EXISTS $table ("
@ -59,21 +108,17 @@ class SqlUpdateManager(private val connnection: Connection, private val chunkmas
}
tableDef = tableDef.substringBeforeLast(",") + ");"
chunkmaster.logger.info("Creating table $table with definition $tableDef")
val stmt = connnection.prepareStatement(tableDef)
stmt.execute()
stmt.close()
} catch (err: Exception) {
executeStatement(tableDef, HashMap(), null)
} catch (e: Exception) {
chunkmaster.logger.severe("Error creating table $table.")
chunkmaster.logger.severe(err.message)
chunkmaster.logger.info(ExceptionUtils.getStackTrace(err));
chunkmaster.logger.severe(e.message)
chunkmaster.logger.info(ExceptionUtils.getStackTrace(e))
}
}
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()
executeStatement(updateSql, HashMap(), null)
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")

@ -2,11 +2,8 @@ package net.trivernis.chunkmaster.lib.generation
import io.papermc.lib.PaperLib
import net.trivernis.chunkmaster.Chunkmaster
import org.bukkit.Chunk
import org.bukkit.Server
import org.bukkit.World
import java.lang.Exception
import java.lang.NullPointerException
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 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)
values (?, ?, ?, ?, ?, ?)
"""
INSERT INTO generation_tasks (center_x, center_z, last_x, last_z, world, stop_after)
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
""".trimIndent()
)
getIdStatement.execute()
val result = getIdStatement.resultSet
result.next()
val id: Int = result.getInt("id")
insertStatement.close()
getIdStatement.close()
""".trimIndent(),
HashMap()
) {
it.next()
id = it.getInt("id")
}
generationTask.onEndReached {
chunkmaster.logger.info("Task #${id} finished after ${generationTask.count} chunks.")
chunkmaster.logger.info("Task #${id} finished after ${it.count} chunks.")
removeTask(id)
}
@ -115,14 +114,13 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
}
if (taskEntry != null) {
taskEntry.cancel()
val deleteTask = chunkmaster.sqliteConnection.prepareStatement(
chunkmaster.sqliteManager.executeStatement(
"""
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.task.isCancelled) {
@ -173,24 +171,24 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
* Starts all generation tasks.
*/
fun startAll() {
val savedTasksStatement = chunkmaster.sqliteConnection.prepareStatement("SELECT * FROM generation_tasks")
savedTasksStatement.execute()
val res = savedTasksStatement.resultSet
while (res.next()) {
try {
val id = res.getInt("id")
val world = server.getWorld(res.getString("world"))
val center = ChunkCoordinates(res.getInt("center_x"), res.getInt("center_z"))
val last = ChunkCoordinates(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, stopAfter)
chunkmaster.sqliteManager.executeStatement("SELECT * FROM generation_tasks", HashMap()) {
val res = it
while (res.next()) {
try {
val id = res.getInt("id")
val world = server.getWorld(res.getString("world"))
val center = ChunkCoordinates(res.getInt("center_x"), res.getInt("center_z"))
val last = ChunkCoordinates(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, 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()) {
chunkmaster.logger.info("${tasks.size} saved tasks loaded.")
}
@ -226,22 +224,21 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
chunkmaster.logger.info(
"""Task #${task.id} running for "${genTask.world.name}".
|Progress ${task.generationTask.count} chunks
|${if (task.generationTask.stopAfter > 0) "(${"%.2f".format((task.generationTask.count.toDouble() /
task.generationTask.stopAfter.toDouble()) * 100)}%)" else ""}.
|${if (task.generationTask.stopAfter > 0) "(${"%.2f".format(
(task.generationTask.count.toDouble() /
task.generationTask.stopAfter.toDouble()) * 100
)}%)" else ""}.
| Speed: ${"%.1f".format(task.generationSpeed)} chunks/sec,
|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 = ?
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) {
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 chunksPerStep = plugin.config.getInt("generation.chunks-per-step")
protected var endReachedCallback: (() -> Unit)? = null
protected var endReachedCallback: ((GenerationTask) -> Unit)? = null
private set
abstract override fun run()
@ -58,7 +58,7 @@ abstract class GenerationTask(plugin: Chunkmaster, centerChunk: ChunkCoordinates
/**
* Registers end reached callback
*/
fun onEndReached(cb: () -> Unit) {
fun onEndReached(cb: (GenerationTask) -> Unit) {
endReachedCallback = cb
}
}

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

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

Loading…
Cancel
Save