mirror of
https://github.com/GayPizzaSpecifications/foundation.git
synced 2025-08-02 13:10:55 +00:00
Convert to Kotlin.
This commit is contained in:
parent
9413211a82
commit
7259de6c17
@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
java
|
||||
id("com.github.johnrengelman.shadow") version("7.1.1")
|
||||
kotlin("jvm") version "1.6.10"
|
||||
id("com.github.johnrengelman.shadow") version "7.1.1"
|
||||
}
|
||||
|
||||
group = "io.gorence"
|
||||
@ -19,8 +19,15 @@ repositories {
|
||||
}
|
||||
|
||||
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")
|
||||
|
||||
// Paper API
|
||||
compileOnly("io.papermc.paper:paper-api:1.18.1-R0.1-SNAPSHOT")
|
||||
}
|
||||
|
||||
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