Add completed command to list completed tasks

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/109/head
trivernis 4 years ago
parent 90f2bbc2af
commit e545445348
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -1,2 +1,2 @@
kotlin.code.style=official kotlin.code.style=official
PLUGIN_VERSION=1.3.4 PLUGIN_VERSION=1.4.0

@ -0,0 +1,44 @@
package net.trivernis.chunkmaster.commands
import net.trivernis.chunkmaster.Chunkmaster
import net.trivernis.chunkmaster.lib.Subcommand
import org.bukkit.command.Command
import org.bukkit.command.CommandSender
class CmdCompleted(private val plugin: Chunkmaster) : Subcommand {
override val name = "completed"
override fun execute(sender: CommandSender, args: List<String>): Boolean {
plugin.sqliteManager.completedGenerationTasks.getCompletedTasks().thenAccept { tasks ->
val worlds = tasks.map { it.world }.toHashSet()
var response = "\n" + plugin.langManager.getLocalized("COMPLETED_TASKS_HEADER") + "\n\n"
for (world in worlds) {
response += plugin.langManager.getLocalized("COMPLETED_WORLD_HEADER", world) + "\n"
for (task in tasks.filter { it.world == world }) {
response += plugin.langManager.getLocalized(
"COMPLETED_TASK_ENTRY",
task.id,
task.radius,
task.center.x,
task.center.z,
task.shape
) + "\n"
}
response += "\n"
}
sender.sendMessage(response)
}
return true
}
override fun onTabComplete(
sender: CommandSender,
command: Command,
alias: String,
args: List<String>
): MutableList<String> {
return mutableListOf()
}
}

@ -41,6 +41,7 @@ class CmdStats(private val chunkmaster: Chunkmaster) : Subcommand {
${chunkmaster.langManager.getLocalized("STATS_WORLD_NAME", world.name)} ${chunkmaster.langManager.getLocalized("STATS_WORLD_NAME", world.name)}
${chunkmaster.langManager.getLocalized("STATS_ENTITY_COUNT", world.entities.size)} ${chunkmaster.langManager.getLocalized("STATS_ENTITY_COUNT", world.entities.size)}
${chunkmaster.langManager.getLocalized("STATS_LOADED_CHUNKS", world.loadedChunks.size)} ${chunkmaster.langManager.getLocalized("STATS_LOADED_CHUNKS", world.loadedChunks.size)}
${chunkmaster.langManager.getLocalized("STATS_GENERATING", chunkmaster.generationManager.tasks.find { it.generationTask.world == world } != null)}
""".trimIndent() """.trimIndent()
return message return message
} }

@ -87,5 +87,8 @@ class CommandChunkmaster(private val chunkmaster: Chunkmaster, private val serve
val cmdStats = CmdStats(chunkmaster) val cmdStats = CmdStats(chunkmaster)
commands[cmdStats.name] = cmdStats commands[cmdStats.name] = cmdStats
val cmdCompleted = CmdCompleted(chunkmaster)
commands[cmdCompleted.name] = cmdCompleted
} }
} }

@ -0,0 +1,5 @@
package net.trivernis.chunkmaster.lib.database
import net.trivernis.chunkmaster.lib.generation.ChunkCoordinates
data class CompletedGenerationTask (val id: Int, val world: String, val radius: Int, val center: ChunkCoordinates, val shape: String)

