Generation task seperation

- Added interface GenerationTask
- added method to GenerationManager that creates a task depending on the server type
- added generation task for paper and spigot (to profit from async chunk loading)
pull/1/head
Trivernis 5 years ago
parent 726e4ba34c
commit 08bec14df9

@ -1,3 +1,12 @@
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "com.github.jengelman.gradle.plugins:shadow:2.0.2"
}
}
plugins {
id 'idea'
@ -13,7 +22,7 @@ idea {
}
group "net.trivernis"
version "1.0-SNAPSHOT"
version "0.10-beta"
sourceCompatibility = 1.8
@ -28,6 +37,10 @@ repositories {
maven {
url 'https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc'
}
maven {
name 'papermc'
url 'https://papermc.io/repo/repository/maven-public/'
}
}
dependencies {
@ -36,16 +49,24 @@ dependencies {
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"
}
apply plugin: "com.github.johnrengelman.shadow"
shadowJar {
relocate 'io.papermc.lib', 'net.trivernis.chunkmaster.paperlib'
}
jar {
from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"

@ -1,5 +1,6 @@
package net.trivernis.chunkmaster
import io.papermc.lib.PaperLib
import net.trivernis.chunkmaster.commands.*
import net.trivernis.chunkmaster.lib.GenerationManager
import net.trivernis.chunkmaster.lib.SqlUpdateManager
@ -21,6 +22,7 @@ class Chunkmaster: JavaPlugin() {
* On enable of the plugin
*/
override fun onEnable() {
PaperLib.suggestPaper(this)
configure()
initDatabase()
generationManager = GenerationManager(this, server)

@ -1,11 +1,10 @@
package net.trivernis.chunkmaster.lib
import javafx.concurrent.Task
import io.papermc.lib.PaperLib
import net.trivernis.chunkmaster.Chunkmaster
import org.bukkit.Chunk
import org.bukkit.Server
import org.bukkit.World
import org.bukkit.scheduler.BukkitTask
import java.lang.Exception
import java.lang.NullPointerException
@ -20,7 +19,8 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
*/
fun addTask(world: World, stopAfter: Int = -1): Int {
val centerChunk = world.getChunkAt(world.spawnLocation)
val generationTask = GenerationTask(chunkmaster, world, centerChunk, centerChunk, stopAfter)
val generationTask = createGenerationTask(world, centerChunk, centerChunk, stopAfter)
val insertStatement = chunkmaster.sqliteConnection.prepareStatement("""
INSERT INTO generation_tasks (center_x, center_z, last_x, last_z, world, stop_after)
values (?, ?, ?, ?, ?, ?)
@ -59,7 +59,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
private fun resumeTask(world: World, center: Chunk, last: Chunk, id: Int, stopAfter: Int = -1) {
if (!paused) {
chunkmaster.logger.info("Resuming chunk generation task for world \"${world.name}\"")
val generationTask = GenerationTask(chunkmaster, world, center, last, stopAfter)
val generationTask = createGenerationTask(world, center, last, stopAfter)
val task = server.scheduler.runTaskTimer(chunkmaster, generationTask, 10,
chunkmaster.config.getLong("generation.period"))
tasks.add(TaskEntry(id, task, generationTask))
@ -174,7 +174,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
val genTask = task.generationTask
server.consoleSender.sendMessage("""Task #${task.id} running for "${genTask.world.name}".
|Progress ${task.generationTask.count} chunks
|${if (task.generationTask.stopAfter > 0)"(${(task.generationTask.count.toDouble()/task.generationTask.stopAfter.toDouble())*100}%)." else ""}
|${if (task.generationTask.stopAfter > 0)"(${(task.generationTask.count.toDouble()/task.generationTask.stopAfter.toDouble())*100}%)" else ""}.
|Last Chunk: ${genTask.lastChunk.x}, ${genTask.lastChunk.z}""".trimMargin("|").replace('\n', ' '))
val updateStatement = chunkmaster.sqliteConnection.prepareStatement("""
UPDATE generation_tasks SET last_x = ?, last_z = ?
@ -195,4 +195,16 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
}
}
}
/**
* 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: Chunk, start: Chunk, stopAfter: Int): GenerationTask {
return if (PaperLib.isPaper()) {
GenerationTaskPaper(chunkmaster, world, center, start, stopAfter)
} else {
GenerationTaskSpigot(chunkmaster, world, center, start, stopAfter)
}
}
}

@ -3,73 +3,33 @@ package net.trivernis.chunkmaster.lib
import net.trivernis.chunkmaster.Chunkmaster
import org.bukkit.Chunk
import org.bukkit.World
import org.bukkit.scheduler.BukkitRunnable
import org.bukkit.scheduler.BukkitTask
class GenerationTask(private val plugin: Chunkmaster, val world: World,
centerChunk: Chunk, private val startChunk: Chunk,
val stopAfter: Int = -1): Runnable {
private val spiral: Spiral = Spiral(Pair(centerChunk.x, centerChunk.z), Pair(startChunk.x, startChunk.z))
private val loadedChunks: HashSet<Chunk> = HashSet()
private val chunkSkips = plugin.config.getInt("generation.chunks-skips-per-step")
private val msptThreshold = plugin.config.getLong("generation.mspt-pause-threshold")
/**
* Interface for generation tasks.
*/
abstract class GenerationTask(plugin: Chunkmaster, centerChunk: Chunk, startChunk: Chunk) : Runnable {
var count = 0
private set
var lastChunk: Chunk = startChunk
private set
var endReached: Boolean = false
private set
abstract val stopAfter: Int
abstract val world: World
abstract val count: Int
abstract val lastChunk: Chunk
abstract val endReached: Boolean
/**
* 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 > 10) {
for (chunk in loadedChunks) {
if (chunk.isLoaded) {
chunk.unload(true)
}
}
} else {
if (!world.worldBorder.isInside(lastChunk.getBlock(8, 0, 8).location) || (stopAfter in 1..count)) {
endReached = true
return
}
var nextChunkCoords = spiral.next()
var chunk = world.getChunkAt(nextChunkCoords.first, nextChunkCoords.second)
protected val spiral: Spiral = Spiral(Pair(centerChunk.x, centerChunk.z), Pair(startChunk.x, startChunk.z))
protected val loadedChunks: HashSet<Chunk> = HashSet()
protected val chunkSkips = plugin.config.getInt("generation.chunks-skips-per-step")
protected val msptThreshold = plugin.config.getLong("generation.mspt-pause-threshold")
for (i in 1 until chunkSkips) {
if (world.isChunkGenerated(chunk.x, chunk.z)) {
nextChunkCoords = spiral.next() // if the chunk is generated skip 10 chunks per step
chunk = world.getChunkAt(nextChunkCoords.first, nextChunkCoords.second)
} else {
break
}
}
abstract override fun run()
abstract fun cancel()
if (!world.isChunkGenerated(chunk.x, chunk.z)) {
chunk.load(true)
loadedChunks.add(chunk)
}
lastChunk = chunk
count = spiral.count // set the count to the more accurate spiral count
}
val nextChunk: Chunk
get() {
val nextChunkCoords = spiral.next()
return world.getChunkAt(nextChunkCoords.first, nextChunkCoords.second)
}
}
/**
* Cancels the generation task.
* This unloads all chunks that were generated but not unloaded yet.
*/
fun cancel() {
for (chunk in loadedChunks) {
if (chunk.isLoaded) {
chunk.unload(true)
}
}
protected fun borderReached(): Boolean {
return !world.worldBorder.isInside(lastChunk.getBlock(8, 0, 8).location) || (stopAfter in 1..count)
}
}

@ -0,0 +1,97 @@
package net.trivernis.chunkmaster.lib
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: Chunk, private val startChunk: Chunk,
override val stopAfter: Int = -1
) : GenerationTask(plugin, centerChunk, startChunk) {
private val pendingChunks = HashSet<CompletableFuture<Chunk>>()
override var count = 0
private set
override var lastChunk: Chunk = startChunk
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 > 10) {
for (chunk in loadedChunks) {
if (chunk.isLoaded) {
chunk.unload(true)
}
}
} else if (pendingChunks.size < 10) { // if more than 10 chunks are pending, wait.
if (borderReached()) {
endReached = true
return
}
var chunk = nextChunk
for (i in 1 until chunkSkips) {
if (PaperLib.isChunkGenerated(world, chunk.x, chunk.z)) {
chunk = nextChunk
} else {
break
}
}
if (!PaperLib.isChunkGenerated(world, chunk.x, chunk.z)) {
pendingChunks.add(PaperLib.getChunkAtAsync(world, chunk.x, chunk.z, true))
}
lastChunk = 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() {
for (pendingChunk in pendingChunks) {
if (pendingChunk.isDone) {
loadedChunks.add(pendingChunk.get())
} else {
pendingChunk.cancel(true)
}
}
pendingChunks.clear()
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)
}
}

@ -0,0 +1,74 @@
package net.trivernis.chunkmaster.lib
import net.trivernis.chunkmaster.Chunkmaster
import org.bukkit.Chunk
import org.bukkit.World
import org.bukkit.scheduler.BukkitRunnable
import org.bukkit.scheduler.BukkitTask
import java.util.concurrent.CompletableFuture
class GenerationTaskSpigot(
private val plugin: Chunkmaster, override val world: World,
centerChunk: Chunk, private val startChunk: Chunk,
override val stopAfter: Int = -1
) : GenerationTask(plugin, centerChunk, startChunk) {
override var count = 0
private set
override var lastChunk: Chunk = startChunk
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 > 10) {
for (chunk in loadedChunks) {
if (chunk.isLoaded) {
chunk.unload(true)
}
}
} else {
if (borderReached()) {
endReached = true
return
}
var chunk = nextChunk
for (i in 1 until chunkSkips) {
if (world.isChunkGenerated(chunk.x, chunk.z)) {
chunk = nextChunk
} else {
break
}
}
if (!world.isChunkGenerated(chunk.x, chunk.z)) {
chunk.load(true)
loadedChunks.add(chunk)
}
lastChunk = 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)
}
}
}
}
Loading…
Cancel
Save