Fixed GUI, rate limit, and some NBT checks
This commit is contained in:
@@ -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<NBTStorage> {
|
||||
|
||||
// Mapping from file name to owner UUID (as a String)
|
||||
public Map<String, String> 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<NBTStorage> {
|
||||
*/
|
||||
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<NBTStorage> {
|
||||
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<String, Object> 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<String, Object> 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<String> 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)
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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<T> {
|
||||
|
||||
@@ -24,6 +25,10 @@ public abstract class PaginatedGUI<T> {
|
||||
protected static final Map<UUID, FilterOperator> 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<T> {
|
||||
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<T> {
|
||||
|
||||
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<T> 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<T> filtered = filterEntries(p, operator);
|
||||
int totalEntries = filtered.size();
|
||||
int startIndex = page * ITEMS_PER_PAGE;
|
||||
int endIndex = Math.min(startIndex + ITEMS_PER_PAGE, totalEntries);
|
||||
List<T> 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<T> {
|
||||
.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<String> operatorList = new ArrayList<>();
|
||||
FilterOperator chosen = chosenOperator.computeIfAbsent(p.getUniqueId(), v -> FilterOperator.AND);
|
||||
|
||||
@@ -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<Map.Entry<String,String>> {
|
||||
|
||||
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<Map.Entry<String, String>> filtered = filterEntries(p, chosenOperator.computeIfAbsent(p.getUniqueId(), v -> FilterOperator.AND));
|
||||
int index = page * ITEMS_PER_PAGE + slot;
|
||||
if (index < filtered.size()) {
|
||||
Map.Entry<String, String> 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<String, String> entry) {
|
||||
ItemStack item = NBTStorage.toItem(entry.getKey());
|
||||
if (item == null) return null;
|
||||
|
||||
List<String> 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<String> filters) {
|
||||
// Add any specific filter items here if needed
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Map.Entry<String, String>> filterEntries(Player p, FilterOperator operator) {
|
||||
Set<String> 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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<CommandBlockHolder> {
|
||||
|
||||
private static final Map<UUID, String> 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<CommandBlockHolder> 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<String> 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<String> 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<AsyncChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.getInstance().getDirector().io.violationConfig);
|
||||
protected void queuePlayer(Player player, BiConsumer<ViolationConfig, Args> 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<CommandBlockHolder> filterEntries(Player p, FilterOperator operator) {
|
||||
Set<String> 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();
|
||||
}
|
||||
}
|
||||
@@ -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<CommandBlockHolder> {
|
||||
|
||||
private static final Map<UUID, String> chosenPlayer = new HashMap<>();
|
||||
|
||||
private static final Map<UUID, Integer> currentPages = new HashMap<>();
|
||||
private static final Map<UUID, Set<Filter>> activeFilters = new HashMap<>();
|
||||
private static final Map<UUID, FilterOperator> 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<CommandBlockHolder> 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<CommandBlockHolder> 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<CommandBlockHolder> 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<String> 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<String> 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<AsyncChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.getInstance().getDirector().io.violationConfig);
|
||||
protected void queuePlayer(Player player, BiConsumer<ViolationConfig, Args> 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<CommandBlockHolder> filterEntries(Player p, FilterOperator operator) {
|
||||
Set<String> 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<CommandBlockHolder> filterEntries(Player p, FilterOperator operator) {
|
||||
Set<Filter> 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<Filter> 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<Filter> 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<String> 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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user