@ -0,0 +1,68 @@
package net.trivernis.chunkmaster.lib.database
import net.trivernis.chunkmaster.lib.generation.ChunkCoordinates
import java.sql.ResultSet
import java.util.concurrent.CompletableFuture
class CompletedGenerationTasks(private val sqliteManager: SqliteManager) {
/**
* Returns the list of all completed tasks
*/
fun getCompletedTasks(): CompletableFuture<List<CompletedGenerationTask>> {
val completableFuture = CompletableFuture<List<CompletedGenerationTask>>()
sqliteManager.executeStatement("SELECT * FROM completed_generation_tasks", HashMap()) { res ->
val tasks = ArrayList<CompletedGenerationTask>()
while (res!!.next()) {
tasks.add(mapSqlResponseToWrapperObject(res))
}
completableFuture.complete(tasks)
}
return completableFuture
}
/**
* Returns a list of completed tasks for a world
*/
fun getCompletedTasksForWorld(world: String): CompletableFuture<List<CompletedGenerationTask>> {
val completableFuture = CompletableFuture<List<CompletedGenerationTask>>()
sqliteManager.executeStatement("SELECT * FROM completed_generation_tasks WHERE world = ?", hashMapOf(1 to world)) { res ->
val tasks = ArrayList<CompletedGenerationTask>()
while (res!!.next()) {
tasks.add(mapSqlResponseToWrapperObject(res))
}
completableFuture.complete(tasks)
}
return completableFuture
}
private fun mapSqlResponseToWrapperObject(res: ResultSet): CompletedGenerationTask {
val id = res.getInt("id")
val world = res.getString("world")
val center = ChunkCoordinates(res.getInt("center_x"), res.getInt("center_z"))
val radius = res.getInt("completed_radius")
val shape = res.getString("shape")
return CompletedGenerationTask(id, world, radius, center, shape)
}
/**
* Adds a completed task
*/
fun addCompletedTask(id: Int, world: String, radius: Int, center: ChunkCoordinates, shape: String): CompletableFuture<Void> {
val completableFuture = CompletableFuture<Void>()
sqliteManager.executeStatement("INSERT INTO completed_generation_tasks (id, world, completed_radius, center_x, center_z, shape) VALUES (?, ?, ?, ?, ?, ?)", hashMapOf(
1 to id,
2 to world,
3 to radius,
4 to center.x,
5 to center.z,
6 to shape,
)) {
completableFuture.complete(null)
}
return completableFuture
}
}

