From 07d60ee5b6fae328c78777fcc63d64718ff73cce Mon Sep 17 00:00:00 2001 From: Kleedje30 Date: Fri, 23 May 2025 20:59:45 +0000 Subject: [PATCH 01/18] Update plugin.yml --- plugin.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/plugin.yml b/plugin.yml index c819c8b..8ac304a 100755 --- a/plugin.yml +++ b/plugin.yml @@ -1,7 +1,8 @@ -name: GriefAlert -main: net.ardakaz.griefalert.GriefAlert -version: 0.2.1 -api-version: 1.21 -depends: [CoreProtect] -description: A simple grief alert plugin using CoreProtect API. -author: Ardakaz +name: GriefAlert +main: net.ardakaz.griefalert.GriefAlert +version: 0.2.1 +api-version: 1.21 +depends: [CoreProtect] +description: A simple grief alert plugin using CoreProtect API. +author: [Ardakaz, kleedje30] +#Kleedje30 only made some small modifications From fe413d59eaa531f92d18877f7f686b004d01b809 Mon Sep 17 00:00:00 2001 From: Ardakaz Date: Fri, 23 May 2025 21:01:01 +0000 Subject: [PATCH 02/18] Update plugin.yml "authors" instead "author" --- plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.yml b/plugin.yml index 8ac304a..8c0db2a 100755 --- a/plugin.yml +++ b/plugin.yml @@ -4,5 +4,5 @@ version: 0.2.1 api-version: 1.21 depends: [CoreProtect] description: A simple grief alert plugin using CoreProtect API. -author: [Ardakaz, kleedje30] +authors: [Ardakaz, kleedje30] #Kleedje30 only made some small modifications From af1fb5fd62931f3ce8dea5cfd2186aeafda99297 Mon Sep 17 00:00:00 2001 From: Kleedje30 Date: Fri, 23 May 2025 21:07:47 +0000 Subject: [PATCH 03/18] Update plugin.yml --- plugin.yml | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/plugin.yml b/plugin.yml index 8c0db2a..1199157 100755 --- a/plugin.yml +++ b/plugin.yml @@ -4,5 +4,24 @@ version: 0.2.1 api-version: 1.21 depends: [CoreProtect] description: A simple grief alert plugin using CoreProtect API. -authors: [Ardakaz, kleedje30] -#Kleedje30 only made some small modifications +author: Ardakaz +#modified by kleedje30 version 0.4.1 + + +commands: + disablelocation: + description: Disable GriefAlert at a location + usage: /disablelocation + permission: griefalert.staff.disable + enablelocation: + description: Enable GriefAlert at a location + usage: /enablelocation + permission: griefalert.staff.enable + clearlocations: + description: Clears all disabled alert locations from the database + usage: /clearlocations + permission: griefalert.staff.clear + checklocation: + description: Checks if a location is disabled for alerts + usage: /checklocation + permission: griefalert.staff.check \ No newline at end of file From 3bc045e79946ac31a7539386bb8227c3b6f73632 Mon Sep 17 00:00:00 2001 From: Kleedje30 Date: Fri, 23 May 2025 21:36:04 +0000 Subject: [PATCH 04/18] Update plugin.yml --- plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.yml b/plugin.yml index 1199157..88b89d9 100755 --- a/plugin.yml +++ b/plugin.yml @@ -5,7 +5,7 @@ api-version: 1.21 depends: [CoreProtect] description: A simple grief alert plugin using CoreProtect API. author: Ardakaz -#modified by kleedje30 version 0.4.1 +#modified by kleedje30 version 0.4.2 commands: From 594523fb38b7c46850d543594c82df16563afc7f Mon Sep 17 00:00:00 2001 From: Kleedje30 Date: Fri, 23 May 2025 21:38:41 +0000 Subject: [PATCH 05/18] Update src/net/ardakaz/griefalert/GriefAlert.java --- src/net/ardakaz/griefalert/GriefAlert.java | 552 +++++++++++---------- 1 file changed, 297 insertions(+), 255 deletions(-) diff --git a/src/net/ardakaz/griefalert/GriefAlert.java b/src/net/ardakaz/griefalert/GriefAlert.java index 961c312..e5efe2b 100755 --- a/src/net/ardakaz/griefalert/GriefAlert.java +++ b/src/net/ardakaz/griefalert/GriefAlert.java @@ -1,256 +1,298 @@ -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.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.util.Arrays; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -public class GriefAlert extends JavaPlugin implements Listener { - - 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; - - // 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"); - - getLogger().info("GriefAlert has been enabled."); - } - - @Override - public void onDisable() { - getLogger().info("GriefAlert has been disabled."); - } - - @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) { - // Alert - String message = ChatColor.GRAY + playerName + " broke " + blockType + " placed by " + target + " at " + x + " " + y + " " + z + getHumanWorldName(worldName); - alert(message, playerName, "[Map Link](" + MAP_LINK + "/?worldname=" + worldName + "&zoom=7&x=" + x + "&y=" + y + "&z=" + z + ")", target); - } - } - - // 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) { - // 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); - } else { - // Putting back - String message = ChatColor.GRAY + playerName + " put " + amount + " " + itemName + " into " + target + "'s container at " + x + " " + y + " " + z + getHumanWorldName(worldName); - alert(message, playerName, "[Map Link](" + MAP_LINK + "/?worldname=" + worldName + "&zoom=7&x=" + x + "&y=" + y + "&z=" + z + ")", target); - } - } - } - - // Sends the alert (or cancels it) - private void alert(String message, String playerName, String mapLink, String target) { - // Exclude trusted people - Player griefer = Bukkit.getPlayer(playerName); - if (griefer.hasPermission("griefalert.exclude") || griefer.hasPermission("griefalert.exclude." + target)) { - return; - } - - // Spam limiter - String realAlertMessage = message; - String[] alert1 = null; - if (lastAlert != null) { - alert1 = lastAlert.split(" "); - } - String[] alert2 = message.split(" "); - - if (alert1 != null) { - if (alert1[2].equals(alert2[2]) && alert1[5].equals(alert2[5]) && alert1[1].equals("broke") && alert2[1].equals("broke")) { - identicalAlerts += 1; - } - else if (Arrays.equals(alert1, alert2)) { - identicalAlerts += 1; - } - else { - identicalAlerts = 1; - } - } - - if (identicalAlerts == 4) { - message = ChatColor.GRAY + "Same behavior continues."; - mapLink = null; - } - - if (identicalAlerts > 4) { - return; - } - - // Send an event for external hooks - GriefAlertEvent griefalert_event; - if (mapLink != null && !mapLink.isEmpty()) { - griefalert_event = new GriefAlertEvent(message + " (" + mapLink + ")"); - } - else { - griefalert_event = new GriefAlertEvent(message); - } - getServer().getPluginManager().callEvent(griefalert_event); - - // Notify staff ingame - for (Player player : Bukkit.getOnlinePlayers()) { - if (player.hasPermission("griefalert.notify")) { - player.sendMessage(message); - } - } - - lastAlert = realAlertMessage; - } - - // Block inspector: if the block was placed by another player, returns their name. - private static String inspectBlock(Block block, Player player) { - List lookup = coreProtectAPI.blockLookup(block, 50000000); - if (lookup == null || lookup.size() <= 0) { - // Natural block - return null; - } - - String[] result = lookup.get(0); - ParseResult parseResult = coreProtectAPI.parseResult(result); - if (parseResult.isRolledBack() && lookup.size() != 1) { - result = lookup.get(1); - parseResult = coreProtectAPI.parseResult(result); - } - - if (result == null || parseResult == null || parseResult.getPlayer().startsWith("#") || parseResult.getPlayer().equals(player.getName())) { - // Placed by breaker or natural event - return null; - } - return parseResult.getPlayer(); - - } - - 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.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.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class GriefAlert extends JavaPlugin implements Listener { + + 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 AlertsLogic alertsLogic; + + // 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"); + + getLogger().info("GriefAlert has been enabled."); + + // Initialize AlertsLogic and register commands + alertsLogic = new AlertsLogic(this); + getCommand("disablelocation").setExecutor(this); + getCommand("enablelocation").setExecutor(this); + getCommand("clearlocations").setExecutor(this); + getCommand("checklocation").setExecutor(this); + } + + @Override + public void onDisable() { + if (alertsLogic != null) { + alertsLogic.close(); + } + getLogger().info("GriefAlert has been disabled."); + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + String cmd = command.getName().toLowerCase(); + switch (cmd) { + case "disablelocation": + return alertsLogic.handleDisableLocation(sender, args); + case "enablelocation": + return alertsLogic.handleEnableLocation(sender, args); + case "clearlocations": + return alertsLogic.handleClearLocations(sender, args); + case "checklocation": + return alertsLogic.handleCheckLocation(sender, args); + default: + return false; + } + } + + @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 alerts are disabled for this location + if (alertsLogic != null && alertsLogic.isLocationSaved(x, y, z, worldName)) { + return; + } + + // Check if grief + String target = inspectBlock(event.getBlock(), event.getPlayer()); + if (target != null) { + // Alert + String message = ChatColor.GRAY + playerName + " broke " + blockType + " placed by " + target + " at " + x + " " + y + " " + z + getHumanWorldName(worldName); + alert(message, playerName, "[Map Link](" + MAP_LINK + "/?worldname=" + worldName + "&zoom=7&x=" + x + "&y=" + y + "&z=" + z + ")", target); + } + } + + // 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; + } + + // Check if alerts are disabled for this location + int x = inventory.getLocation().getBlockX(); + int y = inventory.getLocation().getBlockY(); + int z = inventory.getLocation().getBlockZ(); + String worldName = inventory.getLocation().getWorld().getName(); + if (alertsLogic != null && alertsLogic.isLocationSaved(x, y, z, worldName)) { + return; + } + + // 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(); + // x, y, z, worldName already defined above + + if (stealing) { + // Stealing + String message = ChatColor.GRAY + playerName + " took " + amount + " " + itemName + " from " + target + "'s container at " + x + " " + y + " " + z + getHumanWorldName(worldName); + alert(message, playerName, "[Map Link](" + MAP_LINK + "/?worldname=" + worldName + "&zoom=7&x=" + x + "&y=" + y + "&z=" + z + ")", target); + } else { + // Putting back + String message = ChatColor.GRAY + playerName + " put " + amount + " " + itemName + " into " + target + "'s container at " + x + " " + y + " " + z + getHumanWorldName(worldName); + alert(message, playerName, "[Map Link](" + MAP_LINK + "/?worldname=" + worldName + "&zoom=7&x=" + x + "&y=" + y + "&z=" + z + ")", target); + } + } + } + + // Sends the alert (or cancels it) + private void alert(String message, String playerName, String mapLink, String target) { + // Exclude trusted people + Player griefer = Bukkit.getPlayer(playerName); + if (griefer.hasPermission("griefalert.exclude") || griefer.hasPermission("griefalert.exclude." + target)) { + return; + } + + // Spam limiter + String realAlertMessage = message; + String[] alert1 = null; + if (lastAlert != null) { + alert1 = lastAlert.split(" "); + } + String[] alert2 = message.split(" "); + + if (alert1 != null) { + if (alert1[2].equals(alert2[2]) && alert1[5].equals(alert2[5]) && alert1[1].equals("broke") && alert2[1].equals("broke")) { + identicalAlerts += 1; + } + else if (Arrays.equals(alert1, alert2)) { + identicalAlerts += 1; + } + else { + identicalAlerts = 1; + } + } + + if (identicalAlerts == 4) { + message = ChatColor.GRAY + "Same behavior continues."; + mapLink = null; + } + + if (identicalAlerts > 4) { + return; + } + + // Send an event for external hooks + GriefAlertEvent griefalert_event; + if (mapLink != null && !mapLink.isEmpty()) { + griefalert_event = new GriefAlertEvent(message + " (" + mapLink + ")"); + } + else { + griefalert_event = new GriefAlertEvent(message); + } + getServer().getPluginManager().callEvent(griefalert_event); + + // Notify staff ingame + for (Player player : Bukkit.getOnlinePlayers()) { + if (player.hasPermission("griefalert.notify")) { + player.sendMessage(message); + } + } + + lastAlert = realAlertMessage; + } + + // Block inspector: if the block was placed by another player, returns their name. + private static String inspectBlock(Block block, Player player) { + List lookup = coreProtectAPI.blockLookup(block, 50000000); + if (lookup == null || lookup.size() <= 0) { + // Natural block + return null; + } + + String[] result = lookup.get(0); + ParseResult parseResult = coreProtectAPI.parseResult(result); + if (parseResult.isRolledBack() && lookup.size() != 1) { + result = lookup.get(1); + parseResult = coreProtectAPI.parseResult(result); + } + + if (result == null || parseResult == null || parseResult.getPlayer().startsWith("#") || parseResult.getPlayer().equals(player.getName())) { + // Placed by breaker or natural event + return null; + } + return parseResult.getPlayer(); + + } + + 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 From 14a655478f4e277fb492c067648825e51b6f34d8 Mon Sep 17 00:00:00 2001 From: Kleedje30 Date: Fri, 23 May 2025 21:39:42 +0000 Subject: [PATCH 06/18] Add src/net/ardakaz/griefalert/AlertsLogic.java --- src/net/ardakaz/griefalert/AlertsLogic.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/net/ardakaz/griefalert/AlertsLogic.java diff --git a/src/net/ardakaz/griefalert/AlertsLogic.java b/src/net/ardakaz/griefalert/AlertsLogic.java new file mode 100644 index 0000000..e69de29 From 41ef9d6d0d0a3a8a48658b3b3af606a55f45de9c Mon Sep 17 00:00:00 2001 From: Kleedje30 Date: Fri, 23 May 2025 21:44:34 +0000 Subject: [PATCH 07/18] Update src/net/ardakaz/griefalert/AlertsLogic.java --- src/net/ardakaz/griefalert/AlertsLogic.java | 211 ++++++++++++++++++++ 1 file changed, 211 insertions(+) diff --git a/src/net/ardakaz/griefalert/AlertsLogic.java b/src/net/ardakaz/griefalert/AlertsLogic.java index e69de29..c0aea39 100644 --- a/src/net/ardakaz/griefalert/AlertsLogic.java +++ b/src/net/ardakaz/griefalert/AlertsLogic.java @@ -0,0 +1,211 @@ +/*this SHOULD handle checking the alert for example: +if a block is broken at world 10 10 10 and the location has been disabled +then it should return null and cancle actualy giving an alert +(if location is enabled the alert will just alert) +and it SHOULD handle the logic and processing of /disable or /enable +and the SQL database logic +*/ +package net.ardakaz.griefalert; + +import java.sql.*; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.command.CommandSender; +import org.bukkit.ChatColor; + +public class AlertsLogic { + private final String DB_URL = "jdbc:sqlite:plugins/GriefAlert/locations.db"; + private Connection connection; + private final JavaPlugin plugin; + + public AlertsLogic(JavaPlugin plugin) { + this.plugin = plugin; + try { + connectDatabase(); + createLocationsTable(); + } catch (SQLException e) { + plugin.getLogger().severe("Could not initialize SQLite database: " + e.getMessage()); + } + } + + private void connectDatabase() throws SQLException { + connection = DriverManager.getConnection(DB_URL); + } + + private void createLocationsTable() throws SQLException { + String sql = "CREATE TABLE IF NOT EXISTS locations (id INTEGER PRIMARY KEY AUTOINCREMENT, a INTEGER, b INTEGER, c INTEGER, world TEXT)"; + try (Statement stmt = connection.createStatement()) { + stmt.execute(sql); + } + } + + public void saveLocation(int a, int b, int c, String world) { + String sql = "INSERT INTO locations (a, b, c, world) VALUES (?, ?, ?, ?)"; + try (PreparedStatement pstmt = connection.prepareStatement(sql)) { + pstmt.setInt(1, a); + pstmt.setInt(2, b); + pstmt.setInt(3, c); + pstmt.setString(4, world); + pstmt.executeUpdate(); + } catch (SQLException e) { + plugin.getLogger().severe("Error saving location: " + e.getMessage()); + } + } + + public boolean isLocationSaved(int a, int b, int c, String world) { + String sql = "SELECT id FROM locations WHERE a = ? AND b = ? AND c = ? AND world = ?"; + try (PreparedStatement pstmt = connection.prepareStatement(sql)) { + pstmt.setInt(1, a); + pstmt.setInt(2, b); + pstmt.setInt(3, c); + pstmt.setString(4, world); + ResultSet rs = pstmt.executeQuery(); + return rs.next(); + } catch (SQLException e) { + plugin.getLogger().severe("Error querying location: " + e.getMessage()); + return false; + } + } + + public boolean removeLocation(int a, int b, int c, String world) { + String sql = "DELETE FROM locations WHERE a = ? AND b = ? AND c = ? AND world = ?"; + try (PreparedStatement pstmt = connection.prepareStatement(sql)) { + pstmt.setInt(1, a); + pstmt.setInt(2, b); + pstmt.setInt(3, c); + pstmt.setString(4, world); + int affected = pstmt.executeUpdate(); + return affected > 0; + } catch (SQLException e) { + plugin.getLogger().severe("Error removing location: " + e.getMessage()); + return false; + } + } + + public boolean clearAllLocations() { + String sql = "DELETE FROM locations"; + try (Statement stmt = connection.createStatement()) { + stmt.executeUpdate(sql); + return true; + } catch (SQLException e) { + plugin.getLogger().severe("Error clearing locations: " + e.getMessage()); + return false; + } + } + + public void close() { + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + plugin.getLogger().severe("Error closing SQLite connection: " + e.getMessage()); + } + } + } + + public boolean handleDisableLocation(CommandSender sender, String[] args) { + if (!sender.hasPermission("griefalert.staff.disable")) { + sender.sendMessage(ChatColor.RED + "You do not have permission to use this command."); + return true; + } + if (args.length != 4) { + sender.sendMessage(ChatColor.RED + "Usage: /disablelocation "); + return true; + } + String world = args[0]; + if (!world.equals("world_world") && !world.equals("world_nether") && !world.equals("world_end")) { + sender.sendMessage(ChatColor.RED + "Invalid world. Use one of: world_world, world_nether, world_end"); + return true; + } + int a, b, c; + try { + a = Integer.parseInt(args[1]); + b = Integer.parseInt(args[2]); + c = Integer.parseInt(args[3]); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be whole numbers."); + return true; + } + saveLocation(a, b, c, world); + sender.sendMessage(ChatColor.GREEN + "Location disabled for alerts at " + world + " X: " + a + ", Y: " + b + ", Z: " + c + "."); + return true; + } + + public boolean handleEnableLocation(CommandSender sender, String[] args) { + if (!sender.hasPermission("griefalert.staff.enable")) { + sender.sendMessage(ChatColor.RED + "You do not have permission to use this command."); + return true; + } + if (args.length != 4) { + sender.sendMessage(ChatColor.RED + "Usage: /enablelocation "); + return true; + } + String world = args[0]; + if (!world.equals("world_world") && !world.equals("world_nether") && !world.equals("world_end")) { + sender.sendMessage(ChatColor.RED + "Invalid world. Use one of: world_world, world_nether, world_end"); + return true; + } + int a, b, c; + try { + a = Integer.parseInt(args[1]); + b = Integer.parseInt(args[2]); + c = Integer.parseInt(args[3]); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be whole numbers."); + return true; + } + if (removeLocation(a, b, c, world)) { + sender.sendMessage(ChatColor.GREEN + "Location enabled for alerts at " + world + " X: " + a + ", Y: " + b + ", Z: " + c + "."); + } else { + sender.sendMessage(ChatColor.RED + "Location was not disabled or could not be found."); + } + return true; + } + + public boolean handleClearLocations(CommandSender sender, String[] args) { + if (!sender.hasPermission("griefalert.staff.clear")) { + sender.sendMessage(ChatColor.RED + "You do not have permission to use this command."); + return true; + } + if (args.length != 0) { + sender.sendMessage(ChatColor.RED + "Usage: /clearlocations"); + return true; + } + if (clearAllLocations()) { + sender.sendMessage(ChatColor.GREEN + "All disabled alert locations (a, b, c) have been cleared."); + } else { + sender.sendMessage(ChatColor.RED + "Failed to clear locations. Check console for errors."); + } + return true; + } + + public boolean handleCheckLocation(CommandSender sender, String[] args) { + if (!sender.hasPermission("griefalert.staff.check")) { + sender.sendMessage(ChatColor.RED + "You do not have permission to use this command."); + return true; + } + if (args.length != 4) { + sender.sendMessage(ChatColor.RED + "Usage: /checklocation "); + return true; + } + String world = args[0]; + if (!world.equals("world_world") && !world.equals("world_nether") && !world.equals("world_end")) { + sender.sendMessage(ChatColor.RED + "Invalid world. Use one of: world_world, world_nether, world_end"); + return true; + } + int a, b, c; + try { + a = Integer.parseInt(args[1]); + b = Integer.parseInt(args[2]); + c = Integer.parseInt(args[3]); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be whole numbers."); + return true; + } + if (isLocationSaved(a, b, c, world)) { + sender.sendMessage(ChatColor.YELLOW + "This block (X: " + a + ", Y: " + b + ", Z: " + c + ") has been disabled for alerts."); + } else { + sender.sendMessage(ChatColor.GREEN + "This block (X: " + a + ", Y: " + b + ", Z: " + c + ") is still enabled for alerts."); + } + return true; + } +} From b2284bbcef28f1dca9d135515475d7c977896524 Mon Sep 17 00:00:00 2001 From: Ardakaz Date: Sat, 24 May 2025 04:07:11 +0000 Subject: [PATCH 08/18] Updated version & website --- plugin.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin.yml b/plugin.yml index 88b89d9..84265cc 100755 --- a/plugin.yml +++ b/plugin.yml @@ -1,13 +1,13 @@ name: GriefAlert main: net.ardakaz.griefalert.GriefAlert -version: 0.2.1 +version: 0.3.1 api-version: 1.21 depends: [CoreProtect] description: A simple grief alert plugin using CoreProtect API. author: Ardakaz +website: "https://git.ardakaz.net/ArdakazMC/GriefAlert" #modified by kleedje30 version 0.4.2 - commands: disablelocation: description: Disable GriefAlert at a location From a288d261c157263bdabff8296f0161d5921af130 Mon Sep 17 00:00:00 2001 From: Ardakaz Date: Sat, 24 May 2025 04:12:44 +0000 Subject: [PATCH 09/18] Quick fix --- plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.yml b/plugin.yml index 84265cc..03e4931 100755 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: GriefAlert main: net.ardakaz.griefalert.GriefAlert -version: 0.3.1 +version: 0.3 api-version: 1.21 depends: [CoreProtect] description: A simple grief alert plugin using CoreProtect API. From 618d737203036ff5e1c70e546a1448d86f8c2db8 Mon Sep 17 00:00:00 2001 From: Kleedje30 Date: Wed, 28 May 2025 18:18:52 +0000 Subject: [PATCH 10/18] Upload files to "/" --- GriefAlert.java | 473 +++++++++++++++++++++++++++++++++++++++++++ GriefAlertEvent.java | 28 +++ config.yml | 0 plugin.yml | 41 ++-- 4 files changed, 515 insertions(+), 27 deletions(-) create mode 100644 GriefAlert.java create mode 100644 GriefAlertEvent.java mode change 100755 => 100644 config.yml mode change 100755 => 100644 plugin.yml diff --git a/GriefAlert.java b/GriefAlert.java new file mode 100644 index 0000000..e851671 --- /dev/null +++ b/GriefAlert.java @@ -0,0 +1,473 @@ +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 org.bukkit.World; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class GriefAlert extends JavaPlugin implements Listener, 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; + } + } + + private void clearIgnoredLocations() { + try { + Statement stmt = connection.createStatement(); + stmt.executeUpdate("DELETE FROM ignored_locations"); + stmt.close(); + } catch (SQLException e) { + getLogger().warning("DB error: " + e.getMessage()); + } + } + + private void listIgnoredLocations(CommandSender sender) { + try { + Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT x, y, z, world FROM ignored_locations"); + sender.sendMessage(ChatColor.YELLOW + "Ignored Locations:"); + while (rs.next()) { + sender.sendMessage(ChatColor.GRAY + "- " + rs.getInt("x") + ", " + rs.getInt("y") + ", " + rs.getInt("z") + " in " + rs.getString("world")); + } + rs.close(); + stmt.close(); + } catch (SQLException e) { + sender.sendMessage(ChatColor.RED + "DB error: " + e.getMessage()); + } + } + + @EventHandler (ignoreCancelled = true) + // Block break alerts + public void onBlockBreak(BlockBreakEvent event) { + // Exclusion list + if (EXCLUDED_BLOCKS.contains(event.getBlock().getType())) { + return; + } + + // 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")) { + getLogger().info("[DEBUG] /griefalert ignore called by " + sender.getName() + " with args: " + String.join(" ", args)); + if (!sender.hasPermission("griefalert.staff.ignore")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + getLogger().info("[DEBUG] Permission denied for ignore"); + return true; + } + if (args.length != 5) { + sender.sendMessage(ChatColor.RED + "Usage: /griefalert ignore "); + getLogger().info("[DEBUG] Incorrect number of arguments for ignore: " + args.length); + return true; + } + try { + int x = Integer.parseInt(args[1]); + int y = Integer.parseInt(args[2]); + int z = Integer.parseInt(args[3]); + String world = args[4]; + boolean result = addIgnoredLocation(x, y, z, world); + if (result) { + sender.sendMessage(ChatColor.GREEN + "Location ignored."); + getLogger().info("[DEBUG] Location ignored: " + x + "," + y + "," + z + "," + world); + } else { + sender.sendMessage(ChatColor.YELLOW + "Location was already ignored."); + getLogger().info("[DEBUG] Location already ignored: " + x + "," + y + "," + z + "," + world); + } + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + getLogger().info("[DEBUG] Invalid coordinates for ignore"); + } + return true; + } else if (sub.equals("unignore")) { + getLogger().info("[DEBUG] /griefalert unignore called by " + sender.getName() + " with args: " + String.join(" ", args)); + if (!sender.hasPermission("griefalert.staff.UnIgnore")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + getLogger().info("[DEBUG] Permission denied for unignore"); + return true; + } + if (args.length != 5) { + sender.sendMessage(ChatColor.RED + "Usage: /griefalert unignore "); + getLogger().info("[DEBUG] Incorrect number of arguments for unignore: " + args.length); + return true; + } + try { + int x = Integer.parseInt(args[1]); + int y = Integer.parseInt(args[2]); + int z = Integer.parseInt(args[3]); + String world = args[4]; + boolean result = removeIgnoredLocation(x, y, z, world); + if (result) { + sender.sendMessage(ChatColor.GREEN + "Location unignored."); + getLogger().info("[DEBUG] Location unignored: " + x + "," + y + "," + z + "," + world); + } else { + sender.sendMessage(ChatColor.YELLOW + "Location was not ignored."); + getLogger().info("[DEBUG] Location was not ignored: " + x + "," + y + "," + z + "," + world); + } + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + getLogger().info("[DEBUG] Invalid coordinates for unignore"); + } + return true; + } else if (sub.equals("list")) { + getLogger().info("[DEBUG] /griefalert list called by " + sender.getName()); + if (!sender.hasPermission("griefalert.staff.list")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + getLogger().info("[DEBUG] Permission denied for list"); + return true; + } + listIgnoredLocations(sender); + sender.sendMessage(ChatColor.GRAY + "[DEBUG] Ignored locations listed in chat and console."); + getLogger().info("[DEBUG] Ignored locations listed for " + sender.getName()); + return true; + } else if (sub.equals("clear")) { + getLogger().info("[DEBUG] /griefalert clear called by " + sender.getName()); + if (!sender.hasPermission("griefalert.staff.clear")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + getLogger().info("[DEBUG] Permission denied for clear"); + return true; + } + clearIgnoredLocations(); + sender.sendMessage(ChatColor.GREEN + "All ignored locations cleared."); + getLogger().info("[DEBUG] All ignored locations cleared by " + sender.getName()); + return true; + } + getLogger().info("[DEBUG] Unknown subcommand: " + sub); + sender.sendMessage(ChatColor.RED + "Unknown subcommand. Use: ignore, unignore, list, clear"); + return false; + } + + @Override + public java.util.List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + if (!command.getName().equalsIgnoreCase("griefalert")) return null; + java.util.List completions = new java.util.ArrayList<>(); + if (args.length == 1) { + java.util.List subs = java.util.Arrays.asList("ignore", "unignore", "list", "clear"); + for (String s : subs) { + if (s.startsWith(args[0].toLowerCase())) completions.add(s); + } + return completions; + } + if ((args[0].equalsIgnoreCase("ignore") || args[0].equalsIgnoreCase("unignore")) && args.length == 5) { + // Suggest world names for the 5th argument + for (World world : getServer().getWorlds()) { + if (world.getName().toLowerCase().startsWith(args[4].toLowerCase())) { + completions.add(world.getName()); + } + } + return completions; + } + return null; + } + + // Sends the alert (or cancels it) + private void alert(String message, String playerName, String mapLink, String target, int x, int y, int z, String world) { + // Exclude trusted people + Player griefer = Bukkit.getPlayer(playerName); + if (griefer.hasPermission("griefalert.exclude") || griefer.hasPermission("griefalert.exclude." + target)) { + return; + } + // Spam limiter + String realAlertMessage = message; + String[] alert1 = null; + if (lastAlert != null) { + alert1 = lastAlert.split(" "); + } + String[] alert2 = message.split(" "); + if (alert1 != null) { + if (alert1[2].equals(alert2[2]) && alert1[5].equals(alert2[5]) && alert1[1].equals("broke") && alert2[1].equals("broke")) { + identicalAlerts += 1; + } else if (Arrays.equals(alert1, alert2)) { + identicalAlerts += 1; + } else { + identicalAlerts = 1; + } + } + if (identicalAlerts == 4) { + message = ChatColor.GRAY + "Same behavior continues."; + mapLink = null; + } + if (identicalAlerts > 4) { + return; + } + // Use direct location check + if (world != null && isLocationIgnored(x, y, z, world)) { + return; // Do not alert if location is ignored + } + // Send an event for external hooks + GriefAlertEvent griefalert_event; + if (mapLink != null && !mapLink.isEmpty()) { + griefalert_event = new GriefAlertEvent(message + " (" + mapLink + ")"); + } else { + griefalert_event = new GriefAlertEvent(message); + } + getServer().getPluginManager().callEvent(griefalert_event); + // Notify staff ingame + for (Player player : Bukkit.getOnlinePlayers()) { + if (player.hasPermission("griefalert.notify")) { + player.sendMessage(message); + } + } + lastAlert = realAlertMessage; + } + + // Block inspector: 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..511595c --- /dev/null +++ b/GriefAlertEvent.java @@ -0,0 +1,28 @@ +// "API" for other plugins + +package net.ardakaz.griefalert; + +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; + } + + 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/config.yml b/config.yml old mode 100755 new mode 100644 diff --git a/plugin.yml b/plugin.yml old mode 100755 new mode 100644 index 03e4931..71f349b --- a/plugin.yml +++ b/plugin.yml @@ -1,27 +1,14 @@ -name: GriefAlert -main: net.ardakaz.griefalert.GriefAlert -version: 0.3 -api-version: 1.21 -depends: [CoreProtect] -description: A simple grief alert plugin using CoreProtect API. -author: Ardakaz -website: "https://git.ardakaz.net/ArdakazMC/GriefAlert" -#modified by kleedje30 version 0.4.2 - -commands: - disablelocation: - description: Disable GriefAlert at a location - usage: /disablelocation - permission: griefalert.staff.disable - enablelocation: - description: Enable GriefAlert at a location - usage: /enablelocation - permission: griefalert.staff.enable - clearlocations: - description: Clears all disabled alert locations from the database - usage: /clearlocations - permission: griefalert.staff.clear - checklocation: - description: Checks if a location is disabled for alerts - usage: /checklocation - permission: griefalert.staff.check \ No newline at end of file +name: GriefAlert +main: net.ardakaz.griefalert.GriefAlert +version: 0.2.1 +api-version: 1.21 +depends: [CoreProtect] +description: A simple grief alert plugin using CoreProtect API. +author: Ardakaz +#modified by Kleedje30 version 0.0.1 +commands: + griefalert: + description: GriefAlert staff commands + usage: / + permission: griefalert.staff + permission-message: You do not have permission to use this command. From c9ad3dd3a4f48d73973cb5f23b6f3f3ee76803ff Mon Sep 17 00:00:00 2001 From: Ardakaz Date: Wed, 28 May 2025 18:21:35 +0000 Subject: [PATCH 11/18] Moving to the right place --- src/net/ardakaz/griefalert/GriefAlert.java | 447 ++++++++++++++------- 1 file changed, 311 insertions(+), 136 deletions(-) diff --git a/src/net/ardakaz/griefalert/GriefAlert.java b/src/net/ardakaz/griefalert/GriefAlert.java index e5efe2b..a469dac 100755 --- a/src/net/ardakaz/griefalert/GriefAlert.java +++ b/src/net/ardakaz/griefalert/GriefAlert.java @@ -8,6 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.block.Block; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -15,30 +18,36 @@ import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.inventory.InventoryAction; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.World; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -public class GriefAlert extends JavaPlugin implements Listener { +public class GriefAlert extends JavaPlugin implements Listener, TabCompleter { private static CoreProtectAPI coreProtectAPI; private Integer identicalAlerts = 1; private String lastAlert; private Set EXCLUDED_BLOCKS; - private Set VALID_CONTAINERS; + private Set VALID_CONTAINERS; private String MAP_LINK; private Boolean ALLOW_STEALING; - private AlertsLogic alertsLogic; + private Connection connection; + private final String DB_FILE = "ignored_locations.db"; // Init GriefAlert @Override @@ -62,48 +71,114 @@ public class GriefAlert extends JavaPlugin implements Listener { MAP_LINK = getConfig().getString("map-link"); ALLOW_STEALING = getConfig().getBoolean("allow-stealing"); - getLogger().info("GriefAlert has been enabled."); + setupDatabase(); + + getCommand("griefalert").setTabCompleter(this); - // Initialize AlertsLogic and register commands - alertsLogic = new AlertsLogic(this); - getCommand("disablelocation").setExecutor(this); - getCommand("enablelocation").setExecutor(this); - getCommand("clearlocations").setExecutor(this); - getCommand("checklocation").setExecutor(this); + getLogger().info("GriefAlert has been enabled."); } @Override public void onDisable() { - if (alertsLogic != null) { - alertsLogic.close(); - } getLogger().info("GriefAlert has been disabled."); + if (connection != null) { + try { connection.close(); } catch (SQLException ignored) {} + } } - @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - String cmd = command.getName().toLowerCase(); - switch (cmd) { - case "disablelocation": - return alertsLogic.handleDisableLocation(sender, args); - case "enablelocation": - return alertsLogic.handleEnableLocation(sender, args); - case "clearlocations": - return alertsLogic.handleClearLocations(sender, args); - case "checklocation": - return alertsLogic.handleCheckLocation(sender, args); - default: - return false; + private void setupDatabase() { + try { + connection = DriverManager.getConnection("jdbc:sqlite:" + getDataFolder().getAbsolutePath() + "/" + DB_FILE); + Statement stmt = connection.createStatement(); + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ignored_locations (x INTEGER, y INTEGER, z INTEGER, world TEXT, PRIMARY KEY (x, y, z, world))"); + stmt.close(); + } catch (SQLException e) { + getLogger().severe("Could not set up SQLite database: " + e.getMessage()); + } + } + + private boolean isLocationIgnored(int x, int y, int z, String world) { + try { + PreparedStatement ps = connection.prepareStatement("SELECT 1 FROM ignored_locations WHERE x=? AND y=? AND z=? AND world=?"); + ps.setInt(1, x); + ps.setInt(2, y); + ps.setInt(3, z); + ps.setString(4, world); + ResultSet rs = ps.executeQuery(); + boolean exists = rs.next(); + rs.close(); + ps.close(); + return exists; + } catch (SQLException e) { + getLogger().warning("DB error: " + e.getMessage()); + return false; + } + } + + private boolean addIgnoredLocation(int x, int y, int z, String world) { + try { + PreparedStatement ps = connection.prepareStatement("INSERT OR IGNORE INTO ignored_locations (x, y, z, world) VALUES (?, ?, ?, ?)"); + ps.setInt(1, x); + ps.setInt(2, y); + ps.setInt(3, z); + ps.setString(4, world); + int updated = ps.executeUpdate(); + ps.close(); + return updated > 0; + } catch (SQLException e) { + getLogger().warning("DB error: " + e.getMessage()); + return false; + } + } + + private boolean removeIgnoredLocation(int x, int y, int z, String world) { + try { + PreparedStatement ps = connection.prepareStatement("DELETE FROM ignored_locations WHERE x=? AND y=? AND z=? AND world=?"); + ps.setInt(1, x); + ps.setInt(2, y); + ps.setInt(3, z); + ps.setString(4, world); + int updated = ps.executeUpdate(); + ps.close(); + return updated > 0; + } catch (SQLException e) { + getLogger().warning("DB error: " + e.getMessage()); + return false; + } + } + + private void clearIgnoredLocations() { + try { + Statement stmt = connection.createStatement(); + stmt.executeUpdate("DELETE FROM ignored_locations"); + stmt.close(); + } catch (SQLException e) { + getLogger().warning("DB error: " + e.getMessage()); + } + } + + private void listIgnoredLocations(CommandSender sender) { + try { + Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT x, y, z, world FROM ignored_locations"); + sender.sendMessage(ChatColor.YELLOW + "Ignored Locations:"); + while (rs.next()) { + sender.sendMessage(ChatColor.GRAY + "- " + rs.getInt("x") + ", " + rs.getInt("y") + ", " + rs.getInt("z") + " in " + rs.getString("world")); + } + rs.close(); + stmt.close(); + } catch (SQLException e) { + sender.sendMessage(ChatColor.RED + "DB error: " + e.getMessage()); } } @EventHandler (ignoreCancelled = true) // Block break alerts public void onBlockBreak(BlockBreakEvent event) { - // Exclusion list - if (EXCLUDED_BLOCKS.contains(event.getBlock().getType())) { - return; - } + // Exclusion list + if (EXCLUDED_BLOCKS.contains(event.getBlock().getType())) { + return; + } // Event parser String playerName = event.getPlayer().getName(); @@ -113,29 +188,23 @@ public class GriefAlert extends JavaPlugin implements Listener { int z = event.getBlock().getZ(); String worldName = event.getBlock().getWorld().getName(); - // Check if alerts are disabled for this location - if (alertsLogic != null && alertsLogic.isLocationSaved(x, y, z, worldName)) { - return; - } - // Check if grief String target = inspectBlock(event.getBlock(), event.getPlayer()); if (target != null) { - // Alert String message = ChatColor.GRAY + playerName + " broke " + blockType + " placed by " + target + " at " + x + " " + y + " " + z + getHumanWorldName(worldName); - alert(message, playerName, "[Map Link](" + MAP_LINK + "/?worldname=" + worldName + "&zoom=7&x=" + x + "&y=" + y + "&z=" + z + ")", target); + alert(message, playerName, "[Map Link](" + MAP_LINK + "/?worldname=" + worldName + "&zoom=7&x=" + x + "&y=" + y + "&z=" + z + ")", target, x, y, z, worldName); } } // Stealing alerts @EventHandler (ignoreCancelled = true) public void onInventoryClick(InventoryClickEvent event) { - if (ALLOW_STEALING) { - return; - } - boolean stealing; - - // Event parser for inv + if (ALLOW_STEALING) { + return; + } + boolean stealing; + + // Event parser for inv if (!(event.getWhoClicked() instanceof Player)) return; Player player = (Player) event.getWhoClicked(); Inventory inventory = event.getInventory(); @@ -143,22 +212,13 @@ public class GriefAlert extends JavaPlugin implements Listener { ItemStack item = event.getCurrentItem(); if (item == null || inventory.getLocation() == null || item.getType() == Material.AIR) { - return; + return; } // Exclusion list - if (!VALID_CONTAINERS.contains(inventory.getType())) { - return; - } - - // Check if alerts are disabled for this location - int x = inventory.getLocation().getBlockX(); - int y = inventory.getLocation().getBlockY(); - int z = inventory.getLocation().getBlockZ(); - String worldName = inventory.getLocation().getWorld().getName(); - if (alertsLogic != null && alertsLogic.isLocationSaved(x, y, z, worldName)) { - return; - } + if (!VALID_CONTAINERS.contains(inventory.getType())) { + return; + } // Inv actions (needs fixing) InventoryAction action = event.getAction(); @@ -168,109 +228,224 @@ public class GriefAlert extends JavaPlugin implements Listener { stealing = true; } else if (action == InventoryAction.PLACE_ALL || action == InventoryAction.PLACE_SOME || action == InventoryAction.PLACE_ONE || (action == InventoryAction.MOVE_TO_OTHER_INVENTORY && clickedInventory != inventory)) { - stealing = false; + stealing = false; } else { - return; + return; } // Event parser for container + check if grief String target = inspectBlock(inventory.getLocation().getBlock(), player); if (target != null) { - String playerName = player.getName(); + String playerName = player.getName(); String itemName = item.getType().toString(); int amount = item.getAmount(); - // x, y, z, worldName already defined above - + int x = inventory.getLocation().getBlockX(); + int y = inventory.getLocation().getBlockY(); + int z = inventory.getLocation().getBlockZ(); + String worldName = inventory.getLocation().getWorld().getName(); if (stealing) { - // Stealing String message = ChatColor.GRAY + playerName + " took " + amount + " " + itemName + " from " + target + "'s container at " + x + " " + y + " " + z + getHumanWorldName(worldName); - alert(message, playerName, "[Map Link](" + MAP_LINK + "/?worldname=" + worldName + "&zoom=7&x=" + x + "&y=" + y + "&z=" + z + ")", target); + alert(message, playerName, "[Map Link](" + MAP_LINK + "/?worldname=" + worldName + "&zoom=7&x=" + x + "&y=" + y + "&z=" + z + ")", target, x, y, z, worldName); } else { - // Putting back String message = ChatColor.GRAY + playerName + " put " + amount + " " + itemName + " into " + target + "'s container at " + x + " " + y + " " + z + getHumanWorldName(worldName); - alert(message, playerName, "[Map Link](" + MAP_LINK + "/?worldname=" + worldName + "&zoom=7&x=" + x + "&y=" + y + "&z=" + z + ")", target); + alert(message, playerName, "[Map Link](" + MAP_LINK + "/?worldname=" + worldName + "&zoom=7&x=" + x + "&y=" + y + "&z=" + z + ")", target, x, y, z, worldName); } } } + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!command.getName().equalsIgnoreCase("griefalert")) return false; + if (args.length == 0) return false; + String sub = args[0].toLowerCase(); + if (sub.equals("ignore")) { + getLogger().info("[DEBUG] /griefalert ignore called by " + sender.getName() + " with args: " + String.join(" ", args)); + if (!sender.hasPermission("griefalert.staff.ignore")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + getLogger().info("[DEBUG] Permission denied for ignore"); + return true; + } + if (args.length != 5) { + sender.sendMessage(ChatColor.RED + "Usage: /griefalert ignore "); + getLogger().info("[DEBUG] Incorrect number of arguments for ignore: " + args.length); + return true; + } + try { + int x = Integer.parseInt(args[1]); + int y = Integer.parseInt(args[2]); + int z = Integer.parseInt(args[3]); + String world = args[4]; + boolean result = addIgnoredLocation(x, y, z, world); + if (result) { + sender.sendMessage(ChatColor.GREEN + "Location ignored."); + getLogger().info("[DEBUG] Location ignored: " + x + "," + y + "," + z + "," + world); + } else { + sender.sendMessage(ChatColor.YELLOW + "Location was already ignored."); + getLogger().info("[DEBUG] Location already ignored: " + x + "," + y + "," + z + "," + world); + } + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + getLogger().info("[DEBUG] Invalid coordinates for ignore"); + } + return true; + } else if (sub.equals("unignore")) { + getLogger().info("[DEBUG] /griefalert unignore called by " + sender.getName() + " with args: " + String.join(" ", args)); + if (!sender.hasPermission("griefalert.staff.UnIgnore")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + getLogger().info("[DEBUG] Permission denied for unignore"); + return true; + } + if (args.length != 5) { + sender.sendMessage(ChatColor.RED + "Usage: /griefalert unignore "); + getLogger().info("[DEBUG] Incorrect number of arguments for unignore: " + args.length); + return true; + } + try { + int x = Integer.parseInt(args[1]); + int y = Integer.parseInt(args[2]); + int z = Integer.parseInt(args[3]); + String world = args[4]; + boolean result = removeIgnoredLocation(x, y, z, world); + if (result) { + sender.sendMessage(ChatColor.GREEN + "Location unignored."); + getLogger().info("[DEBUG] Location unignored: " + x + "," + y + "," + z + "," + world); + } else { + sender.sendMessage(ChatColor.YELLOW + "Location was not ignored."); + getLogger().info("[DEBUG] Location was not ignored: " + x + "," + y + "," + z + "," + world); + } + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + getLogger().info("[DEBUG] Invalid coordinates for unignore"); + } + return true; + } else if (sub.equals("list")) { + getLogger().info("[DEBUG] /griefalert list called by " + sender.getName()); + if (!sender.hasPermission("griefalert.staff.list")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + getLogger().info("[DEBUG] Permission denied for list"); + return true; + } + listIgnoredLocations(sender); + sender.sendMessage(ChatColor.GRAY + "[DEBUG] Ignored locations listed in chat and console."); + getLogger().info("[DEBUG] Ignored locations listed for " + sender.getName()); + return true; + } else if (sub.equals("clear")) { + getLogger().info("[DEBUG] /griefalert clear called by " + sender.getName()); + if (!sender.hasPermission("griefalert.staff.clear")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + getLogger().info("[DEBUG] Permission denied for clear"); + return true; + } + clearIgnoredLocations(); + sender.sendMessage(ChatColor.GREEN + "All ignored locations cleared."); + getLogger().info("[DEBUG] All ignored locations cleared by " + sender.getName()); + return true; + } + getLogger().info("[DEBUG] Unknown subcommand: " + sub); + sender.sendMessage(ChatColor.RED + "Unknown subcommand. Use: ignore, unignore, list, clear"); + return false; + } + + @Override + public java.util.List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + if (!command.getName().equalsIgnoreCase("griefalert")) return null; + java.util.List completions = new java.util.ArrayList<>(); + if (args.length == 1) { + java.util.List subs = java.util.Arrays.asList("ignore", "unignore", "list", "clear"); + for (String s : subs) { + if (s.startsWith(args[0].toLowerCase())) completions.add(s); + } + return completions; + } + if ((args[0].equalsIgnoreCase("ignore") || args[0].equalsIgnoreCase("unignore")) && args.length == 5) { + // Suggest world names for the 5th argument + for (World world : getServer().getWorlds()) { + if (world.getName().toLowerCase().startsWith(args[4].toLowerCase())) { + completions.add(world.getName()); + } + } + return completions; + } + return null; + } + // Sends the alert (or cancels it) - private void alert(String message, String playerName, String mapLink, String target) { - // Exclude trusted people - Player griefer = Bukkit.getPlayer(playerName); - if (griefer.hasPermission("griefalert.exclude") || griefer.hasPermission("griefalert.exclude." + target)) { - return; - } - - // Spam limiter - String realAlertMessage = message; - String[] alert1 = null; - if (lastAlert != null) { - alert1 = lastAlert.split(" "); - } - String[] alert2 = message.split(" "); - - if (alert1 != null) { - if (alert1[2].equals(alert2[2]) && alert1[5].equals(alert2[5]) && alert1[1].equals("broke") && alert2[1].equals("broke")) { - identicalAlerts += 1; - } - else if (Arrays.equals(alert1, alert2)) { - identicalAlerts += 1; - } - else { - identicalAlerts = 1; - } - } - - if (identicalAlerts == 4) { - message = ChatColor.GRAY + "Same behavior continues."; - mapLink = null; - } - - if (identicalAlerts > 4) { - return; - } - - // Send an event for external hooks - GriefAlertEvent griefalert_event; - if (mapLink != null && !mapLink.isEmpty()) { - griefalert_event = new GriefAlertEvent(message + " (" + mapLink + ")"); - } - else { - griefalert_event = new GriefAlertEvent(message); - } - getServer().getPluginManager().callEvent(griefalert_event); - - // Notify staff ingame - for (Player player : Bukkit.getOnlinePlayers()) { - if (player.hasPermission("griefalert.notify")) { - player.sendMessage(message); + private void alert(String message, String playerName, String mapLink, String target, int x, int y, int z, String world) { + // Exclude trusted people + Player griefer = Bukkit.getPlayer(playerName); + if (griefer.hasPermission("griefalert.exclude") || griefer.hasPermission("griefalert.exclude." + target)) { + return; + } + // Spam limiter + String realAlertMessage = message; + String[] alert1 = null; + if (lastAlert != null) { + alert1 = lastAlert.split(" "); + } + String[] alert2 = message.split(" "); + if (alert1 != null) { + if (alert1[2].equals(alert2[2]) && alert1[5].equals(alert2[5]) && alert1[1].equals("broke") && alert2[1].equals("broke")) { + identicalAlerts += 1; + } else if (Arrays.equals(alert1, alert2)) { + identicalAlerts += 1; + } else { + identicalAlerts = 1; + } + } + if (identicalAlerts == 4) { + message = ChatColor.GRAY + "Same behavior continues."; + mapLink = null; + } + if (identicalAlerts > 4) { + return; + } + // Use direct location check + if (world != null && isLocationIgnored(x, y, z, world)) { + return; // Do not alert if location is ignored + } + // Send an event for external hooks + GriefAlertEvent griefalert_event; + if (mapLink != null && !mapLink.isEmpty()) { + griefalert_event = new GriefAlertEvent(message + " (" + mapLink + ")"); + } else { + griefalert_event = new GriefAlertEvent(message); + } + getServer().getPluginManager().callEvent(griefalert_event); + // Notify staff ingame + for (Player player : Bukkit.getOnlinePlayers()) { + if (player.hasPermission("griefalert.notify")) { + player.sendMessage(message); } } - lastAlert = realAlertMessage; } - // Block inspector: if the block was placed by another player, returns their name. + // Block inspector: only the most recent placement counts for ownership. private static String inspectBlock(Block block, Player player) { List lookup = coreProtectAPI.blockLookup(block, 50000000); - if (lookup == null || lookup.size() <= 0) { - // Natural block - return null; - } - - String[] result = lookup.get(0); - ParseResult parseResult = coreProtectAPI.parseResult(result); - if (parseResult.isRolledBack() && lookup.size() != 1) { - result = lookup.get(1); - parseResult = coreProtectAPI.parseResult(result); - } - - if (result == null || parseResult == null || parseResult.getPlayer().startsWith("#") || parseResult.getPlayer().equals(player.getName())) { - // Placed by breaker or natural event + if (lookup == null || lookup.size() == 0) { + // Natural block return null; } - return parseResult.getPlayer(); - + // Find the most recent placement event only + for (String[] result : lookup) { + ParseResult parseResult = coreProtectAPI.parseResult(result); + if (parseResult == null) continue; + if (parseResult.getActionId() == 1 && !parseResult.isRolledBack() && !parseResult.getPlayer().startsWith("#")) { + // If the current player placed it, it's theirs (no alert) + if (parseResult.getPlayer().equals(player.getName())) { + return null; + } else { + return parseResult.getPlayer(); + } + } + // If we see a break before a placement, stop (block is gone) + if (parseResult.getActionId() == 0) { + break; + } + } + // No valid placement found + return null; } private static String getHumanWorldName(String worldName) { From 4b4820bf1774c9d69bb97598ca81ee677c014394 Mon Sep 17 00:00:00 2001 From: Ardakaz Date: Wed, 28 May 2025 18:21:56 +0000 Subject: [PATCH 12/18] Moving to the right place --- GriefAlert.java | 473 ------------------------------------------------ 1 file changed, 473 deletions(-) delete mode 100644 GriefAlert.java diff --git a/GriefAlert.java b/GriefAlert.java deleted file mode 100644 index e851671..0000000 --- a/GriefAlert.java +++ /dev/null @@ -1,473 +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 org.bukkit.World; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Arrays; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -public class GriefAlert extends JavaPlugin implements Listener, 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; - } - } - - private void clearIgnoredLocations() { - try { - Statement stmt = connection.createStatement(); - stmt.executeUpdate("DELETE FROM ignored_locations"); - stmt.close(); - } catch (SQLException e) { - getLogger().warning("DB error: " + e.getMessage()); - } - } - - private void listIgnoredLocations(CommandSender sender) { - try { - Statement stmt = connection.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT x, y, z, world FROM ignored_locations"); - sender.sendMessage(ChatColor.YELLOW + "Ignored Locations:"); - while (rs.next()) { - sender.sendMessage(ChatColor.GRAY + "- " + rs.getInt("x") + ", " + rs.getInt("y") + ", " + rs.getInt("z") + " in " + rs.getString("world")); - } - rs.close(); - stmt.close(); - } catch (SQLException e) { - sender.sendMessage(ChatColor.RED + "DB error: " + e.getMessage()); - } - } - - @EventHandler (ignoreCancelled = true) - // Block break alerts - public void onBlockBreak(BlockBreakEvent event) { - // Exclusion list - if (EXCLUDED_BLOCKS.contains(event.getBlock().getType())) { - return; - } - - // 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")) { - getLogger().info("[DEBUG] /griefalert ignore called by " + sender.getName() + " with args: " + String.join(" ", args)); - if (!sender.hasPermission("griefalert.staff.ignore")) { - sender.sendMessage(ChatColor.RED + "You do not have permission."); - getLogger().info("[DEBUG] Permission denied for ignore"); - return true; - } - if (args.length != 5) { - sender.sendMessage(ChatColor.RED + "Usage: /griefalert ignore "); - getLogger().info("[DEBUG] Incorrect number of arguments for ignore: " + args.length); - return true; - } - try { - int x = Integer.parseInt(args[1]); - int y = Integer.parseInt(args[2]); - int z = Integer.parseInt(args[3]); - String world = args[4]; - boolean result = addIgnoredLocation(x, y, z, world); - if (result) { - sender.sendMessage(ChatColor.GREEN + "Location ignored."); - getLogger().info("[DEBUG] Location ignored: " + x + "," + y + "," + z + "," + world); - } else { - sender.sendMessage(ChatColor.YELLOW + "Location was already ignored."); - getLogger().info("[DEBUG] Location already ignored: " + x + "," + y + "," + z + "," + world); - } - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); - getLogger().info("[DEBUG] Invalid coordinates for ignore"); - } - return true; - } else if (sub.equals("unignore")) { - getLogger().info("[DEBUG] /griefalert unignore called by " + sender.getName() + " with args: " + String.join(" ", args)); - if (!sender.hasPermission("griefalert.staff.UnIgnore")) { - sender.sendMessage(ChatColor.RED + "You do not have permission."); - getLogger().info("[DEBUG] Permission denied for unignore"); - return true; - } - if (args.length != 5) { - sender.sendMessage(ChatColor.RED + "Usage: /griefalert unignore "); - getLogger().info("[DEBUG] Incorrect number of arguments for unignore: " + args.length); - return true; - } - try { - int x = Integer.parseInt(args[1]); - int y = Integer.parseInt(args[2]); - int z = Integer.parseInt(args[3]); - String world = args[4]; - boolean result = removeIgnoredLocation(x, y, z, world); - if (result) { - sender.sendMessage(ChatColor.GREEN + "Location unignored."); - getLogger().info("[DEBUG] Location unignored: " + x + "," + y + "," + z + "," + world); - } else { - sender.sendMessage(ChatColor.YELLOW + "Location was not ignored."); - getLogger().info("[DEBUG] Location was not ignored: " + x + "," + y + "," + z + "," + world); - } - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); - getLogger().info("[DEBUG] Invalid coordinates for unignore"); - } - return true; - } else if (sub.equals("list")) { - getLogger().info("[DEBUG] /griefalert list called by " + sender.getName()); - if (!sender.hasPermission("griefalert.staff.list")) { - sender.sendMessage(ChatColor.RED + "You do not have permission."); - getLogger().info("[DEBUG] Permission denied for list"); - return true; - } - listIgnoredLocations(sender); - sender.sendMessage(ChatColor.GRAY + "[DEBUG] Ignored locations listed in chat and console."); - getLogger().info("[DEBUG] Ignored locations listed for " + sender.getName()); - return true; - } else if (sub.equals("clear")) { - getLogger().info("[DEBUG] /griefalert clear called by " + sender.getName()); - if (!sender.hasPermission("griefalert.staff.clear")) { - sender.sendMessage(ChatColor.RED + "You do not have permission."); - getLogger().info("[DEBUG] Permission denied for clear"); - return true; - } - clearIgnoredLocations(); - sender.sendMessage(ChatColor.GREEN + "All ignored locations cleared."); - getLogger().info("[DEBUG] All ignored locations cleared by " + sender.getName()); - return true; - } - getLogger().info("[DEBUG] Unknown subcommand: " + sub); - sender.sendMessage(ChatColor.RED + "Unknown subcommand. Use: ignore, unignore, list, clear"); - return false; - } - - @Override - public java.util.List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { - if (!command.getName().equalsIgnoreCase("griefalert")) return null; - java.util.List completions = new java.util.ArrayList<>(); - if (args.length == 1) { - java.util.List subs = java.util.Arrays.asList("ignore", "unignore", "list", "clear"); - for (String s : subs) { - if (s.startsWith(args[0].toLowerCase())) completions.add(s); - } - return completions; - } - if ((args[0].equalsIgnoreCase("ignore") || args[0].equalsIgnoreCase("unignore")) && args.length == 5) { - // Suggest world names for the 5th argument - for (World world : getServer().getWorlds()) { - if (world.getName().toLowerCase().startsWith(args[4].toLowerCase())) { - completions.add(world.getName()); - } - } - return completions; - } - return null; - } - - // Sends the alert (or cancels it) - private void alert(String message, String playerName, String mapLink, String target, int x, int y, int z, String world) { - // Exclude trusted people - Player griefer = Bukkit.getPlayer(playerName); - if (griefer.hasPermission("griefalert.exclude") || griefer.hasPermission("griefalert.exclude." + target)) { - return; - } - // Spam limiter - String realAlertMessage = message; - String[] alert1 = null; - if (lastAlert != null) { - alert1 = lastAlert.split(" "); - } - String[] alert2 = message.split(" "); - if (alert1 != null) { - if (alert1[2].equals(alert2[2]) && alert1[5].equals(alert2[5]) && alert1[1].equals("broke") && alert2[1].equals("broke")) { - identicalAlerts += 1; - } else if (Arrays.equals(alert1, alert2)) { - identicalAlerts += 1; - } else { - identicalAlerts = 1; - } - } - if (identicalAlerts == 4) { - message = ChatColor.GRAY + "Same behavior continues."; - mapLink = null; - } - if (identicalAlerts > 4) { - return; - } - // Use direct location check - if (world != null && isLocationIgnored(x, y, z, world)) { - return; // Do not alert if location is ignored - } - // Send an event for external hooks - GriefAlertEvent griefalert_event; - if (mapLink != null && !mapLink.isEmpty()) { - griefalert_event = new GriefAlertEvent(message + " (" + mapLink + ")"); - } else { - griefalert_event = new GriefAlertEvent(message); - } - getServer().getPluginManager().callEvent(griefalert_event); - // Notify staff ingame - for (Player player : Bukkit.getOnlinePlayers()) { - if (player.hasPermission("griefalert.notify")) { - player.sendMessage(message); - } - } - lastAlert = realAlertMessage; - } - - // Block inspector: 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 From 38df0f7e825712349caa22614fc9110997455149 Mon Sep 17 00:00:00 2001 From: Ardakaz Date: Wed, 28 May 2025 18:22:33 +0000 Subject: [PATCH 13/18] Moving to the right place --- GriefAlertEvent.java | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 GriefAlertEvent.java diff --git a/GriefAlertEvent.java b/GriefAlertEvent.java deleted file mode 100644 index 511595c..0000000 --- a/GriefAlertEvent.java +++ /dev/null @@ -1,28 +0,0 @@ -// "API" for other plugins - -package net.ardakaz.griefalert; - -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; - } - - public static HandlerList getHandlerList() { - return HANDLERS; - } - - @Override - public HandlerList getHandlers() { - return HANDLERS; - } - - public String getAlert() { - return this.alert; - } -} \ No newline at end of file From 7725974444e4ada0e0a290997ebd0b1a62ead5d0 Mon Sep 17 00:00:00 2001 From: Ardakaz Date: Wed, 28 May 2025 18:24:56 +0000 Subject: [PATCH 14/18] Update plugin.yml --- plugin.yml | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/plugin.yml b/plugin.yml index 71f349b..2024b00 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,14 +1,13 @@ -name: GriefAlert -main: net.ardakaz.griefalert.GriefAlert -version: 0.2.1 -api-version: 1.21 -depends: [CoreProtect] -description: A simple grief alert plugin using CoreProtect API. -author: Ardakaz -#modified by Kleedje30 version 0.0.1 -commands: - griefalert: - description: GriefAlert staff commands - usage: / - permission: griefalert.staff - permission-message: You do not have permission to use this command. +name: GriefAlert +main: net.ardakaz.griefalert.GriefAlert +version: 0.3 +api-version: 1.21 +depends: [CoreProtect] +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. From 3e0818bef92405d20d5aa07c1b405a8fc3755e82 Mon Sep 17 00:00:00 2001 From: Ardakaz Date: Wed, 28 May 2025 18:41:03 +0000 Subject: [PATCH 15/18] No need --- src/net/ardakaz/griefalert/AlertsLogic.java | 211 -------------------- 1 file changed, 211 deletions(-) delete mode 100644 src/net/ardakaz/griefalert/AlertsLogic.java diff --git a/src/net/ardakaz/griefalert/AlertsLogic.java b/src/net/ardakaz/griefalert/AlertsLogic.java deleted file mode 100644 index c0aea39..0000000 --- a/src/net/ardakaz/griefalert/AlertsLogic.java +++ /dev/null @@ -1,211 +0,0 @@ -/*this SHOULD handle checking the alert for example: -if a block is broken at world 10 10 10 and the location has been disabled -then it should return null and cancle actualy giving an alert -(if location is enabled the alert will just alert) -and it SHOULD handle the logic and processing of /disable or /enable -and the SQL database logic -*/ -package net.ardakaz.griefalert; - -import java.sql.*; -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.command.CommandSender; -import org.bukkit.ChatColor; - -public class AlertsLogic { - private final String DB_URL = "jdbc:sqlite:plugins/GriefAlert/locations.db"; - private Connection connection; - private final JavaPlugin plugin; - - public AlertsLogic(JavaPlugin plugin) { - this.plugin = plugin; - try { - connectDatabase(); - createLocationsTable(); - } catch (SQLException e) { - plugin.getLogger().severe("Could not initialize SQLite database: " + e.getMessage()); - } - } - - private void connectDatabase() throws SQLException { - connection = DriverManager.getConnection(DB_URL); - } - - private void createLocationsTable() throws SQLException { - String sql = "CREATE TABLE IF NOT EXISTS locations (id INTEGER PRIMARY KEY AUTOINCREMENT, a INTEGER, b INTEGER, c INTEGER, world TEXT)"; - try (Statement stmt = connection.createStatement()) { - stmt.execute(sql); - } - } - - public void saveLocation(int a, int b, int c, String world) { - String sql = "INSERT INTO locations (a, b, c, world) VALUES (?, ?, ?, ?)"; - try (PreparedStatement pstmt = connection.prepareStatement(sql)) { - pstmt.setInt(1, a); - pstmt.setInt(2, b); - pstmt.setInt(3, c); - pstmt.setString(4, world); - pstmt.executeUpdate(); - } catch (SQLException e) { - plugin.getLogger().severe("Error saving location: " + e.getMessage()); - } - } - - public boolean isLocationSaved(int a, int b, int c, String world) { - String sql = "SELECT id FROM locations WHERE a = ? AND b = ? AND c = ? AND world = ?"; - try (PreparedStatement pstmt = connection.prepareStatement(sql)) { - pstmt.setInt(1, a); - pstmt.setInt(2, b); - pstmt.setInt(3, c); - pstmt.setString(4, world); - ResultSet rs = pstmt.executeQuery(); - return rs.next(); - } catch (SQLException e) { - plugin.getLogger().severe("Error querying location: " + e.getMessage()); - return false; - } - } - - public boolean removeLocation(int a, int b, int c, String world) { - String sql = "DELETE FROM locations WHERE a = ? AND b = ? AND c = ? AND world = ?"; - try (PreparedStatement pstmt = connection.prepareStatement(sql)) { - pstmt.setInt(1, a); - pstmt.setInt(2, b); - pstmt.setInt(3, c); - pstmt.setString(4, world); - int affected = pstmt.executeUpdate(); - return affected > 0; - } catch (SQLException e) { - plugin.getLogger().severe("Error removing location: " + e.getMessage()); - return false; - } - } - - public boolean clearAllLocations() { - String sql = "DELETE FROM locations"; - try (Statement stmt = connection.createStatement()) { - stmt.executeUpdate(sql); - return true; - } catch (SQLException e) { - plugin.getLogger().severe("Error clearing locations: " + e.getMessage()); - return false; - } - } - - public void close() { - if (connection != null) { - try { - connection.close(); - } catch (SQLException e) { - plugin.getLogger().severe("Error closing SQLite connection: " + e.getMessage()); - } - } - } - - public boolean handleDisableLocation(CommandSender sender, String[] args) { - if (!sender.hasPermission("griefalert.staff.disable")) { - sender.sendMessage(ChatColor.RED + "You do not have permission to use this command."); - return true; - } - if (args.length != 4) { - sender.sendMessage(ChatColor.RED + "Usage: /disablelocation "); - return true; - } - String world = args[0]; - if (!world.equals("world_world") && !world.equals("world_nether") && !world.equals("world_end")) { - sender.sendMessage(ChatColor.RED + "Invalid world. Use one of: world_world, world_nether, world_end"); - return true; - } - int a, b, c; - try { - a = Integer.parseInt(args[1]); - b = Integer.parseInt(args[2]); - c = Integer.parseInt(args[3]); - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be whole numbers."); - return true; - } - saveLocation(a, b, c, world); - sender.sendMessage(ChatColor.GREEN + "Location disabled for alerts at " + world + " X: " + a + ", Y: " + b + ", Z: " + c + "."); - return true; - } - - public boolean handleEnableLocation(CommandSender sender, String[] args) { - if (!sender.hasPermission("griefalert.staff.enable")) { - sender.sendMessage(ChatColor.RED + "You do not have permission to use this command."); - return true; - } - if (args.length != 4) { - sender.sendMessage(ChatColor.RED + "Usage: /enablelocation "); - return true; - } - String world = args[0]; - if (!world.equals("world_world") && !world.equals("world_nether") && !world.equals("world_end")) { - sender.sendMessage(ChatColor.RED + "Invalid world. Use one of: world_world, world_nether, world_end"); - return true; - } - int a, b, c; - try { - a = Integer.parseInt(args[1]); - b = Integer.parseInt(args[2]); - c = Integer.parseInt(args[3]); - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be whole numbers."); - return true; - } - if (removeLocation(a, b, c, world)) { - sender.sendMessage(ChatColor.GREEN + "Location enabled for alerts at " + world + " X: " + a + ", Y: " + b + ", Z: " + c + "."); - } else { - sender.sendMessage(ChatColor.RED + "Location was not disabled or could not be found."); - } - return true; - } - - public boolean handleClearLocations(CommandSender sender, String[] args) { - if (!sender.hasPermission("griefalert.staff.clear")) { - sender.sendMessage(ChatColor.RED + "You do not have permission to use this command."); - return true; - } - if (args.length != 0) { - sender.sendMessage(ChatColor.RED + "Usage: /clearlocations"); - return true; - } - if (clearAllLocations()) { - sender.sendMessage(ChatColor.GREEN + "All disabled alert locations (a, b, c) have been cleared."); - } else { - sender.sendMessage(ChatColor.RED + "Failed to clear locations. Check console for errors."); - } - return true; - } - - public boolean handleCheckLocation(CommandSender sender, String[] args) { - if (!sender.hasPermission("griefalert.staff.check")) { - sender.sendMessage(ChatColor.RED + "You do not have permission to use this command."); - return true; - } - if (args.length != 4) { - sender.sendMessage(ChatColor.RED + "Usage: /checklocation "); - return true; - } - String world = args[0]; - if (!world.equals("world_world") && !world.equals("world_nether") && !world.equals("world_end")) { - sender.sendMessage(ChatColor.RED + "Invalid world. Use one of: world_world, world_nether, world_end"); - return true; - } - int a, b, c; - try { - a = Integer.parseInt(args[1]); - b = Integer.parseInt(args[2]); - c = Integer.parseInt(args[3]); - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be whole numbers."); - return true; - } - if (isLocationSaved(a, b, c, world)) { - sender.sendMessage(ChatColor.YELLOW + "This block (X: " + a + ", Y: " + b + ", Z: " + c + ") has been disabled for alerts."); - } else { - sender.sendMessage(ChatColor.GREEN + "This block (X: " + a + ", Y: " + b + ", Z: " + c + ") is still enabled for alerts."); - } - return true; - } -} From 9549c1854635ec5fcfbbba1801c80cc80cc64a9e Mon Sep 17 00:00:00 2001 From: Kleedje30 Date: Wed, 28 May 2025 20:29:16 +0000 Subject: [PATCH 16/18] Upload files to "/" removed debug changed needed to input world --- GriefAlert.java | 432 +++++++++++++++++++++++++++++++++++++++++++ GriefAlertEvent.java | 28 +++ 2 files changed, 460 insertions(+) create mode 100644 GriefAlert.java create mode 100644 GriefAlertEvent.java diff --git a/GriefAlert.java b/GriefAlert.java new file mode 100644 index 0000000..d9e7c13 --- /dev/null +++ b/GriefAlert.java @@ -0,0 +1,432 @@ +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.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 { + + 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; + } + } + + private void listIgnoredLocations(CommandSender sender) { + try { + Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT x, y, z, world FROM ignored_locations"); + sender.sendMessage(ChatColor.YELLOW + "Ignored Locations:"); + while (rs.next()) { + sender.sendMessage(ChatColor.GRAY + "- " + rs.getInt("x") + ", " + rs.getInt("y") + ", " + rs.getInt("z") + " in " + rs.getString("world")); + } + rs.close(); + stmt.close(); + } catch (SQLException e) { + sender.sendMessage(ChatColor.RED + "DB error: " + e.getMessage()); + } + } + + @EventHandler (ignoreCancelled = true) + // Block break alerts + public void onBlockBreak(BlockBreakEvent event) { + // Exclusion list + if (EXCLUDED_BLOCKS.contains(event.getBlock().getType())) { + return; + } + + // 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; + } + if (args.length != 4) { + sender.sendMessage(ChatColor.RED + "Usage: /griefalert ignore "); + return true; + } + if (!(sender instanceof Player)) { + sender.sendMessage(ChatColor.RED + "Only players can use this command without specifying a world."); + return true; + } + try { + int x = Integer.parseInt(args[1]); + int y = Integer.parseInt(args[2]); + int z = Integer.parseInt(args[3]); + String world = ((Player)sender).getWorld().getName(); + boolean result = addIgnoredLocation(x, y, z, world); + if (result) { + sender.sendMessage(ChatColor.GREEN + "Location ignored."); + } else { + sender.sendMessage(ChatColor.YELLOW + "Location was already ignored."); + } + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + } + return true; + } else if (sub.equals("unignore")) { + if (!sender.hasPermission("griefalert.staff.UnIgnore")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + return true; + } + if (args.length != 4) { + sender.sendMessage(ChatColor.RED + "Usage: /griefalert unignore "); + return true; + } + if (!(sender instanceof Player)) { + sender.sendMessage(ChatColor.RED + "Only players can use this command without specifying a world."); + return true; + } + try { + int x = Integer.parseInt(args[1]); + int y = Integer.parseInt(args[2]); + int z = Integer.parseInt(args[3]); + String world = ((Player)sender).getWorld().getName(); + boolean result = removeIgnoredLocation(x, y, z, world); + if (result) { + sender.sendMessage(ChatColor.GREEN + "Location unignored."); + } else { + sender.sendMessage(ChatColor.YELLOW + "Location was not ignored."); + } + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + } + return true; + } else if (sub.equals("list")) { + if (!sender.hasPermission("griefalert.staff.list")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + return true; + } + listIgnoredLocations(sender); + return true; + } + sender.sendMessage(ChatColor.RED + "Unknown subcommand. Use: ignore, unignore, list"); + return false; + } + + @Override + public java.util.List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + if (!command.getName().equalsIgnoreCase("griefalert")) return null; + java.util.List completions = new java.util.ArrayList<>(); + if (args.length == 1) { + java.util.List subs = java.util.Arrays.asList("ignore", "unignore", "list"); + for (String s : subs) { + if (s.startsWith(args[0].toLowerCase())) completions.add(s); + } + return completions; + } + // No world argument needed anymore + 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 (mapLink != null && !mapLink.isEmpty()) { + griefalert_event = new GriefAlertEvent(message + " (" + mapLink + ")"); + } else { + griefalert_event = new GriefAlertEvent(message); + } + getServer().getPluginManager().callEvent(griefalert_event); + // Notify staff ingame + for (Player player : Bukkit.getOnlinePlayers()) { + if (player.hasPermission("griefalert.notify")) { + player.sendMessage(message); + } + } + lastAlert = realAlertMessage; + } + + // Block inspector: 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..511595c --- /dev/null +++ b/GriefAlertEvent.java @@ -0,0 +1,28 @@ +// "API" for other plugins + +package net.ardakaz.griefalert; + +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; + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } + + @Override + public HandlerList getHandlers() { + return HANDLERS; + } + + public String getAlert() { + return this.alert; + } +} \ No newline at end of file From 2bfe822fb1017f8c4975e6892549bded3d4c61be Mon Sep 17 00:00:00 2001 From: Kleedje30 Date: Wed, 28 May 2025 20:30:39 +0000 Subject: [PATCH 17/18] revert 9549c1854635ec5fcfbbba1801c80cc80cc64a9e revert Upload files to "/" removed debug changed needed to input world --- GriefAlert.java | 432 ------------------------------------------- GriefAlertEvent.java | 28 --- 2 files changed, 460 deletions(-) delete mode 100644 GriefAlert.java delete mode 100644 GriefAlertEvent.java diff --git a/GriefAlert.java b/GriefAlert.java deleted file mode 100644 index d9e7c13..0000000 --- a/GriefAlert.java +++ /dev/null @@ -1,432 +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.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 { - - 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; - } - } - - private void listIgnoredLocations(CommandSender sender) { - try { - Statement stmt = connection.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT x, y, z, world FROM ignored_locations"); - sender.sendMessage(ChatColor.YELLOW + "Ignored Locations:"); - while (rs.next()) { - sender.sendMessage(ChatColor.GRAY + "- " + rs.getInt("x") + ", " + rs.getInt("y") + ", " + rs.getInt("z") + " in " + rs.getString("world")); - } - rs.close(); - stmt.close(); - } catch (SQLException e) { - sender.sendMessage(ChatColor.RED + "DB error: " + e.getMessage()); - } - } - - @EventHandler (ignoreCancelled = true) - // Block break alerts - public void onBlockBreak(BlockBreakEvent event) { - // Exclusion list - if (EXCLUDED_BLOCKS.contains(event.getBlock().getType())) { - return; - } - - // 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; - } - if (args.length != 4) { - sender.sendMessage(ChatColor.RED + "Usage: /griefalert ignore "); - return true; - } - if (!(sender instanceof Player)) { - sender.sendMessage(ChatColor.RED + "Only players can use this command without specifying a world."); - return true; - } - try { - int x = Integer.parseInt(args[1]); - int y = Integer.parseInt(args[2]); - int z = Integer.parseInt(args[3]); - String world = ((Player)sender).getWorld().getName(); - boolean result = addIgnoredLocation(x, y, z, world); - if (result) { - sender.sendMessage(ChatColor.GREEN + "Location ignored."); - } else { - sender.sendMessage(ChatColor.YELLOW + "Location was already ignored."); - } - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); - } - return true; - } else if (sub.equals("unignore")) { - if (!sender.hasPermission("griefalert.staff.UnIgnore")) { - sender.sendMessage(ChatColor.RED + "You do not have permission."); - return true; - } - if (args.length != 4) { - sender.sendMessage(ChatColor.RED + "Usage: /griefalert unignore "); - return true; - } - if (!(sender instanceof Player)) { - sender.sendMessage(ChatColor.RED + "Only players can use this command without specifying a world."); - return true; - } - try { - int x = Integer.parseInt(args[1]); - int y = Integer.parseInt(args[2]); - int z = Integer.parseInt(args[3]); - String world = ((Player)sender).getWorld().getName(); - boolean result = removeIgnoredLocation(x, y, z, world); - if (result) { - sender.sendMessage(ChatColor.GREEN + "Location unignored."); - } else { - sender.sendMessage(ChatColor.YELLOW + "Location was not ignored."); - } - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); - } - return true; - } else if (sub.equals("list")) { - if (!sender.hasPermission("griefalert.staff.list")) { - sender.sendMessage(ChatColor.RED + "You do not have permission."); - return true; - } - listIgnoredLocations(sender); - return true; - } - sender.sendMessage(ChatColor.RED + "Unknown subcommand. Use: ignore, unignore, list"); - return false; - } - - @Override - public java.util.List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { - if (!command.getName().equalsIgnoreCase("griefalert")) return null; - java.util.List completions = new java.util.ArrayList<>(); - if (args.length == 1) { - java.util.List subs = java.util.Arrays.asList("ignore", "unignore", "list"); - for (String s : subs) { - if (s.startsWith(args[0].toLowerCase())) completions.add(s); - } - return completions; - } - // No world argument needed anymore - 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 (mapLink != null && !mapLink.isEmpty()) { - griefalert_event = new GriefAlertEvent(message + " (" + mapLink + ")"); - } else { - griefalert_event = new GriefAlertEvent(message); - } - getServer().getPluginManager().callEvent(griefalert_event); - // Notify staff ingame - for (Player player : Bukkit.getOnlinePlayers()) { - if (player.hasPermission("griefalert.notify")) { - player.sendMessage(message); - } - } - lastAlert = realAlertMessage; - } - - // Block inspector: 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 deleted file mode 100644 index 511595c..0000000 --- a/GriefAlertEvent.java +++ /dev/null @@ -1,28 +0,0 @@ -// "API" for other plugins - -package net.ardakaz.griefalert; - -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; - } - - public static HandlerList getHandlerList() { - return HANDLERS; - } - - @Override - public HandlerList getHandlers() { - return HANDLERS; - } - - public String getAlert() { - return this.alert; - } -} \ No newline at end of file From 0ee14339b723c4b9e62efad7921abdf64309a0fc Mon Sep 17 00:00:00 2001 From: Kleedje30 Date: Wed, 28 May 2025 20:31:34 +0000 Subject: [PATCH 18/18] Upload files to "src/net/ardakaz/griefalert" removed debug changed needed to input world into options for ignore --- src/net/ardakaz/griefalert/GriefAlert.java | 903 ++++++++++----------- 1 file changed, 431 insertions(+), 472 deletions(-) mode change 100755 => 100644 src/net/ardakaz/griefalert/GriefAlert.java diff --git a/src/net/ardakaz/griefalert/GriefAlert.java b/src/net/ardakaz/griefalert/GriefAlert.java old mode 100755 new mode 100644 index a469dac..d9e7c13 --- a/src/net/ardakaz/griefalert/GriefAlert.java +++ b/src/net/ardakaz/griefalert/GriefAlert.java @@ -1,473 +1,432 @@ -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 org.bukkit.World; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Arrays; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -public class GriefAlert extends JavaPlugin implements Listener, 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; - } - } - - private void clearIgnoredLocations() { - try { - Statement stmt = connection.createStatement(); - stmt.executeUpdate("DELETE FROM ignored_locations"); - stmt.close(); - } catch (SQLException e) { - getLogger().warning("DB error: " + e.getMessage()); - } - } - - private void listIgnoredLocations(CommandSender sender) { - try { - Statement stmt = connection.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT x, y, z, world FROM ignored_locations"); - sender.sendMessage(ChatColor.YELLOW + "Ignored Locations:"); - while (rs.next()) { - sender.sendMessage(ChatColor.GRAY + "- " + rs.getInt("x") + ", " + rs.getInt("y") + ", " + rs.getInt("z") + " in " + rs.getString("world")); - } - rs.close(); - stmt.close(); - } catch (SQLException e) { - sender.sendMessage(ChatColor.RED + "DB error: " + e.getMessage()); - } - } - - @EventHandler (ignoreCancelled = true) - // Block break alerts - public void onBlockBreak(BlockBreakEvent event) { - // Exclusion list - if (EXCLUDED_BLOCKS.contains(event.getBlock().getType())) { - return; - } - - // 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")) { - getLogger().info("[DEBUG] /griefalert ignore called by " + sender.getName() + " with args: " + String.join(" ", args)); - if (!sender.hasPermission("griefalert.staff.ignore")) { - sender.sendMessage(ChatColor.RED + "You do not have permission."); - getLogger().info("[DEBUG] Permission denied for ignore"); - return true; - } - if (args.length != 5) { - sender.sendMessage(ChatColor.RED + "Usage: /griefalert ignore "); - getLogger().info("[DEBUG] Incorrect number of arguments for ignore: " + args.length); - return true; - } - try { - int x = Integer.parseInt(args[1]); - int y = Integer.parseInt(args[2]); - int z = Integer.parseInt(args[3]); - String world = args[4]; - boolean result = addIgnoredLocation(x, y, z, world); - if (result) { - sender.sendMessage(ChatColor.GREEN + "Location ignored."); - getLogger().info("[DEBUG] Location ignored: " + x + "," + y + "," + z + "," + world); - } else { - sender.sendMessage(ChatColor.YELLOW + "Location was already ignored."); - getLogger().info("[DEBUG] Location already ignored: " + x + "," + y + "," + z + "," + world); - } - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); - getLogger().info("[DEBUG] Invalid coordinates for ignore"); - } - return true; - } else if (sub.equals("unignore")) { - getLogger().info("[DEBUG] /griefalert unignore called by " + sender.getName() + " with args: " + String.join(" ", args)); - if (!sender.hasPermission("griefalert.staff.UnIgnore")) { - sender.sendMessage(ChatColor.RED + "You do not have permission."); - getLogger().info("[DEBUG] Permission denied for unignore"); - return true; - } - if (args.length != 5) { - sender.sendMessage(ChatColor.RED + "Usage: /griefalert unignore "); - getLogger().info("[DEBUG] Incorrect number of arguments for unignore: " + args.length); - return true; - } - try { - int x = Integer.parseInt(args[1]); - int y = Integer.parseInt(args[2]); - int z = Integer.parseInt(args[3]); - String world = args[4]; - boolean result = removeIgnoredLocation(x, y, z, world); - if (result) { - sender.sendMessage(ChatColor.GREEN + "Location unignored."); - getLogger().info("[DEBUG] Location unignored: " + x + "," + y + "," + z + "," + world); - } else { - sender.sendMessage(ChatColor.YELLOW + "Location was not ignored."); - getLogger().info("[DEBUG] Location was not ignored: " + x + "," + y + "," + z + "," + world); - } - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); - getLogger().info("[DEBUG] Invalid coordinates for unignore"); - } - return true; - } else if (sub.equals("list")) { - getLogger().info("[DEBUG] /griefalert list called by " + sender.getName()); - if (!sender.hasPermission("griefalert.staff.list")) { - sender.sendMessage(ChatColor.RED + "You do not have permission."); - getLogger().info("[DEBUG] Permission denied for list"); - return true; - } - listIgnoredLocations(sender); - sender.sendMessage(ChatColor.GRAY + "[DEBUG] Ignored locations listed in chat and console."); - getLogger().info("[DEBUG] Ignored locations listed for " + sender.getName()); - return true; - } else if (sub.equals("clear")) { - getLogger().info("[DEBUG] /griefalert clear called by " + sender.getName()); - if (!sender.hasPermission("griefalert.staff.clear")) { - sender.sendMessage(ChatColor.RED + "You do not have permission."); - getLogger().info("[DEBUG] Permission denied for clear"); - return true; - } - clearIgnoredLocations(); - sender.sendMessage(ChatColor.GREEN + "All ignored locations cleared."); - getLogger().info("[DEBUG] All ignored locations cleared by " + sender.getName()); - return true; - } - getLogger().info("[DEBUG] Unknown subcommand: " + sub); - sender.sendMessage(ChatColor.RED + "Unknown subcommand. Use: ignore, unignore, list, clear"); - return false; - } - - @Override - public java.util.List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { - if (!command.getName().equalsIgnoreCase("griefalert")) return null; - java.util.List completions = new java.util.ArrayList<>(); - if (args.length == 1) { - java.util.List subs = java.util.Arrays.asList("ignore", "unignore", "list", "clear"); - for (String s : subs) { - if (s.startsWith(args[0].toLowerCase())) completions.add(s); - } - return completions; - } - if ((args[0].equalsIgnoreCase("ignore") || args[0].equalsIgnoreCase("unignore")) && args.length == 5) { - // Suggest world names for the 5th argument - for (World world : getServer().getWorlds()) { - if (world.getName().toLowerCase().startsWith(args[4].toLowerCase())) { - completions.add(world.getName()); - } - } - return completions; - } - return null; - } - - // Sends the alert (or cancels it) - private void alert(String message, String playerName, String mapLink, String target, int x, int y, int z, String world) { - // Exclude trusted people - Player griefer = Bukkit.getPlayer(playerName); - if (griefer.hasPermission("griefalert.exclude") || griefer.hasPermission("griefalert.exclude." + target)) { - return; - } - // Spam limiter - String realAlertMessage = message; - String[] alert1 = null; - if (lastAlert != null) { - alert1 = lastAlert.split(" "); - } - String[] alert2 = message.split(" "); - if (alert1 != null) { - if (alert1[2].equals(alert2[2]) && alert1[5].equals(alert2[5]) && alert1[1].equals("broke") && alert2[1].equals("broke")) { - identicalAlerts += 1; - } else if (Arrays.equals(alert1, alert2)) { - identicalAlerts += 1; - } else { - identicalAlerts = 1; - } - } - if (identicalAlerts == 4) { - message = ChatColor.GRAY + "Same behavior continues."; - mapLink = null; - } - if (identicalAlerts > 4) { - return; - } - // Use direct location check - if (world != null && isLocationIgnored(x, y, z, world)) { - return; // Do not alert if location is ignored - } - // Send an event for external hooks - GriefAlertEvent griefalert_event; - if (mapLink != null && !mapLink.isEmpty()) { - griefalert_event = new GriefAlertEvent(message + " (" + mapLink + ")"); - } else { - griefalert_event = new GriefAlertEvent(message); - } - getServer().getPluginManager().callEvent(griefalert_event); - // Notify staff ingame - for (Player player : Bukkit.getOnlinePlayers()) { - if (player.hasPermission("griefalert.notify")) { - player.sendMessage(message); - } - } - lastAlert = realAlertMessage; - } - - // Block inspector: 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.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 { + + 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; + } + } + + private void listIgnoredLocations(CommandSender sender) { + try { + Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT x, y, z, world FROM ignored_locations"); + sender.sendMessage(ChatColor.YELLOW + "Ignored Locations:"); + while (rs.next()) { + sender.sendMessage(ChatColor.GRAY + "- " + rs.getInt("x") + ", " + rs.getInt("y") + ", " + rs.getInt("z") + " in " + rs.getString("world")); + } + rs.close(); + stmt.close(); + } catch (SQLException e) { + sender.sendMessage(ChatColor.RED + "DB error: " + e.getMessage()); + } + } + + @EventHandler (ignoreCancelled = true) + // Block break alerts + public void onBlockBreak(BlockBreakEvent event) { + // Exclusion list + if (EXCLUDED_BLOCKS.contains(event.getBlock().getType())) { + return; + } + + // 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; + } + if (args.length != 4) { + sender.sendMessage(ChatColor.RED + "Usage: /griefalert ignore "); + return true; + } + if (!(sender instanceof Player)) { + sender.sendMessage(ChatColor.RED + "Only players can use this command without specifying a world."); + return true; + } + try { + int x = Integer.parseInt(args[1]); + int y = Integer.parseInt(args[2]); + int z = Integer.parseInt(args[3]); + String world = ((Player)sender).getWorld().getName(); + boolean result = addIgnoredLocation(x, y, z, world); + if (result) { + sender.sendMessage(ChatColor.GREEN + "Location ignored."); + } else { + sender.sendMessage(ChatColor.YELLOW + "Location was already ignored."); + } + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + } + return true; + } else if (sub.equals("unignore")) { + if (!sender.hasPermission("griefalert.staff.UnIgnore")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + return true; + } + if (args.length != 4) { + sender.sendMessage(ChatColor.RED + "Usage: /griefalert unignore "); + return true; + } + if (!(sender instanceof Player)) { + sender.sendMessage(ChatColor.RED + "Only players can use this command without specifying a world."); + return true; + } + try { + int x = Integer.parseInt(args[1]); + int y = Integer.parseInt(args[2]); + int z = Integer.parseInt(args[3]); + String world = ((Player)sender).getWorld().getName(); + boolean result = removeIgnoredLocation(x, y, z, world); + if (result) { + sender.sendMessage(ChatColor.GREEN + "Location unignored."); + } else { + sender.sendMessage(ChatColor.YELLOW + "Location was not ignored."); + } + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "Coordinates must be numbers."); + } + return true; + } else if (sub.equals("list")) { + if (!sender.hasPermission("griefalert.staff.list")) { + sender.sendMessage(ChatColor.RED + "You do not have permission."); + return true; + } + listIgnoredLocations(sender); + return true; + } + sender.sendMessage(ChatColor.RED + "Unknown subcommand. Use: ignore, unignore, list"); + return false; + } + + @Override + public java.util.List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + if (!command.getName().equalsIgnoreCase("griefalert")) return null; + java.util.List completions = new java.util.ArrayList<>(); + if (args.length == 1) { + java.util.List subs = java.util.Arrays.asList("ignore", "unignore", "list"); + for (String s : subs) { + if (s.startsWith(args[0].toLowerCase())) completions.add(s); + } + return completions; + } + // No world argument needed anymore + 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 (mapLink != null && !mapLink.isEmpty()) { + griefalert_event = new GriefAlertEvent(message + " (" + mapLink + ")"); + } else { + griefalert_event = new GriefAlertEvent(message); + } + getServer().getPluginManager().callEvent(griefalert_event); + // Notify staff ingame + for (Player player : Bukkit.getOnlinePlayers()) { + if (player.hasPermission("griefalert.notify")) { + player.sendMessage(message); + } + } + lastAlert = realAlertMessage; + } + + // Block inspector: 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