From ecfecaf19410e91437764cf42e9a61276ae6a09b Mon Sep 17 00:00:00 2001 From: Flarp Date: Thu, 28 Dec 2023 16:54:23 -0500 Subject: First commit --- .gitignore | 2 + license.txt | 5 + pom.xml | 88 +++++++++++++++ .../java/dev/deeve/containeraudit/ChestEvent.java | 14 +++ .../dev/deeve/containeraudit/ChestListener.java | 95 ++++++++++++++++ .../java/dev/deeve/containeraudit/Database.java | 125 +++++++++++++++++++++ src/main/java/dev/deeve/containeraudit/Plugin.java | 44 ++++++++ src/main/resources/plugin.yml | 4 + 8 files changed, 377 insertions(+) create mode 100644 .gitignore create mode 100644 license.txt create mode 100644 pom.xml create mode 100644 src/main/java/dev/deeve/containeraudit/ChestEvent.java create mode 100644 src/main/java/dev/deeve/containeraudit/ChestListener.java create mode 100644 src/main/java/dev/deeve/containeraudit/Database.java create mode 100644 src/main/java/dev/deeve/containeraudit/Plugin.java create mode 100644 src/main/resources/plugin.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bcbfeee --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode/ +target/ diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..4839257 --- /dev/null +++ b/license.txt @@ -0,0 +1,5 @@ +Copyright 2023 Ethan Dorta + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..5c1bf82 --- /dev/null +++ b/pom.xml @@ -0,0 +1,88 @@ + + + + 4.0.0 + + dev.deeve.containeraudit + ContainerAudit + 0.0.1 + + ContainerAudit + + + UTF-8 + 1.8 + 1.8 + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + + + org.spigotmc + spigot-api + 1.19.2-R0.1-SNAPSHOT + provided + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + + ${project.basedir}/src/main/resources + + plugin.yml + + + + + diff --git a/src/main/java/dev/deeve/containeraudit/ChestEvent.java b/src/main/java/dev/deeve/containeraudit/ChestEvent.java new file mode 100644 index 0000000..a59e0ea --- /dev/null +++ b/src/main/java/dev/deeve/containeraudit/ChestEvent.java @@ -0,0 +1,14 @@ +package dev.deeve.containeraudit; + +import java.time.LocalDateTime; + +public class ChestEvent { + private int count; + private String block; + private int x; + private int y; + private int z; + private String player; + private String type; + private LocalDateTime timestamp; +} diff --git a/src/main/java/dev/deeve/containeraudit/ChestListener.java b/src/main/java/dev/deeve/containeraudit/ChestListener.java new file mode 100644 index 0000000..bc93b9c --- /dev/null +++ b/src/main/java/dev/deeve/containeraudit/ChestListener.java @@ -0,0 +1,95 @@ +package dev.deeve.containeraudit; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +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; + + +public class ChestListener implements Listener { + + public static List ignore; + + public ChestListener(List ignore) { + ChestListener.ignore = ignore; + } + + @EventHandler + public final void onInventoryClick(InventoryClickEvent event) { + String playerName = event.getWhoClicked().getName(); + Inventory inventory = event.getClickedInventory(); + + if (inventory == null || ignore.contains(playerName)) return; + + + + Location loc = inventory.getLocation(); + + InventoryAction action = event.getAction(); + + if (inventory.getType() == InventoryType.CHEST && (action == InventoryAction.COLLECT_TO_CURSOR || + action == InventoryAction.PICKUP_ALL || + action == InventoryAction.PICKUP_HALF || + action == InventoryAction.PICKUP_ONE || + action == InventoryAction.PICKUP_SOME || + action == InventoryAction.MOVE_TO_OTHER_INVENTORY)) { + + // we are taking from the chest + Database.logInventoryEvent(playerName, true, loc, event.getCurrentItem()); + + } else if (inventory.getType() == InventoryType.CHEST && (action == InventoryAction.PLACE_ALL || + action == InventoryAction.PLACE_ONE || + action == InventoryAction.PLACE_SOME || + action == InventoryAction.HOTBAR_MOVE_AND_READD || + action == InventoryAction.HOTBAR_SWAP)) { + + // we are placing into the chest + Database.logInventoryEvent(playerName, false, loc, event.getCursor()); + + } else if (inventory.getType() == InventoryType.CHEST && (action == InventoryAction.SWAP_WITH_CURSOR)) { + + // we are swapping what is in our cursor with what is in the given slot, reverse + Database.logInventoryEvent(playerName, true, loc, event.getCursor()); + Database.logInventoryEvent(playerName, false, loc, event.getCurrentItem()); + + } + + + } + + @EventHandler + public final void onBlockPlace(BlockPlaceEvent event) { + Block placedBlock = event.getBlock(); + String playerName = event.getPlayer().getName(); + + if (ignore.contains(playerName)) return; + + if (placedBlock.getType() == Material.CHEST) + Database.logBlockEvent(playerName, "PLACE", placedBlock.getLocation()); + + } + + @EventHandler + public final void onBlockBreak(BlockBreakEvent event) { + Block placedBlock = event.getBlock(); + String playerName = event.getPlayer().getName(); + + if (ignore.contains(playerName)) return; + + if (placedBlock.getType() == Material.CHEST) + Database.logBlockEvent(playerName, "BREAK", placedBlock.getLocation()); + + } +} diff --git a/src/main/java/dev/deeve/containeraudit/Database.java b/src/main/java/dev/deeve/containeraudit/Database.java new file mode 100644 index 0000000..ad409f4 --- /dev/null +++ b/src/main/java/dev/deeve/containeraudit/Database.java @@ -0,0 +1,125 @@ +package dev.deeve.containeraudit; + +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.inventory.ItemStack; +import org.bukkit.Location; +import org.bukkit.Bukkit; + +import java.sql.PreparedStatement; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Date; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.sql.Connection; +import java.sql.ResultSet; + +public class Database { + static Connection connection; + static Logger logger; + + public static void connect(String path, Logger logger) throws SQLException { + connection = DriverManager.getConnection("jdbc:sqlite:" + path); + Database.logger = logger; + } + + static String playerID(Player player) { + return player.getUniqueId().toString().replaceAll("-", ""); + } + + public static void createTables() throws SQLException { + PreparedStatement chestEventCreate = connection.prepareStatement( + "CREATE TABLE IF NOT EXISTS chest_events (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + + "player TEXT NOT NULL, " + + "x REAL NOT NULL, " + + "y REAL NOT NULL, " + + "z REAL NOT NULL, " + + "block TEXT NOT NULL," + + "count INTEGER NOT NULL," + + "event_type TEXT NOT NULL," + + "timestamp INTEGER NOT NULL" + + ")" + ); + chestEventCreate.executeUpdate(); + + PreparedStatement chestCreate = connection.prepareStatement( + "CREATE TABLE IF NOT EXISTS chests (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + + "actor TEXT NOT NULL, " + + "action TEXT NOT NULL," + + "x REAL NOT NULL, " + + "y REAL NOT NULL, " + + "z REAL NOT NULL," + + "timestamp INTEGER NOT NULL" + + ")" + ); + + chestCreate.executeUpdate(); + + PreparedStatement viewCreate = connection.prepareStatement( + "CREATE VIEW IF NOT EXISTS chest_block_events AS " + + "SELECT * FROM chests AS placed_chest FULL OUTER JOIN chests AS broke_chest ON " + + "broke_chest.action = \"BREAK\" AND placed_chest.action = \"PLACE\" AND " + + " placed_chest.x = broke_chest.x AND placed_chest.y = broke_chest.y AND placed_chest.z = broke_chest.z AND " + + "broke_chest.timestamp = (SELECT MIN(timestamp) FROM chests WHERE timestamp > placed_chest.timestamp) WHERE " + + " placed_chest.action = \"PLACE\" OR broke_chest.action = \"BREAK\";" + ); + + viewCreate.executeUpdate(); + + // software engineering is dead + PreparedStatement shmiewShmeate = connection.prepareStatement("CREATE VIEW IF NOT EXISTS chest_event_owned AS SELECT chest_events.id, chest_events.player, chest_events.x, chest_events.y, chest_events.z, chest_events.timestamp AS event_timestamp, action, block, `count`, chests.timestamp AS chest_placed_timestamp, chests.actor AS chest_owner FROM chest_events LEFT JOIN chests ON chests.timestamp = (SELECT MAX(timestamp) FROM chests WHERE chest_events.x = x AND chest_events.y = y AND chest_events.z = z AND chests.action = \"PLACE\" AND chest_events.timestamp > timestamp)"); + shmiewShmeate.executeUpdate(); + + + } + + public static void logInventoryEvent(String player, boolean pickup, Location loc, ItemStack stack) { + try { + PreparedStatement statement = connection.prepareStatement( + "INSERT INTO chest_events (player, x, y, z, block, count, event_type, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); + + statement.setString(1, player); + statement.setInt(2, loc.getBlockX()); + statement.setInt(3, loc.getBlockY()); + statement.setInt(4, loc.getBlockZ()); + statement.setString(5, stack.getType().getKey().toString()); + statement.setInt(6, stack.getAmount()); + statement.setString(7, pickup ? "PICKUP" : "PLACE"); + statement.setLong(8, Instant.now().getEpochSecond()); + + statement.executeUpdate(); + + } catch (SQLException ex) { + logger.log(Level.SEVERE, "Error occured when inserting into chest_events"); + ex.printStackTrace(); + } + + } + + public static void logBlockEvent(String player, String type, Location loc) { + try { + PreparedStatement statement = connection.prepareStatement( + "INSERT INTO chests (actor, action, x, y, z, timestamp) VALUES (?, ?, ?, ?, ?, ?)"); + + statement.setString(1, player); + statement.setString(2, type); + statement.setInt(3, loc.getBlockX()); + statement.setInt(4, loc.getBlockY()); + statement.setInt(5, loc.getBlockZ()); + statement.setLong(6, Instant.now().getEpochSecond()); + + statement.executeUpdate(); + + } catch (SQLException ex) { + logger.log(Level.SEVERE, "Error occured when inserting into chests"); + ex.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/dev/deeve/containeraudit/Plugin.java b/src/main/java/dev/deeve/containeraudit/Plugin.java new file mode 100644 index 0000000..a92d6c0 --- /dev/null +++ b/src/main/java/dev/deeve/containeraudit/Plugin.java @@ -0,0 +1,44 @@ +package dev.deeve.containeraudit; + + +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.logging.Logger; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.sql.SQLException; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class Plugin extends JavaPlugin { + public static Logger logger; + + + public void onEnable() { + logger = getLogger(); + getDataFolder().mkdirs(); + + try { + Database.connect(new File(getDataFolder(), "database.db").getPath(), logger); + List ignores = new ArrayList(); + Path okay = Paths.get(getDataFolder().getPath(), "okay.txt"); + + if (Files.exists(okay)) { + ignores = Files.readAllLines(okay); + } + + getServer().getPluginManager().registerEvents(new ChestListener(ignores), this); + Database.createTables(); + } + catch (SQLException | IOException ex) { + logger.log(Level.SEVERE, "Database error: (not enabling plugin)"); + ex.printStackTrace(); + return; + } + + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..7f6135b --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,4 @@ +main: dev.deeve.containeraudit.Plugin +name: ContainerAudit +version: 0.0.1 +api-version: 1.19 \ No newline at end of file -- cgit v1.2.3