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.
pull/29/head
trivernis 4 years ago
parent 832b1c4b55
commit 8953b63c2b

@ -12,6 +12,15 @@ is lower than 2 (configurable).
- [Sqlite JDBC](https://bitbucket.org/xerial/sqlite-jdbc/) - Database Driver for JDBC - [Sqlite JDBC](https://bitbucket.org/xerial/sqlite-jdbc/) - Database Driver for JDBC
- [bStats](https://bstats.org/) - Statistical Insights - [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 ## Installing
Just download the jar from the latest release and place it into the servers plugins folder. 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. # For built-in support please create a PullRequest with your translation.
language: en 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 <true/false>
dynmap: true
generation: generation:
# If set to true the plugin ignores the vanilla world border and doesn't stop # If set to true the plugin ignores the vanilla world border and doesn't stop

@ -42,6 +42,11 @@ repositories {
name 'CodeMc' name 'CodeMc'
url 'https://repo.codemc.org/repository/maven-public' url 'https://repo.codemc.org/repository/maven-public'
} }
maven {
name 'mikeprimm'
url 'http://repo.mikeprimm.com'
}
} }
dependencies { dependencies {
@ -49,6 +54,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
testCompile group: 'junit', name: 'junit', version: '4.12' testCompile group: 'junit', name: 'junit', version: '4.12'
compileOnly "com.destroystokyo.paper:paper-api:1.14.4-R0.1-SNAPSHOT" 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 group: 'org.xerial', name: 'sqlite-jdbc', version: '3.28.0'
compile "io.papermc:paperlib:1.0.2" compile "io.papermc:paperlib:1.0.2"
compile "org.bstats:bstats-bukkit:1.5" compile "org.bstats:bstats-bukkit:1.5"

@ -8,6 +8,8 @@ import net.trivernis.chunkmaster.lib.SqliteManager
import org.bstats.bukkit.Metrics import org.bstats.bukkit.Metrics
import org.bukkit.plugin.java.JavaPlugin import org.bukkit.plugin.java.JavaPlugin
import org.bukkit.scheduler.BukkitTask import org.bukkit.scheduler.BukkitTask
import org.dynmap.DynmapAPI
import org.dynmap.DynmapCommonAPI
import java.lang.Exception import java.lang.Exception
class Chunkmaster: JavaPlugin() { class Chunkmaster: JavaPlugin() {
@ -15,6 +17,8 @@ class Chunkmaster: JavaPlugin() {
lateinit var generationManager: GenerationManager lateinit var generationManager: GenerationManager
lateinit var langManager: LanguageManager lateinit var langManager: LanguageManager
private lateinit var tpsTask: BukkitTask private lateinit var tpsTask: BukkitTask
var dynmapApi: DynmapAPI? = null
private set
var mspt = 20 // keep track of the milliseconds per tick var mspt = 20 // keep track of the milliseconds per tick
private set private set
@ -29,6 +33,9 @@ class Chunkmaster: JavaPlugin() {
langManager = LanguageManager(this) langManager = LanguageManager(this)
langManager.loadProperties() langManager.loadProperties()
this.dynmapApi = getDynmap()
initDatabase() initDatabase()
generationManager = GenerationManager(this, server) generationManager = GenerationManager(this, server)
generationManager.init() generationManager.init()
@ -75,6 +82,7 @@ class Chunkmaster: JavaPlugin() {
config.addDefault("generation.ignore-worldborder", false) config.addDefault("generation.ignore-worldborder", false)
config.addDefault("database.filename", "chunkmaster.db") config.addDefault("database.filename", "chunkmaster.db")
config.addDefault("language", "en") config.addDefault("language", "en")
config.addDefault("dynmap", true)
config.options().copyDefaults(true) config.options().copyDefaults(true)
saveConfig() saveConfig()
} }
@ -92,4 +100,14 @@ class Chunkmaster: JavaPlugin() {
logger.warning(langManager.getLocalized("DB_INIT_EROR", e.message!!)) 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
}
}
} }

@ -27,6 +27,7 @@ class CmdReload(private val chunkmaster: Chunkmaster): Subcommand {
chunkmaster.generationManager.stopAll() chunkmaster.generationManager.stopAll()
chunkmaster.reloadConfig() chunkmaster.reloadConfig()
chunkmaster.generationManager.startAll() chunkmaster.generationManager.startAll()
chunkmaster.langManager.loadProperties()
sender.sendMessage(chunkmaster.langManager.getLocalized("CONFIG_RELOADED")) sender.sendMessage(chunkmaster.langManager.getLocalized("CONFIG_RELOADED"))
return true return true
} }

@ -3,16 +3,20 @@ package net.trivernis.chunkmaster.lib.generation
import net.trivernis.chunkmaster.Chunkmaster import net.trivernis.chunkmaster.Chunkmaster
import net.trivernis.chunkmaster.lib.Spiral import net.trivernis.chunkmaster.lib.Spiral
import org.bukkit.Chunk import org.bukkit.Chunk
import org.bukkit.Location
import org.bukkit.World import org.bukkit.World
import kotlin.math.*
/** /**
* Interface for generation tasks. * 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 { Runnable {
abstract val stopAfter: Int abstract val stopAfter: Int
abstract val world: World abstract val world: World
abstract val count: Int abstract val count: Int
abstract val endReached: Boolean abstract var endReached: Boolean
protected val spiral: Spiral = protected val spiral: Spiral =
Spiral(Pair(centerChunk.x, centerChunk.z), Pair(startChunk.x, startChunk.z)) 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 msptThreshold = plugin.config.getLong("generation.mspt-pause-threshold")
protected val maxLoadedChunks = plugin.config.getInt("generation.max-loaded-chunks") protected val maxLoadedChunks = plugin.config.getInt("generation.max-loaded-chunks")
protected val chunksPerStep = plugin.config.getInt("generation.chunks-per-step") 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 protected var endReachedCallback: ((GenerationTask) -> Unit)? = null
private set 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 override fun run()
abstract fun cancel() abstract fun cancel()
@ -56,6 +64,77 @@ abstract class GenerationTask(plugin: Chunkmaster, centerChunk: ChunkCoordinates
|| (stopAfter in 1..count) || (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<ChunkCoordinates, ChunkCoordinates> {
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 * Registers end reached callback
*/ */

@ -18,7 +18,10 @@ class GenerationTaskPaper(
override var count = 0 override var count = 0
private set private set
override var endReached: Boolean = false override var endReached: Boolean = false
private set
init {
updateDynmapMarker()
}
/** /**
* Runs the generation task. Every Iteration the next chunk will be generated if * Runs the generation task. Every Iteration the next chunk will be generated if
@ -28,16 +31,10 @@ class GenerationTaskPaper(
override fun run() { override fun run() {
if (plugin.mspt < msptThreshold) { // pause when tps < 2 if (plugin.mspt < msptThreshold) { // pause when tps < 2
if (loadedChunks.size > maxLoadedChunks) { if (loadedChunks.size > maxLoadedChunks) {
for (chunk in loadedChunks) { unloadLoadedChunks()
if (chunk.isLoaded) {
chunk.unload(true)
}
}
loadedChunks.clear()
} else if (pendingChunks.size < maxPendingChunks) { // if more than 10 chunks are pending, wait. } else if (pendingChunks.size < maxPendingChunks) { // if more than 10 chunks are pending, wait.
if (borderReached()) { if (borderReached()) {
endReached = true setEndReached()
endReachedCallback?.invoke(this)
return return
} }
@ -73,13 +70,14 @@ class GenerationTaskPaper(
* This unloads all chunks that were generated but not unloaded yet. * This unloads all chunks that were generated but not unloaded yet.
*/ */
override fun cancel() { override fun cancel() {
updateDynmapMarker(true)
unloadAllChunks() unloadAllChunks()
} }
/** /**
* Cancels all pending chunks and unloads all loaded chunks. * Cancels all pending chunks and unloads all loaded chunks.
*/ */
fun unloadAllChunks() { private fun unloadAllChunks() {
for (pendingChunk in pendingChunks) { for (pendingChunk in pendingChunks) {
if (pendingChunk.isDone) { if (pendingChunk.isDone) {
loadedChunks.add(pendingChunk.get()) loadedChunks.add(pendingChunk.get())

@ -14,7 +14,10 @@ class GenerationTaskSpigot(
override var count = 0 override var count = 0
private set private set
override var endReached: Boolean = false override var endReached: Boolean = false
private set
init {
updateDynmapMarker()
}
/** /**
* Runs the generation task. Every Iteration the next chunk will be generated if * Runs the generation task. Every Iteration the next chunk will be generated if
@ -24,16 +27,10 @@ class GenerationTaskSpigot(
override fun run() { override fun run() {
if (plugin.mspt < msptThreshold) { // pause when tps < 2 if (plugin.mspt < msptThreshold) { // pause when tps < 2
if (loadedChunks.size > maxLoadedChunks) { if (loadedChunks.size > maxLoadedChunks) {
for (chunk in loadedChunks) { unloadLoadedChunks()
if (chunk.isLoaded) {
chunk.unload(true)
}
}
loadedChunks.clear()
} else { } else {
if (borderReached()) { if (borderReached()) {
endReached = true setEndReached()
endReachedCallback?.invoke(this)
return return
} }
@ -70,5 +67,6 @@ class GenerationTaskSpigot(
} }
} }
} }
updateDynmapMarker(true)
} }
} }

@ -51,4 +51,6 @@ UPDATE_TABLE_FAILED = Failed to update table %s with sql %s.
TOO_FEW_ARGUMENTS = §cYou did not provide enough arguments. TOO_FEW_ARGUMENTS = §cYou did not provide enough arguments.
COORD_INVALID = §cThe provided coordinate ('%s', '%s') is invalid! 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_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. CENTER_INFO = §9The center for world §2%s §9is §2(%s, %s)§9.
PLUGIN_DETECTED = Detected %s version %s

@ -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. TOO_FEW_ARGUMENTS = §cDu hast nicht genug arguments angegeben.
COORD_INVALID = §cDie Koordinate ('%s', '%s') ist ungültig! 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_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. CENTER_INFO = §9Die Mitte der Welt §2%s §9ist §2(%s, %s)§9.
PLUGIN_DETECTED = Plugin %s in der Version %s gefunden!

@ -51,4 +51,6 @@ UPDATE_TABLE_FAILED = Failed to update table %s with sql %s.
TOO_FEW_ARGUMENTS = §cYou did not provide enough arguments. TOO_FEW_ARGUMENTS = §cYou did not provide enough arguments.
COORD_INVALID = §cThe provided coordinate ('%s', '%s') is invalid! 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_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. CENTER_INFO = §9The center for world §2%s §9is §2(%s, %s)§9.
PLUGIN_DETECTED = Detected %s version %s

@ -5,6 +5,9 @@ description: Chunk commands plugin.
author: Trivernis author: Trivernis
website: trivernis.net website: trivernis.net
api-version: '1.14' api-version: '1.14'
database: true
softdepend:
- dynmap
commands: commands:
chunkmaster: chunkmaster:
description: Main command description: Main command

Loading…
Cancel
Save