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

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

@ -1,11 +1,10 @@
package net.trivernis.chunkmaster.lib package net.trivernis.chunkmaster.lib
import javafx.concurrent.Task import io.papermc.lib.PaperLib
import net.trivernis.chunkmaster.Chunkmaster import net.trivernis.chunkmaster.Chunkmaster
import org.bukkit.Chunk import org.bukkit.Chunk
import org.bukkit.Server import org.bukkit.Server
import org.bukkit.World import org.bukkit.World
import org.bukkit.scheduler.BukkitTask
import java.lang.Exception import java.lang.Exception
import java.lang.NullPointerException 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 { fun addTask(world: World, stopAfter: Int = -1): Int {
val centerChunk = world.getChunkAt(world.spawnLocation) 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(""" val insertStatement = chunkmaster.sqliteConnection.prepareStatement("""
INSERT INTO generation_tasks (center_x, center_z, last_x, last_z, world, stop_after) INSERT INTO generation_tasks (center_x, center_z, last_x, last_z, world, stop_after)
values (?, ?, ?, ?, ?, ?) 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) { private fun resumeTask(world: World, center: Chunk, last: Chunk, id: Int, stopAfter: Int = -1) {
if (!paused) { if (!paused) {
chunkmaster.logger.info("Resuming chunk generation task for world \"${world.name}\"") 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, val task = server.scheduler.runTaskTimer(chunkmaster, generationTask, 10,
chunkmaster.config.getLong("generation.period")) chunkmaster.config.getLong("generation.period"))
tasks.add(TaskEntry(id, task, generationTask)) tasks.add(TaskEntry(id, task, generationTask))
@ -174,7 +174,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
val genTask = task.generationTask val genTask = task.generationTask
server.consoleSender.sendMessage("""Task #${task.id} running for "${genTask.world.name}". server.consoleSender.sendMessage("""Task #${task.id} running for "${genTask.world.name}".
|Progress ${task.generationTask.count} chunks |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', ' ')) |Last Chunk: ${genTask.lastChunk.x}, ${genTask.lastChunk.z}""".trimMargin("|").replace('\n', ' '))
val updateStatement = chunkmaster.sqliteConnection.prepareStatement(""" val updateStatement = chunkmaster.sqliteConnection.prepareStatement("""
UPDATE generation_tasks SET last_x = ?, last_z = ? 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 net.trivernis.chunkmaster.Chunkmaster
import org.bukkit.Chunk import org.bukkit.Chunk
import org.bukkit.World 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, * Interface for generation tasks.
val stopAfter: Int = -1): Runnable { */
private val spiral: Spiral = Spiral(Pair(centerChunk.x, centerChunk.z), Pair(startChunk.x, startChunk.z)) abstract class GenerationTask(plugin: Chunkmaster, centerChunk: Chunk, startChunk: Chunk) : Runnable {
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")
var count = 0 abstract val stopAfter: Int
private set abstract val world: World
var lastChunk: Chunk = startChunk abstract val count: Int
private set abstract val lastChunk: Chunk
var endReached: Boolean = false abstract val endReached: Boolean
private set
/** protected val spiral: Spiral = Spiral(Pair(centerChunk.x, centerChunk.z), Pair(startChunk.x, startChunk.z))
* Runs the generation task. Every Iteration the next chunk will be generated if protected val loadedChunks: HashSet<Chunk> = HashSet()
* it hasn't been generated already. protected val chunkSkips = plugin.config.getInt("generation.chunks-skips-per-step")
* After 10 chunks have been generated, they will all be unloaded and saved. protected val msptThreshold = plugin.config.getLong("generation.mspt-pause-threshold")
*/
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)
for (i in 1 until chunkSkips) { abstract override fun run()
if (world.isChunkGenerated(chunk.x, chunk.z)) { abstract fun cancel()
nextChunkCoords = spiral.next() // if the chunk is generated skip 10 chunks per step
chunk = world.getChunkAt(nextChunkCoords.first, nextChunkCoords.second)
} else {
break
}
}
if (!world.isChunkGenerated(chunk.x, chunk.z)) { val nextChunk: Chunk
chunk.load(true) get() {
loadedChunks.add(chunk) val nextChunkCoords = spiral.next()
} return world.getChunkAt(nextChunkCoords.first, nextChunkCoords.second)
lastChunk = chunk
count = spiral.count // set the count to the more accurate spiral count
}
}
} }
/** protected fun borderReached(): Boolean {
* Cancels the generation task. return !world.worldBorder.isInside(lastChunk.getBlock(8, 0, 8).location) || (stopAfter in 1..count)
* This unloads all chunks that were generated but not unloaded yet.
*/
fun cancel() {
for (chunk in loadedChunks) {
if (chunk.isLoaded) {
chunk.unload(true)
}
}
} }
} }

@ -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