diff --git a/.gradle/8.5/checksums/checksums.lock b/.gradle/8.5/checksums/checksums.lock index d8436f8..57f4afc 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 733ccbc..24a1a6a 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 3058fdd..fe187f1 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 666c7f9..27a1391 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 ecd5a41..e63c391 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 b5263b9..c13e6e9 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 cde5abb..a9168dc 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 3019584..340d4d4 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 1205e39..922c19f 100644 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/.gradle/buildOutputCleanup/outputFiles.bin b/.gradle/buildOutputCleanup/outputFiles.bin index 6610768..ae02add 100644 Binary files a/.gradle/buildOutputCleanup/outputFiles.bin and b/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe index 6a78d3a..2a5025c 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/ViolationConfig$CommandBlockEdit.class b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockEdit.class index b74f19a..e76542c 100644 Binary files a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockEdit.class and b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockEdit.class differ diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockMinecartPlace.class b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockMinecartPlace.class index 0c1d6d2..8d7162f 100644 Binary files a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockMinecartPlace.class and b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockMinecartPlace.class differ diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockMinecartUse.class b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockMinecartUse.class index 0483e71..1263e41 100644 Binary files a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockMinecartUse.class and b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockMinecartUse.class differ diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockPlace.class b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockPlace.class index a3c034b..4ae7773 100644 Binary files a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockPlace.class and b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockPlace.class differ diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockUse.class b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockUse.class index dcaed8c..a643f0a 100644 Binary files a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockUse.class and b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockUse.class differ diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute$Dangerous.class b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute$Dangerous.class index 06d3eac..4cc1acb 100644 Binary files a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute$Dangerous.class and b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute$Dangerous.class differ diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute$Logged.class b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute$Logged.class index ad430f7..9bd41d0 100644 Binary files a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute$Logged.class and b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute$Logged.class differ diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute$Specific.class b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute$Specific.class index 3651003..0af73a1 100644 Binary files a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute$Specific.class and b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute$Specific.class differ diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute.class b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute.class index feb553d..9e170a3 100644 Binary files a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute.class and b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute.class differ diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CreativeHotbarAction.class b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CreativeHotbarAction.class index 8a24f38..0b5d141 100644 Binary files a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CreativeHotbarAction.class and b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CreativeHotbarAction.class differ diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig.class b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig.class index 76b1f60..90b5afc 100644 Binary files a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig.class and b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig.class differ diff --git a/build/classes/java/main/me/trouper/sentinel/server/gui/Items.class b/build/classes/java/main/me/trouper/sentinel/server/gui/Items.class index 35cbc01..64977ab 100644 Binary files a/build/classes/java/main/me/trouper/sentinel/server/gui/Items.class and b/build/classes/java/main/me/trouper/sentinel/server/gui/Items.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 250d33d..6a7713c 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/tmp/compileJava/previous-compilation-data.bin b/build/tmp/compileJava/previous-compilation-data.bin index 32aa867..8a1e90a 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/IO.java b/src/main/java/me/trouper/sentinel/data/IO.java index ae63fda..9fdaee2 100644 --- a/src/main/java/me/trouper/sentinel/data/IO.java +++ b/src/main/java/me/trouper/sentinel/data/IO.java @@ -6,6 +6,7 @@ import me.trouper.sentinel.data.config.*; import me.trouper.sentinel.data.config.lang.LanguageFile; import me.trouper.sentinel.data.storage.ExtraStorage; import me.trouper.sentinel.data.storage.CommandBlockStorage; +import me.trouper.sentinel.data.storage.NBTStorage; import java.io.File; @@ -20,6 +21,7 @@ public class IO { private final File advcfg = new File(dataFolder, "/advanced-config.json"); private final File cmdWhitelist = new File(dataFolder, "/storage/whitelist.json"); private final File extraFile = new File(dataFolder, "/storage/extra.json"); + private final File nbtFile = new File(dataFolder,"/storage/nbt.json"); public LanguageFile lang; public ViolationConfig violationConfig = JsonSerializable.load(violationcfg, ViolationConfig.class, new ViolationConfig()); @@ -31,6 +33,7 @@ public class IO { public StrictConfig strictConfig = JsonSerializable.load(strctcfg, StrictConfig.class, new StrictConfig()); public NBTConfig nbtConfig = JsonSerializable.load(nbtcfg, NBTConfig.class, new NBTConfig()); public AdvancedConfig advConfig = JsonSerializable.load(advcfg, AdvancedConfig.class, new AdvancedConfig()); + public NBTStorage nbtStorage = JsonSerializable.load(nbtFile, NBTStorage.class, new NBTStorage()); public void loadConfig() { // Init @@ -41,6 +44,7 @@ public class IO { swearConfig = JsonSerializable.load(swrcfg,SwearsConfig.class,new SwearsConfig()); nbtConfig = JsonSerializable.load(nbtcfg,NBTConfig.class,new NBTConfig()); violationConfig = JsonSerializable.load(violationcfg,ViolationConfig.class,new ViolationConfig()); + // Save mainConfig.save(); @@ -50,11 +54,17 @@ public class IO { swearConfig.save(); nbtConfig.save(); violationConfig.save(); + + // Storage commandBlocks = JsonSerializable.load(cmdWhitelist, CommandBlockStorage.class, new CommandBlockStorage()); extraStorage = JsonSerializable.load(extraFile, ExtraStorage.class, new ExtraStorage()); + nbtStorage = JsonSerializable.load(nbtFile,NBTStorage.class,new NBTStorage()); + commandBlocks.save(); extraStorage.save(); + nbtStorage.save(); + Sentinel.getInstance().getLogger().info("Loading Dictionary (%s)...".formatted(mainConfig.plugin.lang)); diff --git a/src/main/java/me/trouper/sentinel/data/storage/NBTStorage.java b/src/main/java/me/trouper/sentinel/data/storage/NBTStorage.java new file mode 100644 index 0000000..e034bf5 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/data/storage/NBTStorage.java @@ -0,0 +1,46 @@ +package me.trouper.sentinel.data.storage; + +import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable; +import me.trouper.sentinel.Sentinel; +import org.bukkit.inventory.ItemStack; + +import java.io.*; +import java.util.*; + +public class NBTStorage implements JsonSerializable { + @Override + public File getFile() { + File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "/storage/nbt.json"); + file.getParentFile().mkdirs(); + return file; + } + + public Map caughtItems = new HashMap<>(); + + public static ItemStack toItem(String data) { + try { + byte[] bytes = Base64.getDecoder().decode(data); + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); + ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); + ItemStack item = (ItemStack) objectInputStream.readObject(); + objectInputStream.close(); + return item; + } catch (IOException | ClassNotFoundException e) { + Sentinel.getInstance().getLogger().warning("Could not deserialize ItemStack: " + e.getMessage()); + return null; + } + } + + public static String toB64(ItemStack item) { + try { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); + objectOutputStream.writeObject(item); + objectOutputStream.close(); + return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray()); + } catch (IOException e) { + Sentinel.getInstance().getLogger().warning("Could not serialize ItemStack: " + e.getMessage()); + return null; + } + } +} 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 227f81a..c758c5a 100644 --- a/src/main/java/me/trouper/sentinel/data/types/CommandBlockHolder.java +++ b/src/main/java/me/trouper/sentinel/data/types/CommandBlockHolder.java @@ -8,10 +8,7 @@ import me.trouper.sentinel.utils.DisplayUtils; import me.trouper.sentinel.utils.ServerUtils; import me.trouper.sentinel.utils.Text; import me.trouper.sentinel.utils.display.BlockDisplayRaytracer; -import org.bukkit.Bukkit; -import org.bukkit.Color; -import org.bukkit.Location; -import org.bukkit.Material; +import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.CommandBlock; @@ -288,8 +285,11 @@ public class CommandBlockHolder { } if (!preLoaded) where.getChunk().unload(); } - - if (changesMade) updater.sendMessage(Text.prefix("Successfully updated a &b%s&7.".formatted(Text.cleanName(this.type())))); + + if (changesMade) { + updater.sendMessage(Text.prefix("Successfully updated a &b%s&7.".formatted(Text.cleanName(this.type())))); + updater.playSound(updater.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING,1,1.5F); + } return changesMade; } @@ -313,7 +313,10 @@ public class CommandBlockHolder { } - if (changesMade) updater.sendMessage(Text.prefix("Successfully updated a &b%s&7.".formatted(Text.cleanName(this.type())))); + if (changesMade) { + updater.sendMessage(Text.prefix("Successfully updated a &b%s&7.".formatted(Text.cleanName(this.type())))); + updater.playSound(updater.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING,1,1.5F); + } return changesMade; } @@ -360,7 +363,10 @@ public class CommandBlockHolder { changesMade = true; } - if (changesMade) updater.sendMessage(Text.prefix("Successfully updated a &b%s&7.".formatted(Text.cleanName(this.type())))); + if (changesMade) { + updater.sendMessage(Text.prefix("Successfully updated a &b%s&7.".formatted(Text.cleanName(this.type())))); + updater.playSound(updater.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING,1,1.5F); + } return changesMade; } } diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockBreak.java b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockBreak.java index 0c1cd0e..27170f9 100644 --- a/src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockBreak.java +++ b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockBreak.java @@ -45,7 +45,7 @@ public class CommandBlockBreak extends AbstractViolation{ if (!Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.enabled) { ServerUtils.verbose("Not enabled, deletion allowed."); - holder.delete(); + if (!holder.isWhitelisted()) holder.delete(); return; } diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/entities/CommandMinecartBreak.java b/src/main/java/me/trouper/sentinel/server/events/violations/entities/CommandMinecartBreak.java index 31a14fc..2ee70d8 100644 --- a/src/main/java/me/trouper/sentinel/server/events/violations/entities/CommandMinecartBreak.java +++ b/src/main/java/me/trouper/sentinel/server/events/violations/entities/CommandMinecartBreak.java @@ -48,7 +48,7 @@ public class CommandMinecartBreak extends AbstractViolation { if (!Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartBreak.enabled) { ServerUtils.verbose("Not enabled, deletion allowed."); - holder.delete(); + if (!holder.isWhitelisted()) holder.delete(); return; } 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 cd7bcb7..efb8a69 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 @@ -2,6 +2,7 @@ package me.trouper.sentinel.server.events.violations.players; import io.github.itzispyder.pdk.plugin.gui.CustomGui; import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.data.storage.NBTStorage; import me.trouper.sentinel.server.events.violations.AbstractViolation; import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; import me.trouper.sentinel.server.functions.itemchecks.ItemCheck; @@ -42,6 +43,9 @@ 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(); + ActionConfiguration.Builder config = new ActionConfiguration.Builder() .setEvent(e) .setPlayer(p) diff --git a/src/main/java/me/trouper/sentinel/server/gui/Items.java b/src/main/java/me/trouper/sentinel/server/gui/Items.java index 0b2019e..2a0adba 100644 --- a/src/main/java/me/trouper/sentinel/server/gui/Items.java +++ b/src/main/java/me/trouper/sentinel/server/gui/Items.java @@ -82,9 +82,16 @@ public class Items { .enchant(Enchantment.PROTECTION, 64) .flag(ItemFlag.HIDE_ENCHANTS) .build(); + public static final ItemStack NBT = ItemBuilder.create() + .material(Material.HONEY_BOTTLE) + .name(Text.color("&aNBT Honeypot")) + .lore(Text.color("&8&l➥&7 View caught NBT")) + .enchant(Enchantment.PROTECTION, 64) + .flag(ItemFlag.HIDE_ENCHANTS) + .build(); public static ItemStack configItem(String valueName, Material material, String description) { - ServerUtils.verbose("Items#configItem: Creating a config item:\n Value Name -> %s\nMaterial in use -> %s".formatted(valueName,material.toString())); + ServerUtils.verbose("Creating a config item:\n Value Name -> %s\nMaterial in use -> %s".formatted(valueName,material.toString())); List desc = Arrays.stream(description.split("\n")).toList(); 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 5d7bd5c..85c40ad 100644 --- a/src/main/java/me/trouper/sentinel/server/gui/MainGUI.java +++ b/src/main/java/me/trouper/sentinel/server/gui/MainGUI.java @@ -3,6 +3,8 @@ package me.trouper.sentinel.server.gui; 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; @@ -23,13 +25,17 @@ public class MainGUI { .size(27) .onDefine(this::blankPage) .defineMain(this::mainClick) - .define(11,Items.CREDITS) - .define(13,Items.WHITELIST,this::openWhitelist) - .define(15,Items.CONFIG,this::openConfig) + .define(10,Items.CREDITS) + .define(12,Items.WHITELIST,this::openWhitelist) + .define(14,Items.NBT,this::openNBT) + .define(16,Items.CONFIG,this::openConfig) .build(); private void openWhitelist(InventoryClickEvent e) { - e.getWhoClicked().openInventory(new WhitelistGUI().createGUI((Player) e.getWhoClicked()).getInventory()); + e.getWhoClicked().openInventory(new NewWhitelistGUI().createGUI((Player) e.getWhoClicked()).getInventory()); + } + private void openNBT(InventoryClickEvent e) { + e.getWhoClicked().openInventory(new NBTGui().createGUI((Player) e.getWhoClicked()).getInventory()); } private void openConfig(InventoryClickEvent e) { diff --git a/src/main/java/me/trouper/sentinel/server/gui/PaginatedGUI.java b/src/main/java/me/trouper/sentinel/server/gui/PaginatedGUI.java new file mode 100644 index 0000000..1fbdbef --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/gui/PaginatedGUI.java @@ -0,0 +1,158 @@ +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; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.*; + +public abstract class PaginatedGUI { + + protected static final int ITEMS_PER_PAGE = 45; + protected static final Map currentPages = new HashMap<>(); + protected static final Map> activeFilters = new HashMap<>(); + protected 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, v == null ? 0 : v)); + return CustomGui.create() + .title(getTitle(p)) + .size(54) + .onDefine(inv -> setupPage(p, inv)) + .defineMain(e -> handleMainClick(p, e)) + .define(45, createNavigationItem("Previous", page - 1), e -> changePage(p, -1)) + .define(49, createFilterItem(p), e -> openFilterMenu(p)) + .define(53, createNavigationItem("Next", page + 1), e -> changePage(p, 1)) + .build(); + } + + protected abstract String getTitle(Player p); + + protected 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, 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 < 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 + inv.setItem(45, createNavigationItem("Previous", realizePage(p, page - 1))); + inv.setItem(49, createFilterItem(p)); + inv.setItem(53, createNavigationItem("Next", realizePage(p, page + 1))); + } + + protected abstract void handleMainClick(Player p, InventoryClickEvent e); + + protected abstract ItemStack createDisplayItem(T item); + + protected void openFilterMenu(Player p) { + ServerUtils.verbose("Creating filter menu for %s", p); + Set filters = activeFilters.computeIfAbsent(p.getUniqueId(), k -> new HashSet<>()); + + CustomGui.GuiBuilder filterGui = CustomGui.create() + .title(Text.color("&6&lFilters")) + .size(27) + .defineMain(e -> e.setCancelled(true)) + .define(26, Items.BACK, e -> { + p.playSound(p.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1, 0.8F); + p.openInventory(createGUI(p).getInventory()); + }); + + addFilterItems(filterGui, p, filters); + + p.openInventory(filterGui.build().getInventory()); + } + + protected abstract void addFilterItems(CustomGui.GuiBuilder filterGui, Player p, Set filters); + + protected void toggleFilter(Player p, String 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); + } + + protected int getFilterCount(Player p) { + return activeFilters.getOrDefault(p.getUniqueId(), new HashSet<>()).size(); + } + + protected 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()); + } + + protected 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, ITEMS_PER_PAGE) - 1); + return Math.min(validRequested, maxPages); + } + + private ItemStack createNavigationItem(String direction, int pageTo) { + return new ItemBuilder() + .material(Material.ARROW) + .name(Text.color("&b" + direction + "&7 Page")) + .lore(Text.color("&7 > &b" + pageTo)) + .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(); + } + + protected abstract List filterEntries(Player p, FilterOperator operator); + + 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; + }; + } + } +} \ No newline at end of file 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 new file mode 100644 index 0000000..2335c2c --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/gui/nbt/NBTGui.java @@ -0,0 +1,104 @@ +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.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 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 new file mode 100644 index 0000000..3f608a1 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/gui/whitelist/NewWhitelistGUI.java @@ -0,0 +1,219 @@ +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.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + +public class NewWhitelistGUI extends PaginatedGUI { + + @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")); + } + + @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(); + 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(); + } + + 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+"))); + cfg.save(); + player.sendMessage(Text.prefix("Value updated successfully")); + player.openInventory(getConfigGui().getInventory()); + }); + 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))); + } +} \ 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 16d1cec..ac87c66 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 @@ -24,44 +24,45 @@ public class WhitelistGUI { private static final Map currentPages = new HashMap<>(); private static final Map> activeFilters = new HashMap<>(); private static final Map chosenOperator = new HashMap<>(); + private static final Map chosenPlayer = new HashMap<>(); - public CustomGui createGUI(Player player) { - ServerUtils.verbose("Creating GUI for player: %s", player.getName()); - int page = currentPages.compute(player.getUniqueId(), (k,v) -> realizePage(player,realizePage(player,(v == null ? 0 : v)))); + 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(player) + " filters)")) + .title(Text.color("&6&lCommand Blocks &7(" + getFilterCount(p) + " filters)")) .size(54) - .onDefine(inv -> setupPage(player, inv)) + .onDefine(inv -> setupPage(p, inv)) .defineMain(e -> { e.setCancelled(true); - handleMainClick(player, e); + handleMainClick(p, e); }) .define(45, createNavigationItem("Previous",page - 1), e -> { - player.playSound(player.getLocation(),Sound.BLOCK_NOTE_BLOCK_HAT,1,0.9F); - changePage(player, -1); + p.playSound(p.getLocation(),Sound.BLOCK_NOTE_BLOCK_HAT,1,0.9F); + changePage(p, -1); }) - .define(49, createFilterItem(player), e -> { + .define(49, createFilterItem(p), e -> { if (e.isShiftClick()) { - FilterOperator op = chosenOperator.computeIfAbsent(player.getUniqueId(),v-> FilterOperator.AND); + FilterOperator op = chosenOperator.computeIfAbsent(p.getUniqueId(),v-> FilterOperator.AND); FilterOperator[] values = FilterOperator.values(); - chosenOperator.put(player.getUniqueId(),values[(op.ordinal() + 1) % values.length]); - e.getClickedInventory().setItem(e.getSlot(),createFilterItem(player)); - player.playSound(player.getLocation(),Sound.BLOCK_NOTE_BLOCK_HAT,1,1.3F); + 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(player); + openFilterMenu(p); }) .define(53, createNavigationItem("Next",page + 1), e -> { - player.playSound(player.getLocation(),Sound.BLOCK_NOTE_BLOCK_HAT,1,1.1F); - changePage(player, 1); + p.playSound(p.getLocation(),Sound.BLOCK_NOTE_BLOCK_HAT,1,1.1F); + changePage(p, 1); }) .build(); } - private void setupPage(Player player, Inventory inv) { - ServerUtils.verbose("Setting up page for player: %s", player.getName()); - int page = currentPages.compute(player.getUniqueId(), (k,v) -> realizePage(player,realizePage(player,(v == null ? 0 : v)))); - List filtered = filterEntries(player,chosenOperator.computeIfAbsent(player.getUniqueId(),v->FilterOperator.AND)); + 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 @@ -76,24 +77,24 @@ public class WhitelistGUI { } // Add persistent bottom items - inv.setItem(45, createNavigationItem("Previous",realizePage(player, page - 1))); - inv.setItem(49, createFilterItem(player)); - inv.setItem(53, createNavigationItem("Next", realizePage(player,page + 1))); + inv.setItem(45, createNavigationItem("Previous",realizePage(p, page - 1))); + inv.setItem(49, createFilterItem(p)); + inv.setItem(53, createNavigationItem("Next", realizePage(p,page + 1))); } - private void handleMainClick(Player player, InventoryClickEvent e) { + private 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(player.getUniqueId(), (k,v) -> realizePage(player,realizePage(player,(v == null ? 0 : v)))); - List filtered = filterEntries(player,chosenOperator.computeIfAbsent(player.getUniqueId(),v->FilterOperator.AND)); + 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; if (index < filtered.size()) { CommandBlockHolder holder = filtered.get(index); - player.playSound(player.getLocation(),Sound.BLOCK_NOTE_BLOCK_CHIME,1,0.8F); - openManagementMenu(player, holder); + p.playSound(p.getLocation(),Sound.BLOCK_NOTE_BLOCK_CHIME,1,0.8F); + openManagementMenu(p, holder); } } @@ -114,7 +115,7 @@ public class WhitelistGUI { //ServerUtils.verbose("Name is %s", name); List lore = new ArrayList<>(); - lore.add(Text.color("&7Owner: " + Bukkit.getOfflinePlayer(holder.owner()).getName())); + lore.add(Text.color("&7Owner: " + Bukkit.getOfflinePlayer(UUID.fromString(holder.owner())).getName())); //ServerUtils.verbose("Got owner"); lore.add(Text.color("&7Command: &f" + holder.command())); //ServerUtils.verbose("Got command"); @@ -136,7 +137,7 @@ public class WhitelistGUI { .build(); } - private void openManagementMenu(Player player, CommandBlockHolder holder) { + private void openManagementMenu(Player p, CommandBlockHolder holder) { ServerUtils.verbose("Opening management menu for %s", holder.owner()); boolean whitelisted = holder.isWhitelisted(); @@ -147,8 +148,8 @@ public class WhitelistGUI { .define(0,createDisplayItem(holder)) .define(2, createActionItem(whitelisted ? "Un-Whitelist" : "Whitelist", whitelisted ? Material.BARRIER : Material.PAPER), e -> { holder.setWhitelisted(!whitelisted); - player.playSound(player.getLocation(),Sound.BLOCK_NOTE_BLOCK_PLING,1,1F); - openManagementMenu(player,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()) { @@ -160,39 +161,39 @@ public class WhitelistGUI { .name("&cTeleport Unavailable") .lore("&7This entity is not loaded.") .build()); - player.playSound(player.getLocation(),Sound.BLOCK_NOTE_BLOCK_BASS,1,1F); + p.playSound(p.getLocation(),Sound.BLOCK_NOTE_BLOCK_BASS,1,1F); return; } - player.teleport(entity.getLocation()); + p.teleport(entity.getLocation()); } else { - player.teleport(holder.loc().translate()); + p.teleport(holder.loc().translate()); } - player.playSound(player.getLocation(),Sound.ENTITY_ENDERMAN_TELEPORT,1,0.5F); - player.closeInventory(); + p.playSound(p.getLocation(),Sound.ENTITY_ENDERMAN_TELEPORT,1,0.5F); + p.closeInventory(); }) .define(4, createActionItem("Restore", Material.DISPENSER), e -> { holder.restore(); - player.openInventory(createGUI(player).getInventory()); - player.playSound(player.getLocation(),Sound.BLOCK_AMETHYST_BLOCK_RESONATE,1,1F); + 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(); - player.playSound(player.getLocation(),Sound.ENTITY_GENERIC_EXPLODE,1,2F); - player.openInventory(createGUI(player).getInventory()); + 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(player.getUniqueId().toString()); - player.playSound(player.getLocation(),Sound.ENTITY_VILLAGER_TRADE,1,1F); - openManagementMenu(player,holder); + holder.setOwner(p.getUniqueId().toString()); + p.playSound(p.getLocation(),Sound.ENTITY_VILLAGER_TRADE,1,1F); + openManagementMenu(p,holder); }) .define(8,Items.BACK,e->{ - player.playSound(player.getLocation(),Sound.ITEM_BOOK_PAGE_TURN,1,0.8F); - player.openInventory(createGUI(player).getInventory()); + p.playSound(p.getLocation(),Sound.ITEM_BOOK_PAGE_TURN,1,0.8F); + p.openInventory(createGUI(p).getInventory()); }) .build(); - player.openInventory(menu.getInventory()); + p.openInventory(menu.getInventory()); } private ItemStack createActionItem(String name, Material mat) { @@ -207,7 +208,8 @@ public class WhitelistGUI { private enum Filter { OWNER, CURRENT_WORLD, OTHER_OWNERS, MINECART, REPEAT, CHAIN, IMPULSE, - WHITELISTED, NOT_WHITELISTED, NOT_PRESENT + WHITELISTED, NOT_WHITELISTED, NOT_PRESENT, + USER } public enum FilterOperator { @@ -226,9 +228,9 @@ public class WhitelistGUI { } } - private List filterEntries(Player player, FilterOperator operator) { - Set filters = activeFilters.computeIfAbsent(player.getUniqueId(), v -> new HashSet<>()); - ServerUtils.verbose("Filtering entries for %s. Current: ", player,filters.toString()); + 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; @@ -237,9 +239,9 @@ public class WhitelistGUI { for (Filter filter : filters) { boolean conditionMet = switch (filter) { - case OWNER -> holder.owner().equals(player.getUniqueId().toString()); - case CURRENT_WORLD -> holder.loc().world().equals(player.getWorld().getName()); - case OTHER_OWNERS -> !holder.owner().equals(player.getUniqueId().toString()); + 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); @@ -247,6 +249,7 @@ public class WhitelistGUI { case WHITELISTED -> holder.isWhitelisted(); case NOT_WHITELISTED -> !holder.isWhitelisted(); case NOT_PRESENT -> !holder.present(); + case USER -> holder.owner().equals(chosenPlayer.get(p.getUniqueId())); }; result = operator.apply(result, conditionMet); @@ -262,42 +265,49 @@ public class WhitelistGUI { .collect(Collectors.toList()); } - private void openFilterMenu(Player player) { - ServerUtils.verbose("Creating filter menu for %s", player); - Set filters = activeFilters.computeIfAbsent(player.getUniqueId(), k -> new HashSet<>()); + 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(player, Filter.OWNER)) + e -> toggleFilter(p, Filter.OWNER)) .define(1, createFilterToggleItem("Other Owners", Material.SPYGLASS, filters.contains(Filter.OTHER_OWNERS)), - e -> toggleFilter(player, Filter.OTHER_OWNERS)) + e -> toggleFilter(p, Filter.OTHER_OWNERS)) .define(2, createFilterToggleItem("Current World", Material.TARGET, filters.contains(Filter.CURRENT_WORLD)), - e -> toggleFilter(player, Filter.CURRENT_WORLD)) + e -> toggleFilter(p, Filter.CURRENT_WORLD)) .define(3, createFilterToggleItem("Whitelisted Blocks", Material.PAPER, filters.contains(Filter.WHITELISTED)), - e -> toggleFilter(player, Filter.WHITELISTED)) + e -> toggleFilter(p, Filter.WHITELISTED)) .define(4, createFilterToggleItem("Not Whitelisted Only", Material.BARRIER, filters.contains(Filter.NOT_WHITELISTED)), - e -> toggleFilter(player, Filter.NOT_WHITELISTED)) + e -> toggleFilter(p, Filter.NOT_WHITELISTED)) .define(5, createFilterToggleItem("Missing Command Blocks", Material.GLASS, filters.contains(Filter.NOT_PRESENT)), - e -> toggleFilter(player, 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(player, Filter.REPEAT)) + e -> toggleFilter(p, Filter.REPEAT)) .define(7, createFilterToggleItem("Chain Command Blocks", Material.CHAIN_COMMAND_BLOCK, filters.contains(Filter.CHAIN)), - e -> toggleFilter(player, Filter.CHAIN)) + e -> toggleFilter(p, Filter.CHAIN)) .define(8, createFilterToggleItem("Impulse Command Blocks", Material.COMMAND_BLOCK, filters.contains(Filter.IMPULSE)), - e -> toggleFilter(player, Filter.IMPULSE)) + e -> toggleFilter(p, Filter.IMPULSE)) .define(9, createFilterToggleItem("Minecart Commands", Material.COMMAND_BLOCK_MINECART, filters.contains(Filter.MINECART)), - e -> toggleFilter(player, Filter.MINECART)) + e -> toggleFilter(p, Filter.MINECART)) + .define(10, createFilterToggleItemValue("Specific Player",Material.BOW,filters.contains(Filter.USER),chosenPlayer.getOrDefault(p.getUniqueId(),"null")), + e -> { + if (e.isLeftClick()) toggleFilter(p,Filter.USER); + else if (e.isRightClick()) { + Callback + } + }) .define(26, Items.BACK, e-> { - player.playSound(player.getLocation(),Sound.ITEM_BOOK_PAGE_TURN,1,0.8F); - player.openInventory(createGUI(player).getInventory()); + p.playSound(p.getLocation(),Sound.ITEM_BOOK_PAGE_TURN,1,0.8F); + p.openInventory(createGUI(p).getInventory()); }) .build(); - player.openInventory(filterGui.getInventory()); + p.openInventory(filterGui.getInventory()); } private ItemStack createFilterToggleItem(String name, Material mat, boolean active) { @@ -308,30 +318,40 @@ public class WhitelistGUI { .build(); } - private void toggleFilter(Player player, Filter filter) { - Set filters = activeFilters.computeIfAbsent(player.getUniqueId(), k -> new HashSet<>()); - ServerUtils.verbose("%s is now toggling the %s filter. Current %s", player,filter,filters); + 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(); + } + + 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", player,filters); - openFilterMenu(player); + ServerUtils.verbose("Current filters for %s: %s", p,filters); + openFilterMenu(p); } - private int getFilterCount(Player player) { - return activeFilters.getOrDefault(player.getUniqueId(), new HashSet<>()).size(); + private int getFilterCount(Player p) { + return activeFilters.getOrDefault(p.getUniqueId(), new HashSet<>()).size(); } - private void changePage(Player player, int direction) { - int current = currentPages.getOrDefault(player.getUniqueId(), 0); - int newPage = realizePage(player, current + direction); - currentPages.put(player.getUniqueId(), newPage); - player.openInventory(createGUI(player).getInventory()); + 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 player, int requested) { + private int realizePage(Player p, int requested) { int validRequested = Math.max(0, requested); - int totalEntries = filterEntries(player, - chosenOperator.computeIfAbsent(player.getUniqueId(), v -> FilterOperator.AND)).size(); + 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); } @@ -344,9 +364,9 @@ public class WhitelistGUI { .build(); } - private ItemStack createFilterItem(Player player) { + private ItemStack createFilterItem(Player p) { List operatorList = new ArrayList<>(); - FilterOperator chosen = chosenOperator.computeIfAbsent(player.getUniqueId(),v->FilterOperator.AND); + 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())); @@ -354,7 +374,7 @@ public class WhitelistGUI { return new ItemBuilder() .material(Material.HOPPER) .name(Text.color("&6&lFilters")) - .lore(Text.color("&7Filters Selected: &e" + getFilterCount(player))) + .lore(Text.color("&7Filters Selected: &e" + getFilterCount(p))) .lore(Text.color("&7Shift-Click to cycle filter operator.")) .lore(Text.color("&7Operator: ")) .lore(operatorList) diff --git a/src/main/java/me/trouper/sentinel/startup/drm/Loader.java b/src/main/java/me/trouper/sentinel/startup/drm/Loader.java index 55a4742..340e0b0 100644 --- a/src/main/java/me/trouper/sentinel/startup/drm/Loader.java +++ b/src/main/java/me/trouper/sentinel/startup/drm/Loader.java @@ -19,6 +19,7 @@ import me.trouper.sentinel.server.events.violations.blocks.structure.StructureBl import me.trouper.sentinel.server.events.violations.command.DangerousCommand; import me.trouper.sentinel.server.events.violations.command.LoggedCommand; import me.trouper.sentinel.server.events.violations.command.SpecificCommand; +import me.trouper.sentinel.server.events.violations.entities.CommandMinecartEdit; import me.trouper.sentinel.server.events.violations.players.*; import me.trouper.sentinel.server.events.violations.whitelist.CommandBlockExecute; import me.trouper.sentinel.server.events.extras.ShadowRealmEvents; @@ -150,6 +151,7 @@ public final class Loader { PacketEvents.getAPI().getEventManager().registerListener(new PluginCloakingPacket(), PacketListenerPriority.NORMAL); PacketEvents.getAPI().getEventManager().registerListener(new ShadowRealmEvents(), PacketListenerPriority.HIGHEST); PacketEvents.getAPI().getEventManager().registerListener(new CommandBlockEdit(), PacketListenerPriority.NORMAL); + PacketEvents.getAPI().getEventManager().registerListener(new CommandMinecartEdit(), PacketListenerPriority.NORMAL); // Events new AntiBanEvents().register();