From 52ba24390fe1051be67777ad73926f2ae27b0c68 Mon Sep 17 00:00:00 2001 From: Trivernis Date: Tue, 17 Sep 2019 18:11:27 +0200 Subject: [PATCH] Bug fixes, config file, more commands - fixed chunk skips skipping not-generated chunks - changed command structure to /chunkmaster - added config options - updated readme --- README.md | 51 ++++++++++--- .../net/trivernis/chunkmaster/Chunkmaster.kt | 21 ++--- .../chunkmaster/ChunkmasterEvents.kt | 20 +++-- .../chunkmaster/commands/CmdCancel.kt | 28 +++++++ .../chunkmaster/commands/CmdGenerate.kt | 55 ++++++++++++++ .../trivernis/chunkmaster/commands/CmdList.kt | 27 +++++++ .../chunkmaster/commands/CmdPause.kt | 27 +++++++ .../chunkmaster/commands/CmdResume.kt | 24 ++++++ .../commands/CommandChunkmaster.kt | 76 +++++++++++++++++++ .../chunkmaster/commands/CommandGenerate.kt | 54 ------------- .../commands/CommandListGenTasks.kt | 24 ------ .../commands/CommandRemoveGenTask.kt | 22 ------ .../chunkmaster/lib/GenerationManager.kt | 68 ++++++++++++----- .../chunkmaster/lib/GenerationTask.kt | 22 ++++-- .../net/trivernis/chunkmaster/lib/Spiral.kt | 4 +- .../trivernis/chunkmaster/lib/Subcommand.kt | 8 ++ src/main/resources/plugin.yml | 51 +++++++------ 17 files changed, 410 insertions(+), 172 deletions(-) create mode 100644 src/main/kotlin/net/trivernis/chunkmaster/commands/CmdCancel.kt create mode 100644 src/main/kotlin/net/trivernis/chunkmaster/commands/CmdGenerate.kt create mode 100644 src/main/kotlin/net/trivernis/chunkmaster/commands/CmdList.kt create mode 100644 src/main/kotlin/net/trivernis/chunkmaster/commands/CmdPause.kt create mode 100644 src/main/kotlin/net/trivernis/chunkmaster/commands/CmdResume.kt create mode 100644 src/main/kotlin/net/trivernis/chunkmaster/commands/CommandChunkmaster.kt delete mode 100644 src/main/kotlin/net/trivernis/chunkmaster/commands/CommandGenerate.kt delete mode 100644 src/main/kotlin/net/trivernis/chunkmaster/commands/CommandListGenTasks.kt delete mode 100644 src/main/kotlin/net/trivernis/chunkmaster/commands/CommandRemoveGenTask.kt create mode 100644 src/main/kotlin/net/trivernis/chunkmaster/lib/Subcommand.kt diff --git a/README.md b/README.md index d148660..9f59125 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,50 @@ # chunkmaster -This plugin can be used to pre-generate the region of a world around the spawn chunk. The plugin provides the commands - -- `/generate [world] [stopAt]` - Pre-generates chunks in the current world until the world border or the stopAt chunk count is reached. -- `/listgentasks` - Lists all running generation tasks (and their ids) -- `/removegentask [taskId]` - Removes a generation task (stops it permanently) - +This plugin can be used to pre-generate the region of a world around the spawn chunk. The generation automatically pauses when a player joins the server (assuming the server was empty before) and resumes when the server is empty again. The generation also auto-resumes after a server restart. The plugin tracks the ticks per second and pauses the generation when the tps is lower than 2. -## Future Features +## Commands + +All features can be accessed with the command `/chunkmaster` or the aliases `/chm`, `chunkm`, `cmaster`. + +- `/chunkmaster generate [world] [chunk count]` Starts the generation until the specified chunk count or the world border is reached. +- `/chunkmaster list` Lists all running generation tasks +- `/chunkmaster cancel ` Cancels the generation task with the specified id (if it is running). +- `/chunkmaster pause` Pauses all generation tasks until the resume command is executed. +- `/chunkmaster resume` Resumes all paused generation tasks. +- `/chunkmaster reload` Reloads the configuration file. + +## Config + +```yaml +generation: + + # The period (in ticks) in which a generation step is run. + # Higher values mean less performance impact but slower generation. + # The value should be a positive integer. + period: 2 + + # The number of already generated chunks that will be skipped for each step. + # Notice that these still have a performance impact because the server needs to check + # if the chunk is generated. + # Higher values mean faster generation but greater performance impact. + # The value should be a positive integer. + chunks-skips-per-step: 5 + + # The maximum milliseconds per tick the server is allowed to have + # during the cunk generation process. + # If the mspt is greather than this, the chunk generation task pauses. + # The value should be a positive integer greater than 50. + mspt-pause-threshold: 500 -- pause generation tasks until restarted by command -- configure the tps pause limit \ No newline at end of file + # If the chunk generation process should pause on player join. + # Notice that playing on a server that constantly generates chunks can be + # very laggy and can cause it to crash. + # You could configure the values above so that the performance impact of the generation + # process is minimal. + # The value should be a boolean + pause-on-join: true +``` \ No newline at end of file diff --git a/src/main/kotlin/net/trivernis/chunkmaster/Chunkmaster.kt b/src/main/kotlin/net/trivernis/chunkmaster/Chunkmaster.kt index ef8a8c8..af8ca59 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/Chunkmaster.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/Chunkmaster.kt @@ -1,17 +1,13 @@ package net.trivernis.chunkmaster -import net.trivernis.chunkmaster.commands.CommandGenerate -import net.trivernis.chunkmaster.commands.CommandListGenTasks -import net.trivernis.chunkmaster.commands.CommandRemoveGenTask +import net.trivernis.chunkmaster.commands.* 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 import java.sql.Connection import java.sql.DriverManager -import kotlin.math.log class Chunkmaster: JavaPlugin() { lateinit var sqliteConnection: Connection @@ -19,7 +15,7 @@ class Chunkmaster: JavaPlugin() { lateinit var generationManager: GenerationManager private lateinit var tpsTask: BukkitTask var mspt = 50L // keep track of the milliseconds per tick - get() = field + private set /** * On enable of the plugin @@ -29,10 +25,12 @@ class Chunkmaster: JavaPlugin() { initDatabase() generationManager = GenerationManager(this, server) generationManager.init() - getCommand("generate")?.setExecutor(CommandGenerate(this)) - getCommand("listgentasks")?.setExecutor(CommandListGenTasks(this)) - getCommand("removegentask")?.setExecutor(CommandRemoveGenTask(this)) + + getCommand("chunkmaster")?.setExecutor(CommandChunkmaster(this, server)) + getCommand("chunkmaster")?.aliases = mutableListOf("chm", "chunkm", "cmaster") + server.pluginManager.registerEvents(ChunkmasterEvents(this, server), this) + tpsTask = server.scheduler.runTaskTimer(this, Runnable { val start = System.currentTimeMillis() server.scheduler.runTaskLater(this, Runnable { @@ -55,7 +53,12 @@ class Chunkmaster: JavaPlugin() { */ private fun configure() { dataFolder.mkdir() + config.addDefault("generation.period", 2L) + config.addDefault("generation.chunks-skips-per-step", 4) + config.addDefault("generation.mspt-pause-threshold", 500L) + config.addDefault("generation.pause-on-join", true) config.options().copyDefaults(true) + saveConfig() } /** diff --git a/src/main/kotlin/net/trivernis/chunkmaster/ChunkmasterEvents.kt b/src/main/kotlin/net/trivernis/chunkmaster/ChunkmasterEvents.kt index 2dc94fe..9cae7f2 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/ChunkmasterEvents.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/ChunkmasterEvents.kt @@ -8,14 +8,20 @@ import org.bukkit.event.player.PlayerQuitEvent class ChunkmasterEvents(private val chunkmaster: Chunkmaster, private val server: Server): Listener { + private val pauseOnJoin = chunkmaster.config.getBoolean("generation.pause-on-join") + /** * Autostart generation tasks */ @EventHandler fun onPlayerQuit(event: PlayerQuitEvent) { - if (server.onlinePlayers.size == 1 && server.onlinePlayers.contains(event.player) || + if (pauseOnJoin) { + if (server.onlinePlayers.size == 1 && server.onlinePlayers.contains(event.player) || server.onlinePlayers.isEmpty()) { - chunkmaster.generationManager.startAll() - chunkmaster.logger.info("Server is empty. Starting chunk generation tasks.") + if (!chunkmaster.generationManager.paused) { + chunkmaster.generationManager.startAll() + chunkmaster.logger.info("Server is empty. Starting chunk generation tasks.") + } + } } } @@ -23,9 +29,11 @@ class ChunkmasterEvents(private val chunkmaster: Chunkmaster, private val server * Autostop generation tasks */ @EventHandler 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.") + if (pauseOnJoin) { + if (server.onlinePlayers.size == 1 || server.onlinePlayers.isEmpty()) { + chunkmaster.generationManager.stopAll() + chunkmaster.logger.info("Stopping generation tasks because of player join.") + } } } } \ No newline at end of file diff --git a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdCancel.kt b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdCancel.kt new file mode 100644 index 0000000..80daf60 --- /dev/null +++ b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdCancel.kt @@ -0,0 +1,28 @@ +package net.trivernis.chunkmaster.commands + +import net.md_5.bungee.api.ChatColor +import net.md_5.bungee.api.chat.ComponentBuilder +import net.trivernis.chunkmaster.Chunkmaster +import net.trivernis.chunkmaster.lib.Subcommand +import org.bukkit.command.CommandSender + +class CmdCancel(private val chunkmaster: Chunkmaster): Subcommand { + override val name = "cancel" + + override fun execute(sender: CommandSender, args: List): Boolean { + return if (args.isNotEmpty() && args[0].toIntOrNull() != null) { + if (chunkmaster.generationManager.removeTask(args[0].toInt())) { + sender.sendMessage("Task ${args[0]} canceled.") + true + } else { + sender.spigot().sendMessage(*ComponentBuilder("Task ${args[0]} not found!") + .color(ChatColor.RED).create()) + false + } + } else { + sender.spigot().sendMessage(*ComponentBuilder("You need to provide a task id to cancel.") + .color(ChatColor.RED).create()) + false + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdGenerate.kt b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdGenerate.kt new file mode 100644 index 0000000..4ef2a15 --- /dev/null +++ b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdGenerate.kt @@ -0,0 +1,55 @@ +package net.trivernis.chunkmaster.commands + +import net.md_5.bungee.api.ChatColor +import net.md_5.bungee.api.chat.ComponentBuilder +import net.trivernis.chunkmaster.Chunkmaster +import net.trivernis.chunkmaster.lib.Subcommand +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +class CmdGenerate(private val chunkmaster: Chunkmaster): Subcommand { + override val name = "generate" + + override fun execute(sender: CommandSender, args: List): Boolean { + var worldName = "" + var stopAfter = -1 + if (sender is Player) { + if (args.isNotEmpty()) { + if (args[0].toIntOrNull() != null) { + stopAfter = args[0].toInt() + } else { + worldName = args[0] + } + if (args.size > 1 && args[1].toIntOrNull() != null) { + stopAfter = args[1].toInt() + } + } else { + worldName = sender.world.name + } + } else { + if (args.isNotEmpty()) { + worldName = args[0] + if (args.size > 1 && args[1].toIntOrNull() != null) { + stopAfter = args[1].toInt() + } + } else { + sender.spigot().sendMessage( + *ComponentBuilder("You need to provide a world name").color(ChatColor.RED).create()) + return false + } + } + val world = chunkmaster.server.getWorld(worldName) + return if (world != null) { + chunkmaster.generationManager.addTask(world, stopAfter) + sender.spigot().sendMessage(*ComponentBuilder("Generation task for world ").color(ChatColor.BLUE) + .append(worldName).color(ChatColor.GREEN).append(" until ").color(ChatColor.BLUE) + .append(if (stopAfter > 0) "$stopAfter chunks" else "WorldBorder").color(ChatColor.GREEN) + .append(" successfully created").color(ChatColor.BLUE).create()) + true + } else { + sender.spigot().sendMessage(*ComponentBuilder("World ").color(ChatColor.RED) + .append(worldName).color(ChatColor.GREEN).append(" not found!").color(ChatColor.RED).create()) + false + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdList.kt b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdList.kt new file mode 100644 index 0000000..3e5cca8 --- /dev/null +++ b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdList.kt @@ -0,0 +1,27 @@ +package net.trivernis.chunkmaster.commands + +import net.md_5.bungee.api.ChatColor +import net.md_5.bungee.api.chat.ComponentBuilder +import net.trivernis.chunkmaster.Chunkmaster +import net.trivernis.chunkmaster.lib.Subcommand +import org.bukkit.command.CommandSender + +class CmdList(private val chunkmaster: Chunkmaster): Subcommand { + override val name = "list" + + override fun execute(sender: CommandSender, args: List): Boolean { + val runningTasks = chunkmaster.generationManager.tasks + if (runningTasks.isEmpty()) { + sender.spigot().sendMessage(*ComponentBuilder("There are no running generation tasks.") + .color(ChatColor.BLUE).create()) + } else { + val response = ComponentBuilder("Currently running generation tasks:").color(ChatColor.WHITE) + for (task in runningTasks) { + response.append("\n - #${task.id}: ${task.generationTask.world.name}, Progress ${task.generationTask.count}") + .color(ChatColor.BLUE) + } + sender.spigot().sendMessage(*response.create()) + } + return true + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdPause.kt b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdPause.kt new file mode 100644 index 0000000..b125f68 --- /dev/null +++ b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdPause.kt @@ -0,0 +1,27 @@ +package net.trivernis.chunkmaster.commands + +import net.md_5.bungee.api.ChatColor +import net.md_5.bungee.api.chat.ComponentBuilder +import net.trivernis.chunkmaster.Chunkmaster +import net.trivernis.chunkmaster.lib.Subcommand +import org.bukkit.command.CommandSender + +class CmdPause(private val chunkmaster: Chunkmaster) : Subcommand { + override val name: String = "pause" + + override fun execute(sender: CommandSender, args: List): Boolean { + return if (!chunkmaster.generationManager.paused) { + chunkmaster.generationManager.pauseAll() + sender.spigot().sendMessage( + *ComponentBuilder("Paused all generation tasks.") + .color(ChatColor.BLUE).create() + ) + true + } else { + sender.spigot().sendMessage( + *ComponentBuilder("The generation process is already paused.").color(ChatColor.RED).create() + ) + false + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdResume.kt b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdResume.kt new file mode 100644 index 0000000..72b3068 --- /dev/null +++ b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdResume.kt @@ -0,0 +1,24 @@ +package net.trivernis.chunkmaster.commands + +import net.md_5.bungee.api.ChatColor +import net.md_5.bungee.api.chat.ComponentBuilder +import net.trivernis.chunkmaster.Chunkmaster +import net.trivernis.chunkmaster.lib.Subcommand +import org.bukkit.command.CommandSender + +class CmdResume(private val chunkmaster: Chunkmaster): Subcommand { + override val name = "resume" + + override fun execute(sender: CommandSender, args: List): Boolean { + return if (chunkmaster.generationManager.paused) { + chunkmaster.generationManager.resumeAll() + sender.spigot().sendMessage( + *ComponentBuilder("Resumed all generation tasks.").color(ChatColor.BLUE).create()) + true + } else { + sender.spigot().sendMessage( + *ComponentBuilder("There are no paused generation tasks.").color(ChatColor.RED).create()) + false + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/trivernis/chunkmaster/commands/CommandChunkmaster.kt b/src/main/kotlin/net/trivernis/chunkmaster/commands/CommandChunkmaster.kt new file mode 100644 index 0000000..c59f0ac --- /dev/null +++ b/src/main/kotlin/net/trivernis/chunkmaster/commands/CommandChunkmaster.kt @@ -0,0 +1,76 @@ +package net.trivernis.chunkmaster.commands + +import net.md_5.bungee.api.ChatColor +import net.md_5.bungee.api.chat.ComponentBuilder +import net.trivernis.chunkmaster.Chunkmaster +import net.trivernis.chunkmaster.lib.Subcommand +import org.bukkit.Server +import org.bukkit.command.Command +import org.bukkit.command.CommandExecutor +import org.bukkit.command.CommandSender +import org.bukkit.command.TabCompleter + +class CommandChunkmaster(private val chunkmaster: Chunkmaster, private val server: Server): CommandExecutor, + TabCompleter { + private val commands = HashMap() + + init { + registerCommands() + } + + /** + * Tab complete for commands + */ + override fun onTabComplete(sender: CommandSender, command: Command, alias: String, args: Array): + MutableList { + return commands.keys.filter { it.indexOf(args[0]) == 0 }.toMutableList() + } + + /** + * /chunkmaster command to handle all commands + */ + override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array): Boolean { + if (args.isNotEmpty()) { + when (args[0]) { + "reload" -> { + chunkmaster.reloadConfig() + sender.sendMessage("Configuration file reloaded.") + } + else -> { + if (sender.hasPermission("chunkmaster.${args[0]}")) { + return if (commands.containsKey(args[0])) { + commands[args[0]]!!.execute(sender, args.slice(1 until args.size)) + } else { + sender.spigot().sendMessage(*ComponentBuilder("Subcommand ").color(ChatColor.RED) + .append(args[0]).color(ChatColor.GREEN).append(" not found").color(ChatColor.RED).create()) + false + } + } else { + sender.spigot().sendMessage(*ComponentBuilder("You do not have permission!") + .color(ChatColor.RED).create()) + } + } + } + return true + } else { + return false + } + } + + fun registerCommands() { + val cmdGenerate = CmdGenerate(chunkmaster) + commands[cmdGenerate.name] = cmdGenerate + + val cmdPause = CmdPause(chunkmaster) + commands[cmdPause.name] = cmdPause + + val cmdResume = CmdResume(chunkmaster) + commands[cmdResume.name] = cmdResume + + val cmdCancel = CmdCancel(chunkmaster) + commands[cmdCancel.name] = cmdCancel + + val cmdList = CmdList(chunkmaster) + commands[cmdList.name] = cmdList + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/trivernis/chunkmaster/commands/CommandGenerate.kt b/src/main/kotlin/net/trivernis/chunkmaster/commands/CommandGenerate.kt deleted file mode 100644 index 512572c..0000000 --- a/src/main/kotlin/net/trivernis/chunkmaster/commands/CommandGenerate.kt +++ /dev/null @@ -1,54 +0,0 @@ -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): Boolean { - if (sender is Player) { - return if (command.testPermission(sender)) { - 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) { - val world = sender.server.getWorld(args[0]) - if (world != null) { - 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") - false - } - } else { - sender.sendMessage("You need to specify a world") - false - } - } - } - -} \ No newline at end of file diff --git a/src/main/kotlin/net/trivernis/chunkmaster/commands/CommandListGenTasks.kt b/src/main/kotlin/net/trivernis/chunkmaster/commands/CommandListGenTasks.kt deleted file mode 100644 index 76c2f47..0000000 --- a/src/main/kotlin/net/trivernis/chunkmaster/commands/CommandListGenTasks.kt +++ /dev/null @@ -1,24 +0,0 @@ -package net.trivernis.chunkmaster.commands - -import net.md_5.bungee.api.ChatColor -import net.md_5.bungee.api.chat.ComponentBuilder -import net.trivernis.chunkmaster.Chunkmaster -import org.bukkit.command.Command -import org.bukkit.command.CommandExecutor -import org.bukkit.command.CommandSender - -class CommandListGenTasks(private val chunkmaster: Chunkmaster): CommandExecutor { - /** - * Responds with a list of running generation tasks. - */ - override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array): Boolean { - val runningTasks = chunkmaster.generationManager.tasks - val response = ComponentBuilder("Currently running generation tasks").color(ChatColor.BLUE) - for (task in runningTasks) { - response.append("\n - #${task.id}: ${task.generationTask.world.name}, Progress ${task.generationTask.count}") - } - response.color(ChatColor.GREEN) - sender.spigot().sendMessage(*response.create()) - return true - } -} \ No newline at end of file diff --git a/src/main/kotlin/net/trivernis/chunkmaster/commands/CommandRemoveGenTask.kt b/src/main/kotlin/net/trivernis/chunkmaster/commands/CommandRemoveGenTask.kt deleted file mode 100644 index 90672b8..0000000 --- a/src/main/kotlin/net/trivernis/chunkmaster/commands/CommandRemoveGenTask.kt +++ /dev/null @@ -1,22 +0,0 @@ -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 - -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): Boolean { - return if (command.testPermission(sender) && args.size == 1) { - chunkmaster.generationManager.removeTask(args[0].toInt()) - sender.sendMessage("Task ${args[1].toInt()} canceled.") - true - } else { - sender.sendMessage("Invalid argument.") - false - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/net/trivernis/chunkmaster/lib/GenerationManager.kt b/src/main/kotlin/net/trivernis/chunkmaster/lib/GenerationManager.kt index 67f7352..4516deb 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/lib/GenerationManager.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/lib/GenerationManager.kt @@ -12,14 +12,15 @@ import java.lang.NullPointerException class GenerationManager(private val chunkmaster: Chunkmaster, private val server: Server) { val tasks: HashSet = HashSet() + var paused = false + private set /** * Adds a generation task */ 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 generationTask = GenerationTask(chunkmaster, world, centerChunk, centerChunk, stopAfter) val insertStatement = chunkmaster.sqliteConnection.prepareStatement(""" INSERT INTO generation_tasks (center_x, center_z, last_x, last_z, world, stop_after) values (?, ?, ?, ?, ?, ?) @@ -31,6 +32,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server 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 """.trimIndent()) @@ -38,9 +40,16 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server val result = getIdStatement.resultSet result.next() val id: Int = result.getInt("id") - tasks.add(TaskEntry(id, task, generationTask)) + insertStatement.close() getIdStatement.close() + + if (!paused) { + val task = server.scheduler.runTaskTimer(chunkmaster, generationTask, 10, + chunkmaster.config.getLong("generation.period")) + tasks.add(TaskEntry(id, task, generationTask)) + } + return id } @@ -48,30 +57,35 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server * Resumes a generation task */ 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, stopAfter) - val task = server.scheduler.runTaskTimer(chunkmaster, generationTask, 10, 2) - tasks.add(TaskEntry(id, task, generationTask)) + if (!paused) { + chunkmaster.logger.info("Resuming chunk generation task for world \"${world.name}\"") + val generationTask = GenerationTask(chunkmaster, world, center, last, stopAfter) + val task = server.scheduler.runTaskTimer(chunkmaster, generationTask, 10, + chunkmaster.config.getLong("generation.period")) + tasks.add(TaskEntry(id, task, generationTask)) + } } /** * Stops a running generation task. */ - fun removeTask(id: Int) { + fun removeTask(id: Int): Boolean { val taskEntry = this.tasks.find {it.id == id} if (taskEntry != null) { taskEntry.generationTask.cancel() taskEntry.task.cancel() + val deleteTask = chunkmaster.sqliteConnection.prepareStatement(""" + DELETE FROM generation_tasks WHERE id = ?; + """.trimIndent()) + deleteTask.setInt(1, taskEntry.id) + deleteTask.execute() + deleteTask.close() if (taskEntry.task.isCancelled) { tasks.remove(taskEntry) } - val setAutostart = chunkmaster.sqliteConnection.prepareStatement(""" - DELETE FROM generation_tasks WHERE id = ? - """.trimIndent()) - setAutostart.setInt(1, id) - setAutostart.execute() - setAutostart.close() + return true } + return false } /** @@ -80,6 +94,9 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server */ fun init() { chunkmaster.logger.info("Creating task to load chunk generation Tasks later...") + server.scheduler.runTaskTimer(chunkmaster, Runnable { + saveProgress() // save progress every 30 seconds + }, 600, 600) server.scheduler.runTaskLater(chunkmaster, Runnable { if (server.onlinePlayers.isEmpty()) { startAll() // run startAll after 10 seconds if empty @@ -92,14 +109,16 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server */ fun stopAll() { saveProgress() + val removalSet = HashSet() for (task in tasks) { task.generationTask.cancel() task.task.cancel() if (task.task.isCancelled) { - tasks.remove(task) + removalSet.add(task) } chunkmaster.logger.info("Canceled task #${task.id}") } + tasks.removeAll(removalSet) } /** @@ -127,12 +146,25 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server } } savedTasksStatement.close() - server.scheduler.runTaskTimer(chunkmaster, Runnable { - saveProgress() // save progress every 30 seconds - }, 600, 600) chunkmaster.logger.info("${tasks.size} saved tasks loaded.") } + /** + * Pauses all tasks + */ + fun pauseAll() { + paused = true + stopAll() + } + + /** + * Resumes all tasks + */ + fun resumeAll() { + paused = false + startAll() + } + /** * Saves the task progress */ diff --git a/src/main/kotlin/net/trivernis/chunkmaster/lib/GenerationTask.kt b/src/main/kotlin/net/trivernis/chunkmaster/lib/GenerationTask.kt index a923bbd..0c62ef7 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/lib/GenerationTask.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/lib/GenerationTask.kt @@ -8,9 +8,11 @@ import org.bukkit.scheduler.BukkitTask class GenerationTask(private val plugin: Chunkmaster, val world: World, private val centerChunk: Chunk, private val startChunk: Chunk, - val stopAfter: Int = -1): Runnable { + private val stopAfter: Int = -1): Runnable { private val spiral: Spiral = Spiral(Pair(centerChunk.x, centerChunk.z), Pair(startChunk.x, startChunk.z)) private val loadedChunks: HashSet = HashSet() + private val chunkSkips = plugin.config.getInt("generation.chunks-skips-per-step") + private val msptThreshold = plugin.config.getLong("generation.mspt-pause-threshold") var count = 0 private set @@ -25,7 +27,7 @@ class GenerationTask(private val plugin: Chunkmaster, val world: World, * After 10 chunks have been generated, they will all be unloaded and saved. */ override fun run() { - if (plugin.mspt < 500L) { // pause when tps < 2 + if (plugin.mspt < msptThreshold) { // pause when tps < 2 if (loadedChunks.size > 10) { for (chunk in loadedChunks) { if (chunk.isLoaded) { @@ -33,13 +35,21 @@ class GenerationTask(private val plugin: Chunkmaster, val world: World, } } } else { - 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)) { + if (!world.worldBorder.isInside(lastChunk.getBlock(8, 0, 8).location) || (stopAfter in 1..count)) { endReached = true return } + var nextChunkCoords = spiral.next() + var chunk = world.getChunkAt(nextChunkCoords.first, nextChunkCoords.second) + + for (i in 1 until chunkSkips) { + if (world.isChunkGenerated(chunk.x, chunk.z)) { + nextChunkCoords = spiral.next() // if the chunk is generated skip 10 chunks per step + chunk = world.getChunkAt(nextChunkCoords.first, nextChunkCoords.second) + } else { + break + } + } if (!world.isChunkGenerated(chunk.x, chunk.z)) { chunk.load(true) diff --git a/src/main/kotlin/net/trivernis/chunkmaster/lib/Spiral.kt b/src/main/kotlin/net/trivernis/chunkmaster/lib/Spiral.kt index cf98481..de201be 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/lib/Spiral.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/lib/Spiral.kt @@ -3,8 +3,8 @@ package net.trivernis.chunkmaster.lib import kotlin.math.abs class Spiral(private val center: Pair, start: Pair) { - var currentPos = start - var direction = 0 + private var currentPos = start + private var direction = 0 var count = 0 /** diff --git a/src/main/kotlin/net/trivernis/chunkmaster/lib/Subcommand.kt b/src/main/kotlin/net/trivernis/chunkmaster/lib/Subcommand.kt new file mode 100644 index 0000000..65cb06f --- /dev/null +++ b/src/main/kotlin/net/trivernis/chunkmaster/lib/Subcommand.kt @@ -0,0 +1,8 @@ +package net.trivernis.chunkmaster.lib + +import org.bukkit.command.CommandSender + +interface Subcommand { + val name: String + fun execute(sender: CommandSender, args: List): Boolean +} \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 6b4c685..26d5d3d 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -6,31 +6,35 @@ author: Trivernis website: trivernis.net api-version: '1.14' commands: - generate: - 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] [stopAfter] - listgentasks: - description: Lists all generation tasks - permission: chunkmaster.listgentasks - usage: /listgentasks - removegentask: - description: Removes the generation task with the specified id - permission: chunkmaster.removegentask - usage: /removegentask {task-id} + chunkmaster: + description: Main command + permission: chunkmaster.chunkmaster + usage: /chunkmaster + aliases: + - chm + - chunkm + - cmaster permissions: cunkmaster.generate: - description: Allows generate command. + description: Allows the generate subcommand. default: op - chunkmaster.listgentasks: - description: Allows the listgentask command. + chunkmaster.list: + description: Allows the list subcommand. default: op - chunkmaster.removegentask: - description: Allows the removegentask command. + chunkmaster.cancel: + description: Allows the remove subcommand. + default: op + chunkmaster.pause: + description: Allows the pause subcommand. + default: op + chunkmaster.resume: + description: Allows the resume subcommand. + default: op + chunkmaster.reload: + description: Allows the reload subcommand. + default: op + chunkmaster.chunkmaster: + description: Allows Chunkmaster commands. default: op chunkmaster.*: description: Wildcard permission @@ -38,4 +42,7 @@ permissions: children: - chunkmaster.generate - chunkmaster.listgentasks - - chunkmaster.removegentask \ No newline at end of file + - chunkmaster.removegentask + - chunkmaster.pausegentasks + - chunkmaster.resumegentasks + - chunkmaster.chunkmaster \ No newline at end of file