Merge pull request #7 from Trivernis/release/0.12-beta

Release/0.12 beta
pull/8/head
Trivernis 5 years ago committed by GitHub
commit b5d84186f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -4,18 +4,19 @@ This plugin can be used to pre-generate the region of a world around the spawn c
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.
is lower than 2 (configurable).
## 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 generate [world] [chunk count] [unit]` Starts the generation until the specified chunk count or the world border is reached.
- `/chunkmaster list` Lists all running generation tasks
- `/chunkmaster cancel <Task id>` 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.
- `/chunkmaster tpchunk <X> <Z>` Teleports you to the specified chunk coordinates.
## Config
@ -39,13 +40,18 @@ generation:
# The value should be a positive integer.
period: 2
# The max amount of chunks that should be generated per step.
# Higher values mean higher generation speed but higher performance impact.
# The value should be a positive integer.
chunks-per-step: 4
# Paper Only
# 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
chunk-skips-per-step: 100
# The maximum milliseconds per tick the server is allowed to have
# during the cunk generation process.

@ -22,7 +22,7 @@ idea {
}
group "net.trivernis"
version "0.11-beta"
version "0.12-beta"
sourceCompatibility = 1.8

@ -32,8 +32,8 @@ class Chunkmaster: JavaPlugin() {
generationManager = GenerationManager(this, server)
generationManager.init()
getCommand("chunkmaster")?.setExecutor(CommandChunkmaster(this, server))
getCommand("chunkmaster")?.aliases = mutableListOf("chm", "chunkm", "cmaster")
getCommand("chunkmaster")?.setExecutor(CommandChunkmaster(this, server))
server.pluginManager.registerEvents(ChunkmasterEvents(this, server), this)
@ -60,7 +60,8 @@ class Chunkmaster: JavaPlugin() {
private fun configure() {
dataFolder.mkdir()
config.addDefault("generation.period", 2L)
config.addDefault("generation.chunks-skips-per-step", 10)
config.addDefault("generation.chunks-per-step", 2)
config.addDefault("generation.chunk-skips-per-step", 100)
config.addDefault("generation.mspt-pause-threshold", 500L)
config.addDefault("generation.pause-on-join", true)
config.addDefault("generation.max-pending-chunks", 10)

@ -1,10 +1,12 @@
package net.trivernis.chunkmaster
import net.trivernis.chunkmaster.lib.generation.GenerationTaskPaper
import org.bukkit.Server
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.event.player.PlayerQuitEvent
import org.bukkit.event.world.WorldSaveEvent
class ChunkmasterEvents(private val chunkmaster: Chunkmaster, private val server: Server) : Listener {
@ -21,8 +23,10 @@ class ChunkmasterEvents(private val chunkmaster: Chunkmaster, private val server
server.onlinePlayers.isEmpty()
) {
if (!playerPaused) {
if (chunkmaster.generationManager.pausedTasks.isNotEmpty()) {
chunkmaster.logger.info("Server is empty. Resuming chunk generation tasks.")
}
chunkmaster.generationManager.resumeAll()
chunkmaster.logger.info("Server is empty. Resuming chunk generation tasks.")
} else if (chunkmaster.generationManager.paused){
chunkmaster.logger.info("Generation was manually paused. Not resuming automatically.")
playerPaused = chunkmaster.generationManager.paused
@ -40,9 +44,24 @@ class ChunkmasterEvents(private val chunkmaster: Chunkmaster, private val server
fun onPlayerJoin(event: PlayerJoinEvent) {
if (pauseOnJoin) {
if (server.onlinePlayers.size == 1 || server.onlinePlayers.isEmpty()) {
if (chunkmaster.generationManager.tasks.isNotEmpty()) {
chunkmaster.logger.info("Pausing generation tasks because of player join.")
}
playerPaused = chunkmaster.generationManager.paused
chunkmaster.generationManager.pauseAll()
chunkmaster.logger.info("Pausing generation tasks because of player join.")
}
}
}
/**
* Unload all chunks before a save.
*/
@EventHandler
fun onWorldSave(event: WorldSaveEvent) {
val task = chunkmaster.generationManager.tasks.find { it.generationTask.world == event.world }
if (task != null) {
if (task.generationTask is GenerationTaskPaper) {
task.generationTask.unloadAllChunks()
}
}
}

@ -7,6 +7,7 @@ import net.trivernis.chunkmaster.lib.Subcommand
import org.bukkit.command.Command
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
import kotlin.math.pow
class CmdGenerate(private val chunkmaster: Chunkmaster): Subcommand {
override val name = "generate"
@ -23,9 +24,19 @@ class CmdGenerate(private val chunkmaster: Chunkmaster): Subcommand {
if (args.size == 1) {
return sender.server.worlds.filter { it.name.indexOf(args[0]) == 0 }
.map {it.name}.toMutableList()
} else if (args.size == 2) {
if (args[0].toIntOrNull() != null) {
return units.filter {it.indexOf(args[1]) == 0}.toMutableList()
}
} else if (args.size > 2) {
if (args[1].toIntOrNull() != null) {
return units.filter {it.indexOf(args[2]) == 0}.toMutableList()
}
}
return emptyList<String>().toMutableList()
}
val units = listOf("blockradius", "radius", "diameter")
/**
* Creates a new generation task for the world and chunk count.
@ -37,11 +48,21 @@ class CmdGenerate(private val chunkmaster: Chunkmaster): Subcommand {
if (args.isNotEmpty()) {
if (args[0].toIntOrNull() != null) {
stopAfter = args[0].toInt()
worldName = sender.world.name
} else {
worldName = args[0]
}
if (args.size > 1 && args[1].toIntOrNull() != null) {
stopAfter = args[1].toInt()
if (args.size > 1) {
if (args[1].toIntOrNull() != null) {
stopAfter = args[1].toInt()
} else if (args[1] in units && args[0].toIntOrNull() != null) {
stopAfter = getStopAfter(stopAfter, args[1])
} else {
worldName = args[1]
}
}
if (args.size > 2 && args[2] in units && args[1].toIntOrNull() != null) {
stopAfter = getStopAfter(stopAfter, args[2])
}
} else {
worldName = sender.world.name
@ -49,8 +70,13 @@ class CmdGenerate(private val chunkmaster: Chunkmaster): Subcommand {
} else {
if (args.isNotEmpty()) {
worldName = args[0]
if (args.size > 1 && args[1].toIntOrNull() != null) {
stopAfter = args[1].toInt()
if (args.size > 1) {
if (args[1].toIntOrNull() != null) {
stopAfter = args[1].toInt()
}
}
if (args.size > 2 && args[2] in units) {
stopAfter = getStopAfter(stopAfter, args[2])
}
} else {
sender.spigot().sendMessage(
@ -58,6 +84,34 @@ class CmdGenerate(private val chunkmaster: Chunkmaster): Subcommand {
return false
}
}
return createTask(sender, worldName, stopAfter)
}
/**
* Returns stopAfter for a given unit
*/
private fun getStopAfter(number: Int, unit: String): Int {
if (unit in units) {
return when (unit) {
"radius" -> {
((number * 2)+1).toDouble().pow(2.0).toInt()
}
"diameter" -> {
number.toDouble().pow(2.0).toInt()
}
"blockradius" -> {
((number.toDouble()+1)/8).pow(2.0).toInt()
}
else -> number
}
}
return number
}
/**
* Creates the task with the given arguments.
*/
private fun createTask(sender: CommandSender, worldName: String, stopAfter: Int): Boolean {
val world = chunkmaster.server.getWorld(worldName)
val allTasks = chunkmaster.generationManager.allTasks
return if (world != null && (allTasks.find { it.generationTask.world == world }) == null) {

@ -0,0 +1,34 @@
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.Command
import org.bukkit.command.CommandSender
class CmdReload(private val chunkmaster: Chunkmaster): Subcommand {
override val name = "reload"
override fun onTabComplete(
sender: CommandSender,
command: Command,
alias: String,
args: List<String>
): MutableList<String> {
return emptyList<String>().toMutableList()
}
/**
* Reload command to reload the config and restart the tasks.
*/
override fun execute(sender: CommandSender, args: List<String>): Boolean {
sender.spigot().sendMessage(*ComponentBuilder("Reloading config and restarting tasks...")
.color(ChatColor.YELLOW).create())
chunkmaster.generationManager.stopAll()
chunkmaster.reloadConfig()
chunkmaster.generationManager.startAll()
sender.spigot().sendMessage(*ComponentBuilder("Config reload complete!").color(ChatColor.GREEN).create())
return true
}
}

@ -0,0 +1,52 @@
package net.trivernis.chunkmaster.commands
import io.papermc.lib.PaperLib
import net.md_5.bungee.api.ChatColor
import net.md_5.bungee.api.chat.ComponentBuilder
import net.trivernis.chunkmaster.lib.Subcommand
import org.bukkit.Material
import org.bukkit.command.Command
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
class CmdTpChunk: Subcommand {
override val name = "tpchunk"
override fun onTabComplete(
sender: CommandSender,
command: Command,
alias: String,
args: List<String>
): MutableList<String> {
return emptyList<String>().toMutableList()
}
/**
* Teleports the player to a save location in the chunk
*/
override fun execute(sender: CommandSender, args: List<String>): Boolean {
if (sender is Player) {
if (args.size == 2 && args[0].toIntOrNull() != null && args[1].toIntOrNull() != null) {
val location = sender.world.getChunkAt(args[0].toInt(), args[1].toInt()).getBlock(8, 60, 8).location
while (location.block.blockData.material != Material.AIR) {
location.y++
}
if (PaperLib.isPaper()) {
PaperLib.teleportAsync(sender, location)
} else {
sender.teleport(location)
}
sender.spigot().sendMessage(*ComponentBuilder("You have been teleportet to chunk")
.color(ChatColor.YELLOW).append("${args[0]}, ${args[1]}").color(ChatColor.BLUE).create())
return true
} else {
return false
}
} else {
sender.spigot().sendMessage(*ComponentBuilder("This command can only be executed by a player!")
.color(ChatColor.RED).create())
return false
}
}
}

@ -10,7 +10,7 @@ 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,
class CommandChunkmaster(private val chunkmaster: Chunkmaster, private val server: Server) : CommandExecutor,
TabCompleter {
private val commands = HashMap<String, Subcommand>()
@ -25,7 +25,7 @@ class CommandChunkmaster(private val chunkmaster: Chunkmaster, private val serve
MutableList<String> {
if (args.size == 1) {
return commands.keys.filter { it.indexOf(args[0]) == 0 }.toMutableList()
} else if (args.isNotEmpty()){
} else if (args.isNotEmpty()) {
if (commands.containsKey(args[0])) {
val commandEntry = commands[args[0]]
@ -40,25 +40,21 @@ class CommandChunkmaster(private val chunkmaster: Chunkmaster, private val serve
*/
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): 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())
}
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 {
@ -66,7 +62,10 @@ class CommandChunkmaster(private val chunkmaster: Chunkmaster, private val serve
}
}
fun registerCommands() {
/**
* Registers all subcommands.
*/
private fun registerCommands() {
val cmdGenerate = CmdGenerate(chunkmaster)
commands[cmdGenerate.name] = cmdGenerate
@ -81,5 +80,11 @@ class CommandChunkmaster(private val chunkmaster: Chunkmaster, private val serve
val cmdList = CmdList(chunkmaster)
commands[cmdList.name] = cmdList
val cmdReload = CmdReload(chunkmaster)
commands[cmdReload.name] = cmdReload
val cmdTpChunk = CmdTpChunk()
commands[cmdTpChunk.name] = cmdTpChunk
}
}

@ -15,8 +15,7 @@ class SqlUpdateManager(private val connnection: Connection, private val chunkmas
Pair("last_x", "integer NOT NULL DEFAULT 0"),
Pair("last_z", "integer NOT NULL DEFAULT 0"),
Pair("world", "text UNIQUE NOT NULL DEFAULT 'world'"),
Pair("stop_after", "integer DEFAULT -1"),
Pair("autostart", "integer DEFAULT 1")
Pair("stop_after", "integer DEFAULT -1")
)
)
)

@ -28,13 +28,15 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
fun addTask(world: World, stopAfter: Int = -1): Int {
val foundTask = allTasks.find { it.generationTask.world == world }
if (foundTask == null) {
val centerChunk = world.getChunkAt(world.spawnLocation)
val centerChunk = ChunkCoordinates(world.spawnLocation.chunk.x, world.spawnLocation.chunk.z)
val generationTask = createGenerationTask(world, centerChunk, centerChunk, stopAfter)
val insertStatement = chunkmaster.sqliteConnection.prepareStatement("""
val insertStatement = chunkmaster.sqliteConnection.prepareStatement(
"""
INSERT INTO generation_tasks (center_x, center_z, last_x, last_z, world, stop_after)
values (?, ?, ?, ?, ?, ?)
""")
"""
)
insertStatement.setInt(1, centerChunk.x)
insertStatement.setInt(2, centerChunk.z)
insertStatement.setInt(3, centerChunk.x)
@ -43,9 +45,11 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
insertStatement.setInt(6, stopAfter)
insertStatement.execute()
val getIdStatement = chunkmaster.sqliteConnection.prepareStatement("""
val getIdStatement = chunkmaster.sqliteConnection.prepareStatement(
"""
SELECT id FROM generation_tasks ORDER BY id DESC LIMIT 1
""".trimIndent())
""".trimIndent()
)
getIdStatement.execute()
val result = getIdStatement.resultSet
result.next()
@ -55,13 +59,15 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
getIdStatement.close()
generationTask.onEndReached {
server.consoleSender.sendMessage("Task #${id} finished after ${generationTask.count} chunks.")
chunkmaster.logger.info("Task #${id} finished after ${generationTask.count} chunks.")
removeTask(id)
}
if (!paused) {
val task = server.scheduler.runTaskTimer(chunkmaster, generationTask, 10,
chunkmaster.config.getLong("generation.period"))
val task = server.scheduler.runTaskTimer(
chunkmaster, generationTask, 200, // 10 sec delay
chunkmaster.config.getLong("generation.period")
)
tasks.add(RunningTaskEntry(id, task, generationTask))
} else {
pausedTasks.add(PausedTaskEntry(id, generationTask))
@ -76,15 +82,23 @@ 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) {
private fun resumeTask(
world: World,
center: ChunkCoordinates,
last: ChunkCoordinates,
id: Int,
stopAfter: Int = -1
) {
if (!paused) {
chunkmaster.logger.info("Resuming chunk generation task for world \"${world.name}\"")
val generationTask = createGenerationTask(world, center, last, stopAfter)
val task = server.scheduler.runTaskTimer(chunkmaster, generationTask, 10,
chunkmaster.config.getLong("generation.period"))
val task = server.scheduler.runTaskTimer(
chunkmaster, generationTask, 200, // 10 sec delay
chunkmaster.config.getLong("generation.period")
)
tasks.add(RunningTaskEntry(id, task, generationTask))
generationTask.onEndReached {
server.consoleSender.sendMessage("Task #${id} finished after ${generationTask.count} chunks.")
chunkmaster.logger.info("Task #${id} finished after ${generationTask.count} chunks.")
removeTask(id)
}
}
@ -95,15 +109,17 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
*/
fun removeTask(id: Int): Boolean {
val taskEntry: TaskEntry? = if (this.paused) {
this.pausedTasks.find {it.id == id}
this.pausedTasks.find { it.id == id }
} else {
this.tasks.find {it.id == id}
this.tasks.find { it.id == id }
}
if (taskEntry != null) {
taskEntry.cancel()
val deleteTask = chunkmaster.sqliteConnection.prepareStatement("""
val deleteTask = chunkmaster.sqliteConnection.prepareStatement(
"""
DELETE FROM generation_tasks WHERE id = ?;
""".trimIndent())
""".trimIndent()
)
deleteTask.setInt(1, taskEntry.id)
deleteTask.execute()
deleteTask.close()
@ -133,7 +149,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
if (server.onlinePlayers.isEmpty()) {
startAll() // run startAll after 10 seconds if empty
}
}, 200)
}, 600)
}
/**
@ -157,28 +173,27 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
* 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 {
if (res.getBoolean("autostart")) {
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"))
val stopAfter = res.getInt("stop_after")
if (this.tasks.find {it.id == id} == null) {
resumeTask(world, center, last, id, stopAfter)
}
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) {
server.consoleSender.sendMessage("Failed to load Task ${res.getInt("id")}.")
chunkmaster.logger.severe("Failed to load Task ${res.getInt("id")}.")
}
}
savedTasksStatement.close()
chunkmaster.logger.info("${tasks.size} saved tasks loaded.")
if (tasks.isNotEmpty()) {
chunkmaster.logger.info("${tasks.size} saved tasks loaded.")
}
}
/**
@ -208,22 +223,27 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
for (task in tasks) {
try {
val genTask = task.generationTask
server.consoleSender.sendMessage("""Task #${task.id} running for "${genTask.world.name}".
chunkmaster.logger.info(
"""Task #${task.id} running for "${genTask.world.name}".
|Progress ${task.generationTask.count} chunks
|${if (task.generationTask.stopAfter > 0)"(${(task.generationTask.count.toDouble()/
task.generationTask.stopAfter.toDouble())*100}%)" else ""}.
|Last Chunk: ${genTask.lastChunk.x}, ${genTask.lastChunk.z}""".trimMargin("|").replace('\n', ' '))
val updateStatement = chunkmaster.sqliteConnection.prepareStatement("""
|${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(
"""
UPDATE generation_tasks SET last_x = ?, last_z = ?
WHERE id = ?
""".trimIndent())
""".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}")
chunkmaster.logger.warning("Exception when saving task progress ${error.message}")
}
}
}
@ -232,10 +252,15 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
* Creates a new generation task. This method is used to create a task depending
* on the server type (Paper/Spigot).
*/
private fun createGenerationTask(world: World, center: Chunk, start: Chunk, stopAfter: Int): GenerationTask {
private fun createGenerationTask(
world: World,
center: ChunkCoordinates,
start: ChunkCoordinates,
stopAfter: Int
): GenerationTask {
return if (PaperLib.isPaper()) {
GenerationTaskPaper(chunkmaster, world, center, start, stopAfter)
} else {
} else {
GenerationTaskSpigot(chunkmaster, world, center, start, stopAfter)
}
}

@ -8,7 +8,8 @@ import org.bukkit.World
/**
* Interface for generation tasks.
*/
abstract class GenerationTask(plugin: Chunkmaster, centerChunk: Chunk, startChunk: Chunk) : Runnable {
abstract class GenerationTask(plugin: Chunkmaster, centerChunk: ChunkCoordinates, startChunk: ChunkCoordinates) :
Runnable {
abstract val stopAfter: Int
abstract val world: World
@ -19,9 +20,10 @@ abstract class GenerationTask(plugin: Chunkmaster, centerChunk: Chunk, startChun
Spiral(Pair(centerChunk.x, centerChunk.z), Pair(startChunk.x, startChunk.z))
protected val loadedChunks: HashSet<Chunk> = HashSet()
protected var lastChunkCoords = ChunkCoordinates(startChunk.x, startChunk.z)
protected val chunkSkips = plugin.config.getInt("generation.chunks-skips-per-step")
protected val chunkSkips = plugin.config.getInt("generation.chunk-skips-per-step")
protected val msptThreshold = plugin.config.getLong("generation.mspt-pause-threshold")
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
private set
@ -34,11 +36,12 @@ abstract class GenerationTask(plugin: Chunkmaster, centerChunk: Chunk, startChun
val nextChunkCoords = spiral.next()
return ChunkCoordinates(nextChunkCoords.first, nextChunkCoords.second)
}
var lastChunk: Chunk = startChunk
val lastChunk: Chunk
get() {
return world.getChunkAt(lastChunkCoords.x, lastChunkCoords.z)
}
private set
val nextChunk: Chunk
get() {
val next = nextChunkCoordinates

@ -8,7 +8,7 @@ import io.papermc.lib.PaperLib
class GenerationTaskPaper(
private val plugin: Chunkmaster, override val world: World,
centerChunk: Chunk, private val startChunk: Chunk,
centerChunk: ChunkCoordinates, private val startChunk: ChunkCoordinates,
override val stopAfter: Int = -1
) : GenerationTask(plugin, centerChunk, startChunk) {
@ -52,7 +52,15 @@ class GenerationTaskPaper(
}
if (!PaperLib.isChunkGenerated(world, chunk.x, chunk.z)) {
pendingChunks.add(PaperLib.getChunkAtAsync(world, chunk.x, chunk.z, true))
for (i in 0 until minOf(chunksPerStep, (stopAfter - count) - 1)) {
if (!PaperLib.isChunkGenerated(world, chunk.x, chunk.z)) {
pendingChunks.add(PaperLib.getChunkAtAsync(world, chunk.x, chunk.z, true))
}
chunk = nextChunkCoordinates
}
if (!PaperLib.isChunkGenerated(world, chunk.x, chunk.z)) {
pendingChunks.add(PaperLib.getChunkAtAsync(world, chunk.x, chunk.z, true))
}
}
lastChunkCoords = chunk
count = spiral.count // set the count to the more accurate spiral count
@ -66,6 +74,13 @@ class GenerationTaskPaper(
* This unloads all chunks that were generated but not unloaded yet.
*/
override fun cancel() {
unloadAllChunks()
}
/**
* Cancels all pending chunks and unloads all loaded chunks.
*/
fun unloadAllChunks() {
for (pendingChunk in pendingChunks) {
if (pendingChunk.isDone) {
loadedChunks.add(pendingChunk.get())

@ -6,7 +6,7 @@ import org.bukkit.World
class GenerationTaskSpigot(
private val plugin: Chunkmaster, override val world: World,
centerChunk: Chunk, private val startChunk: Chunk,
centerChunk: ChunkCoordinates, private val startChunk: ChunkCoordinates,
override val stopAfter: Int = -1
) : GenerationTask(plugin, centerChunk, startChunk) {
@ -37,9 +37,15 @@ class GenerationTaskSpigot(
return
}
val chunk = nextChunkCoordinates
var chunk = nextChunkCoordinates
if (!world.isChunkGenerated(chunk.x, chunk.z)) {
for (i in 0 until minOf(chunksPerStep, stopAfter - count)) {
val chunkInstance = world.getChunkAt(chunk.x, chunk.z)
chunkInstance.load(true)
loadedChunks.add(chunkInstance)
chunk = nextChunkCoordinates
}
val chunkInstance = world.getChunkAt(chunk.x, chunk.z)
chunkInstance.load(true)
loadedChunks.add(chunkInstance)

@ -7,6 +7,29 @@ class RunningTaskEntry(
val task: BukkitTask,
override val generationTask: GenerationTask
) : TaskEntry {
private var lastProgress: Pair<Long, Int>? = null
/**
* Returns the generation Speed
*/
val generationSpeed: Double?
get() {
var generationSpeed: Double? = null
if (lastProgress != null) {
val chunkDiff = generationTask.count - lastProgress!!.second
val timeDiff = (System.currentTimeMillis() - lastProgress!!.first).toDouble()/1000
generationSpeed = chunkDiff.toDouble()/timeDiff
}
lastProgress = Pair(System.currentTimeMillis(), generationTask.count)
return generationSpeed
}
init {
lastProgress = Pair(System.currentTimeMillis(), generationTask.count)
}
override fun cancel() {
task.cancel()
generationTask.cancel()

@ -1,6 +1,6 @@
main: net.trivernis.chunkmaster.Chunkmaster
name: Chunkmaster
version: '0.11-beta'
version: '0.12-beta'
description: Chunk commands plugin.
author: Trivernis
website: trivernis.net
@ -9,7 +9,14 @@ commands:
chunkmaster:
description: Main command
permission: chunkmaster.chunkmaster
usage: /chunkmaster <generate|list|pause|resume|cancel|reload>
usage: |
/<command> generate [<world>, <chunk-count>] - generates chunks starting from the spawn until the chunk-count is reached
/<command> cancel <task-id> - cancels the generation task with the task-id
/<command> list - lists all running and paused generation tasks
/<command> pause - pauses all generation tasks
/<command> resume - resumes all generation tasks
/<command> reload - reloads the configuration and restarts all tasks
/<command> tpchunk <chunkX> <chunkZ> - teleports you to the chunk with the given chunk coordinates
aliases:
- chm
- chunkm
@ -33,6 +40,9 @@ permissions:
chunkmaster.reload:
description: Allows the reload subcommand.
default: op
chunkmaster.tpchunk:
description: Allows the tpchunk subcommand.
default: op
chunkmaster.chunkmaster:
description: Allows Chunkmaster commands.
default: op

Loading…
Cancel
Save