Fixed GUI, rate limit, and some NBT checks
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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