Add ignore-worldborder config option

- [Convert to unix linebreaks]
- Add ignore-worldborder config option to generate past the vanilla world border
pull/15/head
trivernis 5 years ago
parent ce826419f1
commit f89262e538

@ -1,45 +1,45 @@
# Java Gradle CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-java/ for more details
#
version: 2
jobs:
build:
docker:
# specify the version you desire here
- image: circleci/openjdk:8-jdk
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
# - image: circleci/postgres:9.4
working_directory: ~/repo
environment:
# Customize the JVM maximum heap limit
JVM_OPTS: -Xmx3200m
TERM: dumb
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "build.gradle" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run: gradle dependencies
- save_cache:
paths:
- ~/.gradle
key: v1-dependencies-{{ checksum "build.gradle" }}
# Build jar
- run: gradle shadowJar
- store_artifacts:
path: build/libs
# Java Gradle CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-java/ for more details
#
version: 2
jobs:
build:
docker:
# specify the version you desire here
- image: circleci/openjdk:8-jdk
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
# - image: circleci/postgres:9.4
working_directory: ~/repo
environment:
# Customize the JVM maximum heap limit
JVM_OPTS: -Xmx3200m
TERM: dumb
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "build.gradle" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run: gradle dependencies
- save_cache:
paths:
- ~/.gradle
key: v1-dependencies-{{ checksum "build.gradle" }}
# Build jar
- run: gradle shadowJar
- store_artifacts:
path: build/libs
destination: build

10
.gitignore vendored

@ -1,6 +1,6 @@
gradle
.gradle
.idea
build
out
gradle
.gradle
.idea
build
out
gradlew*

1348
LICENSE

File diff suppressed because it is too large Load Diff

@ -1,75 +1,75 @@
# chunkmaster
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 (configurable).
## Commands
All features can be accessed with the command `/chunkmaster` or the aliases `/chm`, `chunkm`, `cmaster`.
- `/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
```yaml
generation:
# The maximum amount of chunks that are loaded before unloading and saving them.
# Higher values mean higher generation speed but greater memory usage.
# The value should be a positive integer.
max-loaded-chunks: 10
# Paper Only
# The maximum amount of requested chunks with the asynchronous paper chunk
# loading method. Higher values mean faster generation but more memory usage
# (and probably bigger performance impact).
# The value should be a positive integer.
max-pending-chunks: 10
# 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 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.
chunk-skips-per-step: 100
# 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
# 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
```
## Spigot and Paper
The plugin works on spigot and paper servers but is significantly faster on paper servers
(because it profits from asynchronous chunk loading an the better implementation of the
# chunkmaster
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 (configurable).
## Commands
All features can be accessed with the command `/chunkmaster` or the aliases `/chm`, `chunkm`, `cmaster`.
- `/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
```yaml
generation:
# The maximum amount of chunks that are loaded before unloading and saving them.
# Higher values mean higher generation speed but greater memory usage.
# The value should be a positive integer.
max-loaded-chunks: 10
# Paper Only
# The maximum amount of requested chunks with the asynchronous paper chunk
# loading method. Higher values mean faster generation but more memory usage
# (and probably bigger performance impact).
# The value should be a positive integer.
max-pending-chunks: 10
# 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 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.
chunk-skips-per-step: 100
# 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
# 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
```
## Spigot and Paper
The plugin works on spigot and paper servers but is significantly faster on paper servers
(because it profits from asynchronous chunk loading an the better implementation of the
isChunkGenerated method).

@ -1,79 +1,80 @@
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "com.github.jengelman.gradle.plugins:shadow:2.0.2"
}
}
plugins {
id 'idea'
id 'org.jetbrains.kotlin.jvm' version '1.3.50'
id 'com.github.johnrengelman.shadow' version '2.0.4'
}
idea {
module {
downloadJavadoc = true
downloadSources = true
}
}
group "net.trivernis"
version "0.13-beta"
sourceCompatibility = 1.8
repositories {
mavenCentral()
maven {
url "https://hub.spigotmc.org/nexus/content/repositories/snapshots"
}
maven {
url 'https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc'
}
maven {
name 'papermc'
url 'https://papermc.io/repo/repository/maven-public/'
}
maven {
name 'CodeMc'
url 'https://repo.codemc.org/repository/maven-public'
}
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
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'
compile "io.papermc:paperlib:1.0.2"
compile "org.bstats:bstats-bukkit:1.5"
}
apply plugin: "com.github.johnrengelman.shadow"
apply plugin: 'java'
shadowJar {
relocate 'io.papermc.lib', 'net.trivernis.chunkmaster.paperlib'
relocate 'org.bstats', 'net.trivernis.chunkmaster.bstats'
}
jar {
from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "com.github.jengelman.gradle.plugins:shadow:2.0.2"
}
}
plugins {
id 'idea'
id 'org.jetbrains.kotlin.jvm' version '1.3.50'
id 'com.github.johnrengelman.shadow' version '2.0.4'
}
idea {
module {
downloadJavadoc = true
downloadSources = true
}
}
group "net.trivernis"
version "0.13-beta"
sourceCompatibility = 1.8
repositories {
mavenCentral()
maven {
url "https://hub.spigotmc.org/nexus/content/repositories/snapshots"
}
maven {
url 'https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc'
}
maven {
name 'papermc'
url 'https://papermc.io/repo/repository/maven-public/'
}
maven {
name 'CodeMc'
url 'https://repo.codemc.org/repository/maven-public'
}
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
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"
compileOnly "com.destroystokyo.paper:paper-api:1.14.4-R0.1-SNAPSHOT"
compile group: 'org.xerial', name: 'sqlite-jdbc', version: '3.28.0'
compile "io.papermc:paperlib:1.0.2"
compile "org.bstats:bstats-bukkit:1.5"
}
apply plugin: "com.github.johnrengelman.shadow"
apply plugin: 'java'
shadowJar {
relocate 'io.papermc.lib', 'net.trivernis.chunkmaster.paperlib'
relocate 'org.bstats', 'net.trivernis.chunkmaster.bstats'
}
jar {
from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}

@ -1,2 +1,2 @@
rootProject.name = 'chunkmaster'
rootProject.name = 'chunkmaster'

