From 8953b63c2b5a0e2677371f798e50cff5b7637b1b Mon Sep 17 00:00:00 2001 From: trivernis Date: Sat, 11 Apr 2020 14:12:05 +0200 Subject: [PATCH] Add integration for dynmap By integrating with dynmap the plugin can show the area of the generation task for each world via an area marker and triggers the rendering of tiles for generated chunks. --- README.md | 17 ++++ build.gradle | 6 ++ .../net/trivernis/chunkmaster/Chunkmaster.kt | 18 ++++ .../chunkmaster/commands/CmdReload.kt | 1 + .../lib/generation/GenerationTask.kt | 87 ++++++++++++++++++- .../lib/generation/GenerationTaskPaper.kt | 18 ++-- .../lib/generation/GenerationTaskSpigot.kt | 16 ++-- .../resources/i18n/DEFAULT.i18n.properties | 4 +- src/main/resources/i18n/de.i18n.properties | 4 +- src/main/resources/i18n/en.i18n.properties | 4 +- src/main/resources/plugin.yml | 3 + 11 files changed, 152 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index a52e6c9..a281f65 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,15 @@ is lower than 2 (configurable). - [Sqlite JDBC](https://bitbucket.org/xerial/sqlite-jdbc/) - Database Driver for JDBC - [bStats](https://bstats.org/) - Statistical Insights +## Features + +- Pregeneration of a specific area around the world center +- Configuration of world centers +- Integration into dynmap +- Teleportation to chunks +- Auto-Pause/Resume on player join/leave +- Highly configurable + ## Installing Just download the jar from the latest release and place it into the servers plugins folder. @@ -66,6 +75,14 @@ All features can be accessed with the command `/chunkmaster` or the aliases `/ch # For built-in support please create a PullRequest with your translation. language: en +# Actiates/deactivates the dynmap integration. +# With the setting set to 'true' the plugin tries to trigger the rendering +# of generated chunks right before unloading them. It also adds an area +# marker to the dynmap to show the area that will be pregenerated. +# The marker is removed automatically when the task is finished or canceled. +# The value should be a boolean +dynmap: true + generation: # If set to true the plugin ignores the vanilla world border and doesn't stop diff --git a/build.gradle b/build.gradle index a24dac6..66a32f9 100644 --- a/build.gradle +++ b/build.gradle @@ -42,6 +42,11 @@ repositories { name 'CodeMc' url 'https://repo.codemc.org/repository/maven-public' } + + maven { + name 'mikeprimm' + url 'http://repo.mikeprimm.com' + } } dependencies { @@ -49,6 +54,7 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" testCompile group: 'junit', name: 'junit', version: '4.12' compileOnly "com.destroystokyo.paper:paper-api:1.14.4-R0.1-SNAPSHOT" + compileOnly "org.dynmap:dynmap-api:2.0" 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" diff --git a/src/main/kotlin/net/trivernis/chunkmaster/Chunkmaster.kt b/src/main/kotlin/net/trivernis/chunkmaster/Chunkmaster.kt index efc8916..f1d6db4 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/Chunkmaster.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/Chunkmaster.kt @@ -8,6 +8,8 @@ import net.trivernis.chunkmaster.lib.SqliteManager import org.bstats.bukkit.Metrics import org.bukkit.plugin.java.JavaPlugin import org.bukkit.scheduler.BukkitTask +import org.dynmap.DynmapAPI +import org.dynmap.DynmapCommonAPI import java.lang.Exception class Chunkmaster: JavaPlugin() { @@ -15,6 +17,8 @@ class Chunkmaster: JavaPlugin() { lateinit var generationManager: GenerationManager lateinit var langManager: LanguageManager private lateinit var tpsTask: BukkitTask + var dynmapApi: DynmapAPI? = null + private set var mspt = 20 // keep track of the milliseconds per tick private set @@ -29,6 +33,9 @@ class Chunkmaster: JavaPlugin() { langManager = LanguageManager(this) langManager.loadProperties() + + this.dynmapApi = getDynmap() + initDatabase() generationManager = GenerationManager(this, server) generationManager.init() @@ -75,6 +82,7 @@ class Chunkmaster: JavaPlugin() { config.addDefault("generation.ignore-worldborder", false) config.addDefault("database.filename", "chunkmaster.db") config.addDefault("language", "en") + config.addDefault("dynmap", true) config.options().copyDefaults(true) saveConfig() } @@ -92,4 +100,14 @@ class Chunkmaster: JavaPlugin() { logger.warning(langManager.getLocalized("DB_INIT_EROR", e.message!!)) } } + + private fun getDynmap(): DynmapAPI? { + val dynmap = server.pluginManager.getPlugin("dynmap") + return if (dynmap != null && dynmap is DynmapAPI) { + logger.info(langManager.getLocalized("PLUGIN_DETECTED", "dynmap", dynmap.dynmapVersion)) + dynmap + } else { + null + } + } } \ No newline at end of file diff --git a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdReload.kt b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdReload.kt index 4a38fa6..1503770 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdReload.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdReload.kt @@ -27,6 +27,7 @@ class CmdReload(private val chunkmaster: Chunkmaster): Subcommand { chunkmaster.generationManager.stopAll() chunkmaster.reloadConfig() chunkmaster.generationManager.startAll() + chunkmaster.langManager.loadProperties() sender.sendMessage(chunkmaster.langManager.getLocalized("CONFIG_RELOADED")) return true } diff --git a/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationTask.kt b/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationTask.kt index 7a2f1bd..8436a92 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationTask.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationTask.kt @@ -3,16 +3,20 @@ package net.trivernis.chunkmaster.lib.generation import net.trivernis.chunkmaster.Chunkmaster import net.trivernis.chunkmaster.lib.Spiral import org.bukkit.Chunk +import org.bukkit.Location import org.bukkit.World +import kotlin.math.* + /** * Interface for generation tasks. */ -abstract class GenerationTask(plugin: Chunkmaster, centerChunk: ChunkCoordinates, startChunk: ChunkCoordinates) : +abstract class GenerationTask(plugin: Chunkmaster, private val centerChunk: ChunkCoordinates, startChunk: ChunkCoordinates) : Runnable { + abstract val stopAfter: Int abstract val world: World abstract val count: Int - abstract val endReached: Boolean + abstract var endReached: Boolean protected val spiral: Spiral = Spiral(Pair(centerChunk.x, centerChunk.z), Pair(startChunk.x, startChunk.z)) @@ -23,11 +27,15 @@ abstract class GenerationTask(plugin: Chunkmaster, centerChunk: ChunkCoordinates 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 val dynmapIntegration = plugin.config.getBoolean("dynmap") + protected val dynmap = plugin.dynmapApi protected var endReachedCallback: ((GenerationTask) -> Unit)? = null private set + private val markerId = "chunkmaster_genarea" + private val markerName = "Chunkmaster Generation Area" + private val ignoreWorldborder = plugin.config.getBoolean("generation.ignore-worldborder") + abstract override fun run() abstract fun cancel() @@ -56,6 +64,77 @@ abstract class GenerationTask(plugin: Chunkmaster, centerChunk: ChunkCoordinates || (stopAfter in 1..count) } + /** + * Unloads all chunks that have been loaded + */ + protected fun unloadLoadedChunks() { + for (chunk in loadedChunks) { + if (chunk.isLoaded) { + chunk.unload(true) + } + if (dynmapIntegration) { + dynmap?.triggerRenderOfVolume(chunk.getBlock(0, 0, 0).location, chunk.getBlock(15, 255, 15).location) + } + } + + loadedChunks.clear() + } + + /** + * Updates the dynmap marker for the generation radius + */ + protected fun updateDynmapMarker(clear: Boolean = false) { + val markerSet = dynmap?.markerAPI?.getMarkerSet("markers") + var marker = markerSet?.findAreaMarker(markerId) + if (clear) { + marker?.deleteMarker() + } else if (dynmapIntegration && stopAfter > 0) { + val (topLeft, bottomRight) = this.getAreaCorners() + if (marker != null) { + marker.setCornerLocations( + doubleArrayOf((topLeft.x * 16).toDouble(), (bottomRight.x * 16).toDouble()), + doubleArrayOf((topLeft.z * 16).toDouble(), (bottomRight.z * 16).toDouble()) + ) + } else { + marker = markerSet?.createAreaMarker( + markerId, + markerName, + false, + world.name, + doubleArrayOf((topLeft.x * 16).toDouble(), (bottomRight.x * 16).toDouble()), + doubleArrayOf((topLeft.z * 16).toDouble(), (bottomRight.z * 16).toDouble()), + true + ) + } + marker?.setFillStyle(.0, 0) + marker?.setLineStyle(2, 1.0, 0x0000FF) + } + } + + /** + * Returns an approximation of cornders of the generation area + */ + protected fun getAreaCorners(): Pair { + val width = sqrt(stopAfter.toFloat()) + return Pair( + ChunkCoordinates(centerChunk.x - floor(width/2).toInt(), centerChunk.z - floor(width/2).toInt()), + ChunkCoordinates(centerChunk.x + ceil(width/2).toInt(), centerChunk.z + ceil(width/2).toInt()) + ) + } + + /** + * Handles the invocation of the end reached callback and additional logic + */ + protected fun setEndReached() { + endReached = true + endReachedCallback?.invoke(this) + updateDynmapMarker(true) + if (dynmapIntegration) { + val (topLeft, bottomRight) = this.getAreaCorners() + dynmap?.triggerRenderOfVolume(topLeft.getCenterLocation(world), bottomRight.getCenterLocation(world)) + } + } + /** * Registers end reached callback */ diff --git a/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationTaskPaper.kt b/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationTaskPaper.kt index 7342df1..a8e20bc 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationTaskPaper.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationTaskPaper.kt @@ -18,7 +18,10 @@ class GenerationTaskPaper( override var count = 0 private set override var endReached: Boolean = false - private set + + init { + updateDynmapMarker() + } /** * Runs the generation task. Every Iteration the next chunk will be generated if @@ -28,16 +31,10 @@ class GenerationTaskPaper( 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() + unloadLoadedChunks() } else if (pendingChunks.size < maxPendingChunks) { // if more than 10 chunks are pending, wait. if (borderReached()) { - endReached = true - endReachedCallback?.invoke(this) + setEndReached() return } @@ -73,13 +70,14 @@ class GenerationTaskPaper( * This unloads all chunks that were generated but not unloaded yet. */ override fun cancel() { + updateDynmapMarker(true) unloadAllChunks() } /** * Cancels all pending chunks and unloads all loaded chunks. */ - fun unloadAllChunks() { + private fun unloadAllChunks() { for (pendingChunk in pendingChunks) { if (pendingChunk.isDone) { loadedChunks.add(pendingChunk.get()) diff --git a/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationTaskSpigot.kt b/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationTaskSpigot.kt index 6f93203..bf5a36f 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationTaskSpigot.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationTaskSpigot.kt @@ -14,7 +14,10 @@ class GenerationTaskSpigot( override var count = 0 private set override var endReached: Boolean = false - private set + + init { + updateDynmapMarker() + } /** * Runs the generation task. Every Iteration the next chunk will be generated if @@ -24,16 +27,10 @@ class GenerationTaskSpigot( 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() + unloadLoadedChunks() } else { if (borderReached()) { - endReached = true - endReachedCallback?.invoke(this) + setEndReached() return } @@ -70,5 +67,6 @@ class GenerationTaskSpigot( } } } + updateDynmapMarker(true) } } \ No newline at end of file diff --git a/src/main/resources/i18n/DEFAULT.i18n.properties b/src/main/resources/i18n/DEFAULT.i18n.properties index 1b70fdc..fac0f79 100644 --- a/src/main/resources/i18n/DEFAULT.i18n.properties +++ b/src/main/resources/i18n/DEFAULT.i18n.properties @@ -51,4 +51,6 @@ UPDATE_TABLE_FAILED = Failed to update table %s with sql %s. TOO_FEW_ARGUMENTS = §cYou did not provide enough arguments. COORD_INVALID = §cThe provided coordinate ('%s', '%s') is invalid! CENTER_UPDATED = §9The center for world §2%s §9has been set to §2(%s, %s)§9. -CENTER_INFO = §9The center for world §2%s §9is §2(%s, %s)§9. \ No newline at end of file +CENTER_INFO = §9The center for world §2%s §9is §2(%s, %s)§9. + +PLUGIN_DETECTED = Detected %s version %s \ No newline at end of file diff --git a/src/main/resources/i18n/de.i18n.properties b/src/main/resources/i18n/de.i18n.properties index 02b0995..1307e88 100644 --- a/src/main/resources/i18n/de.i18n.properties +++ b/src/main/resources/i18n/de.i18n.properties @@ -51,4 +51,6 @@ UPDATE_TABLE_FAILED = Fehler beim Updaten der Tabelle %s mit sql %s. TOO_FEW_ARGUMENTS = §cDu hast nicht genug arguments angegeben. COORD_INVALID = §cDie Koordinate ('%s', '%s') ist ungültig! CENTER_UPDATED = §9Die Mitte der Welt §2%s §9wurde auf §2(%s, %s)§9 gesetzt. -CENTER_INFO = §9Die Mitte der Welt §2%s §9ist §2(%s, %s)§9. \ No newline at end of file +CENTER_INFO = §9Die Mitte der Welt §2%s §9ist §2(%s, %s)§9. + +PLUGIN_DETECTED = Plugin %s in der Version %s gefunden! \ No newline at end of file diff --git a/src/main/resources/i18n/en.i18n.properties b/src/main/resources/i18n/en.i18n.properties index 1b70fdc..fac0f79 100644 --- a/src/main/resources/i18n/en.i18n.properties +++ b/src/main/resources/i18n/en.i18n.properties @@ -51,4 +51,6 @@ UPDATE_TABLE_FAILED = Failed to update table %s with sql %s. TOO_FEW_ARGUMENTS = §cYou did not provide enough arguments. COORD_INVALID = §cThe provided coordinate ('%s', '%s') is invalid! CENTER_UPDATED = §9The center for world §2%s §9has been set to §2(%s, %s)§9. -CENTER_INFO = §9The center for world §2%s §9is §2(%s, %s)§9. \ No newline at end of file +CENTER_INFO = §9The center for world §2%s §9is §2(%s, %s)§9. + +PLUGIN_DETECTED = Detected %s version %s \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index f61a978..8bf099a 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -5,6 +5,9 @@ description: Chunk commands plugin. author: Trivernis website: trivernis.net api-version: '1.14' +database: true +softdepend: + - dynmap commands: chunkmaster: description: Main command