diff --git a/docs/api/version/v13.md b/docs/api/version/v13.md index 4320cf28d..adc8438b0 100644 --- a/docs/api/version/v13.md +++ b/docs/api/version/v13.md @@ -10,7 +10,7 @@ API version 13 adds entity-spawn lookup support while retaining all API version ## Upgrading from API v12 -- `LookupOptions` supports material inclusion/exclusion filters for typed container, item, and inventory lookups. +- `LookupOptions` supports material inclusion/exclusion filters for typed container, item, and inventory lookups, and `users(List)` / `excludeUsers(List)` filters for all typed lookups. - Added `CoreProtectAction.ENTITY_SPAWN` with action ID `13`. - Added `CoreProtectPreLogEvent.Action.ENTITY_SPAWN`. - `BlockResult#getEntityType()` recognizes entity-spawn results. diff --git a/src/main/java/net/coreprotect/api/BlockAPI.java b/src/main/java/net/coreprotect/api/BlockAPI.java index 6f5a92a0c..008ae2d9c 100644 --- a/src/main/java/net/coreprotect/api/BlockAPI.java +++ b/src/main/java/net/coreprotect/api/BlockAPI.java @@ -158,6 +158,7 @@ public static List performLookup(Block block, LookupOptions options if (userId != null) { query.append(" AND ").append(ConfigHandler.databaseType.getUserColumn()).append(" = ?"); } + LookupFilter.appendUserWhere(query, "", LookupFilter.userIds(connection, options.getUsers()), LookupFilter.userIds(connection, options.getExcludeUsers())); query.append(" ORDER BY ").append(ConfigHandler.getDescendingEventOrder()); if (options.hasLimit()) { query.append(" LIMIT ").append(options.getLimitCount()).append(" OFFSET ").append(options.getLimitOffset()); diff --git a/src/main/java/net/coreprotect/api/LookupFilter.java b/src/main/java/net/coreprotect/api/LookupFilter.java index 73753e485..f43f50c32 100644 --- a/src/main/java/net/coreprotect/api/LookupFilter.java +++ b/src/main/java/net/coreprotect/api/LookupFilter.java @@ -30,8 +30,10 @@ final class LookupFilter { private final List includeMaterials; private final List excludeMaterials; private final Map materialTypes; + private final String includeUserIds; + private final String excludeUserIds; - private LookupFilter(Integer userId, int checkTime, Location location, int radius, int limitOffset, int limitCount, List includeMaterials, List excludeMaterials, Map materialTypes) { + private LookupFilter(Integer userId, int checkTime, Location location, int radius, int limitOffset, int limitCount, List includeMaterials, List excludeMaterials, Map materialTypes, String includeUserIds, String excludeUserIds) { this.userId = userId; this.checkTime = checkTime; this.location = location; @@ -41,6 +43,8 @@ private LookupFilter(Integer userId, int checkTime, Location location, int radiu this.includeMaterials = includeMaterials; this.excludeMaterials = excludeMaterials; this.materialTypes = materialTypes; + this.includeUserIds = includeUserIds; + this.excludeUserIds = excludeUserIds; } static LookupFilter fromOptions(Connection connection, LookupOptions options) throws Exception { @@ -65,7 +69,8 @@ static LookupFilter fromOptions(Connection connection, LookupOptions options) th } return new LookupFilter(userId, checkTime, options.getLocation(), options.getRadius(), options.getLimitOffset(), options.getLimitCount(), - options.getIncludeMaterials(), options.getExcludeMaterials(), materialTypes); + options.getIncludeMaterials(), options.getExcludeMaterials(), materialTypes, + userIds(connection, options.getUsers()), userIds(connection, options.getExcludeUsers())); } boolean hasInvalidUser() { @@ -111,6 +116,7 @@ void appendWhere(StringBuilder query, String alias) { if (userId != null) { query.append(" AND ").append(qualifier).append(ConfigHandler.databaseType.getUserColumn()).append(" = ?"); } + appendUserWhere(query, alias, includeUserIds, excludeUserIds); if (location != null) { query.append(" AND ").append(qualifier).append("wid = ?"); @@ -130,6 +136,7 @@ void appendEntityContainerWhere(StringBuilder query, String transactionAlias, St if (userId != null) { query.append(" AND ").append(transaction).append(ConfigHandler.databaseType.getUserColumn()).append(" = ?"); } + appendUserWhere(query, transactionAlias, includeUserIds, excludeUserIds); if (location == null) { return; } @@ -338,6 +345,32 @@ int bindEntityContainer(PreparedStatement statement, int parameterIndex) throws return parameterIndex; } + static String userIds(Connection connection, List users) throws Exception { + StringJoiner result = new StringJoiner(","); + for (String user : users) { + Integer id = MessageAPI.getUserId(connection, user); + if (id == null) { + // An empty name or #global matches every user. + return null; + } + result.add(String.valueOf(id)); + } + return result.toString(); + } + + static void appendUserWhere(StringBuilder query, String alias, String includeUserIds, String excludeUserIds) { + String column = (alias.isEmpty() ? "" : alias + ".") + ConfigHandler.databaseType.getUserColumn(); + if (includeUserIds != null && !includeUserIds.isEmpty()) { + query.append(" AND ").append(column).append(" IN (").append(includeUserIds).append(")"); + } + if (excludeUserIds == null) { + query.append(" AND 1 = 0"); + } + else if (!excludeUserIds.isEmpty()) { + query.append(" AND ").append(column).append(" NOT IN (").append(excludeUserIds).append(")"); + } + } + private String materialIds(List materials, boolean inventoryBlock) { StringJoiner result = new StringJoiner(","); for (Map.Entry entry : materialTypes.entrySet()) { diff --git a/src/main/java/net/coreprotect/api/LookupOptions.java b/src/main/java/net/coreprotect/api/LookupOptions.java index 491b04f65..31256f3ce 100644 --- a/src/main/java/net/coreprotect/api/LookupOptions.java +++ b/src/main/java/net/coreprotect/api/LookupOptions.java @@ -17,6 +17,8 @@ public final class LookupOptions { private final int limitCount; private final List includeMaterials; private final List excludeMaterials; + private final List users; + private final List excludeUsers; private LookupOptions(Builder builder) { this.user = builder.user; @@ -27,6 +29,8 @@ private LookupOptions(Builder builder) { this.limitCount = builder.limitCount; this.includeMaterials = builder.includeMaterials; this.excludeMaterials = builder.excludeMaterials; + this.users = builder.users; + this.excludeUsers = builder.excludeUsers; } public static Builder builder() { @@ -69,6 +73,14 @@ public List getExcludeMaterials() { return excludeMaterials; } + public List getUsers() { + return users; + } + + public List getExcludeUsers() { + return excludeUsers; + } + public static final class Builder { private String user; private int time; @@ -78,6 +90,8 @@ public static final class Builder { private int limitCount = -1; private List includeMaterials = List.of(); private List excludeMaterials = List.of(); + private List users = List.of(); + private List excludeUsers = List.of(); private Builder() { } @@ -120,6 +134,16 @@ public Builder excludeMaterials(List materials) { return this; } + public Builder users(List users) { + this.users = List.copyOf(users); + return this; + } + + public Builder excludeUsers(List users) { + this.excludeUsers = List.copyOf(users); + return this; + } + public LookupOptions build() { return new LookupOptions(this); } diff --git a/src/main/java/net/coreprotect/api/UsernameAPI.java b/src/main/java/net/coreprotect/api/UsernameAPI.java index 5e7342afc..568558e46 100644 --- a/src/main/java/net/coreprotect/api/UsernameAPI.java +++ b/src/main/java/net/coreprotect/api/UsernameAPI.java @@ -45,11 +45,18 @@ public static List performLookup(LookupOptions options) { return result; } - Set uuids = getUuids(connection, options.getUser()); + Set uuids = getUuids(connection, options); if (uuids == null) { return result; } + Set excludedUuids = getUuids(connection, options.getExcludeUsers()); + if (excludedUuids == null) { + return result; + } + // A NULL in NOT IN would also exclude unrelated users. + excludedUuids.remove(null); + int checkTime = 0; if (options.getTime() > 0) { checkTime = (int) (System.currentTimeMillis() / 1000L) - options.getTime(); @@ -62,6 +69,11 @@ public static List performLookup(LookupOptions options) { appendPlaceholders(query, uuids.size()); query.append(")"); } + if (!excludedUuids.isEmpty()) { + query.append(" AND uuid NOT IN ("); + appendPlaceholders(query, excludedUuids.size()); + query.append(")"); + } query.append(" ORDER BY ").append(ConfigHandler.getDescendingEventOrder()); if (options.hasLimit()) { query.append(" LIMIT ").append(options.getLimitCount()).append(" OFFSET ").append(options.getLimitOffset()); @@ -73,6 +85,9 @@ public static List performLookup(LookupOptions options) { for (String uuid : uuids) { statement.setString(parameterIndex++, uuid); } + for (String uuid : excludedUuids) { + statement.setString(parameterIndex++, uuid); + } try (ResultSet results = statement.executeQuery()) { while (results.next()) { @@ -97,6 +112,25 @@ private static void appendPlaceholders(StringBuilder query, int count) { } } + private static Set getUuids(Connection connection, LookupOptions options) throws Exception { + Set result = getUuids(connection, options.getUser()); + if (result == null || options.getUsers().isEmpty()) { + return result; + } + + Set includedUuids = getUuids(connection, options.getUsers()); + if (includedUuids == null) { + return result; + } + if (result.isEmpty()) { + result.addAll(includedUuids); + } + else { + result.retainAll(includedUuids); + } + return result.isEmpty() ? null : result; + } + private static Set getUuids(Connection connection, String user) throws Exception { Set result = new LinkedHashSet<>(); @@ -130,6 +164,21 @@ private static Set getUuids(Connection connection, String user) throws E return result.isEmpty() ? null : result; } + private static Set getUuids(Connection connection, List users) throws Exception { + Set result = new LinkedHashSet<>(); + for (String user : users) { + Set matches = getUuids(connection, user); + if (matches != null) { + if (matches.isEmpty()) { + // An empty name or #global matches every user. + return null; + } + result.addAll(matches); + } + } + return result; + } + private static boolean looksLikeUuid(String value) { return value.length() == 36 && value.charAt(8) == '-' && value.charAt(13) == '-' && value.charAt(18) == '-' && value.charAt(23) == '-'; }