From 2239b6a963e27033eb1aca1ebfbf493c58e46e3a Mon Sep 17 00:00:00 2001 From: Restitutor Date: Sat, 7 Feb 2026 15:42:39 -0500 Subject: [PATCH 1/2] Add ContainerResult and BoxAPI --- .../java/net/coreprotect/CoreProtectAPI.java | 15 + .../java/net/coreprotect/api/BlockAPI.java | 133 ++++++-- src/main/java/net/coreprotect/api/BoxAPI.java | 306 ++++++++++++++++++ .../api/result/ContainerResult.java | 202 ++++++++++++ 4 files changed, 637 insertions(+), 19 deletions(-) create mode 100644 src/main/java/net/coreprotect/api/BoxAPI.java create mode 100644 src/main/java/net/coreprotect/api/result/ContainerResult.java diff --git a/src/main/java/net/coreprotect/CoreProtectAPI.java b/src/main/java/net/coreprotect/CoreProtectAPI.java index 757427b36..dfab55a94 100755 --- a/src/main/java/net/coreprotect/CoreProtectAPI.java +++ b/src/main/java/net/coreprotect/CoreProtectAPI.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Map; +import net.coreprotect.api.result.ContainerResult; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -117,6 +118,20 @@ public List queueLookup(Block block) { return QueueLookup.performLookup(block); } + /** + * Performs a lookup of container-related actions at the specified location. + * + * @param location The location to look up + * @param time Time constraint in seconds (0 means no time constraint) + * @return List of results in a ContainerResult list format + */ + public List containerLookup(Location location, int time) { + if (isEnabled()) { + return BlockAPI.performContainerLookup(location, time); + } + return null; + } + /** * Performs a lookup on session data for the specified user. * diff --git a/src/main/java/net/coreprotect/api/BlockAPI.java b/src/main/java/net/coreprotect/api/BlockAPI.java index d9a835c72..cdb566018 100644 --- a/src/main/java/net/coreprotect/api/BlockAPI.java +++ b/src/main/java/net/coreprotect/api/BlockAPI.java @@ -1,13 +1,6 @@ package net.coreprotect.api; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.List; - -import org.bukkit.block.Block; - +import net.coreprotect.api.result.ContainerResult; import net.coreprotect.config.Config; import net.coreprotect.config.ConfigHandler; import net.coreprotect.database.Database; @@ -15,6 +8,14 @@ import net.coreprotect.utility.BlockUtils; import net.coreprotect.utility.StringUtils; import net.coreprotect.utility.WorldUtils; +import org.bukkit.Location; +import org.bukkit.block.Block; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.List; /** * Provides API methods for block-related lookups in the CoreProtect database. @@ -29,13 +30,42 @@ private BlockAPI() { throw new IllegalStateException("API class"); } + /** + * Creates a prepared statement with WHERE clause for block/container lookups. + * + * @param connection Database connection + * @param tableType Either "block" or "container" + * @param selectClause The SELECT portion of the query + * @param location The block to look up + * @param checkTime The minimum time constraint + * @return PreparedStatement with parameters set + * @throws Exception if there's an error creating the statement + */ + private static PreparedStatement createLocationQuery(Connection connection, String tableType, String selectClause, Location location, int checkTime) throws Exception { + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + int worldId = WorldUtils.getWorldId(location.getWorld().getName()); + + String query = "SELECT " + selectClause + " FROM " + ConfigHandler.prefix + tableType + " " + + WorldUtils.getWidIndex(tableType) + + " WHERE wid = ? AND x = ? AND z = ? AND y = ? AND time > ? ORDER BY rowid DESC"; + + PreparedStatement statement = connection.prepareStatement(query); + statement.setInt(1, worldId); + statement.setInt(2, x); + statement.setInt(3, z); + statement.setInt(4, y); + statement.setInt(5, checkTime); + + return statement; + } + /** * Performs a lookup of block-related actions at the specified block. - * - * @param block - * The block to look up - * @param offset - * Time constraint in seconds (0 means no time constraint) + * + * @param block The block to look up + * @param offset Time constraint in seconds (0 means no time constraint) * @return List of results in a String array format */ public static List performLookup(Block block, int offset) { @@ -65,10 +95,8 @@ public static List performLookup(Block block, int offset) { checkTime = time - offset; } - try (Statement statement = connection.createStatement()) { - String query = "SELECT time,user,action,type,data,blockdata,rolled_back FROM " + ConfigHandler.prefix + "block " + WorldUtils.getWidIndex("block") + "WHERE wid = '" + worldId + "' AND x = '" + x + "' AND z = '" + z + "' AND y = '" + y + "' AND time > '" + checkTime + "' ORDER BY rowid DESC"; - - try (ResultSet results = statement.executeQuery(query)) { + try (PreparedStatement statement = createLocationQuery(connection, "block", "time,user,action,type,data,blockdata,rolled_back", block.getLocation(), checkTime)) { + try (ResultSet results = statement.executeQuery()) { while (results.next()) { String resultTime = results.getString("time"); int resultUserId = results.getInt("user"); @@ -85,14 +113,81 @@ public static List performLookup(Block block, int offset) { String resultUser = ConfigHandler.playerIdCacheReversed.get(resultUserId); String blockData = BlockUtils.byteDataToString(resultBlockData, resultType); - String[] lookupData = new String[] { resultTime, resultUser, String.valueOf(x), String.valueOf(y), String.valueOf(z), String.valueOf(resultType), resultData, resultAction, resultRolledBack, String.valueOf(worldId), blockData }; + String[] lookupData = new String[]{resultTime, resultUser, String.valueOf(x), String.valueOf(y), String.valueOf(z), String.valueOf(resultType), resultData, resultAction, resultRolledBack, String.valueOf(worldId), blockData}; result.add(StringUtils.toStringArray(lookupData)); } } } + } catch (Exception e) { + e.printStackTrace(); } - catch (Exception e) { + + return result; + } + + /** + * Performs a lookup of container-related actions at the specified location. + * + * @param location The location to look up + * @param offset Time constraint in seconds (0 means no time constraint) + * @return List of results in a ContainerResult array format + */ + public static List performContainerLookup(Location location, int offset) { + List result = new ArrayList<>(); + + if (!Config.getGlobal().API_ENABLED) { + return result; + } + + if (location == null) { + return result; + } + + try (Connection connection = Database.getConnection(false, 1000)) { + if (connection == null) { + return result; + } + + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + int time = (int) (System.currentTimeMillis() / 1000L); + int checkTime = 0; + + if (offset > 0) { + checkTime = time - offset; + } + + try (PreparedStatement statement = createLocationQuery(connection, "container", "time,user,type,data,amount,metadata,action,rolled_back", location, checkTime)) { + try (ResultSet results = statement.executeQuery()) { + while (results.next()) { + int resultUserId = results.getInt("user"); + int resultAction = results.getInt("action"); + int resultType = results.getInt("type"); + int resultData = results.getInt("data"); + long resultTime = results.getLong("time"); + int resultAmount = results.getInt("amount"); + int resultRolledBack = results.getInt("rolled_back"); + byte[] resultMetadata = results.getBytes("metadata"); + + if (ConfigHandler.playerIdCacheReversed.get(resultUserId) == null) { + UserStatement.loadName(connection, resultUserId); + } + + String resultUser = ConfigHandler.playerIdCacheReversed.get(resultUserId); + + ContainerResult containerResult = new ContainerResult( + resultTime, resultUser, location.getWorld().getName(), x, y, z, + resultType, resultData, resultAmount, resultMetadata, + resultAction, resultRolledBack + ); + + result.add(containerResult); + } + } + } + } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/net/coreprotect/api/BoxAPI.java b/src/main/java/net/coreprotect/api/BoxAPI.java new file mode 100644 index 000000000..f556e4ffe --- /dev/null +++ b/src/main/java/net/coreprotect/api/BoxAPI.java @@ -0,0 +1,306 @@ +package net.coreprotect.api; + +import net.coreprotect.api.result.ContainerResult; +import net.coreprotect.config.Config; +import net.coreprotect.config.ConfigHandler; +import net.coreprotect.database.Database; +import net.coreprotect.database.statement.UserStatement; +import net.coreprotect.utility.BlockUtils; +import net.coreprotect.utility.StringUtils; +import net.coreprotect.utility.WorldUtils; +import org.bukkit.World; +import org.bukkit.util.BoundingBox; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * Provides API methods for area-based lookups using bounding boxes in the CoreProtect database. + */ +public class BoxAPI { + + /** + * Maximum number of results to return from area queries to prevent performance issues. + */ + private static final int MAX_RESULTS = 10000; + + /** + * Maximum bounding box volume to prevent excessive database queries. + */ + private static final long MAX_BOUNDING_BOX_VOLUME = 1000000; // 100x100x100 blocks + + /** + * Private constructor to prevent instantiation. + * This is a utility class with static methods only. + */ + private BoxAPI() { + throw new IllegalStateException("API class"); + } + + /** + * Functional interface for processing result sets into specific types. + */ + @FunctionalInterface + private interface ResultProcessor { + T processResult(ResultSet rs, Connection connection) throws SQLException; + } + + /** + * Validates bounding box size to prevent performance issues. + */ + private static boolean isValidBoundingBoxSize(BoundingBox boundingBox) { + double volume = boundingBox.getVolume(); + return volume > 0 && volume <= MAX_BOUNDING_BOX_VOLUME; + } + + /** + * Creates a prepared statement with WHERE clause for area-based lookups. + * Fixed coordinate conversion to handle BoundingBox boundaries correctly. + */ + private static PreparedStatement createAreaQuery(Connection connection, String tableType, String selectClause, + World world, BoundingBox boundingBox, int checkTime, String orderBy) throws Exception { + // BoundingBox: min is inclusive, max is exclusive + int minX = (int) Math.floor(boundingBox.getMinX()); + int maxX = (int) Math.floor(boundingBox.getMaxX()) - 1; + int minY = (int) Math.floor(boundingBox.getMinY()); + int maxY = (int) Math.floor(boundingBox.getMaxY()) - 1; + int minZ = (int) Math.floor(boundingBox.getMinZ()); + int maxZ = (int) Math.floor(boundingBox.getMaxZ()) - 1; + int worldId = WorldUtils.getWorldId(world.getName()); + + // Fix SQL spacing issue + String query = "SELECT " + selectClause + " FROM " + ConfigHandler.prefix + tableType + " " + + WorldUtils.getWidIndex(tableType) + " " + + "WHERE wid = ? AND x BETWEEN ? AND ? AND y BETWEEN ? AND ? AND z BETWEEN ? AND ? AND time > ? " + + "ORDER BY " + orderBy + " LIMIT " + MAX_RESULTS; + + PreparedStatement statement = connection.prepareStatement(query); + statement.setInt(1, worldId); + statement.setInt(2, minX); + statement.setInt(3, maxX); + statement.setInt(4, minY); + statement.setInt(5, maxY); + statement.setInt(6, minZ); + statement.setInt(7, maxZ); + statement.setInt(8, checkTime); + + return statement; + } + + /** + * Generic method for performing area-based lookups. + */ + private static List performAreaQuery(World world, BoundingBox boundingBox, int offset, + String tableType, String selectClause, String orderBy, ResultProcessor processor) { + List result = new ArrayList<>(); + + if (!Config.getGlobal().API_ENABLED || world == null || boundingBox == null) { + return result; + } + + // Validate bounding box size + if (!isValidBoundingBoxSize(boundingBox)) { + System.err.println("BoxAPI: Bounding box too large or invalid, volume: " + boundingBox.getVolume()); + return result; + } + + try (Connection connection = Database.getConnection(false, 1000)) { + if (connection == null) { + return result; + } + + int time = (int) (System.currentTimeMillis() / 1000L); + int checkTime = offset > 0 ? time - offset : 0; + + try (PreparedStatement statement = createAreaQuery(connection, tableType, selectClause, world, boundingBox, checkTime, orderBy)) { + try (ResultSet rs = statement.executeQuery()) { + while (rs.next()) { + result.add(processor.processResult(rs, connection)); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + return result; + } + + /** + * Process block lookup results into String arrays. + */ + private static String[] processBlockResult(ResultSet rs, Connection connection) throws SQLException { + String resultTime = rs.getString("time"); + int resultUserId = rs.getInt("user"); + String resultAction = rs.getString("action"); + int resultType = rs.getInt("type"); + String resultData = rs.getString("data"); + byte[] resultBlockData = rs.getBytes("blockdata"); + String resultRolledBack = rs.getString("rolled_back"); + int x = rs.getInt("x"); + int y = rs.getInt("y"); + int z = rs.getInt("z"); + int worldId = rs.getInt("wid"); + + if (ConfigHandler.playerIdCacheReversed.get(resultUserId) == null) { + UserStatement.loadName(connection, resultUserId); + } + + String resultUser = ConfigHandler.playerIdCacheReversed.get(resultUserId); + String blockData = BlockUtils.byteDataToString(resultBlockData, resultType); + + String[] lookupData = new String[]{resultTime, resultUser, String.valueOf(x), String.valueOf(y), + String.valueOf(z), String.valueOf(resultType), resultData, resultAction, resultRolledBack, + String.valueOf(worldId), blockData}; + + return StringUtils.toStringArray(lookupData); + } + + /** + * Process container lookup results into ContainerResult objects. + */ + private static ContainerResult processContainerResult(ResultSet rs, Connection connection, World world) throws SQLException { + int resultUserId = rs.getInt("user"); + int resultAction = rs.getInt("action"); + int resultType = rs.getInt("type"); + int resultData = rs.getInt("data"); + long resultTime = rs.getLong("time"); + int resultAmount = rs.getInt("amount"); + int resultRolledBack = rs.getInt("rolled_back"); + byte[] resultMetadata = rs.getBytes("metadata"); + int x = rs.getInt("x"); + int y = rs.getInt("y"); + int z = rs.getInt("z"); + + if (ConfigHandler.playerIdCacheReversed.get(resultUserId) == null) { + UserStatement.loadName(connection, resultUserId); + } + + String resultUser = ConfigHandler.playerIdCacheReversed.get(resultUserId); + + return new ContainerResult(resultTime, resultUser, world.getName(), x, y, z, + resultType, resultData, resultAmount, resultMetadata, resultAction, resultRolledBack); + } + + /** + * Performs a lookup of block-related actions within the specified bounding box. + * Note: Uses rowid DESC ordering to maintain compatibility with original BlockAPI. + */ + public static List performAreaLookup(World world, BoundingBox boundingBox, int offset) { + return performAreaQuery(world, boundingBox, offset, "block", + "time,user,action,type,data,blockdata,rolled_back,x,y,z,wid", + "rowid DESC", // Maintain compatibility with original BlockAPI + BoxAPI::processBlockResult); + } + + /** + * Performs a lookup of container-related actions within the specified bounding box. + */ + public static List performAreaContainerLookup(World world, BoundingBox boundingBox, int offset) { + return performAreaQuery(world, boundingBox, offset, "container", + "time,user,type,data,amount,metadata,action,rolled_back,x,y,z", + "rowid DESC", + (rs, conn) -> processContainerResult(rs, conn, world)); + } + + /** + * Performs a lookup of block-related actions within a cubic area around a center point. + * Note: This creates a cubic area, not a spherical radius. + */ + public static List performCubicLookup(World world, int centerX, int centerY, int centerZ, int radius, int offset) { + if (world == null || radius < 0) { + return new ArrayList<>(); + } + + BoundingBox boundingBox = new BoundingBox( + centerX - radius, centerY - radius, centerZ - radius, + centerX + radius + 1, centerY + radius + 1, centerZ + radius + 1 + ); + + return performAreaLookup(world, boundingBox, offset); + } + + /** + * Performs a lookup of container-related actions within a cubic area around a center point. + * Note: This creates a cubic area, not a spherical radius. + */ + public static List performCubicContainerLookup(World world, int centerX, int centerY, int centerZ, int radius, int offset) { + if (world == null || radius < 0) { + return new ArrayList<>(); + } + + BoundingBox boundingBox = new BoundingBox( + centerX - radius, centerY - radius, centerZ - radius, + centerX + radius + 1, centerY + radius + 1, centerZ + radius + 1 + ); + + return performAreaContainerLookup(world, boundingBox, offset); + } + + /** + * Performs a lookup of block-related actions within a spherical radius around a center point. + * This filters results to only include blocks within the actual spherical distance. + */ + public static List performSphericalLookup(World world, int centerX, int centerY, int centerZ, int radius, int offset) { + if (world == null || radius < 0) { + return new ArrayList<>(); + } + + // First get all blocks in the cubic area + List cubicResults = performCubicLookup(world, centerX, centerY, centerZ, radius, offset); + + // Filter to only include blocks within spherical distance + List sphericalResults = new ArrayList<>(); + double radiusSquared = radius * radius; + + for (String[] result : cubicResults) { + try { + int x = Integer.parseInt(result[2]); + int y = Integer.parseInt(result[3]); + int z = Integer.parseInt(result[4]); + + double distanceSquared = Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2) + Math.pow(z - centerZ, 2); + if (distanceSquared <= radiusSquared) { + sphericalResults.add(result); + } + } catch (NumberFormatException e) { + // Skip malformed results + continue; + } + } + + return sphericalResults; + } + + /** + * Performs a lookup of container-related actions within a spherical radius around a center point. + * This filters results to only include containers within the actual spherical distance. + */ + public static List performSphericalContainerLookup(World world, int centerX, int centerY, int centerZ, int radius, int offset) { + if (world == null || radius < 0) { + return new ArrayList<>(); + } + + // First get all containers in the cubic area + List cubicResults = performCubicContainerLookup(world, centerX, centerY, centerZ, radius, offset); + + // Filter to only include containers within spherical distance + List sphericalResults = new ArrayList<>(); + double radiusSquared = radius * radius; + + for (ContainerResult result : cubicResults) { + double distanceSquared = Math.pow(result.getX() - centerX, 2) + + Math.pow(result.getY() - centerY, 2) + + Math.pow(result.getZ() - centerZ, 2); + if (distanceSquared <= radiusSquared) { + sphericalResults.add(result); + } + } + + return sphericalResults; + } +} diff --git a/src/main/java/net/coreprotect/api/result/ContainerResult.java b/src/main/java/net/coreprotect/api/result/ContainerResult.java new file mode 100644 index 000000000..d57c6f67f --- /dev/null +++ b/src/main/java/net/coreprotect/api/result/ContainerResult.java @@ -0,0 +1,202 @@ +package net.coreprotect.api.result; + +import net.coreprotect.utility.MaterialUtils; +import net.coreprotect.utility.WorldUtils; +import org.bukkit.Material; + +/** + * Represents the result of a container action lookup with typed fields. + */ +public class ContainerResult { + private final long time; + private final String username; + private final String world; + private final int x; + private final int y; + private final int z; + private final int type; + private final int data; + private final int amount; + private final byte[] metadata; + private final int action; + private final int rolledBack; + + /** + * Creates a new ContainerResult with the specified parameters. + * + * @param time The timestamp in seconds + * @param username The username of the player who performed the action + * @param world The world name where the action occurred + * @param x The X coordinate + * @param y The Y coordinate + * @param z The Z coordinate + * @param type The material type ID + * @param data The data value + * @param amount The amount of items + * @param metadata The item metadata + * @param action The action ID + * @param rolledBack Whether the action has been rolled back + * @throws IllegalArgumentException if validation fails + */ + public ContainerResult(long time, String username, String world, int x, int y, int z, + int type, int data, int amount, byte[] metadata, int action, int rolledBack) { + // Validation + if (time < 0) { + throw new IllegalArgumentException("Time cannot be negative"); + } + if (username == null || username.trim().isEmpty()) { + throw new IllegalArgumentException("Username cannot be null or empty"); + } + if (type < 0) { + throw new IllegalArgumentException("Type cannot be negative"); + } + if (data < 0) { + throw new IllegalArgumentException("Data cannot be negative"); + } + if (amount < 0) { + throw new IllegalArgumentException("Amount cannot be negative"); + } + if (action < 0) { + throw new IllegalArgumentException("Action cannot be negative"); + } + if (rolledBack < 0) { + throw new IllegalArgumentException("Rolled back cannot be negative"); + } + + this.time = time; + this.username = username.trim(); + this.world = world; + this.x = x; + this.y = y; + this.z = z; + this.type = type; + this.data = data; + this.amount = amount; + this.metadata = metadata != null ? metadata.clone() : null; + this.action = action; + this.rolledBack = rolledBack; + } + + /** + * Gets the action ID. + * + * @return The action ID + */ + public int getActionId() { + return action; + } + + /** + * Gets the action as a human-readable string. + * + * @return "remove", "add", or "unknown" + */ + public String getActionString() { + if (action == 0) { + return "remove"; + } else if (action == 1) { + return "add"; + } + return "unknown"; + } + + /** + * Gets the data value. + * + * @return The data value + */ + public int getData() { + return data; + } + + /** + * Gets the amount of items. + * + * @return The amount + */ + public int getAmount() { + return amount; + } + + /** + * Gets the username of the player who performed the action. + * + * @return The player username + */ + public String getPlayer() { + return username; + } + + /** + * Gets the timestamp in milliseconds. + * + * @return The timestamp in milliseconds + */ + public long getTimestamp() { + return time * 1000L; + } + + /** + * Gets the material type of the item. + * + * @return The Material, or Material.AIR if unknown + */ + public Material getType() { + Material material = MaterialUtils.getType(type); + return material != null ? material : Material.AIR; + } + + /** + * Gets the X coordinate. + * + * @return The X coordinate + */ + public int getX() { + return x; + } + + /** + * Gets the Y coordinate. + * + * @return The Y coordinate + */ + public int getY() { + return y; + } + + /** + * Gets the Z coordinate. + * + * @return The Z coordinate + */ + public int getZ() { + return z; + } + + /** + * Gets the item metadata. + * + * @return A copy of the metadata array, or null if no metadata + */ + public byte[] getMetadata() { + return metadata != null ? metadata.clone() : null; + } + + /** + * Checks if this action has been rolled back. + * + * @return true if rolled back, false otherwise + */ + public boolean isRolledBack() { + return rolledBack == 1 || rolledBack == 3; + } + + /** + * Gets the world name where the action occurred. + * + * @return The world name + */ + public String worldName() { + return world; + } +} From 644307a4151052dda1e7cfdee33750b6ae2b4b17 Mon Sep 17 00:00:00 2001 From: Restitutor Date: Sun, 8 Feb 2026 15:39:39 -0500 Subject: [PATCH 2/2] Added area container lookup and "v12" docs --- docs/api/version/v11.md | 47 ++++++++++++++++++- .../java/net/coreprotect/CoreProtectAPI.java | 18 +++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/docs/api/version/v11.md b/docs/api/version/v11.md index 4453dad49..e2357ffff 100644 --- a/docs/api/version/v11.md +++ b/docs/api/version/v11.md @@ -94,6 +94,10 @@ List sessionLookup(String user, int time) List queueLookup(Block block) +List containerLookup(Location location, int time) + +List containerLookup(World world, BoundingBox boundingBox, int time) + ParseResult parseResult(String[] result) boolean logChat(Player player, String message) @@ -224,6 +228,25 @@ This will perform a session lookup on a single player. --- +#### `containerLookup(Location location, int time)` + +This will perform a container lookup at a single location. + +* **location:** The location to perform the lookup on. +* **time:** Specify the amount of time to search back. "5" would return results from the last 5 seconds. Use "0" for no time constraint. + +--- + +#### `containerLookup(World world, BoundingBox boundingBox, int time)` + +This will perform a container lookup within a bounding box area. + +* **world:** The world to perform the lookup in. +* **boundingBox:** The bounding box defining the area to search within. +* **time:** Specify the amount of time to search back. "5" would return results from the last 5 seconds. Use "0" for no time constraint. + +--- + #### `ParseResult parseResult(String[] result)` This will parse results from a lookup. You'll then be able to view the following: @@ -518,13 +541,33 @@ class BasicThread implements Runnable { } } catch (Exception e){ - e.printStackTrace(); + e.printStackTrace(); } } } Runnable runnable = new BasicThread(); Thread thread = new Thread(runnable); thread.start(); -``` +``` + +--- + +- Get the last 5 minutes of container transactions within a bounding box area. +```java +CoreProtectAPI CoreProtect = getCoreProtect(); +if (CoreProtect != null){ // Ensure we have access to the API + World world = Bukkit.getWorld("world"); + BoundingBox area = new BoundingBox(100, 60, 100, 200, 80, 200); + List lookup = CoreProtect.containerLookup(world, area, (5 * 60)); + if (lookup != null){ + for (ContainerResult result : lookup){ + int x = result.getX(); + int y = result.getY(); + int z = result.getZ(); + // ... + } + } +} +``` --- \ No newline at end of file diff --git a/src/main/java/net/coreprotect/CoreProtectAPI.java b/src/main/java/net/coreprotect/CoreProtectAPI.java index dfab55a94..5a306d59b 100755 --- a/src/main/java/net/coreprotect/CoreProtectAPI.java +++ b/src/main/java/net/coreprotect/CoreProtectAPI.java @@ -12,13 +12,16 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Server; +import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.block.data.BlockData; import org.bukkit.entity.EntityType; +import org.bukkit.util.BoundingBox; import org.bukkit.entity.Player; import net.coreprotect.api.BlockAPI; +import net.coreprotect.api.BoxAPI; import net.coreprotect.api.QueueLookup; import net.coreprotect.api.SessionLookup; import net.coreprotect.config.Config; @@ -132,6 +135,21 @@ public List containerLookup(Location location, int time) { return null; } + /** + * Performs a lookup of container-related actions within the specified bounding box area. + * + * @param world The world to perform the lookup in + * @param boundingBox The bounding box defining the area to search + * @param time Time constraint in seconds (0 means no time constraint) + * @return List of results in a ContainerResult list format, or null if API is disabled + */ + public List containerLookup(World world, BoundingBox boundingBox, int time) { + if (isEnabled()) { + return BoxAPI.performAreaContainerLookup(world, boundingBox, time); + } + return null; + } + /** * Performs a lookup on session data for the specified user. *