diff --git a/src/main/java/de/shiewk/smoderation/SModeration.java b/src/main/java/de/shiewk/smoderation/SModeration.java index 976b782..d8075cb 100644 --- a/src/main/java/de/shiewk/smoderation/SModeration.java +++ b/src/main/java/de/shiewk/smoderation/SModeration.java @@ -8,24 +8,31 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.logger.slf4j.ComponentLogger; import org.bukkit.command.PluginCommand; import org.bukkit.plugin.java.JavaPlugin; +import java.io.File; + import static org.bukkit.Bukkit.getPluginManager; public final class SModeration extends JavaPlugin { public static final PunishmentContainer container = new PunishmentContainer(); + public static ComponentLogger LOGGER = null; public static SModeration PLUGIN = null; + public static File SAVE_FILE = null; public static final TextColor PRIMARY_COLOR = TextColor.color(212, 0, 255); public static final TextColor SECONDARY_COLOR = TextColor.color(52, 143, 255); public static final TextColor INACTIVE_COLOR = NamedTextColor.GRAY; - public static final TextComponent CHAT_PREFIX = Component.text("SM \u00BB ").color(SECONDARY_COLOR); + public static final TextComponent CHAT_PREFIX = Component.text("SM \u00BB ").color(PRIMARY_COLOR); @Override public void onLoad() { + LOGGER = getComponentLogger(); PLUGIN = this; + SAVE_FILE = new File(this.getDataFolder().getAbsolutePath() + "/container.gz"); } @Override @@ -67,5 +74,12 @@ public final class SModeration extends JavaPlugin { assert unban != null; unban.setExecutor(new UnbanCommand()); unban.setTabCompleter(new UnbanCommand()); + + container.load(SAVE_FILE); + } + + @Override + public void onDisable() { + SModeration.container.save(SModeration.SAVE_FILE); } } diff --git a/src/main/java/de/shiewk/smoderation/listener/PunishmentListener.java b/src/main/java/de/shiewk/smoderation/listener/PunishmentListener.java index 28df11b..e5cef23 100644 --- a/src/main/java/de/shiewk/smoderation/listener/PunishmentListener.java +++ b/src/main/java/de/shiewk/smoderation/listener/PunishmentListener.java @@ -12,6 +12,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.event.world.WorldSaveEvent; import static de.shiewk.smoderation.SModeration.CHAT_PREFIX; @@ -59,4 +60,11 @@ public class PunishmentListener implements Listener { } } } + + @EventHandler + public void onWorldSave(WorldSaveEvent event){ + if (event.getWorld().equals(Bukkit.getServer().getWorlds().get(0))){ + SModeration.container.save(SModeration.SAVE_FILE); + } + } } diff --git a/src/main/java/de/shiewk/smoderation/punishments/Punishment.java b/src/main/java/de/shiewk/smoderation/punishments/Punishment.java index 2e3698d..3f89896 100644 --- a/src/main/java/de/shiewk/smoderation/punishments/Punishment.java +++ b/src/main/java/de/shiewk/smoderation/punishments/Punishment.java @@ -9,7 +9,10 @@ import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; -import java.nio.ByteBuffer; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.UUID; import static de.shiewk.smoderation.SModeration.*; @@ -25,12 +28,41 @@ public class Punishment { private UUID undoneBy; public Punishment(PunishmentType type, long time, long until, UUID by, UUID to, String reason) { + this(type, time, until, by, to, reason, null); + } + + private Punishment(PunishmentType type, long time, long until, UUID by, UUID to, String reason, UUID undoneBy) { this.type = type; this.time = time; this.until = until; this.by = by; this.to = to; this.reason = reason; + this.undoneBy = undoneBy; + } + + private static byte[] readStreamInternal(InputStream stream, int len) throws IOException { + final byte[] bytes = stream.readNBytes(len); + if (bytes.length != len){ + throw new EOFException("Stream has ended before enough bytes were read"); + } + return bytes; + } + + public static Punishment load(InputStream in) throws IOException { + PunishmentType type = PunishmentType.values()[ByteUtil.bytesToInt(readStreamInternal(in, 4))]; + long time = ByteUtil.bytesToLong(readStreamInternal(in, 8)); + long until = ByteUtil.bytesToLong(readStreamInternal(in, 8)); + UUID by = ByteUtil.bytesToUuid(readStreamInternal(in, 16)); + UUID to = ByteUtil.bytesToUuid(readStreamInternal(in, 16)); + int reasonLen = ByteUtil.bytesToInt(readStreamInternal(in, 4)); + String reason = new String(readStreamInternal(in, reasonLen)); + UUID undoneBy = null; + boolean undone = in.read() == 1; + if (undone){ + undoneBy = ByteUtil.bytesToUuid(readStreamInternal(in, 16)); + } + return new Punishment(type, time, until, by, to, reason, undoneBy); } public boolean wasUndone(){ @@ -66,21 +98,19 @@ public class Punishment { private static final int BUFFER_LENGTH = 56; - public byte[] toBytes(){ + public void writeBytes(OutputStream stream) throws IOException { + stream.write(ByteUtil.intToBytes(type.ordinal())); + stream.write(ByteUtil.longToBytes(time)); + stream.write(ByteUtil.longToBytes(until)); + stream.write(ByteUtil.uuidToBytes(by)); + stream.write(ByteUtil.uuidToBytes(to)); final byte[] reasonBytes = reason.getBytes(); - ByteBuffer buffer = ByteBuffer.allocate(BUFFER_LENGTH + reasonBytes.length + (undoneBy != null ? 17 : 1)); - buffer.putInt(0, type.ordinal()); - buffer.putLong(4, time); - buffer.putLong(12, until); - buffer.put(20, ByteUtil.uuidToBytes(by)); - buffer.put(36, ByteUtil.uuidToBytes(to)); - buffer.putInt(40, reason.length()); - buffer.put(44, reasonBytes); - buffer.put(44+reasonBytes.length, undoneBy != null ? (byte) 1 : (byte) 0); - if (undoneBy != null){ - buffer.put(44+reasonBytes.length+1, ByteUtil.uuidToBytes(undoneBy)); + stream.write(ByteUtil.intToBytes(reasonBytes.length)); + stream.write(reasonBytes); + stream.write(wasUndone() ? 1 : 0); + if (wasUndone()){ + stream.write(ByteUtil.uuidToBytes(undoneBy)); } - return buffer.array(); } private Component undoMessage(){ diff --git a/src/main/java/de/shiewk/smoderation/storage/PunishmentContainer.java b/src/main/java/de/shiewk/smoderation/storage/PunishmentContainer.java index 8f5a406..7c30fb2 100644 --- a/src/main/java/de/shiewk/smoderation/storage/PunishmentContainer.java +++ b/src/main/java/de/shiewk/smoderation/storage/PunishmentContainer.java @@ -1,17 +1,22 @@ package de.shiewk.smoderation.storage; +import de.shiewk.smoderation.SModeration; import de.shiewk.smoderation.punishments.Punishment; +import net.kyori.adventure.text.logger.slf4j.ComponentLogger; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Predicate; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; public class PunishmentContainer { @@ -68,4 +73,54 @@ public class PunishmentContainer { public ArrayList copy() { return new ArrayList<>(punishments); } + + public void load(File file){ + final ComponentLogger logger = SModeration.LOGGER; + try { + logger.info("Loading from {}", file.getPath()); + if (!file.isFile()){ + logger.warn("The file does not exist."); + } else { + try (FileInputStream fin = new FileInputStream(file)){ + GZIPInputStream gzin = new GZIPInputStream(fin); + while (gzin.available() > 0){ + add(Punishment.load(gzin)); + } + } + logger.info("Successfully loaded {} items.", punishments.size()); + } + } catch (EOFException e) { + logger.error("The file was not correctly saved, {} items could be recovered!", this.punishments.size()); + } catch (IOException e){ + logger.error("An error occurred while loading: {}", e.toString()); + for (StackTraceElement stackTraceElement : e.getStackTrace()) { + logger.error(stackTraceElement.toString()); + } + } + } + + public void save(File file) { + final ComponentLogger logger = SModeration.LOGGER; + try { + logger.info("Saving to {}", file.getPath()); + if (!file.isFile()){ + file.mkdirs(); + file.delete(); + file.createNewFile(); + } + try (FileOutputStream outputStream = new FileOutputStream(file)) { + GZIPOutputStream gzout = new GZIPOutputStream(outputStream); + for (Punishment punishment : copy()) { + punishment.writeBytes(gzout); + } + gzout.close(); + } + logger.info("Successfully saved."); + } catch (IOException e){ + logger.error("An error occurred while saving: {}", e.toString()); + for (StackTraceElement stackTraceElement : e.getStackTrace()) { + logger.error(stackTraceElement.toString()); + } + } + } } diff --git a/src/main/java/de/shiewk/smoderation/util/ByteUtil.java b/src/main/java/de/shiewk/smoderation/util/ByteUtil.java index 1eae96e..559c371 100644 --- a/src/main/java/de/shiewk/smoderation/util/ByteUtil.java +++ b/src/main/java/de/shiewk/smoderation/util/ByteUtil.java @@ -42,4 +42,19 @@ public abstract class ByteUtil { long m = bytesToLong(new byte[]{ i[0], i[1], i[2], i[3], i[4], i[5], i[6], i[7] }); return new UUID(m, l); } + + public static int bytesToInt(byte[] bytes) { + if (bytes.length != 4){ + throw new IllegalArgumentException("length must be 4"); + } + ByteBuffer buffer = ByteBuffer.allocate(4); + buffer.put(0, bytes); + return buffer.getInt(0); + } + + public static byte[] intToBytes(int value) { + ByteBuffer buffer = ByteBuffer.allocate(4); + buffer.putInt(value); + return buffer.array(); + } }