@ -1,85 +1,90 @@
package net.trivernis.chunkmaster
import io.papermc.lib.PaperLib
import net.trivernis.chunkmaster.commands.*
import net.trivernis.chunkmaster.lib.generation.GenerationManager
import net.trivernis.chunkmaster.lib.SqliteManager
import org.bstats.bukkit.Metrics
import org.bukkit.plugin.java.JavaPlugin
import org.bukkit.scheduler.BukkitTask
import java.lang.Exception
import java.sql.Connection
import java.sql.DriverManager
class Chunkmaster: JavaPlugin() {
lateinit var sqliteManager: SqliteManager
lateinit var generationManager: GenerationManager
private lateinit var tpsTask: BukkitTask
var mspt = 50L // keep track of the milliseconds per tick
private set
/**
* On enable of the plugin
*/
override fun onEnable() {
PaperLib.suggestPaper(this)
configure()
val metrics = Metrics(this)
initDatabase()
generationManager = GenerationManager(this, server)
generationManager.init()
getCommand("chunkmaster")?.aliases = mutableListOf("chm", "chunkm", "cmaster")
getCommand("chunkmaster")?.setExecutor(CommandChunkmaster(this, server))
server.pluginManager.registerEvents(ChunkmasterEvents(this, server), this)
tpsTask = server.scheduler.runTaskTimer(this, Runnable {
val start = System.currentTimeMillis()
server.scheduler.runTaskLater(this, Runnable {
mspt = System.currentTimeMillis() - start
}, 1)
}, 1, 300)
}
/**
* Stop all tasks and close database connection on disable
*/
override fun onDisable() {
logger.info("Stopping all generation tasks...")
generationManager.stopAll()
}
/**
* Cofigure the config file
*/
private fun configure() {
dataFolder.mkdir()
config.addDefault("generation.period", 2L)
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)
config.addDefault("generation.max-loaded-chunks", 10)
config.addDefault("database.filename", "chunkmaster.db")
config.options().copyDefaults(true)
saveConfig()
}
/**
* Initializes the database
*/
private fun initDatabase() {
logger.info("Initializing Database...")
try {
this.sqliteManager = SqliteManager( this)
sqliteManager.init()
logger.info("Database fully initialized.")
} catch(e: Exception) {
logger.warning("Failed to init database: ${e.message}")
}
}
package net.trivernis.chunkmaster
import io.papermc.lib.PaperLib
import net.trivernis.chunkmaster.commands.*
import net.trivernis.chunkmaster.lib.generation.GenerationManager
import net.trivernis.chunkmaster.lib.SqliteManager
import org.bstats.bukkit.Metrics
import org.bukkit.plugin.java.JavaPlugin
import org.bukkit.scheduler.BukkitTask
import java.lang.Exception
class Chunkmaster: JavaPlugin() {
lateinit var sqliteManager: SqliteManager
lateinit var generationManager: GenerationManager
private lateinit var tpsTask: BukkitTask
var mspt = 20 // keep track of the milliseconds per tick
private set
/**
* On enable of the plugin
*/
override fun onEnable() {
PaperLib.suggestPaper(this)
configure()
val metrics = Metrics(this)
initDatabase()
generationManager = GenerationManager(this, server)
generationManager.init()
getCommand("chunkmaster")?.aliases = mutableListOf("chm", "chunkm", "cmaster")
getCommand("chunkmaster")?.setExecutor(CommandChunkmaster(this, server))
server.pluginManager.registerEvents(ChunkmasterEvents(this, server), this)
if (PaperLib.isPaper() && PaperLib.getMinecraftPatchVersion() >= 225) {
tpsTask = server.scheduler.runTaskTimer(this, Runnable {
mspt = 1000/server.currentTick // use papers exposed tick rather than calculating it
}, 1, 300)
} else {
tpsTask = server.scheduler.runTaskTimer(this, Runnable {
val start = System.currentTimeMillis()
server.scheduler.runTaskLater(this, Runnable {
mspt = (System.currentTimeMillis() - start).toInt()
}, 1)
}, 1, 300)
}
}
/**
* Stop all tasks and close database connection on disable
*/
override fun onDisable() {
logger.info("Stopping all generation tasks...")
generationManager.stopAll()
}
/**
* Cofigure the config file
*/
private fun configure() {
dataFolder.mkdir()
config.addDefault("generation.period", 2L)
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)
config.addDefault("generation.max-loaded-chunks", 10)
config.addDefault("generation.ignore-worldborder", false)
config.addDefault("database.filename", "chunkmaster.db")
config.options().copyDefaults(true)
saveConfig()
}
/**
* Initializes the database
*/
private fun initDatabase() {
logger.info("Initializing Database...")
try {
this.sqliteManager = SqliteManager( this)
sqliteManager.init()
logger.info("Database fully initialized.")
} catch(e: Exception) {
logger.warning("Failed to init database: ${e.message}")
}
}
}

@ -1,55 +1,55 @@
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 {
private val pauseOnJoin = chunkmaster.config.getBoolean("generation.pause-on-join")
private var playerPaused = false
/**
* Autostart generation tasks
*/
@EventHandler
fun onPlayerQuit(event: PlayerQuitEvent) {
if (pauseOnJoin) {
if (server.onlinePlayers.size == 1 && server.onlinePlayers.contains(event.player) ||
server.onlinePlayers.isEmpty()
) {
if (!playerPaused) {
if (chunkmaster.generationManager.pausedTasks.isNotEmpty()) {
chunkmaster.logger.info("Server is empty. Resuming chunk generation tasks.")
}
chunkmaster.generationManager.resumeAll()
} else if (chunkmaster.generationManager.paused){
chunkmaster.logger.info("Generation was manually paused. Not resuming automatically.")
playerPaused = chunkmaster.generationManager.paused
} else {
chunkmaster.logger.info("Generation tasks are already running.")
}
}
}
}
/**
* Autostop generation tasks
*/
@EventHandler
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()
}
}
}
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 {
private val pauseOnJoin = chunkmaster.config.getBoolean("generation.pause-on-join")
private var playerPaused = false
/**
* Autostart generation tasks
*/
@EventHandler
fun onPlayerQuit(event: PlayerQuitEvent) {
if (pauseOnJoin) {
if (server.onlinePlayers.size == 1 && server.onlinePlayers.contains(event.player) ||
server.onlinePlayers.isEmpty()
) {
if (!playerPaused) {
if (chunkmaster.generationManager.pausedTasks.isNotEmpty()) {
chunkmaster.logger.info("Server is empty. Resuming chunk generation tasks.")
}
chunkmaster.generationManager.resumeAll()
} else if (chunkmaster.generationManager.paused){
chunkmaster.logger.info("Generation was manually paused. Not resuming automatically.")
playerPaused = chunkmaster.generationManager.paused
} else {
chunkmaster.logger.info("Generation tasks are already running.")
}
}
}
}
/**
* Autostop generation tasks
*/
@EventHandler
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()
}
}
}
}

@ -1,48 +1,48 @@
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 net.trivernis.chunkmaster.lib.generation.TaskEntry
import org.bukkit.command.Command
import org.bukkit.command.CommandSender
class CmdCancel(private val chunkmaster: Chunkmaster): Subcommand {
override val name = "cancel"
/**
* TabComplete for subcommand cancel.
*/
override fun onTabComplete(
sender: CommandSender,
command: Command,
alias: String,
args: List<String>
): MutableList<String> {
val genManager = chunkmaster.generationManager
val allTasks = genManager.allTasks
return allTasks.filter {it.id.toString().indexOf(args[0]) == 0}
.map { it.id.toString() }.toMutableList()
}
/**
* Cancels the generation task if it exists.
*/
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
}
}
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 net.trivernis.chunkmaster.lib.generation.TaskEntry
import org.bukkit.command.Command
import org.bukkit.command.CommandSender
class CmdCancel(private val chunkmaster: Chunkmaster): Subcommand {
override val name = "cancel"
/**
* TabComplete for subcommand cancel.
*/
override fun onTabComplete(
sender: CommandSender,
command: Command,
alias: String,
args: List<String>
): MutableList<String> {
val genManager = chunkmaster.generationManager
val allTasks = genManager.allTasks
return allTasks.filter {it.id.toString().indexOf(args[0]) == 0}
.map { it.id.toString() }.toMutableList()
}
/**
* Cancels the generation task if it exists.
*/
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
}
}
}

@ -1,133 +1,133 @@
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
import org.bukkit.entity.Player
import kotlin.math.pow
class CmdGenerate(private val chunkmaster: Chunkmaster): Subcommand {
override val name = "generate"
/**
* TabComplete for generate command.
*/
override fun onTabComplete(
sender: CommandSender,
command: Command,
alias: String,
args: List<String>
): MutableList<String> {
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.
*/
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()
worldName = sender.world.name
} else {
worldName = args[0]
}
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
}
} else {
if (args.isNotEmpty()) {
worldName = args[0]
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(
*ComponentBuilder("You need to provide a world name").color(ChatColor.RED).create())
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) {
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 if (world == null){
sender.spigot().sendMessage(*ComponentBuilder("World ").color(ChatColor.RED)
.append(worldName).color(ChatColor.GREEN).append(" not found!").color(ChatColor.RED).create())
false
} else {
sender.spigot().sendMessage(*ComponentBuilder("Task already exists!").color(ChatColor.RED).create())
return false
}
}
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
import org.bukkit.entity.Player
import kotlin.math.pow
class CmdGenerate(private val chunkmaster: Chunkmaster): Subcommand {
override val name = "generate"
/**
* TabComplete for generate command.
*/
override fun onTabComplete(
sender: CommandSender,
command: Command,
alias: String,
args: List<String>
): MutableList<String> {
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.
*/
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()
worldName = sender.world.name
} else {
worldName = args[0]
}
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
}
} else {
if (args.isNotEmpty()) {
worldName = args[0]
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(
*ComponentBuilder("You need to provide a world name").color(ChatColor.RED).create())
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) {
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 if (world == null){
sender.spigot().sendMessage(*ComponentBuilder("World ").color(ChatColor.RED)
.append(worldName).color(ChatColor.GREEN).append(" not found!").color(ChatColor.RED).create())
false
} else {
sender.spigot().sendMessage(*ComponentBuilder("Task already exists!").color(ChatColor.RED).create())
return false
}
}
}

