Add circlular generation tasks

pull/55/head
trivernis 4 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.CommandSender
import org.bukkit.entity.Player
import kotlin.math.pow
class CmdGenerate(private val chunkmaster: Chunkmaster): Subcommand {
override val name = "generate"
@ -24,16 +23,17 @@ class CmdGenerate(private val chunkmaster: Chunkmaster): Subcommand {
.map {it.name}.toMutableList()
} else if (args.size == 2) {
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) {
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()
}
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 {
var worldName = ""
var stopAfter = -1
var blockRadius = -1
var shape = "square"
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])
}
worldName = sender.world.name
}
if (args.isEmpty()) {
if (sender is Player) {
return createTask(sender, worldName, blockRadius, shape)
} else {
worldName = sender.world.name
sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NAME_REQUIRED"))
return false
}
} else {
if (args.isNotEmpty()) {
worldName = args[0]
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"))
return false
}
blockRadius = args[0].toInt()
} else {
worldName = args[0]
}
return createTask(sender, worldName, stopAfter)
}
/**
* Returns stopAfter for a given unit
*/
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()
}
"blockradius" -> {
((number.toDouble()+1)/8).pow(2.0).toInt()
}
else -> number
if (args.size == 1) {
return createTask(sender, worldName, blockRadius, shape)
}
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
}
}
return 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 createTask(sender, worldName, blockRadius, shape)
}
/**
* 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 allTasks = chunkmaster.generationManager.allTasks
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
.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
} else if (world == null){
sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NOT_FOUND", worldName));

@ -1,7 +1,5 @@
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.lib.Subcommand
import net.trivernis.chunkmaster.lib.generation.TaskEntry
@ -50,8 +48,8 @@ class CmdList(private val chunkmaster: Chunkmaster): Subcommand {
*/
private fun getGenerationEntry(task: TaskEntry): String {
val genTask = task.generationTask
val percentage = if (genTask.stopAfter > 0)
" (%.1f".format((genTask.count.toDouble()/genTask.stopAfter.toDouble())*100) + "%)."
val percentage = if (genTask.radius > 0)
" (%.1f".format(genTask.shape.progress()*100) + "%)."
else
""
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_z", "integer NOT NULL DEFAULT 0"),
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(

@ -4,6 +4,7 @@ import org.bukkit.Location
import org.dynmap.markers.AreaMarker
import org.dynmap.markers.Marker
import org.dynmap.markers.MarkerSet
import org.dynmap.markers.PolyLineMarker
class ExtendedMarkerSet(private val markerSet: MarkerSet) {
/**
@ -44,6 +45,25 @@ class ExtendedMarkerSet(private val markerSet: MarkerSet) {
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
* @param id - the id of the marker
@ -52,6 +72,13 @@ class ExtendedMarkerSet(private val markerSet: MarkerSet) {
return markerSet.findAreaMarker(id)
}
/**
* Returns the polylinemarker for an id
*/
fun findPolyLineMarker(id: String): PolyLineMarker? {
return markerSet.findPolyLineMarker(id)
}
/**
* Deletes an area marker
* @param id - the id of the marker
@ -60,4 +87,9 @@ class ExtendedMarkerSet(private val markerSet: MarkerSet) {
val marker = this.findAreaMarker(id)
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.lib.shapes.Circle
import net.trivernis.chunkmaster.lib.shapes.Spiral
import org.bukkit.Chunk
import org.bukkit.Server
import org.bukkit.World
@ -35,7 +34,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
/**
* 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 }
if (foundTask == 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]!!
ChunkCoordinates(center.first, center.second)
}
val generationTask = createGenerationTask(world, centerChunk, centerChunk, stopAfter)
val generationTask = createGenerationTask(world, centerChunk, centerChunk, radius, shape)
chunkmaster.sqliteManager.executeStatement(
"""
INSERT INTO generation_tasks (center_x, center_z, last_x, last_z, world, stop_after)
values (?, ?, ?, ?, ?, ?)
INSERT INTO generation_tasks (center_x, center_z, last_x, last_z, world, radius, shape)
values (?, ?, ?, ?, ?, ?, ?)
""",
HashMap(
mapOf(
@ -58,7 +57,8 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
3 to centerChunk.x,
4 to centerChunk.z,
5 to world.name,
6 to stopAfter
6 to radius,
7 to shape
)
),
null
@ -101,14 +101,15 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
center: ChunkCoordinates,
last: ChunkCoordinates,
id: Int,
stopAfter: Int = -1,
delay: Long = 200L
radius: Int = -1,
delay: Long = 200L,
shape: String = "square"
) {
if (!paused) {
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(
chunkmaster, generationTask, delay, // 10 sec delay
chunkmaster, generationTask, delay,
chunkmaster.config.getLong("generation.period")
)
tasks.add(RunningTaskEntry(id, task, generationTask))
@ -163,7 +164,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
if (!server.onlinePlayers.isEmpty()) {
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 center = ChunkCoordinates(res.getInt("center_x"), res.getInt("center_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) {
resumeTask(world!!, center, last, id, stopAfter, 200L + count)
resumeTask(world!!, center, last, id, radius, 200L + count, shape)
}
} catch (error: NullPointerException) {
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) {
try {
val genTask = task.generationTask
val speed = task.generationSpeed!!
val percentage = if (genTask.stopAfter > 0) "(${"%.2f".format(
(genTask.count.toDouble() / genTask.stopAfter.toDouble()) * 100
)}%)" else ""
val eta = if (genTask.stopAfter > 0 && speed > 0) {
val etaSeconds = (genTask.stopAfter - genTask.count).toDouble()/speed
val (speed, chunkSpeed) = task.generationSpeed
val percentage = if (genTask.radius > 0) "(${"%.2f".format(genTask.shape.progress() * 100)}%)" else ""
val eta = if (genTask.radius > 0 && speed!! > 0) {
val etaSeconds = (genTask.shape.progress())/speed
val hours: Int = (etaSeconds/3600).toInt()
val minutes: Int = ((etaSeconds % 3600) / 60).toInt()
val seconds: Int = (etaSeconds % 60).toInt()
@ -306,7 +306,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
genTask.count,
percentage,
eta,
speed,
chunkSpeed!!,
genTask.lastChunk.x,
genTask.lastChunk.z))
saveProgressToDatabase(genTask.lastChunkCoords, task.id)
@ -339,13 +339,19 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
world: World,
center: ChunkCoordinates,
start: ChunkCoordinates,
stopAfter: Int
radius: Int,
shapeName: String
): 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()) {
GenerationTaskPaper(chunkmaster, world, center, start, stopAfter, shape)
GenerationTaskPaper(chunkmaster, world, start, radius, shape)
} 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
import net.trivernis.chunkmaster.Chunkmaster
import net.trivernis.chunkmaster.lib.shapes.Spiral
import net.trivernis.chunkmaster.lib.dynmap.*
import net.trivernis.chunkmaster.lib.shapes.Shape
import org.bukkit.Chunk
import org.bukkit.World
import kotlin.math.*
/**
* 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 {
abstract val stopAfter: Int
abstract val radius: Int
abstract val world: World
abstract val count: Int
abstract var count: Int
abstract var endReached: Boolean
protected val loadedChunks: HashSet<Chunk> = HashSet()
@ -69,7 +71,7 @@ abstract class GenerationTask(plugin: Chunkmaster, private val centerChunk: Chun
*/
protected fun borderReached(): Boolean {
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) {
if (clear) {
markerSet?.deleteAreaMarker(markerAreaId)
} else if (dynmapIntegration && stopAfter > 0) {
val (topLeft, bottomRight) = this.getAreaCorners()
markerSet?.creUpdateAreMarker(
markerSet?.deletePolyLineMarker(markerAreaId)
} else if (dynmapIntegration && radius > 0) {
markerSet?.creUpdatePolyLineMarker(
markerAreaId,
markerAreaName,
topLeft.getCenterLocation(world),
bottomRight.getCenterLocation(world),
this.shape.getShapeEdgeLocations().map { ChunkCoordinates(it.first, it.second).getCenterLocation(this.world) },
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
*/
protected fun setEndReached() {
endReached = true
count = shape.count
endReachedCallback?.invoke(this)
updateGenerationAreaMarker(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
class GenerationTaskPaper(
private val plugin: Chunkmaster, override val world: World,
centerChunk: ChunkCoordinates, private val startChunk: ChunkCoordinates,
override val stopAfter: Int = -1,
private val plugin: Chunkmaster,
override val world: World,
startChunk: ChunkCoordinates,
override val radius: Int = -1,
shape: Shape
) : GenerationTask(plugin, centerChunk, startChunk, shape) {
) : GenerationTask(plugin, startChunk, shape) {
private val maxPendingChunks = plugin.config.getInt("generation.max-pending-chunks")
private val pendingChunks = HashSet<CompletableFuture<Chunk>>()
override var count = 0
private set
override var endReached: Boolean = false
init {
@ -35,10 +35,7 @@ class GenerationTaskPaper(
if (loadedChunks.size > maxLoadedChunks) {
unloadLoadedChunks()
} else if (pendingChunks.size < maxPendingChunks) { // if more than 10 chunks are pending, wait.
if (borderReached()) {
setEndReached()
return
}
if (borderReachedCheck()) return
var chunk = nextChunkCoordinates
for (i in 1 until chunkSkips) {
@ -50,13 +47,14 @@ class GenerationTaskPaper(
}
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)) {
pendingChunks.add(world.getChunkAtAsync(chunk.x, chunk.z, true))
}
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))
}
}

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

@ -8,25 +8,34 @@ class RunningTaskEntry(
override val generationTask: GenerationTask
) : 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
*/
val generationSpeed: Double?
val generationSpeed: Pair<Double?, Double?>
get() {
var generationSpeed: Double? = null
var chunkGenerationSpeed: Double? = 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
generationSpeed = chunkDiff.toDouble()/timeDiff
generationSpeed = progressDiff/timeDiff
}
lastProgress = Pair(System.currentTimeMillis(), generationTask.count)
return generationSpeed
if (lastChunkCount != null) {
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 {
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
import net.trivernis.chunkmaster.lib.dynmap.ExtendedMarkerSet
import net.trivernis.chunkmaster.lib.dynmap.MarkerStyle
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashSet
import kotlin.math.PI
import kotlin.math.pow
import kotlin.math.sqrt
import kotlin.system.exitProcess
class Circle(center: Pair<Int, Int>, start: Pair<Int, Int>): Shape(center, start) {
var r = 0
private set
class Circle(center: Pair<Int, Int>, start: Pair<Int, Int>, radius: Int): Shape(center, start, radius) {
private var r = 0
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> {
if (count == 0 && currentPos != center) {
val tmpCircle = Circle(center, center)
val tmpCircle = Circle(center, center, radius)
while (tmpCircle.next() != currentPos);
this.count = tmpCircle.count
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>>()
tmpCoords.addAll(getCircleCoordinates((r*2)-1).map { Pair(it.first / 2, it.second / 2) })
tmpCoords.addAll(getCircleCoordinates(r))
tmpCoords.removeAll(previousCoords)
previousCoords.clear()
coords.addAll(tmpCoords)
previousCoords.addAll(tmpCoords)
}
count++
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
* Some coordinates might already be present in the list
* @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 segCoords = getSegment(r)
coords.addAll(segCoords)
for (step in 0..7) {
coords.addAll(segCoords.reversed())
for (step in 1..7) {
val tmpSeg = ArrayList<Pair<Int, Int>>()
for (pos in segCoords) {
coords.add(when (step) {
0 -> pos
1 -> Pair(pos.second, pos.first)
2 -> Pair(pos.first, -pos.second)
3 -> Pair(-pos.second, pos.first)
val coord = when (step) {
1 -> Pair(pos.first, -pos.second)
2 ->Pair(pos.second, -pos.first)
3 -> Pair(-pos.second, -pos.first)
4 -> Pair(-pos.first, -pos.second)
5 -> Pair(-pos.second, -pos.first)
6 -> Pair(-pos.first, pos.second)
7 -> Pair(pos.second, -pos.first)
5 -> Pair(-pos.first, pos.second)
6 -> Pair(-pos.second, pos.first)
7 -> Pair(pos.second, pos.first)
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>>()
set.addAll(coords)
return set
return coords
}
/**

@ -1,11 +1,37 @@
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 radius = radius
private set
var count = 0
/**
* Returns the next value
*/
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
import kotlin.math.PI
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
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
*/
override fun next(): Pair<Int, Int> {
if (count == 0 && currentPos != center) {
// simulate the spiral to get the correct direction
// TODO: Improve performance of this workaround (replace it with acutal stuff)
val simSpiral = Spiral(center, center)
// simulate the spiral to get the correct direction and count
val simSpiral = Spiral(center, center, radius)
while (simSpiral.next() != currentPos);
direction = simSpiral.direction
count = simSpiral.count
@ -53,6 +69,14 @@ class Spiral(center: Pair<Int, Int>, start: Pair<Int, Int>): Shape(center, start
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
*/

@ -11,8 +11,11 @@ TASK_SAVE_FAILED = §cException when saving tasks: %s
WORLD_NAME_REQUIRED = §cYou need to provide a world name!
WORLD_NOT_FOUND = §cWorld §2%s §cnot found!
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!
INVALID_ARGUMENT = §cInvalid argument at %s: %s!
PAUSED_TASKS_HEADER = Currently Paused Generation Tasks
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_NOT_FOUND = §c Die Welt §2%s §cwurde nicht gefunden!
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!
INVALID_ARGUMENT = §cFalscher Parameter an Stelle %s: %s!
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_NOT_FOUND = §cWorld §2%s §cnot found!
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!
INVALID_ARGUMENT = §cInvalid argument at %s: %s!
PAUSED_TASKS_HEADER = Currently Paused Generation Tasks
TASKS_ENTRY = - §9#%d§r - §2%s§r - §2%d chunks %s§r

@ -13,7 +13,7 @@ commands:
description: Main command
permission: chunkmaster.chunkmaster
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> list - lists all running and paused generation tasks
/<command> pause - pauses all generation tasks

Loading…
Cancel
Save