Add circlular generation tasks

pull/55/head
trivernis 5 years ago
parent 2eeff350f8
commit 572369192a

@ -5,7 +5,6 @@ import net.trivernis.chunkmaster.lib.Subcommand
import org.bukkit.command.Command import org.bukkit.command.Command
import org.bukkit.command.CommandSender import org.bukkit.command.CommandSender
import org.bukkit.entity.Player import org.bukkit.entity.Player
import kotlin.math.pow
class CmdGenerate(private val chunkmaster: Chunkmaster): Subcommand { class CmdGenerate(private val chunkmaster: Chunkmaster): Subcommand {
override val name = "generate" override val name = "generate"
@ -24,16 +23,17 @@ class CmdGenerate(private val chunkmaster: Chunkmaster): Subcommand {
.map {it.name}.toMutableList() .map {it.name}.toMutableList()
} else if (args.size == 2) { } else if (args.size == 2) {
if (args[0].toIntOrNull() != null) { if (args[0].toIntOrNull() != null) {
return units.filter {it.indexOf(args[1]) == 0}.toMutableList() return shapes.filter {it.indexOf(args[1]) == 0}.toMutableList()
} }
} else if (args.size > 2) { } else if (args.size > 2) {
if (args[1].toIntOrNull() != null) { if (args[1].toIntOrNull() != null) {
return units.filter {it.indexOf(args[2]) == 0}.toMutableList() return shapes.filter {it.indexOf(args[2]) == 0}.toMutableList()
} }
} }
return emptyList<String>().toMutableList() return emptyList<String>().toMutableList()
} }
val units = listOf("blockradius", "radius", "diameter")
val shapes = listOf("circle", "square")
/** /**
@ -41,80 +41,73 @@ class CmdGenerate(private val chunkmaster: Chunkmaster): Subcommand {
*/ */
override fun execute(sender: CommandSender, args: List<String>): Boolean { override fun execute(sender: CommandSender, args: List<String>): Boolean {
var worldName = "" var worldName = ""
var stopAfter = -1 var blockRadius = -1
var shape = "square"
if (sender is Player) { if (sender is Player) {
if (args.isNotEmpty()) {
if (args[0].toIntOrNull() != null) {
stopAfter = args[0].toInt()
worldName = sender.world.name
} else {
worldName = args[0]
}
if (args.size > 1) {
if (args[1].toIntOrNull() != null) {
stopAfter = args[1].toInt()
} else if (args[1] in units && args[0].toIntOrNull() != null) {
stopAfter = getStopAfter(stopAfter, args[1])
} else {
worldName = args[1]
}
}
if (args.size > 2 && args[2] in units && args[1].toIntOrNull() != null) {
stopAfter = getStopAfter(stopAfter, args[2])
}
} else {
worldName = sender.world.name worldName = sender.world.name
} }
if (args.isEmpty()) {
if (sender is Player) {
return createTask(sender, worldName, blockRadius, shape)
} else { } else {
if (args.isNotEmpty()) { sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NAME_REQUIRED"))
worldName = args[0] return false
if (args.size > 1) {
if (args[1].toIntOrNull() != null) {
stopAfter = args[1].toInt()
}
} }
if (args.size > 2 && args[2] in units) {
stopAfter = getStopAfter(stopAfter, args[2])
} }
} else { if (args[0].toIntOrNull() != null) {
if (sender !is Player) {
sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NAME_REQUIRED")) sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NAME_REQUIRED"))
return false return false
} }
} blockRadius = args[0].toInt()
return createTask(sender, worldName, stopAfter) } else {
worldName = args[0]
} }
/** if (args.size == 1) {
* Returns stopAfter for a given unit return createTask(sender, worldName, blockRadius, shape)
*/
private fun getStopAfter(number: Int, unit: String): Int {
if (unit in units) {
return when (unit) {
"radius" -> {
((number * 2)+1).toDouble().pow(2.0).toInt()
} }
"diameter" -> {
number.toDouble().pow(2.0).toInt() when {
args[1].toIntOrNull() != null -> blockRadius = args[1].toInt()
args[1] in shapes -> shape = args[1]
else -> {
sender.sendMessage(chunkmaster.langManager.getLocalized("INVALID_ARGUMENT", 2, args[1]))
return false
} }
"blockradius" -> {
((number.toDouble()+1)/8).pow(2.0).toInt()
} }
else -> number if (args.size == 2) {
return createTask(sender, worldName, blockRadius, shape)
} }
if (args[2] in shapes) {
shape = args[2]
} else {
sender.sendMessage(chunkmaster.langManager.getLocalized("INVALID_ARGUMENT", 3, args[2]))
return false
} }
return number
return createTask(sender, worldName, blockRadius, shape)
} }
/** /**
* Creates the task with the given arguments. * Creates the task with the given arguments.
*/ */
private fun createTask(sender: CommandSender, worldName: String, stopAfter: Int): Boolean { private fun createTask(sender: CommandSender, worldName: String, blockRadius: Int, shape: String): Boolean {
val world = chunkmaster.server.getWorld(worldName) val world = chunkmaster.server.getWorld(worldName)
val allTasks = chunkmaster.generationManager.allTasks val allTasks = chunkmaster.generationManager.allTasks
return if (world != null && (allTasks.find { it.generationTask.world == world }) == null) { return if (world != null && (allTasks.find { it.generationTask.world == world }) == null) {
chunkmaster.generationManager.addTask(world, stopAfter) chunkmaster.generationManager.addTask(world, if (blockRadius > 0) blockRadius/16 else -1 , shape)
sender.sendMessage(chunkmaster.langManager sender.sendMessage(chunkmaster.langManager
.getLocalized("TASK_CREATION_SUCCESS", worldName, if (stopAfter > 0) "$stopAfter chunks" else "WorldBorder")) .getLocalized("TASK_CREATION_SUCCESS",
worldName,
if (blockRadius > 0) {
chunkmaster.langManager.getLocalized("TASK_UNIT_RADIUS", blockRadius)
} else{
chunkmaster.langManager.getLocalized("TASK_UNIT_WORLDBORDER")
},
shape
))
true true
} else if (world == null){ } else if (world == null){
sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NOT_FOUND", worldName)); sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NOT_FOUND", worldName));

@ -1,7 +1,5 @@
package net.trivernis.chunkmaster.commands package net.trivernis.chunkmaster.commands
import net.md_5.bungee.api.ChatColor
import net.md_5.bungee.api.chat.ComponentBuilder
import net.trivernis.chunkmaster.Chunkmaster import net.trivernis.chunkmaster.Chunkmaster
import net.trivernis.chunkmaster.lib.Subcommand import net.trivernis.chunkmaster.lib.Subcommand
import net.trivernis.chunkmaster.lib.generation.TaskEntry import net.trivernis.chunkmaster.lib.generation.TaskEntry
@ -50,8 +48,8 @@ class CmdList(private val chunkmaster: Chunkmaster): Subcommand {
*/ */
private fun getGenerationEntry(task: TaskEntry): String { private fun getGenerationEntry(task: TaskEntry): String {
val genTask = task.generationTask val genTask = task.generationTask
val percentage = if (genTask.stopAfter > 0) val percentage = if (genTask.radius > 0)
" (%.1f".format((genTask.count.toDouble()/genTask.stopAfter.toDouble())*100) + "%)." " (%.1f".format(genTask.shape.progress()*100) + "%)."
else else
"" ""
return "\n" + chunkmaster.langManager.getLocalized("TASKS_ENTRY", return "\n" + chunkmaster.langManager.getLocalized("TASKS_ENTRY",

@ -20,7 +20,8 @@ class SqliteManager(private val chunkmaster: Chunkmaster) {
Pair("last_x", "integer NOT NULL DEFAULT 0"), Pair("last_x", "integer NOT NULL DEFAULT 0"),
Pair("last_z", "integer NOT NULL DEFAULT 0"), Pair("last_z", "integer NOT NULL DEFAULT 0"),
Pair("world", "text UNIQUE NOT NULL DEFAULT 'world'"), Pair("world", "text UNIQUE NOT NULL DEFAULT 'world'"),
Pair("stop_after", "integer DEFAULT -1") Pair("radius", "integer DEFAULT -1"),
Pair("shape", "text NOT NULL DEFAULT 'square'")
) )
), ),
Pair( Pair(

@ -4,6 +4,7 @@ import org.bukkit.Location
import org.dynmap.markers.AreaMarker import org.dynmap.markers.AreaMarker
import org.dynmap.markers.Marker import org.dynmap.markers.Marker
import org.dynmap.markers.MarkerSet import org.dynmap.markers.MarkerSet
import org.dynmap.markers.PolyLineMarker
class ExtendedMarkerSet(private val markerSet: MarkerSet) { class ExtendedMarkerSet(private val markerSet: MarkerSet) {
/** /**
@ -44,6 +45,25 @@ class ExtendedMarkerSet(private val markerSet: MarkerSet) {
return marker return marker
} }
fun creUpdatePolyLineMarker(id: String, label: String, edges: List<Location>, style: MarkerStyle?): PolyLineMarker? {
var marker = markerSet.findPolyLineMarker(id)
val xList = edges.map { it.x }
val yList = edges.map { it.y }
val zList = edges.map { it.z }
if (marker != null) {
marker.setCornerLocations(xList.toDoubleArray(), yList.toDoubleArray(), zList.toDoubleArray())
} else {
marker = markerSet.createPolyLineMarker(id, label, false, edges.first().world.name, xList.toDoubleArray(), yList.toDoubleArray(), zList.toDoubleArray(), true)
}
if (style != null) {
if (style.lineStyle != null) {
marker.setLineStyle(style.lineStyle.weight, style.lineStyle.opacity, style.lineStyle.color)
}
}
return marker
}
/** /**
* Returns the area marker for an id * Returns the area marker for an id
* @param id - the id of the marker * @param id - the id of the marker
@ -52,6 +72,13 @@ class ExtendedMarkerSet(private val markerSet: MarkerSet) {
return markerSet.findAreaMarker(id) return markerSet.findAreaMarker(id)
} }
/**
* Returns the polylinemarker for an id
*/
fun findPolyLineMarker(id: String): PolyLineMarker? {
return markerSet.findPolyLineMarker(id)
}
/** /**
* Deletes an area marker * Deletes an area marker
* @param id - the id of the marker * @param id - the id of the marker
@ -60,4 +87,9 @@ class ExtendedMarkerSet(private val markerSet: MarkerSet) {
val marker = this.findAreaMarker(id) val marker = this.findAreaMarker(id)
marker?.deleteMarker() marker?.deleteMarker()
} }
fun deletePolyLineMarker(id: String) {
val marker = this.findPolyLineMarker(id)
marker?.deleteMarker()
}
} }

@ -4,7 +4,6 @@ import io.papermc.lib.PaperLib
import net.trivernis.chunkmaster.Chunkmaster import net.trivernis.chunkmaster.Chunkmaster
import net.trivernis.chunkmaster.lib.shapes.Circle import net.trivernis.chunkmaster.lib.shapes.Circle
import net.trivernis.chunkmaster.lib.shapes.Spiral import net.trivernis.chunkmaster.lib.shapes.Spiral
import org.bukkit.Chunk
import org.bukkit.Server import org.bukkit.Server
import org.bukkit.World import org.bukkit.World
@ -35,7 +34,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
/** /**
* Adds a generation task * Adds a generation task
*/ */
fun addTask(world: World, stopAfter: Int = -1): Int { fun addTask(world: World, radius: Int = -1, shape: String = "square"): Int {
val foundTask = allTasks.find { it.generationTask.world == world } val foundTask = allTasks.find { it.generationTask.world == world }
if (foundTask == null) { if (foundTask == null) {
val centerChunk = if (worldCenters[world.name] == null) { val centerChunk = if (worldCenters[world.name] == null) {
@ -44,12 +43,12 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
val center = worldCenters[world.name]!! val center = worldCenters[world.name]!!
ChunkCoordinates(center.first, center.second) ChunkCoordinates(center.first, center.second)
} }
val generationTask = createGenerationTask(world, centerChunk, centerChunk, stopAfter) val generationTask = createGenerationTask(world, centerChunk, centerChunk, radius, shape)
chunkmaster.sqliteManager.executeStatement( chunkmaster.sqliteManager.executeStatement(
""" """
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, radius, shape)
values (?, ?, ?, ?, ?, ?) values (?, ?, ?, ?, ?, ?, ?)
""", """,
HashMap( HashMap(
mapOf( mapOf(
@ -58,7 +57,8 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
3 to centerChunk.x, 3 to centerChunk.x,
4 to centerChunk.z, 4 to centerChunk.z,
5 to world.name, 5 to world.name,
6 to stopAfter 6 to radius,
7 to shape
) )
), ),
null null
@ -101,14 +101,15 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
center: ChunkCoordinates, center: ChunkCoordinates,
last: ChunkCoordinates, last: ChunkCoordinates,
id: Int, id: Int,
stopAfter: Int = -1, radius: Int = -1,
delay: Long = 200L delay: Long = 200L,
shape: String = "square"
) { ) {
if (!paused) { if (!paused) {
chunkmaster.logger.info(chunkmaster.langManager.getLocalized("RESUME_FOR_WORLD", world.name)) chunkmaster.logger.info(chunkmaster.langManager.getLocalized("RESUME_FOR_WORLD", world.name))
val generationTask = createGenerationTask(world, center, last, stopAfter) val generationTask = createGenerationTask(world, center, last, radius, shape)
val task = server.scheduler.runTaskTimer( val task = server.scheduler.runTaskTimer(
chunkmaster, generationTask, delay, // 10 sec delay chunkmaster, generationTask, delay,
chunkmaster.config.getLong("generation.period") chunkmaster.config.getLong("generation.period")
) )
tasks.add(RunningTaskEntry(id, task, generationTask)) tasks.add(RunningTaskEntry(id, task, generationTask))
@ -163,7 +164,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
if (!server.onlinePlayers.isEmpty()) { if (!server.onlinePlayers.isEmpty()) {
this.pauseAll() this.pauseAll()
} }
}, 600) }, 20)
} }
/** /**
@ -199,9 +200,10 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
val world = server.getWorld(res.getString("world")) val world = server.getWorld(res.getString("world"))
val center = ChunkCoordinates(res.getInt("center_x"), res.getInt("center_z")) val center = ChunkCoordinates(res.getInt("center_x"), res.getInt("center_z"))
val last = ChunkCoordinates(res.getInt("last_x"), res.getInt("last_z")) val last = ChunkCoordinates(res.getInt("last_x"), res.getInt("last_z"))
val stopAfter = res.getInt("stop_after") val radius = res.getInt("radius")
val shape = res.getString("shape")
if (this.tasks.find { it.id == id } == null) { if (this.tasks.find { it.id == id } == null) {
resumeTask(world!!, center, last, id, stopAfter, 200L + count) resumeTask(world!!, center, last, id, radius, 200L + count, shape)
} }
} catch (error: NullPointerException) { } catch (error: NullPointerException) {
chunkmaster.logger.severe(chunkmaster.langManager.getLocalized("TASK_LOAD_FAILED", res.getInt("id"))) chunkmaster.logger.severe(chunkmaster.langManager.getLocalized("TASK_LOAD_FAILED", res.getInt("id")))
@ -286,12 +288,10 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
for (task in tasks) { for (task in tasks) {
try { try {
val genTask = task.generationTask val genTask = task.generationTask
val speed = task.generationSpeed!! val (speed, chunkSpeed) = task.generationSpeed
val percentage = if (genTask.stopAfter > 0) "(${"%.2f".format( val percentage = if (genTask.radius > 0) "(${"%.2f".format(genTask.shape.progress() * 100)}%)" else ""
(genTask.count.toDouble() / genTask.stopAfter.toDouble()) * 100 val eta = if (genTask.radius > 0 && speed!! > 0) {
)}%)" else "" val etaSeconds = (genTask.shape.progress())/speed
val eta = if (genTask.stopAfter > 0 && speed > 0) {
val etaSeconds = (genTask.stopAfter - genTask.count).toDouble()/speed
val hours: Int = (etaSeconds/3600).toInt() val hours: Int = (etaSeconds/3600).toInt()
val minutes: Int = ((etaSeconds % 3600) / 60).toInt() val minutes: Int = ((etaSeconds % 3600) / 60).toInt()
val seconds: Int = (etaSeconds % 60).toInt() val seconds: Int = (etaSeconds % 60).toInt()
@ -306,7 +306,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
genTask.count, genTask.count,
percentage, percentage,
eta, eta,
speed, chunkSpeed!!,
genTask.lastChunk.x, genTask.lastChunk.x,
genTask.lastChunk.z)) genTask.lastChunk.z))
saveProgressToDatabase(genTask.lastChunkCoords, task.id) saveProgressToDatabase(genTask.lastChunkCoords, task.id)
@ -339,13 +339,19 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
world: World, world: World,
center: ChunkCoordinates, center: ChunkCoordinates,
start: ChunkCoordinates, start: ChunkCoordinates,
stopAfter: Int radius: Int,
shapeName: String
): GenerationTask { ): GenerationTask {
val shape = Spiral(Pair(center.x, center.z), Pair(center.x, center.z)) val shape = when (shapeName) {
"circle" -> Circle(Pair(center.x, center.z), Pair(center.x, center.z), radius)
"square" -> Spiral(Pair(center.x, center.z), Pair(center.x, center.z), radius)
else -> Spiral(Pair(center.x, center.z), Pair(center.x, center.z), radius)
}
return if (PaperLib.isPaper()) { return if (PaperLib.isPaper()) {
GenerationTaskPaper(chunkmaster, world, center, start, stopAfter, shape) GenerationTaskPaper(chunkmaster, world, start, radius, shape)
} else { } else {
GenerationTaskSpigot(chunkmaster, world, center, start, stopAfter, shape) GenerationTaskSpigot(chunkmaster, world, center, start, radius, shape)
} }
} }
} }

@ -1,22 +1,24 @@
package net.trivernis.chunkmaster.lib.generation package net.trivernis.chunkmaster.lib.generation
import net.trivernis.chunkmaster.Chunkmaster import net.trivernis.chunkmaster.Chunkmaster
import net.trivernis.chunkmaster.lib.shapes.Spiral
import net.trivernis.chunkmaster.lib.dynmap.* import net.trivernis.chunkmaster.lib.dynmap.*
import net.trivernis.chunkmaster.lib.shapes.Shape import net.trivernis.chunkmaster.lib.shapes.Shape
import org.bukkit.Chunk import org.bukkit.Chunk
import org.bukkit.World import org.bukkit.World
import kotlin.math.*
/** /**
* Interface for generation tasks. * Interface for generation tasks.
*/ */
abstract class GenerationTask(plugin: Chunkmaster, private val centerChunk: ChunkCoordinates, startChunk: ChunkCoordinates, protected val shape: Shape) : abstract class GenerationTask(
plugin: Chunkmaster,
startChunk: ChunkCoordinates,
val shape: Shape
) :
Runnable { Runnable {
abstract val stopAfter: Int abstract val radius: Int
abstract val world: World abstract val world: World
abstract val count: Int abstract var count: Int
abstract var endReached: Boolean abstract var endReached: Boolean
protected val loadedChunks: HashSet<Chunk> = HashSet() protected val loadedChunks: HashSet<Chunk> = HashSet()
@ -69,7 +71,7 @@ abstract class GenerationTask(plugin: Chunkmaster, private val centerChunk: Chun
*/ */
protected fun borderReached(): Boolean { protected fun borderReached(): Boolean {
return (!world.worldBorder.isInside(lastChunkCoords.getCenterLocation(world)) && !ignoreWorldborder) return (!world.worldBorder.isInside(lastChunkCoords.getCenterLocation(world)) && !ignoreWorldborder)
|| (stopAfter in 1..count) || shape.endReached()
} }
/** /**
@ -93,14 +95,12 @@ abstract class GenerationTask(plugin: Chunkmaster, private val centerChunk: Chun
*/ */
protected fun updateGenerationAreaMarker(clear: Boolean = false) { protected fun updateGenerationAreaMarker(clear: Boolean = false) {
if (clear) { if (clear) {
markerSet?.deleteAreaMarker(markerAreaId) markerSet?.deletePolyLineMarker(markerAreaId)
} else if (dynmapIntegration && stopAfter > 0) { } else if (dynmapIntegration && radius > 0) {
val (topLeft, bottomRight) = this.getAreaCorners() markerSet?.creUpdatePolyLineMarker(
markerSet?.creUpdateAreMarker(
markerAreaId, markerAreaId,
markerAreaName, markerAreaName,
topLeft.getCenterLocation(world), this.shape.getShapeEdgeLocations().map { ChunkCoordinates(it.first, it.second).getCenterLocation(this.world) },
bottomRight.getCenterLocation(world),
markerAreaStyle markerAreaStyle
) )
} }
@ -123,29 +123,26 @@ abstract class GenerationTask(plugin: Chunkmaster, private val centerChunk: Chun
} }
} }
/**
* Returns an approximation of cornders of the generation area
*/
private 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 * Handles the invocation of the end reached callback and additional logic
*/ */
protected fun setEndReached() { protected fun setEndReached() {
endReached = true endReached = true
count = shape.count
endReachedCallback?.invoke(this) endReachedCallback?.invoke(this)
updateGenerationAreaMarker(true) updateGenerationAreaMarker(true)
updateLastChunkMarker(true) updateLastChunkMarker(true)
if (dynmapIntegration) {
val (topLeft, bottomRight) = this.getAreaCorners()
dynmap?.triggerRenderOfVolume(topLeft.getCenterLocation(world), bottomRight.getCenterLocation(world))
} }
/**
* Performs a check if the border has been reached
*/
protected fun borderReachedCheck(): Boolean {
val done = borderReached()
if (done) {
setEndReached()
}
return done
} }
/** /**

@ -7,18 +7,18 @@ import org.bukkit.World
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
class GenerationTaskPaper( class GenerationTaskPaper(
private val plugin: Chunkmaster, override val world: World, private val plugin: Chunkmaster,
centerChunk: ChunkCoordinates, private val startChunk: ChunkCoordinates, override val world: World,
override val stopAfter: Int = -1, startChunk: ChunkCoordinates,
override val radius: Int = -1,
shape: Shape shape: Shape
) : GenerationTask(plugin, centerChunk, startChunk, shape) { ) : GenerationTask(plugin, startChunk, shape) {
private val maxPendingChunks = plugin.config.getInt("generation.max-pending-chunks") 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
override var endReached: Boolean = false override var endReached: Boolean = false
init { init {
@ -35,10 +35,7 @@ class GenerationTaskPaper(
if (loadedChunks.size > maxLoadedChunks) { if (loadedChunks.size > maxLoadedChunks) {
unloadLoadedChunks() unloadLoadedChunks()
} 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 (borderReachedCheck()) return
setEndReached()
return
}
var chunk = nextChunkCoordinates var chunk = nextChunkCoordinates
for (i in 1 until chunkSkips) { for (i in 1 until chunkSkips) {
@ -50,13 +47,14 @@ class GenerationTaskPaper(
} }
if (!world.isChunkGenerated(chunk.x, chunk.z)) { if (!world.isChunkGenerated(chunk.x, chunk.z)) {
for (i in 0 until minOf(chunksPerStep, (stopAfter - count) - 1)) { for (i in 0 until chunksPerStep) {
if (borderReachedCheck()) break
if (!world.isChunkGenerated(chunk.x, chunk.z)) { if (!world.isChunkGenerated(chunk.x, chunk.z)) {
pendingChunks.add(world.getChunkAtAsync(chunk.x, chunk.z, true)) pendingChunks.add(world.getChunkAtAsync(chunk.x, chunk.z, true))
} }
chunk = nextChunkCoordinates chunk = nextChunkCoordinates
} }
if (!world.isChunkGenerated(chunk.x, chunk.z)) { if (!borderReachedCheck() && !world.isChunkGenerated(chunk.x, chunk.z)) {
pendingChunks.add(world.getChunkAtAsync(chunk.x, chunk.z, true)) pendingChunks.add(world.getChunkAtAsync(chunk.x, chunk.z, true))
} }
} }

@ -8,13 +8,12 @@ import java.lang.Exception
class GenerationTaskSpigot( class GenerationTaskSpigot(
private val plugin: Chunkmaster, override val world: World, private val plugin: Chunkmaster, override val world: World,
centerChunk: ChunkCoordinates, private val startChunk: ChunkCoordinates, centerChunk: ChunkCoordinates, private val startChunk: ChunkCoordinates,
override val stopAfter: Int = -1, override val radius: Int = -1,
shape: Shape shape: Shape
) : GenerationTask(plugin, centerChunk, startChunk, shape) { ) : GenerationTask(plugin, startChunk, shape) {
override var count = 0 override var count = 0
private set
override var endReached: Boolean = false override var endReached: Boolean = false
init { init {
@ -31,24 +30,24 @@ class GenerationTaskSpigot(
if (loadedChunks.size > maxLoadedChunks) { if (loadedChunks.size > maxLoadedChunks) {
unloadLoadedChunks() unloadLoadedChunks()
} else { } else {
if (borderReached()) { if (borderReachedCheck()) return
setEndReached()
return
}
var chunk = nextChunkCoordinates var chunk = nextChunkCoordinates
if (!world.isChunkGenerated(chunk.x, chunk.z)) { if (!world.isChunkGenerated(chunk.x, chunk.z)) {
for (i in 0 until minOf(chunksPerStep, stopAfter - count)) { for (i in 0 until minOf(chunksPerStep, radius - shape.currentRadius())) {
if (borderReachedCheck()) break
val chunkInstance = world.getChunkAt(chunk.x, chunk.z) val chunkInstance = world.getChunkAt(chunk.x, chunk.z)
chunkInstance.load(true) chunkInstance.load(true)
loadedChunks.add(chunkInstance) loadedChunks.add(chunkInstance)
chunk = nextChunkCoordinates chunk = nextChunkCoordinates
} }
if (!borderReachedCheck()) {
val chunkInstance = world.getChunkAt(chunk.x, chunk.z) val chunkInstance = world.getChunkAt(chunk.x, chunk.z)
chunkInstance.load(true) chunkInstance.load(true)
loadedChunks.add(chunkInstance) loadedChunks.add(chunkInstance)
} }
}
lastChunkCoords = chunk lastChunkCoords = chunk
count = shape.count // set the count to the more accurate spiral count count = shape.count // set the count to the more accurate spiral count
} }

@ -8,25 +8,34 @@ class RunningTaskEntry(
override val generationTask: GenerationTask override val generationTask: GenerationTask
) : TaskEntry { ) : TaskEntry {
private var lastProgress: Pair<Long, Int>? = null private var lastProgress: Pair<Long, Double>? = null
private var lastChunkCount: Pair<Long, Int>? = null
/** /**
* Returns the generation Speed * Returns the generation Speed
*/ */
val generationSpeed: Double? val generationSpeed: Pair<Double?, Double?>
get() { get() {
var generationSpeed: Double? = null var generationSpeed: Double? = null
var chunkGenerationSpeed: Double? = null
if (lastProgress != null) { if (lastProgress != null) {
val chunkDiff = generationTask.count - lastProgress!!.second val progressDiff = generationTask.shape.progress() - lastProgress!!.second
val timeDiff = (System.currentTimeMillis() - lastProgress!!.first).toDouble()/1000 val timeDiff = (System.currentTimeMillis() - lastProgress!!.first).toDouble()/1000
generationSpeed = chunkDiff.toDouble()/timeDiff generationSpeed = progressDiff/timeDiff
} }
lastProgress = Pair(System.currentTimeMillis(), generationTask.count) if (lastChunkCount != null) {
return generationSpeed val chunkDiff = generationTask.count - lastChunkCount!!.second
val timeDiff = (System.currentTimeMillis() - lastChunkCount!!.first).toDouble()/1000
chunkGenerationSpeed = chunkDiff/timeDiff
}
lastProgress = Pair(System.currentTimeMillis(), generationTask.shape.progress())
lastChunkCount = Pair(System.currentTimeMillis(), generationTask.count)
return Pair(generationSpeed, chunkGenerationSpeed)
} }
init { init {
lastProgress = Pair(System.currentTimeMillis(), generationTask.count) lastProgress = Pair(System.currentTimeMillis(), generationTask.shape.progress())
lastChunkCount = Pair(System.currentTimeMillis(), generationTask.count)
} }

@ -1,18 +1,42 @@
package net.trivernis.chunkmaster.lib.shapes package net.trivernis.chunkmaster.lib.shapes
import net.trivernis.chunkmaster.lib.dynmap.ExtendedMarkerSet
import net.trivernis.chunkmaster.lib.dynmap.MarkerStyle
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.collections.HashSet import kotlin.collections.HashSet
import kotlin.math.PI
import kotlin.math.pow
import kotlin.math.sqrt
import kotlin.system.exitProcess import kotlin.system.exitProcess
class Circle(center: Pair<Int, Int>, start: Pair<Int, Int>): Shape(center, start) { class Circle(center: Pair<Int, Int>, start: Pair<Int, Int>, radius: Int): Shape(center, start, radius) {
var r = 0 private var r = 0
private set
private var coords = Stack<Pair<Int, Int>>() private var coords = Stack<Pair<Int, Int>>()
private var previousCoords = HashSet<Pair<Int, Int>>()
override fun endReached(): Boolean {
if ((radius + 1) in 1..r) return true
return radius > 0 && coords.isEmpty() && r >= radius
}
override fun progress(): Double {
return (PI * r.toFloat().pow(2))/(PI* radius.toFloat().pow(2))
}
override fun currentRadius(): Int {
return r
}
override fun getShapeEdgeLocations(): List<Pair<Int, Int>> {
val locations = this.getCircleCoordinates(this.radius)
locations.add(locations.first())
return locations.map{ Pair(it.first + center.first, it.second + center.second) }
}
override fun next(): Pair<Int, Int> { override fun next(): Pair<Int, Int> {
if (count == 0 && currentPos != center) { if (count == 0 && currentPos != center) {
val tmpCircle = Circle(center, center) val tmpCircle = Circle(center, center, radius)
while (tmpCircle.next() != currentPos); while (tmpCircle.next() != currentPos);
this.count = tmpCircle.count this.count = tmpCircle.count
this.r = tmpCircle.r this.r = tmpCircle.r
@ -26,7 +50,10 @@ class Circle(center: Pair<Int, Int>, start: Pair<Int, Int>): Shape(center, start
val tmpCoords = HashSet<Pair<Int, Int>>() val tmpCoords = HashSet<Pair<Int, Int>>()
tmpCoords.addAll(getCircleCoordinates((r*2)-1).map { Pair(it.first / 2, it.second / 2) }) tmpCoords.addAll(getCircleCoordinates((r*2)-1).map { Pair(it.first / 2, it.second / 2) })
tmpCoords.addAll(getCircleCoordinates(r)) tmpCoords.addAll(getCircleCoordinates(r))
tmpCoords.removeAll(previousCoords)
previousCoords.clear()
coords.addAll(tmpCoords) coords.addAll(tmpCoords)
previousCoords.addAll(tmpCoords)
} }
count++ count++
val coord = coords.pop() val coord = coords.pop()
@ -36,31 +63,38 @@ class Circle(center: Pair<Int, Int>, start: Pair<Int, Int>): Shape(center, start
/** /**
* Returns the int coordinates for a circle * Returns the int coordinates for a circle
* Some coordinates might already be present in the list
* @param r - the radius * @param r - the radius
*/ */
private fun getCircleCoordinates(r: Int): HashSet<Pair<Int, Int>> { private fun getCircleCoordinates(r: Int): ArrayList<Pair<Int, Int>> {
val coords = ArrayList<Pair<Int, Int>>() val coords = ArrayList<Pair<Int, Int>>()
val segCoords = getSegment(r) val segCoords = getSegment(r)
coords.addAll(segCoords) coords.addAll(segCoords.reversed())
for (step in 0..7) { for (step in 1..7) {
val tmpSeg = ArrayList<Pair<Int, Int>>()
for (pos in segCoords) { for (pos in segCoords) {
coords.add(when (step) { val coord = when (step) {
0 -> pos 1 -> Pair(pos.first, -pos.second)
1 -> Pair(pos.second, pos.first) 2 ->Pair(pos.second, -pos.first)
2 -> Pair(pos.first, -pos.second) 3 -> Pair(-pos.second, -pos.first)
3 -> Pair(-pos.second, pos.first)
4 -> Pair(-pos.first, -pos.second) 4 -> Pair(-pos.first, -pos.second)
5 -> Pair(-pos.second, -pos.first) 5 -> Pair(-pos.first, pos.second)
6 -> Pair(-pos.first, pos.second) 6 -> Pair(-pos.second, pos.first)
7 -> Pair(pos.second, -pos.first) 7 -> Pair(pos.second, pos.first)
else -> pos else -> pos
}) }
if (coord !in coords) {
tmpSeg.add(coord)
}
}
if (step % 2 == 0) {
coords.addAll(tmpSeg.reversed())
} else {
coords.addAll(tmpSeg)
} }
} }
val set = HashSet<Pair<Int, Int>>() return coords
set.addAll(coords)
return set
} }
/** /**

@ -1,11 +1,37 @@
package net.trivernis.chunkmaster.lib.shapes package net.trivernis.chunkmaster.lib.shapes
abstract class Shape(protected val center: Pair<Int, Int>, start: Pair<Int, Int>) { import net.trivernis.chunkmaster.lib.dynmap.ExtendedMarkerSet
import net.trivernis.chunkmaster.lib.dynmap.MarkerStyle
import javax.xml.stream.Location
abstract class Shape(protected val center: Pair<Int, Int>, start: Pair<Int, Int>, radius: Int) {
protected var currentPos = start protected var currentPos = start
protected var radius = radius
private set
var count = 0 var count = 0
/** /**
* Returns the next value * Returns the next value
*/ */
abstract fun next(): Pair<Int, Int> abstract fun next(): Pair<Int, Int>
/**
* If the shape can provide a next value
*/
abstract fun endReached(): Boolean
/**
* Returns the progress of the shape
*/
abstract fun progress(): Double
/**
* Returns the current radius
*/
abstract fun currentRadius(): Int
/**
* returns a poly marker for the shape
*/
abstract fun getShapeEdgeLocations(): List<Pair<Int, Int>>
} }

@ -1,18 +1,34 @@
package net.trivernis.chunkmaster.lib.shapes package net.trivernis.chunkmaster.lib.shapes
import kotlin.math.PI
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.pow
import kotlin.math.sqrt
class Spiral(center: Pair<Int, Int>, start: Pair<Int, Int>): Shape(center, start) { class Spiral(center: Pair<Int, Int>, start: Pair<Int, Int>, radius: Int): Shape(center, start, radius) {
private var direction = 0 private var direction = 0
override fun endReached(): Boolean {
val distances = getDistances(center, currentPos)
return radius > 0 && (distances.first > radius || distances.second > radius)
}
override fun progress(): Double {
return (currentRadius()*2).toDouble().pow(2) / (radius * 2).toDouble().pow(2)
}
override fun currentRadius(): Int {
val distances = getDistances(center, currentPos)
return distances.first.coerceAtLeast(distances.second)
}
/** /**
* Returns the next value in the spiral * Returns the next value in the spiral
*/ */
override fun next(): Pair<Int, Int> { override fun next(): Pair<Int, Int> {
if (count == 0 && currentPos != center) { if (count == 0 && currentPos != center) {
// simulate the spiral to get the correct direction // simulate the spiral to get the correct direction and count
// TODO: Improve performance of this workaround (replace it with acutal stuff) val simSpiral = Spiral(center, center, radius)
val simSpiral = Spiral(center, center)
while (simSpiral.next() != currentPos); while (simSpiral.next() != currentPos);
direction = simSpiral.direction direction = simSpiral.direction
count = simSpiral.count count = simSpiral.count
@ -53,6 +69,14 @@ class Spiral(center: Pair<Int, Int>, start: Pair<Int, Int>): Shape(center, start
return currentPos return currentPos
} }
override fun getShapeEdgeLocations(): List<Pair<Int, Int>> {
val a = Pair(this.radius + center.first, this.radius + center.second)
val b = Pair(this.radius + center.first, -this.radius + center.second)
val c = Pair(-this.radius + center.first, -this.radius + center.second)
val d = Pair(-this.radius + center.first, this.radius + center.second)
return listOf(a, b, c, d, a)
}
/** /**
* Returns the distances between 2 coordinates * Returns the distances between 2 coordinates
*/ */

@ -11,8 +11,11 @@ TASK_SAVE_FAILED = §cException when saving tasks: %s
WORLD_NAME_REQUIRED = §cYou need to provide a world name! WORLD_NAME_REQUIRED = §cYou need to provide a world name!
WORLD_NOT_FOUND = §cWorld §2%s §cnot found! WORLD_NOT_FOUND = §cWorld §2%s §cnot found!
TASK_ALREADY_EXISTS = §cA task for '%s' already exists! TASK_ALREADY_EXISTS = §cA task for '%s' already exists!
TASK_CREATION_SUCCESS = §9Generation Task for world §2%s §9 until §2%s §9successfully created! TASK_CREATION_SUCCESS = §9Generation Task for world §2%s§9 §2%s§9 and shape %s successfully created!
TASK_UNIT_WORLDBORDER = until world border
TASK_UNIT_RADIUS = with a radius of %s
TASK_ID_REQUIRED = §cYou need to provide a task id! TASK_ID_REQUIRED = §cYou need to provide a task id!
INVALID_ARGUMENT = §cInvalid argument at %s: %s!
PAUSED_TASKS_HEADER = Currently Paused Generation Tasks PAUSED_TASKS_HEADER = Currently Paused Generation Tasks
TASKS_ENTRY = - §9#%d§r - §2%s§r - §2%d chunks %s§r TASKS_ENTRY = - §9#%d§r - §2%s§r - §2%d chunks %s§r

@ -11,8 +11,11 @@ TASK_SAVE_FAILED = §cFehler beim Speichern der Aufgaben: %s
WORLD_NAME_REQUIRED = §cDu musst einen Weltennamen angeben! WORLD_NAME_REQUIRED = §cDu musst einen Weltennamen angeben!
WORLD_NOT_FOUND = §c Die Welt §2%s §cwurde nicht gefunden! WORLD_NOT_FOUND = §c Die Welt §2%s §cwurde nicht gefunden!
TASK_ALREADY_EXISTS = §cEs existiert bereits eine Aufgabe für §2%s§c! TASK_ALREADY_EXISTS = §cEs existiert bereits eine Aufgabe für §2%s§c!
TASK_CREATION_SUCCESS = §9Generierungs-Aufgabe §2%s §9 bis §2%s §9wurde erfolgreich erstellt! TASK_CREATION_SUCCESS = §9Generierungs-Aufgabe §2%s§9 §2%s§9 in der Form %s wurde erfolgreich erstellt!
TASK_UNIT_WORLDBORDER = bis zur World-Border
TASK_UNIT_RADIUS = mit einem Radius von %s
TASK_ID_REQUIRED = §cDu musst eine Aufgaben-Id angeben! TASK_ID_REQUIRED = §cDu musst eine Aufgaben-Id angeben!
INVALID_ARGUMENT = §cFalscher Parameter an Stelle %s: %s!
PAUSED_TASKS_HEADER = §lPausierte Generierungsaufgaben§r PAUSED_TASKS_HEADER = §lPausierte Generierungsaufgaben§r

@ -11,8 +11,11 @@ TASK_SAVE_FAILED = §cException when saving tasks: %s
WORLD_NAME_REQUIRED = §cYou need to provide a world name! WORLD_NAME_REQUIRED = §cYou need to provide a world name!
WORLD_NOT_FOUND = §cWorld §2%s §cnot found! WORLD_NOT_FOUND = §cWorld §2%s §cnot found!
TASK_ALREADY_EXISTS = §cA task for '%s' already exists! TASK_ALREADY_EXISTS = §cA task for '%s' already exists!
TASK_CREATION_SUCCESS = §9Generation Task for world §2%s §9 until §2%s §9successfully created! TASK_CREATION_SUCCESS = §9Generation Task for world §2%s§9 §2%s§9 and shape %s successfully created!
TASK_UNIT_WORLDBORDER = until world border
TASK_UNIT_RADIUS = with a radius of %s
TASK_ID_REQUIRED = §cYou need to provide a task id! TASK_ID_REQUIRED = §cYou need to provide a task id!
INVALID_ARGUMENT = §cInvalid argument at %s: %s!
PAUSED_TASKS_HEADER = Currently Paused Generation Tasks PAUSED_TASKS_HEADER = Currently Paused Generation Tasks
TASKS_ENTRY = - §9#%d§r - §2%s§r - §2%d chunks %s§r TASKS_ENTRY = - §9#%d§r - §2%s§r - §2%d chunks %s§r

@ -13,7 +13,7 @@ commands:
description: Main command description: Main command
permission: chunkmaster.chunkmaster permission: chunkmaster.chunkmaster
usage: | usage: |
/<command> generate [<world>, <chunk-count>] - generates chunks starting from the spawn until the chunk-count is reached /<command> generate [<world>] <radius> [<shape>] - generates chunks starting from the spawn until the chunk-count is reached
/<command> cancel <task-id> - cancels the generation task with the task-id /<command> cancel <task-id> - cancels the generation task with the task-id
/<command> list - lists all running and paused generation tasks /<command> list - lists all running and paused generation tasks
/<command> pause - pauses all generation tasks /<command> pause - pauses all generation tasks

Loading…
Cancel
Save