Bug fixes, config file, more commands

- fixed chunk skips skipping not-generated chunks
- changed command structure to /chunkmaster <operation>
- added config options
- updated readme
pull/4/head v0.9-beta
Trivernis 5 years ago
parent f726f3e15c
commit 52ba24390f

@ -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 <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.
## 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
# 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 <true/false>
pause-on-join: true
```

@ -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()
}
/**

@ -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.")
}
}
}
}

@ -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<String>): 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
}
}
}

@ -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<String>): 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
}
}
}

@ -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<String>): 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
}
}

@ -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<String>): 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
}
}
}

@ -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<String>): 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
}
}
}

@ -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<String, Subcommand>()
init {
registerCommands()
}
/**
* Tab complete for commands
*/
override fun onTabComplete(sender: CommandSender, command: Command, alias: String, args: Array<out String>):
MutableList<String> {
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<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())
}
}
}
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
}
}

@ -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<out String>): 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
}
}
}
}

@ -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<out String>): 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
}
}

@ -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<out String>): 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
}
}
}

@ -12,14 +12,15 @@ import java.lang.NullPointerException
class GenerationManager(private val chunkmaster: Chunkmaster, private val server: Server) {
val tasks: HashSet<TaskEntry> = 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<TaskEntry>()
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
*/

@ -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<Chunk> = 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)

@ -3,8 +3,8 @@ package net.trivernis.chunkmaster.lib
import kotlin.math.abs
class Spiral(private val center: Pair<Int, Int>, start: Pair<Int, Int>) {
var currentPos = start
var direction = 0
private var currentPos = start
private var direction = 0
var count = 0
/**

@ -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<String>): Boolean
}

@ -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 <generate|list|pause|resume|cancel|reload>
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
- chunkmaster.removegentask
- chunkmaster.pausegentasks
- chunkmaster.resumegentasks
- chunkmaster.chunkmaster
Loading…
Cancel
Save