From c9ad3dd3a4f48d73973cb5f23b6f3f3ee76803ff Mon Sep 17 00:00:00 2001 From: Ardakaz Date: Wed, 28 May 2025 18:21:35 +0000 Subject: [PATCH] Moving to the right place --- src/net/ardakaz/griefalert/GriefAlert.java | 447 ++++++++++++++------- 1 file changed, 311 insertions(+), 136 deletions(-) diff --git a/src/net/ardakaz/griefalert/GriefAlert.java b/src/net/ardakaz/griefalert/GriefAlert.java index e5efe2b..a469dac 100755 --- a/src/net/ardakaz/griefalert/GriefAlert.java +++ b/src/net/ardakaz/griefalert/GriefAlert.java @@ -8,6 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.block.Block; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -15,30 +18,36 @@ import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.inventory.InventoryAction; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.World; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -public class GriefAlert extends JavaPlugin implements Listener { +public class GriefAlert extends JavaPlugin implements Listener, TabCompleter { private static CoreProtectAPI coreProtectAPI; private Integer identicalAlerts = 1; private String lastAlert; private Set EXCLUDED_BLOCKS; - private Set VALID_CONTAINERS; + private Set VALID_CONTAINERS; private String MAP_LINK; private Boolean ALLOW_STEALING; - private AlertsLogic alertsLogic; + private Connection connection; + private final String DB_FILE = "ignored_locations.db"; // Init GriefAlert @Override @@ -62,48 +71,114 @@ public class GriefAlert extends JavaPlugin implements Listener { MAP_LINK = getConfig().getString("map-link"); ALLOW_STEALING = getConfig().getBoolean("allow-stealing"); - getLogger().info("GriefAlert has been enabled."); + setupDatabase(); + + getCommand("griefalert").setTabCompleter(this); - // Initialize AlertsLogic and register commands - alertsLogic = new AlertsLogic(this); - getCommand("disablelocation").setExecutor(this); - getCommand("enablelocation").setExecutor(this); - getCommand("clearlocations").setExecutor(this); - getCommand("checklocation").setExecutor(this); + getLogger().info("GriefAlert has been enabled."); } @Override public void onDisable() { - if (alertsLogic != null) { - alertsLogic.close(); - } getLogger().info("GriefAlert has been disabled."); + if (connection != null) { + try { connection.close(); } catch (SQLException ignored) {} + } } - @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - String cmd = command.getName().toLowerCase(); - switch (cmd) { - case "disablelocation": - return alertsLogic.handleDisableLocation(sender, args); - case "enablelocation": - return alertsLogic.handleEnableLocation(sender, args); - case "clearlocations": - return alertsLogic.handleClearLocations(sender, args); - case "checklocation": - return alertsLogic.handleCheckLocation(sender, args); - default: - return false; + private void setupDatabase() { + try { + connection = DriverManager.getConnection("jdbc:sqlite:" + getDataFolder().getAbsolutePath() + "/" + DB_FILE); + Statement stmt = connection.createStatement(); + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ignored_locations (x INTEGER, y INTEGER, z INTEGER, world TEXT, PRIMARY KEY (x, y, z, world))"); + stmt.close(); + } catch (SQLException e) { + getLogger().severe("Could not set up SQLite database: " + e.getMessage()); + } + } + + private boolean isLocationIgnored(int x, int y, int z, String world) { + try { + PreparedStatement ps = connection.prepareStatement("SELECT 1 FROM ignored_locations WHERE x=? AND y=? AND z=? AND world=?"); + ps.setInt(1, x); + ps.setInt(2, y); + ps.setInt(3, z); + ps.setString(4, world); + ResultSet rs = ps.executeQuery(); + boolean exists = rs.next(); + rs.close(); + ps.close(); + return exists; + } catch (SQLException e) { + getLogger().warning("DB error: " + e.getMessage()); + return false; + } + } + + private boolean addIgnoredLocation(int x, int y, int z, String world) { + try { + PreparedStatement ps = connection.prepareStatement("INSERT OR IGNORE INTO ignored_locations (x, y, z, world) VALUES (?, ?, ?, ?)"); + ps.setInt(1, x); + ps.setInt(2, y); + ps.setInt(3, z); + ps.setString(4, world); + int updated = ps.executeUpdate(); + ps.close(); + return updated > 0; + } catch (SQLException e) { + getLogger().warning("DB error: " + e.getMessage()); + return false; + } + } + + private boolean removeIgnoredLocation(int x, int y, int z, String world) { + try { + PreparedStatement ps = connection.prepareStatement("DELETE FROM ignored_locations WHERE x=? AND y=? AND z=? AND world=?"); + ps.setInt(1, x); + ps.setInt(2, y); + ps.setInt(3, z); + ps.setString(4, world); + int updated = ps.executeUpdate(); + ps.close(); + return updated > 0; + } catch (SQLException e) { + getLogger().warning("DB error: " + e.getMessage()); + return false; + } + } + + private void clearIgnoredLocations() { + try { + Statement stmt = connection.createStatement(); + stmt.executeUpdate("DELETE FROM ignored_locations"); + stmt.close(); + } catch (SQLException e) { + getLogger().warning("DB error: " + e.getMessage()); + } + } + + private void listIgnoredLocations(CommandSender sender) { + try { + Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT x, y, z, world FROM ignored_locations"); + sender.sendMessage(ChatColor.YELLOW + "Ignored Locations:"); + while (rs.next()) { + sender.sendMessage(ChatColor.GRAY + "- " + rs.getInt("x") + ", " + rs.getInt("y") + ", " + rs.getInt("z") + " in " + rs.getString("world")); + } + rs.close(); + stmt.close(); + } catch (SQLException e) { + sender.sendMessage(ChatColor.RED + "DB error: " + e.getMessage()); } } @EventHandler (ignoreCancelled = true) // Block break alerts public void onBlockBreak(BlockBreakEvent event) { - // Exclusion list - if (EXCLUDED_BLOCKS.contains(event.getBlock().getType())) { - return; - } + // Exclusion list + if (EXCLUDED_BLOCKS.contains(event.getBlock().getType())) { + return; + } // Event parser String playerName = event.getPlayer().getName(); @@ -113,29 +188,23 @@ public class GriefAlert extends JavaPlugin implements Listener { int z = event.getBlock().getZ(); String worldName = event.getBlock().getWorld().getName(); - // Check if alerts are disabled for this location - if (alertsLogic != null && alertsLogic.isLocationSaved(x, y, z, worldName)) { - return; - } - // Check if grief String target = inspectBlock(event.getBlock(), event.getPlayer()); if (target != null) { - // Alert String message = ChatColor.GRAY + playerName + " broke " + blockType + " placed by " + target + " at " + x + " " + y + " " + z + getHumanWorldName(worldName); - alert(message, playerName, "[Map Link](" + MAP_LINK + "/?worldname=" + worldName + "&zoom=7&x=" + x + "&y=" + y + "&z=" + z + ")", target); + alert(message, playerName, "[Map Link](" + MAP_LINK + "/?worldname=" + worldName + "&zoom=7&x=" + x + "&y=" + y + "&z=" + z + ")", target, x, y, z, worldName); } } // Stealing alerts @EventHandler (ignoreCancelled = true) public void onInventoryClick(InventoryClickEvent event) { - if (ALLOW_STEALING) { - return; - } - boolean stealing; - - // Event parser for inv + if (ALLOW_STEALING) { + return; + } + boolean stealing; + + // Event parser for inv if (!(event.getWhoClicked() instanceof Player)) return; Player player = (Player) event.getWhoClicked(); Inventory inventory = event.getInventory(); @@ -143,22 +212,13 @@ public class GriefAlert extends JavaPlugin implements Listener { ItemStack item = event.getCurrentItem(); if (item == null || inventory.getLocation() == null || item.getType() == Material.AIR) { - return; + return; } // Exclusion list - if (!VALID_CONTAINERS.contains(inventory.getType())) { - return; - } - - // Check if alerts are disabled for this location - int x = inventory.getLocation().getBlockX(); - int y = inventory.getLocation().getBlockY(); - int z = inventory.getLocation().getBlockZ(); - String worldName = inventory.getLocation().getWorld().getName(); - if (alertsLogic != null && alertsLogic.isLocationSaved(x, y, z, worldName)) { - return; - } + if (!VALID_CONTAINERS.contains(inventory.getType())) { + return; + } // Inv actions (needs fixing) InventoryAction action = event.getAction(); @@ -168,109 +228,224 @@ public class GriefAlert extends JavaPlugin implements Listener { stealing = true; } else if (action == InventoryAction.PLACE_ALL || action == InventoryAction.PLACE_SOME || action == InventoryAction.PLACE_ONE || (action == InventoryAction.MOVE_TO_OTHER_INVENTORY && clickedInventory != inventory)) { - stealing = false; + stealing = false; } else { - return; + return; } // Event parser for container + check if grief String target = inspectBlock(inventory.getLocation().getBlock(), player); if (target != null) { - String playerName = player.getName(); + String playerName = player.getName(); String itemName = item.getType().toString(); int amount = item.getAmount(); - // x, y, z, worldName already defined above - + int x = inventory.getLocation().getBlockX(); + int y = inventory.getLocation().getBlockY(); + int z = inventory.getLocation().getBlockZ(); + String worldName = inventory.getLocation().getWorld().getName(); if (stealing) { - // Stealing String message = ChatColor.GRAY + playerName + " took " + amount + " " + itemName + " from " + target + "'s container at " + x + " " + y + " " + z + getHumanWorldName(worldName); - alert(message, playerName, "[Map Link](" + MAP_LINK + "/?worldname=" + worldName + "&zoom=7&x=" + x + "&y=" + y + "&z=" + z + ")", target); + alert(message, playerName, "[Map Link](" + MAP_LINK + "/?worldname=" + worldName + "&zoom=7&x=" + x + "&y=" + y + "&z=" + z + ")", target, x, y, z, worldName); } else { - // Putting back String message = ChatColor.GRAY + playerName + " put " + amount + " " + itemName + " into " + target + "'s container at " + x + " " + y + " " + z + getHumanWorldName(worldName); - alert(message, playerName, "[Map Link](" + MAP_LINK + "/?worldname=" + worldName + "&zoom=7&x=" + x + "&y=" + y + "&z=" + z + ")", target); + alert(message, playerName, "[Map Link](" + MAP_LINK + "/?worldname=" + worldName + "&zoom=7&x=" + x + "&y=" + y + "&z=" + z + ")", target, x, y, z, worldName); } } } + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!command.getName().equalsIgnoreCase("griefalert")) return false; + if (args.length == 0) return false; + String sub = args[0].toLowerCase(); + if (sub.equals("ignore")) { + getLogger().info("[DEBUG] /griefalert ignore called by " + sender.getName() + " with args: " + String.join(" ", args)); + if (!sender.hasPermission("griefalert.staff.ignore")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + getLogger().info("[DEBUG] Permission denied for ignore"); + return true; + } + if (args.length != 5) { + sender.sendMessage(ChatColor.RED + "Usage: /griefalert ignore "); + getLogger().info("[DEBUG] Incorrect number of arguments for ignore: " + args.length); + return true; + } + try { + int x = Integer.parseInt(args[1]); + int y = Integer.parseInt(args[2]); + int z = Integer.parseInt(args[3]); + String world = args[4]; + boolean result = addIgnoredLocation(x, y, z, world); + if (result) { + sender.sendMessage(ChatColor.GREEN + "Location ignored."); + getLogger().info("[DEBUG] Location ignored: " + x + "," + y + "," + z + "," + world); + } else { + sender.sendMessage(ChatColor.YELLOW + "Location was already ignored."); + getLogger().info("[DEBUG] Location already ignored: " + x + "," + y + "," + z + "," + world); + } + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + getLogger().info("[DEBUG] Invalid coordinates for ignore"); + } + return true; + } else if (sub.equals("unignore")) { + getLogger().info("[DEBUG] /griefalert unignore called by " + sender.getName() + " with args: " + String.join(" ", args)); + if (!sender.hasPermission("griefalert.staff.UnIgnore")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + getLogger().info("[DEBUG] Permission denied for unignore"); + return true; + } + if (args.length != 5) { + sender.sendMessage(ChatColor.RED + "Usage: /griefalert unignore "); + getLogger().info("[DEBUG] Incorrect number of arguments for unignore: " + args.length); + return true; + } + try { + int x = Integer.parseInt(args[1]); + int y = Integer.parseInt(args[2]); + int z = Integer.parseInt(args[3]); + String world = args[4]; + boolean result = removeIgnoredLocation(x, y, z, world); + if (result) { + sender.sendMessage(ChatColor.GREEN + "Location unignored."); + getLogger().info("[DEBUG] Location unignored: " + x + "," + y + "," + z + "," + world); + } else { + sender.sendMessage(ChatColor.YELLOW + "Location was not ignored."); + getLogger().info("[DEBUG] Location was not ignored: " + x + "," + y + "," + z + "," + world); + } + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + getLogger().info("[DEBUG] Invalid coordinates for unignore"); + } + return true; + } else if (sub.equals("list")) { + getLogger().info("[DEBUG] /griefalert list called by " + sender.getName()); + if (!sender.hasPermission("griefalert.staff.list")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + getLogger().info("[DEBUG] Permission denied for list"); + return true; + } + listIgnoredLocations(sender); + sender.sendMessage(ChatColor.GRAY + "[DEBUG] Ignored locations listed in chat and console."); + getLogger().info("[DEBUG] Ignored locations listed for " + sender.getName()); + return true; + } else if (sub.equals("clear")) { + getLogger().info("[DEBUG] /griefalert clear called by " + sender.getName()); + if (!sender.hasPermission("griefalert.staff.clear")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + getLogger().info("[DEBUG] Permission denied for clear"); + return true; + } + clearIgnoredLocations(); + sender.sendMessage(ChatColor.GREEN + "All ignored locations cleared."); + getLogger().info("[DEBUG] All ignored locations cleared by " + sender.getName()); + return true; + } + getLogger().info("[DEBUG] Unknown subcommand: " + sub); + sender.sendMessage(ChatColor.RED + "Unknown subcommand. Use: ignore, unignore, list, clear"); + return false; + } + + @Override + public java.util.List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + if (!command.getName().equalsIgnoreCase("griefalert")) return null; + java.util.List completions = new java.util.ArrayList<>(); + if (args.length == 1) { + java.util.List subs = java.util.Arrays.asList("ignore", "unignore", "list", "clear"); + for (String s : subs) { + if (s.startsWith(args[0].toLowerCase())) completions.add(s); + } + return completions; + } + if ((args[0].equalsIgnoreCase("ignore") || args[0].equalsIgnoreCase("unignore")) && args.length == 5) { + // Suggest world names for the 5th argument + for (World world : getServer().getWorlds()) { + if (world.getName().toLowerCase().startsWith(args[4].toLowerCase())) { + completions.add(world.getName()); + } + } + return completions; + } + return null; + } + // Sends the alert (or cancels it) - private void alert(String message, String playerName, String mapLink, String target) { - // Exclude trusted people - Player griefer = Bukkit.getPlayer(playerName); - if (griefer.hasPermission("griefalert.exclude") || griefer.hasPermission("griefalert.exclude." + target)) { - return; - } - - // Spam limiter - String realAlertMessage = message; - String[] alert1 = null; - if (lastAlert != null) { - alert1 = lastAlert.split(" "); - } - String[] alert2 = message.split(" "); - - if (alert1 != null) { - if (alert1[2].equals(alert2[2]) && alert1[5].equals(alert2[5]) && alert1[1].equals("broke") && alert2[1].equals("broke")) { - identicalAlerts += 1; - } - else if (Arrays.equals(alert1, alert2)) { - identicalAlerts += 1; - } - else { - identicalAlerts = 1; - } - } - - if (identicalAlerts == 4) { - message = ChatColor.GRAY + "Same behavior continues."; - mapLink = null; - } - - if (identicalAlerts > 4) { - return; - } - - // Send an event for external hooks - GriefAlertEvent griefalert_event; - if (mapLink != null && !mapLink.isEmpty()) { - griefalert_event = new GriefAlertEvent(message + " (" + mapLink + ")"); - } - else { - griefalert_event = new GriefAlertEvent(message); - } - getServer().getPluginManager().callEvent(griefalert_event); - - // Notify staff ingame - for (Player player : Bukkit.getOnlinePlayers()) { - if (player.hasPermission("griefalert.notify")) { - player.sendMessage(message); + private void alert(String message, String playerName, String mapLink, String target, int x, int y, int z, String world) { + // Exclude trusted people + Player griefer = Bukkit.getPlayer(playerName); + if (griefer.hasPermission("griefalert.exclude") || griefer.hasPermission("griefalert.exclude." + target)) { + return; + } + // Spam limiter + String realAlertMessage = message; + String[] alert1 = null; + if (lastAlert != null) { + alert1 = lastAlert.split(" "); + } + String[] alert2 = message.split(" "); + if (alert1 != null) { + if (alert1[2].equals(alert2[2]) && alert1[5].equals(alert2[5]) && alert1[1].equals("broke") && alert2[1].equals("broke")) { + identicalAlerts += 1; + } else if (Arrays.equals(alert1, alert2)) { + identicalAlerts += 1; + } else { + identicalAlerts = 1; + } + } + if (identicalAlerts == 4) { + message = ChatColor.GRAY + "Same behavior continues."; + mapLink = null; + } + if (identicalAlerts > 4) { + return; + } + // Use direct location check + if (world != null && isLocationIgnored(x, y, z, world)) { + return; // Do not alert if location is ignored + } + // Send an event for external hooks + GriefAlertEvent griefalert_event; + if (mapLink != null && !mapLink.isEmpty()) { + griefalert_event = new GriefAlertEvent(message + " (" + mapLink + ")"); + } else { + griefalert_event = new GriefAlertEvent(message); + } + getServer().getPluginManager().callEvent(griefalert_event); + // Notify staff ingame + for (Player player : Bukkit.getOnlinePlayers()) { + if (player.hasPermission("griefalert.notify")) { + player.sendMessage(message); } } - lastAlert = realAlertMessage; } - // Block inspector: if the block was placed by another player, returns their name. + // Block inspector: only the most recent placement counts for ownership. private static String inspectBlock(Block block, Player player) { List lookup = coreProtectAPI.blockLookup(block, 50000000); - if (lookup == null || lookup.size() <= 0) { - // Natural block - return null; - } - - String[] result = lookup.get(0); - ParseResult parseResult = coreProtectAPI.parseResult(result); - if (parseResult.isRolledBack() && lookup.size() != 1) { - result = lookup.get(1); - parseResult = coreProtectAPI.parseResult(result); - } - - if (result == null || parseResult == null || parseResult.getPlayer().startsWith("#") || parseResult.getPlayer().equals(player.getName())) { - // Placed by breaker or natural event + if (lookup == null || lookup.size() == 0) { + // Natural block return null; } - return parseResult.getPlayer(); - + // Find the most recent placement event only + for (String[] result : lookup) { + ParseResult parseResult = coreProtectAPI.parseResult(result); + if (parseResult == null) continue; + if (parseResult.getActionId() == 1 && !parseResult.isRolledBack() && !parseResult.getPlayer().startsWith("#")) { + // If the current player placed it, it's theirs (no alert) + if (parseResult.getPlayer().equals(player.getName())) { + return null; + } else { + return parseResult.getPlayer(); + } + } + // If we see a break before a placement, stop (block is gone) + if (parseResult.getActionId() == 0) { + break; + } + } + // No valid placement found + return null; } private static String getHumanWorldName(String worldName) {