Add command to set and get the center

- Add getCenter command to get the center of the world used for generation
- Add setCenter to set the world center for generation tasks. If there is already a task running for the world the center for that task will not be  affected.
- Fix SQL problem with multiple queries at the same time creating new connections
- Add translations for new commands
- Add Examples to Readme
- Add new table to safe world centers
- Add query to Generation manager to get the center of the world for new generation tasks
pull/29/head
Trivernis 4 years ago
parent bdac496ce2
commit 832b1c4b55

@ -29,6 +29,29 @@ All features can be accessed with the command `/chunkmaster` or the aliases `/ch
- `/chunkmaster resume` Resumes all paused generation tasks.
- `/chunkmaster reload` Reloads the configuration file.
- `/chunkmaster tpchunk <X> <Z>` Teleports you to the specified chunk coordinates.
- `/<command> setCenter [<world>] <chunkX> <chunkZ>` - sets the center chunk of the world
- `/<command> getCenter [<world>]` - returns the center chunk of the world
#### Examples
**Generate a 100 chunks * 100 chunks square around the spawn:**
`/chm generate [world] 100 diameter`
**Generate a 100 blocks * 100 blocks square around the spawn:**
`/chm generate [world] 50 blockradius`
**Generate 100 Blocks in every direction from the spawn:**
`/chm generate [world] 100 blockradius`
**Generate 200 Chunks in every direction from the spawn:**
`/chm generate [world] 200 radius`
**Generate 1000 Chunks in total around the spawn:**
`/chm generate [world] 1000`
### Config

@ -0,0 +1,65 @@
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
import org.bukkit.entity.Player
class CmdGetCenter(private val chunkmaster: Chunkmaster): Subcommand {
override val name = "getCenter";
override fun onTabComplete(
sender: CommandSender,
command: Command,
alias: String,
args: List<String>
): MutableList<String> {
if (args.size == 1) {
return sender.server.worlds.filter { it.name.indexOf(args[0]) == 0 }
.map {it.name}.toMutableList()
}
return emptyList<String>().toMutableList()
}
override fun execute(sender: CommandSender, args: List<String>): Boolean {
val worldName: String = if (sender is Player) {
if (args.isNotEmpty()) {
args[0]
} else {
sender.world.name;
}
} else {
if (args.isEmpty()) {
sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NAME_REQUIRED"))
return false
} else {
args[0]
}
}
if (chunkmaster.generationManager.worldCenters.isEmpty()) {
chunkmaster.generationManager.loadWorldCenters() {
sendCenterInfo(sender, worldName)
}
return true
}
sendCenterInfo(sender, worldName)
return true
}
/**
* Sends the center information
*/
private fun sendCenterInfo(sender: CommandSender, worldName: String) {
var center = chunkmaster.generationManager.worldCenters[worldName]
if (center == null) {
val world = sender.server.worlds.find { it.name == worldName }
if (world == null) {
sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NOT_FOUND", worldName))
return
}
center = Pair(world.spawnLocation.chunk.x, world.spawnLocation.chunk.z)
}
sender.sendMessage(chunkmaster.langManager.getLocalized("CENTER_INFO", worldName, center.first, center.second))
}
}

@ -0,0 +1,85 @@
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
import org.bukkit.entity.Player
class CmdSetCenter(private val chunkmaster: Chunkmaster): Subcommand {
override val name = "setCenter";
override fun onTabComplete(
sender: CommandSender,
command: Command,
alias: String,
args: List<String>
): MutableList<String> {
if (args.size == 1) {
if (args[0].toIntOrNull() == null) {
return sender.server.worlds.filter { it.name.indexOf(args[0]) == 0 }
.map {it.name}.toMutableList()
}
}
return emptyList<String>().toMutableList();
}
override fun execute(sender: CommandSender, args: List<String>): Boolean {
val world: String
val centerX: Int
val centerZ: Int
if (args.size < 2) {
sender.sendMessage(chunkmaster.langManager.getLocalized("TOO_FEW_ARGUMENTS"))
return false
}
if (sender is Player) {
if (args.size == 2) {
world = sender.world.name
if (args[0].toIntOrNull() == null || args[1].toIntOrNull() == null) {
sender.sendMessage(chunkmaster.langManager.getLocalized("COORD_INVALID", args[0], args[1]))
return false
}
centerX = args[0].toInt()
centerZ = args[1].toInt()
} else {
if (!validateThreeArgs(sender, args)) {
return false
}
world = args[0]
centerX = args[1].toInt()
centerZ = args[2].toInt()
}
} else {
if (args.size < 3) {
sender.sendMessage(chunkmaster.langManager.getLocalized("TOO_FEW_ARGUMENTS"))
return false
} else {
if (!validateThreeArgs(sender, args)) {
return false
}
world = args[0]
centerX = args[1].toInt()
centerZ = args[2].toInt()
}
}
chunkmaster.generationManager.updateWorldCenter(world, Pair(centerX, centerZ))
sender.sendMessage(chunkmaster.langManager.getLocalized("CENTER_UPDATED", world, centerX, centerZ))
return true
}
/**
* Validates the command values with three arguments
*/
private fun validateThreeArgs(sender: CommandSender, args: List<String>): Boolean {
return if (sender.server.worlds.none { it.name == args[0] }) {
sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NOT_FOUND", args[0]))
false
} else if (args[1].toIntOrNull() == null || args[2].toIntOrNull() == null) {
sender.sendMessage(chunkmaster.langManager.getLocalized("COORD_INVALID", args[1], args[2]))
false
} else {
true
}
}
}

@ -40,7 +40,7 @@ class CommandChunkmaster(private val chunkmaster: Chunkmaster, private val serve
*/
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
if (args.isNotEmpty()) {
if (sender.hasPermission("chunkmaster.${args[0]}")) {
if (sender.hasPermission("chunkmaster.${args[0].toLowerCase()}")) {
return if (commands.containsKey(args[0])) {
commands[args[0]]!!.execute(sender, args.slice(1 until args.size))
} else {
@ -80,5 +80,11 @@ class CommandChunkmaster(private val chunkmaster: Chunkmaster, private val serve
val cmdTpChunk = CmdTpChunk(chunkmaster)
commands[cmdTpChunk.name] = cmdTpChunk
val cmdSetCenter = CmdSetCenter(chunkmaster)
commands[cmdSetCenter.name] = cmdSetCenter
val cmdGetCenter = CmdGetCenter(chunkmaster)
commands[cmdGetCenter.name] = cmdGetCenter
}
}

@ -22,19 +22,33 @@ class SqliteManager(private val chunkmaster: Chunkmaster) {
Pair("world", "text UNIQUE NOT NULL DEFAULT 'world'"),
Pair("stop_after", "integer DEFAULT -1")
)
),
Pair(
"world_properties",
listOf(
Pair("name", "text PRIMARY KEY"),
Pair("center_x", "integer NOT NULL DEFAULT 0"),
Pair("center_z", "integer NOT NULL DEFAULT 0")
)
)
)
private val needUpdate = HashSet<Pair<String, Pair<String, String>>>()
private val needCreation = HashSet<String>()
private var connection: Connection? = null
private var activeTasks = 0
/**
* Returns the connection to the database
*/
fun getConnection(): Connection? {
if (this.connection != null) {
return this.connection
}
try {
Class.forName("org.sqlite.JDBC")
return DriverManager.getConnection("jdbc:sqlite:${chunkmaster.dataFolder.absolutePath}/" +
this.connection = DriverManager.getConnection("jdbc:sqlite:${chunkmaster.dataFolder.absolutePath}/" +
"${chunkmaster.config.getString("database.filename")}")
return this.connection
} catch (e: Exception) {
chunkmaster.logger.severe(chunkmaster.langManager.getLocalized("DATABASE_CONNECTION_ERROR"))
chunkmaster.logger.severe(e.message)
@ -79,6 +93,7 @@ class SqliteManager(private val chunkmaster: Chunkmaster) {
*/
fun executeStatement(sql: String, values: HashMap<Int, Any>, callback: ((ResultSet) -> Unit)?) {
val connection = getConnection()
activeTasks++
if (connection != null) {
try {
val statement = connection.prepareStatement(sql)
@ -92,10 +107,14 @@ class SqliteManager(private val chunkmaster: Chunkmaster) {
}
statement.close()
} catch (e: Exception) {
chunkmaster.logger.severe(chunkmaster.langManager.getLocalized("SQL_ERROR", e.message!!))
chunkmaster.logger.severe(chunkmaster.langManager.getLocalized("SQL_ERROR", e.toString()))
chunkmaster.logger.info(ExceptionUtils.getStackTrace(e))
} finally {
connection.close()
activeTasks--
if (activeTasks == 0) {
connection.close()
this.connection = null
}
}
} else {
chunkmaster.logger.severe(chunkmaster.langManager.getLocalized("NO_DATABASE_CONNECTION"))

@ -10,9 +10,13 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
val tasks: HashSet<RunningTaskEntry> = HashSet()
val pausedTasks: HashSet<PausedTaskEntry> = HashSet()
val worldCenters: HashMap<String, Pair<Int, Int>> = HashMap()
val allTasks: HashSet<TaskEntry>
get() {
if (this.tasks.isEmpty() && this.pausedTasks.isEmpty()) {
if (this.worldCenters.isEmpty()) {
this.loadWorldCenters()
}
this.startAll()
if (!server.onlinePlayers.isEmpty()) {
this.pauseAll()
@ -32,7 +36,12 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
fun addTask(world: World, stopAfter: Int = -1): Int {
val foundTask = allTasks.find { it.generationTask.world == world }
if (foundTask == null) {
val centerChunk = ChunkCoordinates(world.spawnLocation.chunk.x, world.spawnLocation.chunk.z)
val centerChunk = if (worldCenters[world.name] == null) {
ChunkCoordinates(world.spawnLocation.chunk.x, world.spawnLocation.chunk.z)
} else {
val center = worldCenters[world.name]!!
ChunkCoordinates(center.first, center.second)
}
val generationTask = createGenerationTask(world, centerChunk, centerChunk, stopAfter)
chunkmaster.sqliteManager.executeStatement(
@ -147,6 +156,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
saveProgress() // save progress every 30 seconds
}, 600, 600)
server.scheduler.runTaskLater(chunkmaster, Runnable {
this.loadWorldCenters()
this.startAll()
if (!server.onlinePlayers.isEmpty()) {
this.pauseAll()
@ -222,6 +232,51 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server
startAll()
}
/**
* Overload that doesn't need an argument
*/
fun loadWorldCenters() {
loadWorldCenters(null)
}
/**
* Loads the world centers from the database
*/
fun loadWorldCenters(cb: (() -> Unit)?) {
chunkmaster.sqliteManager.executeStatement("SELECT * FROM world_properties", HashMap()) {
while (it.next()) {
worldCenters[it.getString("name")] = Pair(it.getInt("center_x"), it.getInt("center_z"))
}
cb?.invoke()
}
}
/**
* Updates the center of a world
*/
fun updateWorldCenter(worldName: String, center: Pair<Int, Int>) {
chunkmaster.sqliteManager.executeStatement("SELECT * FROM world_properties WHERE name = ?", HashMap(mapOf(1 to worldName))) {
if (it.next()) {
chunkmaster.sqliteManager.executeStatement("UPDATE world_properties SET center_x = ?, center_z = ? WHERE name = ?", HashMap(
mapOf(
1 to center.first,
2 to center.second,
3 to worldName
)
), null)
} else {
chunkmaster.sqliteManager.executeStatement("INSERT INTO world_properties (name, center_x, center_z) VALUES (?, ?, ?)", HashMap(
mapOf(
1 to worldName,
2 to center.first,
3 to center.second
)
), null)
}
}
worldCenters[worldName] = center
}
/**
* Saves the task progress
*/

@ -46,4 +46,9 @@ NO_DATABASE_CONNECTION = §cCould not execute sql: No database connection.
CREATE_TABLE_DEFINITION = Created table %s with definition %s.
TABLE_CREATE_ERROR = §cError when creation table %s.
UPDATE_TABLE_DEFINITION = Updated table %s with sql %s.
UPDATE_TABLE_FAILED = Failed to update table %s with sql %s.
UPDATE_TABLE_FAILED = Failed to update table %s with sql %s.
TOO_FEW_ARGUMENTS = §cYou did not provide enough arguments.
COORD_INVALID = §cThe provided coordinate ('%s', '%s') is invalid!
CENTER_UPDATED = §9The center for world §2%s §9has been set to §2(%s, %s)§9.
CENTER_INFO = §9The center for world §2%s §9is §2(%s, %s)§9.

@ -46,4 +46,9 @@ NO_DATABASE_CONNECTION = §cSql konnte nicht ausgeführt werden: Keine Datenbank
CREATE_TABLE_DEFINITION = Tabelle %s mit Definition %s wurde erstellt.
TABLE_CREATE_ERROR = §cFehler beim erstellen der Tabelle %s.
UPDATE_TABLE_DEFINITION = Tabelle %s wurde mit sql %s geupdated.
UPDATE_TABLE_FAILED = Fehler beim Updaten der Tabelle %s mit sql %s.
UPDATE_TABLE_FAILED = Fehler beim Updaten der Tabelle %s mit sql %s.
TOO_FEW_ARGUMENTS = §cDu hast nicht genug arguments angegeben.
COORD_INVALID = §cDie Koordinate ('%s', '%s') ist ungültig!
CENTER_UPDATED = §9Die Mitte der Welt §2%s §9wurde auf §2(%s, %s)§9 gesetzt.
CENTER_INFO = §9Die Mitte der Welt §2%s §9ist §2(%s, %s)§9.

@ -46,4 +46,9 @@ NO_DATABASE_CONNECTION = §cCould not execute sql: No database connection.
CREATE_TABLE_DEFINITION = Created table %s with definition %s.
TABLE_CREATE_ERROR = §cError when creation table %s.
UPDATE_TABLE_DEFINITION = Updated table %s with sql %s.
UPDATE_TABLE_FAILED = Failed to update table %s with sql %s.
UPDATE_TABLE_FAILED = Failed to update table %s with sql %s.
TOO_FEW_ARGUMENTS = §cYou did not provide enough arguments.
COORD_INVALID = §cThe provided coordinate ('%s', '%s') is invalid!
CENTER_UPDATED = §9The center for world §2%s §9has been set to §2(%s, %s)§9.
CENTER_INFO = §9The center for world §2%s §9is §2(%s, %s)§9.

@ -17,6 +17,8 @@ commands:
/<command> resume - resumes all generation tasks
/<command> reload - reloads the configuration and restarts all tasks
/<command> tpchunk <chunkX> <chunkZ> - teleports you to the chunk with the given chunk coordinates
/<command> setCenter [<world>] <chunkX> <chunkZ> - sets the center chunk of the world
/<command> getCenter [<world>] - returns the center chunk of the world
aliases:
- chm
- chunkm
@ -43,6 +45,12 @@ permissions:
chunkmaster.tpchunk:
description: Allows the tpchunk subcommand.
default: op
chunkmaster.setcenter:
description: Allows the setCenter subcommand.
default: op
chunkmaster.getcenter:
description: Allows the getCenter subcommand.
default: op
chunkmaster.chunkmaster:
description: Allows Chunkmaster commands.
default: op

Loading…
Cancel
Save