Working version

pull/4/head
Trivernis 5 years ago
parent 9bf9c61b0d
commit 1343dbf249

@ -25,6 +25,9 @@ repositories {
maven {
url 'https://papermc.io/repo/repository/maven-public/'
}
maven {
url 'https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc'
}
}
dependencies {
@ -32,6 +35,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
testCompile group: 'junit', name: 'junit', version: '4.12'
compileOnly "org.spigotmc:spigot-api:1.14.4-R0.1-SNAPSHOT"
compile group: 'org.xerial', name: 'sqlite-jdbc', version: '3.28.0'
}
jar {

@ -1,18 +1,63 @@
package net.trivernis.chunkmaster
import net.trivernis.chunkmaster.commands.CommandGenerate
import net.trivernis.chunkmaster.lib.GenerationManager
import net.trivernis.chunkmaster.lib.Spiral
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() {
lateinit var sqliteConnection: Connection
var dbname: String? = null
lateinit var generationManager: GenerationManager
override fun onEnable() {
configure()
initDatabase()
generationManager = GenerationManager(this, server)
generationManager.init()
getCommand("generate")?.setExecutor(CommandGenerate(this))
server.pluginManager.registerEvents(ChunkmasterEvents(this, server), this)
}
override fun onDisable() {
logger.info("Stopping all generation tasks...")
generationManager.stopAll()
sqliteConnection.close()
}
private fun configure() {
dataFolder.mkdir()
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
class GenerationManager {
//TODO: Implement
import javafx.concurrent.Task
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)
}
}
}
}

@ -11,6 +11,17 @@ class Spiral(private val center: Pair<Int, Int>, private val start: Pair<Int, In
* Returns the next value in the spiral
*/
fun next(): Pair<Int, Int> {
if (count == 0 && currentPos != center) {
val distances = getDistances(center, currentPos)
if (distances.second < distances.first && distances.first > 0) {
direction = 1
} else if (distances.first > distances.second && distances.second < 0) {
direction = 2
} else if (distances.second > distances.first && distances.first < 0) {
direction = 3
}
}
if (count == 1) { // because of the center behaviour
count ++
return currentPos

@ -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) {
}

@ -4,9 +4,18 @@ version: '1.0 SNAPSHOT'
description: Chunk commands plugin.
author: Trivernis
website: trivernis.net
api-version: '1.14'
commands:
generate:
description: Generates chunks starting from the world spawnpoint
permission: chumkmaster.generate
usage: /generate [world]
permissions:
cunkmaster.generate:
description: Allows generate command
default: op
chunkmaster.*:
description: Wildcard permission
default: op
default: op
children:
- chunkmaster.generate
Loading…
Cancel
Save