From 2d04fe1b776f729b2f45103e8afd78ac610cbca3 Mon Sep 17 00:00:00 2001 From: Ardakaz Date: Thu, 12 Jun 2025 07:22:48 +0000 Subject: [PATCH 01/12] Reimplemented the Discord integration to work with 1.21.5. --- GriefAlert.java | 491 +++++++++++++++++++++++++++++++++++++++++++ GriefAlertEvent.java | 34 +++ plugin.yml | 3 +- 3 files changed, 527 insertions(+), 1 deletion(-) create mode 100644 GriefAlert.java create mode 100644 GriefAlertEvent.java diff --git a/GriefAlert.java b/GriefAlert.java new file mode 100644 index 0000000..47c171a --- /dev/null +++ b/GriefAlert.java @@ -0,0 +1,491 @@ +package net.ardakaz.griefalert; + +import net.coreprotect.CoreProtect; +import net.coreprotect.CoreProtectAPI; +import net.coreprotect.CoreProtectAPI.ParseResult; + +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; +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.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; +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, TabCompleter { + + private static CoreProtectAPI coreProtectAPI; + private Integer identicalAlerts = 1; + private String lastAlert; + + private Set EXCLUDED_BLOCKS; + private Set VALID_CONTAINERS; + private String MAP_LINK; + private Boolean ALLOW_STEALING; + + private Connection connection; + private final String DB_FILE = "ignored_locations.db"; + + // Init GriefAlert + @Override + public void onEnable() { + + coreProtectAPI = getCoreProtect(); + if (coreProtectAPI == null) { + getLogger().severe("CoreProtect not found! Disabling plugin."); + getServer().getPluginManager().disablePlugin(this); + return; + } + + getServer().getPluginManager().registerEvents(this, this); + + // Config + saveDefaultConfig(); + List excludedBlocks = getConfig().getStringList("excluded-blocks"); + EXCLUDED_BLOCKS = excludedBlocks.stream().map(Material::valueOf).collect(Collectors.toSet()); + List validContainers = getConfig().getStringList("valid-containers"); + VALID_CONTAINERS = validContainers.stream().map(InventoryType::valueOf).collect(Collectors.toSet()); + MAP_LINK = getConfig().getString("map-link"); + ALLOW_STEALING = getConfig().getBoolean("allow-stealing"); + + setupDatabase(); + + getCommand("griefalert").setTabCompleter(this); + + getLogger().info("GriefAlert has been enabled."); + } + + @Override + public void onDisable() { + getLogger().info("GriefAlert has been disabled."); + if (connection != null) { + try { connection.close(); } catch (SQLException ignored) {} + } + } + + 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; + } + } + + @EventHandler (ignoreCancelled = true) + // Block break alerts + public void onBlockBreak(BlockBreakEvent event) { + // Exclusion list + if (EXCLUDED_BLOCKS.contains(event.getBlock().getType())) { + return; + } + + // Event parser + String playerName = event.getPlayer().getName(); + String blockType = event.getBlock().getType().toString(); + int x = event.getBlock().getX(); + int y = event.getBlock().getY(); + int z = event.getBlock().getZ(); + String worldName = event.getBlock().getWorld().getName(); + + // Check if grief + String target = inspectBlock(event.getBlock(), event.getPlayer()); + if (target != null) { + 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, 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 (!(event.getWhoClicked() instanceof Player)) return; + Player player = (Player) event.getWhoClicked(); + Inventory inventory = event.getInventory(); + Inventory clickedInventory = event.getClickedInventory(); + ItemStack item = event.getCurrentItem(); + + if (item == null || inventory.getLocation() == null || item.getType() == Material.AIR) { + return; + } + + // Exclusion list + if (!VALID_CONTAINERS.contains(inventory.getType())) { + return; + } + + // Inv actions (needs fixing) + InventoryAction action = event.getAction(); + if ((action == InventoryAction.PICKUP_ALL || action == InventoryAction.PICKUP_HALF || + action == InventoryAction.PICKUP_ONE || action == InventoryAction.PICKUP_SOME || + action == InventoryAction.MOVE_TO_OTHER_INVENTORY) && clickedInventory == inventory) { + 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; + } else { + return; + } + + // Event parser for container + check if grief + String target = inspectBlock(inventory.getLocation().getBlock(), player); + if (target != null) { + String playerName = player.getName(); + String itemName = item.getType().toString(); + int amount = item.getAmount(); + int x = inventory.getLocation().getBlockX(); + int y = inventory.getLocation().getBlockY(); + int z = inventory.getLocation().getBlockZ(); + String worldName = inventory.getLocation().getWorld().getName(); + if (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, x, y, z, worldName); + } else { + 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, 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")) { + if (!sender.hasPermission("griefalert.staff.ignore")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + return true; + } + String world; + int x, y, z; + if (args.length == 4) { + // /griefalert ignore x y z (use sender's world) + if (!(sender instanceof Player)) { + sender.sendMessage(ChatColor.RED + "Only players can use this command without specifying a world."); + return true; + } + world = ((Player)sender).getWorld().getName(); + try { + x = Integer.parseInt(args[1]); + y = Integer.parseInt(args[2]); + z = Integer.parseInt(args[3]); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + return true; + } + } else if (args.length == 5) { + // /griefalert ignore world x y z + world = args[1]; + try { + x = Integer.parseInt(args[2]); + y = Integer.parseInt(args[3]); + z = Integer.parseInt(args[4]); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + return true; + } + } else { + sender.sendMessage(ChatColor.RED + "Usage: /griefalert ignore or /griefalert ignore "); + return true; + } + boolean result = addIgnoredLocation(x, y, z, world); + if (result) { + sender.sendMessage(ChatColor.GREEN + "Location ignored."); + } else { + sender.sendMessage(ChatColor.YELLOW + "Location was already ignored."); + } + return true; + } else if (sub.equals("unignore")) { + if (!sender.hasPermission("griefalert.staff.UnIgnore")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + return true; + } + String world; + int x, y, z; + if (args.length == 4) { + // /griefalert unignore x y z (use sender's world) + if (!(sender instanceof Player)) { + sender.sendMessage(ChatColor.RED + "Only players can use this command without specifying a world."); + return true; + } + world = ((Player)sender).getWorld().getName(); + try { + x = Integer.parseInt(args[1]); + y = Integer.parseInt(args[2]); + z = Integer.parseInt(args[3]); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be valid."); + return true; + } + } else if (args.length == 5) { + // /griefalert unignore world x y z + world = args[1]; + try { + x = Integer.parseInt(args[2]); + y = Integer.parseInt(args[3]); + z = Integer.parseInt(args[4]); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + return true; + } + } else { + sender.sendMessage(ChatColor.RED + "Usage: /griefalert unignore or /griefalert unignore "); + return true; + } + boolean result = removeIgnoredLocation(x, y, z, world); + if (result) { + sender.sendMessage(ChatColor.GREEN + "Location unignored."); + } else { + sender.sendMessage(ChatColor.YELLOW + "Location was not ignored."); + } + return true; + } else if (sub.equals("check")) { + // /griefalert check or /griefalert check + String world; + int x, y, z; + if (args.length == 4) { + // /griefalert check x y z (use sender's world) + if (!(sender instanceof Player)) { + sender.sendMessage(ChatColor.RED + "Only players can use this command without specifying a world."); + return true; + } + world = ((Player)sender).getWorld().getName(); + try { + x = Integer.parseInt(args[1]); + y = Integer.parseInt(args[2]); + z = Integer.parseInt(args[3]); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + return true; + } + } else if (args.length == 5) { + // /griefalert check world x y z + world = args[1]; + try { + x = Integer.parseInt(args[2]); + y = Integer.parseInt(args[3]); + z = Integer.parseInt(args[4]); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + return true; + } + } else { + sender.sendMessage(ChatColor.RED + "Usage: /griefalert check (optional) "); + return true; + } + boolean ignored = isLocationIgnored(x, y, z, world); + if (ignored) { + sender.sendMessage(ChatColor.YELLOW + "GriefAlert is DISABLED at " + x + ", " + y + ", " + z + " in " + world + "."); + } else { + sender.sendMessage(ChatColor.GREEN + "GriefAlert is ENABLED at " + x + ", " + y + ", " + z + " in " + world + "."); + } + return true; + } + sender.sendMessage(ChatColor.RED + "Unknown subcommand. Use: ignore, unignore, check"); + 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", "check"); + for (String s : subs) { + if (s.startsWith(args[0].toLowerCase())) completions.add(s); + } + return completions; + } + if (args.length == 2 && (args[0].equalsIgnoreCase("ignore") || args[0].equalsIgnoreCase("unignore") || args[0].equalsIgnoreCase("check"))) { + // Suggest world names for the 2nd argument + for (org.bukkit.World world : org.bukkit.Bukkit.getWorlds()) { + completions.add(world.getName()); + } + return completions; + } + // Optionally, you could add number tab completion for coordinates, but it's not necessary + return null; + } + + // Sends the alert (or cancels it) + 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 (MAP_LINK != null && !MAP_LINK.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: 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; + } + // 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) { + String world = ""; + + if (worldName.endsWith("_nether")) { + world = " in the Nether"; + } + else if (worldName.endsWith("_the_end")) { + world = " in the End"; + } + + return world; + } + + private CoreProtectAPI getCoreProtect() { + Plugin plugin = getServer().getPluginManager().getPlugin("CoreProtect"); + + if (plugin == null || !(plugin instanceof CoreProtect)) { + return null; + } + + return ((CoreProtect) plugin).getAPI(); + } +} \ No newline at end of file diff --git a/GriefAlertEvent.java b/GriefAlertEvent.java new file mode 100644 index 0000000..a0755e9 --- /dev/null +++ b/GriefAlertEvent.java @@ -0,0 +1,34 @@ +// "API" for other plugins + +package net.ardakaz.griefalert; + +import org.bukkit.Bukkit; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class GriefAlertEvent extends Event { + private static final HandlerList HANDLERS = new HandlerList(); + private String alert = ""; + + public GriefAlertEvent(String alert) { + this.alert = alert; + + // Discord hook for 1.21.5 + if (Bukkit.getServer().getPluginManager().getPlugin("DiscordSRV") != null) { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "discord broadcast #grief-alerts " + this.getAlert()); + } + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } + + @Override + public HandlerList getHandlers() { + return HANDLERS; + } + + public String getAlert() { + return this.alert; + } +} \ No newline at end of file diff --git a/plugin.yml b/plugin.yml index 9c1af0b..15c24c2 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,8 +1,9 @@ name: GriefAlert main: net.ardakaz.griefalert.GriefAlert -version: 0.3 +version: 0.3.2 api-version: 1.21 depends: [CoreProtect] +softdepend: [DiscordSRV] description: A simple grief alert plugin using the CoreProtect API. authors: [Ardakaz, kleedje30] commands: -- 2.39.5 From f71e6e2fa1fd12f265f533e040c5f24b9f10ef66 Mon Sep 17 00:00:00 2001 From: Ardakaz Date: Thu, 12 Jun 2025 07:23:19 +0000 Subject: [PATCH 02/12] Oops --- GriefAlertEvent.java | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 GriefAlertEvent.java diff --git a/GriefAlertEvent.java b/GriefAlertEvent.java deleted file mode 100644 index a0755e9..0000000 --- a/GriefAlertEvent.java +++ /dev/null @@ -1,34 +0,0 @@ -// "API" for other plugins - -package net.ardakaz.griefalert; - -import org.bukkit.Bukkit; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; - -public class GriefAlertEvent extends Event { - private static final HandlerList HANDLERS = new HandlerList(); - private String alert = ""; - - public GriefAlertEvent(String alert) { - this.alert = alert; - - // Discord hook for 1.21.5 - if (Bukkit.getServer().getPluginManager().getPlugin("DiscordSRV") != null) { - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "discord broadcast #grief-alerts " + this.getAlert()); - } - } - - public static HandlerList getHandlerList() { - return HANDLERS; - } - - @Override - public HandlerList getHandlers() { - return HANDLERS; - } - - public String getAlert() { - return this.alert; - } -} \ No newline at end of file -- 2.39.5 From 9a124598eec3d754ae026bfa6c6e1c2aeab5de18 Mon Sep 17 00:00:00 2001 From: Ardakaz Date: Thu, 12 Jun 2025 07:23:27 +0000 Subject: [PATCH 03/12] Oops --- GriefAlert.java | 491 ------------------------------------------------ 1 file changed, 491 deletions(-) delete mode 100644 GriefAlert.java diff --git a/GriefAlert.java b/GriefAlert.java deleted file mode 100644 index 47c171a..0000000 --- a/GriefAlert.java +++ /dev/null @@ -1,491 +0,0 @@ -package net.ardakaz.griefalert; - -import net.coreprotect.CoreProtect; -import net.coreprotect.CoreProtectAPI; -import net.coreprotect.CoreProtectAPI.ParseResult; - -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; -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.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.java.JavaPlugin; -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, TabCompleter { - - private static CoreProtectAPI coreProtectAPI; - private Integer identicalAlerts = 1; - private String lastAlert; - - private Set EXCLUDED_BLOCKS; - private Set VALID_CONTAINERS; - private String MAP_LINK; - private Boolean ALLOW_STEALING; - - private Connection connection; - private final String DB_FILE = "ignored_locations.db"; - - // Init GriefAlert - @Override - public void onEnable() { - - coreProtectAPI = getCoreProtect(); - if (coreProtectAPI == null) { - getLogger().severe("CoreProtect not found! Disabling plugin."); - getServer().getPluginManager().disablePlugin(this); - return; - } - - getServer().getPluginManager().registerEvents(this, this); - - // Config - saveDefaultConfig(); - List excludedBlocks = getConfig().getStringList("excluded-blocks"); - EXCLUDED_BLOCKS = excludedBlocks.stream().map(Material::valueOf).collect(Collectors.toSet()); - List validContainers = getConfig().getStringList("valid-containers"); - VALID_CONTAINERS = validContainers.stream().map(InventoryType::valueOf).collect(Collectors.toSet()); - MAP_LINK = getConfig().getString("map-link"); - ALLOW_STEALING = getConfig().getBoolean("allow-stealing"); - - setupDatabase(); - - getCommand("griefalert").setTabCompleter(this); - - getLogger().info("GriefAlert has been enabled."); - } - - @Override - public void onDisable() { - getLogger().info("GriefAlert has been disabled."); - if (connection != null) { - try { connection.close(); } catch (SQLException ignored) {} - } - } - - 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; - } - } - - @EventHandler (ignoreCancelled = true) - // Block break alerts - public void onBlockBreak(BlockBreakEvent event) { - // Exclusion list - if (EXCLUDED_BLOCKS.contains(event.getBlock().getType())) { - return; - } - - // Event parser - String playerName = event.getPlayer().getName(); - String blockType = event.getBlock().getType().toString(); - int x = event.getBlock().getX(); - int y = event.getBlock().getY(); - int z = event.getBlock().getZ(); - String worldName = event.getBlock().getWorld().getName(); - - // Check if grief - String target = inspectBlock(event.getBlock(), event.getPlayer()); - if (target != null) { - 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, 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 (!(event.getWhoClicked() instanceof Player)) return; - Player player = (Player) event.getWhoClicked(); - Inventory inventory = event.getInventory(); - Inventory clickedInventory = event.getClickedInventory(); - ItemStack item = event.getCurrentItem(); - - if (item == null || inventory.getLocation() == null || item.getType() == Material.AIR) { - return; - } - - // Exclusion list - if (!VALID_CONTAINERS.contains(inventory.getType())) { - return; - } - - // Inv actions (needs fixing) - InventoryAction action = event.getAction(); - if ((action == InventoryAction.PICKUP_ALL || action == InventoryAction.PICKUP_HALF || - action == InventoryAction.PICKUP_ONE || action == InventoryAction.PICKUP_SOME || - action == InventoryAction.MOVE_TO_OTHER_INVENTORY) && clickedInventory == inventory) { - 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; - } else { - return; - } - - // Event parser for container + check if grief - String target = inspectBlock(inventory.getLocation().getBlock(), player); - if (target != null) { - String playerName = player.getName(); - String itemName = item.getType().toString(); - int amount = item.getAmount(); - int x = inventory.getLocation().getBlockX(); - int y = inventory.getLocation().getBlockY(); - int z = inventory.getLocation().getBlockZ(); - String worldName = inventory.getLocation().getWorld().getName(); - if (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, x, y, z, worldName); - } else { - 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, 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")) { - if (!sender.hasPermission("griefalert.staff.ignore")) { - sender.sendMessage(ChatColor.RED + "You do not have permission."); - return true; - } - String world; - int x, y, z; - if (args.length == 4) { - // /griefalert ignore x y z (use sender's world) - if (!(sender instanceof Player)) { - sender.sendMessage(ChatColor.RED + "Only players can use this command without specifying a world."); - return true; - } - world = ((Player)sender).getWorld().getName(); - try { - x = Integer.parseInt(args[1]); - y = Integer.parseInt(args[2]); - z = Integer.parseInt(args[3]); - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); - return true; - } - } else if (args.length == 5) { - // /griefalert ignore world x y z - world = args[1]; - try { - x = Integer.parseInt(args[2]); - y = Integer.parseInt(args[3]); - z = Integer.parseInt(args[4]); - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); - return true; - } - } else { - sender.sendMessage(ChatColor.RED + "Usage: /griefalert ignore or /griefalert ignore "); - return true; - } - boolean result = addIgnoredLocation(x, y, z, world); - if (result) { - sender.sendMessage(ChatColor.GREEN + "Location ignored."); - } else { - sender.sendMessage(ChatColor.YELLOW + "Location was already ignored."); - } - return true; - } else if (sub.equals("unignore")) { - if (!sender.hasPermission("griefalert.staff.UnIgnore")) { - sender.sendMessage(ChatColor.RED + "You do not have permission."); - return true; - } - String world; - int x, y, z; - if (args.length == 4) { - // /griefalert unignore x y z (use sender's world) - if (!(sender instanceof Player)) { - sender.sendMessage(ChatColor.RED + "Only players can use this command without specifying a world."); - return true; - } - world = ((Player)sender).getWorld().getName(); - try { - x = Integer.parseInt(args[1]); - y = Integer.parseInt(args[2]); - z = Integer.parseInt(args[3]); - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be valid."); - return true; - } - } else if (args.length == 5) { - // /griefalert unignore world x y z - world = args[1]; - try { - x = Integer.parseInt(args[2]); - y = Integer.parseInt(args[3]); - z = Integer.parseInt(args[4]); - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); - return true; - } - } else { - sender.sendMessage(ChatColor.RED + "Usage: /griefalert unignore or /griefalert unignore "); - return true; - } - boolean result = removeIgnoredLocation(x, y, z, world); - if (result) { - sender.sendMessage(ChatColor.GREEN + "Location unignored."); - } else { - sender.sendMessage(ChatColor.YELLOW + "Location was not ignored."); - } - return true; - } else if (sub.equals("check")) { - // /griefalert check or /griefalert check - String world; - int x, y, z; - if (args.length == 4) { - // /griefalert check x y z (use sender's world) - if (!(sender instanceof Player)) { - sender.sendMessage(ChatColor.RED + "Only players can use this command without specifying a world."); - return true; - } - world = ((Player)sender).getWorld().getName(); - try { - x = Integer.parseInt(args[1]); - y = Integer.parseInt(args[2]); - z = Integer.parseInt(args[3]); - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); - return true; - } - } else if (args.length == 5) { - // /griefalert check world x y z - world = args[1]; - try { - x = Integer.parseInt(args[2]); - y = Integer.parseInt(args[3]); - z = Integer.parseInt(args[4]); - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); - return true; - } - } else { - sender.sendMessage(ChatColor.RED + "Usage: /griefalert check (optional) "); - return true; - } - boolean ignored = isLocationIgnored(x, y, z, world); - if (ignored) { - sender.sendMessage(ChatColor.YELLOW + "GriefAlert is DISABLED at " + x + ", " + y + ", " + z + " in " + world + "."); - } else { - sender.sendMessage(ChatColor.GREEN + "GriefAlert is ENABLED at " + x + ", " + y + ", " + z + " in " + world + "."); - } - return true; - } - sender.sendMessage(ChatColor.RED + "Unknown subcommand. Use: ignore, unignore, check"); - 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", "check"); - for (String s : subs) { - if (s.startsWith(args[0].toLowerCase())) completions.add(s); - } - return completions; - } - if (args.length == 2 && (args[0].equalsIgnoreCase("ignore") || args[0].equalsIgnoreCase("unignore") || args[0].equalsIgnoreCase("check"))) { - // Suggest world names for the 2nd argument - for (org.bukkit.World world : org.bukkit.Bukkit.getWorlds()) { - completions.add(world.getName()); - } - return completions; - } - // Optionally, you could add number tab completion for coordinates, but it's not necessary - return null; - } - - // Sends the alert (or cancels it) - 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 (MAP_LINK != null && !MAP_LINK.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: 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; - } - // 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) { - String world = ""; - - if (worldName.endsWith("_nether")) { - world = " in the Nether"; - } - else if (worldName.endsWith("_the_end")) { - world = " in the End"; - } - - return world; - } - - private CoreProtectAPI getCoreProtect() { - Plugin plugin = getServer().getPluginManager().getPlugin("CoreProtect"); - - if (plugin == null || !(plugin instanceof CoreProtect)) { - return null; - } - - return ((CoreProtect) plugin).getAPI(); - } -} \ No newline at end of file -- 2.39.5 From ed05bf48ef2363962277f2cab5d05e00db020b1b Mon Sep 17 00:00:00 2001 From: Ardakaz Date: Thu, 12 Jun 2025 07:23:47 +0000 Subject: [PATCH 04/12] Reimplemented the Discord integration to work with 1.21.5. --- src/net/ardakaz/griefalert/GriefAlert.java | 2 +- src/net/ardakaz/griefalert/GriefAlertEvent.java | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) mode change 100755 => 100644 src/net/ardakaz/griefalert/GriefAlertEvent.java diff --git a/src/net/ardakaz/griefalert/GriefAlert.java b/src/net/ardakaz/griefalert/GriefAlert.java index 4751dc7..47c171a 100644 --- a/src/net/ardakaz/griefalert/GriefAlert.java +++ b/src/net/ardakaz/griefalert/GriefAlert.java @@ -423,7 +423,7 @@ public class GriefAlert extends JavaPlugin implements Listener, TabCompleter { } // Send an event for external hooks GriefAlertEvent griefalert_event; - if (mapLink != null && !mapLink.isEmpty()) { + if (MAP_LINK != null && !MAP_LINK.isEmpty()) { griefalert_event = new GriefAlertEvent(message + " (" + mapLink + ")"); } else { griefalert_event = new GriefAlertEvent(message); diff --git a/src/net/ardakaz/griefalert/GriefAlertEvent.java b/src/net/ardakaz/griefalert/GriefAlertEvent.java old mode 100755 new mode 100644 index 511595c..a0755e9 --- a/src/net/ardakaz/griefalert/GriefAlertEvent.java +++ b/src/net/ardakaz/griefalert/GriefAlertEvent.java @@ -2,6 +2,7 @@ package net.ardakaz.griefalert; +import org.bukkit.Bukkit; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; @@ -11,6 +12,11 @@ public class GriefAlertEvent extends Event { public GriefAlertEvent(String alert) { this.alert = alert; + + // Discord hook for 1.21.5 + if (Bukkit.getServer().getPluginManager().getPlugin("DiscordSRV") != null) { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "discord broadcast #grief-alerts " + this.getAlert()); + } } public static HandlerList getHandlerList() { -- 2.39.5 From 4d9ba74c42073484d4714b9ba429778de85844b0 Mon Sep 17 00:00:00 2001 From: Kleedje30 Date: Fri, 20 Jun 2025 19:53:39 +0000 Subject: [PATCH 05/12] Upload files to "src/net/ardakaz/griefalert" should fix armor stands and item frames --- src/net/ardakaz/griefalert/GriefAlert.java | 1108 +++++++++-------- .../ardakaz/griefalert/GriefAlertEvent.java | 66 +- 2 files changed, 651 insertions(+), 523 deletions(-) diff --git a/src/net/ardakaz/griefalert/GriefAlert.java b/src/net/ardakaz/griefalert/GriefAlert.java index 47c171a..f4ae8d2 100644 --- a/src/net/ardakaz/griefalert/GriefAlert.java +++ b/src/net/ardakaz/griefalert/GriefAlert.java @@ -1,491 +1,619 @@ -package net.ardakaz.griefalert; - -import net.coreprotect.CoreProtect; -import net.coreprotect.CoreProtectAPI; -import net.coreprotect.CoreProtectAPI.ParseResult; - -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; -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.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.java.JavaPlugin; -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, TabCompleter { - - private static CoreProtectAPI coreProtectAPI; - private Integer identicalAlerts = 1; - private String lastAlert; - - private Set EXCLUDED_BLOCKS; - private Set VALID_CONTAINERS; - private String MAP_LINK; - private Boolean ALLOW_STEALING; - - private Connection connection; - private final String DB_FILE = "ignored_locations.db"; - - // Init GriefAlert - @Override - public void onEnable() { - - coreProtectAPI = getCoreProtect(); - if (coreProtectAPI == null) { - getLogger().severe("CoreProtect not found! Disabling plugin."); - getServer().getPluginManager().disablePlugin(this); - return; - } - - getServer().getPluginManager().registerEvents(this, this); - - // Config - saveDefaultConfig(); - List excludedBlocks = getConfig().getStringList("excluded-blocks"); - EXCLUDED_BLOCKS = excludedBlocks.stream().map(Material::valueOf).collect(Collectors.toSet()); - List validContainers = getConfig().getStringList("valid-containers"); - VALID_CONTAINERS = validContainers.stream().map(InventoryType::valueOf).collect(Collectors.toSet()); - MAP_LINK = getConfig().getString("map-link"); - ALLOW_STEALING = getConfig().getBoolean("allow-stealing"); - - setupDatabase(); - - getCommand("griefalert").setTabCompleter(this); - - getLogger().info("GriefAlert has been enabled."); - } - - @Override - public void onDisable() { - getLogger().info("GriefAlert has been disabled."); - if (connection != null) { - try { connection.close(); } catch (SQLException ignored) {} - } - } - - 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; - } - } - - @EventHandler (ignoreCancelled = true) - // Block break alerts - public void onBlockBreak(BlockBreakEvent event) { - // Exclusion list - if (EXCLUDED_BLOCKS.contains(event.getBlock().getType())) { - return; - } - - // Event parser - String playerName = event.getPlayer().getName(); - String blockType = event.getBlock().getType().toString(); - int x = event.getBlock().getX(); - int y = event.getBlock().getY(); - int z = event.getBlock().getZ(); - String worldName = event.getBlock().getWorld().getName(); - - // Check if grief - String target = inspectBlock(event.getBlock(), event.getPlayer()); - if (target != null) { - 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, 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 (!(event.getWhoClicked() instanceof Player)) return; - Player player = (Player) event.getWhoClicked(); - Inventory inventory = event.getInventory(); - Inventory clickedInventory = event.getClickedInventory(); - ItemStack item = event.getCurrentItem(); - - if (item == null || inventory.getLocation() == null || item.getType() == Material.AIR) { - return; - } - - // Exclusion list - if (!VALID_CONTAINERS.contains(inventory.getType())) { - return; - } - - // Inv actions (needs fixing) - InventoryAction action = event.getAction(); - if ((action == InventoryAction.PICKUP_ALL || action == InventoryAction.PICKUP_HALF || - action == InventoryAction.PICKUP_ONE || action == InventoryAction.PICKUP_SOME || - action == InventoryAction.MOVE_TO_OTHER_INVENTORY) && clickedInventory == inventory) { - 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; - } else { - return; - } - - // Event parser for container + check if grief - String target = inspectBlock(inventory.getLocation().getBlock(), player); - if (target != null) { - String playerName = player.getName(); - String itemName = item.getType().toString(); - int amount = item.getAmount(); - int x = inventory.getLocation().getBlockX(); - int y = inventory.getLocation().getBlockY(); - int z = inventory.getLocation().getBlockZ(); - String worldName = inventory.getLocation().getWorld().getName(); - if (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, x, y, z, worldName); - } else { - 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, 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")) { - if (!sender.hasPermission("griefalert.staff.ignore")) { - sender.sendMessage(ChatColor.RED + "You do not have permission."); - return true; - } - String world; - int x, y, z; - if (args.length == 4) { - // /griefalert ignore x y z (use sender's world) - if (!(sender instanceof Player)) { - sender.sendMessage(ChatColor.RED + "Only players can use this command without specifying a world."); - return true; - } - world = ((Player)sender).getWorld().getName(); - try { - x = Integer.parseInt(args[1]); - y = Integer.parseInt(args[2]); - z = Integer.parseInt(args[3]); - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); - return true; - } - } else if (args.length == 5) { - // /griefalert ignore world x y z - world = args[1]; - try { - x = Integer.parseInt(args[2]); - y = Integer.parseInt(args[3]); - z = Integer.parseInt(args[4]); - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); - return true; - } - } else { - sender.sendMessage(ChatColor.RED + "Usage: /griefalert ignore or /griefalert ignore "); - return true; - } - boolean result = addIgnoredLocation(x, y, z, world); - if (result) { - sender.sendMessage(ChatColor.GREEN + "Location ignored."); - } else { - sender.sendMessage(ChatColor.YELLOW + "Location was already ignored."); - } - return true; - } else if (sub.equals("unignore")) { - if (!sender.hasPermission("griefalert.staff.UnIgnore")) { - sender.sendMessage(ChatColor.RED + "You do not have permission."); - return true; - } - String world; - int x, y, z; - if (args.length == 4) { - // /griefalert unignore x y z (use sender's world) - if (!(sender instanceof Player)) { - sender.sendMessage(ChatColor.RED + "Only players can use this command without specifying a world."); - return true; - } - world = ((Player)sender).getWorld().getName(); - try { - x = Integer.parseInt(args[1]); - y = Integer.parseInt(args[2]); - z = Integer.parseInt(args[3]); - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be valid."); - return true; - } - } else if (args.length == 5) { - // /griefalert unignore world x y z - world = args[1]; - try { - x = Integer.parseInt(args[2]); - y = Integer.parseInt(args[3]); - z = Integer.parseInt(args[4]); - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); - return true; - } - } else { - sender.sendMessage(ChatColor.RED + "Usage: /griefalert unignore or /griefalert unignore "); - return true; - } - boolean result = removeIgnoredLocation(x, y, z, world); - if (result) { - sender.sendMessage(ChatColor.GREEN + "Location unignored."); - } else { - sender.sendMessage(ChatColor.YELLOW + "Location was not ignored."); - } - return true; - } else if (sub.equals("check")) { - // /griefalert check or /griefalert check - String world; - int x, y, z; - if (args.length == 4) { - // /griefalert check x y z (use sender's world) - if (!(sender instanceof Player)) { - sender.sendMessage(ChatColor.RED + "Only players can use this command without specifying a world."); - return true; - } - world = ((Player)sender).getWorld().getName(); - try { - x = Integer.parseInt(args[1]); - y = Integer.parseInt(args[2]); - z = Integer.parseInt(args[3]); - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); - return true; - } - } else if (args.length == 5) { - // /griefalert check world x y z - world = args[1]; - try { - x = Integer.parseInt(args[2]); - y = Integer.parseInt(args[3]); - z = Integer.parseInt(args[4]); - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); - return true; - } - } else { - sender.sendMessage(ChatColor.RED + "Usage: /griefalert check (optional) "); - return true; - } - boolean ignored = isLocationIgnored(x, y, z, world); - if (ignored) { - sender.sendMessage(ChatColor.YELLOW + "GriefAlert is DISABLED at " + x + ", " + y + ", " + z + " in " + world + "."); - } else { - sender.sendMessage(ChatColor.GREEN + "GriefAlert is ENABLED at " + x + ", " + y + ", " + z + " in " + world + "."); - } - return true; - } - sender.sendMessage(ChatColor.RED + "Unknown subcommand. Use: ignore, unignore, check"); - 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", "check"); - for (String s : subs) { - if (s.startsWith(args[0].toLowerCase())) completions.add(s); - } - return completions; - } - if (args.length == 2 && (args[0].equalsIgnoreCase("ignore") || args[0].equalsIgnoreCase("unignore") || args[0].equalsIgnoreCase("check"))) { - // Suggest world names for the 2nd argument - for (org.bukkit.World world : org.bukkit.Bukkit.getWorlds()) { - completions.add(world.getName()); - } - return completions; - } - // Optionally, you could add number tab completion for coordinates, but it's not necessary - return null; - } - - // Sends the alert (or cancels it) - 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 (MAP_LINK != null && !MAP_LINK.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: 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; - } - // 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) { - String world = ""; - - if (worldName.endsWith("_nether")) { - world = " in the Nether"; - } - else if (worldName.endsWith("_the_end")) { - world = " in the End"; - } - - return world; - } - - private CoreProtectAPI getCoreProtect() { - Plugin plugin = getServer().getPluginManager().getPlugin("CoreProtect"); - - if (plugin == null || !(plugin instanceof CoreProtect)) { - return null; - } - - return ((CoreProtect) plugin).getAPI(); - } +package net.ardakaz.griefalert; + +import net.coreprotect.CoreProtect; +import net.coreprotect.CoreProtectAPI; +import net.coreprotect.CoreProtectAPI.ParseResult; + +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; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.inventory.InventoryAction; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.entity.ArmorStand; +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; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.entity.ItemFrame; + + + +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 String MAP_LINK; + private Boolean ALLOW_STEALING; + + private Connection connection; + private final String DB_FILE = "ignored_locations.db"; + + // Init GriefAlert + @Override + public void onEnable() { + + coreProtectAPI = getCoreProtect(); + if (coreProtectAPI == null) { + getLogger().severe("CoreProtect not found! Disabling plugin."); + getServer().getPluginManager().disablePlugin(this); + return; + } + + getServer().getPluginManager().registerEvents(this, this); + + // Config + saveDefaultConfig(); + List excludedBlocks = getConfig().getStringList("excluded-blocks"); + EXCLUDED_BLOCKS = excludedBlocks.stream().map(Material::valueOf).collect(Collectors.toSet()); + List validContainers = getConfig().getStringList("valid-containers"); + VALID_CONTAINERS = validContainers.stream().map(InventoryType::valueOf).collect(Collectors.toSet()); + MAP_LINK = getConfig().getString("map-link"); + ALLOW_STEALING = getConfig().getBoolean("allow-stealing"); + + setupDatabase(); + + getCommand("griefalert").setTabCompleter(this); + + getLogger().info("GriefAlert has been enabled."); + } + + @Override + public void onDisable() { + getLogger().info("GriefAlert has been disabled."); + if (connection != null) { + try { connection.close(); } catch (SQLException ignored) {} + } + } + + 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; + } + } + + @EventHandler (ignoreCancelled = true) + // Block break alerts + public void onBlockBreak(BlockBreakEvent event) { + // Exclusion list + if (EXCLUDED_BLOCKS.contains(event.getBlock().getType())) { + return; + } + + // Event parser + String playerName = event.getPlayer().getName(); + String blockType = event.getBlock().getType().toString(); + int x = event.getBlock().getX(); + int y = event.getBlock().getY(); + int z = event.getBlock().getZ(); + String worldName = event.getBlock().getWorld().getName(); + + // Check if grief + String target = inspectBlock(event.getBlock(), event.getPlayer()); + if (target != null) { + 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, 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 (!(event.getWhoClicked() instanceof Player)) return; + Player player = (Player) event.getWhoClicked(); + Inventory inventory = event.getInventory(); + Inventory clickedInventory = event.getClickedInventory(); + ItemStack item = event.getCurrentItem(); + + if (item == null || inventory.getLocation() == null || item.getType() == Material.AIR) { + return; + } + + // Exclusion list + if (!VALID_CONTAINERS.contains(inventory.getType())) { + return; + } + + // Inv actions (needs fixing) + InventoryAction action = event.getAction(); + if ((action == InventoryAction.PICKUP_ALL || action == InventoryAction.PICKUP_HALF || + action == InventoryAction.PICKUP_ONE || action == InventoryAction.PICKUP_SOME || + action == InventoryAction.MOVE_TO_OTHER_INVENTORY) && clickedInventory == inventory) { + 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; + } else { + return; + } + + // Event parser for container + check if grief + String target = inspectBlock(inventory.getLocation().getBlock(), player); + if (target != null) { + String playerName = player.getName(); + String itemName = item.getType().toString(); + int amount = item.getAmount(); + int x = inventory.getLocation().getBlockX(); + int y = inventory.getLocation().getBlockY(); + int z = inventory.getLocation().getBlockZ(); + String worldName = inventory.getLocation().getWorld().getName(); + if (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, x, y, z, worldName); + } else { + 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, x, y, z, worldName); + } + } + } + + // Armor Stand break alerts + @EventHandler(ignoreCancelled = true) + public void onArmorStandBreak(EntityDamageByEntityEvent event) { + if (!(event.getEntity() instanceof ArmorStand)) return; + if (!(event.getDamager() instanceof Player)) return; + Player player = (Player) event.getDamager(); + ArmorStand armorStand = (ArmorStand) event.getEntity(); + int x = armorStand.getLocation().getBlockX(); + int y = armorStand.getLocation().getBlockY(); + int z = armorStand.getLocation().getBlockZ(); + String worldName = armorStand.getWorld().getName(); + if (isLocationIgnored(x, y, z, worldName)) { + event.setCancelled(true); + return; + } + String playerName = player.getName(); + String target = inspectBlock(armorStand.getLocation().getBlock(), player); + if (target != null) { + String message = ChatColor.GRAY + playerName + " broke an ArmorStand 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, x, y, z, worldName); + } + } + + // Armor Stand item interaction alerts + @EventHandler(ignoreCancelled = true) + public void onArmorStandInteract(PlayerInteractAtEntityEvent event) { + if (!(event.getRightClicked() instanceof ArmorStand)) return; + Player player = event.getPlayer(); + ArmorStand armorStand = (ArmorStand) event.getRightClicked(); + int x = armorStand.getLocation().getBlockX(); + int y = armorStand.getLocation().getBlockY(); + int z = armorStand.getLocation().getBlockZ(); + String worldName = armorStand.getWorld().getName(); + if (isLocationIgnored(x, y, z, worldName)) { + event.setCancelled(true); + return; + } + String target = inspectBlock(armorStand.getLocation().getBlock(), player); + if (target != null) { + String playerName = player.getName(); + ItemStack handItem = player.getInventory().getItemInMainHand(); + // Determine which slot is being interacted with + //org.bukkit.inventory.EquipmentSlot slot = event.getHand(); + EntityEquipment equipment = armorStand.getEquipment(); + ItemStack armorItem = null; + if (event.getClickedPosition() != null) { + // Approximate slot by Y position (not perfect, but Bukkit API is limited so it is what it is :3) + double yPos = event.getClickedPosition().getY(); + if (yPos > 1.6) armorItem = equipment.getHelmet(); + else if (yPos > 1.2) armorItem = equipment.getChestplate(); + else if (yPos > 0.8) armorItem = equipment.getLeggings(); + else if (yPos > 0.1) armorItem = equipment.getBoots(); + else armorItem = equipment.getItemInMainHand(); + } else { + armorItem = equipment.getItemInMainHand(); + } + boolean handEmpty = handItem == null || handItem.getType() == Material.AIR; + boolean armorEmpty = armorItem == null || armorItem.getType() == Material.AIR; + String action; + //this works at the instance of interaction before items get moved around + if (handEmpty && !armorEmpty) { + action = "took " + armorItem.getType().toString(); + } else if (!handEmpty && armorEmpty) { + action = "added " + handItem.getType().toString(); + } else if (!handEmpty && !armorEmpty) { + action = "swapped " + armorItem.getType().toString() + " with " + handItem.getType().toString(); + } else { + action = "interacted with"; + } + String message = ChatColor.GRAY + playerName + " " + action + " on ArmorStand owned 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, x, y, z, worldName); + } + } + //event handler for item frames + @EventHandler(ignoreCancelled = true) + public void onPlayerInteractEntity(PlayerInteractEntityEvent event){ + if(!(event.getRightClicked() instanceof ItemFrame)) return; + + Player player = event.getPlayer(); + ItemFrame frame = (ItemFrame) event.getRightClicked(); + int x = frame.getLocation().getBlockX(); + int y = frame.getLocation().getBlockY(); + int z = frame.getLocation().getBlockZ(); + String worldName = frame.getWorld().getName(); + if (isLocationIgnored(x, y, z, worldName)) { + event.setCancelled(true); + return; + } + String target = inspectBlock(frame.getLocation().getBlock(), player); + if(target !=null){ + ItemStack itemInFrame = frame.getItem(); + ItemStack handItem = player.getInventory().getItemInMainHand(); + boolean handEmpty = handItem ==null || handItem.getType() == Material.AIR; + boolean frameEmpty = itemInFrame ==null || itemInFrame.getType() == Material.AIR; + + String action; + if (handEmpty && !frameEmpty) { + action = "took " + itemInFrame.getType(); + } else if (!handEmpty && frameEmpty) { + action = "added " + handItem.getType(); + } else { + return; + } + + String message = ChatColor.GRAY + player.getName() + " " + action + " ItemFrame owned by " + target + + " at " + x + " " + y + " " + z + " (" + worldName + ")"; + alert(message, player.getName(), + "[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")) { + if (!sender.hasPermission("griefalert.staff.ignore")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + return true; + } + String world; + int x, y, z; + if (args.length == 4) { + // /griefalert ignore x y z (use sender's world) + if (!(sender instanceof Player)) { + sender.sendMessage(ChatColor.RED + "Only players can use this command without specifying a world."); + return true; + } + world = ((Player)sender).getWorld().getName(); + try { + x = Integer.parseInt(args[1]); + y = Integer.parseInt(args[2]); + z = Integer.parseInt(args[3]); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + return true; + } + } else if (args.length == 5) { + // /griefalert ignore world x y z + world = args[1]; + try { + x = Integer.parseInt(args[2]); + y = Integer.parseInt(args[3]); + z = Integer.parseInt(args[4]); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + return true; + } + } else { + sender.sendMessage(ChatColor.RED + "Usage: /griefalert ignore or /griefalert ignore "); + return true; + } + boolean result = addIgnoredLocation(x, y, z, world); + if (result) { + sender.sendMessage(ChatColor.GREEN + "Location ignored."); + } else { + sender.sendMessage(ChatColor.YELLOW + "Location was already ignored."); + } + return true; + } else if (sub.equals("unignore")) { + if (!sender.hasPermission("griefalert.staff.UnIgnore")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + return true; + } + String world; + int x, y, z; + if (args.length == 4) { + // /griefalert unignore x y z (use sender's world) + if (!(sender instanceof Player)) { + sender.sendMessage(ChatColor.RED + "Only players can use this command without specifying a world."); + return true; + } + world = ((Player)sender).getWorld().getName(); + try { + x = Integer.parseInt(args[1]); + y = Integer.parseInt(args[2]); + z = Integer.parseInt(args[3]); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be valid."); + return true; + } + } else if (args.length == 5) { + // /griefalert unignore world x y z + world = args[1]; + try { + x = Integer.parseInt(args[2]); + y = Integer.parseInt(args[3]); + z = Integer.parseInt(args[4]); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + return true; + } + } else { + sender.sendMessage(ChatColor.RED + "Usage: /griefalert unignore or /griefalert unignore "); + return true; + } + boolean result = removeIgnoredLocation(x, y, z, world); + if (result) { + sender.sendMessage(ChatColor.GREEN + "Location unignored."); + } else { + sender.sendMessage(ChatColor.YELLOW + "Location was not ignored."); + } + return true; + } else if (sub.equals("check")) { + // /griefalert check or /griefalert check + String world; + int x, y, z; + if (args.length == 4) { + // /griefalert check x y z (use sender's world) + if (!(sender instanceof Player)) { + sender.sendMessage(ChatColor.RED + "Only players can use this command without specifying a world."); + return true; + } + world = ((Player)sender).getWorld().getName(); + try { + x = Integer.parseInt(args[1]); + y = Integer.parseInt(args[2]); + z = Integer.parseInt(args[3]); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + return true; + } + } else if (args.length == 5) { + // /griefalert check world x y z + world = args[1]; + try { + x = Integer.parseInt(args[2]); + y = Integer.parseInt(args[3]); + z = Integer.parseInt(args[4]); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + return true; + } + } else { + sender.sendMessage(ChatColor.RED + "Usage: /griefalert check (optional) "); + return true; + } + boolean ignored = isLocationIgnored(x, y, z, world); + if (ignored) { + sender.sendMessage(ChatColor.YELLOW + "GriefAlert is DISABLED at " + x + ", " + y + ", " + z + " in " + world + "."); + } else { + sender.sendMessage(ChatColor.GREEN + "GriefAlert is ENABLED at " + x + ", " + y + ", " + z + " in " + world + "."); + } + return true; + } + sender.sendMessage(ChatColor.RED + "Unknown subcommand. Use: ignore, unignore, check"); + 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", "check"); + for (String s : subs) { + if (s.startsWith(args[0].toLowerCase())) completions.add(s); + } + return completions; + } + if (args.length == 2 && (args[0].equalsIgnoreCase("ignore") || args[0].equalsIgnoreCase("unignore") || args[0].equalsIgnoreCase("check"))) { + // Suggest world names for the 2nd argument + for (org.bukkit.World world : org.bukkit.Bukkit.getWorlds()) { + completions.add(world.getName()); + } + return completions; + } + // Optionally, you could add number tab completion for coordinates, but it's not necessary + return null; + } + + // Sends the alert (or cancels it) + 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 (MAP_LINK != null && !MAP_LINK.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: 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; + } + // 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) { + String world = ""; + + if (worldName.endsWith("_nether")) { + world = " in the Nether"; + } + else if (worldName.endsWith("_the_end")) { + world = " in the End"; + } + + return world; + } + + private CoreProtectAPI getCoreProtect() { + Plugin plugin = getServer().getPluginManager().getPlugin("CoreProtect"); + + if (plugin == null || !(plugin instanceof CoreProtect)) { + return null; + } + + return ((CoreProtect) plugin).getAPI(); + } } \ No newline at end of file diff --git a/src/net/ardakaz/griefalert/GriefAlertEvent.java b/src/net/ardakaz/griefalert/GriefAlertEvent.java index a0755e9..1079792 100644 --- a/src/net/ardakaz/griefalert/GriefAlertEvent.java +++ b/src/net/ardakaz/griefalert/GriefAlertEvent.java @@ -1,34 +1,34 @@ -// "API" for other plugins - -package net.ardakaz.griefalert; - -import org.bukkit.Bukkit; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; - -public class GriefAlertEvent extends Event { - private static final HandlerList HANDLERS = new HandlerList(); - private String alert = ""; - - public GriefAlertEvent(String alert) { - this.alert = alert; - - // Discord hook for 1.21.5 - if (Bukkit.getServer().getPluginManager().getPlugin("DiscordSRV") != null) { - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "discord broadcast #grief-alerts " + this.getAlert()); - } - } - - public static HandlerList getHandlerList() { - return HANDLERS; - } - - @Override - public HandlerList getHandlers() { - return HANDLERS; - } - - public String getAlert() { - return this.alert; - } +// "API" for other plugins + +package net.ardakaz.griefalert; + +import org.bukkit.Bukkit; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class GriefAlertEvent extends Event { + private static final HandlerList HANDLERS = new HandlerList(); + private String alert = ""; + + public GriefAlertEvent(String alert) { + this.alert = alert; + + // Discord hook for 1.21.5 + if (Bukkit.getServer().getPluginManager().getPlugin("DiscordSRV") != null) { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "discord broadcast #grief-alerts " + this.getAlert()); + } + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } + + @Override + public HandlerList getHandlers() { + return HANDLERS; + } + + public String getAlert() { + return this.alert; + } } \ No newline at end of file -- 2.39.5 From 5b069aca761d9f91c3ed330984f1730e485d5e7f Mon Sep 17 00:00:00 2001 From: Kleedje30 Date: Mon, 23 Jun 2025 11:32:47 +0000 Subject: [PATCH 06/12] Upload files to "src/net/ardakaz/griefalert" --- src/net/ardakaz/griefalert/GriefAlert.jar | Bin 0 -> 12150 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/net/ardakaz/griefalert/GriefAlert.jar diff --git a/src/net/ardakaz/griefalert/GriefAlert.jar b/src/net/ardakaz/griefalert/GriefAlert.jar new file mode 100644 index 0000000000000000000000000000000000000000..466905617df548d6c65470f0ae3d95b3262e7252 GIT binary patch literal 12150 zcma)?1yEeuwyp^pg1fuBySux)2Y2`27Tkg~Zr!*$1P$(uOK^7y5CS*3=f1o5Ira9d zH@a5UT-DX!2jS2Do{d)+q@I`%yzXd!5G=!3rrWli)vNX%<7z6|q zgsKAE`#%|w|C6Trk7CT<8UHI*5>u9wmeSBXIvKSLYJ1~$?e^}!r?C$Z z&zAcABa*+5KhyXC0r7ikY<2Vh_8*{sf9quB!Sc@;i2sve=4NSTZ|3*^$i(@NOlvn= ztB+<5R&M_)|1T4<|HDL@e-_F7(Tn}jGg~;Axx0e{-~%+l_iudO)V`@%nGtf3h^KY< zDM_T7gQTXikdh6_;TznFDe#DjANwbu-#2TY>Q{PJA~bmd$*XrD5G7W6^YT53UC{#!XrS+Y9AKh6$&3RMDE-`=C88!WEMB}q|K zD%++_anj+g>aMPKTg9=cukK%N9wDSTJviK^<;9nYb#s*FA0s@HBV;kcaA>cLO5@?L zc)BG`af+M$6bgwi1&2g?7xN63INm;XcHa{zQXqh=7ZCfu;aUi|cUMAbmqk%;$Za+r(l?s81c+1_? zZEE!%h9RThE~2MRPT(+`GVqIe?>)*w!_(J8Ob0j6&XAi_+;pD%^Rz|kHX?l#&NM|i zIO!LF(Xm<5D9wy}#1BrlkzI;UFkw%WTx4VNGu(VkBu&&d>F@F`3syK~ zil>h}@FycS5&KQY`p6@vz`H>~gf%{P?!@qb zwssLB*P*_pjk7G+q5)t+BD)9zx1)J9n~_%EQ{`rIf*A@+xzH&f`Xddr*q!T@vF2c9 z!iCnl(r2EXL>8E4w%2H7Dn0bY6_6J3Rm=EB&ulclUnI@l%pvD*8FWA^Ns;5Oz8e~U zxUjkPZuMWYZt16Q2|sVjwc`YVfe9yB@^3>@eG+sorSLaxiW(X~+0Ze0Mp-);F1v*i z4UTGPp46z)ekpNd6`J{FN>82wH!Z;ff$eYFr%jQI?4`aP9WO#jzUVac5jcTQ|BY!i-*~c@|uGoSt6ey&~0s-4( z(^Q=Dyrh#lHc6?U(g5ycl)TDG2#!g5TqotC`D*74#P0#|EuwbUe9QVBAY}j~W_YM( zHLyZYPfu&5p{=aP+!uqCOKxPQxSCb1&CBGc;*sp&ZdgJTV6aJ_8FLnBz?{|y>MHAK zC@t!#Y47T&0o9L6uVp!WKUMT8WKzHO^H`P3=K(~jW0f6CoT`w^UfjU|rc#~OwGB3B z)HRG+*QF07vIK*IU4ANl$&>LDXrIpX=tyEVZ?MQ8>MPhfg-6^TUtBpx@6aqBx77UU zrmh*MM5tHCoJ@*QL;D=7tTU5dP}o&g7UZd*;g02_+RDi%Ekz@{)+1kDS-Ni>*`0h* zBd;QMwg^KNG8%y!5>bBiFk$K6g06o~Hneci!>LtI-hpp(b9|3bT#=lcU}$*!Z0 z*pW8CDHUzx1i=H^(L;dH6+~Co=){toyIDLBXu>)B1Pj~c7|Alm(}uQr5(O{WSC?}+ zV}!p^Juf_ZvX04H*T)^qdIml+u0ofv(O1)GE}mSYQY%5cSh5u%VhzYk1E&=98Vyxe zY(IyNNrm>I%!$SOdHO{ovmM8TnNpW!wnm1%UPQO{VXTBJmXb!q3M3cYpWf9qC8+`O znC5QD6ZOvH9K=}&uSu_#kNxkWr4vs)z7U7m4a?RdKB3C*CQn5Z)$dN>u8yg@T)G1C z3P?U(akphgx6*}{VW26miuYIH5;E_|l@VdZYwb^JT^!sDacNzgP+fF9fAj`tpOhU? z|0tN4Fsd6cGFmchZUWUWZ(v+reD-dIlX&&=b_Zu4n0rfDcsly->i@U_HGhf0`^w4o zal&OL@rW)yZ}Irs9mbwl7(BE@mq3@}_qVYcudk6;uwb{+&NB~`Uxww@C6uN2V7~z~ z7qA@mN$V`j7f?3H4n&{5QQncsxwuruyLMuw6|Z$cgn->XeoDU~bl_h8q^ZfGONsX@ zRGDQ?LA8RJGBQtAS1oXa;NEm^aVFCQa`~C2=>FHO6z)wc;HOKrs$Y&{?j(d?DgL&9x1`h3ze; z(XXW>MlCfcDPO>asb0w~zJ3n?%(Y3QMA)br93J1T@+IDK5d&{&`6zf`Hb}9b(&m&# z5i+MNO5RuNn#DtZp7#yFtkZ_1J~>N(T$KB22`InP4BM_Ooj`;G)MbW-z8t5#XqeVQ zeM4pPiJXcsgieK6>-b8Qdadvw5`AOlvwS+lwIcIuo93Zj0R4N#F9!9aP}Y|yq0Wn? z@%yrgGK(endCrG?FL?1P5)A{iLvFB6?4$&79|WwbCvH82B_cs+-&Y%)ETLl2)NPdI zcc0!{elBvb|G@KbDF&+4;49WN;{@Q0ZQ!s?iU2q2Z&0lnmwefDM%)J4J}+PO2Hkpy z(iN+d)5^3_h$;<2D~vQC8F+6oc3!>jiC??)MEJ@m58NI#bE^2%Dhqo-}$DB0h7 z+-1~XT)DKQCFhbjC4e!Tb7GJ6VhHFZ-7dB})zNC^Txwn5p*U#KT&f0%S2!Coxb{@s zKB3bT7nXGu$K_E&_=)Y^H0x|NBr=BGs<}9lpZ@AjibE)aPXqm>Lay4 za9A(!dXj&uzm*-pFFRG{%(^aKT8`0vwL`o+>A`%X32Ryb{q>FxKNtZwENWZ=(1(i1 zFvARNmpBwk!R(~(+zTRu+ZsN#L=3px z|M`77X;0e+($TorWU9WX-(+Y6j+($rU1di(h{6ywE!x!eWS$YG( zL6nraPNrf|BA7Dhsi)2T%il*&Vi*IJgKoucZKFGzXU zHKW#Fv8`$Di=p~=ZhgP|1Gc23v!`x3nCG8R9Mo~26uUTQT> z-5b!_50~en9^U>_{x@Ut<t z7ICwEaAga!*F{h8;pdnCEEeSw!sbgVmeGi_;M*`5}f=eCg zCZRYpB&lbwi(g3(;0%4 zB%2>>djNZSxA;jYgcd5RPBO7Iw$408{tNR8W6Izm2RXgf zxnU59pJrd97dpQzt{pwMr$v7A8^0~({n{WNX{i9}{z?G#5@}vF=WbN0v>drKfh6PH zkX}mkOH#@U1b^;cN}I(VcKVIDYig@wTY~9*8_nk}uIa*d*S{;D5jO0#`a(S!N_*VthRBYfQ zu~8G~)c-vVCj2;ps+?LUbJrY#ZUKf@y_&;ZxC&0rP{v+jY#eyNWeP+&^aOwjo1)b@ zTF}Fx2MYH!`?+~KVHmM94yBL048tOzTI6z~jlDmQIVv&;svf!W??>}&cEniAT4qXI zE{+304!P^MjkClEQDMQHYWO%@!zii;$z-qMc)Io|GB{psOJ}{;65aV3)26!lRF>pP ze_iXILV%Cbz&txtsveomVhCQrI-Z4Y*g2+ok*{3`rcz($c)4b^iNE$2EQ{R^>Ug@Q zx8XC|+&=y)neikdq&AD}6E5{T()MzODFO{QDpoMUsiT;rIXuv!Zr2wn|@0`fbCfbg)b z9ugR_kzL<`%C>r~r#r}=Z+)|(hK=Yne8oQ8Vf+#_ zvy%vbuj*D=FOKo|bj_zMx!Nno$BR=t&}tp`d^p|t)KPsEK|u9w4X0Qz0}7?T?I=m< zPW-TZ85$A~vxTGs9D2@8mLR^>QyF(Dr_f54K`YQ2Ba_vz-t&oo_JTjc1|#uZG0X;b z!OGw+DS50K)jL^EB6v!LNq==55loR-Vdd9tl9FvE9yiS^cHT~@l$1>C{7gWI+E zduJ0zIMn-61vyxKUQhcV*az$I(~nO~$ZqdpL6Fis8XMsYejO8qpH06$PQpL?v7+`) z6WfpvtoseXe+r4Os0##aYW38iRHnn3)(%GqwDE-hNJ@uu@b8(ho@5+jykwLdycy$8 z3SgrS)^t@@j5R5yJCTNlx^@E#ZmnZ704f}`pN_}h;sd7%KR2KvUv}>~v&+4r9_wn<2`n6N_1b$cu)Zmna+R$Rr^EoGuN)2;gscb%GD#B^R{Y|9$~^h# zUf+UFQrki>fb?~p9Z0BiQ{QJsd_Rk`$#a1A+R6!HUz4LtV5zv{Ub?B-15LNB5bqee zHs--pCSb0J@C1$uGjCSg>+cVuvCTBBliDM*encfS=@$|c?h*ui!&w+bD$+0PHu8Le z>B-DhNM4<5Ve)X?zAe5RM)eJygCDby(=9D~gq#Kh!rDn21!*Tb@ zr70V4yz_{6<2V*+P4TC(`b;Y}q9pqXyu7`lD1N^y3KW?8;?mNN68Mx>ZH}+Q>vcaR zuLYLr+ZFztAbMO2>lMK12rEqc;)y=>4ck9 zUR6xh$kpeCBSp4RZ>;2+>g56crn<^0r8gg_DX&rWI%1hwzm%oEky>AQ5-Dyx;z3`F z=m0N@vax-kt>G-c&QKc1hO9~`U{^0zH|d7W%huMw6mr0v5xYT&)eA6y1OHZ#sMf;;vd#@&tyEReCCFgFXDhUZsSFT@J2JbYFsSUdnR z&LYZpV+WKJHMm*EJ|MLqq+q6ePPO*aVEdV6x5su4*hKAu6UWd2QIE!jRVL32zsb3R z`GE{9MGZJJf(3%!Cw+9Q0j0N}4zkHyCDHRxiQ$;N{1Cw=+dskwHtSwoaB9)H#^uXw zX{6dd>LD$$3ety$hA5`e#zDhhb6g`@B^kXg6R&h9y=99E??=S>p{OS53YMGpe)ae6 z|9R^pGCkNCTBA8FV9Szs^We;RWvp6fI7X52rVW{d^DYYt*G_kgDImIS#xJ@lEKTod zLP+-Mr&SD@v3QPD!#XV)13;cX{oV8U5u_lVh1*?yzp*+zMBORN&Rtf=Xrdvkjm?qNy;EADXCa5^l5> zweyp%fvy92q&it70@gu`@v3Xm6qj+&vH}iaQ5?$LzRxea!rN@)4&-hEtkSKsQiBHu z;jvxjcZV@kxp`Z<${$@L7IyA-iUYvk`jRg)r5e%*;YL9#>KNJRksnT-MRv? zw3kWTSwGqlKHMk9_;T##5a}I4BwgLRlV$46oSd}r#;qj+!5c$(CX^7QQLo0Vmf?=aH@aNXSD#?g3=(fdcPj1n;C2@8l5g zB-PBOLP#JU=iPWy2(wQ47tstAn|5iITfG3nOIv&^w#H%i==eqe*Wk+QlL_w{n0Jkf zcdeOs&69U+$))AgwS~Z?g&-s_wV?;8>1WB*@Q%*TlCDWdjqBJN`bJdaQ{g?yJ)6zZ zu15$=_0KrJmCbLo!gdxG-MI$VU8ITm_FNqnvq~Ht7<;bq7nJRDoU~QcTl@M^Z3Pnt z4Mc@EP=*1~);QB1D>v0owok{*=cxIc(^isJco8+9&1c+e6gY;Tec!1O)@M$~3eZcW z_ewo^2;es1nC-4{VmR3lZ@^@F@)*twKND`q7#s!BJ_qrFpgdTFDsZ`y2S;{IO<{3M zX1|%DzT)PlOpssGClP*MvVZ)Uh7m{L6Gb8kNoySc)RaveD1C!KyL>OWqoG<_4vYwbEq`;@-F9b9 zF@g9AW7-A%*`)A?yC!QmUsRC9<-D`mT(1RDfZ7N!-ph#;RI??@H|%q~2D{BCDsdBP3A2jv0z~uGa}AEL=rrVJX-B+G z!Zh~X!m^+uzw{M~kKi&c4-X!tw$iz=% zMj(ZyZ3H8a-snP)T%Rs8j?6!H#KQ>nX;Up6Tc(RUa_-+F4ehNJoxO!G;z7y{9g4(? z;03hUlyyE)@ji*>PDfE%lRPH6v^WQ|%akIR_Kuz-3jW+byWAc^mb6r{LS~+?|CLO^ z?sb)H9CY$|Vm8b7rIEln(%bd!nnQH%$DA8{0NICtQHZJZuR29vO$ffqp+Dx(UU|c+ zX8saEJnKB0^Ia{Tntxw;CE5Xf3iVa*DXY^)F=)|gOvmEN>eq{mjnsp&enx(~LBe_e z7a{%!8jC931f`7IVVT1r*G*OzhxQjQJ+B3-_6E4{ChruYIk~|mf+RbPfvRO?zO#Z^ z7ZMHLRC(UGsqYT@{d`R+GBf;MbAD6BlesEl#ByuCm_~yZM<0k4bjX^vzG56<=gFcp zzmjDB-fkGKS@?y(mpXrditxMoW?$o@e%>U{jrAjG zK5QYN#^Whq%$-oC!>r;aY!nobyEr1t??U!5*13o?q9>bi*)=_RYftzKmN0EHtJ3C`o_WXC+D3z6n~F-%Kx9`V&28)oM+w@q=bvvRO3$ts_$SqY7R55qmFRQqR&JETqJA1)|?nchWpkAi{y zhxG6`sniw*3@gR;=M^cM3XJL0lHcTRvH{QH8pDOmC%u^BxBGXc49u&gODAO1m*w}R8V#St=&Fk{2a8GD)Xu2WH zAJGS0&<0)D23_C>udJfC-Soej6w3F6X56Nu4rwj)>C9vIM81rmw@Lte`*~)-BWaiW zIY)ThC*9)mspPu0(W0uF(5$KBJLI@5`9SEagvU6wqq1JvWA^k$$yoo00biwB(Pf3k zGj}}_w)~8hZu9UxkD8HG#GUmbL$(H$&l%}sBSlc=T?DL0sz_Dq(FQ zpX)}YBr6gJXBs0Fo)|n@iTv`+LByWC%R{)UfV{V{%NV>J{Cbu3s6&pO=^dl*beWLl zv#}4{V>`}$rR&NVPssFpLM}AkbLyK4-~b-3tKTO-=BRs6TmwYT_u!7igHW?}#5(eR z9#LZ!bUu-FD8|DdpVVcvPBxZ@R(7daTX6BH3+xwg1^*(z^+a`lT!oGE{y#CUr@ zbwf9`U!JV+8h&^^-S#)>$?2 z1>5=sO5~H~8)@&9%;S$2iB}43U#Rk+IFtR>CzTiLToS_X-l%SGV*^d_WDTy&ip@Y(7lA^kUI1yTgq;q{jIm=A!gXiL}(M$-r9?`$V zakg->cHonl@~|nst{7f7j9}Ep1zat|FehTl4AI7+7BgwHEvB#x>q*DJXV{t-m8!Ne zDGXXF#C@^5jy}#v;+5*K;607eTV=~e3O*H9=7^U!ZsROe$Ygx@@`PY-J}i^wO?dHB z5?6V?5oPd`duCoB{~;kw*t*c>NYiz5rn7eJO?~IkABmo_PIFk+hF1CT@Nb}6} zDc#fTL(OwaayJ?8)6L&yqO>VE;ssUHMLu)A9{@CUhBTwL*D?dv=xXJzN$n zpB1TFuo&yn0V`#hCxMqDtVhw+ER`| zMtut9(rR;VDb(qfxOp2C^JSDl!RH9xFk?)sy9V6?w>Y`9ChDh z^Xvkdd>=?Kw<*$?NZQQ}YM0dxXxRm5pA%=aGbtQf6jXAm)f*$2qmZ1rcO5gv&0yxI zv{*C@{a&mr$-MoS+GbG=TTx1)RFnkNg(3U27zeOuS+b<{v*Y=(Ty#^GH2dRedW*zu) zqHTPT$4TeJ5_8Dbm4&FmV{;F#nqUry`iIGgF-8IS9GgZQ&c-9;)9xcCoez3hs zHsIZ7p+x?{C)R3&3K>IL6U@xH(jE2=LVlOhHgUFZN-Y-OKU9nE=O7~&T%{u`oC$E9 zOEeIi@rDUI2ZgVi22JH=Fc5>@i9Gua4Rns=F%Sb+6eu8gt044i9)pzBa!1%wqIT>W zV(qt~YKj9~g_JOPW-#j&(2~V=aQ7uRvvdO)<2k$ytS_+NyZ{V&M;J2DU`!%1|5=M2 zI;xPOfgPan_3mTP2}boJHQx{ur}27hw^QOJhcQNQnC{Te55F#VNPK!kQ<=0r1(FJ5 z=6~r{VbWGWs;5$F&bH&wJXLipvuo565N~b=P3oM}f;%(?w3~Oe1k{>SH3g)a=QaIf z?W`+WWScp(3_z2DOotKAhE9L5eAX zA13YuZ#{l^s2skbIB&lD@(qee=mVT4~6jhODB2K0TCBKXFePxGRLt;ukwh@ zV{`$Y1R)aE8qc}DAHa8mMYaexYn#l8urbB0K+wbLOL&i)fLTWGn51ho_MKLm>&5gt z?l8ZcFo~cf<&O>Rjce`pZ>REHv!me8UX#eGbn&jRfwS;TB6XFgIe58)bV$FSG_H}` z3p`WbroPcfX7#^5qIi*YWXnNWyE1$Ap7|GE#)7UKh6Fc`d-k23-KV)RH2I8&O^fE# zSXm>;C9*3Hb^|`64hbi^X;L2|8Gc0^IO29jO}BP^nn^Z@HE>gjO#4|=@1b-l1(oTN zMP@Hxma1USa2;>fG9wh~wbc3LNdmmB0$a0#AJNF(+oG$dU(23bbym5LMP8)KpiHZP zU&HY^7^%ntyBjppfxBUO#PGAFfR{djW>J5Q3(eswTOqp-w5M&6&QshRX+mq}kB1w1 zw6pe^ZBKxsZObBZB9p`8_?T~8iMZ7G-XMf3gUBk_ayl+YZFn>aW-GH0(Z^kDglP+T zUe}D*@f$d|7Dl+0iFoWq^3I1vvX*nA(cwey$s-aLQY8p=5tq6*eAh=HNP<#rsJVWE z=@?E0@71Zd9U6BnA?!U3L(i5CSEuEDK^TEbP7%V(G&i_=6~k;a;YNXWY~8am0n;OO z*k~FM-P7^8G6R@8Wz$$BREGZ;|>FeE`nosX8U=B<3m_R{5tik@haiNqZ{tdDG4PHXHICxsyIx+h?Iv6Ew z$PcojMqJ)v-a7_ATd=444@?b!5V@WHYyqs6Q@D?wYscaAZ0f%#i zX*kHu9cqMwL&qSqvk3F+=4I6~Z%tGfI|~2H=X_Lo-ZYOZjgwJv^9G^8cV}n_>Y9uCkK8YMJv4s?$@VC7%+L(h60UO3l-xK_D zIs>SFy4%1xeES*dj~MBrH({4P^hi?2HWK%gqfkJki({s0ZODdd2tGAI2kapyPjF* zur{HES(Ss97IwUxeU>OR=i1#8i`rrdQ|skqm>IF*KtG~+TnQ^Oa%-c>RRFZ5pYTm0 zZ)~wWt#KMRWLugk;JceDOYiPT@oSD^KVgfZzUyWZexD3Ws6UqW*T#jMS@Bvh3LL@f zM-}TQCofnYhKq2#wii5^oj{5$zgZgVH^GFjwa|-v(hi}BYVx`qW=go`&~VCZAJYQZ zHNWhC@v@g3K=bSbWlC{vf5@a1oo7ZpXV=`8)7xpdI6>DsYeavs->Oq!D@8F{<@Rl( z9(~Ur)irg5Wo3L#i}@cHw=9^Xu3odt}vHd#%mpcWTKB|odzdZmU`S3qbB{? z(gm+V66y59K);Z2B1OH688j5W1@e~C*ah`*zE4}Yhq+~0*mG>Z8!g*YNz%6D!&7d| zFAJvh=dl^u$5;N7y0@`*-K!i0@<&xx2LHKWm~1h5zP~S4 z#&5J)RpA{ZCd9u((f@~d{sTq-d;Blu{0EW#PwC$&0n&dee1Q08 zn)$B^gTIIWVw?Y2@rQ2yD-M1S|0w=qo&Q<+hj#ueP=8zVcjdpa&wsh~hkpM1RFqr Date: Mon, 23 Jun 2025 11:33:58 +0000 Subject: [PATCH 07/12] Delete src/net/ardakaz/griefalert/GriefAlert.jar --- src/net/ardakaz/griefalert/GriefAlert.jar | Bin 12150 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/net/ardakaz/griefalert/GriefAlert.jar diff --git a/src/net/ardakaz/griefalert/GriefAlert.jar b/src/net/ardakaz/griefalert/GriefAlert.jar deleted file mode 100644 index 466905617df548d6c65470f0ae3d95b3262e7252..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12150 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 Date: Mon, 23 Jun 2025 11:37:00 +0000 Subject: [PATCH 08/12] Upload files to "src/net/ardakaz/griefalert" --- src/net/ardakaz/griefalert/GriefAlert.java | 80 +++++++++++++++++++--- 1 file changed, 69 insertions(+), 11 deletions(-) diff --git a/src/net/ardakaz/griefalert/GriefAlert.java b/src/net/ardakaz/griefalert/GriefAlert.java index f4ae8d2..daaf4d8 100644 --- a/src/net/ardakaz/griefalert/GriefAlert.java +++ b/src/net/ardakaz/griefalert/GriefAlert.java @@ -6,6 +6,7 @@ import net.coreprotect.CoreProtectAPI.ParseResult; import org.bukkit.Bukkit; import org.bukkit.ChatColor; +import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.command.Command; @@ -16,6 +17,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.hanging.HangingBreakByEntityEvent; import org.bukkit.event.inventory.InventoryAction; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; @@ -328,30 +330,86 @@ public class GriefAlert extends JavaPlugin implements Listener, TabCompleter { ItemStack handItem = player.getInventory().getItemInMainHand(); boolean handEmpty = handItem ==null || handItem.getType() == Material.AIR; boolean frameEmpty = itemInFrame ==null || itemInFrame.getType() == Material.AIR; + String playerName = player.getName(); String action; - if (handEmpty && !frameEmpty) { - action = "took " + itemInFrame.getType(); - } else if (!handEmpty && frameEmpty) { + if (!handEmpty && frameEmpty) { action = "added " + handItem.getType(); - } else { + } + else { return; } - String message = ChatColor.GRAY + player.getName() + " " + action + " ItemFrame owned by " + target + - " at " + x + " " + y + " " + z + " (" + worldName + ")"; - alert(message, player.getName(), - "[Map Link](" + MAP_LINK + "/?worldname=" + worldName + "&zoom=7&x=" + x + "&y=" + y + "&z=" + z + ")", - target, x, y, z, worldName); - - + String message = ChatColor.GRAY + playerName + " " + action + " an item to an item frame owned 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, x, y, z, worldName); + } + } + //breaking item frame item (uses a different event then placing an item) + @EventHandler (ignoreCancelled = true) + public void onItemFrameDamage(EntityDamageByEntityEvent event){ + if (!(event.getEntity() instanceof ItemFrame)) return; + if (!(event.getDamager() instanceof Player)) return; + + ItemFrame frame = (ItemFrame) event.getEntity(); + Player player = (Player) event.getDamager(); + ItemStack item = frame.getItem(); + String playerName = player.getName(); + + int x = frame.getLocation().getBlockX(); + int y = frame.getLocation().getBlockY(); + int z = frame.getLocation().getBlockZ(); + String worldName = frame.getWorld().getName(); + + if (isLocationIgnored(x, y, z, worldName)) { + event.setCancelled(true); + return; + } + String target = inspectBlock(frame.getLocation().getBlock(), player); + if (target == null || target.equals(player.getName())) return; + + + if (item == null || item.getType() == Material.AIR) return; + // At this point, the player is removing the item + String action = "took " + item.getType(); + String message = ChatColor.GRAY + playerName + " " + action + " from item frame owned 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, x, y, z, worldName); + } + + + @EventHandler(ignoreCancelled = true) + public void onItemFrameBreak(HangingBreakByEntityEvent event){ + if (!(event.getEntity() instanceof ItemFrame)) return; + if (!(event.getRemover() instanceof Player)) return; + + ItemFrame frame = (ItemFrame) event.getEntity(); + Player player = (Player) event.getRemover(); + String playerName = player.getName(); + + + Location loc = frame.getLocation(); + int x = loc.getBlockX(); + int y = loc.getBlockY(); + int z = loc.getBlockZ(); + String worldName = loc.getWorld().getName(); + + if (isLocationIgnored(x, y, z, worldName)) { + event.setCancelled(true); + return; + } + String target = inspectBlock(loc.getBlock(), player); + if (target ==null || target.equals(player.getName())) return; + + String action = "broke an item frame"; + String message = ChatColor.GRAY + playerName + " " + action + " owned 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, x, y, z, worldName); } + @Override -- 2.39.5 From e3da66b37eeef78bcf6cb39ca6917e53e60f5f64 Mon Sep 17 00:00:00 2001 From: Ardakaz Date: Fri, 27 Jun 2025 07:04:10 +0000 Subject: [PATCH 09/12] Updating the version number --- plugin.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/plugin.yml b/plugin.yml index 15c24c2..5840c37 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,14 +1,14 @@ -name: GriefAlert -main: net.ardakaz.griefalert.GriefAlert -version: 0.3.2 -api-version: 1.21 -depends: [CoreProtect] -softdepend: [DiscordSRV] -description: A simple grief alert plugin using the CoreProtect API. -authors: [Ardakaz, kleedje30] -commands: - griefalert: - description: GriefAlert staff commands - usage: /griefalert - permission: griefalert.staff - permission-message: You do not have permission to use this command. +name: GriefAlert +main: net.ardakaz.griefalert.GriefAlert +version: 0.4 +api-version: 1.21 +depends: [CoreProtect] +softdepend: [DiscordSRV] +description: A simple grief alert plugin using the CoreProtect API. +authors: [Ardakaz, kleedje30] +commands: + griefalert: + description: GriefAlert staff commands + usage: /griefalert + permission: griefalert.staff + permission-message: You do not have permission to use this command. -- 2.39.5 From 2837e8f4710c0043b2ca76f89355ed2057f84d4e Mon Sep 17 00:00:00 2001 From: Ardakaz Date: Fri, 27 Jun 2025 09:07:48 +0000 Subject: [PATCH 10/12] Reverted the buggy 1.21.5 behavior --- src/net/ardakaz/griefalert/GriefAlertEvent.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/net/ardakaz/griefalert/GriefAlertEvent.java b/src/net/ardakaz/griefalert/GriefAlertEvent.java index 1079792..7975e88 100644 --- a/src/net/ardakaz/griefalert/GriefAlertEvent.java +++ b/src/net/ardakaz/griefalert/GriefAlertEvent.java @@ -12,11 +12,6 @@ public class GriefAlertEvent extends Event { public GriefAlertEvent(String alert) { this.alert = alert; - - // Discord hook for 1.21.5 - if (Bukkit.getServer().getPluginManager().getPlugin("DiscordSRV") != null) { - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "discord broadcast #grief-alerts " + this.getAlert()); - } } public static HandlerList getHandlerList() { -- 2.39.5 From f1877e38a4dab514c00183de65ba0bc9ad72f56b Mon Sep 17 00:00:00 2001 From: Ardakaz Date: Fri, 27 Jun 2025 09:11:58 +0000 Subject: [PATCH 11/12] Some little styling --- src/net/ardakaz/griefalert/GriefAlert.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/net/ardakaz/griefalert/GriefAlert.java b/src/net/ardakaz/griefalert/GriefAlert.java index daaf4d8..160beb6 100644 --- a/src/net/ardakaz/griefalert/GriefAlert.java +++ b/src/net/ardakaz/griefalert/GriefAlert.java @@ -254,7 +254,7 @@ public class GriefAlert extends JavaPlugin implements Listener, TabCompleter { String playerName = player.getName(); String target = inspectBlock(armorStand.getLocation().getBlock(), player); if (target != null) { - String message = ChatColor.GRAY + playerName + " broke an ArmorStand placed by " + target + " at " + x + " " + y + " " + z + getHumanWorldName(worldName); + String message = ChatColor.GRAY + playerName + " broke an armor stand 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, x, y, z, worldName); } } @@ -305,7 +305,7 @@ public class GriefAlert extends JavaPlugin implements Listener, TabCompleter { } else { action = "interacted with"; } - String message = ChatColor.GRAY + playerName + " " + action + " on ArmorStand owned by " + target + " at " + x + " " + y + " " + z + getHumanWorldName(worldName); + String message = ChatColor.GRAY + playerName + " " + action + " on an armor stand owned 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, x, y, z, worldName); } } -- 2.39.5 From 8ed58411600310cec5c014fc28e92508513437db Mon Sep 17 00:00:00 2001 From: Ardakaz Date: Fri, 27 Jun 2025 09:35:17 +0000 Subject: [PATCH 12/12] Removed the null from "Same behavior continues." --- src/net/ardakaz/griefalert/GriefAlert.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net/ardakaz/griefalert/GriefAlert.java b/src/net/ardakaz/griefalert/GriefAlert.java index 160beb6..3c4afa0 100644 --- a/src/net/ardakaz/griefalert/GriefAlert.java +++ b/src/net/ardakaz/griefalert/GriefAlert.java @@ -609,7 +609,7 @@ public class GriefAlert extends JavaPlugin implements Listener, TabCompleter { } // Send an event for external hooks GriefAlertEvent griefalert_event; - if (MAP_LINK != null && !MAP_LINK.isEmpty()) { + if (MAP_LINK != null && !MAP_LINK.isEmpty() && mapLink != null) { griefalert_event = new GriefAlertEvent(message + " (" + mapLink + ")"); } else { griefalert_event = new GriefAlertEvent(message); -- 2.39.5