Merge pull request #55 from Trivernis/develop

Release Beta 1.16
feature/stats-command v0.16-beta
Trivernis 4 years ago committed by GitHub
commit 9c5f486414
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,13 @@
image: openjdk
matrix:
- env: SCRIPT=shadowJar
install:
- gradle dependencies
script:
- if [[ "SCRIPT" ]]; then gradle $SCRIPT; fi
cache:
- .gradle

@ -1,4 +1,4 @@
# chunkmaster # chunkmaster ![](https://abstruse.trivernis.net/badge/1)
This plugin can be used to pre-generate the region of a world around the spawn chunk(s). This plugin can be used to pre-generate the region of a world around the spawn chunk(s).
The generation automatically pauses when a player joins the server (assuming the server was empty before) The generation automatically pauses when a player joins the server (assuming the server was empty before)

@ -22,7 +22,7 @@ idea {
} }
group "net.trivernis" group "net.trivernis"
version "0.15-beta" version "0.16-beta"
sourceCompatibility = 1.8 sourceCompatibility = 1.8
@ -55,7 +55,7 @@ dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12' testCompile group: 'junit', name: 'junit', version: '4.12'
compileOnly "com.destroystokyo.paper:paper-api:1.14.4-R0.1-SNAPSHOT" compileOnly "com.destroystokyo.paper:paper-api:1.14.4-R0.1-SNAPSHOT"
compileOnly "org.dynmap:dynmap-api:2.0" compileOnly "org.dynmap:dynmap-api:2.0"
compile group: 'org.xerial', name: 'sqlite-jdbc', version: '3.28.0' compileOnly group: 'org.xerial', name: 'sqlite-jdbc', version: '3.28.0'
compile "io.papermc:paperlib:1.0.2" compile "io.papermc:paperlib:1.0.2"
compile "org.bstats:bstats-bukkit:1.5" compile "org.bstats:bstats-bukkit:1.5"
} }

@ -9,7 +9,6 @@ import org.bstats.bukkit.Metrics
import org.bukkit.plugin.java.JavaPlugin import org.bukkit.plugin.java.JavaPlugin
import org.bukkit.scheduler.BukkitTask import org.bukkit.scheduler.BukkitTask
import org.dynmap.DynmapAPI import org.dynmap.DynmapAPI
import org.dynmap.DynmapCommonAPI
import java.lang.Exception import java.lang.Exception
class Chunkmaster: JavaPlugin() { class Chunkmaster: JavaPlugin() {

@ -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()) { worldName = sender.world.name
if (args[0].toIntOrNull() != null) { }
stopAfter = args[0].toInt() if (args.isEmpty()) {
worldName = sender.world.name if (sender is Player) {
} else { return createTask(sender, worldName, blockRadius, shape)
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 { } else {
worldName = sender.world.name sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NAME_REQUIRED"))
return false
} }
} else { }
if (args.isNotEmpty()) { if (args[0].toIntOrNull() != null) {
worldName = args[0] if (sender !is Player) {
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 {
sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NAME_REQUIRED")) sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NAME_REQUIRED"))
return false return false
} }
blockRadius = args[0].toInt()
} else {
worldName = args[0]
} }
return createTask(sender, worldName, stopAfter)
}
/** 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) { when {
return when (unit) { args[1].toIntOrNull() != null -> blockRadius = args[1].toInt()
"radius" -> { args[1] in shapes -> shape = args[1]
((number * 2)+1).toDouble().pow(2.0).toInt() else -> {
} sender.sendMessage(chunkmaster.langManager.getLocalized("INVALID_ARGUMENT", 2, args[1]))
"diameter" -> { return false
number.toDouble().pow(2.0).toInt()
}
"blockradius" -> {
((number.toDouble()+1)/8).pow(2.0).toInt()
}
else -> number
} }
} }
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. * 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(

@ -0,0 +1,30 @@
package net.trivernis.chunkmaster.lib.dynmap
import org.dynmap.DynmapAPI
class DynmapApiWrapper(private val dynmapAPI: DynmapAPI) {
/**
* Returns a marker set by name
*/
fun getMarkerSet(name: String): ExtendedMarkerSet? {
val set = dynmapAPI.markerAPI?.getMarkerSet(name)
return if (set != null) {
ExtendedMarkerSet(set)
} else {
null
}
}
fun getCreateMarkerSet(id: String, name: String): ExtendedMarkerSet? {
var set = dynmapAPI.markerAPI?.getMarkerSet(id)
if (set == null) {
set = dynmapAPI.markerAPI?.createMarkerSet(id, name, null, true)
}
return if (set != null) {
ExtendedMarkerSet(set)
} else {
null
}
}
}