@ -1,63 +1,63 @@
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 CmdList(private val chunkmaster: Chunkmaster): Subcommand {
override val name = "list"
override fun onTabComplete(
sender: CommandSender,
command: Command,
alias: String,
args: List<String>
): MutableList<String> {
return emptyList<String>().toMutableList()
}
/**
* Lists all running or paused generation tasks.
*/
override fun execute(sender: CommandSender, args: List<String>): Boolean {
val runningTasks = chunkmaster.generationManager.tasks
val pausedTasks = chunkmaster.generationManager.pausedTasks
if (runningTasks.isEmpty() && pausedTasks.isEmpty()) {
sender.spigot().sendMessage(*ComponentBuilder("There are no generation tasks.")
.color(ChatColor.BLUE).create())
} else if (runningTasks.isEmpty()) {
val response = ComponentBuilder("Currently paused generation tasks:").color(ChatColor.WHITE).bold(true)
for (taskEntry in pausedTasks) {
val genTask = taskEntry.generationTask
response.append("\n - ").color(ChatColor.WHITE).bold(false)
response.append("#${taskEntry.id}").color(ChatColor.BLUE).append(" - ").color(ChatColor.WHITE)
response.append(genTask.world.name).color(ChatColor.GREEN).append(" - Progress: ").color(ChatColor.WHITE)
response.append("${genTask.count} chunks").color(ChatColor.BLUE)
if (genTask.stopAfter > 0) {
response.append(" (${(genTask.count.toDouble()/genTask.stopAfter.toDouble())*100}%).")
}
}
sender.spigot().sendMessage(*response.create())
} else {
val response = ComponentBuilder("Currently running generation tasks:").color(ChatColor.WHITE).bold(true)
for (task in runningTasks) {
val genTask = task.generationTask
response.append("\n - ").color(ChatColor.WHITE).bold(false)
.append("#${task.id}").color(ChatColor.BLUE).append(" - ").color(ChatColor.WHITE)
.append(genTask.world.name).color(ChatColor.GREEN).append(" - Progress: ").color(ChatColor.WHITE)
.append("${genTask.count} chunks").color(ChatColor.BLUE)
if (genTask.stopAfter > 0) {
response.append(" (${(genTask.count.toDouble()/genTask.stopAfter.toDouble())*100}%).")
}
}
sender.spigot().sendMessage(*response.create())
}
return true
}
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 CmdList(private val chunkmaster: Chunkmaster): Subcommand {
override val name = "list"
override fun onTabComplete(
sender: CommandSender,
command: Command,
alias: String,
args: List<String>
): MutableList<String> {
return emptyList<String>().toMutableList()
}
/**
* Lists all running or paused generation tasks.
*/
override fun execute(sender: CommandSender, args: List<String>): Boolean {
val runningTasks = chunkmaster.generationManager.tasks
val pausedTasks = chunkmaster.generationManager.pausedTasks
if (runningTasks.isEmpty() && pausedTasks.isEmpty()) {
sender.spigot().sendMessage(*ComponentBuilder("There are no generation tasks.")
.color(ChatColor.BLUE).create())
} else if (runningTasks.isEmpty()) {
val response = ComponentBuilder("Currently paused generation tasks:").color(ChatColor.WHITE).bold(true)
for (taskEntry in pausedTasks) {
val genTask = taskEntry.generationTask
response.append("\n - ").color(ChatColor.WHITE).bold(false)
response.append("#${taskEntry.id}").color(ChatColor.BLUE).append(" - ").color(ChatColor.WHITE)
response.append(genTask.world.name).color(ChatColor.GREEN).append(" - Progress: ").color(ChatColor.WHITE)
response.append("${genTask.count} chunks").color(ChatColor.BLUE)
if (genTask.stopAfter > 0) {
response.append(" (${(genTask.count.toDouble()/genTask.stopAfter.toDouble())*100}%).")
}
}
sender.spigot().sendMessage(*response.create())
} else {
val response = ComponentBuilder("Currently running generation tasks:").color(ChatColor.WHITE).bold(true)
for (task in runningTasks) {
val genTask = task.generationTask
response.append("\n - ").color(ChatColor.WHITE).bold(false)
.append("#${task.id}").color(ChatColor.BLUE).append(" - ").color(ChatColor.WHITE)
.append(genTask.world.name).color(ChatColor.GREEN).append(" - Progress: ").color(ChatColor.WHITE)
.append("${genTask.count} chunks").color(ChatColor.BLUE)
if (genTask.stopAfter > 0) {
response.append(" (${(genTask.count.toDouble()/genTask.stopAfter.toDouble())*100}%).")
}
}
sender.spigot().sendMessage(*response.create())
}
return true
}
}

@ -1,37 +1,37 @@
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 CmdPause(private val chunkmaster: Chunkmaster) : Subcommand {
override val name: String = "pause"
override fun onTabComplete(
sender: CommandSender,
command: Command,
alias: String,
args: List<String>
): MutableList<String> {
return emptyList<String>().toMutableList()
}
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
}
}
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 CmdPause(private val chunkmaster: Chunkmaster) : Subcommand {
override val name: String = "pause"
override fun onTabComplete(
sender: CommandSender,
command: Command,
alias: String,
args: List<String>
): MutableList<String> {
return emptyList<String>().toMutableList()
}
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
}
}
}

@ -1,34 +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
}
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
}
}

@ -1,34 +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 CmdResume(private val chunkmaster: Chunkmaster): Subcommand {
override val name = "resume"
override fun onTabComplete(
sender: CommandSender,
command: Command,
alias: String,
args: List<String>
): MutableList<String> {
return emptyList<String>().toMutableList()
}
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
}
}
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 CmdResume(private val chunkmaster: Chunkmaster): Subcommand {
override val name = "resume"
override fun onTabComplete(
sender: CommandSender,
command: Command,
alias: String,
args: List<String>
): MutableList<String> {
return emptyList<String>().toMutableList()
}
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
}
}
}

@ -1,52 +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
}
}
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
}
}
}

@ -1,90 +1,90 @@
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> {
if (args.size == 1) {
return commands.keys.filter { it.indexOf(args[0]) == 0 }.toMutableList()
} else if (args.isNotEmpty()) {
if (commands.containsKey(args[0])) {
val commandEntry = commands[args[0]]
return commandEntry!!.onTabComplete(sender, command, alias, args.slice(1 until args.size))
}
}
return emptyList<String>().toMutableList()
}
/**
* /chunkmaster command to handle all commands
*/
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
if (args.isNotEmpty()) {
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
}
}
/**
* Registers all subcommands.
*/
private 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
val cmdReload = CmdReload(chunkmaster)
commands[cmdReload.name] = cmdReload
val cmdTpChunk = CmdTpChunk()
commands[cmdTpChunk.name] = cmdTpChunk
}
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> {
if (args.size == 1) {
return commands.keys.filter { it.indexOf(args[0]) == 0 }.toMutableList()
} else if (args.isNotEmpty()) {
if (commands.containsKey(args[0])) {
val commandEntry = commands[args[0]]
return commandEntry!!.onTabComplete(sender, command, alias, args.slice(1 until args.size))
}
}
return emptyList<String>().toMutableList()
}
/**
* /chunkmaster command to handle all commands
*/
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
if (args.isNotEmpty()) {
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
}
}
/**
* Registers all subcommands.
*/
private 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
val cmdReload = CmdReload(chunkmaster)
commands[cmdReload.name] = cmdReload
val cmdTpChunk = CmdTpChunk()
commands[cmdTpChunk.name] = cmdTpChunk
}
}

