Performance and stability improvements

- improved performance of paper generation task
- improved stability of spigot generation task
- Updated Readme
- added configuration parameters for max pending chunks and max loaded chunks
pull/1/head
Trivernis 5 years ago
parent c49036e35b
commit 5a5fe74d9c

@ -22,11 +22,24 @@ All features can be accessed with the command `/chunkmaster` or the aliases `/ch
```yaml ```yaml
generation: 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. # The period (in ticks) in which a generation step is run.
# Higher values mean less performance impact but slower generation. # Higher values mean less performance impact but slower generation.
# The value should be a positive integer. # The value should be a positive integer.
period: 2 period: 2
# Paper Only
# The number of already generated chunks that will be skipped for each step. # 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 # Notice that these still have a performance impact because the server needs to check
# if the chunk is generated. # if the chunk is generated.
@ -47,4 +60,10 @@ generation:
# process is minimal. # process is minimal.
# The value should be a boolean <true/false> # The value should be a boolean <true/false>
pause-on-join: true 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).

@ -56,9 +56,11 @@ class Chunkmaster: JavaPlugin() {
private fun configure() { private fun configure() {
dataFolder.mkdir() dataFolder.mkdir()
config.addDefault("generation.period", 2L) config.addDefault("generation.period", 2L)
config.addDefault("generation.chunks-skips-per-step", 4) config.addDefault("generation.chunks-skips-per-step", 10)
config.addDefault("generation.mspt-pause-threshold", 500L) config.addDefault("generation.mspt-pause-threshold", 500L)
config.addDefault("generation.pause-on-join", true) config.addDefault("generation.pause-on-join", true)
config.addDefault("generation.max-pending-chunks", 10)
config.addDefault("generation.max-loaded-chunks", 10)
config.options().copyDefaults(true) config.options().copyDefaults(true)
saveConfig() saveConfig()
} }

@ -1,4 +1,10 @@
package net.trivernis.chunkmaster.lib.generation package net.trivernis.chunkmaster.lib.generation
data class ChunkCoordinates(val x: Int, val z: Int) { 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())
}
} }

@ -13,14 +13,15 @@ abstract class GenerationTask(plugin: Chunkmaster, centerChunk: Chunk, startChun
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 lastChunk: Chunk
abstract val endReached: Boolean abstract val 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))
protected val loadedChunks: HashSet<Chunk> = HashSet() protected val loadedChunks: HashSet<Chunk> = HashSet()
protected var lastChunkCoords = ChunkCoordinates(startChunk.x, startChunk.z)
protected val chunkSkips = plugin.config.getInt("generation.chunks-skips-per-step") protected val chunkSkips = plugin.config.getInt("generation.chunks-skips-per-step")
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")
abstract override fun run() abstract override fun run()
abstract fun cancel() abstract fun cancel()
@ -30,13 +31,21 @@ abstract class GenerationTask(plugin: Chunkmaster, centerChunk: Chunk, startChun
val nextChunkCoords = spiral.next() val nextChunkCoords = spiral.next()
return ChunkCoordinates(nextChunkCoords.first, nextChunkCoords.second) return ChunkCoordinates(nextChunkCoords.first, nextChunkCoords.second)
} }
var lastChunk: Chunk = startChunk
get() {
return world.getChunkAt(lastChunkCoords.x, lastChunkCoords.z)
}
private set
val nextChunk: Chunk val nextChunk: Chunk
get() { get() {
val next = nextChunkCoordinates val next = nextChunkCoordinates
return world.getChunkAt(next.x, next.z) 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 { protected fun borderReached(): Boolean {
return !world.worldBorder.isInside(lastChunk.getBlock(8, 0, 8).location) || (stopAfter in 1..count) return !world.worldBorder.isInside(lastChunkCoords.getCenterLocation(world)) || (stopAfter in 1..count)
} }
} }

@ -12,20 +12,15 @@ class GenerationTaskPaper(
override val stopAfter: Int = -1 override val stopAfter: Int = -1
) : GenerationTask(plugin, centerChunk, startChunk) { ) : GenerationTask(plugin, centerChunk, startChunk) {
private val maxPendingChunks = plugin.config.getInt("generation.max-pending-chunks")
private val pendingChunks = HashSet<CompletableFuture<Chunk>>() private val pendingChunks = HashSet<CompletableFuture<Chunk>>()
override var count = 0 override var count = 0
private set private set
override var lastChunk: Chunk = startChunk
get() {
return world.getChunkAt(lastChunkCoords.x, lastChunkCoords.z)
}
private set
override var endReached: Boolean = false override var endReached: Boolean = false
private set private set
private var lastChunkCoords = ChunkCoordinates(startChunk.x, startChunk.z)
/** /**
* 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
* it hasn't been generated already. * it hasn't been generated already.
@ -33,13 +28,14 @@ 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 > 10) { if (loadedChunks.size > maxLoadedChunks) {
for (chunk in loadedChunks) { for (chunk in loadedChunks) {
if (chunk.isLoaded) { if (chunk.isLoaded) {
chunk.unload(true) chunk.unload(true)
} }
} }
} else if (pendingChunks.size < 10) { // if more than 10 chunks are pending, wait. loadedChunks.clear()
} else if (pendingChunks.size < maxPendingChunks) { // if more than 10 chunks are pending, wait.
if (borderReached()) { if (borderReached()) {
endReached = true endReached = true
return return

@ -13,8 +13,6 @@ class GenerationTaskSpigot(
override var count = 0 override var count = 0
private set private set
override var lastChunk: Chunk = startChunk
private set
override var endReached: Boolean = false override var endReached: Boolean = false
private set private set
@ -25,33 +23,27 @@ 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 > 10) { if (loadedChunks.size > maxLoadedChunks) {
for (chunk in loadedChunks) { for (chunk in loadedChunks) {
if (chunk.isLoaded) { if (chunk.isLoaded) {
chunk.unload(true) chunk.unload(true)
} }
} }
loadedChunks.clear()
} else { } else {
if (borderReached()) { if (borderReached()) {
endReached = true endReached = true
return return
} }
var chunk = nextChunk val chunk = nextChunkCoordinates
for (i in 1 until chunkSkips) {
if (world.isChunkGenerated(chunk.x, chunk.z)) {
chunk = nextChunk
} else {
break
}
}
if (!world.isChunkGenerated(chunk.x, chunk.z)) { if (!world.isChunkGenerated(chunk.x, chunk.z)) {
chunk.load(true) val chunkInstance = world.getChunkAt(chunk.x, chunk.z)
loadedChunks.add(chunk) chunkInstance.load(true)
loadedChunks.add(chunkInstance)
} }
lastChunk = chunk lastChunkCoords = chunk
count = spiral.count // set the count to the more accurate spiral count count = spiral.count // set the count to the more accurate spiral count
} }
} }

Loading…
Cancel
Save