@ -38,6 +38,17 @@ class SqliteManager(private val chunkmaster: Chunkmaster) {
Pair("chunk_x", "integer NOT NULL"), Pair("chunk_x", "integer NOT NULL"),
Pair("chunk_z", "integer NOT NULL") Pair("chunk_z", "integer NOT NULL")
) )
),
Pair(
"completed_generation_tasks",
listOf(
Pair("id", "integer PRIMARY KEY"),
Pair("world", "text NOT NULL"),
Pair("completed_radius", "integer NOT NULL"),
Pair("center_x", "integer NOT NULL"),
Pair("center_z", "integer NOT NULL"),
Pair("shape", "text NOT NULL")
)
) )
) )
private val needUpdate = HashSet<Pair<String, Pair<String, String>>>() private val needUpdate = HashSet<Pair<String, Pair<String, String>>>()
@ -48,6 +59,7 @@ class SqliteManager(private val chunkmaster: Chunkmaster) {
val worldProperties = WorldProperties(this) val worldProperties = WorldProperties(this)
val pendingChunks = PendingChunks(this) val pendingChunks = PendingChunks(this)
val generationTasks = GenerationTasks(this) val generationTasks = GenerationTasks(this)
val completedGenerationTasks = CompletedGenerationTasks(this)
/** /**
* Returns the connection to the database * Returns the connection to the database

@ -5,7 +5,7 @@ import net.trivernis.chunkmaster.lib.generation.taskentry.PausedTaskEntry
import net.trivernis.chunkmaster.lib.generation.taskentry.RunningTaskEntry import net.trivernis.chunkmaster.lib.generation.taskentry.RunningTaskEntry
import net.trivernis.chunkmaster.lib.generation.taskentry.TaskEntry import net.trivernis.chunkmaster.lib.generation.taskentry.TaskEntry
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.Square
import org.bukkit.Server import org.bukkit.Server
import org.bukkit.World import org.bukkit.World
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
@ -17,6 +17,8 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
val worldProperties = chunkmaster.sqliteManager.worldProperties val worldProperties = chunkmaster.sqliteManager.worldProperties
private val pendingChunksTable = chunkmaster.sqliteManager.pendingChunks private val pendingChunksTable = chunkmaster.sqliteManager.pendingChunks
private val generationTasks = chunkmaster.sqliteManager.generationTasks private val generationTasks = chunkmaster.sqliteManager.generationTasks
private val completedGenerationTasks = chunkmaster.sqliteManager.completedGenerationTasks
private val unloadingPeriod: Long private val unloadingPeriod: Long
get() { get() {
return chunkmaster.config.getLong("generation.unloading-period") return chunkmaster.config.getLong("generation.unloading-period")
@ -140,6 +142,13 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
taskEntry.cancel(chunkmaster.config.getLong("mspt-pause-threshold")) taskEntry.cancel(chunkmaster.config.getLong("mspt-pause-threshold"))
} }
generationTasks.deleteGenerationTask(id) generationTasks.deleteGenerationTask(id)
completedGenerationTasks.addCompletedTask(
id,
taskEntry.generationTask.world.name,
taskEntry.generationTask.shape.currentRadius(),
taskEntry.generationTask.startChunk,
taskEntry.generationTask.shape.javaClass.simpleName
)
pendingChunksTable.clearPendingChunks(id) pendingChunksTable.clearPendingChunks(id)
if (taskEntry is RunningTaskEntry) { if (taskEntry is RunningTaskEntry) {
@ -356,8 +365,8 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
): GenerationTask { ): GenerationTask {
val shape = when (shapeName) { val shape = when (shapeName) {
"circle" -> Circle(Pair(center.x, center.z), Pair(start.x, start.z), radius) "circle" -> Circle(Pair(center.x, center.z), Pair(start.x, start.z), radius)
"square" -> Spiral(Pair(center.x, center.z), Pair(start.x, start.z), radius) "square" -> Square(Pair(center.x, center.z), Pair(start.x, start.z), radius)
else -> Spiral(Pair(center.x, center.z), Pair(start.x, start.z), radius) else -> Square(Pair(center.x, center.z), Pair(start.x, start.z), radius)
} }
return DefaultGenerationTask( return DefaultGenerationTask(

@ -10,10 +10,10 @@ import kotlin.math.ceil
* Interface for generation tasks. * Interface for generation tasks.
*/ */
abstract class GenerationTask( abstract class GenerationTask(
private val plugin: Chunkmaster, plugin: Chunkmaster,
val world: World, val world: World,
protected val unloader: ChunkUnloader, protected val unloader: ChunkUnloader,
startChunk: ChunkCoordinates, val startChunk: ChunkCoordinates,
val shape: Shape, val shape: Shape,
val missingChunks: HashSet<ChunkCoordinates>, val missingChunks: HashSet<ChunkCoordinates>,
var state: TaskState var state: TaskState

@ -3,7 +3,7 @@ package net.trivernis.chunkmaster.lib.shapes
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.pow import kotlin.math.pow
class Spiral(center: Pair<Int, Int>, start: Pair<Int, Int>, radius: Int) : Shape(center, start, radius) { class Square(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 { override fun endReached(): Boolean {
@ -40,7 +40,7 @@ class Spiral(center: Pair<Int, Int>, start: Pair<Int, Int>, radius: Int) : Shape
} }
if (count == 0 && currentPos != center) { if (count == 0 && currentPos != center) {
// simulate the spiral to get the correct direction and count // simulate the spiral to get the correct direction and count
val simSpiral = Spiral(center, center, radius) val simSpiral = Square(center, center, radius)
while (simSpiral.next() != currentPos && !simSpiral.endReached()); while (simSpiral.next() != currentPos && !simSpiral.endReached());
direction = simSpiral.direction direction = simSpiral.direction
count = simSpiral.count count = simSpiral.count

@ -23,6 +23,10 @@ TASKS_ENTRY = - §9#%d§r - §2%s§r - §2%s§r - §2%s chunks %s§r
RUNNING_TASKS_HEADER = Currently Running Generation Tasks RUNNING_TASKS_HEADER = Currently Running Generation Tasks
NO_GENERATION_TASKS = There are no generation tasks. NO_GENERATION_TASKS = There are no generation tasks.
COMPLETED_TASKS_HEADER = §nCompleted Generation Tasks§r
COMPLETED_WORLD_HEADER = §l%s§r
COMPLETED_TASK_ENTRY = - §9#%d§r: §2%d§r chunks radius from center §2(%d, %d)§r with shape §2%s§r
PAUSE_SUCCESS = §9Paused all generation tasks. PAUSE_SUCCESS = §9Paused all generation tasks.
ALREADY_PAUSED = §cThe generation process is already paused! ALREADY_PAUSED = §cThe generation process is already paused!
@ -74,6 +78,7 @@ STATS_WORLD_NAME = §l%s§r
STATS_ENTITY_COUNT = - §2%d§r Entities STATS_ENTITY_COUNT = - §2%d§r Entities
STATS_LOADED_CHUNKS = - §2%d§r Loaded Chunks STATS_LOADED_CHUNKS = - §2%d§r Loaded Chunks
STATS_PLUGIN_LOADED_CHUNKS = - §2%d§r Chunks Loaded by Chunkmaster STATS_PLUGIN_LOADED_CHUNKS = - §2%d§r Chunks Loaded by Chunkmaster
STATS_GENERATING = - Generating: §2%s§r
SAVING_CHUNKS = Saving %d loaded chunks... SAVING_CHUNKS = Saving %d loaded chunks...
CANCEL_FAIL = Failed to cancel task #%d in the given timeout! CANCEL_FAIL = Failed to cancel task #%d in the given timeout!

@ -78,3 +78,9 @@ STATS_PLUGIN_LOADED_CHUNKS = - §2%d§r von Chunkmaster geladene Chunks
SAVING_CHUNKS = Speichere %d geladene Chunks... SAVING_CHUNKS = Speichere %d geladene Chunks...
CANCEL_FAIL = Konnte Aufgabe #%d nicht im angegebenen Timeout stoppen! CANCEL_FAIL = Konnte Aufgabe #%d nicht im angegebenen Timeout stoppen!
NO_AUTOSTART = Autostart ist auf §2false§r gesetzt. Pausiere... NO_AUTOSTART = Autostart ist auf §2false§r gesetzt. Pausiere...
COMPLETED_TASKS_HEADER = §nAbgeschlossene Aufgaben§r
COMPLETED_WORLD_HEADER = §l%s§r
COMPLETED_TASK_ENTRY = - §9#%d§r: §2%d§r Chunks Radius von der Mitte §2(%d, %d)§r aus in der Form §2%s§r
STATS_GENERATING = - Generiert: §2%s§r

@ -76,3 +76,9 @@ STATS_PLUGIN_LOADED_CHUNKS = - §2%d§r Chunks Loaded by Chunkmaster
SAVING_CHUNKS = Saving %d loaded chunks... SAVING_CHUNKS = Saving %d loaded chunks...
CANCEL_FAIL = Failed to cancel task #%d in the given timeout! CANCEL_FAIL = Failed to cancel task #%d in the given timeout!
NO_AUTOSTART = Autostart set to §2false§r. Pausing... NO_AUTOSTART = Autostart set to §2false§r. Pausing...
COMPLETED_TASKS_HEADER = §nCompleted Generation Tasks§r
COMPLETED_WORLD_HEADER = §l%s§r
COMPLETED_TASK_ENTRY = - §9#%d§r: §2%d§r chunks radius from center §2(%d, %d)§r with shape §2%s§r
STATS_GENERATING = - Generating: §2%s§r

@ -23,6 +23,7 @@ commands:
/<command> setCenter [[<world>] <chunkX> <chunkZ>]] - sets the center chunk of the world /<command> setCenter [[<world>] <chunkX> <chunkZ>]] - sets the center chunk of the world
/<command> getCenter [<world>] - returns the center chunk of the world /<command> getCenter [<world>] - returns the center chunk of the world
/<command> stats [<world>] - returns some chunk stats for the world or the whole server /<command> stats [<world>] - returns some chunk stats for the world or the whole server
/<command> completed - lists all completed tasks for all worlds
aliases: aliases:
- chm - chm
- chunkm - chunkm
@ -35,7 +36,7 @@ permissions:
description: Allows the list subcommand. description: Allows the list subcommand.
default: op default: op
chunkmaster.cancel: chunkmaster.cancel:
description: Allows the remove subcommand. description: Allows the cancel subcommand.
default: op default: op
chunkmaster.pause: chunkmaster.pause:
description: Allows the pause subcommand. description: Allows the pause subcommand.
@ -55,6 +56,12 @@ permissions:
chunkmaster.getcenter: chunkmaster.getcenter:
description: Allows the getCenter subcommand. description: Allows the getCenter subcommand.
default: op default: op
chunkmaster.stats:
description: Allows the stats subcommand.
deault: op
chunkmaster.completed:
description: Allows the completed subcommand.
default: op
chunkmaster.chunkmaster: chunkmaster.chunkmaster:
description: Allows Chunkmaster commands. description: Allows Chunkmaster commands.
default: op default: op
@ -63,8 +70,14 @@ permissions:
default: op default: op
children: children:
- chunkmaster.generate - chunkmaster.generate
- chunkmaster.listgentasks - chunkmaster.list
- chunkmaster.removegentask - chunkmaster.cancel
- chunkmaster.pausegentasks - chunkmaster.pause
- chunkmaster.resumegentasks - chunkmaster.resume
- chunkmaster.completed
- chunkmaster.tpchunk
- chunkmaster.reload
- chunkmaster.setcenter
- chunkmaster.getcenter
- chunkmaster.stats
- chunkmaster.chunkmaster - chunkmaster.chunkmaster

@ -1,62 +0,0 @@
package net.trivernis.chunkmaster.lib.shapes
import io.kotest.matchers.booleans.shouldBeTrue
import io.kotest.matchers.collections.shouldContainAll
import io.kotest.matchers.shouldBe
import org.junit.Test
import org.junit.jupiter.api.BeforeEach
class SpiralTest {
private val spiral = Spiral(center = Pair(0, 0), radius = 2, start = Pair(0, 0))
@BeforeEach
fun init() {
spiral.reset()
}
@Test
fun `it generates coordinates`() {
spiral.next().shouldBe(Pair(0, 0))
spiral.next().shouldBe(Pair(0, 1))
spiral.next().shouldBe(Pair(1, 1))
spiral.next().shouldBe(Pair(1, 0))
spiral.next().shouldBe(Pair(1, -1))
spiral.next().shouldBe(Pair(0, -1))
spiral.next().shouldBe(Pair(-1, -1))
spiral.next().shouldBe(Pair(-1, 0))
spiral.next().shouldBe(Pair(-1, 1))
spiral.next().shouldBe(Pair(-1, 2))
spiral.next().shouldBe(Pair(0, 2))
}
@Test
fun `it reports when reaching the end`() {
for (i in 1..25) {
spiral.next()
}
spiral.endReached().shouldBeTrue()
}
@Test
fun `it reports the radius`() {
for (i in 1..9) {
spiral.next()
}
spiral.currentRadius().shouldBe(1)
}
@Test
fun `it returns the right edges`() {
spiral.getShapeEdgeLocations().shouldContainAll(listOf(Pair(2, 2), Pair(-2, 2), Pair(2, -2), Pair(-2, -2)))
}
@Test
fun `it returns the progress`() {
spiral.progress(2).shouldBe(0)
for (i in 1..8) {
spiral.next()
}
spiral.progress(2).shouldBe(0.5)
}
}

@ -0,0 +1,62 @@
package net.trivernis.chunkmaster.lib.shapes
import io.kotest.matchers.booleans.shouldBeTrue
import io.kotest.matchers.collections.shouldContainAll
import io.kotest.matchers.shouldBe
import org.junit.Test
import org.junit.jupiter.api.BeforeEach
class SquareTest {
private val square = Square(center = Pair(0, 0), radius = 2, start = Pair(0, 0))
@BeforeEach
fun init() {
square.reset()
}
@Test
fun `it generates coordinates`() {
square.next().shouldBe(Pair(0, 0))
square.next().shouldBe(Pair(0, 1))
square.next().shouldBe(Pair(1, 1))
square.next().shouldBe(Pair(1, 0))
square.next().shouldBe(Pair(1, -1))
square.next().shouldBe(Pair(0, -1))
square.next().shouldBe(Pair(-1, -1))
square.next().shouldBe(Pair(-1, 0))
square.next().shouldBe(Pair(-1, 1))
square.next().shouldBe(Pair(-1, 2))
square.next().shouldBe(Pair(0, 2))
}
@Test
fun `it reports when reaching the end`() {
for (i in 1..25) {
square.next()
}
square.endReached().shouldBeTrue()
}
@Test
fun `it reports the radius`() {
for (i in 1..9) {
square.next()
}
square.currentRadius().shouldBe(1)
}
@Test
fun `it returns the right edges`() {
square.getShapeEdgeLocations().shouldContainAll(listOf(Pair(2, 2), Pair(-2, 2), Pair(2, -2), Pair(-2, -2)))
}
@Test
fun `it returns the progress`() {
square.progress(2).shouldBe(0)
for (i in 1..8) {
square.next()
}
square.progress(2).shouldBe(0.5)
}
}
Loading…
Cancel
Save