@ -0,0 +1,95 @@
package net.trivernis.chunkmaster.lib.dynmap
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) {
/**
* Creates or updates an area marker depending on if it exists
* @param id - the unique id of the area marker
* @param label - the label that is displayed when clicking on the marker
* @param l1 - the top left corner
* @param l2 - the bottom right corner
*/
fun creUpdateAreMarker(id: String, label: String, l1: Location, l2: Location, style: MarkerStyle?): AreaMarker {
var marker = markerSet.findAreaMarker(id)
if (marker != null) {
marker.setCornerLocations(
doubleArrayOf(l1.x, l2.x),
doubleArrayOf(l1.z, l2.z)
)
} else {
marker = markerSet.createAreaMarker(
id,
label,
false,
l1.world.name,
doubleArrayOf(l1.x, l2.x),
doubleArrayOf(l1.z, l2.z),
true
)
}
if (style != null) {
marker.boostFlag = style.boostFlag
if (style.lineStyle != null) {
marker.setLineStyle(style.lineStyle.weight, style.lineStyle.opacity, style.lineStyle.color)
}
if (style.fillStyle != null) {
marker.setFillStyle(style.fillStyle.opacity, style.fillStyle.color)
}
}
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
*/
fun findAreaMarker(id: String): AreaMarker? {
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
*/
fun deleteAreaMarker(id: String) {
val marker = this.findAreaMarker(id)
marker?.deleteMarker()
}
fun deletePolyLineMarker(id: String) {
val marker = this.findPolyLineMarker(id)
marker?.deleteMarker()
}
}

@ -0,0 +1,3 @@
package net.trivernis.chunkmaster.lib.dynmap
data class FillStyle(val opacity: Double, val color: Int)

@ -0,0 +1,3 @@
package net.trivernis.chunkmaster.lib.dynmap
data class LineStyle(val weight: Int, val opacity: Double, val color: Int)

@ -0,0 +1,5 @@
package net.trivernis.chunkmaster.lib.dynmap
import org.dynmap.markers.MarkerIcon
data class MarkerStyle(val icon: MarkerIcon?, val lineStyle: LineStyle?, val fillStyle: FillStyle?, val boostFlag: Boolean = false)

@ -2,7 +2,8 @@ package net.trivernis.chunkmaster.lib.generation
import io.papermc.lib.PaperLib import io.papermc.lib.PaperLib
import net.trivernis.chunkmaster.Chunkmaster import net.trivernis.chunkmaster.Chunkmaster
import org.bukkit.Chunk import net.trivernis.chunkmaster.lib.shapes.Circle
import net.trivernis.chunkmaster.lib.shapes.Spiral
import org.bukkit.Server import org.bukkit.Server
import org.bukkit.World import org.bukkit.World
@ -33,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) {
@ -42,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(
@ -56,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
@ -99,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))
@ -161,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)
} }
/** /**
@ -197,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")))
@ -284,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()
@ -304,10 +306,11 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
genTask.count, genTask.count,
percentage, percentage,
eta, eta,
speed, chunkSpeed!!,
genTask.lastChunk.x, genTask.lastChunkCoords.x,
genTask.lastChunk.z)) genTask.lastChunkCoords.z))
saveProgressToDatabase(genTask.lastChunkCoords, task.id) saveProgressToDatabase(genTask.lastChunkCoords, task.id)
genTask.updateLastChunkMarker()
} catch (error: Exception) { } catch (error: Exception) {
chunkmaster.logger.warning(chunkmaster.langManager.getLocalized("TASK_SAVE_FAILED", error.toString())) chunkmaster.logger.warning(chunkmaster.langManager.getLocalized("TASK_SAVE_FAILED", error.toString()))
} }
@ -336,12 +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 = 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) GenerationTaskPaper(chunkmaster, world, start, radius, shape)
} else { } else {
GenerationTaskSpigot(chunkmaster, world, center, start, stopAfter) GenerationTaskSpigot(chunkmaster, world, start, radius, shape)
} }
} }
} }

@ -1,25 +1,26 @@
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.Spiral import net.trivernis.chunkmaster.lib.dynmap.*
import net.trivernis.chunkmaster.lib.shapes.Shape
import org.bukkit.Chunk import org.bukkit.Chunk
import org.bukkit.Location
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) : 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 spiral: Spiral =
Spiral(Pair(centerChunk.x, centerChunk.z), Pair(startChunk.x, startChunk.z))
protected val loadedChunks: HashSet<Chunk> = HashSet() protected val loadedChunks: HashSet<Chunk> = HashSet()
var lastChunkCoords = ChunkCoordinates(startChunk.x, startChunk.z) var lastChunkCoords = ChunkCoordinates(startChunk.x, startChunk.z)
protected set protected set
@ -27,13 +28,22 @@ abstract class GenerationTask(plugin: Chunkmaster, private val centerChunk: Chun
protected val msptThreshold = plugin.config.getLong("generation.mspt-pause-threshold") protected val msptThreshold = plugin.config.getLong("generation.mspt-pause-threshold")
protected val maxLoadedChunks = plugin.config.getInt("generation.max-loaded-chunks") protected val maxLoadedChunks = plugin.config.getInt("generation.max-loaded-chunks")
protected val chunksPerStep = plugin.config.getInt("generation.chunks-per-step") protected val chunksPerStep = plugin.config.getInt("generation.chunks-per-step")
protected val dynmapIntegration = plugin.config.getBoolean("dynmap")
protected val dynmap = plugin.dynmapApi
protected var endReachedCallback: ((GenerationTask) -> Unit)? = null
private set
private val markerId = "chunkmaster_genarea" private var endReachedCallback: ((GenerationTask) -> Unit)? = null
private val markerName = "Chunkmaster Generation Area"
private val dynmapIntegration = plugin.config.getBoolean("dynmap")
private val dynmap = plugin.dynmapApi
private val markerSet: ExtendedMarkerSet? = if (dynmap != null) {
DynmapApiWrapper(dynmap).getCreateMarkerSet("chunkmaster", "Chunkmaster")
} else {
null
}
private val markerAreaStyle = MarkerStyle(null, LineStyle(2, 1.0, 0x0022FF), FillStyle(.0, 0))
private val markerAreaId = "chunkmaster_genarea"
private val markerAreaName = "Chunkmaster Generation Area"
private val markerLastStyle = MarkerStyle(null, LineStyle(2, 1.0, 0x0077FF), FillStyle(.5, 0x0077FF))
private val markerLastId = "chunkmaster_lastchunk"
private val markerLastName = "Chunkmaster Last Chunk"
private val ignoreWorldborder = plugin.config.getBoolean("generation.ignore-worldborder") private val ignoreWorldborder = plugin.config.getBoolean("generation.ignore-worldborder")
abstract override fun run() abstract override fun run()
@ -41,27 +51,16 @@ abstract class GenerationTask(plugin: Chunkmaster, private val centerChunk: Chun
val nextChunkCoordinates: ChunkCoordinates val nextChunkCoordinates: ChunkCoordinates
get() { get() {
val nextChunkCoords = spiral.next() val nextChunkCoords = shape.next()
return ChunkCoordinates(nextChunkCoords.first, nextChunkCoords.second) return ChunkCoordinates(nextChunkCoords.first, nextChunkCoords.second)
} }
val lastChunk: Chunk
get() {
return world.getChunkAt(lastChunkCoords.x, lastChunkCoords.z)
}
val nextChunk: Chunk
get() {
val next = nextChunkCoordinates
return world.getChunkAt(next.x, next.z)
}
/** /**
* Checks if the World border or the maximum chunk setting for the task is reached. * Checks if the World border or the maximum chunk setting for the task is reached.
*/ */
protected fun borderReached(): Boolean { protected fun borderReached(): Boolean {
return (!world.worldBorder.isInside(lastChunkCoords.getCenterLocation(world)) && !ignoreWorldborder) return (!world.worldBorder.isInside(lastChunkCoords.getCenterLocation(world)) && !ignoreWorldborder)
|| (stopAfter in 1..count) || shape.endReached()
} }
/** /**
@ -83,56 +82,57 @@ abstract class GenerationTask(plugin: Chunkmaster, private val centerChunk: Chun
/** /**
* Updates the dynmap marker for the generation radius * Updates the dynmap marker for the generation radius
*/ */
protected fun updateDynmapMarker(clear: Boolean = false) { protected fun updateGenerationAreaMarker(clear: Boolean = false) {
val markerSet = dynmap?.markerAPI?.getMarkerSet("markers")
var marker = markerSet?.findAreaMarker(markerId)
if (clear) { if (clear) {
marker?.deleteMarker() markerSet?.deletePolyLineMarker(markerAreaId)
} else if (dynmapIntegration && stopAfter > 0) { } else if (dynmapIntegration && radius > 0) {
val (topLeft, bottomRight) = this.getAreaCorners() markerSet?.creUpdatePolyLineMarker(
if (marker != null) { markerAreaId,
marker.setCornerLocations( markerAreaName,
doubleArrayOf((topLeft.x * 16).toDouble(), (bottomRight.x * 16).toDouble()), this.shape.getShapeEdgeLocations().map { ChunkCoordinates(it.first, it.second).getCenterLocation(this.world) },
doubleArrayOf((topLeft.z * 16).toDouble(), (bottomRight.z * 16).toDouble()) markerAreaStyle
) )
} else {
marker = markerSet?.createAreaMarker(
markerId,
markerName,
false,
world.name,
doubleArrayOf((topLeft.x * 16).toDouble(), (bottomRight.x * 16).toDouble()),
doubleArrayOf((topLeft.z * 16).toDouble(), (bottomRight.z * 16).toDouble()),
true
)
}
marker?.setFillStyle(.0, 0)
marker?.setLineStyle(2, 1.0, 0x0000FF)
} }
} }
/** /**
* Returns an approximation of cornders of the generation area * Updates the dynmap marker for the generation radius
*/ */
protected fun getAreaCorners(): Pair<ChunkCoordinates, ChunkCoordinates> { fun updateLastChunkMarker(clear: Boolean = false) {
val width = sqrt(stopAfter.toFloat()) if (clear) {
return Pair( markerSet?.deleteAreaMarker(markerLastId)
ChunkCoordinates(centerChunk.x - floor(width/2).toInt(), centerChunk.z - floor(width/2).toInt()), } else if (dynmapIntegration) {
ChunkCoordinates(centerChunk.x + ceil(width/2).toInt(), centerChunk.z + ceil(width/2).toInt()) markerSet?.creUpdateAreMarker(
) markerLastId,
markerLastName,
this.lastChunkCoords.getCenterLocation(world).chunk.getBlock(0, 0, 0).location,
this.lastChunkCoords.getCenterLocation(world).chunk.getBlock(15, 0, 15).location,
markerLastStyle
)
}
} }
/** /**
* 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() { private fun setEndReached() {
endReached = true endReached = true
count = shape.count
endReachedCallback?.invoke(this) endReachedCallback?.invoke(this)
updateDynmapMarker(true) updateGenerationAreaMarker(true)
if (dynmapIntegration) { updateLastChunkMarker(true)
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) {
unloadLoadedChunks()
setEndReached()
} }
return done
} }
/** /**

@ -1,45 +1,44 @@
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.Shape
import org.bukkit.Chunk import org.bukkit.Chunk
import org.bukkit.World 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,
) : GenerationTask(plugin, centerChunk, startChunk) { override val radius: Int = -1,
shape: 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 {
updateDynmapMarker() updateGenerationAreaMarker()
} }
/** /**
* Runs the generation task. Every Iteration the next chunk will be generated if * Runs the generation task. Every Iteration the next chunks will be generated if
* it hasn't been generated already. * they haven't been generated already
* After 10 chunks have been generated, they will all be unloaded and saved. * After a configured number of chunks chunks have been generated, they will all be unloaded and saved.
*/ */
override fun run() { override fun run() {
if (plugin.mspt < msptThreshold) { // pause when tps < 2 if (plugin.mspt < msptThreshold) {
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 (borderReached()) { if (borderReachedCheck()) return
setEndReached()
return
}
var chunk = nextChunkCoordinates var chunk = nextChunkCoordinates
for (i in 1 until chunkSkips) { for (i in 0 until chunkSkips) {
if (world.isChunkGenerated(chunk.x, chunk.z)) { if (world.isChunkGenerated(chunk.x, chunk.z)) {
chunk = nextChunkCoordinates chunk = nextChunkCoordinates
} else { } else {
@ -48,7 +47,8 @@ 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 (borderReached()) 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))
} }
@ -59,7 +59,7 @@ class GenerationTaskPaper(
} }
} }
lastChunkCoords = chunk lastChunkCoords = chunk
count = spiral.count // set the count to the more accurate spiral count count = shape.count
} }
} }
checkChunksLoaded() checkChunksLoaded()
@ -70,7 +70,8 @@ class GenerationTaskPaper(
* This unloads all chunks that were generated but not unloaded yet. * This unloads all chunks that were generated but not unloaded yet.
*/ */
override fun cancel() { override fun cancel() {
updateDynmapMarker(true) updateGenerationAreaMarker(true)
updateLastChunkMarker(true)
unloadAllChunks() unloadAllChunks()
} }

@ -1,54 +1,52 @@
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.Shape
import org.bukkit.World import org.bukkit.World
import java.lang.Exception import java.lang.Exception
class GenerationTaskSpigot( class GenerationTaskSpigot(
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,
) : GenerationTask(plugin, centerChunk, startChunk) { override val radius: Int = -1,
shape: 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 {
updateDynmapMarker() updateGenerationAreaMarker()
} }
/** /**
* Runs the generation task. Every Iteration the next chunk will be generated if * Runs the generation task. Every Iteration the next chunks will be generated if
* it hasn't been generated already. * they haven't been generated already
* After 10 chunks have been generated, they will all be unloaded and saved. * After a configured number of chunks chunks have been generated, they will all be unloaded and saved.
*/ */
override fun run() { override fun run() {
if (plugin.mspt < msptThreshold) { // pause when tps < 2 if (plugin.mspt < msptThreshold) {
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
for (i in 0 until chunksPerStep) {
if (!world.isChunkGenerated(chunk.x, chunk.z)) { if (borderReached()) break
for (i in 0 until minOf(chunksPerStep, stopAfter - count)) {
val chunkInstance = world.getChunkAt(chunk.x, chunk.z)
chunkInstance.load(true)
loadedChunks.add(chunkInstance)
chunk = nextChunkCoordinates
}
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
} }
val chunkInstance = world.getChunkAt(chunk.x, chunk.z)
chunkInstance.load(true)
loadedChunks.add(chunkInstance)
lastChunkCoords = chunk lastChunkCoords = chunk
count = spiral.count // set the count to the more accurate spiral count count = shape.count
} }
} }
} }
@ -67,6 +65,7 @@ class GenerationTaskSpigot(
} }
} }
} }
updateDynmapMarker(true) updateGenerationAreaMarker(true)
updateLastChunkMarker(true)
} }
} }

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

