diff --git a/src/main/kotlin/net/trivernis/chunkmaster/Chunkmaster.kt b/src/main/kotlin/net/trivernis/chunkmaster/Chunkmaster.kt index f24c79e..efc8916 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/Chunkmaster.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/Chunkmaster.kt @@ -2,6 +2,7 @@ package net.trivernis.chunkmaster import io.papermc.lib.PaperLib import net.trivernis.chunkmaster.commands.* +import net.trivernis.chunkmaster.lib.LanguageManager import net.trivernis.chunkmaster.lib.generation.GenerationManager import net.trivernis.chunkmaster.lib.SqliteManager import org.bstats.bukkit.Metrics @@ -12,6 +13,7 @@ import java.lang.Exception class Chunkmaster: JavaPlugin() { lateinit var sqliteManager: SqliteManager lateinit var generationManager: GenerationManager + lateinit var langManager: LanguageManager private lateinit var tpsTask: BukkitTask var mspt = 20 // keep track of the milliseconds per tick private set @@ -25,6 +27,8 @@ class Chunkmaster: JavaPlugin() { val metrics = Metrics(this) + langManager = LanguageManager(this) + langManager.loadProperties() initDatabase() generationManager = GenerationManager(this, server) generationManager.init() @@ -52,7 +56,7 @@ class Chunkmaster: JavaPlugin() { * Stop all tasks and close database connection on disable */ override fun onDisable() { - logger.info("Stopping all generation tasks...") + logger.info(langManager.getLocalized("STOPPING_ALL_TASKS")) generationManager.stopAll() } @@ -70,6 +74,7 @@ class Chunkmaster: JavaPlugin() { config.addDefault("generation.max-loaded-chunks", 10) config.addDefault("generation.ignore-worldborder", false) config.addDefault("database.filename", "chunkmaster.db") + config.addDefault("language", "en") config.options().copyDefaults(true) saveConfig() } @@ -78,13 +83,13 @@ class Chunkmaster: JavaPlugin() { * Initializes the database */ private fun initDatabase() { - logger.info("Initializing Database...") + logger.info(langManager.getLocalized("DB_INIT")) try { this.sqliteManager = SqliteManager( this) sqliteManager.init() - logger.info("Database fully initialized.") + logger.info(langManager.getLocalized("DB_INIT_FINISHED")) } catch(e: Exception) { - logger.warning("Failed to init database: ${e.message}") + logger.warning(langManager.getLocalized("DB_INIT_EROR", e.message!!)) } } } \ No newline at end of file diff --git a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdCancel.kt b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdCancel.kt index b1fa084..0c662c5 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdCancel.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdCancel.kt @@ -32,16 +32,14 @@ class CmdCancel(private val chunkmaster: Chunkmaster): Subcommand { override fun execute(sender: CommandSender, args: List): Boolean { return if (args.isNotEmpty() && args[0].toIntOrNull() != null) { if (chunkmaster.generationManager.removeTask(args[0].toInt())) { - sender.sendMessage("Task ${args[0]} canceled.") + sender.sendMessage(chunkmaster.langManager.getLocalized("TASK_CANCELED", args[0])) true } else { - sender.spigot().sendMessage(*ComponentBuilder("Task ${args[0]} not found!") - .color(ChatColor.RED).create()) + sender.sendMessage(chunkmaster.langManager.getLocalized("TASK_NOT_FOUND", args[0])) false } } else { - sender.spigot().sendMessage(*ComponentBuilder("You need to provide a task id to cancel.") - .color(ChatColor.RED).create()) + sender.sendMessage(chunkmaster.langManager.getLocalized("TASK_ID_REQUIRED")); false } } diff --git a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdGenerate.kt b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdGenerate.kt index 9ea4dde..deaa7c3 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdGenerate.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdGenerate.kt @@ -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 org.bukkit.command.Command @@ -79,8 +77,7 @@ class CmdGenerate(private val chunkmaster: Chunkmaster): Subcommand { stopAfter = getStopAfter(stopAfter, args[2]) } } else { - sender.spigot().sendMessage( - *ComponentBuilder("You need to provide a world name").color(ChatColor.RED).create()) + sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NAME_REQUIRED")) return false } } @@ -116,17 +113,14 @@ class CmdGenerate(private val chunkmaster: Chunkmaster): Subcommand { val allTasks = chunkmaster.generationManager.allTasks return if (world != null && (allTasks.find { it.generationTask.world == world }) == null) { chunkmaster.generationManager.addTask(world, stopAfter) - sender.spigot().sendMessage(*ComponentBuilder("Generation task for world ").color(ChatColor.BLUE) - .append(worldName).color(ChatColor.GREEN).append(" until ").color(ChatColor.BLUE) - .append(if (stopAfter > 0) "$stopAfter chunks" else "WorldBorder").color(ChatColor.GREEN) - .append(" successfully created").color(ChatColor.BLUE).create()) + sender.sendMessage(chunkmaster.langManager + .getLocalized("TASK_CREATION_SUCCESS", worldName, if (stopAfter > 0) "$stopAfter chunks" else "WorldBorder")) true } else if (world == null){ - sender.spigot().sendMessage(*ComponentBuilder("World ").color(ChatColor.RED) - .append(worldName).color(ChatColor.GREEN).append(" not found!").color(ChatColor.RED).create()) + sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NOT_FOUND", worldName)); false } else { - sender.spigot().sendMessage(*ComponentBuilder("Task already exists!").color(ChatColor.RED).create()) + sender.sendMessage(chunkmaster.langManager.getLocalized("TASK_ALREADY_EXISTS", worldName)) return false } } diff --git a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdList.kt b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdList.kt index b98a6f4..86f98c3 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdList.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdList.kt @@ -4,6 +4,7 @@ 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 import org.bukkit.command.Command import org.bukkit.command.CommandSender @@ -27,37 +28,33 @@ class CmdList(private val chunkmaster: Chunkmaster): Subcommand { val pausedTasks = chunkmaster.generationManager.pausedTasks if (runningTasks.isEmpty() && pausedTasks.isEmpty()) { - sender.spigot().sendMessage(*ComponentBuilder("There are no generation tasks.") - .color(ChatColor.BLUE).create()) + sender.sendMessage(chunkmaster.langManager.getLocalized("NO_GENERATION_TASKS")) } else if (runningTasks.isEmpty()) { - val response = ComponentBuilder("Currently paused generation tasks:").color(ChatColor.WHITE).bold(true) + var response = chunkmaster.langManager.getLocalized("PAUSED_TASKS_HEADER") for (taskEntry in pausedTasks) { - val genTask = taskEntry.generationTask - response.append("\n - ").color(ChatColor.WHITE).bold(false) - response.append("#${taskEntry.id}").color(ChatColor.BLUE).append(" - ").color(ChatColor.WHITE) - response.append(genTask.world.name).color(ChatColor.GREEN).append(" - Progress: ").color(ChatColor.WHITE) - response.append("${genTask.count} chunks").color(ChatColor.BLUE) - - if (genTask.stopAfter > 0) { - response.append(" (${(genTask.count.toDouble()/genTask.stopAfter.toDouble())*100}%).") - } + response += getGenerationEntry(taskEntry) } - sender.spigot().sendMessage(*response.create()) + sender.sendMessage(response) } else { - val response = ComponentBuilder("Currently running generation tasks:").color(ChatColor.WHITE).bold(true) + var response = chunkmaster.langManager.getLocalized("RUNNING_TASKS_HEADER") for (task in runningTasks) { - val genTask = task.generationTask - response.append("\n - ").color(ChatColor.WHITE).bold(false) - .append("#${task.id}").color(ChatColor.BLUE).append(" - ").color(ChatColor.WHITE) - .append(genTask.world.name).color(ChatColor.GREEN).append(" - Progress: ").color(ChatColor.WHITE) - .append("${genTask.count} chunks").color(ChatColor.BLUE) - - if (genTask.stopAfter > 0) { - response.append(" (${(genTask.count.toDouble()/genTask.stopAfter.toDouble())*100}%).") - } + response += getGenerationEntry(task) } - sender.spigot().sendMessage(*response.create()) + sender.sendMessage(response) } return true } + + /** + * Returns the report string for one generation task + */ + 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) + "%)." + else + "" + return "\n" + chunkmaster.langManager.getLocalized("TASKS_ENTRY", + task.id, genTask.world.name, genTask.count, percentage) + } } \ No newline at end of file diff --git a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdPause.kt b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdPause.kt index 6e1d46c..c49cddc 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdPause.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdPause.kt @@ -22,15 +22,10 @@ class CmdPause(private val chunkmaster: Chunkmaster) : Subcommand { override fun execute(sender: CommandSender, args: List): Boolean { return if (!chunkmaster.generationManager.paused) { chunkmaster.generationManager.pauseAll() - sender.spigot().sendMessage( - *ComponentBuilder("Paused all generation tasks.") - .color(ChatColor.BLUE).create() - ) + sender.sendMessage(chunkmaster.langManager.getLocalized("PAUSE_SUCCESS")) true } else { - sender.spigot().sendMessage( - *ComponentBuilder("The generation process is already paused.").color(ChatColor.RED).create() - ) + sender.sendMessage(chunkmaster.langManager.getLocalized("ALREADY_PAUSED")) false } } diff --git a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdReload.kt b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdReload.kt index 91b6c81..4a38fa6 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdReload.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdReload.kt @@ -23,12 +23,11 @@ class CmdReload(private val chunkmaster: Chunkmaster): Subcommand { * Reload command to reload the config and restart the tasks. */ override fun execute(sender: CommandSender, args: List): Boolean { - sender.spigot().sendMessage(*ComponentBuilder("Reloading config and restarting tasks...") - .color(ChatColor.YELLOW).create()) + sender.sendMessage(chunkmaster.langManager.getLocalized("CONFIG_RELOADING")) chunkmaster.generationManager.stopAll() chunkmaster.reloadConfig() chunkmaster.generationManager.startAll() - sender.spigot().sendMessage(*ComponentBuilder("Config reload complete!").color(ChatColor.GREEN).create()) + sender.sendMessage(chunkmaster.langManager.getLocalized("CONFIG_RELOADED")) return true } } \ No newline at end of file diff --git a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdResume.kt b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdResume.kt index c5beb9c..2d143f0 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdResume.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdResume.kt @@ -22,12 +22,10 @@ class CmdResume(private val chunkmaster: Chunkmaster): Subcommand { override fun execute(sender: CommandSender, args: List): Boolean { return if (chunkmaster.generationManager.paused) { chunkmaster.generationManager.resumeAll() - sender.spigot().sendMessage( - *ComponentBuilder("Resumed all generation tasks.").color(ChatColor.BLUE).create()) + sender.sendMessage(chunkmaster.langManager.getLocalized("RESUME_SUCCESS")) true } else { - sender.spigot().sendMessage( - *ComponentBuilder("There are no paused generation tasks.").color(ChatColor.RED).create()) + sender.sendMessage(chunkmaster.langManager.getLocalized("NOT_PAUSED")) false } } diff --git a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdTpChunk.kt b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdTpChunk.kt index 20d6c4d..8ffcb6d 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdTpChunk.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/commands/CmdTpChunk.kt @@ -3,13 +3,14 @@ package net.trivernis.chunkmaster.commands import io.papermc.lib.PaperLib 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 org.bukkit.Material import org.bukkit.command.Command import org.bukkit.command.CommandSender import org.bukkit.entity.Player -class CmdTpChunk: Subcommand { +class CmdTpChunk(private val chunkmaster: Chunkmaster): Subcommand { override val name = "tpchunk" override fun onTabComplete( @@ -37,15 +38,13 @@ class CmdTpChunk: Subcommand { } else { sender.teleport(location) } - sender.spigot().sendMessage(*ComponentBuilder("You have been teleportet to chunk") - .color(ChatColor.YELLOW).append("${args[0]}, ${args[1]}").color(ChatColor.BLUE).create()) + sender.sendMessage(chunkmaster.langManager.getLocalized("TELEPORTED", args[0], args[1])) return true } else { return false } } else { - sender.spigot().sendMessage(*ComponentBuilder("This command can only be executed by a player!") - .color(ChatColor.RED).create()) + sender.sendMessage(chunkmaster.langManager.getLocalized("TP_ONLY_PLAYER")) return false } } diff --git a/src/main/kotlin/net/trivernis/chunkmaster/commands/CommandChunkmaster.kt b/src/main/kotlin/net/trivernis/chunkmaster/commands/CommandChunkmaster.kt index a3072cc..bf05ce2 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/commands/CommandChunkmaster.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/commands/CommandChunkmaster.kt @@ -44,17 +44,11 @@ class CommandChunkmaster(private val chunkmaster: Chunkmaster, private val serve return if (commands.containsKey(args[0])) { commands[args[0]]!!.execute(sender, args.slice(1 until args.size)) } else { - sender.spigot().sendMessage( - *ComponentBuilder("Subcommand ").color(ChatColor.RED) - .append(args[0]).color(ChatColor.GREEN).append(" not found").color(ChatColor.RED).create() - ) + sender.sendMessage(chunkmaster.langManager.getLocalized("SUBCOMMAND_NOT_FOUND", args[0])) false } } else { - sender.spigot().sendMessage( - *ComponentBuilder("You do not have permission!") - .color(ChatColor.RED).create() - ) + sender.sendMessage(chunkmaster.langManager.getLocalized("NO_PERMISSION")) } return true } else { @@ -84,7 +78,7 @@ class CommandChunkmaster(private val chunkmaster: Chunkmaster, private val serve val cmdReload = CmdReload(chunkmaster) commands[cmdReload.name] = cmdReload - val cmdTpChunk = CmdTpChunk() + val cmdTpChunk = CmdTpChunk(chunkmaster) commands[cmdTpChunk.name] = cmdTpChunk } } \ No newline at end of file diff --git a/src/main/kotlin/net/trivernis/chunkmaster/lib/LanguageManager.kt b/src/main/kotlin/net/trivernis/chunkmaster/lib/LanguageManager.kt new file mode 100644 index 0000000..da48546 --- /dev/null +++ b/src/main/kotlin/net/trivernis/chunkmaster/lib/LanguageManager.kt @@ -0,0 +1,61 @@ +package net.trivernis.chunkmaster.lib +import net.trivernis.chunkmaster.Chunkmaster +import java.io.BufferedReader +import java.io.File +import java.lang.Exception +import java.util.Properties + +class LanguageManager(private val plugin: Chunkmaster) { + private val langProps = Properties() + private val languageFolder = "${plugin.dataFolder.absolutePath}/i18n" + private var langFileLoaded = false + + /** + * Loads the default properties file and then the language specific ones. + * If no lang-specific file is found in the plugins directory under i18n an attempt is made to + * load the file from inside the jar in i18n. + */ + fun loadProperties() { + val language = plugin.config.getString("language") + val langFile = "$languageFolder/$language.i18n.properties" + val file = File(langFile) + val loader = Thread.currentThread().contextClassLoader + val defaultStream = this.javaClass.getResourceAsStream("/i18n/DEFAULT.i18n.properties") + if (defaultStream != null) { + langProps.load(defaultStream) + defaultStream.close() + } else { + plugin.logger.severe("Couldn't load default language properties.") + } + if (file.exists()) { + try { + val inputStream = loader.getResourceAsStream(langFile) + if (inputStream != null) { + langProps.load(inputStream) + langFileLoaded = true + inputStream.close() + } + } catch (e: Exception) { + plugin.logger.warning("Language file $langFile could not be loaded!") + plugin.logger.fine(e.toString()) + } + } else { + val inputStream = this.javaClass.getResourceAsStream("/i18n/$language.i18n.properties") + if (inputStream != null) { + langProps.load(inputStream) + langFileLoaded = true + inputStream.close() + } else { + plugin.logger.warning("Language File $langFile could not be found!") + } + } + } + + /** + * Returns a localized message with replacements + */ + fun getLocalized(key: String, vararg replacements: Any): String { + val localizedString = langProps.getProperty(key) + return String.format(localizedString, *replacements) + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/trivernis/chunkmaster/lib/SqliteManager.kt b/src/main/kotlin/net/trivernis/chunkmaster/lib/SqliteManager.kt index d28db0b..507f97d 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/lib/SqliteManager.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/lib/SqliteManager.kt @@ -36,7 +36,7 @@ class SqliteManager(private val chunkmaster: Chunkmaster) { return DriverManager.getConnection("jdbc:sqlite:${chunkmaster.dataFolder.absolutePath}/" + "${chunkmaster.config.getString("database.filename")}") } catch (e: Exception) { - chunkmaster.logger.severe("Could not get database connection.") + chunkmaster.logger.severe(chunkmaster.langManager.getLocalized("DATABASE_CONNECTION_ERROR")) chunkmaster.logger.severe(e.message) } return null @@ -92,13 +92,13 @@ class SqliteManager(private val chunkmaster: Chunkmaster) { } statement.close() } catch (e: Exception) { - chunkmaster.logger.severe("An error occured on sql $sql. ${e.message}") + chunkmaster.logger.severe(chunkmaster.langManager.getLocalized("SQL_ERROR", e.message!!)) chunkmaster.logger.info(ExceptionUtils.getStackTrace(e)) } finally { connection.close() } } else { - chunkmaster.logger.severe("Could not execute sql $sql. No database connection established.") + chunkmaster.logger.severe(chunkmaster.langManager.getLocalized("NO_DATABASE_CONNECTION")) } } @@ -114,10 +114,10 @@ class SqliteManager(private val chunkmaster: Chunkmaster) { tableDef += "${column.first} ${column.second}," } tableDef = tableDef.substringBeforeLast(",") + ");" - chunkmaster.logger.info("Creating table $table with definition $tableDef") + chunkmaster.logger.finest(chunkmaster.langManager.getLocalized("CREATE_TABLE_DEFINITION", table, tableDef)) executeStatement(tableDef, HashMap(), null) } catch (e: Exception) { - chunkmaster.logger.severe("Error creating table $table.") + chunkmaster.logger.severe(chunkmaster.langManager.getLocalized("TABLE_CREATE_ERROR", table)) chunkmaster.logger.severe(e.message) chunkmaster.logger.info(ExceptionUtils.getStackTrace(e)) } @@ -126,9 +126,9 @@ class SqliteManager(private val chunkmaster: Chunkmaster) { val updateSql = "ALTER TABLE ${table.first} ADD COLUMN ${table.second.first} ${table.second.second}" try { executeStatement(updateSql, HashMap(), null) - chunkmaster.logger.info("Updated table ${table.first} with sql $updateSql") + chunkmaster.logger.finest(chunkmaster.langManager.getLocalized("UPDATE_TABLE_DEFINITION", table.first, updateSql)) } catch (e: Exception) { - chunkmaster.logger.severe("Failed to update table ${table.first} with sql $updateSql") + chunkmaster.logger.severe(chunkmaster.langManager.getLocalized("UPDATE_TABLE_FAILED", table.first, updateSql)) chunkmaster.logger.severe(e.message) chunkmaster.logger.info(ExceptionUtils.getStackTrace(e)) } diff --git a/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/ChunkCoordinates.kt b/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/ChunkCoordinates.kt index 1376d53..cbb4441 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/ChunkCoordinates.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/ChunkCoordinates.kt @@ -5,6 +5,6 @@ import org.bukkit.World class ChunkCoordinates(val x: Int, val z: Int) { fun getCenterLocation(world: World): Location { - return Location(world, ((x*16) + 8).toDouble(), 1.0, ((x*16) + 8).toDouble()) + return Location(world, ((x*16) + 8).toDouble(), 1.0, ((z*16) + 8).toDouble()) } } \ No newline at end of file diff --git a/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationManager.kt b/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationManager.kt index 6a08d76..b9e0bfb 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationManager.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationManager.kt @@ -47,18 +47,15 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server ) var id = 0 - chunkmaster.sqliteManager.executeStatement( - """ - SELECT id FROM generation_tasks ORDER BY id DESC LIMIT 1 - """.trimIndent(), - HashMap() - ) { + chunkmaster.sqliteManager.executeStatement(""" + SELECT id FROM generation_tasks ORDER BY id DESC LIMIT 1 + """.trimIndent(), HashMap()) { it.next() id = it.getInt("id") } generationTask.onEndReached { - chunkmaster.logger.info("Task #${id} finished after ${it.count} chunks.") + chunkmaster.logger.info(chunkmaster.langManager.getLocalized("TASK_FINISHED", id, it.count)) removeTask(id) } @@ -89,7 +86,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server stopAfter: Int = -1 ) { if (!paused) { - chunkmaster.logger.info("Resuming chunk generation task for world \"${world.name}\"") + chunkmaster.logger.info(chunkmaster.langManager.getLocalized("RESUME_FOR_WORLD", world.name)) val generationTask = createGenerationTask(world, center, last, stopAfter) val task = server.scheduler.runTaskTimer( chunkmaster, generationTask, 200, // 10 sec delay @@ -97,7 +94,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server ) tasks.add(RunningTaskEntry(id, task, generationTask)) generationTask.onEndReached { - chunkmaster.logger.info("Task #${id} finished after ${generationTask.count} chunks.") + chunkmaster.logger.info(chunkmaster.langManager.getLocalized("TASK_FINISHED", id, generationTask.count)) removeTask(id) } } @@ -114,11 +111,9 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server } if (taskEntry != null) { taskEntry.cancel() - chunkmaster.sqliteManager.executeStatement( - """ + chunkmaster.sqliteManager.executeStatement(""" DELETE FROM generation_tasks WHERE id = ?; - """.trimIndent(), - HashMap(mapOf(1 to taskEntry.id)), + """.trimIndent(), HashMap(mapOf(1 to taskEntry.id)), null ) @@ -139,7 +134,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server * Loads tasks from the database and resumes them */ fun init() { - chunkmaster.logger.info("Creating task to load chunk generation Tasks later...") + chunkmaster.logger.info(chunkmaster.langManager.getLocalized("CREATE_DELAYED_LOAD")) server.scheduler.runTaskTimer(chunkmaster, Runnable { saveProgress() // save progress every 30 seconds }, 600, 600) @@ -162,7 +157,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server if (task.task.isCancelled) { removalSet.add(task) } - chunkmaster.logger.info("Canceled task #${task.id}") + chunkmaster.logger.info(chunkmaster.langManager.getLocalized("TASK_CANCELED", task.id)) } tasks.removeAll(removalSet) } @@ -171,8 +166,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server * Starts all generation tasks. */ fun startAll() { - chunkmaster.sqliteManager.executeStatement("SELECT * FROM generation_tasks", HashMap()) { - val res = it + chunkmaster.sqliteManager.executeStatement("SELECT * FROM generation_tasks", HashMap()) { res -> while (res.next()) { try { val id = res.getInt("id") @@ -184,13 +178,13 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server resumeTask(world!!, center, last, id, stopAfter) } } catch (error: NullPointerException) { - chunkmaster.logger.severe("Failed to load Task ${res.getInt("id")}.") + chunkmaster.logger.severe(chunkmaster.langManager.getLocalized("TASK_LOAD_FAILED", res.getInt("id"))) } } } if (tasks.isNotEmpty()) { - chunkmaster.logger.info("${tasks.size} saved tasks loaded.") + chunkmaster.logger.info(chunkmaster.langManager.getLocalized("TASK_LOAD_SUCCESS", tasks.size)) } } @@ -221,16 +215,30 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server for (task in tasks) { try { val genTask = task.generationTask - chunkmaster.logger.info( - """Task #${task.id} running for "${genTask.world.name}". - |Progress ${task.generationTask.count} chunks - |${if (task.generationTask.stopAfter > 0) "(${"%.2f".format( - (task.generationTask.count.toDouble() / - task.generationTask.stopAfter.toDouble()) * 100 - )}%)" else ""}. - | Speed: ${"%.1f".format(task.generationSpeed)} chunks/sec, - |Last Chunk: ${genTask.lastChunk.x}, ${genTask.lastChunk.z}""".trimMargin("|").replace('\n', ' ') - ) + 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 + chunkmaster.logger.info(""+etaSeconds) + val hours: Int = (etaSeconds/3600).toInt() + val minutes: Int = ((etaSeconds % 3600) / 60).toInt() + val seconds: Int = (etaSeconds % 60).toInt() + ", ETA: %d:%02d:%02d".format(hours, minutes, seconds) + } else { + "" + } + chunkmaster.logger.info(chunkmaster.langManager.getLocalized( + "TASK_PERIODIC_REPORT", + task.id, + genTask.world.name, + genTask.count, + percentage, + eta, + speed, + genTask.lastChunk.x, + genTask.lastChunk.z)) chunkmaster.sqliteManager.executeStatement( """ UPDATE generation_tasks SET last_x = ?, last_z = ? @@ -240,7 +248,7 @@ class GenerationManager(private val chunkmaster: Chunkmaster, private val server null ) } catch (error: Exception) { - chunkmaster.logger.warning("Exception when saving task progress ${error.message}") + chunkmaster.logger.warning(chunkmaster.langManager.getLocalized("TASK_SAVE_FAILED", error.toString())) } } } diff --git a/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationTask.kt b/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationTask.kt index d089772..92f3ea5 100644 --- a/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationTask.kt +++ b/src/main/kotlin/net/trivernis/chunkmaster/lib/generation/GenerationTask.kt @@ -4,13 +4,11 @@ import net.trivernis.chunkmaster.Chunkmaster import net.trivernis.chunkmaster.lib.Spiral import org.bukkit.Chunk import org.bukkit.World - /** * Interface for generation tasks. */ abstract class GenerationTask(plugin: Chunkmaster, centerChunk: ChunkCoordinates, startChunk: ChunkCoordinates) : Runnable { - abstract val stopAfter: Int abstract val world: World abstract val count: Int diff --git a/src/main/resources/i18n/DEFAULT.i18n.properties b/src/main/resources/i18n/DEFAULT.i18n.properties new file mode 100644 index 0000000..e0771de --- /dev/null +++ b/src/main/resources/i18n/DEFAULT.i18n.properties @@ -0,0 +1,48 @@ +RESUME_FOR_WORLD = Resuming chunk generation task for world '%s'... +TASK_FINISHED = Task #%d finished after %d chunks. +TASK_CANCELED = Canceled task #%s. +TASK_LOAD_FAILED = \u00A7cFailed to load task #%d. +TASK_LOAD_SUCCESS = %d saved tasks loaded. +TASK_NOT_FOUND = \u00A7cTask %s not found! +CREATE_DELAYED_LOAD = Creating task to load chunk generation Tasks later... +TASK_PERIODIC_REPORT = Task #%d running for '%s'. Progress: %d chunks %s %s, Speed: %.1f ch/s, Last Chunk: %d, %d +TASK_SAVE_FAILED = \u00A7cException when saving tasks: %s + +WORLD_NAME_REQUIRED = \u00A7cYou need to provide a world name! +WORLD_NOT_FOUND = \u00A7cWorld \u00A72%s \u00A7cnot found! +TASK_ALREADY_EXISTS = \u00A7cA task for '%s' already exists! +TASK_CREATION_SUCCESS = \u00A79Generation Task for world \u00A72%s \u00A79 until \u00A72%s \u00A79successfully created! +TASK_ID_REQUIRED = \u00A7cYou need to provide a task id! + +PAUSED_TASKS_HEADER = Currently Paused Generation Tasks +TASKS_ENTRY = - \u00A79#%d\u00A7r - \u00A72%s\u00A7r - \u00A72%d chunks %s\u00A7r +RUNNING_TASKS_HEADER = Currently Running Generation Tasks +NO_GENERATION_TASKS = There are no generation tasks. + +PAUSE_SUCCESS = \u00A79Paused all generation tasks. +ALREADY_PAUSED = \u00A7cThe generation process is already paused! + +RESUME_SUCCESS = \u00A79Resumed all generation Tasks. +NOT_PAUSED = \u00A7cThe generation process is not paused! + +CONFIG_RELOADING = Reloading the config file... +CONFIG_RELOADED = \u00A72The config file has been reloaded! + +TELEPORTED = \u00A79You have been teleported to chunk \u00A72%s, %s +TP_ONLY_PLAYER = \u00A7cThis command can only be executed by a player! + +NO_PERMISSION = \u00A7cYou do not have the permission for this command! +SUBCOMMAND_NOT_FOUND = \u00A7cSubcommand \u00A72%s \u00A7cnot found! + +STOPPING_ALL_TASKS = Stopping all generation tasks... +DB_INIT = Initializing database... +DB_INIT_FINISHED = Database fully initialized. +DB_INIT_EROR = Failed to init database: %s. + +DATABASE_CONNECTION_ERROR = \u00A7cCould not get the database connection! +SQL_ERROR = \u00A7cAn eror occured on sql %s! +NO_DATABASE_CONNECTION = \u00A7cCould not execute sql: No database connection. +CREATE_TABLE_DEFINITION = Created table %s with definition %s. +TABLE_CREATE_ERROR = \u00A7cError when creation table %s. +UPDATE_TABLE_DEFINITION = Updated table %s with sql %s. +UPDATE_TABLE_FAILED = Failed to update table %s with sql %s. \ No newline at end of file diff --git a/src/main/resources/i18n/de.i18n.properties b/src/main/resources/i18n/de.i18n.properties new file mode 100644 index 0000000..e1ee078 --- /dev/null +++ b/src/main/resources/i18n/de.i18n.properties @@ -0,0 +1,48 @@ +RESUME_FOR_WORLD = Setze das Chunk-Generieren f\u00fcr Welt '%s' fort... +TASK_FINISHED = Aufgabe #%d wurde nach %d chunks beendet. +TASK_CANCELED = Aufgabe #%s wurde abgebrochen. +TASK_LOAD_FAILED = \u00A7cAufgabe #%d konnte nicht geladen werden. +TASK_LOAD_SUCCESS = %d gespeicherte Aufgaben wurden geladen. +TASK_NOT_FOUND = \u00A7cAufgabe %s konnte nicht gefunden werden! +CREATE_DELAYED_LOAD = Erstelle einen Bukkit-Task zum verz\u00f6gerten Laden von Aufgaben... +TASK_PERIODIC_REPORT = Aufgabe #%d f\u00fcr Welt '%s'. Fortschritt: %d chunks %s %s, Geschwindigkeit: %.1f ch/s, Letzer Chunk: %d, %d +TASK_SAVE_FAILED = \u00A7cFehler beim Speichern der Aufgaben: %s + +WORLD_NAME_REQUIRED = \u00A7cDu musst einen Weltennamen angeben! +WORLD_NOT_FOUND = \u00A7c Die Welt \u00A72%s \u00A7cwurde nicht gefunden! +TASK_ALREADY_EXISTS = \u00A7cEs existiert bereits eine Aufgabe f\u00fcr \u00A72%s\u00A7c! +TASK_CREATION_SUCCESS = \u00A79Generierungs-Aufgabe \u00A72%s \u00A79 bis \u00A72%s \u00A79wurde erfolgreich erstellt! +TASK_ID_REQUIRED = \u00A7cDu musst eine Aufgaben-Id angeben! + +PAUSED_TASKS_HEADER = \u00A7lPausierte Generierungsaufgaben\u00A7r + +RUNNING_TASKS_HEADER = \u00A7lLaufende Generierungsaufgaben\u00A7r +NO_GENERATION_TASKS = Es gibt keine Aufgaben. + +PAUSE_SUCCESS = \u00A79Alle Aufgaben wurden pausiert. +ALREADY_PAUSED = \u00A7cDas Generieren ist bereits pausiert. + +RESUME_SUCCESS = \u00A79Alle Aufgaben wurden fortgesetzt. +NOT_PAUSED = \u00A7cEs gibt keine pausierten Aufgaben! + +CONFIG_RELOADING = Die Konfigurationsdatei wird neu eingelesen... +CONFIG_RELOADED = \u00A72Die Konfigurationsdatei wurde neu geladen! + +TELEPORTED = \u00A79Du wurdest zum Chunk \u00A72%s, %s \u00A79teleportiert +TP_ONLY_PLAYER = \u00A7cDieser Befehl kann nur von einem Spieler ausgef\u00fchrt werden. + +NO_PERMISSION = \u00A7cDu hast nicht die Rechte für diesen Befehl! +SUBCOMMAND_NOT_FOUND = \u00A7cUnteraktion \u00A72%s \u00A7cwurde nicht gefunden! + +STOPPING_ALL_TASKS = Stoppt alle Aufgaben... +DB_INIT = Initialisiere Datenbank... +DB_INIT_FINISHED = Die Datenbank wurde initialisiert. +DB_INIT_EROR = Fehler beim Initalisieren der Datenbank: %s. + +DATABASE_CONNECTION_ERROR = \u00A7cDie Datenbankverbindung konnte nicht erzeugt werden. +SQL_ERROR = \u00A7cEin Fehler trat mit sql %s auf! +NO_DATABASE_CONNECTION = \u00A7cSql konnte nicht ausgef\u00fchrt werden: Keine Datenbankverbindung. +CREATE_TABLE_DEFINITION = Tabelle %s mit Definition %s wurde erstellt. +TABLE_CREATE_ERROR = \u00A7cFehler 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. \ No newline at end of file diff --git a/src/main/resources/i18n/en.i18n.properties b/src/main/resources/i18n/en.i18n.properties new file mode 100644 index 0000000..e0771de --- /dev/null +++ b/src/main/resources/i18n/en.i18n.properties @@ -0,0 +1,48 @@ +RESUME_FOR_WORLD = Resuming chunk generation task for world '%s'... +TASK_FINISHED = Task #%d finished after %d chunks. +TASK_CANCELED = Canceled task #%s. +TASK_LOAD_FAILED = \u00A7cFailed to load task #%d. +TASK_LOAD_SUCCESS = %d saved tasks loaded. +TASK_NOT_FOUND = \u00A7cTask %s not found! +CREATE_DELAYED_LOAD = Creating task to load chunk generation Tasks later... +TASK_PERIODIC_REPORT = Task #%d running for '%s'. Progress: %d chunks %s %s, Speed: %.1f ch/s, Last Chunk: %d, %d +TASK_SAVE_FAILED = \u00A7cException when saving tasks: %s + +WORLD_NAME_REQUIRED = \u00A7cYou need to provide a world name! +WORLD_NOT_FOUND = \u00A7cWorld \u00A72%s \u00A7cnot found! +TASK_ALREADY_EXISTS = \u00A7cA task for '%s' already exists! +TASK_CREATION_SUCCESS = \u00A79Generation Task for world \u00A72%s \u00A79 until \u00A72%s \u00A79successfully created! +TASK_ID_REQUIRED = \u00A7cYou need to provide a task id! + +PAUSED_TASKS_HEADER = Currently Paused Generation Tasks +TASKS_ENTRY = - \u00A79#%d\u00A7r - \u00A72%s\u00A7r - \u00A72%d chunks %s\u00A7r +RUNNING_TASKS_HEADER = Currently Running Generation Tasks +NO_GENERATION_TASKS = There are no generation tasks. + +PAUSE_SUCCESS = \u00A79Paused all generation tasks. +ALREADY_PAUSED = \u00A7cThe generation process is already paused! + +RESUME_SUCCESS = \u00A79Resumed all generation Tasks. +NOT_PAUSED = \u00A7cThe generation process is not paused! + +CONFIG_RELOADING = Reloading the config file... +CONFIG_RELOADED = \u00A72The config file has been reloaded! + +TELEPORTED = \u00A79You have been teleported to chunk \u00A72%s, %s +TP_ONLY_PLAYER = \u00A7cThis command can only be executed by a player! + +NO_PERMISSION = \u00A7cYou do not have the permission for this command! +SUBCOMMAND_NOT_FOUND = \u00A7cSubcommand \u00A72%s \u00A7cnot found! + +STOPPING_ALL_TASKS = Stopping all generation tasks... +DB_INIT = Initializing database... +DB_INIT_FINISHED = Database fully initialized. +DB_INIT_EROR = Failed to init database: %s. + +DATABASE_CONNECTION_ERROR = \u00A7cCould not get the database connection! +SQL_ERROR = \u00A7cAn eror occured on sql %s! +NO_DATABASE_CONNECTION = \u00A7cCould not execute sql: No database connection. +CREATE_TABLE_DEFINITION = Created table %s with definition %s. +TABLE_CREATE_ERROR = \u00A7cError when creation table %s. +UPDATE_TABLE_DEFINITION = Updated table %s with sql %s. +UPDATE_TABLE_FAILED = Failed to update table %s with sql %s. \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index c63905a..dee18cb 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,6 +1,6 @@ main: net.trivernis.chunkmaster.Chunkmaster name: Chunkmaster -version: '0.13-beta' +version: '0.14-beta' description: Chunk commands plugin. author: Trivernis website: trivernis.net