Add ignore-worldborder config option
- [Convert to unix linebreaks] - Add ignore-worldborder config option to generate past the vanilla world borderpull/15/head
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
|
@ -1,6 +1,6 @@
|
||||
gradle
|
||||
.gradle
|
||||
.idea
|
||||
build
|
||||
out
|
||||
gradle
|
||||
.gradle
|
||||
.idea
|
||||
build
|
||||
out
|
||||
gradlew*
|
@ -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…
Reference in New Issue