@ -0,0 +1,130 @@
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>, 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
}
/**
* Returns the edge locations of the shape to be used
* with dynmap markers
*/
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) }
}
/**
* Returns the next coordinate of the circle until the end is reached
*/
override fun next(): Pair<Int, Int> {
if (endReached()) {
return currentPos
}
if (count == 0 && currentPos != center) {
val tmpCircle = Circle(center, center, radius)
while (tmpCircle.next() != currentPos);
this.count = tmpCircle.count
this.r = tmpCircle.r
}
if (count == 0) {
count++
return center
}
if (coords.isEmpty()) {
r++
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()
currentPos = Pair(coord.first + center.first, coord.second + center.second)
return currentPos
}
/**
* 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): ArrayList<Pair<Int, Int>> {
val coords = ArrayList<Pair<Int, Int>>()
val segCoords = getSegment(r)
coords.addAll(segCoords.reversed())
for (step in 1..7) {
val tmpSeg = ArrayList<Pair<Int, Int>>()
for (pos in segCoords) {
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.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)
}
}
return coords
}
/**
* Returns the int coordinates for a circles segment
* @param r - the radius
*/
private fun getSegment(r: Int): ArrayList<Pair<Int, Int>> {
var d = -r
var x = r
var y = 0
val coords = ArrayList<Pair<Int, Int>>()
while (y <= x) {
coords.add(Pair(x, y))
d += 2 * y + 1
y += 1
if (d > 0) {
x -= 1
d -= 2 * x
}
}
return coords
}
}

@ -0,0 +1,37 @@
package net.trivernis.chunkmaster.lib.shapes
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,20 +1,34 @@
package net.trivernis.chunkmaster.lib 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(private val center: Pair<Int, Int>, start: Pair<Int, Int>) { class Spiral(center: Pair<Int, Int>, start: Pair<Int, Int>, radius: Int): Shape(center, start, radius) {
private var currentPos = start
private var direction = 0 private var direction = 0
var count = 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
*/ */
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
@ -55,6 +69,17 @@ class Spiral(private val center: Pair<Int, Int>, start: Pair<Int, Int>) {
return currentPos return currentPos
} }
/**
* Returns the edges to be used with dynmap markers
*/
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

@ -1,6 +1,6 @@
main: net.trivernis.chunkmaster.Chunkmaster main: net.trivernis.chunkmaster.Chunkmaster
name: Chunkmaster name: Chunkmaster
version: '0.15-beta' version: '0.16-beta'
description: Chunk commands plugin. description: Chunk commands plugin.
author: Trivernis author: Trivernis
website: trivernis.net website: trivernis.net
@ -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