diff --git a/.gradle/8.5/checksums/checksums.lock b/.gradle/8.5/checksums/checksums.lock index 7c1a560..3f3fdf9 100644 Binary files a/.gradle/8.5/checksums/checksums.lock and b/.gradle/8.5/checksums/checksums.lock differ diff --git a/.gradle/8.5/checksums/md5-checksums.bin b/.gradle/8.5/checksums/md5-checksums.bin index 5268b4d..599822c 100644 Binary files a/.gradle/8.5/checksums/md5-checksums.bin and b/.gradle/8.5/checksums/md5-checksums.bin differ diff --git a/.gradle/8.5/checksums/sha1-checksums.bin b/.gradle/8.5/checksums/sha1-checksums.bin index 44d0c1a..be7a183 100644 Binary files a/.gradle/8.5/checksums/sha1-checksums.bin and b/.gradle/8.5/checksums/sha1-checksums.bin differ diff --git a/.gradle/8.5/executionHistory/executionHistory.bin b/.gradle/8.5/executionHistory/executionHistory.bin index 070bcd1..1a5e002 100644 Binary files a/.gradle/8.5/executionHistory/executionHistory.bin and b/.gradle/8.5/executionHistory/executionHistory.bin differ diff --git a/.gradle/8.5/executionHistory/executionHistory.lock b/.gradle/8.5/executionHistory/executionHistory.lock index fdf7eea..c279a91 100644 Binary files a/.gradle/8.5/executionHistory/executionHistory.lock and b/.gradle/8.5/executionHistory/executionHistory.lock differ diff --git a/.gradle/8.5/fileHashes/fileHashes.bin b/.gradle/8.5/fileHashes/fileHashes.bin index b0bd502..89695c9 100644 Binary files a/.gradle/8.5/fileHashes/fileHashes.bin and b/.gradle/8.5/fileHashes/fileHashes.bin differ diff --git a/.gradle/8.5/fileHashes/fileHashes.lock b/.gradle/8.5/fileHashes/fileHashes.lock index aa5d31e..fc78c35 100644 Binary files a/.gradle/8.5/fileHashes/fileHashes.lock and b/.gradle/8.5/fileHashes/fileHashes.lock differ diff --git a/.gradle/8.5/fileHashes/resourceHashesCache.bin b/.gradle/8.5/fileHashes/resourceHashesCache.bin index b56e646..add70a5 100644 Binary files a/.gradle/8.5/fileHashes/resourceHashesCache.bin and b/.gradle/8.5/fileHashes/resourceHashesCache.bin differ diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 4e38d46..77f36e0 100644 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe index 31823eb..c020374 100644 Binary files a/.gradle/file-system.probe and b/.gradle/file-system.probe differ diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/NBTConfig.class b/build/classes/java/main/me/trouper/sentinel/data/config/NBTConfig.class index 0247896..a81e4ca 100644 Binary files a/build/classes/java/main/me/trouper/sentinel/data/config/NBTConfig.class and b/build/classes/java/main/me/trouper/sentinel/data/config/NBTConfig.class differ diff --git a/build/classes/java/main/me/trouper/sentinel/server/gui/MainGUI.class b/build/classes/java/main/me/trouper/sentinel/server/gui/MainGUI.class index 6a7713c..0af32fb 100644 Binary files a/build/classes/java/main/me/trouper/sentinel/server/gui/MainGUI.class and b/build/classes/java/main/me/trouper/sentinel/server/gui/MainGUI.class differ diff --git a/build/classes/java/main/me/trouper/sentinel/utils/FileUtils.class b/build/classes/java/main/me/trouper/sentinel/utils/FileUtils.class index 6f395c1..629fc4f 100644 Binary files a/build/classes/java/main/me/trouper/sentinel/utils/FileUtils.class and b/build/classes/java/main/me/trouper/sentinel/utils/FileUtils.class differ diff --git a/build/classes/java/main/me/trouper/sentinel/utils/Text.class b/build/classes/java/main/me/trouper/sentinel/utils/Text.class index 4bde813..f042c3a 100644 Binary files a/build/classes/java/main/me/trouper/sentinel/utils/Text.class and b/build/classes/java/main/me/trouper/sentinel/utils/Text.class differ diff --git a/build/tmp/compileJava/previous-compilation-data.bin b/build/tmp/compileJava/previous-compilation-data.bin index 586d9ff..87f6c0e 100644 Binary files a/build/tmp/compileJava/previous-compilation-data.bin and b/build/tmp/compileJava/previous-compilation-data.bin differ diff --git a/src/main/java/me/trouper/sentinel/data/storage/NBTStorage.java b/src/main/java/me/trouper/sentinel/data/storage/NBTStorage.java index 2a0e9a4..de7a0cf 100644 --- a/src/main/java/me/trouper/sentinel/data/storage/NBTStorage.java +++ b/src/main/java/me/trouper/sentinel/data/storage/NBTStorage.java @@ -1,13 +1,22 @@ package me.trouper.sentinel.data.storage; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; +import com.github.retrooper.packetevents.protocol.nbt.serializer.NBTSerializer; +import de.tr7zw.changeme.nbtapi.NBTContainer; +import de.tr7zw.changeme.nbtapi.NBTItem; +import io.github.itzispyder.pdk.plugin.builders.ItemBuilder; import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable; import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import net.md_5.bungee.api.chat.hover.content.ItemSerializer; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.inventory.ItemStack; import java.io.*; -import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.Base64; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -16,22 +25,7 @@ public class NBTStorage implements JsonSerializable { // Mapping from file name to owner UUID (as a String) public Map caughtItems = new HashMap<>(); - - private final File mappingFile; - private final File storageDir; - - public NBTStorage() { - // Create the storage directory: /storage/nbt/ inside the plugin data folder - File dataFolder = Sentinel.getInstance().getDirector().io.getDataFolder(); - storageDir = new File(dataFolder, "storage/nbt"); - if (!storageDir.exists()) { - storageDir.mkdirs(); - } - // The mapping file that stores the file-name to owner UUID mapping - mappingFile = new File(dataFolder, "storage/nbt.json"); - mappingFile.getParentFile().mkdirs(); - } - + /** * Stores an ItemStack's serialized NBT to a unique file * and maps the generated file name to the owner UUID. @@ -41,11 +35,12 @@ public class NBTStorage implements JsonSerializable { */ public void storeItem(ItemStack item, UUID owner) { // Generate a unique file name with a .nbt extension + File storageDir = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "storage/nbt"); String fileName = UUID.randomUUID().toString() + ".nbt"; File file = new File(storageDir, fileName); try (FileOutputStream fos = new FileOutputStream(file); - OutputStreamWriter writer = new OutputStreamWriter(fos)) { - + OutputStreamWriter writer = new OutputStreamWriter(fos, StandardCharsets.UTF_8)) { + String nbt = serializeItem(item); writer.write(nbt); } catch (IOException e) { @@ -55,25 +50,98 @@ public class NBTStorage implements JsonSerializable { caughtItems.put(fileName, owner.toString()); save(); } + + public boolean deleteItem(String fileName) { + File storageDir = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "storage/nbt"); + File file = new File(storageDir, fileName); + caughtItems.remove(fileName); + save(); + return file.delete(); + } - /** - * Placeholder for item serialization. - * Replace this with an actual NBT serialization logic. - * - * @param item the ItemStack to serialize - * @return a String representing the NBT data of the item - */ - private String serializeItem(ItemStack item) { - - return item.toString(); + public static ItemStack getItem(String fileName) { + File storageDir = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "storage/nbt"); + File file = new File(storageDir, fileName); + try (FileInputStream fis = new FileInputStream(file)) { + StringBuilder b64 = new StringBuilder(); + int content; + while ((content = fis.read()) != -1) { + b64.append((char) content); + } + //ServerUtils.verbose("Getting item with fis: " + b64); + return deserializeItem(b64.toString()); + } catch (FileNotFoundException e) { + Sentinel.getInstance().getDirector().io.nbtStorage.caughtItems.remove(fileName); + Sentinel.getInstance().getDirector().io.nbtStorage.save(); + return new ItemBuilder().material(Material.STRUCTURE_VOID) + .name(Text.color("&cFile not found.")) + .lore(Text.color("&7This item no longer exists and has been removed from the list.")) + .build(); + } catch (IOException e) { + e.printStackTrace(); + return new ItemBuilder().material(Material.STRUCTURE_VOID) + .name(Text.color("&cUnknown IO exception.")) + .lore(Text.color("&4Check Console.")) + .build(); + } } - // Make a deserialize method too. - + public static String serializeItem(ItemStack item) { + if (item == null) { + return null; + } + try { + // Serialize ItemStack to a Map + Map serializedItem = item.serialize(); + + // Save the Map into a YAML configuration + YamlConfiguration config = new YamlConfiguration(); + config.set("item", serializedItem); + String yamlString = config.saveToString(); + + // Encode YAML string to Base64 + return Base64.getEncoder().encodeToString(yamlString.getBytes(StandardCharsets.UTF_8)); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static ItemStack deserializeItem(String data) { + if (data == null || data.isEmpty()) { + return null; + } + try { + // Decode Base64 to YAML string + byte[] decodedData = Base64.getDecoder().decode(data); + String yamlString = new String(decodedData, StandardCharsets.UTF_8); + + // Load YAML configuration from string + YamlConfiguration config = new YamlConfiguration(); + config.loadFromString(yamlString); + + // Extract the serialized Map from the configuration + ConfigurationSection itemSection = config.getConfigurationSection("item"); + if (itemSection == null) { + return null; // Invalid data + } + + // Convert ConfigurationSection to a nested Map + Map serializedItem = itemSection.getValues(true); + + // Deserialize the Map back into an ItemStack + return ItemStack.deserialize(serializedItem); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } @Override public File getFile() { - return mappingFile; + File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "storage/nbt.json"); + new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "storage/nbt").mkdirs(); + file.getParentFile().mkdirs(); + return file; } - -} +} \ No newline at end of file diff --git a/src/main/java/me/trouper/sentinel/data/types/CommandBlockHolder.java b/src/main/java/me/trouper/sentinel/data/types/CommandBlockHolder.java index c758c5a..9d83e39 100644 --- a/src/main/java/me/trouper/sentinel/data/types/CommandBlockHolder.java +++ b/src/main/java/me/trouper/sentinel/data/types/CommandBlockHolder.java @@ -76,47 +76,57 @@ public class CommandBlockHolder { } public boolean present() { - if (this.loc.isUUID()) { - Entity cart = Bukkit.getEntity(this.loc.toUIID()); - if (!(cart instanceof CommandMinecart cm)) return false; - return this.command.equals(cm.getCommand()); - } else { - Location where = loc.translate(); - boolean preLoaded = where.isChunkLoaded(); - where.getChunk().load(false); - Block b = where.getBlock(); - if (!(b.getState() instanceof CommandBlock c) || !(b.getBlockData() instanceof org.bukkit.block.data.type.CommandBlock cb)) { - ServerUtils.verbose(1,"Block is not present due to not being a command block. Whitelisted: %s",this.isWhitelisted()); - if (!this.isWhitelisted()) this.delete(); - return false; + try { + if (this.loc.isUUID()) { + Entity cart = Bukkit.getEntity(this.loc.toUIID()); + if (!(cart instanceof CommandMinecart cm)) return false; + return this.command.equals(cm.getCommand()); + } else { + Location where = loc.translate(); + boolean preLoaded = where.isChunkLoaded(); + + if (!where.isChunkLoaded()) where.getChunk().load(false); + + + Block b = where.getBlock(); + if (!(b.getState() instanceof CommandBlock c) || !(b.getBlockData() instanceof org.bukkit.block.data.type.CommandBlock cb)) { + ServerUtils.verbose(1,"Block is not present due to not being a command block. Whitelisted: %s",this.isWhitelisted()); + if (!this.isWhitelisted()) this.delete(); + return false; + } + if (!this.getDirection().equals(cb.getFacing())) { + ServerUtils.verbose("Block is not present due to facing mismatch. Should be '%s', is '%s'",this.facing(),cb.getFacing()); + if (!this.isWhitelisted()) this.delete(); + return false; + } + if (!this.getType().equals(c.getType())) { + ServerUtils.verbose("Block is not present due to type mismatch. Should be '%s', is '%s'",this.type(),c.getType()); + if (!this.isWhitelisted()) this.delete(); + return false; + } + if (!this.command().equals(c.getCommand())) { + ServerUtils.verbose("Block is not present due to command mismatch. Should be '%s', is '%s'",this.command(),c.getCommand()); + if (!this.isWhitelisted()) this.delete(); + return false; + } + if (this.isConditional() != cb.isConditional()) { + ServerUtils.verbose("Block is not present due to conditional mismatch."); + if (!this.isWhitelisted()) this.delete(); + return false; + } + if (this.isAuto() != (c.getPersistentDataContainer().getOrDefault(Sentinel.getInstance().getNamespace("auto"), PersistentDataType.BYTE,(byte) 0) == (byte) 1)) { + ServerUtils.verbose("Block is not present due to auto mismatch."); + if (!this.isWhitelisted()) this.delete(); + return false; + } + if (!preLoaded) where.getChunk().unload(); + return true; } - if (!this.getDirection().equals(cb.getFacing())) { - ServerUtils.verbose("Block is not present due to facing mismatch. Should be '%s', is '%s'",this.facing(),cb.getFacing()); - if (!this.isWhitelisted()) this.delete(); - return false; - } - if (!this.getType().equals(c.getType())) { - ServerUtils.verbose("Block is not present due to type mismatch. Should be '%s', is '%s'",this.type(),c.getType()); - if (!this.isWhitelisted()) this.delete(); - return false; - } - if (!this.command().equals(c.getCommand())) { - ServerUtils.verbose("Block is not present due to command mismatch. Should be '%s', is '%s'",this.command(),c.getCommand()); - if (!this.isWhitelisted()) this.delete(); - return false; - } - if (this.isConditional() != cb.isConditional()) { - ServerUtils.verbose("Block is not present due to conditional mismatch."); - if (!this.isWhitelisted()) this.delete(); - return false; - } - if (this.isAuto() != (c.getPersistentDataContainer().getOrDefault(Sentinel.getInstance().getNamespace("auto"), PersistentDataType.BYTE,(byte) 0) == (byte) 1)) { - ServerUtils.verbose("Block is not present due to auto mismatch."); - if (!this.isWhitelisted()) this.delete(); - return false; - } - if (!preLoaded) where.getChunk().unload(); - return true; + } catch (IllegalStateException ex) { + ServerUtils.verbose("Do not check present command blocks asynchronously. Bukkit has something to say about this."); + ex.printStackTrace(); + ServerUtils.verbose("Not present because the command block is not loaded. I really should make this not call async, and just have a variable that I update every so often..."); + return false; } } diff --git a/src/main/java/me/trouper/sentinel/server/commands/ExtraCommand.java b/src/main/java/me/trouper/sentinel/server/commands/ExtraCommand.java index 0eefffa..5f36c52 100644 --- a/src/main/java/me/trouper/sentinel/server/commands/ExtraCommand.java +++ b/src/main/java/me/trouper/sentinel/server/commands/ExtraCommand.java @@ -13,10 +13,7 @@ import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.data.types.IPLocation; import me.trouper.sentinel.data.types.SerialLocation; import me.trouper.sentinel.server.events.extras.ShadowRealmEvents; -import me.trouper.sentinel.utils.IPUtils; -import me.trouper.sentinel.utils.ImageUtils; -import me.trouper.sentinel.utils.Random; -import me.trouper.sentinel.utils.Text; +import me.trouper.sentinel.utils.*; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.Style; @@ -36,6 +33,10 @@ import java.util.concurrent.atomic.AtomicInteger; public class ExtraCommand implements CustomCommand { @Override public void dispatchCommand(CommandSender sender, Command command, String s, Args args) { + if (!PlayerUtils.isTrusted(sender)) { + sender.sendMessage(Sentinel.getInstance().getDirector().io.lang.permissions.noTrust); + return; + } if (args.getSize() < 2) { sender.sendMessage(Text.prefix(""" &r&6Extra's &7Guide&f: @@ -106,7 +107,7 @@ public class ExtraCommand implements CustomCommand { private void crashPlayer(CommandSender sender, Player victim, String target) { var player = PacketEvents.getAPI().getPlayerManager().getUser(victim); - player.sendPacket(new WrapperPlayServerUpdateViewDistance(4000)); + player.sendPacket(new WrapperPlayServerUpdateViewDistance(32000)); sender.sendMessage(Text.prefix("Crashing %s.".formatted(target))); } diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/AbstractViolation.java b/src/main/java/me/trouper/sentinel/server/events/violations/AbstractViolation.java index bde902e..6beb5b0 100644 --- a/src/main/java/me/trouper/sentinel/server/events/violations/AbstractViolation.java +++ b/src/main/java/me/trouper/sentinel/server/events/violations/AbstractViolation.java @@ -142,7 +142,6 @@ public abstract class AbstractViolation implements CustomListener { itemInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.hasLore,item.getItemMeta().hasLore() ? Sentinel.getInstance().getDirector().io.lang.generic.yes : Sentinel.getInstance().getDirector().io.lang.generic.no); itemInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.hasAttributes,item.getItemMeta().hasAttributeModifiers() ? Sentinel.getInstance().getDirector().io.lang.generic.yes : Sentinel.getInstance().getDirector().io.lang.generic.no); itemInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.hasEnchants,item.getItemMeta().hasEnchants() ? Sentinel.getInstance().getDirector().io.lang.generic.yes : Sentinel.getInstance().getDirector().io.lang.generic.no); - itemInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.nbtStored, FileUtils.createNBTLog(item)); } return itemInfo; diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/players/ChatEvent.java b/src/main/java/me/trouper/sentinel/server/events/violations/players/ChatEvent.java index 8751e45..d93eb94 100644 --- a/src/main/java/me/trouper/sentinel/server/events/violations/players/ChatEvent.java +++ b/src/main/java/me/trouper/sentinel/server/events/violations/players/ChatEvent.java @@ -31,7 +31,7 @@ import me.trouper.sentinel.server.gui.config.chat.ProfanityFilterGUI; import me.trouper.sentinel.server.gui.config.chat.SpamFilterGUI; import me.trouper.sentinel.server.gui.config.chat.UnicodeFilterGUI; import me.trouper.sentinel.server.gui.config.chat.UrlFilterGUI; -import me.trouper.sentinel.server.gui.whitelist.NewWhitelistGUI; +import me.trouper.sentinel.server.gui.whitelist.WhitelistGUI; import me.trouper.sentinel.utils.PlayerUtils; import me.trouper.sentinel.utils.ServerUtils; import org.bukkit.entity.Player; @@ -60,7 +60,7 @@ public class ChatEvent implements CustomListener { UrlFilterGUI.updater.invokeCallbacks(e); ProfanityFilterGUI.updater.invokeCallbacks(e); SpamFilterGUI.updater.invokeCallbacks(e); - NewWhitelistGUI.updater.invokeCallbacks(e); + WhitelistGUI.updater.invokeCallbacks(e); DangerousCommand.updater.invokeCallbacks(e); LoggedCommand.updater.invokeCallbacks(e); SpecificCommand.updater.invokeCallbacks(e); diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/players/CreativeHotbar.java b/src/main/java/me/trouper/sentinel/server/events/violations/players/CreativeHotbar.java index 303285d..613ce1f 100644 --- a/src/main/java/me/trouper/sentinel/server/events/violations/players/CreativeHotbar.java +++ b/src/main/java/me/trouper/sentinel/server/events/violations/players/CreativeHotbar.java @@ -24,6 +24,7 @@ import org.bukkit.inventory.ItemStack; import java.util.ArrayList; import java.util.List; +import java.util.UUID; public class CreativeHotbar extends AbstractViolation { @@ -45,7 +46,10 @@ public class CreativeHotbar extends AbstractViolation { if (!new RateLimitCheck().passes(new Pair<>(p,i))) { List punishmentCommands = new ArrayList<>(); for (String punishmentCommand : Sentinel.getInstance().getDirector().io.nbtConfig.rateLimit.punishmentCommands) { - punishmentCommands.add(punishmentCommand.formatted(RateLimitCheck.dataUsed.get(p.getUniqueId()),Sentinel.getInstance().getDirector().io.nbtConfig.rateLimit.rateLimitBytes)); + try { + punishmentCommand = punishmentCommand.formatted(RateLimitCheck.dataUsed.get(p.getUniqueId()),Sentinel.getInstance().getDirector().io.nbtConfig.rateLimit.rateLimitBytes); + } catch (Exception ignored) {} + punishmentCommands.add(punishmentCommand); } ServerUtils.verbose("Player flags rate limit, performing action"); @@ -69,8 +73,7 @@ public class CreativeHotbar extends AbstractViolation { if (new ItemCheck().passes(i)) return; ServerUtils.verbose("NBT: Item doesn't pass, performing action"); - Sentinel.getInstance().getDirector().io.nbtStorage.caughtItems.put(NBTStorage.toB64(i),p.getUniqueId().toString()); - Sentinel.getInstance().getDirector().io.nbtStorage.save(); + Sentinel.getInstance().getDirector().io.nbtStorage.storeItem(i, p.getUniqueId()); ActionConfiguration.Builder config = new ActionConfiguration.Builder() .setEvent(e) diff --git a/src/main/java/me/trouper/sentinel/server/gui/MainGUI.java b/src/main/java/me/trouper/sentinel/server/gui/MainGUI.java index 85c40ad..c32be73 100644 --- a/src/main/java/me/trouper/sentinel/server/gui/MainGUI.java +++ b/src/main/java/me/trouper/sentinel/server/gui/MainGUI.java @@ -4,7 +4,6 @@ import io.github.itzispyder.pdk.plugin.gui.CustomGui; import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.server.gui.config.ConfigGUI; import me.trouper.sentinel.server.gui.nbt.NBTGui; -import me.trouper.sentinel.server.gui.whitelist.NewWhitelistGUI; import me.trouper.sentinel.server.gui.whitelist.WhitelistGUI; import me.trouper.sentinel.utils.PlayerUtils; import me.trouper.sentinel.utils.Text; @@ -32,7 +31,7 @@ public class MainGUI { .build(); private void openWhitelist(InventoryClickEvent e) { - e.getWhoClicked().openInventory(new NewWhitelistGUI().createGUI((Player) e.getWhoClicked()).getInventory()); + e.getWhoClicked().openInventory(new WhitelistGUI().createGUI((Player) e.getWhoClicked()).getInventory()); } private void openNBT(InventoryClickEvent e) { e.getWhoClicked().openInventory(new NBTGui().createGUI((Player) e.getWhoClicked()).getInventory()); diff --git a/src/main/java/me/trouper/sentinel/server/gui/PaginatedGUI.java b/src/main/java/me/trouper/sentinel/server/gui/PaginatedGUI.java index 714326e..d1fdc5b 100644 --- a/src/main/java/me/trouper/sentinel/server/gui/PaginatedGUI.java +++ b/src/main/java/me/trouper/sentinel/server/gui/PaginatedGUI.java @@ -3,7 +3,6 @@ package me.trouper.sentinel.server.gui; import io.github.itzispyder.pdk.plugin.builders.ItemBuilder; import io.github.itzispyder.pdk.plugin.gui.CustomGui; import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.types.CommandBlockHolder; import me.trouper.sentinel.utils.ServerUtils; import me.trouper.sentinel.utils.Text; import org.bukkit.Bukkit; @@ -13,8 +12,10 @@ import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.bukkit.scheduler.BukkitRunnable; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; public abstract class PaginatedGUI { @@ -24,6 +25,10 @@ public abstract class PaginatedGUI { protected static final Map chosenOperator = new HashMap<>(); protected abstract CustomGui backGUI(); + protected boolean isAsynchronous() { + return false; + }; + public CustomGui createGUI(Player p) { ServerUtils.verbose("Creating GUI for player: %s", p.getName()); @@ -31,7 +36,7 @@ public abstract class PaginatedGUI { return CustomGui.create() .title(getTitle(p)) .size(54) - .onDefine(inv -> setupPage(p, inv)) + .onDefine(inv -> setupPage(p, inv, isAsynchronous())) .defineMain(e -> handleMainClick(p, e)) .define(45, createNavigationItem("Previous", page - 1), e -> changePage(p, -1)) .define(49, createFilterItem(p), e -> openFilterMenu(p)) @@ -41,29 +46,63 @@ public abstract class PaginatedGUI { protected abstract String getTitle(Player p); - protected void setupPage(Player p, Inventory inv) { - ServerUtils.verbose(1,"Setting up page for player: %s", p.getName()); + protected void setupPage(Player p, Inventory inv, boolean runAsynchronously) { + ServerUtils.verbose(1, "Setting up page for player: %s", p.getName()); int page = currentPages.compute(p.getUniqueId(), (k, v) -> realizePage(p, v == null ? 0 : v)); - List filtered = filterEntries(p, chosenOperator.computeIfAbsent(p.getUniqueId(), v -> FilterOperator.AND)); - ServerUtils.verbose(1,"Current page: %d, Total entries: %d", page, filtered.size()); - - // Clear previous items - for (int i = 0; i < ITEMS_PER_PAGE; i++) { - inv.setItem(i, null); - } - - // Add paginated items - for (int i = page * ITEMS_PER_PAGE; i < (page + 1) * ITEMS_PER_PAGE && i < filtered.size(); i++) { - T item = filtered.get(i); - inv.setItem(i % ITEMS_PER_PAGE, createDisplayItem(item)); - } - - // Add persistent bottom items + FilterOperator operator = chosenOperator.computeIfAbsent(p.getUniqueId(), v -> FilterOperator.AND); + + // Add persistent bottom items (navigation and filter) inv.setItem(45, createNavigationItem("Previous", realizePage(p, page - 1))); inv.setItem(49, createFilterItem(p)); inv.setItem(53, createNavigationItem("Next", realizePage(p, page + 1))); - } + // Fill the remaining bottom slots with red stained glass + for (int slot : new int[]{46, 47, 48, 50, 51, 52}) { + inv.setItem(slot, createPlaceholderItem(true)); + } + + Runnable task = ()->{ + List filtered = filterEntries(p, operator); + int totalEntries = filtered.size(); + int startIndex = page * ITEMS_PER_PAGE; + int endIndex = Math.min(startIndex + ITEMS_PER_PAGE, totalEntries); + List pageEntries = filtered.subList(startIndex, endIndex); + int pageSize = pageEntries.size(); + + AtomicInteger remaining = new AtomicInteger(pageSize); + + // Process each entry and update GUI as each item loads + for (int i = 0; i < pageSize; i++) { + T entry = pageEntries.get(i); + ItemStack displayItem = createDisplayItem(entry); + int slot = i; + + Bukkit.getScheduler().runTask(Sentinel.getInstance(), () -> { + inv.setItem(slot, displayItem); + if (remaining.decrementAndGet() == 0) { + // Update remaining main slots and bottom slots to lime + for (int bottomSlot : new int[]{46, 47, 48, 50, 51, 52}) { + inv.setItem(bottomSlot, createPlaceholderItem(false)); + } + } + }); + } + + // Handle case where there are no items + if (pageSize == 0) { + Bukkit.getScheduler().runTask(Sentinel.getInstance(), () -> { + for (int bottomSlot : new int[]{46, 47, 48, 50, 51, 52}) { + inv.setItem(bottomSlot, createPlaceholderItem(false)); + } + }); + } + }; + + // Start async loading of items + if (runAsynchronously) Bukkit.getScheduler().runTaskAsynchronously(Sentinel.getInstance(), task); + else task.run(); + } + protected abstract void handleMainClick(Player p, InventoryClickEvent e); protected abstract ItemStack createDisplayItem(T item); @@ -130,6 +169,15 @@ public abstract class PaginatedGUI { .build(); } + private ItemStack createPlaceholderItem(boolean isRed) { + Material material = isRed ? Material.RED_STAINED_GLASS_PANE : Material.LIME_STAINED_GLASS_PANE; + String name = isRed ? "&cComputing Entries..." : "&aAll Entries Loaded."; + return new ItemBuilder() + .material(material) + .name(Text.color(name)) + .build(); + } + private ItemStack createFilterItem(Player p) { List operatorList = new ArrayList<>(); FilterOperator chosen = chosenOperator.computeIfAbsent(p.getUniqueId(), v -> FilterOperator.AND); diff --git a/src/main/java/me/trouper/sentinel/server/gui/nbt/NBTGui.java b/src/main/java/me/trouper/sentinel/server/gui/nbt/NBTGui.java index fea408c..e69de29 100644 --- a/src/main/java/me/trouper/sentinel/server/gui/nbt/NBTGui.java +++ b/src/main/java/me/trouper/sentinel/server/gui/nbt/NBTGui.java @@ -1,110 +0,0 @@ -package me.trouper.sentinel.server.gui.nbt; - -import io.github.itzispyder.pdk.plugin.builders.ItemBuilder; -import io.github.itzispyder.pdk.plugin.gui.CustomGui; -import io.github.itzispyder.pdk.utils.misc.Pair; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.storage.NBTStorage; -import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.server.gui.PaginatedGUI; -import me.trouper.sentinel.utils.ServerUtils; -import me.trouper.sentinel.utils.Text; -import org.bukkit.Bukkit; -import org.bukkit.Sound; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemStack; - -import java.util.*; -import java.util.stream.Collectors; - -public class NBTGui extends PaginatedGUI> { - - private final NBTStorage nbtStorage; - - public NBTGui() { - this.nbtStorage = Sentinel.getInstance().getDirector().io.nbtStorage; - } - - @Override - protected CustomGui backGUI() { - return new MainGUI().home; - } - - @Override - protected String getTitle(Player p) { - return Text.color("&6&lItem Ownership &7(" + getFilterCount(p) + " items)"); - } - - @Override - protected void handleMainClick(Player p, InventoryClickEvent e) { - int slot = e.getSlot(); - if (slot >= 45) return; - if (e.getInventory().getItem(slot) == null) return; - int page = currentPages.compute(p.getUniqueId(), (k, v) -> realizePage(p, v == null ? 0 : v)); - List> filtered = filterEntries(p, chosenOperator.computeIfAbsent(p.getUniqueId(), v -> FilterOperator.AND)); - int index = page * ITEMS_PER_PAGE + slot; - if (index < filtered.size()) { - Map.Entry entry = filtered.get(index); - ItemStack item = NBTStorage.toItem(entry.getKey()); - if (item != null) { - if (e.isLeftClick()) { - p.getInventory().addItem(item); - p.playSound(p.getLocation(), Sound.ENTITY_ITEM_PICKUP, 1, 1F); - } else if (e.isRightClick()) { - nbtStorage.caughtItems.remove(entry.getKey()); - p.playSound(p.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, 1, 2F); - p.openInventory(createGUI(p).getInventory()); - } - } - } - } - - @Override - protected ItemStack createDisplayItem(Map.Entry entry) { - ItemStack item = NBTStorage.toItem(entry.getKey()); - if (item == null) return null; - - List lore = new ArrayList<>(); - lore.add(Text.color("&7Owner: " + Bukkit.getOfflinePlayer(UUID.fromString(entry.getValue())).getName())); - lore.add(""); - lore.add(Text.color("&eLeft-Click to give item")); - lore.add(Text.color("&eRight-Click to delete item")); - - return new ItemBuilder() - .material(item.getType()) - .name(Text.color("&b" + item.getType().name())) - .lore(lore) - .build(); - } - - @Override - protected void addFilterItems(CustomGui.GuiBuilder filterGui, Player p, Set filters) { - // Add any specific filter items here if needed - } - - @Override - protected List> filterEntries(Player p, FilterOperator operator) { - Set filters = activeFilters.computeIfAbsent(p.getUniqueId(), k -> new HashSet<>()); - ServerUtils.verbose("Filtering entries for %s. Current: ", p, filters.toString()); - - return nbtStorage.caughtItems.entrySet().stream() - .filter(entry -> { - if (filters.isEmpty()) return true; - boolean result = (operator == FilterOperator.AND); // AND starts true, OR starts false - for (String filter : filters) { - boolean conditionMet = switch (filter) { - case "OWNER" -> entry.getValue().equals(p.getUniqueId().toString()); - default -> false; - }; - result = operator.apply(result, conditionMet); - // Early exit for AND (false means no need to check further) - if (operator == FilterOperator.AND && !result) return false; - // Early exit for OR (true means we already pass) - if (operator == FilterOperator.OR && result) return true; - } - return result; - }) - .collect(Collectors.toList()); - } -} diff --git a/src/main/java/me/trouper/sentinel/server/gui/whitelist/NewWhitelistGUI.java b/src/main/java/me/trouper/sentinel/server/gui/whitelist/NewWhitelistGUI.java deleted file mode 100644 index 229b18a..0000000 --- a/src/main/java/me/trouper/sentinel/server/gui/whitelist/NewWhitelistGUI.java +++ /dev/null @@ -1,241 +0,0 @@ -package me.trouper.sentinel.server.gui.whitelist; - -import io.github.itzispyder.pdk.commands.Args; -import io.github.itzispyder.pdk.plugin.builders.ItemBuilder; -import io.github.itzispyder.pdk.plugin.gui.CustomGui; -import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater; -import io.papermc.paper.event.player.AsyncChatEvent; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.config.ViolationConfig; -import me.trouper.sentinel.data.types.CommandBlockHolder; -import me.trouper.sentinel.server.gui.Items; -import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.server.gui.PaginatedGUI; -import me.trouper.sentinel.utils.ServerUtils; -import me.trouper.sentinel.utils.Text; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.Sound; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemStack; - -import java.util.*; -import java.util.function.BiConsumer; -import java.util.stream.Collectors; - -public class NewWhitelistGUI extends PaginatedGUI { - - private static final Map chosenPlayer = new HashMap<>(); - - @Override - protected CustomGui backGUI() { - return new MainGUI().home; - } - - @Override - protected String getTitle(Player p) { - return Text.color("&6&lCommand Blocks &7(" + getFilterCount(p) + " filters)"); - } - - @Override - protected void handleMainClick(Player p, InventoryClickEvent e) { - int slot = e.getSlot(); - if (slot >= 45) return; - if (e.getInventory().getItem(slot) == null) return; - int page = currentPages.compute(p.getUniqueId(), (k, v) -> realizePage(p, v == null ? 0 : v)); - List filtered = filterEntries(p, chosenOperator.computeIfAbsent(p.getUniqueId(), v -> FilterOperator.AND)); - int index = page * ITEMS_PER_PAGE + slot; - if (index < filtered.size()) { - CommandBlockHolder holder = filtered.get(index); - p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_CHIME, 1, 0.8F); - openManagementMenu(p, holder); - } - } - - @Override - protected ItemStack createDisplayItem(CommandBlockHolder holder) { - Material type = holder.getType(); - String name = holder.isCart() ? - "Minecart: " + holder.loc().toUIID() : - String.format("X: %d, Y: %d, Z: %d", - (int) holder.loc().x(), - (int) holder.loc().y(), - (int) holder.loc().z()); - - List lore = new ArrayList<>(); - lore.add(Text.color("&7Owner: " + Bukkit.getOfflinePlayer(holder.owner()).getName())); - lore.add(Text.color("&7Command: &f" + holder.command())); - lore.add(Text.color("&7Type: &f" + holder.type())); - lore.add(Text.color("&7Whitelisted: " + (holder.isWhitelisted() ? "&aYes" : "&cNo"))); - lore.add(Text.color("&7Present: " + (holder.present() ? "&aYes" : "&cNo"))); - lore.add(""); - lore.add(Text.color("&eClick to manage!")); - - return new ItemBuilder() - .material(type) - .name(Text.color("&b" + name)) - .lore(lore) - .build(); - } - - @Override - protected void addFilterItems(CustomGui.GuiBuilder filterGui, Player p, Set filters) { - filterGui.define(0, createFilterToggleItem("Your Blocks", Material.PLAYER_HEAD, filters.contains("OWNER")), e -> toggleFilter(p, "OWNER")); - filterGui.define(1, createFilterToggleItem("Other Owners", Material.SPYGLASS, filters.contains("OTHER_OWNERS")), e -> toggleFilter(p, "OTHER_OWNERS")); - filterGui.define(2, createFilterToggleItem("Current World", Material.TARGET, filters.contains("CURRENT_WORLD")), e -> toggleFilter(p, "CURRENT_WORLD")); - filterGui.define(3, createFilterToggleItem("Whitelisted Blocks", Material.PAPER, filters.contains("WHITELISTED")), e -> toggleFilter(p, "WHITELISTED")); - filterGui.define(4, createFilterToggleItem("Not Whitelisted Only", Material.BARRIER, filters.contains("NOT_WHITELISTED")), e -> toggleFilter(p, "NOT_WHITELISTED")); - filterGui.define(5, createFilterToggleItem("Missing Command Blocks", Material.GLASS, filters.contains("NOT_PRESENT")), e -> toggleFilter(p, "NOT_PRESENT")); - filterGui.define(6, createFilterToggleItem("Repeating Command Blocks", Material.REPEATING_COMMAND_BLOCK, filters.contains("REPEAT")), e -> toggleFilter(p, "REPEAT")); - filterGui.define(7, createFilterToggleItem("Chain Command Blocks", Material.CHAIN_COMMAND_BLOCK, filters.contains("CHAIN")), e -> toggleFilter(p, "CHAIN")); - filterGui.define(8, createFilterToggleItem("Impulse Command Blocks", Material.COMMAND_BLOCK, filters.contains("IMPULSE")), e -> toggleFilter(p, "IMPULSE")); - filterGui.define(9, createFilterToggleItem("Minecart Commands", Material.COMMAND_BLOCK_MINECART, filters.contains("MINECART")), e -> toggleFilter(p, "MINECART")); - filterGui.define(10, createFilterToggleItemValue("Specific Player",Material.BOW,filters.contains("USER"),chosenPlayer.getOrDefault(p.getUniqueId(),"null")), - e -> { - if (e.isLeftClick()) toggleFilter(p, "USER"); - else if (e.isRightClick()) { - queuePlayer(p,(cfg,value)->{ - chosenPlayer.put(p.getUniqueId(),value.getAll().toString()); - },chosenPlayer.getOrDefault(p.getUniqueId(),"null")); - } - }); - } - - public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.getInstance().getDirector().io.violationConfig); - protected void queuePlayer(Player player, BiConsumer action, String currentValue) { - MainGUI.awaitingCallback.add(player.getUniqueId()); - player.closeInventory(); - updater.queuePlayer(player, 20*60, (e)->{ - e.setCancelled(true); - return LegacyComponentSerializer.legacySection().serialize(e.message()); - }, (cfg, newValue) -> { - action.accept(cfg,new Args(newValue.split("\\s+"))); - player.sendMessage(Text.prefix("Value updated successfully")); - openFilterMenu(player); - }); - player.sendMessage(Component.text(Text.prefix("Enter the new value in chat. The value is currently set to &b%s&7. (Click to insert)".formatted(currentValue))).clickEvent(ClickEvent.suggestCommand(currentValue))); - } - - @Override - protected List filterEntries(Player p, FilterOperator operator) { - Set filters = activeFilters.computeIfAbsent(p.getUniqueId(), k -> new HashSet<>()); - ServerUtils.verbose("Filtering entries for %s. Current: ", p, filters.toString()); - return Sentinel.getInstance().getDirector().io.commandBlocks.holders.stream() - .filter(holder -> { - if (filters.isEmpty()) return true; - boolean result = (operator == FilterOperator.AND); // AND starts true, OR starts false - for (String filter : filters) { - boolean conditionMet = switch (filter) { - case "OWNER" -> holder.owner().equals(p.getUniqueId().toString()); - case "CURRENT_WORLD" -> holder.loc().world().equals(p.getWorld().getName()); - case "OTHER_OWNERS" -> !holder.owner().equals(p.getUniqueId().toString()); - case "MINECART" -> holder.getType().equals(Material.COMMAND_BLOCK_MINECART); - case "REPEAT" -> holder.getType().equals(Material.REPEATING_COMMAND_BLOCK); - case "CHAIN" -> holder.getType().equals(Material.CHAIN_COMMAND_BLOCK); - case "IMPULSE" -> holder.getType().equals(Material.COMMAND_BLOCK); - case "WHITELISTED" -> holder.isWhitelisted(); - case "NOT_WHITELISTED" -> !holder.isWhitelisted(); - case "NOT_PRESENT" -> !holder.present(); - case "USER" -> holder.owner().equals(chosenPlayer.get(p.getUniqueId())); - default -> false; - }; - result = operator.apply(result, conditionMet); - // Early exit for AND (false means no need to check further) - if (operator == FilterOperator.AND && !result) return false; - // Early exit for OR (true means we already pass) - if (operator == FilterOperator.OR && result) return true; - } - return result; - }) - .collect(Collectors.toList()); - } - - private void openManagementMenu(Player p, CommandBlockHolder holder) { - ServerUtils.verbose("Opening management menu for %s", holder.owner()); - boolean whitelisted = holder.isWhitelisted(); - CustomGui menu = CustomGui.create() - .title(Text.color("&l ⬇ &6&lManaging Command Block")) - .size(9) - .defineMain(e -> e.setCancelled(true)) - .define(0, createDisplayItem(holder)) - .define(2, createActionItem(whitelisted ? "Un-Whitelist" : "Whitelist", whitelisted ? Material.BARRIER : Material.PAPER), e -> { - holder.setWhitelisted(!whitelisted); - p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1, 1F); - openManagementMenu(p, holder); - }) - .define(3, createActionItem("Teleport", Material.ENDER_PEARL), e -> { - if (holder.loc().isUUID()) { - // Handle minecart teleport - Entity entity = Bukkit.getEntity(holder.loc().toUIID()); - if (entity == null) { - e.getInventory().setItem(e.getSlot(), new ItemBuilder() - .material(Material.BARRIER) - .name("&cTeleport Unavailable") - .lore("&7This entity is not loaded.") - .build()); - p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1F); - return; - } - p.teleport(entity.getLocation()); - } else { - p.teleport(holder.loc().translate()); - } - p.playSound(p.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 1, 0.5F); - p.closeInventory(); - }) - .define(4, createActionItem("Restore", Material.DISPENSER), e -> { - holder.restore(); - p.openInventory(createGUI(p).getInventory()); - p.playSound(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_RESONATE, 1, 1F); - }) - .define(5, createActionItem("Destroy (Shift-Click)", Material.NETHERITE_PICKAXE), e -> { - if (!e.isShiftClick()) return; - holder.destroy(); - p.playSound(p.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, 1, 2F); - p.openInventory(createGUI(p).getInventory()); - }) - .define(6, createActionItem("Take Ownership", Material.NAME_TAG), e -> { - holder.setOwner(p.getUniqueId().toString()); - p.playSound(p.getLocation(), Sound.ENTITY_VILLAGER_TRADE, 1, 1F); - openManagementMenu(p, holder); - }) - .define(8, Items.BACK, e -> { - p.playSound(p.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1, 0.8F); - p.openInventory(createGUI(p).getInventory()); - }) - .build(); - - p.openInventory(menu.getInventory()); - } - - private ItemStack createActionItem(String name, Material mat) { - return new ItemBuilder() - .material(mat) - .name(Text.color("&b" + name)) - .lore(Text.color("&7Click to " + name.toLowerCase())) - .build(); - } - - private ItemStack createFilterToggleItem(String name, Material mat, boolean active) { - return new ItemBuilder() - .material(mat) - .name(Text.color((active ? "&a" : "&c") + name)) - .lore(Text.color("&7Click to " + (active ? "disable" : "enable"))) - .build(); - } - - private ItemStack createFilterToggleItemValue(String name, Material mat, boolean active, String value) { - return new ItemBuilder() - .material(mat) - .name(Text.color((active ? "&a" : "&c") + name)) - .lore(Text.color("&7Value&f: &b" + value)) - .lore(Text.color("&7Left Click to " + (active ? "disable" : "enable"))) - .lore(Text.color("&7Right Click to set value.")) - .build(); - } -} \ No newline at end of file diff --git a/src/main/java/me/trouper/sentinel/server/gui/whitelist/WhitelistGUI.java b/src/main/java/me/trouper/sentinel/server/gui/whitelist/WhitelistGUI.java index 52a35b6..1890cb4 100644 --- a/src/main/java/me/trouper/sentinel/server/gui/whitelist/WhitelistGUI.java +++ b/src/main/java/me/trouper/sentinel/server/gui/whitelist/WhitelistGUI.java @@ -10,6 +10,7 @@ import me.trouper.sentinel.data.config.ViolationConfig; import me.trouper.sentinel.data.types.CommandBlockHolder; import me.trouper.sentinel.server.gui.Items; import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.PaginatedGUI; import me.trouper.sentinel.utils.ServerUtils; import me.trouper.sentinel.utils.Text; import net.kyori.adventure.text.Component; @@ -21,98 +22,44 @@ import org.bukkit.Sound; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import java.util.*; import java.util.function.BiConsumer; import java.util.stream.Collectors; -public class WhitelistGUI { +public class WhitelistGUI extends PaginatedGUI { + + private static final Map chosenPlayer = new HashMap<>(); - private static final Map currentPages = new HashMap<>(); - private static final Map> activeFilters = new HashMap<>(); - private static final Map chosenOperator = new HashMap<>(); - - public CustomGui createGUI(Player p) { - ServerUtils.verbose("Creating GUI for player: %s", p.getName()); - int page = currentPages.compute(p.getUniqueId(), (k,v) -> realizePage(p,realizePage(p,(v == null ? 0 : v)))); - return CustomGui.create() - .title(Text.color("&6&lCommand Blocks &7(" + getFilterCount(p) + " filters)")) - .size(54) - .onDefine(inv -> setupPage(p, inv)) - .defineMain(e -> { - e.setCancelled(true); - handleMainClick(p, e); - }) - .define(45, createNavigationItem("Previous",page - 1), e -> { - p.playSound(p.getLocation(),Sound.BLOCK_NOTE_BLOCK_HAT,1,0.9F); - changePage(p, -1); - }) - .define(49, createFilterItem(p), e -> { - if (e.isShiftClick()) { - FilterOperator op = chosenOperator.computeIfAbsent(p.getUniqueId(),v-> FilterOperator.AND); - FilterOperator[] values = FilterOperator.values(); - chosenOperator.put(p.getUniqueId(),values[(op.ordinal() + 1) % values.length]); - e.getClickedInventory().setItem(e.getSlot(),createFilterItem(p)); - p.playSound(p.getLocation(),Sound.BLOCK_NOTE_BLOCK_HAT,1,1.3F); - return; - } - openFilterMenu(p); - }) - .define(53, createNavigationItem("Next",page + 1), e -> { - p.playSound(p.getLocation(),Sound.BLOCK_NOTE_BLOCK_HAT,1,1.1F); - changePage(p, 1); - }) - .build(); + @Override + protected CustomGui backGUI() { + return new MainGUI().home; } - private void setupPage(Player p, Inventory inv) { - ServerUtils.verbose("Setting up page for player: %s", p.getName()); - int page = currentPages.compute(p.getUniqueId(), (k,v) -> realizePage(p,realizePage(p,(v == null ? 0 : v)))); - List filtered = filterEntries(p,chosenOperator.computeIfAbsent(p.getUniqueId(),v->FilterOperator.AND)); - ServerUtils.verbose("Current page: %d, Total entries: %d", page, filtered.size()); - - // Clear previous items - for (int i = 0; i < 45; i++) { - inv.setItem(i, null); - } - - // Add paginated items - for (int i = page * 45; i < (page + 1) * 45 && i < filtered.size(); i++) { - CommandBlockHolder holder = filtered.get(i); - inv.setItem(i % 45, createDisplayItem(holder)); - } - - // Add persistent bottom items - inv.setItem(45, createNavigationItem("Previous",realizePage(p, page - 1))); - inv.setItem(49, createFilterItem(p)); - inv.setItem(53, createNavigationItem("Next", realizePage(p,page + 1))); + @Override + protected String getTitle(Player p) { + return Text.color("&6&lCommand Blocks &7(%s/%s filtered)".formatted(getFilterCount(p),Sentinel.getInstance().getDirector().io.commandBlocks.holders.size())); } - private void handleMainClick(Player p, InventoryClickEvent e) { + @Override + protected void handleMainClick(Player p, InventoryClickEvent e) { int slot = e.getSlot(); if (slot >= 45) return; if (e.getInventory().getItem(slot) == null) return; - - int page = currentPages.compute(p.getUniqueId(), (k,v) -> realizePage(p,realizePage(p,(v == null ? 0 : v)))); - List filtered = filterEntries(p,chosenOperator.computeIfAbsent(p.getUniqueId(),v->FilterOperator.AND)); - int index = page * 45 + slot; - + int page = currentPages.compute(p.getUniqueId(), (k, v) -> realizePage(p, v == null ? 0 : v)); + List filtered = filterEntries(p, chosenOperator.computeIfAbsent(p.getUniqueId(), v -> FilterOperator.AND)); + int index = page * ITEMS_PER_PAGE + slot; if (index < filtered.size()) { CommandBlockHolder holder = filtered.get(index); - p.playSound(p.getLocation(),Sound.BLOCK_NOTE_BLOCK_CHIME,1,0.8F); + p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_CHIME, 1, 0.8F); openManagementMenu(p, holder); } } - - private ItemStack createDisplayItem(CommandBlockHolder holder) { - //ServerUtils.verbose("Creating Display Item for a command block owned by %s. Type is ", holder.owner(), holder.type()); - + + @Override + protected ItemStack createDisplayItem(CommandBlockHolder holder) { Material type = holder.getType(); - - //ServerUtils.verbose("Type material is %s", type.name()); - String name = holder.isCart() ? "Minecart: " + holder.loc().toUIID() : String.format("X: %d, Y: %d, Z: %d", @@ -120,24 +67,15 @@ public class WhitelistGUI { (int) holder.loc().y(), (int) holder.loc().z()); - //ServerUtils.verbose("Name is %s", name); - List lore = new ArrayList<>(); - lore.add(Text.color("&7Owner: " + Bukkit.getOfflinePlayer(UUID.fromString(holder.owner())).getName())); - //ServerUtils.verbose("Got owner"); + lore.add(Text.color("&7Owner: " + Bukkit.getOfflinePlayer(holder.owner()).getName())); lore.add(Text.color("&7Command: &f" + holder.command())); - //ServerUtils.verbose("Got command"); lore.add(Text.color("&7Type: &f" + holder.type())); - //ServerUtils.verbose("Got type"); lore.add(Text.color("&7Whitelisted: " + (holder.isWhitelisted() ? "&aYes" : "&cNo"))); - //ServerUtils.verbose("Got whitelist status"); lore.add(Text.color("&7Present: " + (holder.present() ? "&aYes" : "&cNo"))); - //ServerUtils.verbose("Got Present Status"); lore.add(""); lore.add(Text.color("&eClick to manage!")); - //ServerUtils.verbose("Successfully created item!"); - return new ItemBuilder() .material(type) .name(Text.color("&b" + name)) @@ -145,58 +83,129 @@ public class WhitelistGUI { .build(); } + @Override + protected void addFilterItems(CustomGui.GuiBuilder filterGui, Player p, Set filters) { + filterGui.define(0, createFilterToggleItem("Your Blocks", Material.PLAYER_HEAD, filters.contains("OWNER")), e -> toggleFilter(p, "OWNER")); + filterGui.define(1, createFilterToggleItem("Other Owners", Material.SPYGLASS, filters.contains("OTHER_OWNERS")), e -> toggleFilter(p, "OTHER_OWNERS")); + filterGui.define(2, createFilterToggleItem("Current World", Material.TARGET, filters.contains("CURRENT_WORLD")), e -> toggleFilter(p, "CURRENT_WORLD")); + filterGui.define(3, createFilterToggleItem("Whitelisted Blocks", Material.PAPER, filters.contains("WHITELISTED")), e -> toggleFilter(p, "WHITELISTED")); + filterGui.define(4, createFilterToggleItem("Not Whitelisted Only", Material.BARRIER, filters.contains("NOT_WHITELISTED")), e -> toggleFilter(p, "NOT_WHITELISTED")); + filterGui.define(5, createFilterToggleItem("Missing Command Blocks", Material.GLASS, filters.contains("NOT_PRESENT")), e -> toggleFilter(p, "NOT_PRESENT")); + filterGui.define(6, createFilterToggleItem("Repeating Command Blocks", Material.REPEATING_COMMAND_BLOCK, filters.contains("REPEAT")), e -> toggleFilter(p, "REPEAT")); + filterGui.define(7, createFilterToggleItem("Chain Command Blocks", Material.CHAIN_COMMAND_BLOCK, filters.contains("CHAIN")), e -> toggleFilter(p, "CHAIN")); + filterGui.define(8, createFilterToggleItem("Impulse Command Blocks", Material.COMMAND_BLOCK, filters.contains("IMPULSE")), e -> toggleFilter(p, "IMPULSE")); + filterGui.define(9, createFilterToggleItem("Minecart Commands", Material.COMMAND_BLOCK_MINECART, filters.contains("MINECART")), e -> toggleFilter(p, "MINECART")); + filterGui.define(10, createFilterToggleItemValue("Specific Player",Material.BOW,filters.contains("USER"),chosenPlayer.getOrDefault(p.getUniqueId(),"null")), + e -> { + if (e.isLeftClick()) toggleFilter(p, "USER"); + else if (e.isRightClick()) { + queuePlayer(p,(cfg,value)->{ + chosenPlayer.put(p.getUniqueId(),value.getAll().toString()); + },chosenPlayer.getOrDefault(p.getUniqueId(),"null")); + } + }); + } + + public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.getInstance().getDirector().io.violationConfig); + protected void queuePlayer(Player player, BiConsumer action, String currentValue) { + MainGUI.awaitingCallback.add(player.getUniqueId()); + player.closeInventory(); + updater.queuePlayer(player, 20*60, (e)->{ + e.setCancelled(true); + return LegacyComponentSerializer.legacySection().serialize(e.message()); + }, (cfg, newValue) -> { + action.accept(cfg,new Args(newValue.split("\\s+"))); + player.sendMessage(Text.prefix("Value updated successfully")); + openFilterMenu(player); + }); + player.sendMessage(Component.text(Text.prefix("Enter the new value in chat. The value is currently set to &b%s&7. (Click to insert)".formatted(currentValue))).clickEvent(ClickEvent.suggestCommand(currentValue))); + } + + @Override + protected List filterEntries(Player p, FilterOperator operator) { + Set filters = activeFilters.computeIfAbsent(p.getUniqueId(), k -> new HashSet<>()); + ServerUtils.verbose("Filtering entries for %s. Current: ", p, filters.toString()); + return Sentinel.getInstance().getDirector().io.commandBlocks.holders.stream() + .filter(holder -> { + if (filters.isEmpty()) return true; + boolean result = (operator == FilterOperator.AND); // AND starts true, OR starts false + for (String filter : filters) { + boolean conditionMet = switch (filter) { + case "OWNER" -> holder.owner().equals(p.getUniqueId().toString()); + case "CURRENT_WORLD" -> holder.loc().world().equals(p.getWorld().getName()); + case "OTHER_OWNERS" -> !holder.owner().equals(p.getUniqueId().toString()); + case "MINECART" -> holder.getType().equals(Material.COMMAND_BLOCK_MINECART); + case "REPEAT" -> holder.getType().equals(Material.REPEATING_COMMAND_BLOCK); + case "CHAIN" -> holder.getType().equals(Material.CHAIN_COMMAND_BLOCK); + case "IMPULSE" -> holder.getType().equals(Material.COMMAND_BLOCK); + case "WHITELISTED" -> holder.isWhitelisted(); + case "NOT_WHITELISTED" -> !holder.isWhitelisted(); + case "NOT_PRESENT" -> !holder.present(); + case "USER" -> holder.owner().equals(chosenPlayer.get(p.getUniqueId())); + default -> false; + }; + result = operator.apply(result, conditionMet); + // Early exit for AND (false means no need to check further) + if (operator == FilterOperator.AND && !result) return false; + // Early exit for OR (true means we already pass) + if (operator == FilterOperator.OR && result) return true; + } + return result; + }) + .collect(Collectors.toList()); + } + private void openManagementMenu(Player p, CommandBlockHolder holder) { ServerUtils.verbose("Opening management menu for %s", holder.owner()); boolean whitelisted = holder.isWhitelisted(); - CustomGui menu = CustomGui.create() .title(Text.color("&l ⬇ &6&lManaging Command Block")) .size(9) .defineMain(e -> e.setCancelled(true)) - .define(0,createDisplayItem(holder)) - .define(2, createActionItem(whitelisted ? "Un-Whitelist" : "Whitelist", whitelisted ? Material.BARRIER : Material.PAPER), e -> { + .define(0, createDisplayItem(holder)) + .define(2, createActionItem(whitelisted ? "Un-Whitelist" : "Whitelist", whitelisted ? Material.BARRIER : Material.PAPER), e -> { holder.setWhitelisted(!whitelisted); - p.playSound(p.getLocation(),Sound.BLOCK_NOTE_BLOCK_PLING,1,1F); - openManagementMenu(p,holder); + p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1, 1F); + openManagementMenu(p, holder); }) .define(3, createActionItem("Teleport", Material.ENDER_PEARL), e -> { if (holder.loc().isUUID()) { // Handle minecart teleport Entity entity = Bukkit.getEntity(holder.loc().toUIID()); if (entity == null) { - e.getInventory().setItem(e.getSlot(),ItemBuilder.create() - .material(Material.BARRIER) - .name("&cTeleport Unavailable") - .lore("&7This entity is not loaded.") + e.getInventory().setItem(e.getSlot(), new ItemBuilder() + .material(Material.BARRIER) + .name("&cTeleport Unavailable") + .lore("&7This entity is not loaded.") .build()); - p.playSound(p.getLocation(),Sound.BLOCK_NOTE_BLOCK_BASS,1,1F); + p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1F); return; } p.teleport(entity.getLocation()); } else { p.teleport(holder.loc().translate()); } - p.playSound(p.getLocation(),Sound.ENTITY_ENDERMAN_TELEPORT,1,0.5F); + p.playSound(p.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 1, 0.5F); p.closeInventory(); }) .define(4, createActionItem("Restore", Material.DISPENSER), e -> { holder.restore(); p.openInventory(createGUI(p).getInventory()); - p.playSound(p.getLocation(),Sound.BLOCK_AMETHYST_BLOCK_RESONATE,1,1F); + p.playSound(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_RESONATE, 1, 1F); }) .define(5, createActionItem("Destroy (Shift-Click)", Material.NETHERITE_PICKAXE), e -> { if (!e.isShiftClick()) return; holder.destroy(); - p.playSound(p.getLocation(),Sound.ENTITY_GENERIC_EXPLODE,1,2F); + p.playSound(p.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, 1, 2F); p.openInventory(createGUI(p).getInventory()); }) - .define(6,createActionItem("Take Ownership",Material.NAME_TAG), e -> { + .define(6, createActionItem("Take Ownership", Material.NAME_TAG), e -> { holder.setOwner(p.getUniqueId().toString()); - p.playSound(p.getLocation(),Sound.ENTITY_VILLAGER_TRADE,1,1F); - openManagementMenu(p,holder); + p.playSound(p.getLocation(), Sound.ENTITY_VILLAGER_TRADE, 1, 1F); + openManagementMenu(p, holder); }) - .define(8,Items.BACK,e->{ - p.playSound(p.getLocation(),Sound.ITEM_BOOK_PAGE_TURN,1,0.8F); + .define(8, Items.BACK, e -> { + p.playSound(p.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1, 0.8F); p.openInventory(createGUI(p).getInventory()); }) .build(); @@ -212,103 +221,6 @@ public class WhitelistGUI { .build(); } - // Filter handling methods - private enum Filter { - OWNER, CURRENT_WORLD, OTHER_OWNERS, - MINECART, REPEAT, CHAIN, IMPULSE, - WHITELISTED, NOT_WHITELISTED, NOT_PRESENT - } - - public enum FilterOperator { - AND, // All conditions must be met - OR, // At least one condition must be met - NAND, // At least one condition must NOT be met - XOR; // Exactly one condition must be met - - public boolean apply(boolean currentValue, boolean newCondition) { - return switch (this) { - case AND -> currentValue & newCondition; - case OR -> currentValue | newCondition; - case NAND -> !(currentValue & newCondition); - case XOR -> currentValue ^ newCondition; - }; - } - } - - private List filterEntries(Player p, FilterOperator operator) { - Set filters = activeFilters.computeIfAbsent(p.getUniqueId(), v -> new HashSet<>()); - ServerUtils.verbose("Filtering entries for %s. Current: ", p,filters.toString()); - return Sentinel.getInstance().getDirector().io.commandBlocks.holders.stream() - .filter(holder -> { - if (filters.isEmpty()) return true; - - boolean result = (operator == FilterOperator.AND); // AND starts true, OR starts false - - for (Filter filter : filters) { - boolean conditionMet = switch (filter) { - case OWNER -> holder.owner().equals(p.getUniqueId().toString()); - case CURRENT_WORLD -> holder.loc().world().equals(p.getWorld().getName()); - case OTHER_OWNERS -> !holder.owner().equals(p.getUniqueId().toString()); - case MINECART -> holder.getType().equals(Material.COMMAND_BLOCK_MINECART); - case REPEAT -> holder.getType().equals(Material.REPEATING_COMMAND_BLOCK); - case CHAIN -> holder.getType().equals(Material.CHAIN_COMMAND_BLOCK); - case IMPULSE -> holder.getType().equals(Material.COMMAND_BLOCK); - case WHITELISTED -> holder.isWhitelisted(); - case NOT_WHITELISTED -> !holder.isWhitelisted(); - case NOT_PRESENT -> !holder.present(); - }; - - result = operator.apply(result, conditionMet); - - // Early exit for AND (false means no need to check further) - if (operator == FilterOperator.AND && !result) return false; - // Early exit for OR (true means we already pass) - if (operator == FilterOperator.OR && result) return true; - } - - return result; - }) - .collect(Collectors.toList()); - } - - private void openFilterMenu(Player p) { - ServerUtils.verbose("Creating filter menu for %s", p); - Set filters = activeFilters.computeIfAbsent(p.getUniqueId(), k -> new HashSet<>()); - - CustomGui filterGui = CustomGui.create() - .title(Text.color("&6&lFilters")) - .size(27) - .defineMain(e -> e.setCancelled(true)) - .define(0, createFilterToggleItem("Your Blocks", Material.PLAYER_HEAD, filters.contains(Filter.OWNER)), - e -> toggleFilter(p, Filter.OWNER)) - .define(1, createFilterToggleItem("Other Owners", Material.SPYGLASS, filters.contains(Filter.OTHER_OWNERS)), - e -> toggleFilter(p, Filter.OTHER_OWNERS)) - .define(2, createFilterToggleItem("Current World", Material.TARGET, filters.contains(Filter.CURRENT_WORLD)), - e -> toggleFilter(p, Filter.CURRENT_WORLD)) - .define(3, createFilterToggleItem("Whitelisted Blocks", Material.PAPER, filters.contains(Filter.WHITELISTED)), - e -> toggleFilter(p, Filter.WHITELISTED)) - .define(4, createFilterToggleItem("Not Whitelisted Only", Material.BARRIER, filters.contains(Filter.NOT_WHITELISTED)), - e -> toggleFilter(p, Filter.NOT_WHITELISTED)) - .define(5, createFilterToggleItem("Missing Command Blocks", Material.GLASS, filters.contains(Filter.NOT_PRESENT)), - e -> toggleFilter(p, Filter.NOT_PRESENT)) - .define(6, createFilterToggleItem("Repeating Command Blocks", Material.REPEATING_COMMAND_BLOCK, filters.contains(Filter.REPEAT)), - e -> toggleFilter(p, Filter.REPEAT)) - .define(7, createFilterToggleItem("Chain Command Blocks", Material.CHAIN_COMMAND_BLOCK, filters.contains(Filter.CHAIN)), - e -> toggleFilter(p, Filter.CHAIN)) - .define(8, createFilterToggleItem("Impulse Command Blocks", Material.COMMAND_BLOCK, filters.contains(Filter.IMPULSE)), - e -> toggleFilter(p, Filter.IMPULSE)) - .define(9, createFilterToggleItem("Minecart Commands", Material.COMMAND_BLOCK_MINECART, filters.contains(Filter.MINECART)), - e -> toggleFilter(p, Filter.MINECART)) - .define(26, Items.BACK, - e-> { - p.playSound(p.getLocation(),Sound.ITEM_BOOK_PAGE_TURN,1,0.8F); - p.openInventory(createGUI(p).getInventory()); - }) - .build(); - - p.openInventory(filterGui.getInventory()); - } - private ItemStack createFilterToggleItem(String name, Material mat, boolean active) { return new ItemBuilder() .material(mat) @@ -317,60 +229,13 @@ public class WhitelistGUI { .build(); } - - - private void toggleFilter(Player p, Filter filter) { - Set filters = activeFilters.computeIfAbsent(p.getUniqueId(), k -> new HashSet<>()); - ServerUtils.verbose("%s is now toggling the %s filter. Current %s", p,filter,filters); - if (filters.contains(filter)) filters.remove(filter); - else filters.add(filter); - ServerUtils.verbose("Current filters for %s: %s", p,filters); - openFilterMenu(p); - } - - private int getFilterCount(Player p) { - return activeFilters.getOrDefault(p.getUniqueId(), new HashSet<>()).size(); - } - - private void changePage(Player p, int direction) { - int current = currentPages.getOrDefault(p.getUniqueId(), 0); - int newPage = realizePage(p, current + direction); - currentPages.put(p.getUniqueId(), newPage); - p.openInventory(createGUI(p).getInventory()); - } - - private int realizePage(Player p, int requested) { - int validRequested = Math.max(0, requested); - int totalEntries = filterEntries(p, - chosenOperator.computeIfAbsent(p.getUniqueId(), v -> FilterOperator.AND)).size(); - int maxPages = Math.max(0, Math.ceilDiv(totalEntries, 45) - 1); - return Math.min(validRequested, maxPages); - } - - private ItemStack createNavigationItem(String direction, int pageTo) { + private ItemStack createFilterToggleItemValue(String name, Material mat, boolean active, String value) { return new ItemBuilder() - .material(Material.ARROW) - .name(Text.color("&b" + direction + "&7 Page")) - .lore(Text.color("&7 > &b" + pageTo)) + .material(mat) + .name(Text.color((active ? "&a" : "&c") + name)) + .lore(Text.color("&7Value&f: &b" + value)) + .lore(Text.color("&7Left Click to " + (active ? "disable" : "enable"))) + .lore(Text.color("&7Right Click to set value.")) .build(); } - - private ItemStack createFilterItem(Player p) { - List operatorList = new ArrayList<>(); - FilterOperator chosen = chosenOperator.computeIfAbsent(p.getUniqueId(),v->FilterOperator.AND); - for (FilterOperator value : FilterOperator.values()) { - if (value.equals(chosen)) operatorList.add(Text.color("&b&n" + value.name())); - else operatorList.add(Text.color("&b" + value.name())); - } - return new ItemBuilder() - .material(Material.HOPPER) - .name(Text.color("&6&lFilters")) - .lore(Text.color("&7Filters Selected: &e" + getFilterCount(p))) - .lore(Text.color("&7Shift-Click to cycle filter operator.")) - .lore(Text.color("&7Operator: ")) - .lore(operatorList) - .build(); - } - - } \ No newline at end of file diff --git a/src/main/java/me/trouper/sentinel/utils/FileUtils.java b/src/main/java/me/trouper/sentinel/utils/FileUtils.java index 7c51ab8..d88a152 100644 --- a/src/main/java/me/trouper/sentinel/utils/FileUtils.java +++ b/src/main/java/me/trouper/sentinel/utils/FileUtils.java @@ -46,63 +46,6 @@ public final class FileUtils { } } - public static String createNBTLog(String contents) { - ServerUtils.verbose("FileUtils: Creating NBT log"); - String fileName = "nbt_log-" + Random.generateID(); - - File dataFolder = Sentinel.getInstance().getDirector().io.getDataFolder(); - - File loggedNBTFolder = new File(dataFolder,"LoggedNBT"); - if (!loggedNBTFolder.exists()) { - loggedNBTFolder.mkdirs(); - } - - File file = new File(loggedNBTFolder, fileName + ".txt"); - try { - if (!file.exists()) { - file.createNewFile(); - } - - BufferedWriter writer = new BufferedWriter(new FileWriter(file, true)); - writer.append(contents); - writer.close(); - - } catch (IOException e) { - e.printStackTrace(); - } - return fileName; - } - - public static String createNBTLog(ItemStack i) { - ServerUtils.verbose("FileUtils: Creating NBT log"); - - String item = i.getType().name().toLowerCase() + i.getItemMeta().getAsString(); - - String fileName = "nbt_log-" + Random.generateID(); - - File dataFolder = Sentinel.getInstance().getDirector().io.getDataFolder(); - - File loggedNBTFolder = new File(dataFolder,"LoggedNBT"); - if (!loggedNBTFolder.exists()) { - loggedNBTFolder.mkdirs(); - } - - File file = new File(loggedNBTFolder, fileName + ".txt"); - try { - if (!file.exists()) { - file.createNewFile(); - } - - BufferedWriter writer = new BufferedWriter(new FileWriter(file, true)); - writer.append(item); - writer.close(); - - } catch (IOException e) { - e.printStackTrace(); - } - return fileName; - } - public static String createCommandLog(String command) { diff --git a/src/main/java/me/trouper/sentinel/utils/Text.java b/src/main/java/me/trouper/sentinel/utils/Text.java index 2f69154..e4f05cf 100644 --- a/src/main/java/me/trouper/sentinel/utils/Text.java +++ b/src/main/java/me/trouper/sentinel/utils/Text.java @@ -4,6 +4,7 @@ package me.trouper.sentinel.utils; import me.trouper.sentinel.Sentinel; import org.bukkit.Location; +import java.util.Locale; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -115,7 +116,7 @@ public final class Text { } public static String cleanName(String type) { - return type.replaceAll("_"," ").toLowerCase(); + return type.replaceAll("_"," ").toUpperCase(Locale.US); } public static String formatMillis(long millis) { diff --git a/src/main/java/me/trouper/sentinel/utils/display/BlockDisplayRaytracer.java b/src/main/java/me/trouper/sentinel/utils/display/BlockDisplayRaytracer.java index 8d00d0f..c047a06 100644 --- a/src/main/java/me/trouper/sentinel/utils/display/BlockDisplayRaytracer.java +++ b/src/main/java/me/trouper/sentinel/utils/display/BlockDisplayRaytracer.java @@ -8,13 +8,13 @@ import org.bukkit.World; import org.bukkit.entity.BlockDisplay; import org.bukkit.entity.Display; import org.bukkit.entity.Player; -import org.bukkit.util.Consumer; import org.bukkit.util.Transformation; import org.bukkit.util.Vector; import org.joml.AxisAngle4f; import org.joml.Vector3f; import java.util.List; +import java.util.function.Consumer; public class BlockDisplayRaytracer { diff --git a/src/main/java/me/trouper/sentinel/utils/trees/EmbedFormatter.java b/src/main/java/me/trouper/sentinel/utils/trees/EmbedFormatter.java index 32314ac..c52727c 100644 --- a/src/main/java/me/trouper/sentinel/utils/trees/EmbedFormatter.java +++ b/src/main/java/me/trouper/sentinel/utils/trees/EmbedFormatter.java @@ -24,7 +24,8 @@ public class EmbedFormatter { try { webhook.send(spec); } catch (IOException e) { - Sentinel.getInstance().getLogger().warning(e.getMessage()); + Sentinel.getInstance().getLogger().info("Discord declined the web request: " + e.getMessage()); + Sentinel.getInstance().getLogger().info("Please insure your webhook URL is correct, otherwise nothing will be logged to discord."); success.set(false); return; }