Working version
parent
9bf9c61b0d
commit
1343dbf249
@ -1,18 +1,63 @@
|
|||||||
package net.trivernis.chunkmaster
|
package net.trivernis.chunkmaster
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.commands.CommandGenerate
|
||||||
|
import net.trivernis.chunkmaster.lib.GenerationManager
|
||||||
import net.trivernis.chunkmaster.lib.Spiral
|
import net.trivernis.chunkmaster.lib.Spiral
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
import java.lang.Exception
|
||||||
|
import java.sql.Connection
|
||||||
|
import java.sql.DriverManager
|
||||||
|
import kotlin.math.log
|
||||||
|
|
||||||
class Chunkmaster: JavaPlugin() {
|
class Chunkmaster: JavaPlugin() {
|
||||||
|
lateinit var sqliteConnection: Connection
|
||||||
|
var dbname: String? = null
|
||||||
|
lateinit var generationManager: GenerationManager
|
||||||
|
|
||||||
override fun onEnable() {
|
override fun onEnable() {
|
||||||
configure()
|
configure()
|
||||||
|
initDatabase()
|
||||||
|
generationManager = GenerationManager(this, server)
|
||||||
|
generationManager.init()
|
||||||
|
getCommand("generate")?.setExecutor(CommandGenerate(this))
|
||||||
|
server.pluginManager.registerEvents(ChunkmasterEvents(this, server), this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDisable() {
|
override fun onDisable() {
|
||||||
|
logger.info("Stopping all generation tasks...")
|
||||||
|
generationManager.stopAll()
|
||||||
|
sqliteConnection.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun configure() {
|
private fun configure() {
|
||||||
|
dataFolder.mkdir()
|
||||||
config.options().copyDefaults(true)
|
config.options().copyDefaults(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the database
|
||||||
|
*/
|
||||||
|
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 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'
|
||||||
|
);
|
||||||
|
""".trimIndent())
|
||||||
|
createTableStatement.execute()
|
||||||
|
createTableStatement.close()
|
||||||
|
logger.info("Database tables created.")
|
||||||
|
} catch(e: Exception) {
|
||||||
|
logger.warning("Failed to init database: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package net.trivernis.chunkmaster
|
||||||
|
|
||||||
|
import org.bukkit.Server
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent
|
||||||
|
|
||||||
|
class ChunkmasterEvents(private val chunkmaster: Chunkmaster, private val server: Server): Listener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Autostart generation tasks
|
||||||
|
*/
|
||||||
|
fun onPlayerQuit(event: PlayerQuitEvent) {
|
||||||
|
if (server.onlinePlayers.size == 1 && server.onlinePlayers.contains(event.player)) {
|
||||||
|
chunkmaster.generationManager.startAll()
|
||||||
|
chunkmaster.logger.info("Server is empty. Starting chunk generation tasks.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Autostop generation tasks
|
||||||
|
*/
|
||||||
|
fun onPlayerJoin(event: PlayerJoinEvent) {
|
||||||
|
if (server.onlinePlayers.size == 1 || server.onlinePlayers.isEmpty()) {
|
||||||
|
chunkmaster.generationManager.stopAll()
|
||||||
|
chunkmaster.logger.info("Stopping generation tasks because of player join.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package net.trivernis.chunkmaster.commands
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
|
import org.bukkit.command.Command
|
||||||
|
import org.bukkit.command.CommandExecutor
|
||||||
|
import org.bukkit.command.CommandSender
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
|
||||||
|
class CommandGenerate(private val chunkmaster: Chunkmaster): CommandExecutor {
|
||||||
|
/**
|
||||||
|
* Start world generation task on commmand
|
||||||
|
*/
|
||||||
|
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}\"")
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
sender.sendMessage("You do not have permission.")
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return if (args.size == 1) {
|
||||||
|
val world = sender.server.getWorld(args[0])
|
||||||
|
if (world != null) {
|
||||||
|
chunkmaster.generationManager.addTask(world)
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
sender.sendMessage("World \"${args[0]}\" not found")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sender.sendMessage("You need to specify a world")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,127 @@
|
|||||||
package net.trivernis.chunkmaster.lib
|
package net.trivernis.chunkmaster.lib
|
||||||
|
|
||||||
class GenerationManager {
|
import javafx.concurrent.Task
|
||||||
//TODO: Implement
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
|
import org.bukkit.Chunk
|
||||||
|
import org.bukkit.Server
|
||||||
|
import org.bukkit.World
|
||||||
|
import org.bukkit.scheduler.BukkitTask
|
||||||
|
import java.lang.Exception
|
||||||
|
import java.lang.NullPointerException
|
||||||
|
|
||||||
|
class GenerationManager(private val chunkmaster: Chunkmaster, private val server: Server) {
|
||||||
|
|
||||||
|
private val tasks: HashSet<TaskEntry> = HashSet()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a generation task
|
||||||
|
*/
|
||||||
|
fun addTask(world: World): 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 (?, ?, ?, ?, ?)
|
||||||
|
""")
|
||||||
|
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.execute()
|
||||||
|
val getIdStatement = chunkmaster.sqliteConnection.prepareStatement("""
|
||||||
|
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")
|
||||||
|
tasks.add(TaskEntry(id, task, generationTask))
|
||||||
|
insertStatement.close()
|
||||||
|
getIdStatement.close()
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resumes a generation task
|
||||||
|
*/
|
||||||
|
fun resumeTask(world: World, center: Chunk, last: Chunk, id: Int) {
|
||||||
|
chunkmaster.logger.info("Resuming chunk generation task for world \"${world.name}\"")
|
||||||
|
val generationTask = GenerationTask(chunkmaster, world, center, last)
|
||||||
|
val task = server.scheduler.runTaskTimer(chunkmaster, generationTask, 10, 2)
|
||||||
|
tasks.add(TaskEntry(id, task, generationTask))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init
|
||||||
|
* Loads tasks from the database and resumes them
|
||||||
|
*/
|
||||||
|
fun init() {
|
||||||
|
chunkmaster.logger.info("Creating task to load chunk generation Tasks later...")
|
||||||
|
server.scheduler.runTaskLater(chunkmaster, Runnable {
|
||||||
|
startAll() // run startAll after 10 seconds
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops all generation tasks
|
||||||
|
*/
|
||||||
|
fun stopAll() {
|
||||||
|
saveProgress()
|
||||||
|
for (task in tasks) {
|
||||||
|
task.generationTask.cancel()
|
||||||
|
task.task.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts all generation tasks.
|
||||||
|
*/
|
||||||
|
fun startAll() {
|
||||||
|
chunkmaster.logger.info("Loading saved chunk generation tasks...")
|
||||||
|
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 = world!!.getChunkAt(res.getInt("center_x"), res.getInt("center_z"))
|
||||||
|
val last = world.getChunkAt(res.getInt("last_x"), res.getInt("last_z"))
|
||||||
|
resumeTask(world, center, last, id)
|
||||||
|
} catch (error: NullPointerException) {
|
||||||
|
server.consoleSender.sendMessage("Failed to load Task ${res.getInt("id")}.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
savedTasksStatement.close()
|
||||||
|
server.scheduler.runTaskTimer(chunkmaster, Runnable {
|
||||||
|
saveProgress() // save progress every 30 seconds
|
||||||
|
}, 600, 600)
|
||||||
|
chunkmaster.logger.info("${tasks.size} saved tasks loaded.")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the task progress
|
||||||
|
*/
|
||||||
|
private fun saveProgress() {
|
||||||
|
for (task in tasks) {
|
||||||
|
try {
|
||||||
|
val genTask = task.generationTask
|
||||||
|
server.consoleSender.sendMessage("Task #${task.id} running for \"${genTask.world.name}\"." +
|
||||||
|
"Progress ${task.generationTask.count} chunks. Last Chunk: ${genTask.lastChunk.x}, ${genTask.lastChunk.z}")
|
||||||
|
val updateStatement = chunkmaster.sqliteConnection.prepareStatement("""
|
||||||
|
UPDATE generation_tasks SET last_x = ?, last_z = ?
|
||||||
|
WHERE id = ?
|
||||||
|
""".trimIndent())
|
||||||
|
updateStatement.setInt(1, genTask.lastChunk.x)
|
||||||
|
updateStatement.setInt(2, genTask.lastChunk.z)
|
||||||
|
updateStatement.setInt(3, task.id)
|
||||||
|
updateStatement.execute()
|
||||||
|
updateStatement.close()
|
||||||
|
} catch (error: Exception) {
|
||||||
|
server.consoleSender.sendMessage("Exception when saving task progress ${error.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
|
import org.bukkit.Chunk
|
||||||
|
import org.bukkit.World
|
||||||
|
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 spiral: Spiral = Spiral(Pair(centerChunk.x, centerChunk.z), Pair(startChunk.x, startChunk.z))
|
||||||
|
private val loadedChunks: HashSet<Chunk> = HashSet()
|
||||||
|
var count = 0
|
||||||
|
get() = field
|
||||||
|
var lastChunk: Chunk = startChunk
|
||||||
|
get() = field
|
||||||
|
|
||||||
|
override fun run() {
|
||||||
|
if (loadedChunks.size > 10) {
|
||||||
|
for (chunk in loadedChunks) {
|
||||||
|
if (chunk.isLoaded) {
|
||||||
|
chunk.unload(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val nextChunkCoords = spiral.next()
|
||||||
|
val chunk = world.getChunkAt(nextChunkCoords.first, nextChunkCoords.second)
|
||||||
|
|
||||||
|
if (!world.isChunkGenerated(chunk.x, chunk.z)) {
|
||||||
|
chunk.load(true)
|
||||||
|
loadedChunks.add(chunk)
|
||||||
|
}
|
||||||
|
lastChunk = chunk
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancel() {
|
||||||
|
for (chunk in loadedChunks) {
|
||||||
|
if (chunk.isLoaded) {
|
||||||
|
chunk.unload(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib
|
||||||
|
|
||||||
|
import org.bukkit.scheduler.BukkitTask
|
||||||
|
|
||||||
|
data class TaskEntry(val id: Int, val task: BukkitTask, val generationTask: GenerationTask) {
|
||||||
|
}
|
Loading…
Reference in New Issue