Added typed API helpers

This commit is contained in:
Intelli committed 2026-05-06 13:41:46 -06:00
1 parent cebcc8655a
commit 9924a9376e
13 files changed
+419 -122

No files matched your search

+3
View File
@@ -216,6 +216,9 @@ hs_err_pid*
# Documentation dev folder
docs/dev/
# API Test Plugin
CoreProtect-TestAPI
# Extensions
/src/main/java/net/coreprotect/utility/extensions
!/src/main/java/net/coreprotect/utility/extensions/Extensions.java
+62
View File
@@ -47,6 +47,13 @@ List<SignResult> signLookup(String user, int time)
List<SignResult> signLookup(String user, int time, int radius, Location radius_location)
```
- Added `SignResult` for reading sign text data without parsing `String[]` lookup rows.
- Added shared typed API helpers:
```java
LookupOptions
CoreProtectResult
CoreProtectAction
```
- `chatLookup`, `commandLookup`, and `signLookup` now support `LookupOptions` overloads for shared user, time, location, radius, and limit options.
---
@@ -120,14 +127,20 @@ List<ContainerResult> containerLookup(Location location, int time, int radius)
List<MessageResult> chatLookup(String user, int time)
List<MessageResult> chatLookup(LookupOptions options)
List<MessageResult> chatLookup(String user, int time, int radius, Location radius_location)
List<MessageResult> commandLookup(String user, int time)
List<MessageResult> commandLookup(LookupOptions options)
List<MessageResult> commandLookup(String user, int time, int radius, Location radius_location)
List<SignResult> signLookup(Location location, int time)
List<SignResult> signLookup(LookupOptions options)
List<SignResult> signLookup(Location location, int time, int radius)
List<SignResult> signLookup(String user, int time)
@@ -190,6 +203,31 @@ Fired when a CoreProtect logger is about to log an action. Cancellable.
*Detailed method information is listed below.*
#### Shared typed API helpers
`LookupOptions` can be used with typed lookup methods that support shared filters.
```java
LookupOptions options = LookupOptions.builder()
.user("Notch")
.time(60)
.radius(location, 5)
.limit(0, 100)
.build();
```
* **user(String user):** Specify the username to perform the lookup on. Use `null` or `#global` to search all users.
* **time(int time):** Specify the amount of time to lookup in seconds.
* **location(Location location):** Search an exact location.
* **radius(Location location, int radius):** Search around a location in the X/Z plane. Use `0` for the exact location.
* **limit(int offset, int count):** Limit the returned result range.
`ContainerResult`, `MessageResult`, `SignResult`, and `ParseResult` implement `CoreProtectResult`, which provides shared access to player, timestamp, coordinates, world, and action data.
`CoreProtectAction` provides named constants for lookup filter action IDs while retaining integer action IDs for backward compatibility.
---
#### `isEnabled()`
Calling this will return true if the server has the CoreProtect API enabled, and false if it does not.
@@ -300,6 +338,14 @@ This will perform a chat message lookup for a user. Use `null` or `#global` to s
---
#### `chatLookup(LookupOptions options)`
This will perform a chat message lookup using shared lookup options.
* **options:** Specify user, time, location, radius, and limit options.
---
#### `chatLookup(String user, int time, int radius, Location radius_location)`
This will perform a chat message lookup around a location, similar to running `/co lookup a:chat r:<radius>`.
@@ -320,6 +366,14 @@ This will perform a command lookup for a user. Use `null` or `#global` to search
---
#### `commandLookup(LookupOptions options)`
This will perform a command lookup using shared lookup options.
* **options:** Specify user, time, location, radius, and limit options.
---
#### `commandLookup(String user, int time, int radius, Location radius_location)`
This will perform a command lookup around a location, similar to running `/co lookup a:command r:<radius>`.
@@ -352,6 +406,14 @@ This will perform a sign text lookup at a single location.
---
#### `signLookup(LookupOptions options)`
This will perform a sign text lookup using shared lookup options.
* **options:** Specify user, time, location, radius, and limit options.
---
#### `signLookup(Location location, int time, int radius)`
This will perform a sign text lookup around a location, similar to running `/co lookup a:sign r:<radius>`.
@@ -18,6 +18,7 @@ import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import net.coreprotect.api.BlockAPI;
import net.coreprotect.api.LookupOptions;
import net.coreprotect.api.MessageAPI;
import net.coreprotect.api.QueueLookup;
import net.coreprotect.api.SessionLookup;
@@ -185,6 +186,20 @@ public class CoreProtectAPI extends Queue {
return null;
}
/**
* Performs a chat message lookup with shared lookup options.
*
* @param options
* Lookup options
* @return List of results or null if API is disabled
*/
public List<MessageResult> chatLookup(LookupOptions options) {
if (isEnabled()) {
return MessageAPI.performChatLookup(options);
}
return null;
}
/**
* Performs a chat message lookup around the specified location.
*
@@ -221,6 +236,20 @@ public class CoreProtectAPI extends Queue {
return null;
}
/**
* Performs a command lookup with shared lookup options.
*
* @param options
* Lookup options
* @return List of results or null if API is disabled
*/
public List<MessageResult> commandLookup(LookupOptions options) {
if (isEnabled()) {
return MessageAPI.performCommandLookup(options);
}
return null;
}
/**
* Performs a command lookup around the specified location.
*
@@ -257,6 +286,20 @@ public class CoreProtectAPI extends Queue {
return null;
}
/**
* Performs a sign text lookup with shared lookup options.
*
* @param options
* Lookup options
* @return List of results or null if API is disabled
*/
public List<SignResult> signLookup(LookupOptions options) {
if (isEnabled()) {
return SignAPI.performLookup(options);
}
return null;
}
/**
* Performs a sign text lookup around the specified location.
*
@@ -0,0 +1,45 @@
package net.coreprotect.api;
/**
* CoreProtect action identifiers used by lookup filters.
*/
public enum CoreProtectAction {
BLOCK_BREAK(0, "break"),
BLOCK_PLACE(1, "place"),
INTERACTION(2, "click"),
ENTITY_KILL(3, "kill"),
CONTAINER(4, "container"),
CHAT(6, "chat"),
COMMAND(7, "command"),
SESSION(8, "session"),
USERNAME(9, "username"),
SIGN(10, "sign"),
ITEM(11, "item"),
UNKNOWN(-1, "unknown");
private final int id;
private final String actionString;
CoreProtectAction(int id, String actionString) {
this.id = id;
this.actionString = actionString;
}
public int id() {
return id;
}
public String actionString() {
return actionString;
}
public static CoreProtectAction fromId(int id) {
for (CoreProtectAction action : values()) {
if (action.id == id) {
return action;
}
}
return UNKNOWN;
}
}
@@ -0,0 +1,106 @@
package net.coreprotect.api;
import java.sql.Connection;
import java.sql.PreparedStatement;
import org.bukkit.Location;
import net.coreprotect.utility.WorldUtils;
final class LookupFilter {
private final Integer userId;
private final int checkTime;
private final Location location;
private final int radius;
private final int limitOffset;
private final int limitCount;
private LookupFilter(Integer userId, int checkTime, Location location, int radius, int limitOffset, int limitCount) {
this.userId = userId;
this.checkTime = checkTime;
this.location = location;
this.radius = radius;
this.limitOffset = limitOffset;
this.limitCount = limitCount;
}
static LookupFilter fromOptions(Connection connection, LookupOptions options) throws Exception {
if (options == null) {
options = LookupOptions.builder().build();
}
Integer userId = MessageAPI.getUserId(connection, options.getUser());
int checkTime = 0;
if (options.getTime() > 0) {
checkTime = (int) (System.currentTimeMillis() / 1000L) - options.getTime();
}
return new LookupFilter(userId, checkTime, options.getLocation(), options.getRadius(), options.getLimitOffset(), options.getLimitCount());
}
boolean hasInvalidUser() {
return userId != null && userId == -1;
}
boolean hasInvalidLocation() {
return location != null && location.getWorld() == null;
}
boolean hasLocation() {
return location != null;
}
void appendWhere(StringBuilder query) {
query.append("WHERE time > ?");
if (userId != null) {
query.append(" AND user = ?");
}
if (location != null) {
query.append(" AND wid = ?");
if (radius > 0) {
query.append(" AND x >= ? AND x <= ? AND z >= ? AND z <= ?");
}
else {
query.append(" AND x = ? AND y = ? AND z = ?");
}
}
}
void appendLimit(StringBuilder query) {
if (limitOffset >= 0 && limitCount >= 0) {
query.append(" LIMIT ").append(limitOffset).append(", ").append(limitCount);
}
}
int bind(PreparedStatement statement) throws Exception {
int parameterIndex = 1;
statement.setInt(parameterIndex++, checkTime);
if (userId != null) {
statement.setInt(parameterIndex++, userId);
}
if (location != null) {
int x = location.getBlockX();
int y = location.getBlockY();
int z = location.getBlockZ();
statement.setInt(parameterIndex++, WorldUtils.getWorldId(location.getWorld().getName()));
if (radius > 0) {
statement.setInt(parameterIndex++, MessageAPI.clampToInt((long) x - radius));
statement.setInt(parameterIndex++, MessageAPI.clampToInt((long) x + radius));
statement.setInt(parameterIndex++, MessageAPI.clampToInt((long) z - radius));
statement.setInt(parameterIndex++, MessageAPI.clampToInt((long) z + radius));
}
else {
statement.setInt(parameterIndex++, x);
statement.setInt(parameterIndex++, y);
statement.setInt(parameterIndex++, z);
}
}
return parameterIndex;
}
}
@@ -0,0 +1,100 @@
package net.coreprotect.api;
import org.bukkit.Location;
/**
* Shared options for typed lookup API methods.
*/
public final class LookupOptions {
private final String user;
private final int time;
private final int radius;
private final Location location;
private final int limitOffset;
private final int limitCount;
private LookupOptions(Builder builder) {
this.user = builder.user;
this.time = builder.time;
this.radius = builder.radius;
this.location = builder.location;
this.limitOffset = builder.limitOffset;
this.limitCount = builder.limitCount;
}
public static Builder builder() {
return new Builder();
}
public String getUser() {
return user;
}
public int getTime() {
return time;
}
public int getRadius() {
return radius;
}
public Location getLocation() {
return location;
}
public int getLimitOffset() {
return limitOffset;
}
public int getLimitCount() {
return limitCount;
}
public boolean hasLimit() {
return limitOffset >= 0 && limitCount >= 0;
}
public static final class Builder {
private String user;
private int time;
private int radius = -1;
private Location location;
private int limitOffset = -1;
private int limitCount = -1;
private Builder() {
}
public Builder user(String user) {
this.user = user;
return this;
}
public Builder time(int time) {
this.time = time;
return this;
}
public Builder location(Location location) {
this.location = location;
this.radius = 0;
return this;
}
public Builder radius(Location location, int radius) {
this.location = location;
this.radius = radius;
return this;
}
public Builder limit(int offset, int count) {
this.limitOffset = offset;
this.limitCount = count;
return this;
}
public LookupOptions build() {
return new LookupOptions(this);
}
}
}
@@ -19,108 +19,66 @@ import net.coreprotect.utility.WorldUtils;
* Provides API methods for player chat and command lookups.
*/
public class MessageAPI {
private static final int CHAT_ACTION_ID = 6;
private static final int COMMAND_ACTION_ID = 7;
private MessageAPI() {
throw new IllegalStateException("API class");
}
public static List<MessageResult> performChatLookup(String user, int offset) {
return performLookup("chat", CHAT_ACTION_ID, user, offset, -1, null);
return performChatLookup(LookupOptions.builder().user(user).time(offset).build());
}
public static List<MessageResult> performChatLookup(String user, int offset, int radius, Location location) {
return performLookup("chat", CHAT_ACTION_ID, user, offset, radius, location);
return performChatLookup(LookupOptions.builder().user(user).time(offset).radius(location, radius).build());
}
public static List<MessageResult> performChatLookup(LookupOptions options) {
return performLookup("chat", CoreProtectAction.CHAT, options);
}
public static List<MessageResult> performCommandLookup(String user, int offset) {
return performLookup("command", COMMAND_ACTION_ID, user, offset, -1, null);
return performCommandLookup(LookupOptions.builder().user(user).time(offset).build());
}
public static List<MessageResult> performCommandLookup(String user, int offset, int radius, Location location) {
return performLookup("command", COMMAND_ACTION_ID, user, offset, radius, location);
return performCommandLookup(LookupOptions.builder().user(user).time(offset).radius(location, radius).build());
}
private static List<MessageResult> performLookup(String table, int actionId, String user, int offset, int radius, Location location) {
public static List<MessageResult> performCommandLookup(LookupOptions options) {
return performLookup("command", CoreProtectAction.COMMAND, options);
}
private static List<MessageResult> performLookup(String table, CoreProtectAction action, LookupOptions options) {
List<MessageResult> result = new ArrayList<>();
if (!Config.getGlobal().API_ENABLED) {
return result;
}
if (location != null && location.getWorld() == null) {
return result;
}
try (Connection connection = Database.getConnection(false, 1000)) {
if (connection == null) {
return result;
}
Integer userId = getUserId(connection, user);
if (userId != null && userId == -1) {
LookupFilter filter = LookupFilter.fromOptions(connection, options);
if (filter.hasInvalidUser() || filter.hasInvalidLocation()) {
return result;
}
int checkTime = 0;
if (offset > 0) {
checkTime = (int) (System.currentTimeMillis() / 1000L) - offset;
}
StringBuilder query = new StringBuilder("SELECT time,user,wid,x,y,z,message FROM ");
query.append(ConfigHandler.prefix).append(table).append(" ");
if (location != null) {
if (filter.hasLocation()) {
query.append(WorldUtils.getWidIndex(table));
}
query.append("WHERE time > ?");
if (userId != null) {
query.append(" AND user = ?");
}
if (location != null) {
query.append(" AND wid = ?");
if (radius > 0) {
query.append(" AND x >= ? AND x <= ? AND z >= ? AND z <= ?");
}
else {
query.append(" AND x = ? AND y = ? AND z = ?");
}
}
filter.appendWhere(query);
query.append(" ORDER BY rowid DESC");
filter.appendLimit(query);
try (PreparedStatement statement = connection.prepareStatement(query.toString())) {
int parameterIndex = 1;
statement.setInt(parameterIndex++, checkTime);
if (userId != null) {
statement.setInt(parameterIndex++, userId);
}
if (location != null) {
int x = location.getBlockX();
int y = location.getBlockY();
int z = location.getBlockZ();
statement.setInt(parameterIndex++, WorldUtils.getWorldId(location.getWorld().getName()));
if (radius > 0) {
statement.setInt(parameterIndex++, clampToInt((long) x - radius));
statement.setInt(parameterIndex++, clampToInt((long) x + radius));
statement.setInt(parameterIndex++, clampToInt((long) z - radius));
statement.setInt(parameterIndex++, clampToInt((long) z + radius));
}
else {
statement.setInt(parameterIndex++, x);
statement.setInt(parameterIndex++, y);
statement.setInt(parameterIndex, z);
}
}
filter.bind(statement);
try (ResultSet results = statement.executeQuery()) {
while (results.next()) {
result.add(parseMessageResult(connection, results, actionId));
result.add(parseMessageResult(connection, results, action.id()));
}
}
}
+14 -56
View File
@@ -25,92 +25,50 @@ public class SignAPI {
}
public static List<SignResult> performLookup(Location location, int offset) {
return performLookup(null, offset, 0, location);
return performLookup(LookupOptions.builder().time(offset).location(location).build());
}
public static List<SignResult> performLookup(Location location, int offset, int radius) {
return performLookup(null, offset, radius, location);
return performLookup(LookupOptions.builder().time(offset).radius(location, radius).build());
}
public static List<SignResult> performLookup(String user, int offset) {
return performLookup(user, offset, -1, null);
return performLookup(LookupOptions.builder().user(user).time(offset).build());
}
public static List<SignResult> performLookup(String user, int offset, int radius, Location location) {
return performLookup(LookupOptions.builder().user(user).time(offset).radius(location, radius).build());
}
public static List<SignResult> performLookup(LookupOptions options) {
List<SignResult> result = new ArrayList<>();
if (!Config.getGlobal().API_ENABLED) {
return result;
}
if (location != null && location.getWorld() == null) {
return result;
}
try (Connection connection = Database.getConnection(false, 1000)) {
if (connection == null) {
return result;
}
Integer userId = MessageAPI.getUserId(connection, user);
if (userId != null && userId == -1) {
LookupFilter filter = LookupFilter.fromOptions(connection, options);
if (filter.hasInvalidUser() || filter.hasInvalidLocation()) {
return result;
}
int checkTime = 0;
if (offset > 0) {
checkTime = (int) (System.currentTimeMillis() / 1000L) - offset;
}
StringBuilder query = new StringBuilder("SELECT time,user,wid,x,y,z,action,color,color_secondary,data,waxed,face,line_1,line_2,line_3,line_4,line_5,line_6,line_7,line_8 FROM ");
query.append(ConfigHandler.prefix).append("sign ");
if (location != null) {
if (filter.hasLocation()) {
query.append(WorldUtils.getWidIndex("sign"));
}
query.append("WHERE time > ? AND action = '1' AND (LENGTH(line_1) > 0 OR LENGTH(line_2) > 0 OR LENGTH(line_3) > 0 OR LENGTH(line_4) > 0 OR LENGTH(line_5) > 0 OR LENGTH(line_6) > 0 OR LENGTH(line_7) > 0 OR LENGTH(line_8) > 0)");
if (userId != null) {
query.append(" AND user = ?");
}
if (location != null) {
query.append(" AND wid = ?");
if (radius > 0) {
query.append(" AND x >= ? AND x <= ? AND z >= ? AND z <= ?");
}
else {
query.append(" AND x = ? AND y = ? AND z = ?");
}
}
filter.appendWhere(query);
query.append(" AND action = '1' AND (LENGTH(line_1) > 0 OR LENGTH(line_2) > 0 OR LENGTH(line_3) > 0 OR LENGTH(line_4) > 0 OR LENGTH(line_5) > 0 OR LENGTH(line_6) > 0 OR LENGTH(line_7) > 0 OR LENGTH(line_8) > 0)");
query.append(" ORDER BY rowid DESC");
filter.appendLimit(query);
try (PreparedStatement statement = connection.prepareStatement(query.toString())) {
int parameterIndex = 1;
statement.setInt(parameterIndex++, checkTime);
if (userId != null) {
statement.setInt(parameterIndex++, userId);
}
if (location != null) {
int x = location.getBlockX();
int y = location.getBlockY();
int z = location.getBlockZ();
statement.setInt(parameterIndex++, WorldUtils.getWorldId(location.getWorld().getName()));
if (radius > 0) {
statement.setInt(parameterIndex++, MessageAPI.clampToInt((long) x - radius));
statement.setInt(parameterIndex++, MessageAPI.clampToInt((long) x + radius));
statement.setInt(parameterIndex++, MessageAPI.clampToInt((long) z - radius));
statement.setInt(parameterIndex++, MessageAPI.clampToInt((long) z + radius));
}
else {
statement.setInt(parameterIndex++, x);
statement.setInt(parameterIndex++, y);
statement.setInt(parameterIndex, z);
}
}
filter.bind(statement);
try (ResultSet results = statement.executeQuery()) {
while (results.next()) {
@@ -7,7 +7,7 @@ import net.coreprotect.utility.MaterialUtils;
/**
* Represents the result of a container action lookup with typed fields.
*/
public class ContainerResult {
public class ContainerResult implements CoreProtectResult {
private final long time;
private final String username;
private final String world;
@@ -0,0 +1,22 @@
package net.coreprotect.api.result;
/**
* Common fields shared by typed CoreProtect lookup results.
*/
public interface CoreProtectResult {
int getActionId();
String getActionString();
String getPlayer();
long getTimestamp();
int getX();
int getY();
int getZ();
String worldName();
}
@@ -3,7 +3,7 @@ package net.coreprotect.api.result;
/**
* Represents a logged player chat message or command.
*/
public class MessageResult {
public class MessageResult implements CoreProtectResult {
private final long time;
private final String username;
private final String world;
@@ -14,7 +14,7 @@ import net.coreprotect.utility.MaterialUtils;
import net.coreprotect.utility.StringUtils;
import net.coreprotect.utility.WorldUtils;
public class ParseResult {
public class ParseResult implements CoreProtectResult {
private final String[] parse;
public ParseResult(String[] data) {
@@ -5,7 +5,7 @@ import net.coreprotect.utility.BlockUtils;
/**
* Represents logged sign text.
*/
public class SignResult {
public class SignResult implements CoreProtectResult {
private final long time;
private final String username;
private final String world;