mirror of
https://github.com/GayPizzaSpecifications/foundation.git
synced 2025-08-03 05:30:55 +00:00
Convert to Kotlin.
This commit is contained in:
parent
9413211a82
commit
7259de6c17
@ -1,6 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
java
|
kotlin("jvm") version "1.6.10"
|
||||||
id("com.github.johnrengelman.shadow") version("7.1.1")
|
id("com.github.johnrengelman.shadow") version "7.1.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "io.gorence"
|
group = "io.gorence"
|
||||||
@ -19,8 +19,15 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly("io.papermc.paper:paper-api:1.18.1-R0.1-SNAPSHOT")
|
// Kotlin dependencies
|
||||||
|
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||||
|
|
||||||
|
// Database layer
|
||||||
implementation("org.jetbrains.xodus:xodus-openAPI:1.3.232")
|
implementation("org.jetbrains.xodus:xodus-openAPI:1.3.232")
|
||||||
|
|
||||||
|
// Paper API
|
||||||
|
compileOnly("io.papermc.paper:paper-api:1.18.1-R0.1-SNAPSHOT")
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
package cloud.kubelet.foundation;
|
|
||||||
|
|
||||||
import cloud.kubelet.foundation.command.BackupCommand;
|
|
||||||
import io.papermc.paper.event.player.ChatEvent;
|
|
||||||
import java.io.File;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.Objects;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
|
||||||
|
|
||||||
public final class Foundation extends JavaPlugin implements Listener {
|
|
||||||
public static final boolean BACKUP_ENABLED = true;
|
|
||||||
private static final String BACKUPS_DIRECTORY = "backups";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnable() {
|
|
||||||
Path dataPath = getDataFolder().toPath();
|
|
||||||
Path backupPath = dataPath.resolve(BACKUPS_DIRECTORY);
|
|
||||||
|
|
||||||
// Create Foundation plugin directories.
|
|
||||||
dataPath.toFile().mkdir();
|
|
||||||
backupPath.toFile().mkdir();
|
|
||||||
|
|
||||||
// Register this as an event listener.
|
|
||||||
getServer().getPluginManager().registerEvents(this, this);
|
|
||||||
|
|
||||||
// Set up commands.
|
|
||||||
Objects.requireNonNull(getCommand("fbackup")).setExecutor(new BackupCommand(backupPath));
|
|
||||||
|
|
||||||
final var log = getSLF4JLogger();
|
|
||||||
log.info("Features:");
|
|
||||||
Util.printFeatureStatus(log, "Backup: ", BACKUP_ENABLED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDisable() {
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Component leftBracket = Component.text('[');
|
|
||||||
private final Component rightBracket = Component.text(']');
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
private void onChatMessage(ChatEvent e) {
|
|
||||||
e.setCancelled(true);
|
|
||||||
|
|
||||||
final var name = e.getPlayer().displayName();
|
|
||||||
final var component = Component.empty()
|
|
||||||
.append(leftBracket)
|
|
||||||
.append(name)
|
|
||||||
.append(rightBracket)
|
|
||||||
.append(Component.text(' '))
|
|
||||||
.append(e.message());
|
|
||||||
|
|
||||||
getServer().sendMessage(component);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
package cloud.kubelet.foundation;
|
|
||||||
|
|
||||||
import net.kyori.adventure.text.format.TextColor;
|
|
||||||
|
|
||||||
public final class TextColors {
|
|
||||||
public static final TextColor AMARANTH_PINK = TextColor.fromHexString("#F7A8B8");
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
package cloud.kubelet.foundation;
|
|
||||||
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import net.kyori.adventure.text.format.TextColor;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
|
|
||||||
public class Util {
|
|
||||||
|
|
||||||
private static final Component leftBracket = Component.text('[');
|
|
||||||
private static final Component rightBracket = Component.text(']');
|
|
||||||
private static final Component whitespace = Component.text(' ');
|
|
||||||
private static final Component foundationName = Component.text("Foundation");
|
|
||||||
|
|
||||||
public static void printFeatureStatus(Logger logger, String feature, boolean state) {
|
|
||||||
logger.info("{}: {}", feature, state ? "Enabled" : "Disabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Component formatSystemMessage(String message) {
|
|
||||||
return formatSystemMessage(TextColors.AMARANTH_PINK, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Component formatSystemMessage(TextColor prefixColor, String message) {
|
|
||||||
return leftBracket
|
|
||||||
.append(foundationName.color(prefixColor))
|
|
||||||
.append(rightBracket)
|
|
||||||
.append(whitespace)
|
|
||||||
.append(Component.text(message));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,131 +0,0 @@
|
|||||||
package cloud.kubelet.foundation.command;
|
|
||||||
|
|
||||||
import cloud.kubelet.foundation.Foundation;
|
|
||||||
import cloud.kubelet.foundation.Util;
|
|
||||||
import java.io.BufferedOutputStream;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.zip.ZipEntry;
|
|
||||||
import java.util.zip.ZipOutputStream;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import net.kyori.adventure.text.format.TextColor;
|
|
||||||
import org.bukkit.Server;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.command.Command;
|
|
||||||
import org.bukkit.command.CommandExecutor;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class BackupCommand implements CommandExecutor {
|
|
||||||
|
|
||||||
private static final AtomicBoolean RUNNING = new AtomicBoolean();
|
|
||||||
private final Path backupPath;
|
|
||||||
|
|
||||||
public BackupCommand(Path backupPath) {
|
|
||||||
this.backupPath = backupPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
|
|
||||||
@NotNull String label, @NotNull String[] args) {
|
|
||||||
if (!Foundation.BACKUP_ENABLED) {
|
|
||||||
sender.sendMessage(
|
|
||||||
Component
|
|
||||||
.text("Backup is not enabled.")
|
|
||||||
.color(TextColor.fromHexString("#FF0000"))
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (RUNNING.get()) {
|
|
||||||
sender.sendMessage(
|
|
||||||
Component
|
|
||||||
.text("Backup is already running.")
|
|
||||||
.color(TextColor.fromHexString("#FF0000"))
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
runBackup(sender);
|
|
||||||
} catch (Exception e) {
|
|
||||||
sender.sendMessage(String.format("Failed to backup: %s", e.getMessage()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runBackup(CommandSender sender) throws IOException {
|
|
||||||
RUNNING.set(true);
|
|
||||||
|
|
||||||
final var server = sender.getServer();
|
|
||||||
server.sendMessage(Util.formatSystemMessage("Backup started."));
|
|
||||||
|
|
||||||
final var backupFile = backupPath.resolve(
|
|
||||||
String.format("backup-%s.zip", Instant.now().toString())).toFile();
|
|
||||||
final var zipFileStream = new FileOutputStream(backupFile);
|
|
||||||
final var zipStream = new ZipOutputStream(new BufferedOutputStream(zipFileStream));
|
|
||||||
|
|
||||||
try (zipFileStream; zipStream) {
|
|
||||||
backupPlugins(server, zipStream);
|
|
||||||
backupWorlds(server, zipStream);
|
|
||||||
} finally {
|
|
||||||
RUNNING.set(false);
|
|
||||||
server.sendMessage(Util.formatSystemMessage("Backup finished."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void backupPlugins(Server server, ZipOutputStream zipStream) {
|
|
||||||
try {
|
|
||||||
addDirectoryToZip(zipStream, server.getPluginsFolder().toPath());
|
|
||||||
} catch (IOException e) {
|
|
||||||
// TODO: Add error handling.
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void backupWorlds(Server server, ZipOutputStream zipStream) {
|
|
||||||
final var worlds = server.getWorlds();
|
|
||||||
for (World world : worlds) {
|
|
||||||
final var worldPath = world.getWorldFolder().toPath();
|
|
||||||
|
|
||||||
// Save the world.
|
|
||||||
world.save();
|
|
||||||
|
|
||||||
// Disable auto saving to prevent any world corruption while creating a ZIP.
|
|
||||||
world.setAutoSave(false);
|
|
||||||
|
|
||||||
try {
|
|
||||||
addDirectoryToZip(zipStream, worldPath);
|
|
||||||
} catch (IOException e) {
|
|
||||||
// TODO: Add error handling.
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-enable auto saving for this world.
|
|
||||||
world.setAutoSave(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addDirectoryToZip(ZipOutputStream zipStream, Path directoryPath) throws IOException {
|
|
||||||
final var paths = Files.walk(directoryPath)
|
|
||||||
.filter(Files::isRegularFile)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
for (Path path : paths) {
|
|
||||||
try (InputStream fileStream = new FileInputStream(path.toFile())) {
|
|
||||||
final var entry = new ZipEntry(path.toString());
|
|
||||||
zipStream.putNextEntry(entry);
|
|
||||||
int n;
|
|
||||||
byte[] buffer = new byte[1024];
|
|
||||||
while ((n = fileStream.read(buffer)) > -1) {
|
|
||||||
zipStream.write(buffer, 0, n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
57
src/main/kotlin/cloud/kubelet/foundation/Foundation.kt
Normal file
57
src/main/kotlin/cloud/kubelet/foundation/Foundation.kt
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package cloud.kubelet.foundation
|
||||||
|
|
||||||
|
import cloud.kubelet.foundation.command.BackupCommand
|
||||||
|
import io.papermc.paper.event.player.ChatEvent
|
||||||
|
import net.kyori.adventure.text.Component
|
||||||
|
import org.bukkit.command.CommandExecutor
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
|
||||||
|
class Foundation : JavaPlugin(), Listener {
|
||||||
|
override fun onEnable() {
|
||||||
|
val dataPath = dataFolder.toPath()
|
||||||
|
val backupPath = dataPath.resolve(BACKUPS_DIRECTORY)
|
||||||
|
|
||||||
|
// Create Foundation plugin directories.
|
||||||
|
dataPath.toFile().mkdir()
|
||||||
|
backupPath.toFile().mkdir()
|
||||||
|
|
||||||
|
// Register this as an event listener.
|
||||||
|
server.pluginManager.registerEvents(this, this)
|
||||||
|
|
||||||
|
// Register commands.
|
||||||
|
registerCommandExecutor("fbackup", BackupCommand(this, backupPath))
|
||||||
|
|
||||||
|
val log = slF4JLogger
|
||||||
|
log.info("Features:")
|
||||||
|
Util.printFeatureStatus(log, "Backup: ", BACKUP_ENABLED)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerCommandExecutor(name: String, executor: CommandExecutor) {
|
||||||
|
val command = getCommand(name) ?: throw Exception("Failed to get $name command")
|
||||||
|
command.setExecutor(executor)
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
private fun onChatMessage(e: ChatEvent) {
|
||||||
|
e.isCancelled = true
|
||||||
|
val name = e.player.displayName()
|
||||||
|
val component = Component.empty()
|
||||||
|
.append(leftBracket)
|
||||||
|
.append(name)
|
||||||
|
.append(rightBracket)
|
||||||
|
.append(Component.text(' '))
|
||||||
|
.append(e.message())
|
||||||
|
server.sendMessage(component)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val BACKUPS_DIRECTORY = "backups"
|
||||||
|
|
||||||
|
private val leftBracket: Component = Component.text('[')
|
||||||
|
private val rightBracket: Component = Component.text(']')
|
||||||
|
|
||||||
|
const val BACKUP_ENABLED = true
|
||||||
|
}
|
||||||
|
}
|
7
src/main/kotlin/cloud/kubelet/foundation/TextColors.kt
Normal file
7
src/main/kotlin/cloud/kubelet/foundation/TextColors.kt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package cloud.kubelet.foundation
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.format.TextColor
|
||||||
|
|
||||||
|
object TextColors {
|
||||||
|
val AMARANTH_PINK = TextColor.fromHexString("#F7A8B8")
|
||||||
|
}
|
28
src/main/kotlin/cloud/kubelet/foundation/Util.kt
Normal file
28
src/main/kotlin/cloud/kubelet/foundation/Util.kt
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package cloud.kubelet.foundation
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component
|
||||||
|
import net.kyori.adventure.text.format.TextColor
|
||||||
|
import org.slf4j.Logger
|
||||||
|
|
||||||
|
object Util {
|
||||||
|
private val leftBracket: Component = Component.text('[')
|
||||||
|
private val rightBracket: Component = Component.text(']')
|
||||||
|
private val whitespace: Component = Component.text(' ')
|
||||||
|
private val foundationName: Component = Component.text("Foundation")
|
||||||
|
|
||||||
|
fun printFeatureStatus(logger: Logger, feature: String?, state: Boolean) {
|
||||||
|
logger.info("{}: {}", feature, if (state) "Enabled" else "Disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun formatSystemMessage(message: String?): Component {
|
||||||
|
return formatSystemMessage(TextColors.AMARANTH_PINK, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun formatSystemMessage(prefixColor: TextColor?, message: String?): Component {
|
||||||
|
return leftBracket
|
||||||
|
.append(foundationName.color(prefixColor))
|
||||||
|
.append(rightBracket)
|
||||||
|
.append(whitespace)
|
||||||
|
.append(Component.text(message!!))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,144 @@
|
|||||||
|
package cloud.kubelet.foundation.command
|
||||||
|
|
||||||
|
import cloud.kubelet.foundation.Foundation
|
||||||
|
import cloud.kubelet.foundation.Util
|
||||||
|
import net.kyori.adventure.text.Component
|
||||||
|
import net.kyori.adventure.text.format.TextColor
|
||||||
|
import org.bukkit.Server
|
||||||
|
import org.bukkit.command.Command
|
||||||
|
import org.bukkit.command.CommandExecutor
|
||||||
|
import org.bukkit.command.CommandSender
|
||||||
|
import java.io.BufferedOutputStream
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
import java.util.zip.ZipEntry
|
||||||
|
import java.util.zip.ZipOutputStream
|
||||||
|
|
||||||
|
class BackupCommand(
|
||||||
|
private val plugin: Foundation,
|
||||||
|
private val backupPath: Path
|
||||||
|
) : CommandExecutor {
|
||||||
|
override fun onCommand(
|
||||||
|
sender: CommandSender, command: Command,
|
||||||
|
label: String, args: Array<String>
|
||||||
|
): Boolean {
|
||||||
|
if (!Foundation.BACKUP_ENABLED) {
|
||||||
|
sender.sendMessage(
|
||||||
|
Component
|
||||||
|
.text("Backup is not enabled.")
|
||||||
|
.color(TextColor.fromHexString("#FF0000"))
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RUNNING.get()) {
|
||||||
|
sender.sendMessage(
|
||||||
|
Component
|
||||||
|
.text("Backup is already running.")
|
||||||
|
.color(TextColor.fromHexString("#FF0000"))
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val server = sender.server
|
||||||
|
server.scheduler.runTaskAsynchronously(plugin, Runnable {
|
||||||
|
runBackup(server)
|
||||||
|
})
|
||||||
|
} catch (e: Exception) {
|
||||||
|
sender.sendMessage(String.format("Failed to backup: %s", e.message))
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun runBackup(server: Server) {
|
||||||
|
RUNNING.set(true)
|
||||||
|
|
||||||
|
server.sendMessage(Util.formatSystemMessage("Backup started."))
|
||||||
|
|
||||||
|
val backupFile =
|
||||||
|
backupPath.resolve(String.format("backup-%s.zip", Instant.now().toString())).toFile()
|
||||||
|
|
||||||
|
try {
|
||||||
|
FileOutputStream(backupFile).use { zipFileStream ->
|
||||||
|
ZipOutputStream(BufferedOutputStream(zipFileStream)).use { zipStream ->
|
||||||
|
backupPlugins(server, zipStream)
|
||||||
|
backupWorlds(server, zipStream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
RUNNING.set(false)
|
||||||
|
server.sendMessage(Util.formatSystemMessage("Backup finished."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun backupPlugins(server: Server, zipStream: ZipOutputStream) {
|
||||||
|
try {
|
||||||
|
addDirectoryToZip(zipStream, server.pluginsFolder.toPath())
|
||||||
|
} catch (e: IOException) {
|
||||||
|
// TODO: Add error handling.
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun backupWorlds(server: Server, zipStream: ZipOutputStream) {
|
||||||
|
val worlds = server.worlds
|
||||||
|
for (world in worlds) {
|
||||||
|
val worldPath = world.worldFolder.toPath()
|
||||||
|
|
||||||
|
// Save the world.
|
||||||
|
server.scheduler.runTask(plugin, Runnable {
|
||||||
|
world.save()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Disable auto saving to prevent any world corruption while creating a ZIP.
|
||||||
|
world.isAutoSave = false
|
||||||
|
try {
|
||||||
|
addDirectoryToZip(zipStream, worldPath)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
// TODO: Add error handling.
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-enable auto saving for this world.
|
||||||
|
world.isAutoSave = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addDirectoryToZip(zipStream: ZipOutputStream, directoryPath: Path) {
|
||||||
|
val paths = Files.walk(directoryPath)
|
||||||
|
.filter { path: Path? -> Files.isRegularFile(path) }
|
||||||
|
.toList()
|
||||||
|
val buffer = ByteArray(1024)
|
||||||
|
val backupsPath = backupPath.toRealPath()
|
||||||
|
|
||||||
|
for (path in paths) {
|
||||||
|
val realPath = path.toRealPath()
|
||||||
|
|
||||||
|
if (realPath.startsWith(backupsPath)) {
|
||||||
|
plugin.slF4JLogger.info("Skipping file for backup: {}", realPath)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
FileInputStream(path.toFile()).use { fileStream ->
|
||||||
|
val entry = ZipEntry(path.toString())
|
||||||
|
zipStream.putNextEntry(entry)
|
||||||
|
|
||||||
|
var n: Int
|
||||||
|
while (fileStream.read(buffer).also { n = it } > -1) {
|
||||||
|
zipStream.write(buffer, 0, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val RUNNING = AtomicBoolean()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user