@ -1,64 +1,64 @@
package net.trivernis.chunkmaster.lib
import kotlin.math.abs
class Spiral(private val center: Pair<Int, Int>, start: Pair<Int, Int>) {
private var currentPos = start
private var direction = 0
var count = 0
/**
* Returns the next value in the spiral
*/
fun next(): Pair<Int, Int> {
if (count == 0 && currentPos != center) {
// simulate the spiral to get the correct direction
// TODO: Improve performance of this workaround (replace it with acutal stuff)
val simSpiral = Spiral(center, center)
while (simSpiral.next() != currentPos);
direction = simSpiral.direction
count = simSpiral.count
}
if (count == 1) { // because of the center behaviour
count ++
return currentPos
}
if (currentPos == center) { // the center has to be handled exclusively
currentPos = Pair(center.first, center.second + 1)
count ++
return center
} else {
val distances = getDistances(center, currentPos)
if (abs(distances.first) == abs(distances.second)) {
direction = (direction + 1)%5
}
}
when(direction) {
0 -> {
currentPos = Pair(currentPos.first + 1, currentPos.second)
}
1 -> {
currentPos = Pair(currentPos.first, currentPos.second - 1)
}
2 -> {
currentPos = Pair(currentPos.first - 1, currentPos.second)
}
3 -> {
currentPos = Pair(currentPos.first, currentPos.second + 1)
}
4 -> {
currentPos = Pair(currentPos.first, currentPos.second + 1)
direction = 0
}
}
count ++
return currentPos
}
/**
* Returns the distances between 2 coordinates
*/
private fun getDistances(pos1: Pair<Int, Int>, pos2: Pair<Int, Int>): Pair<Int, Int> {
return Pair(pos2.first - pos1.first, pos2.second - pos1.second)
}
package net.trivernis.chunkmaster.lib
import kotlin.math.abs
class Spiral(private val center: Pair<Int, Int>, start: Pair<Int, Int>) {
private var currentPos = start
private var direction = 0
var count = 0
/**
* Returns the next value in the spiral
*/
fun next(): Pair<Int, Int> {
if (count == 0 && currentPos != center) {
// simulate the spiral to get the correct direction
// TODO: Improve performance of this workaround (replace it with acutal stuff)
val simSpiral = Spiral(center, center)
while (simSpiral.next() != currentPos);
direction = simSpiral.direction
count = simSpiral.count
}
if (count == 1) { // because of the center behaviour
count ++
return currentPos
}
if (currentPos == center) { // the center has to be handled exclusively
currentPos = Pair(center.first, center.second + 1)
count ++
return center
} else {
val distances = getDistances(center, currentPos)
if (abs(distances.first) == abs(distances.second)) {
direction = (direction + 1)%5
}
}
when(direction) {
0 -> {
currentPos = Pair(currentPos.first + 1, currentPos.second)
}
1 -> {
currentPos = Pair(currentPos.first, currentPos.second - 1)
}
2 -> {
currentPos = Pair(currentPos.first - 1, currentPos.second)
}
3 -> {
currentPos = Pair(currentPos.first, currentPos.second + 1)
}
4 -> {
currentPos = Pair(currentPos.first, currentPos.second + 1)
direction = 0
}
}
count ++
return currentPos
}
/**
* Returns the distances between 2 coordinates
*/
private fun getDistances(pos1: Pair<Int, Int>, pos2: Pair<Int, Int>): Pair<Int, Int> {
return Pair(pos2.first - pos1.first, pos2.second - pos1.second)
}
}

@ -1,137 +1,137 @@
package net.trivernis.chunkmaster.lib
import net.trivernis.chunkmaster.Chunkmaster
import org.apache.commons.lang.exception.ExceptionUtils
import org.sqlite.SQLiteConnection
import java.lang.Exception
import java.sql.Connection
import java.sql.DriverManager
import java.sql.PreparedStatement
import java.sql.ResultSet
class SqliteManager(private val chunkmaster: Chunkmaster) {
private val tables = listOf(
Pair(
"generation_tasks",
listOf(
Pair("id", "integer PRIMARY KEY AUTOINCREMENT"),
Pair("center_x", "integer NOT NULL DEFAULT 0"),
Pair("center_z", "integer NOT NULL DEFAULT 0"),
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")
)
)
)
private val needUpdate = HashSet<Pair<String, Pair<String, String>>>()
private val needCreation = HashSet<String>()
/**
* Returns the connection to the database
*/
fun getConnection(): Connection? {
try {
Class.forName("org.sqlite.JDBC")
return DriverManager.getConnection("jdbc:sqlite:${chunkmaster.dataFolder.absolutePath}/" +
"${chunkmaster.config.getString("database.filename")}")
} catch (e: Exception) {
chunkmaster.logger.severe("Could not get database connection.")
chunkmaster.logger.severe(e.message)
}
return null
}
/**
* Checks for and performs an update
*/
fun init() {
this.checkUpdate()
this.performUpdate()
}
/**
* Checks which tables need an update or creation.
*/
private fun checkUpdate() {
val meta = getConnection()!!.metaData
for (table in tables) {
val resTables = meta.getTables(null, null, table.first, null)
if (resTables.next()) { // table exists
for (column in table.second) {
val resColumn = meta.getColumns(null, null, table.first, column.first)
if (!resColumn.next()) {
needUpdate.add(Pair(table.first, column))
}
resColumn.close()
}
} else {
needCreation.add(table.first)
}
resTables.close()
}
}
/**
* Executes a sql statement on the database.
*/
fun executeStatement(sql: String, values: HashMap<Int, Any>, callback: ((ResultSet) -> Unit)?) {
val connection = getConnection()
if (connection != null) {
try {
val statement = connection.prepareStatement(sql)
for (parameterValue in values) {
statement.setObject(parameterValue.key, parameterValue.value)
}
statement.execute()
val res = statement.resultSet
if (callback != null) {
callback(res)
}
statement.close()
} catch (e: Exception) {
chunkmaster.logger.severe("An error occured on sql $sql. ${e.message}")
chunkmaster.logger.info(ExceptionUtils.getStackTrace(e))
} finally {
connection.close()
}
} else {
chunkmaster.logger.severe("Could not execute sql $sql. No database connection established.")
}
}
/**
* Creates or updates tables that needed an update.
*/
private fun performUpdate() {
for (table in needCreation) {
try {
var tableDef = "CREATE TABLE IF NOT EXISTS $table ("
for (column in tables.find{it.first == table}!!.second) {
tableDef += "${column.first} ${column.second},"
}
tableDef = tableDef.substringBeforeLast(",") + ");"
chunkmaster.logger.info("Creating table $table with definition $tableDef")
executeStatement(tableDef, HashMap(), null)
} catch (e: Exception) {
chunkmaster.logger.severe("Error creating table $table.")
chunkmaster.logger.severe(e.message)
chunkmaster.logger.info(ExceptionUtils.getStackTrace(e))
}
}
for (table in needUpdate) {
val updateSql = "ALTER TABLE ${table.first} ADD COLUMN ${table.second.first} ${table.second.second}"
try {
executeStatement(updateSql, HashMap(), null)
chunkmaster.logger.info("Updated table ${table.first} with sql $updateSql")
} catch (e: Exception) {
chunkmaster.logger.severe("Failed to update table ${table.first} with sql $updateSql")
chunkmaster.logger.severe(e.message)
chunkmaster.logger.info(ExceptionUtils.getStackTrace(e))
}
}
}
package net.trivernis.chunkmaster.lib
import net.trivernis.chunkmaster.Chunkmaster
import org.apache.commons.lang.exception.ExceptionUtils
import org.sqlite.SQLiteConnection
import java.lang.Exception
import java.sql.Connection
import java.sql.DriverManager
import java.sql.PreparedStatement
import java.sql.ResultSet
class SqliteManager(private val chunkmaster: Chunkmaster) {
private val tables = listOf(
Pair(
"generation_tasks",
listOf(
Pair("id", "integer PRIMARY KEY AUTOINCREMENT"),
Pair("center_x", "integer NOT NULL DEFAULT 0"),
Pair("center_z", "integer NOT NULL DEFAULT 0"),
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")
)
)
)
private val needUpdate = HashSet<Pair<String, Pair<String, String>>>()
private val needCreation = HashSet<String>()
/**
* Returns the connection to the database
*/
fun getConnection(): Connection? {
try {
Class.forName("org.sqlite.JDBC")
return DriverManager.getConnection("jdbc:sqlite:${chunkmaster.dataFolder.absolutePath}/" +
"${chunkmaster.config.getString("database.filename")}")
} catch (e: Exception) {
chunkmaster.logger.severe("Could not get database connection.")
chunkmaster.logger.severe(e.message)
}
return null
}
/**
* Checks for and performs an update
*/
fun init() {
this.checkUpdate()
this.performUpdate()
}
/**
* Checks which tables need an update or creation.
*/
private fun checkUpdate() {
val meta = getConnection()!!.metaData
for (table in tables) {
val resTables = meta.getTables(null, null, table.first, null)
if (resTables.next()) { // table exists
for (column in table.second) {
val resColumn = meta.getColumns(null, null, table.first, column.first)
if (!resColumn.next()) {
needUpdate.add(Pair(table.first, column))
}
resColumn.close()
}
} else {
needCreation.add(table.first)
}
resTables.close()
}
}
/**
* Executes a sql statement on the database.
*/
fun executeStatement(sql: String, values: HashMap<Int, Any>, callback: ((ResultSet) -> Unit)?) {
val connection = getConnection()
if (connection != null) {
try {
val statement = connection.prepareStatement(sql)
for (parameterValue in values) {
statement.setObject(parameterValue.key, parameterValue.value)
}
statement.execute()
val res = statement.resultSet
if (callback != null) {
callback(res)
}
statement.close()
} catch (e: Exception) {
chunkmaster.logger.severe("An error occured on sql $sql. ${e.message}")
chunkmaster.logger.info(ExceptionUtils.getStackTrace(e))
} finally {
connection.close()
}
} else {
chunkmaster.logger.severe("Could not execute sql $sql. No database connection established.")
}
}
/**
* Creates or updates tables that needed an update.
*/
private fun performUpdate() {
for (table in needCreation) {
try {
var tableDef = "CREATE TABLE IF NOT EXISTS $table ("
for (column in tables.find{it.first == table}!!.second) {
tableDef += "${column.first} ${column.second},"
}
tableDef = tableDef.substringBeforeLast(",") + ");"
chunkmaster.logger.info("Creating table $table with definition $tableDef")
executeStatement(tableDef, HashMap(), null)
} catch (e: Exception) {
chunkmaster.logger.severe("Error creating table $table.")
chunkmaster.logger.severe(e.message)
chunkmaster.logger.info(ExceptionUtils.getStackTrace(e))
}
}
for (table in needUpdate) {
val updateSql = "ALTER TABLE ${table.first} ADD COLUMN ${table.second.first} ${table.second.second}"
try {
executeStatement(updateSql, HashMap(), null)
chunkmaster.logger.info("Updated table ${table.first} with sql $updateSql")
} catch (e: Exception) {
chunkmaster.logger.severe("Failed to update table ${table.first} with sql $updateSql")
chunkmaster.logger.severe(e.message)
chunkmaster.logger.info(ExceptionUtils.getStackTrace(e))
}
}
}
}

@ -1,10 +1,10 @@
package net.trivernis.chunkmaster.lib
import org.bukkit.command.Command
import org.bukkit.command.CommandSender
interface Subcommand {
val name: String
fun execute(sender: CommandSender, args: List<String>): Boolean
fun onTabComplete(sender: CommandSender, command: Command, alias: String, args: List<String>): MutableList<String>
package net.trivernis.chunkmaster.lib
import org.bukkit.command.Command
import org.bukkit.command.CommandSender
interface Subcommand {
val name: String
fun execute(sender: CommandSender, args: List<String>): Boolean
fun onTabComplete(sender: CommandSender, command: Command, alias: String, args: List<String>): MutableList<String>
}

@ -1,10 +1,10 @@
package net.trivernis.chunkmaster.lib.generation
import org.bukkit.Location
import org.bukkit.World
class ChunkCoordinates(val x: Int, val z: Int) {
fun getCenterLocation(world: World): Location {
return Location(world, ((x*16) + 8).toDouble(), 1.0, ((x*16) + 8).toDouble())
}
package net.trivernis.chunkmaster.lib.generation
import org.bukkit.Location
import org.bukkit.World
class ChunkCoordinates(val x: Int, val z: Int) {
fun getCenterLocation(world: World): Location {
return Location(world, ((x*16) + 8).toDouble(), 1.0, ((x*16) + 8).toDouble())
}
}

@ -1,264 +1,264 @@
package net.trivernis.chunkmaster.lib.generation
import io.papermc.lib.PaperLib
import net.trivernis.chunkmaster.Chunkmaster
import org.bukkit.Server
import org.bukkit.World
class GenerationManager(private val chunkmaster: Chunkmaster, private val server: Server) {
val tasks: HashSet<RunningTaskEntry> = HashSet()
val pausedTasks: HashSet<PausedTaskEntry> = HashSet()
val allTasks: HashSet<TaskEntry>
get() {
val all = HashSet<TaskEntry>()
all.addAll(pausedTasks)
all.addAll(tasks)
return all
}
var paused = false
private set
/**
* Adds a generation task
*/
fun addTask(world: World, stopAfter: Int = -1): Int {
val foundTask = allTasks.find { it.generationTask.world == world }
if (foundTask == null) {
val centerChunk = ChunkCoordinates(world.spawnLocation.chunk.x, world.spawnLocation.chunk.z)
val generationTask = createGenerationTask(world, centerChunk, centerChunk, stopAfter)
chunkmaster.sqliteManager.executeStatement(
"""
INSERT INTO generation_tasks (center_x, center_z, last_x, last_z, world, stop_after)
values (?, ?, ?, ?, ?, ?)
""",
HashMap(
mapOf(
1 to centerChunk.x,
2 to centerChunk.z,
3 to centerChunk.x,
4 to centerChunk.z,
5 to world.name,
6 to stopAfter
)
),
null
)
var id = 0
chunkmaster.sqliteManager.executeStatement(
"""
SELECT id FROM generation_tasks ORDER BY id DESC LIMIT 1
""".trimIndent(),
HashMap()
) {
it.next()
id = it.getInt("id")
}
generationTask.onEndReached {
chunkmaster.logger.info("Task #${id} finished after ${it.count} chunks.")
removeTask(id)
}
if (!paused) {
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))
}
return id
} else {
return foundTask.id
}
}
/**
* Resumes a generation task
*/
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, 200, // 10 sec delay
chunkmaster.config.getLong("generation.period")
)
tasks.add(RunningTaskEntry(id, task, generationTask))
generationTask.onEndReached {
chunkmaster.logger.info("Task #${id} finished after ${generationTask.count} chunks.")
removeTask(id)
}
}
}
/**
* Stops a running generation task.
*/
fun removeTask(id: Int): Boolean {
val taskEntry: TaskEntry? = if (this.paused) {
this.pausedTasks.find { it.id == id }
} else {
this.tasks.find { it.id == id }
}
if (taskEntry != null) {
taskEntry.cancel()
chunkmaster.sqliteManager.executeStatement(
"""
DELETE FROM generation_tasks WHERE id = ?;
""".trimIndent(),
HashMap(mapOf(1 to taskEntry.id)),
null
)
if (taskEntry is RunningTaskEntry) {
if (taskEntry.task.isCancelled) {
tasks.remove(taskEntry)
}
} else if (taskEntry is PausedTaskEntry) {
pausedTasks.remove(taskEntry)
}
return true
}
return false
}
/**
* Init
* Loads tasks from the database and resumes them
*/
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
}
}, 600)
}
/**
* Stops all generation tasks
*/
fun stopAll() {
saveProgress()
val removalSet = HashSet<RunningTaskEntry>()
for (task in tasks) {
task.generationTask.cancel()
task.task.cancel()
if (task.task.isCancelled) {
removalSet.add(task)
}
chunkmaster.logger.info("Canceled task #${task.id}")
}
tasks.removeAll(removalSet)
}
/**
* Starts all generation tasks.
*/
fun startAll() {
chunkmaster.sqliteManager.executeStatement("SELECT * FROM generation_tasks", HashMap()) {
val res = it
while (res.next()) {
try {
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) {
chunkmaster.logger.severe("Failed to load Task ${res.getInt("id")}.")
}
}
}
if (tasks.isNotEmpty()) {
chunkmaster.logger.info("${tasks.size} saved tasks loaded.")
}
}
/**
* Pauses all tasks
*/
fun pauseAll() {
paused = true
for (task in tasks) {
pausedTasks.add(PausedTaskEntry(task.id, task.generationTask))
}
stopAll()
}
/**
* Resumes all tasks
*/
fun resumeAll() {
paused = false
pausedTasks.clear()
startAll()
}
/**
* Saves the task progress
*/
private fun saveProgress() {
for (task in tasks) {
try {
val genTask = task.generationTask
chunkmaster.logger.info(
"""Task #${task.id} running for "${genTask.world.name}".
|Progress ${task.generationTask.count} chunks
|${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', ' ')
)
chunkmaster.sqliteManager.executeStatement(
"""
UPDATE generation_tasks SET last_x = ?, last_z = ?
WHERE id = ?
""".trimIndent(),
HashMap(mapOf(1 to genTask.lastChunk.x, 2 to genTask.lastChunk.z, 3 to task.id)),
null
)
} catch (error: Exception) {
chunkmaster.logger.warning("Exception when saving task progress ${error.message}")
}
}
}
/**
* 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: ChunkCoordinates,
start: ChunkCoordinates,
stopAfter: Int
): GenerationTask {
return if (PaperLib.isPaper()) {
GenerationTaskPaper(chunkmaster, world, center, start, stopAfter)
} else {
GenerationTaskSpigot(chunkmaster, world, center, start, stopAfter)
}
}
package net.trivernis.chunkmaster.lib.generation
import io.papermc.lib.PaperLib
import net.trivernis.chunkmaster.Chunkmaster
import org.bukkit.Server
import org.bukkit.World
class GenerationManager(private val chunkmaster: Chunkmaster, private val server: Server) {
val tasks: HashSet<RunningTaskEntry> = HashSet()
val pausedTasks: HashSet<PausedTaskEntry> = HashSet()
val allTasks: HashSet<TaskEntry>
get() {
val all = HashSet<TaskEntry>()
all.addAll(pausedTasks)
all.addAll(tasks)
return all
}
var paused = false
private set
/**
* Adds a generation task
*/
fun addTask(world: World, stopAfter: Int = -1): Int {
val foundTask = allTasks.find { it.generationTask.world == world }
if (foundTask == null) {
val centerChunk = ChunkCoordinates(world.spawnLocation.chunk.x, world.spawnLocation.chunk.z)
val generationTask = createGenerationTask(world, centerChunk, centerChunk, stopAfter)
chunkmaster.sqliteManager.executeStatement(
"""
INSERT INTO generation_tasks (center_x, center_z, last_x, last_z, world, stop_after)
values (?, ?, ?, ?, ?, ?)
""",
HashMap(
mapOf(
1 to centerChunk.x,
2 to centerChunk.z,
3 to centerChunk.x,
4 to centerChunk.z,
5 to world.name,
6 to stopAfter
)
),
null
)
var id = 0
chunkmaster.sqliteManager.executeStatement(
"""
SELECT id FROM generation_tasks ORDER BY id DESC LIMIT 1
""".trimIndent(),
HashMap()
) {
it.next()
id = it.getInt("id")
}
generationTask.onEndReached {
chunkmaster.logger.info("Task #${id} finished after ${it.count} chunks.")
removeTask(id)
}
if (!paused) {
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))
}
return id
} else {
return foundTask.id
}
}
/**
* Resumes a generation task
*/
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, 200, // 10 sec delay
chunkmaster.config.getLong("generation.period")
)
tasks.add(RunningTaskEntry(id, task, generationTask))
generationTask.onEndReached {
chunkmaster.logger.info("Task #${id} finished after ${generationTask.count} chunks.")
removeTask(id)
}
}
}
/**
* Stops a running generation task.
*/
fun removeTask(id: Int): Boolean {
val taskEntry: TaskEntry? = if (this.paused) {
this.pausedTasks.find { it.id == id }
} else {
this.tasks.find { it.id == id }
}
if (taskEntry != null) {
taskEntry.cancel()
chunkmaster.sqliteManager.executeStatement(
"""
DELETE FROM generation_tasks WHERE id = ?;
""".trimIndent(),
HashMap(mapOf(1 to taskEntry.id)),
null
)
if (taskEntry is RunningTaskEntry) {
if (taskEntry.task.isCancelled) {
tasks.remove(taskEntry)
}
} else if (taskEntry is PausedTaskEntry) {
pausedTasks.remove(taskEntry)
}
return true
}
return false
}
/**
* Init
* Loads tasks from the database and resumes them
*/
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
}
}, 600)
}
/**
* Stops all generation tasks
*/
fun stopAll() {
saveProgress()
val removalSet = HashSet<RunningTaskEntry>()
for (task in tasks) {
task.generationTask.cancel()
task.task.cancel()
if (task.task.isCancelled) {
removalSet.add(task)
}
chunkmaster.logger.info("Canceled task #${task.id}")
}
tasks.removeAll(removalSet)
}
/**
* Starts all generation tasks.
*/
fun startAll() {
chunkmaster.sqliteManager.executeStatement("SELECT * FROM generation_tasks", HashMap()) {
val res = it
while (res.next()) {
try {
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) {
chunkmaster.logger.severe("Failed to load Task ${res.getInt("id")}.")
}
}
}
if (tasks.isNotEmpty()) {
chunkmaster.logger.info("${tasks.size} saved tasks loaded.")
}
}
/**
* Pauses all tasks
*/
fun pauseAll() {
paused = true
for (task in tasks) {
pausedTasks.add(PausedTaskEntry(task.id, task.generationTask))
}
stopAll()
}
/**
* Resumes all tasks
*/
fun resumeAll() {
paused = false
pausedTasks.clear()
startAll()
}
/**
* Saves the task progress
*/
private fun saveProgress() {
for (task in tasks) {
try {
val genTask = task.generationTask
chunkmaster.logger.info(
"""Task #${task.id} running for "${genTask.world.name}".
|Progress ${task.generationTask.count} chunks
|${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', ' ')
)
chunkmaster.sqliteManager.executeStatement(
"""
UPDATE generation_tasks SET last_x = ?, last_z = ?
WHERE id = ?
""".trimIndent(),
HashMap(mapOf(1 to genTask.lastChunk.x, 2 to genTask.lastChunk.z, 3 to task.id)),
null
)
} catch (error: Exception) {
chunkmaster.logger.warning("Exception when saving task progress ${error.message}")
}
}
}
/**
* 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: ChunkCoordinates,
start: ChunkCoordinates,
stopAfter: Int
): GenerationTask {
return if (PaperLib.isPaper()) {
GenerationTaskPaper(chunkmaster, world, center, start, stopAfter)
} else {
GenerationTaskSpigot(chunkmaster, world, center, start, stopAfter)
}
}
}

@ -1,64 +1,66 @@
package net.trivernis.chunkmaster.lib.generation
import net.trivernis.chunkmaster.Chunkmaster
import net.trivernis.chunkmaster.lib.Spiral
import org.bukkit.Chunk
import org.bukkit.World
/**
* Interface for generation tasks.
*/
abstract class GenerationTask(plugin: Chunkmaster, centerChunk: ChunkCoordinates, startChunk: ChunkCoordinates) :
Runnable {
abstract val stopAfter: Int
abstract val world: World
abstract val count: Int
abstract val endReached: Boolean
protected val spiral: Spiral =
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.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: ((GenerationTask) -> Unit)? = null
private set
abstract override fun run()
abstract fun cancel()
val nextChunkCoordinates: ChunkCoordinates
get() {
val nextChunkCoords = spiral.next()
return ChunkCoordinates(nextChunkCoords.first, nextChunkCoords.second)
}
val lastChunk: Chunk
get() {
return world.getChunkAt(lastChunkCoords.x, lastChunkCoords.z)
}
val nextChunk: Chunk
get() {
val next = nextChunkCoordinates
return world.getChunkAt(next.x, next.z)
}
/**
* Checks if the World border or the maximum chunk setting for the task is reached.
*/
protected fun borderReached(): Boolean {
return !world.worldBorder.isInside(lastChunkCoords.getCenterLocation(world)) || (stopAfter in 1..count)
}
/**
* Registers end reached callback
*/
fun onEndReached(cb: (GenerationTask) -> Unit) {
endReachedCallback = cb
}
package net.trivernis.chunkmaster.lib.generation
import net.trivernis.chunkmaster.Chunkmaster
import net.trivernis.chunkmaster.lib.Spiral
import org.bukkit.Chunk
import org.bukkit.World
/**
* Interface for generation tasks.
*/
abstract class GenerationTask(plugin: Chunkmaster, centerChunk: ChunkCoordinates, startChunk: ChunkCoordinates) :
Runnable {
abstract val stopAfter: Int
abstract val world: World
abstract val count: Int
abstract val endReached: Boolean
protected val spiral: Spiral =
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.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 val ignoreWorldborder = plugin.config.getBoolean("generation.ignore-worldborder")
protected var endReachedCallback: ((GenerationTask) -> Unit)? = null
private set
abstract override fun run()
abstract fun cancel()
val nextChunkCoordinates: ChunkCoordinates
get() {
val nextChunkCoords = spiral.next()
return ChunkCoordinates(nextChunkCoords.first, nextChunkCoords.second)
}
val lastChunk: Chunk
get() {
return world.getChunkAt(lastChunkCoords.x, lastChunkCoords.z)
}
val nextChunk: Chunk
get() {
val next = nextChunkCoordinates
return world.getChunkAt(next.x, next.z)
}
/**
* Checks if the World border or the maximum chunk setting for the task is reached.
*/
protected fun borderReached(): Boolean {
return (!world.worldBorder.isInside(lastChunkCoords.getCenterLocation(world)) && !ignoreWorldborder)
|| (stopAfter in 1..count)
}
/**
* Registers end reached callback
*/
fun onEndReached(cb: (GenerationTask) -> Unit) {
endReachedCallback = cb
}
}

@ -1,117 +1,116 @@
package net.trivernis.chunkmaster.lib.generation
import net.trivernis.chunkmaster.Chunkmaster
import org.bukkit.Chunk
import org.bukkit.World
import java.util.concurrent.CompletableFuture
import io.papermc.lib.PaperLib
class GenerationTaskPaper(
private val plugin: Chunkmaster, override val world: World,
centerChunk: ChunkCoordinates, private val startChunk: ChunkCoordinates,
override val stopAfter: Int = -1
) : GenerationTask(plugin, centerChunk, startChunk) {
private val maxPendingChunks = plugin.config.getInt("generation.max-pending-chunks")
private val pendingChunks = HashSet<CompletableFuture<Chunk>>()
override var count = 0
private set
override var endReached: Boolean = false
private set
/**
* Runs the generation task. Every Iteration the next chunk will be generated if
* it hasn't been generated already.
* After 10 chunks have been generated, they will all be unloaded and saved.
*/
override fun run() {
if (plugin.mspt < msptThreshold) { // pause when tps < 2
if (loadedChunks.size > maxLoadedChunks) {
for (chunk in loadedChunks) {
if (chunk.isLoaded) {
chunk.unload(true)
}
}
loadedChunks.clear()
} else if (pendingChunks.size < maxPendingChunks) { // if more than 10 chunks are pending, wait.
if (borderReached()) {
endReached = true
endReachedCallback?.invoke(this)
return
}
var chunk = nextChunkCoordinates
for (i in 1 until chunkSkips) {
if (PaperLib.isChunkGenerated(world, chunk.x, chunk.z)) {
chunk = nextChunkCoordinates
} else {
break
}
}
if (!PaperLib.isChunkGenerated(world, chunk.x, chunk.z)) {
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
}
}
checkChunksLoaded()
}
/**
* Cancels the generation task.
* 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())
} else {
pendingChunk.cancel(true)
}
}
pendingChunks.clear()
if (loadedChunks.isNotEmpty()) {
lastChunkCoords = ChunkCoordinates(loadedChunks.last().x, loadedChunks.last().z)
}
for (chunk in loadedChunks) {
if (chunk.isLoaded) {
chunk.unload(true)
}
}
}
/**
* Checks if some chunks have been loaded and adds them to the loaded chunk set.
*/
private fun checkChunksLoaded() {
val completedEntrys = HashSet<CompletableFuture<Chunk>>()
for (pendingChunk in pendingChunks) {
if (pendingChunk.isDone) {
completedEntrys.add(pendingChunk)
loadedChunks.add(pendingChunk.get())
} else if (pendingChunk.isCompletedExceptionally || pendingChunk.isCancelled) {
completedEntrys.add(pendingChunk)
}
}
pendingChunks.removeAll(completedEntrys)
}
package net.trivernis.chunkmaster.lib.generation
import net.trivernis.chunkmaster.Chunkmaster
import org.bukkit.Chunk
import org.bukkit.World
import java.util.concurrent.CompletableFuture
class GenerationTaskPaper(
private val plugin: Chunkmaster, override val world: World,
centerChunk: ChunkCoordinates, private val startChunk: ChunkCoordinates,
override val stopAfter: Int = -1
) : GenerationTask(plugin, centerChunk, startChunk) {
private val maxPendingChunks = plugin.config.getInt("generation.max-pending-chunks")
private val pendingChunks = HashSet<CompletableFuture<Chunk>>()
override var count = 0
private set
override var endReached: Boolean = false
private set
/**
* Runs the generation task. Every Iteration the next chunk will be generated if
* it hasn't been generated already.
* After 10 chunks have been generated, they will all be unloaded and saved.
*/
override fun run() {
if (plugin.mspt < msptThreshold) { // pause when tps < 2
if (loadedChunks.size > maxLoadedChunks) {
for (chunk in loadedChunks) {
if (chunk.isLoaded) {
chunk.unload(true)
}
}
loadedChunks.clear()
} else if (pendingChunks.size < maxPendingChunks) { // if more than 10 chunks are pending, wait.
if (borderReached()) {
endReached = true
endReachedCallback?.invoke(this)
return
}
var chunk = nextChunkCoordinates
for (i in 1 until chunkSkips) {
if (world.isChunkGenerated(chunk.x, chunk.z)) {
chunk = nextChunkCoordinates
} else {
break
}
}
if (!world.isChunkGenerated(chunk.x, chunk.z)) {
for (i in 0 until minOf(chunksPerStep, (stopAfter - count) - 1)) {
if (!world.isChunkGenerated(chunk.x, chunk.z)) {
pendingChunks.add(world.getChunkAtAsync(chunk.x, chunk.z, true))
}
chunk = nextChunkCoordinates
}
if (!world.isChunkGenerated(chunk.x, chunk.z)) {
pendingChunks.add(world.getChunkAtAsync(chunk.x, chunk.z, true))
}
}
lastChunkCoords = chunk
count = spiral.count // set the count to the more accurate spiral count
}
}
checkChunksLoaded()
}
/**
* Cancels the generation task.
* 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())
} else {
pendingChunk.cancel(true)
}
}
pendingChunks.clear()
if (loadedChunks.isNotEmpty()) {
lastChunkCoords = ChunkCoordinates(loadedChunks.last().x, loadedChunks.last().z)
}
for (chunk in loadedChunks) {
if (chunk.isLoaded) {
chunk.unload(true)
}
}
}
/**
* Checks if some chunks have been loaded and adds them to the loaded chunk set.
*/
private fun checkChunksLoaded() {
val completedEntrys = HashSet<CompletableFuture<Chunk>>()
for (pendingChunk in pendingChunks) {
if (pendingChunk.isDone) {
completedEntrys.add(pendingChunk)
loadedChunks.add(pendingChunk.get())
} else if (pendingChunk.isCompletedExceptionally || pendingChunk.isCancelled) {
completedEntrys.add(pendingChunk)
}
}
pendingChunks.removeAll(completedEntrys)
}
}

@ -1,70 +1,69 @@
package net.trivernis.chunkmaster.lib.generation
import net.trivernis.chunkmaster.Chunkmaster
import org.bukkit.Chunk
import org.bukkit.World
class GenerationTaskSpigot(
private val plugin: Chunkmaster, override val world: World,
centerChunk: ChunkCoordinates, private val startChunk: ChunkCoordinates,
override val stopAfter: Int = -1
) : GenerationTask(plugin, centerChunk, startChunk) {
override var count = 0
private set
override var endReached: Boolean = false
private set
/**
* Runs the generation task. Every Iteration the next chunk will be generated if
* it hasn't been generated already.
* After 10 chunks have been generated, they will all be unloaded and saved.
*/
override fun run() {
if (plugin.mspt < msptThreshold) { // pause when tps < 2
if (loadedChunks.size > maxLoadedChunks) {
for (chunk in loadedChunks) {
if (chunk.isLoaded) {
chunk.unload(true)
}
}
loadedChunks.clear()
} else {
if (borderReached()) {
endReached = true
endReachedCallback?.invoke(this)
return
}
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)
}
lastChunkCoords = chunk
count = spiral.count // set the count to the more accurate spiral count
}
}
}
/**
* Cancels the generation task.
* This unloads all chunks that were generated but not unloaded yet.
*/
override fun cancel() {
for (chunk in loadedChunks) {
if (chunk.isLoaded) {
chunk.unload(true)
}
}
}
package net.trivernis.chunkmaster.lib.generation
import net.trivernis.chunkmaster.Chunkmaster
import org.bukkit.World
class GenerationTaskSpigot(
private val plugin: Chunkmaster, override val world: World,
centerChunk: ChunkCoordinates, private val startChunk: ChunkCoordinates,
override val stopAfter: Int = -1
) : GenerationTask(plugin, centerChunk, startChunk) {
override var count = 0
private set
override var endReached: Boolean = false
private set
/**
* Runs the generation task. Every Iteration the next chunk will be generated if
* it hasn't been generated already.
* After 10 chunks have been generated, they will all be unloaded and saved.
*/
override fun run() {
if (plugin.mspt < msptThreshold) { // pause when tps < 2
if (loadedChunks.size > maxLoadedChunks) {
for (chunk in loadedChunks) {
if (chunk.isLoaded) {
chunk.unload(true)
}
}
loadedChunks.clear()
} else {
if (borderReached()) {
endReached = true
endReachedCallback?.invoke(this)
return
}
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)
}
lastChunkCoords = chunk
count = spiral.count // set the count to the more accurate spiral count
}
}
}
/**
* Cancels the generation task.
* This unloads all chunks that were generated but not unloaded yet.
*/
override fun cancel() {
for (chunk in loadedChunks) {
if (chunk.isLoaded) {
chunk.unload(true)
}
}
}
}

@ -1,10 +1,10 @@
package net.trivernis.chunkmaster.lib.generation
class PausedTaskEntry(
override val id: Int,
override val generationTask: GenerationTask
) : TaskEntry {
override fun cancel() {
generationTask.cancel()
}
package net.trivernis.chunkmaster.lib.generation
class PausedTaskEntry(
override val id: Int,
override val generationTask: GenerationTask
) : TaskEntry {
override fun cancel() {
generationTask.cancel()
}
}

@ -1,37 +1,37 @@
package net.trivernis.chunkmaster.lib.generation
import org.bukkit.scheduler.BukkitTask
class RunningTaskEntry(
override val id: Int,
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()
}
package net.trivernis.chunkmaster.lib.generation
import org.bukkit.scheduler.BukkitTask
class RunningTaskEntry(
override val id: Int,
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,13 +1,13 @@
package net.trivernis.chunkmaster.lib.generation
import org.bukkit.scheduler.BukkitTask
/**
* Generic task entry
*/
interface TaskEntry {
val id: Int
val generationTask: GenerationTask
fun cancel()
package net.trivernis.chunkmaster.lib.generation
import org.bukkit.scheduler.BukkitTask
/**
* Generic task entry
*/
interface TaskEntry {
val id: Int
val generationTask: GenerationTask
fun cancel()
}

@ -1,58 +1,58 @@
main: net.trivernis.chunkmaster.Chunkmaster
name: Chunkmaster
version: '0.13-beta'
description: Chunk commands plugin.
author: Trivernis
website: trivernis.net
api-version: '1.14'
commands:
chunkmaster:
description: Main command
permission: chunkmaster.chunkmaster
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
- cmaster
permissions:
cunkmaster.generate:
description: Allows the generate subcommand.
default: op
chunkmaster.list:
description: Allows the list subcommand.
default: op
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.tpchunk:
description: Allows the tpchunk subcommand.
default: op
chunkmaster.chunkmaster:
description: Allows Chunkmaster commands.
default: op
chunkmaster.*:
description: Wildcard permission
default: op
children:
- chunkmaster.generate
- chunkmaster.listgentasks
- chunkmaster.removegentask
- chunkmaster.pausegentasks
- chunkmaster.resumegentasks
main: net.trivernis.chunkmaster.Chunkmaster
name: Chunkmaster
version: '0.13-beta'
description: Chunk commands plugin.
author: Trivernis
website: trivernis.net
api-version: '1.14'
commands:
chunkmaster:
description: Main command
permission: chunkmaster.chunkmaster
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
- cmaster
permissions:
cunkmaster.generate:
description: Allows the generate subcommand.
default: op
chunkmaster.list:
description: Allows the list subcommand.
default: op
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.tpchunk:
description: Allows the tpchunk subcommand.
default: op
chunkmaster.chunkmaster:
description: Allows Chunkmaster commands.
default: op
chunkmaster.*:
description: Wildcard permission
default: op
children:
- chunkmaster.generate
- chunkmaster.listgentasks
- chunkmaster.removegentask
- chunkmaster.pausegentasks
- chunkmaster.resumegentasks
- chunkmaster.chunkmaster
Loading…
Cancel
Save