From 6aec5601c75f8b41a9742a9a4f3ccbc2b6a9cb5b Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Mon, 20 Apr 2026 20:14:21 +0200 Subject: [PATCH 01/38] New snapshot version. --- core/pom.xml | 2 +- core/src/main/resources/proxy-config.yml | 2 +- database/pom.xml | 2 +- plugin/pom.xml | 2 +- plugin/src/main/java/org/btuk/proxy/Proxy.java | 2 +- pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 378d69c..6248470 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,7 +6,7 @@ org.btuk Proxy - 1.11.0 + 1.12.0-SNAPSHOT org.btuk.proxy diff --git a/core/src/main/resources/proxy-config.yml b/core/src/main/resources/proxy-config.yml index fbace1e..1e031f8 100644 --- a/core/src/main/resources/proxy-config.yml +++ b/core/src/main/resources/proxy-config.yml @@ -1,5 +1,5 @@ #Config version (do not update) -version: "1.11.0" +version: "1.12.0-SNAPSHOT" #Bot token token: "insert token here" diff --git a/database/pom.xml b/database/pom.xml index b1ef94a..1df1ae9 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -4,7 +4,7 @@ org.btuk Proxy - 1.11.0 + 1.12.0-SNAPSHOT org.btuk.proxy diff --git a/plugin/pom.xml b/plugin/pom.xml index 58e5c54..dfd9751 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -7,7 +7,7 @@ org.btuk Proxy - 1.11.0 + 1.12.0-SNAPSHOT org.btuk.proxy diff --git a/plugin/src/main/java/org/btuk/proxy/Proxy.java b/plugin/src/main/java/org/btuk/proxy/Proxy.java index 3ad339f..2cb45ef 100644 --- a/plugin/src/main/java/org/btuk/proxy/Proxy.java +++ b/plugin/src/main/java/org/btuk/proxy/Proxy.java @@ -40,7 +40,7 @@ import org.btuk.proxy.core.ProxyController; -@Plugin(id = "proxy", name = "Proxy", version = "1.11.0", +@Plugin(id = "proxy", name = "Proxy", version = "1.12.0-SNAPSHOT", url = "https://github.com/BTEUK/Proxy", description = "Proxy plugin, managed chat, discord and server related actions.", authors = {"ELgamer"}) public class Proxy { diff --git a/pom.xml b/pom.xml index 9a430b4..2168ea5 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.btuk Proxy - 1.11.0 + 1.12.0-SNAPSHOT pom Proxy From 63ebb090c7b34d7467459e515ad2c54dcd4e245b Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sun, 3 May 2026 12:07:29 +0200 Subject: [PATCH 02/38] Base api implementation and new database indexes on relevant columns. --- api/pom.xml | 99 +++++++++++ .../btuk/proxy/api/impl/BuildingsApiImpl.java | 55 ++++++ .../btuk/proxy/api/impl/PlayerApiImpl.java | 43 +++++ .../btuk/proxy/api/impl/StatusApiImpl.java | 15 ++ .../org/btuk/proxy/api/server/ProxyApi.java | 61 +++++++ api/src/main/resources/openapi.yaml | 159 ++++++++++++++++++ core/pom.xml | 4 + .../org/btuk/proxy/core/ProxyController.java | 9 + .../org/btuk/proxy/core/utils/Moderation.java | 2 + core/src/main/resources/proxy-config.yml | 5 + .../btuk/proxy/database/DatabaseUpdates.java | 25 ++- .../btuk/proxy/database/sql/GlobalSQL.java | 87 +++++++++- .../src/main/resources/dbsetup_global.sql | 13 +- pom.xml | 8 +- 14 files changed, 572 insertions(+), 13 deletions(-) create mode 100644 api/pom.xml create mode 100644 api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java create mode 100644 api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java create mode 100644 api/src/main/java/org/btuk/proxy/api/impl/StatusApiImpl.java create mode 100644 api/src/main/java/org/btuk/proxy/api/server/ProxyApi.java create mode 100644 api/src/main/resources/openapi.yaml diff --git a/api/pom.xml b/api/pom.xml new file mode 100644 index 0000000..9b42680 --- /dev/null +++ b/api/pom.xml @@ -0,0 +1,99 @@ + + + 4.0.0 + + org.btuk + Proxy + 1.12.0-SNAPSHOT + + + org.btuk.proxy + api + Proxy API + + + UTF-8 + 7.4.0 + 3.1.0 + 3.1.5 + 4.0.2 + + + + + org.btuk.proxy + database + + + + org.projectlombok + lombok + + + + jakarta.ws.rs + jakarta.ws.rs-api + ${jakarta-ws-rs-api.version} + + + jakarta.annotation + jakarta.annotation-api + 2.1.1 + provided + + + + + org.glassfish.jersey.containers + jersey-container-grizzly2-http + ${jersey.version} + + + org.glassfish.jersey.inject + jersey-hk2 + ${jersey.version} + + + org.glassfish.jersey.media + jersey-media-json-jackson + ${jersey.version} + + + + + + + org.openapitools + openapi-generator-maven-plugin + ${openapi-generator.version} + + + + generate + + + ${project.basedir}/src/main/resources/openapi.yaml + jaxrs-spec + org.btuk.proxy.api + org.btuk.proxy.api.model + + true + true + true + true + false + false + false + false + false + + false + + + + + + + diff --git a/api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java b/api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java new file mode 100644 index 0000000..8a2336d --- /dev/null +++ b/api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java @@ -0,0 +1,55 @@ +package org.btuk.proxy.api.impl; + +import jakarta.ws.rs.core.Response; +import org.btuk.proxy.api.BuildingsApi; +import org.btuk.proxy.api.model.Building; +import org.btuk.proxy.database.dto.BuildingDTO; +import org.btuk.proxy.database.sql.GlobalSQL; + +import java.time.ZoneOffset; +import java.util.Date; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +public class BuildingsApiImpl implements BuildingsApi { + + private final GlobalSQL globalSQL; + + public BuildingsApiImpl(GlobalSQL globalSQL) { + this.globalSQL = globalSQL; + } + + @Override + public Response getBuildingsByArea(Double minLat, Double maxLat, Double minLon, Double maxLon) { + List buildingDTOs = globalSQL.getBuildingsByArea(minLat, maxLat, minLon, maxLon); + List buildings = buildingDTOs.stream() + .map(this::mapBuilding) + .collect(Collectors.toList()); + return Response.ok(buildings).build(); + } + + @Override + public Response getBuildingsByPlayer(UUID uuid) { + List buildingDTOs = globalSQL.getBuildingsByPlayer(uuid.toString()); + List buildings = buildingDTOs.stream() + .map(this::mapBuilding) + .collect(Collectors.toList()); + return Response.ok(buildings).build(); + } + + private Building mapBuilding(BuildingDTO dto) { + Building building = new Building(); + building.setBuildingId(dto.buildingId()); + building.setCoordinateId(dto.coordinateId()); + building.setPlayerId(dto.playerId() != null ? UUID.fromString(dto.playerId()) : null); + building.setIsPublic(dto.isPublic()); + building.setPlayerBuilt(dto.playerBuilt()); + if (dto.timeAdded() != null) { + building.setTimeAdded(Date.from(dto.timeAdded().toInstant(ZoneOffset.UTC))); + } + building.setLat(dto.lat()); + building.setLon(dto.lon()); + return building; + } +} diff --git a/api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java b/api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java new file mode 100644 index 0000000..80b74ac --- /dev/null +++ b/api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java @@ -0,0 +1,43 @@ +package org.btuk.proxy.api.impl; + +import jakarta.ws.rs.core.Response; +import org.btuk.proxy.api.PlayerApi; +import org.btuk.proxy.api.model.Player; +import org.btuk.proxy.database.dto.PlayerDTO; +import org.btuk.proxy.database.sql.GlobalSQL; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +public class PlayerApiImpl implements PlayerApi { + + private final GlobalSQL globalSQL; + + public PlayerApiImpl(GlobalSQL globalSQL) { + this.globalSQL = globalSQL; + } + + @Override + public Response getOnlinePlayers() { + List onlinePlayers = globalSQL.getOnlinePlayers(); + List players = onlinePlayers.stream() + .map(dto -> { + Player p = new Player(); + p.setUuid(UUID.fromString(dto.uuid())); + p.setName(dto.name()); + return p; + }) + .collect(Collectors.toList()); + return Response.ok(players).build(); + } + + @Override + public Response getPlayerUuid(String name) { + String uuid = globalSQL.getPlayerUuidByName(name); + if (uuid == null) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + return Response.ok(UUID.fromString(uuid)).build(); + } +} diff --git a/api/src/main/java/org/btuk/proxy/api/impl/StatusApiImpl.java b/api/src/main/java/org/btuk/proxy/api/impl/StatusApiImpl.java new file mode 100644 index 0000000..5433870 --- /dev/null +++ b/api/src/main/java/org/btuk/proxy/api/impl/StatusApiImpl.java @@ -0,0 +1,15 @@ +package org.btuk.proxy.api.impl; + +import jakarta.ws.rs.core.Response; +import org.btuk.proxy.api.StatusApi; +import org.btuk.proxy.api.model.Status; + +public class StatusApiImpl implements StatusApi { + + @Override + public Response getStatus() { + Status status = new Status(); + status.setStatus("UP"); + return Response.ok(status).build(); + } +} diff --git a/api/src/main/java/org/btuk/proxy/api/server/ProxyApi.java b/api/src/main/java/org/btuk/proxy/api/server/ProxyApi.java new file mode 100644 index 0000000..b7a94b6 --- /dev/null +++ b/api/src/main/java/org/btuk/proxy/api/server/ProxyApi.java @@ -0,0 +1,61 @@ +package org.btuk.proxy.api.server; + +import lombok.extern.java.Log; +import org.btuk.proxy.api.impl.BuildingsApiImpl; +import org.btuk.proxy.api.impl.PlayerApiImpl; +import org.btuk.proxy.api.impl.StatusApiImpl; +import org.btuk.proxy.database.sql.GlobalSQL; + +import org.glassfish.grizzly.http.server.HttpServer; +import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; +import org.glassfish.jersey.server.ResourceConfig; + +import java.net.URI; + +@Log +public class ProxyApi { + + private HttpServer server; + private final boolean enabled; + private final int port; + private final GlobalSQL globalSQL; + + public ProxyApi(boolean enabled, int port, GlobalSQL globalSQL) { + this.enabled = enabled; + this.port = port; + this.globalSQL = globalSQL; + } + + public void start() { + if (!enabled) { + return; + } + + int apiPort = port; + if (apiPort == 0) { + log.warning("API port is not set or 0, defaulting to 8080"); + apiPort = 8080; + } + + String baseUri = "http://0.0.0.0:" + apiPort + "/v1/"; + + ResourceConfig rc = new ResourceConfig() + .register(new StatusApiImpl()) + .register(new PlayerApiImpl(globalSQL)) + .register(new BuildingsApiImpl(globalSQL)); + + try { + server = GrizzlyHttpServerFactory.createHttpServer(URI.create(baseUri), rc); + log.info("API server started at " + baseUri); + } catch (Exception e) { + log.severe("Failed to start API server: " + e.getMessage()); + } + } + + public void stop() { + if (server != null && server.isStarted()) { + server.shutdownNow(); + log.info("API server stopped"); + } + } +} diff --git a/api/src/main/resources/openapi.yaml b/api/src/main/resources/openapi.yaml new file mode 100644 index 0000000..27fb547 --- /dev/null +++ b/api/src/main/resources/openapi.yaml @@ -0,0 +1,159 @@ +openapi: 3.0.3 +info: + title: Proxy API + description: Lightweight API for Proxy internal communication. + version: 1.0.0 +servers: + - url: http://localhost:8080/v1 +paths: + /status: + get: + tags: + - Status + summary: Get API status + operationId: getStatus + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /player/{name}: + get: + tags: + - Player + summary: Get player uuid by name + operationId: getPlayerUuid + parameters: + - name: name + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: string + format: uuid + '404': + description: Player not found + /players/online: + get: + tags: + - Player + summary: Get online players + operationId: getOnlinePlayers + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Player' + /buildings/player/{uuid}: + get: + tags: + - Buildings + summary: Get buildings by player uuid + operationId: getBuildingsByPlayer + parameters: + - name: uuid + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Building' + /buildings/area: + get: + tags: + - Buildings + summary: Get buildings for a specific area + operationId: getBuildingsByArea + parameters: + - name: minLat + in: query + required: true + schema: + type: number + format: double + - name: maxLat + in: query + required: true + schema: + type: number + format: double + - name: minLon + in: query + required: true + schema: + type: number + format: double + - name: maxLon + in: query + required: true + schema: + type: number + format: double + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Building' +components: + schemas: + Status: + type: object + properties: + status: + type: string + example: UP + Player: + type: object + properties: + uuid: + type: string + format: uuid + name: + type: string + Building: + type: object + properties: + buildingId: + type: integer + coordinateId: + type: integer + playerId: + type: string + format: uuid + isPublic: + type: boolean + playerBuilt: + type: boolean + timeAdded: + type: string + format: date-time + lat: + type: number + format: double + lon: + type: number + format: double diff --git a/core/pom.xml b/core/pom.xml index 6248470..c4d8e99 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -20,6 +20,10 @@ + + org.btuk.proxy + api + org.btuk.proxy database diff --git a/core/src/main/java/org/btuk/proxy/core/ProxyController.java b/core/src/main/java/org/btuk/proxy/core/ProxyController.java index 3440eb4..a5c68be 100644 --- a/core/src/main/java/org/btuk/proxy/core/ProxyController.java +++ b/core/src/main/java/org/btuk/proxy/core/ProxyController.java @@ -21,6 +21,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import org.btuk.proxy.api.server.ProxyApi; import org.btuk.proxy.core.chat.ChatHandler; import org.btuk.proxy.core.chat.ChatManager; import org.btuk.proxy.core.config.Config; @@ -74,6 +75,8 @@ public class ProxyController { private UserManager userManager; + private final ProxyApi proxyApi; + private static final String PROXY_CONFIG_NAME = "proxy-config.yml"; private static final String AUTOMOD_CONFIG_NAME = "automod.yml"; @@ -96,6 +99,7 @@ public ProxyController(File dataFolder) { setupDatabase(); this.coreUserManager = new CoreUserManager(); + this.proxyApi = new ProxyApi(config.getBoolean("api.enabled"), config.getInt("api.port"), globalSQL); this.enabled = true; } @@ -131,10 +135,15 @@ public void start(ChatHandler chatHandler, Scheduler scheduler, CoreServerManage socketInitializer.accept(new ProxySocketHandler(chatManager, discord, userManager, serverManager, tabManager)); + proxyApi.start(); + started = true; } public void stop() { + if (proxyApi != null) { + proxyApi.stop(); + } if (started) { // Show the disconnect message for all players in discord. if (discord != null) { diff --git a/core/src/main/java/org/btuk/proxy/core/utils/Moderation.java b/core/src/main/java/org/btuk/proxy/core/utils/Moderation.java index b7860c5..f2d4174 100644 --- a/core/src/main/java/org/btuk/proxy/core/utils/Moderation.java +++ b/core/src/main/java/org/btuk/proxy/core/utils/Moderation.java @@ -2,6 +2,8 @@ import org.btuk.proxy.database.sql.GlobalSQL; +import java.util.UUID; + public class Moderation { private final GlobalSQL globalSQL; diff --git a/core/src/main/resources/proxy-config.yml b/core/src/main/resources/proxy-config.yml index 1e031f8..ce3d27d 100644 --- a/core/src/main/resources/proxy-config.yml +++ b/core/src/main/resources/proxy-config.yml @@ -63,6 +63,11 @@ role_syncing: give: [ ] +#API Configuration +api: + enabled: true + port: 8080 + #Database login host: localhost port: 3306 diff --git a/database/src/main/java/org/btuk/proxy/database/DatabaseUpdates.java b/database/src/main/java/org/btuk/proxy/database/DatabaseUpdates.java index e1c9039..1f69302 100644 --- a/database/src/main/java/org/btuk/proxy/database/DatabaseUpdates.java +++ b/database/src/main/java/org/btuk/proxy/database/DatabaseUpdates.java @@ -18,7 +18,7 @@ public class DatabaseUpdates { // Version of the database that this build expects. - private static final Version EXPECTED_VERSION = Version.of(1, 10, 0); + private static final Version EXPECTED_VERSION = Version.of(1, 12, 0); private final GlobalSQL globalSQL; @@ -78,10 +78,19 @@ private List migrationSteps() { new MigrationStep(Version.of(1, 7, 3), this::update1_7_3), new MigrationStep(Version.of(1, 9, 4), this::update1_9_4), new MigrationStep(Version.of(1, 9, 5), this::update1_9_5), - new MigrationStep(Version.of(1, 11, 0), () -> {}) + new MigrationStep(Version.of(1, 12, 0), this::update1_12_0) ); } + private void update1_12_0() { + // Add indexes for performance optimization. + globalSQL.update("CREATE INDEX idx_player_data_1 ON player_data(name);"); + globalSQL.update("CREATE INDEX idx_buildings_1 ON buildings(player_id);"); + globalSQL.update("CREATE INDEX idx_buildings_2 ON buildings(lat, lon);"); + globalSQL.update("CREATE INDEX idx_messages_1 ON messages(recipient);"); + globalSQL.update("CREATE INDEX idx_moderation_1 ON moderation(uuid, end_time, type);"); + } + private void update1_9_5() { //add the new fields isPublic, playerBuilt and timeAdded to the buildings database globalSQL.update("ALTER TABLE buildings ADD COLUMN is_public BOOLEAN NOT NULL DEFAULT TRUE, ADD COLUMN player_built BOOLEAN NOT NULL DEFAULT TRUE, ADD COLUMN time_added DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;"); @@ -292,10 +301,14 @@ private MigrationStep(Version targetVersion, Runnable migration) { private Version migrate(Version currentVersion) { log.info("Migrating database from version " + currentVersion + " to version " + targetVersion); - migration.run(); - - globalSQL.update("UPDATE unique_data SET data_value='" + targetVersion + "' WHERE data_key='version';"); - return targetVersion; + try { + migration.run(); + globalSQL.update("UPDATE unique_data SET data_value='" + targetVersion + "' WHERE data_key='version';"); + return targetVersion; + } catch (Exception e) { + log.severe("Failed to migrate database to version " + targetVersion + ": " + e.getMessage()); + throw e; + } } } diff --git a/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java b/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java index 2c23c1a..760271c 100644 --- a/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java +++ b/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java @@ -2,16 +2,19 @@ import lombok.extern.java.Log; +import org.btuk.proxy.database.dto.AutoModFlagDTO; +import org.btuk.proxy.database.dto.BuildingDTO; +import org.btuk.proxy.database.dto.PlayerDTO; + import javax.sql.DataSource; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; -import org.btuk.proxy.database.dto.AutoModFlagDTO; - @Log public class GlobalSQL extends AbstractSQL { public GlobalSQL(DataSource datasource) { @@ -201,4 +204,84 @@ INSERT INTO moderation(uuid, start_time, end_time, reason, type) log.severe("Failed to insert moderation record for " + uuid + ": " + e.getMessage()); } } + + public String getPlayerUuidByName(String name) { + if (name == null) { + return null; + } + + try (Connection conn = conn(); PreparedStatement statement = conn.prepareStatement("SELECT uuid FROM player_data WHERE name=?;")) { + statement.setString(1, name); + try (ResultSet results = statement.executeQuery()) { + if (results.next()) { + return results.getString(1); + } + } + } catch (SQLException e) { + log.severe("Failed to get player uuid for " + name + ": " + e.getMessage()); + } + return null; + } + + public List getOnlinePlayers() { + List players = new ArrayList<>(); + try (Connection conn = conn(); PreparedStatement statement = conn.prepareStatement("SELECT uuid, name FROM player_data WHERE uuid IN (SELECT uuid FROM online_users);"); + ResultSet results = statement.executeQuery()) { + while (results.next()) { + players.add(new PlayerDTO(results.getString(1), results.getString(2))); + } + } catch (SQLException e) { + log.severe("Failed to get online players: " + e.getMessage()); + } + return players; + } + + public List getBuildingsByPlayer(String uuid) { + List buildings = new ArrayList<>(); + if (uuid == null) return buildings; + + try (Connection conn = conn(); PreparedStatement statement = conn.prepareStatement("SELECT building_id, coordinate_id, player_id, is_public, player_built, time_added, lat, lon FROM buildings WHERE player_id=?;")) { + statement.setString(1, uuid); + try (ResultSet results = statement.executeQuery()) { + while (results.next()) { + buildings.add(mapBuilding(results)); + } + } + } catch (SQLException e) { + log.severe("Failed to get buildings for " + uuid + ": " + e.getMessage()); + } + return buildings; + } + + public List getBuildingsByArea(double minLat, double maxLat, double minLon, double maxLon) { + List buildings = new ArrayList<>(); + try (Connection conn = conn(); PreparedStatement statement = conn.prepareStatement("SELECT building_id, coordinate_id, player_id, is_public, player_built, time_added, lat, lon FROM buildings WHERE lat BETWEEN ? AND ? AND lon BETWEEN ? AND ?;")) { + statement.setDouble(1, minLat); + statement.setDouble(2, maxLat); + statement.setDouble(3, minLon); + statement.setDouble(4, maxLon); + try (ResultSet results = statement.executeQuery()) { + while (results.next()) { + buildings.add(mapBuilding(results)); + } + } + } catch (SQLException e) { + log.severe("Failed to get buildings by area: " + e.getMessage()); + } + return buildings; + } + + private BuildingDTO mapBuilding(ResultSet results) throws SQLException { + Timestamp timestamp = results.getTimestamp(6); + return new BuildingDTO( + results.getInt(1), + results.getInt(2), + results.getString(3), + results.getBoolean(4), + results.getBoolean(5), + timestamp != null ? timestamp.toLocalDateTime() : null, + results.getDouble(7), + results.getDouble(8) + ); + } } diff --git a/database/src/main/resources/dbsetup_global.sql b/database/src/main/resources/dbsetup_global.sql index 3a30940..7102d15 100644 --- a/database/src/main/resources/dbsetup_global.sql +++ b/database/src/main/resources/dbsetup_global.sql @@ -42,7 +42,8 @@ CREATE TABLE IF NOT EXISTS player_data player_skin TEXT NULL DEFAULT NULL, tips_enabled TINYINT(1) NOT NULL DEFAULT 1, display_name TEXT NULL DEFAULT NULL, - PRIMARY KEY (uuid) + PRIMARY KEY (uuid), + INDEX idx_player_data_1 (name) ); CREATE TABLE IF NOT EXISTS messages @@ -51,7 +52,8 @@ CREATE TABLE IF NOT EXISTS messages recipient CHAR(36) NOT NULL, message TEXT NOT NULL, PRIMARY KEY(id), - CONSTRAINT fk_messages_1 FOREIGN KEY(recipient) REFERENCES player_data(uuid) + CONSTRAINT fk_messages_1 FOREIGN KEY(recipient) REFERENCES player_data(uuid), + INDEX idx_messages_1 (recipient) ); CREATE TABLE IF NOT EXISTS join_events @@ -155,7 +157,8 @@ CREATE TABLE IF NOT EXISTS moderation type ENUM('ban', 'mute') NOT NULL, PRIMARY KEY(uuid,start_time), - CONSTRAINT fk_moderation_1 FOREIGN KEY(uuid) REFERENCES player_data(uuid) + CONSTRAINT fk_moderation_1 FOREIGN KEY(uuid) REFERENCES player_data(uuid), + INDEX idx_moderation_1 (uuid, end_time, type) ); CREATE TABLE IF NOT EXISTS coins @@ -221,7 +224,9 @@ CREATE TABLE IF NOT EXISTS buildings lon DOUBLE DEFAULT 0, PRIMARY KEY(building_id), CONSTRAINT fk_buildings_1 FOREIGN KEY(coordinate_id) REFERENCES coordinates(id), - CONSTRAINT fk_buildings_2 FOREIGN KEY(player_id) REFERENCES player_data(uuid) + CONSTRAINT fk_buildings_2 FOREIGN KEY(player_id) REFERENCES player_data(uuid), + INDEX idx_buildings_1 (player_id), + INDEX idx_buildings_2 (lat, lon) ); CREATE TABLE IF NOT EXISTS survey diff --git a/pom.xml b/pom.xml index 2168ea5..75d7f3c 100644 --- a/pom.xml +++ b/pom.xml @@ -12,8 +12,9 @@ Proxy database - plugin + api core + plugin @@ -73,6 +74,11 @@ database ${project.version} + + org.btuk.proxy + api + ${project.version} + org.btuk.proxy core From 1039f018e0b8bc01dccaa91f2bdffbb7f43016d9 Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Fri, 8 May 2026 19:45:23 +0200 Subject: [PATCH 03/38] Allow player skin to be null when creating a user, other issues will throw an exception so no invalid message is sent to the server. --- core/src/main/java/org/btuk/proxy/core/user/User.java | 7 ++++++- .../main/java/org/btuk/proxy/database/sql/GlobalSQL.java | 8 ++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/btuk/proxy/core/user/User.java b/core/src/main/java/org/btuk/proxy/core/user/User.java index e4dec96..00809f2 100644 --- a/core/src/main/java/org/btuk/proxy/core/user/User.java +++ b/core/src/main/java/org/btuk/proxy/core/user/User.java @@ -295,7 +295,12 @@ public boolean isMuted() { public UserConnectReply createUserConnectReply() { // Create database object if not exists. - if (newUser && globalSQL.createUser(uuid, name, playerSkin)) { + if (newUser) { + if (!globalSQL.createUser(uuid, name, playerSkin)) { + // We don't want to send a reply to the server since this could cause issues. + // The user won't be able to do anything, so this is not a perfect solution. + throw new RuntimeException("Failed to create user " + uuid + " in database."); + } newUser = false; } diff --git a/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java b/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java index 2c23c1a..f387010 100644 --- a/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java +++ b/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java @@ -19,11 +19,15 @@ public GlobalSQL(DataSource datasource) { } public boolean createUser(String uuid, String name, String playerSkin) { - if (uuid == null || name == null || playerSkin == null) { - log.warning("createUser called with null argument(s)"); + if (uuid == null || name == null) { + log.severe("createUser called with null argument(s)"); return false; } + if (playerSkin == null) { + log.warning("createUser called without a player skin."); + } + final String sql = """ INSERT INTO player_data(uuid, name, last_online, last_submit, player_skin) VALUES(?, ?, ?, ?, ?) From 635c93e58ae14e8e6d62f2be5e3b27607f687a2e Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Fri, 8 May 2026 19:51:05 +0200 Subject: [PATCH 04/38] Update the player skin in the database when a recurring user connects. --- core/src/main/java/org/btuk/proxy/core/user/User.java | 6 ++++++ .../src/main/java/org/btuk/proxy/core/user/UserManager.java | 3 +++ 2 files changed, 9 insertions(+) diff --git a/core/src/main/java/org/btuk/proxy/core/user/User.java b/core/src/main/java/org/btuk/proxy/core/user/User.java index 00809f2..70b75af 100644 --- a/core/src/main/java/org/btuk/proxy/core/user/User.java +++ b/core/src/main/java/org/btuk/proxy/core/user/User.java @@ -555,6 +555,12 @@ public void saveAutoModFlags() { globalSQL.saveAutoModFlags(uuid, flags); } + public void updatePlayerSkin() { + if (playerSkin != null) { + globalSQL.update("UPDATE player_data SET skin='" + playerSkin + "' WHERE uuid='" + uuid + "';"); + } + } + private static JsonNode getJsonNodeFromUrl(URL url) throws IOException { StringBuilder inline = new StringBuilder(); Scanner scanner = new Scanner(url.openStream()); diff --git a/core/src/main/java/org/btuk/proxy/core/user/UserManager.java b/core/src/main/java/org/btuk/proxy/core/user/UserManager.java index dc680fb..4f8c9ae 100644 --- a/core/src/main/java/org/btuk/proxy/core/user/UserManager.java +++ b/core/src/main/java/org/btuk/proxy/core/user/UserManager.java @@ -298,6 +298,9 @@ public User addUser(UserConnectRequest request) { } else { // Send the connect message. joinMessage = JOIN_MESSAGE; + + // Update the player skin in the database. + user.updatePlayerSkin(); } } From 1d8d94f778c139317aa78fb18c34257943067c2f Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Tue, 2 Jun 2026 19:13:35 +0200 Subject: [PATCH 05/38] Fix version in plugin. --- plugin/pom.xml | 2 +- plugin/src/main/java/org/btuk/proxy/Proxy.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/pom.xml b/plugin/pom.xml index 58e5c54..dfd9751 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -7,7 +7,7 @@ org.btuk Proxy - 1.11.0 + 1.12.0-SNAPSHOT org.btuk.proxy diff --git a/plugin/src/main/java/org/btuk/proxy/Proxy.java b/plugin/src/main/java/org/btuk/proxy/Proxy.java index 3ad339f..2cb45ef 100644 --- a/plugin/src/main/java/org/btuk/proxy/Proxy.java +++ b/plugin/src/main/java/org/btuk/proxy/Proxy.java @@ -40,7 +40,7 @@ import org.btuk.proxy.core.ProxyController; -@Plugin(id = "proxy", name = "Proxy", version = "1.11.0", +@Plugin(id = "proxy", name = "Proxy", version = "1.12.0-SNAPSHOT", url = "https://github.com/BTEUK/Proxy", description = "Proxy plugin, managed chat, discord and server related actions.", authors = {"ELgamer"}) public class Proxy { From 04223f118970fdf86b1c8a85b384a34df77bc044 Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Tue, 2 Jun 2026 19:16:13 +0200 Subject: [PATCH 06/38] Fix SQL update for player skin. --- core/src/main/java/org/btuk/proxy/core/user/User.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/btuk/proxy/core/user/User.java b/core/src/main/java/org/btuk/proxy/core/user/User.java index 70b75af..7b39b29 100644 --- a/core/src/main/java/org/btuk/proxy/core/user/User.java +++ b/core/src/main/java/org/btuk/proxy/core/user/User.java @@ -557,7 +557,7 @@ public void saveAutoModFlags() { public void updatePlayerSkin() { if (playerSkin != null) { - globalSQL.update("UPDATE player_data SET skin='" + playerSkin + "' WHERE uuid='" + uuid + "';"); + globalSQL.update("UPDATE player_data SET player_skin='" + playerSkin + "' WHERE uuid='" + uuid + "';"); } } From 0548541cb8f7962892f31fd1f344bafc232415df Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sat, 6 Jun 2026 21:53:33 +0200 Subject: [PATCH 07/38] Add generated sources so it is automatically recognised. --- api/pom.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/api/pom.xml b/api/pom.xml index 9b42680..844548e 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -16,6 +16,7 @@ UTF-8 7.4.0 + 3.6.0 3.1.0 3.1.5 4.0.2 @@ -94,6 +95,25 @@ + + org.codehaus.mojo + build-helper-maven-plugin + ${build-helper-maven-plugin.version} + + + add-source + generate-sources + + add-source + + + + ${project.build.directory}/generated-sources/openapi/src/main/java + + + + + From 89f88a947d128648603d9412d3fea0448478e2a1 Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sat, 6 Jun 2026 22:27:08 +0200 Subject: [PATCH 08/38] Add DTO's --- .../main/java/org/btuk/proxy/database/dto/BuildingDTO.java | 7 +++++++ .../main/java/org/btuk/proxy/database/dto/PlayerDTO.java | 6 ++++++ 2 files changed, 13 insertions(+) create mode 100644 database/src/main/java/org/btuk/proxy/database/dto/BuildingDTO.java create mode 100644 database/src/main/java/org/btuk/proxy/database/dto/PlayerDTO.java diff --git a/database/src/main/java/org/btuk/proxy/database/dto/BuildingDTO.java b/database/src/main/java/org/btuk/proxy/database/dto/BuildingDTO.java new file mode 100644 index 0000000..86acb2a --- /dev/null +++ b/database/src/main/java/org/btuk/proxy/database/dto/BuildingDTO.java @@ -0,0 +1,7 @@ +package org.btuk.proxy.database.dto; + +import java.time.LocalDateTime; +import java.util.UUID; + +public record BuildingDTO(int buildingId, int coordinateId, String playerId, boolean isPublic, boolean playerBuilt, LocalDateTime timeAdded, double lat, double lon) { +} diff --git a/database/src/main/java/org/btuk/proxy/database/dto/PlayerDTO.java b/database/src/main/java/org/btuk/proxy/database/dto/PlayerDTO.java new file mode 100644 index 0000000..b63661f --- /dev/null +++ b/database/src/main/java/org/btuk/proxy/database/dto/PlayerDTO.java @@ -0,0 +1,6 @@ +package org.btuk.proxy.database.dto; + +import java.util.UUID; + +public record PlayerDTO(String uuid, String name) { +} From e4b4547135335590b6768534679f7c146fdecff9 Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sun, 7 Jun 2026 15:13:35 +0200 Subject: [PATCH 09/38] Fix compilation issues for the api. --- api/pom.xml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/api/pom.xml b/api/pom.xml index 844548e..c38968b 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -108,12 +108,26 @@ - ${project.build.directory}/generated-sources/openapi/src/main/java + ${project.build.directory}/generated-sources/openapi/src/gen/java + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + + org.projectlombok + lombok + ${lombok.version} + + + + From 32cc719a359d567030a4c50e982adf68fd782f97 Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sun, 7 Jun 2026 15:57:35 +0200 Subject: [PATCH 10/38] Correct snapshot version. --- plugin/pom.xml | 2 +- plugin/src/main/java/org/btuk/proxy/Proxy.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/pom.xml b/plugin/pom.xml index 58e5c54..dfd9751 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -7,7 +7,7 @@ org.btuk Proxy - 1.11.0 + 1.12.0-SNAPSHOT org.btuk.proxy diff --git a/plugin/src/main/java/org/btuk/proxy/Proxy.java b/plugin/src/main/java/org/btuk/proxy/Proxy.java index 3ad339f..2cb45ef 100644 --- a/plugin/src/main/java/org/btuk/proxy/Proxy.java +++ b/plugin/src/main/java/org/btuk/proxy/Proxy.java @@ -40,7 +40,7 @@ import org.btuk.proxy.core.ProxyController; -@Plugin(id = "proxy", name = "Proxy", version = "1.11.0", +@Plugin(id = "proxy", name = "Proxy", version = "1.12.0-SNAPSHOT", url = "https://github.com/BTEUK/Proxy", description = "Proxy plugin, managed chat, discord and server related actions.", authors = {"ELgamer"}) public class Proxy { From 3c167a0953329e7d72facd538d8025230808e814 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 12 Jul 2026 21:30:32 +0100 Subject: [PATCH 11/38] added 2 endpoints on the API for sending a player a message and getting the username of a player from their uuid. Moved Proxy controller into plugin package to prevent circular imports. --- api/pom.xml | 5 ++ .../btuk/proxy/api/impl/PlayerApiImpl.java | 39 +++++++++++++- .../org/btuk/proxy/api/server/ProxyApi.java | 10 ++-- api/src/main/resources/openapi.yaml | 52 ++++++++++++++++++- core/pom.xml | 4 -- core/src/main/resources/proxy-config.yml | 2 +- .../btuk/proxy/database/sql/GlobalSQL.java | 18 +++++++ plugin/pom.xml | 8 +++ .../src/main/java/org/btuk/proxy/Proxy.java | 2 - .../java/org/btuk/proxy}/ProxyController.java | 7 ++- 10 files changed, 129 insertions(+), 18 deletions(-) rename {core/src/main/java/org/btuk/proxy/core => plugin/src/main/java/org/btuk/proxy}/ProxyController.java (98%) diff --git a/api/pom.xml b/api/pom.xml index c38968b..b7691dd 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -28,6 +28,11 @@ database + + org.btuk.proxy + core + + org.projectlombok lombok diff --git a/api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java b/api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java index 80b74ac..a67946d 100644 --- a/api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java +++ b/api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java @@ -1,8 +1,12 @@ package org.btuk.proxy.api.impl; import jakarta.ws.rs.core.Response; +import net.bteuk.network.lib.dto.DirectMessage; +import net.bteuk.network.lib.utils.ChatUtils; import org.btuk.proxy.api.PlayerApi; +import org.btuk.proxy.api.model.Message; import org.btuk.proxy.api.model.Player; +import org.btuk.proxy.core.chat.ChatManager; import org.btuk.proxy.database.dto.PlayerDTO; import org.btuk.proxy.database.sql.GlobalSQL; @@ -10,12 +14,16 @@ import java.util.UUID; import java.util.stream.Collectors; +import static net.bteuk.network.lib.enums.ChatChannels.GLOBAL; +import static org.btuk.proxy.core.utils.Constants.SERVER_SENDER; + public class PlayerApiImpl implements PlayerApi { private final GlobalSQL globalSQL; + private final ChatManager chatManager; - public PlayerApiImpl(GlobalSQL globalSQL) { - this.globalSQL = globalSQL; + public PlayerApiImpl(GlobalSQL globalSQL, ChatManager chatManager) { + this.globalSQL = globalSQL;this.chatManager = chatManager; } @Override @@ -40,4 +48,31 @@ public Response getPlayerUuid(String name) { } return Response.ok(UUID.fromString(uuid)).build(); } + + @Override + public Response getPlayerUsername(String uuid) { + String username = globalSQL.getPlayerUsernameByUuid(uuid); + if (uuid == null) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + return Response.ok(username).build(); + } + + @Override + public Response sendPlayerMessage(String playerID, Message message){ + + try { + String messagePlainText = message.getMessage(); + DirectMessage m = new DirectMessage(GLOBAL.getChannelName(), playerID.toString(), SERVER_SENDER, ChatUtils.success(messagePlainText), true); + chatManager.sendDirectMessage(m); + return Response.status(Response.Status.CREATED).build(); + }catch (Exception e) + { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Failed to deliver message to player.") + .build(); + } + + + } } diff --git a/api/src/main/java/org/btuk/proxy/api/server/ProxyApi.java b/api/src/main/java/org/btuk/proxy/api/server/ProxyApi.java index b7a94b6..6feadaa 100644 --- a/api/src/main/java/org/btuk/proxy/api/server/ProxyApi.java +++ b/api/src/main/java/org/btuk/proxy/api/server/ProxyApi.java @@ -4,6 +4,7 @@ import org.btuk.proxy.api.impl.BuildingsApiImpl; import org.btuk.proxy.api.impl.PlayerApiImpl; import org.btuk.proxy.api.impl.StatusApiImpl; +import org.btuk.proxy.core.chat.ChatManager; import org.btuk.proxy.database.sql.GlobalSQL; import org.glassfish.grizzly.http.server.HttpServer; @@ -19,11 +20,14 @@ public class ProxyApi { private final boolean enabled; private final int port; private final GlobalSQL globalSQL; + private final ChatManager chatManager; - public ProxyApi(boolean enabled, int port, GlobalSQL globalSQL) { + + public ProxyApi(boolean enabled, int port, GlobalSQL globalSQL, ChatManager chatManager) { this.enabled = enabled; this.port = port; this.globalSQL = globalSQL; + this.chatManager = chatManager; } public void start() { @@ -37,11 +41,11 @@ public void start() { apiPort = 8080; } - String baseUri = "http://0.0.0.0:" + apiPort + "/v1/"; + String baseUri = "http://0.0.0.0:" + apiPort + "/api/"; ResourceConfig rc = new ResourceConfig() .register(new StatusApiImpl()) - .register(new PlayerApiImpl(globalSQL)) + .register(new PlayerApiImpl(globalSQL,chatManager)) .register(new BuildingsApiImpl(globalSQL)); try { diff --git a/api/src/main/resources/openapi.yaml b/api/src/main/resources/openapi.yaml index 27fb547..03a24c8 100644 --- a/api/src/main/resources/openapi.yaml +++ b/api/src/main/resources/openapi.yaml @@ -4,7 +4,7 @@ info: description: Lightweight API for Proxy internal communication. version: 1.0.0 servers: - - url: http://localhost:8080/v1 + - url: http://51.195.190.215:61101/api paths: /status: get: @@ -19,7 +19,7 @@ paths: application/json: schema: $ref: '#/components/schemas/Status' - /player/{name}: + /player/uuid/{name}: get: tags: - Player @@ -41,6 +41,49 @@ paths: format: uuid '404': description: Player not found + /player/username/{uuid}: + get: + tags: + - Player + summary: Get player username by uuid + operationId: getPlayerUsername + parameters: + - name: uuid + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: string + '404': + description: Player not found + /player/message/{uuid}: + post: + tags: + - Player + summary: Send a message to the player on the server + operationId: sendPlayerMessage + parameters: + - name: uuid + in: path + required: true + schema: + type: string + requestBody: + description: The message to send and the player to send to + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Message' + responses: + "201": + description: Message Added /players/online: get: tags: @@ -134,6 +177,11 @@ components: format: uuid name: type: string + Message: + type: object + properties: + message: + type: string Building: type: object properties: diff --git a/core/pom.xml b/core/pom.xml index c4d8e99..6248470 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -20,10 +20,6 @@ - - org.btuk.proxy - api - org.btuk.proxy database diff --git a/core/src/main/resources/proxy-config.yml b/core/src/main/resources/proxy-config.yml index ce3d27d..ad24a34 100644 --- a/core/src/main/resources/proxy-config.yml +++ b/core/src/main/resources/proxy-config.yml @@ -66,7 +66,7 @@ role_syncing: #API Configuration api: enabled: true - port: 8080 + port: 61101 #Database login host: localhost diff --git a/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java b/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java index 760271c..71c794c 100644 --- a/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java +++ b/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java @@ -223,6 +223,24 @@ public String getPlayerUuidByName(String name) { return null; } + public String getPlayerUsernameByUuid(String uuid) { + if (uuid == null) { + return null; + } + + try (Connection conn = conn(); PreparedStatement statement = conn.prepareStatement("SELECT name FROM player_data WHERE uuid=?;")) { + statement.setString(1, uuid); + try (ResultSet results = statement.executeQuery()) { + if (results.next()) { + return results.getString(1); + } + } + } catch (SQLException e) { + log.severe("Failed to get player uuid for " + uuid + ": " + e.getMessage()); + } + return null; + } + public List getOnlinePlayers() { List players = new ArrayList<>(); try (Connection conn = conn(); PreparedStatement statement = conn.prepareStatement("SELECT uuid, name FROM player_data WHERE uuid IN (SELECT uuid FROM online_users);"); diff --git a/plugin/pom.xml b/plugin/pom.xml index dfd9751..4c018b8 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -109,6 +109,14 @@ org.btuk.proxy core + + org.btuk.proxy + api + + + org.btuk.proxy + database + com.velocitypowered diff --git a/plugin/src/main/java/org/btuk/proxy/Proxy.java b/plugin/src/main/java/org/btuk/proxy/Proxy.java index 2cb45ef..2250eac 100644 --- a/plugin/src/main/java/org/btuk/proxy/Proxy.java +++ b/plugin/src/main/java/org/btuk/proxy/Proxy.java @@ -38,8 +38,6 @@ import java.util.UUID; import java.util.function.Consumer; -import org.btuk.proxy.core.ProxyController; - @Plugin(id = "proxy", name = "Proxy", version = "1.12.0-SNAPSHOT", url = "https://github.com/BTEUK/Proxy", description = "Proxy plugin, managed chat, discord and server related actions.", authors = {"ELgamer"}) public class Proxy { diff --git a/core/src/main/java/org/btuk/proxy/core/ProxyController.java b/plugin/src/main/java/org/btuk/proxy/ProxyController.java similarity index 98% rename from core/src/main/java/org/btuk/proxy/core/ProxyController.java rename to plugin/src/main/java/org/btuk/proxy/ProxyController.java index a5c68be..698fe0e 100644 --- a/core/src/main/java/org/btuk/proxy/core/ProxyController.java +++ b/plugin/src/main/java/org/btuk/proxy/ProxyController.java @@ -1,4 +1,4 @@ -package org.btuk.proxy.core; +package org.btuk.proxy; import lombok.Getter; import lombok.extern.java.Log; @@ -75,7 +75,7 @@ public class ProxyController { private UserManager userManager; - private final ProxyApi proxyApi; + private ProxyApi proxyApi; private static final String PROXY_CONFIG_NAME = "proxy-config.yml"; @@ -99,7 +99,6 @@ public ProxyController(File dataFolder) { setupDatabase(); this.coreUserManager = new CoreUserManager(); - this.proxyApi = new ProxyApi(config.getBoolean("api.enabled"), config.getInt("api.port"), globalSQL); this.enabled = true; } @@ -130,7 +129,7 @@ public void start(ChatHandler chatHandler, Scheduler scheduler, CoreServerManage new ReviewStatus(config, globalSQL, plotSQL, regionSQL, discord, scheduler); this.discord.addJDAEventListeners(chatManager, coreUserManager, tabManager, plotSQL); - + this.proxyApi = new ProxyApi(config.getBoolean("api.enabled"), config.getInt("api.port"), globalSQL,chatManager); serverManager.initOnlineServers(); socketInitializer.accept(new ProxySocketHandler(chatManager, discord, userManager, serverManager, tabManager)); From ad616e570ea476370ea984a6e41d72451f353145 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 23 Jul 2026 00:23:27 +0100 Subject: [PATCH 12/38] created new endpoints for buildings. (AI gen mostly but have been tested well) apart from the GlobalSQL.getBuildingCount shall test that tomorrow. --- .../btuk/proxy/api/impl/BuildingsApiImpl.java | 60 ++++++- api/src/main/resources/openapi.yaml | 169 ++++++++++++++++-- .../btuk/proxy/database/dto/GridCellDTO.java | 3 + .../btuk/proxy/database/sql/GlobalSQL.java | 145 +++++++++++++-- 4 files changed, 344 insertions(+), 33 deletions(-) create mode 100644 database/src/main/java/org/btuk/proxy/database/dto/GridCellDTO.java diff --git a/api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java b/api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java index 8a2336d..31c7565 100644 --- a/api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java +++ b/api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java @@ -3,10 +3,15 @@ import jakarta.ws.rs.core.Response; import org.btuk.proxy.api.BuildingsApi; import org.btuk.proxy.api.model.Building; +import org.btuk.proxy.api.model.BuildingCount; +import org.btuk.proxy.api.model.BuildingGridResponse; +import org.btuk.proxy.api.model.GridCell; import org.btuk.proxy.database.dto.BuildingDTO; +import org.btuk.proxy.database.dto.GridCellDTO; import org.btuk.proxy.database.sql.GlobalSQL; import java.time.ZoneOffset; +import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.UUID; @@ -20,9 +25,14 @@ public BuildingsApiImpl(GlobalSQL globalSQL) { this.globalSQL = globalSQL; } + + + @Override - public Response getBuildingsByArea(Double minLat, Double maxLat, Double minLon, Double maxLon) { - List buildingDTOs = globalSQL.getBuildingsByArea(minLat, maxLat, minLon, maxLon); + public Response getBuildingsByArea(Double minLat, Double maxLat, Double minLon, Double maxLon, UUID playerUuid) { + String playerUuidStr = playerUuid != null ? playerUuid.toString() : null; + List buildingDTOs = globalSQL.getBuildingsByArea(minLat, maxLat, minLon, maxLon, playerUuidStr); + List buildings = buildingDTOs.stream() .map(this::mapBuilding) .collect(Collectors.toList()); @@ -30,12 +40,46 @@ public Response getBuildingsByArea(Double minLat, Double maxLat, Double minLon, } @Override - public Response getBuildingsByPlayer(UUID uuid) { - List buildingDTOs = globalSQL.getBuildingsByPlayer(uuid.toString()); - List buildings = buildingDTOs.stream() - .map(this::mapBuilding) - .collect(Collectors.toList()); - return Response.ok(buildings).build(); + public Response getBuildingCount(List playerUuid, Double minLat, Double maxLat, Double minLon, Double maxLon, Boolean isPublic, Boolean playerBuilt) { + List playerUuidStrs = null; + if (playerUuid != null && !playerUuid.isEmpty()) { + playerUuidStrs = playerUuid.stream() + .map(UUID::toString) + .collect(Collectors.toList()); + } + + int count = globalSQL.getBuildingCount(playerUuidStrs, minLat, maxLat, minLon, maxLon, isPublic, playerBuilt); + + BuildingCount buildingCount = new BuildingCount(); + buildingCount.setCount(count); + + return Response.ok(buildingCount).build(); + } + + @Override + public Response getBuildingGridCount(Double minLat, Double maxLat, Double minLon, Double maxLon, Double stepLat, Double stepLon, UUID playerUuid) { + if (stepLat == null || stepLat <= 0 || stepLon == null || stepLon <= 0) { + return Response.status(Response.Status.BAD_REQUEST).entity("stepLat and stepLon must be greater than 0").build(); + } + + String playerUuidStr = (playerUuid != null) ? playerUuid.toString() : null; + + List dbGridCounts = globalSQL.getBuildingGridCounts(minLat, maxLat, minLon, maxLon, stepLat, stepLon, playerUuidStr); + + List cells = new ArrayList<>(); + + for (GridCellDTO dbCell : dbGridCounts) { + GridCell cell = new GridCell(); + cell.setLat(dbCell.lat()); + cell.setLon(dbCell.lon()); + cell.setCount(dbCell.count()); + cells.add(cell); + } + + BuildingGridResponse response = new BuildingGridResponse(); + response.setCells(cells); + + return Response.ok(response).build(); } private Building mapBuilding(BuildingDTO dto) { diff --git a/api/src/main/resources/openapi.yaml b/api/src/main/resources/openapi.yaml index 03a24c8..63def55 100644 --- a/api/src/main/resources/openapi.yaml +++ b/api/src/main/resources/openapi.yaml @@ -99,16 +99,42 @@ paths: type: array items: $ref: '#/components/schemas/Player' - /buildings/player/{uuid}: + /buildings/area: get: tags: - Buildings - summary: Get buildings by player uuid - operationId: getBuildingsByPlayer + summary: Get buildings within an area with privacy filtering + description: Returns full building objects within a bounding box. Excludes private buildings unless they belong to the specified `playerUuid`. + operationId: getBuildingsByArea parameters: - - name: uuid - in: path + - name: minLat + in: query required: true + schema: + type: number + format: double + - name: maxLat + in: query + required: true + schema: + type: number + format: double + - name: minLon + in: query + required: true + schema: + type: number + format: double + - name: maxLon + in: query + required: true + schema: + type: number + format: double + - name: playerUuid + in: query + required: false + description: Context player UUID. If provided, includes private buildings owned by this player. schema: type: string format: uuid @@ -121,12 +147,77 @@ paths: type: array items: $ref: '#/components/schemas/Building' - /buildings/area: + + /buildings/count: get: tags: - Buildings - summary: Get buildings for a specific area - operationId: getBuildingsByArea + summary: Get building counts with filters + description: Returns the total count of buildings matching optional area, player, visibility, and source filters. + operationId: getBuildingCount + parameters: + - name: playerUuid + in: query + required: false + description: Filter by one or multiple player UUIDs. Repeat parameter for multiple (`?playerUuid=...&playerUuid=...`). + style: form + explode: true + schema: + type: array + items: + type: string + format: uuid + - name: minLat + in: query + required: false + schema: + type: number + format: double + - name: maxLat + in: query + required: false + schema: + type: number + format: double + - name: minLon + in: query + required: false + schema: + type: number + format: double + - name: maxLon + in: query + required: false + schema: + type: number + format: double + - name: isPublic + in: query + required: false + description: Filter specifically for public or private buildings. + schema: + type: boolean + - name: playerBuilt + in: query + required: false + description: Filter by player-built status. + schema: + type: boolean + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/BuildingCount' + + /buildings/grid-count: + get: + tags: + - Buildings + summary: Get aggregated building counts mapped over a grid + description: Divides a bounding box into a grid of cells and returns building counts per cell. Automatically filters out private buildings unless owned by the requesting player. + operationId: getBuildingGridCount parameters: - name: minLat in: query @@ -152,15 +243,35 @@ paths: schema: type: number format: double + - name: stepLat + in: query + required: true + description: Number of grid divisions along latitude. + schema: + type: number + format: double + - name: stepLon + in: query + required: true + description: Number of grid divisions along longitude. + schema: + type: number + format: double + - name: playerUuid + in: query + required: false + description: Context player UUID to include their own private buildings in the cell counts. + schema: + type: string + format: uuid responses: '200': description: OK content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/Building' + $ref: '#/components/schemas/BuildingGridResponse' + components: schemas: Status: @@ -205,3 +316,39 @@ components: lon: type: number format: double + BuildingCount: + type: object + properties: + count: + type: integer + example: 42 + + GridCell: + type: object + properties: + lat: + type: number + format: double + lon: + type: number + format: double + count: + type: integer + example: 12 + + BuildingGridResponse: + type: object + properties: + rows: + type: integer + example: 10 + cols: + type: integer + example: 10 + totalCount: + type: integer + example: 142 + cells: + type: array + items: + $ref: '#/components/schemas/GridCell' \ No newline at end of file diff --git a/database/src/main/java/org/btuk/proxy/database/dto/GridCellDTO.java b/database/src/main/java/org/btuk/proxy/database/dto/GridCellDTO.java new file mode 100644 index 0000000..36499e7 --- /dev/null +++ b/database/src/main/java/org/btuk/proxy/database/dto/GridCellDTO.java @@ -0,0 +1,3 @@ +package org.btuk.proxy.database.dto; + +public record GridCellDTO(double lat, double lon, int count) {} diff --git a/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java b/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java index 71c794c..f5f6f85 100644 --- a/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java +++ b/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java @@ -4,6 +4,7 @@ import org.btuk.proxy.database.dto.AutoModFlagDTO; import org.btuk.proxy.database.dto.BuildingDTO; +import org.btuk.proxy.database.dto.GridCellDTO; import org.btuk.proxy.database.dto.PlayerDTO; import javax.sql.DataSource; @@ -271,13 +272,92 @@ public List getBuildingsByPlayer(String uuid) { return buildings; } - public List getBuildingsByArea(double minLat, double maxLat, double minLon, double maxLon) { + private BuildingDTO mapBuilding(ResultSet results) throws SQLException { + Timestamp timestamp = results.getTimestamp(6); + return new BuildingDTO( + results.getInt(1), + results.getInt(2), + results.getString(3), + results.getBoolean(4), + results.getBoolean(5), + timestamp != null ? timestamp.toLocalDateTime() : null, + results.getDouble(7), + results.getDouble(8) + ); + } + + // Dynamic flexible building count with optional spatial, player, and status filters + public int getBuildingCount(List playerUuids, Double minLat, Double maxLat, Double minLon, Double maxLon, Boolean isPublic, Boolean playerBuilt) { + StringBuilder sql = new StringBuilder("SELECT COUNT(*) FROM buildings WHERE 1=1"); + List params = new ArrayList<>(); + + if (minLat != null && maxLat != null) { + sql.append(" AND lat BETWEEN ? AND ?"); + params.add(minLat); + params.add(maxLat); + } + if (minLon != null && maxLon != null) { + sql.append(" AND lon BETWEEN ? AND ?"); + params.add(minLon); + params.add(maxLon); + } + if (isPublic != null) { + sql.append(" AND is_public = ?"); + params.add(isPublic); + } + if (playerBuilt != null) { + sql.append(" AND player_built = ?"); + params.add(playerBuilt); + } + if (playerUuids != null && !playerUuids.isEmpty()) { + sql.append(" AND player_id IN ("); + for (int i = 0; i < playerUuids.size(); i++) { + sql.append(i == 0 ? "?" : ", ?"); + params.add(playerUuids.get(i)); + } + sql.append(")"); + } + + try (Connection conn = conn(); PreparedStatement statement = conn.prepareStatement(sql.toString())) { + for (int i = 0; i < params.size(); i++) { + Object param = params.get(i); + if (param instanceof Double d) { + statement.setDouble(i + 1, d); + } else if (param instanceof Boolean b) { + statement.setBoolean(i + 1, b); + } else if (param instanceof String s) { + statement.setString(i + 1, s); + } + } + + try (ResultSet results = statement.executeQuery()) { + if (results.next()) { + return results.getInt(1); + } + } + } catch (SQLException e) { + log.severe("Failed to get building count: " + e.getMessage()); + } + return 0; + } + + // Area fetch with privacy filtering (excludes private buildings unless owned by playerUuid) + public List getBuildingsByArea(double minLat, double maxLat, double minLon, double maxLon, String playerUuid) { List buildings = new ArrayList<>(); - try (Connection conn = conn(); PreparedStatement statement = conn.prepareStatement("SELECT building_id, coordinate_id, player_id, is_public, player_built, time_added, lat, lon FROM buildings WHERE lat BETWEEN ? AND ? AND lon BETWEEN ? AND ?;")) { + + boolean hasPlayer = (playerUuid != null && !playerUuid.isEmpty()); + String sql = "SELECT building_id, coordinate_id, player_id, is_public, player_built, time_added, lat, lon " + + "FROM buildings WHERE lat BETWEEN ? AND ? AND lon BETWEEN ? AND ? " + + (hasPlayer ? "AND (is_public = TRUE OR player_id = ?);" : "AND is_public = TRUE;"); + + try (Connection conn = conn(); PreparedStatement statement = conn.prepareStatement(sql)) { statement.setDouble(1, minLat); statement.setDouble(2, maxLat); statement.setDouble(3, minLon); statement.setDouble(4, maxLon); + if (hasPlayer) { + statement.setString(5, playerUuid); + } try (ResultSet results = statement.executeQuery()) { while (results.next()) { buildings.add(mapBuilding(results)); @@ -289,17 +369,54 @@ public List getBuildingsByArea(double minLat, double maxLat, double return buildings; } - private BuildingDTO mapBuilding(ResultSet results) throws SQLException { - Timestamp timestamp = results.getTimestamp(6); - return new BuildingDTO( - results.getInt(1), - results.getInt(2), - results.getString(3), - results.getBoolean(4), - results.getBoolean(5), - timestamp != null ? timestamp.toLocalDateTime() : null, - results.getDouble(7), - results.getDouble(8) - ); + //this works could be updated in the future to group buildings in a more organic way (not just a grid) + public List getBuildingGridCounts(double minLat, double maxLat, double minLon, double maxLon, double stepLat, double stepLon, String playerUuid) { + List cells = new ArrayList<>(); + boolean hasPlayer = (playerUuid != null && !playerUuid.isEmpty()); + + // Anchor cells globally to (0,0) using FLOOR(coord / step) + String sql = """ + SELECT + CAST(FLOOR(lat / ?) AS SIGNED) AS cell_y, + CAST(FLOOR(lon / ?) AS SIGNED) AS cell_x, + COUNT(*) AS cell_count + FROM buildings + WHERE lat BETWEEN ? AND ? + AND lon BETWEEN ? AND ? + """ + (hasPlayer ? "AND (is_public = TRUE OR player_id = ?) " : "AND is_public = TRUE ") + """ + GROUP BY cell_y, cell_x; + """; + + try (Connection conn = conn(); PreparedStatement statement = conn.prepareStatement(sql)) { + int idx = 1; + statement.setDouble(idx++, stepLat); + statement.setDouble(idx++, stepLon); + + statement.setDouble(idx++, minLat); + statement.setDouble(idx++, maxLat); + statement.setDouble(idx++, minLon); + statement.setDouble(idx++, maxLon); + + if (hasPlayer) { + statement.setString(idx++, playerUuid); + } + + try (ResultSet results = statement.executeQuery()) { + while (results.next()) { + int cellY = results.getInt("cell_y"); + int cellX = results.getInt("cell_x"); + int count = results.getInt("cell_count"); + + // Calculate the exact center coordinate of this fixed world cell + double centerLat = (cellY + 0.5) * stepLat; + double centerLon = (cellX + 0.5) * stepLon; + + cells.add(new GridCellDTO(centerLat, centerLon, count)); + } + } + } catch (SQLException e) { + log.severe("Failed to calculate building grid count in SQL: " + e.getMessage()); + } + return cells; } } From 70c49106fd8e2ab61ffefed5b60c02f86b88f503 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 24 Jul 2026 22:08:07 +0100 Subject: [PATCH 13/38] updated the grid cell count to return more info --- .../btuk/proxy/api/impl/BuildingsApiImpl.java | 8 +- api/src/main/resources/openapi.yaml | 20 +++- .../btuk/proxy/database/dto/BuildingDTO.java | 12 +- .../btuk/proxy/database/dto/GridCellDTO.java | 12 +- .../btuk/proxy/database/sql/GlobalSQL.java | 103 ++++++++++-------- 5 files changed, 104 insertions(+), 51 deletions(-) diff --git a/api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java b/api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java index 31c7565..df80fea 100644 --- a/api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java +++ b/api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java @@ -72,6 +72,12 @@ public Response getBuildingGridCount(Double minLat, Double maxLat, Double minLon GridCell cell = new GridCell(); cell.setLat(dbCell.lat()); cell.setLon(dbCell.lon()); + cell.setMinLat(dbCell.minLat()); + cell.setMaxLat(dbCell.maxLat()); + cell.setMinLon(dbCell.minLon()); + cell.setMaxLon(dbCell.maxLon()); + cell.setRow(dbCell.row()); + cell.setCol(dbCell.col()); cell.setCount(dbCell.count()); cells.add(cell); } @@ -85,7 +91,7 @@ public Response getBuildingGridCount(Double minLat, Double maxLat, Double minLon private Building mapBuilding(BuildingDTO dto) { Building building = new Building(); building.setBuildingId(dto.buildingId()); - building.setCoordinateId(dto.coordinateId()); + building.setPlayerName(dto.playerName()); building.setPlayerId(dto.playerId() != null ? UUID.fromString(dto.playerId()) : null); building.setIsPublic(dto.isPublic()); building.setPlayerBuilt(dto.playerBuilt()); diff --git a/api/src/main/resources/openapi.yaml b/api/src/main/resources/openapi.yaml index 63def55..e9ad653 100644 --- a/api/src/main/resources/openapi.yaml +++ b/api/src/main/resources/openapi.yaml @@ -298,8 +298,8 @@ components: properties: buildingId: type: integer - coordinateId: - type: integer + playerName: + type: string playerId: type: string format: uuid @@ -332,6 +332,22 @@ components: lon: type: number format: double + maxLat: + type: number + format: double + maxLon: + type: number + format: double + minLat: + type: number + format: double + minLon: + type: number + format: double + row: + type: integer + col: + type: integer count: type: integer example: 12 diff --git a/database/src/main/java/org/btuk/proxy/database/dto/BuildingDTO.java b/database/src/main/java/org/btuk/proxy/database/dto/BuildingDTO.java index 86acb2a..d797948 100644 --- a/database/src/main/java/org/btuk/proxy/database/dto/BuildingDTO.java +++ b/database/src/main/java/org/btuk/proxy/database/dto/BuildingDTO.java @@ -3,5 +3,13 @@ import java.time.LocalDateTime; import java.util.UUID; -public record BuildingDTO(int buildingId, int coordinateId, String playerId, boolean isPublic, boolean playerBuilt, LocalDateTime timeAdded, double lat, double lon) { -} +public record BuildingDTO( + int buildingId, + String playerId, + String playerName, // Added field + boolean isPublic, + boolean playerBuilt, + LocalDateTime timeAdded, + double lat, + double lon +) {} diff --git a/database/src/main/java/org/btuk/proxy/database/dto/GridCellDTO.java b/database/src/main/java/org/btuk/proxy/database/dto/GridCellDTO.java index 36499e7..1c7c4a3 100644 --- a/database/src/main/java/org/btuk/proxy/database/dto/GridCellDTO.java +++ b/database/src/main/java/org/btuk/proxy/database/dto/GridCellDTO.java @@ -1,3 +1,13 @@ package org.btuk.proxy.database.dto; -public record GridCellDTO(double lat, double lon, int count) {} +public record GridCellDTO( + double lat, + double lon, + double minLat, + double maxLat, + double minLon, + double maxLon, + int row, + int col, + int count +) {} diff --git a/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java b/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java index f5f6f85..fde5e52 100644 --- a/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java +++ b/database/src/main/java/org/btuk/proxy/database/sql/GlobalSQL.java @@ -255,34 +255,17 @@ public List getOnlinePlayers() { return players; } - public List getBuildingsByPlayer(String uuid) { - List buildings = new ArrayList<>(); - if (uuid == null) return buildings; - - try (Connection conn = conn(); PreparedStatement statement = conn.prepareStatement("SELECT building_id, coordinate_id, player_id, is_public, player_built, time_added, lat, lon FROM buildings WHERE player_id=?;")) { - statement.setString(1, uuid); - try (ResultSet results = statement.executeQuery()) { - while (results.next()) { - buildings.add(mapBuilding(results)); - } - } - } catch (SQLException e) { - log.severe("Failed to get buildings for " + uuid + ": " + e.getMessage()); - } - return buildings; - } - private BuildingDTO mapBuilding(ResultSet results) throws SQLException { - Timestamp timestamp = results.getTimestamp(6); + Timestamp timestamp = results.getTimestamp("time_added"); return new BuildingDTO( - results.getInt(1), - results.getInt(2), - results.getString(3), - results.getBoolean(4), - results.getBoolean(5), + results.getInt("building_id"), + results.getString("player_id"), + results.getString("player_name"), + results.getBoolean("is_public"), + results.getBoolean("player_built"), timestamp != null ? timestamp.toLocalDateTime() : null, - results.getDouble(7), - results.getDouble(8) + results.getDouble("lat"), + results.getDouble("lon") ); } @@ -341,14 +324,28 @@ public int getBuildingCount(List playerUuids, Double minLat, Double maxL return 0; } - // Area fetch with privacy filtering (excludes private buildings unless owned by playerUuid) + // Area fetch with JOIN to retrieve builder username public List getBuildingsByArea(double minLat, double maxLat, double minLon, double maxLon, String playerUuid) { List buildings = new ArrayList<>(); boolean hasPlayer = (playerUuid != null && !playerUuid.isEmpty()); - String sql = "SELECT building_id, coordinate_id, player_id, is_public, player_built, time_added, lat, lon " + - "FROM buildings WHERE lat BETWEEN ? AND ? AND lon BETWEEN ? AND ? " + - (hasPlayer ? "AND (is_public = TRUE OR player_id = ?);" : "AND is_public = TRUE;"); + + // INNER JOIN on player_data table using building.player_id = player_data.uuid + String sql = """ + SELECT + b.building_id, + b.player_id, + p.name AS player_name, + b.is_public, + b.player_built, + b.time_added, + b.lat, + b.lon + FROM buildings b + INNER JOIN player_data p ON b.player_id = p.uuid + WHERE b.lat BETWEEN ? AND ? + AND b.lon BETWEEN ? AND ? + """ + (hasPlayer ? "AND (b.is_public = TRUE OR b.player_id = ?);" : "AND b.is_public = TRUE;"); try (Connection conn = conn(); PreparedStatement statement = conn.prepareStatement(sql)) { statement.setDouble(1, minLat); @@ -374,18 +371,20 @@ public List getBuildingGridCounts(double minLat, double maxLat, dou List cells = new ArrayList<>(); boolean hasPlayer = (playerUuid != null && !playerUuid.isEmpty()); - // Anchor cells globally to (0,0) using FLOOR(coord / step) + // Anchor cells globally to (0,0) using FLOOR(coord / step) and average building positions String sql = """ - SELECT - CAST(FLOOR(lat / ?) AS SIGNED) AS cell_y, - CAST(FLOOR(lon / ?) AS SIGNED) AS cell_x, - COUNT(*) AS cell_count - FROM buildings - WHERE lat BETWEEN ? AND ? - AND lon BETWEEN ? AND ? - """ + (hasPlayer ? "AND (is_public = TRUE OR player_id = ?) " : "AND is_public = TRUE ") + """ - GROUP BY cell_y, cell_x; - """; + SELECT + CAST(FLOOR(lat / ?) AS SIGNED) AS cell_y, + CAST(FLOOR(lon / ?) AS SIGNED) AS cell_x, + AVG(lat) AS avg_lat, + AVG(lon) AS avg_lon, + COUNT(*) AS cell_count + FROM buildings + WHERE lat BETWEEN ? AND ? + AND lon BETWEEN ? AND ? + """ + (hasPlayer ? "AND (is_public = TRUE OR player_id = ?) " : "AND is_public = TRUE ") + """ + GROUP BY cell_y, cell_x; + """; try (Connection conn = conn(); PreparedStatement statement = conn.prepareStatement(sql)) { int idx = 1; @@ -405,13 +404,27 @@ public List getBuildingGridCounts(double minLat, double maxLat, dou while (results.next()) { int cellY = results.getInt("cell_y"); int cellX = results.getInt("cell_x"); + double avgLat = results.getDouble("avg_lat"); + double avgLon = results.getDouble("avg_lon"); int count = results.getInt("cell_count"); - // Calculate the exact center coordinate of this fixed world cell - double centerLat = (cellY + 0.5) * stepLat; - double centerLon = (cellX + 0.5) * stepLon; - - cells.add(new GridCellDTO(centerLat, centerLon, count)); + // Calculate bounding box bounds for this specific cell grid + double cellMinLat = cellY * stepLat; + double cellMaxLat = (cellY + 1) * stepLat; + double cellMinLon = cellX * stepLon; + double cellMaxLon = (cellX + 1) * stepLon; + + cells.add(new GridCellDTO( + avgLat, + avgLon, + cellMinLat, + cellMaxLat, + cellMinLon, + cellMaxLon, + cellY, + cellX, + count + )); } } } catch (SQLException e) { From ed4e828db66bda4190af9c963fc06cd89cba839a Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 24 Jul 2026 22:28:14 +0100 Subject: [PATCH 14/38] updated the opanapi.yaml so that it has the correct production url --- api/src/main/resources/openapi.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/resources/openapi.yaml b/api/src/main/resources/openapi.yaml index e9ad653..f3c2dbd 100644 --- a/api/src/main/resources/openapi.yaml +++ b/api/src/main/resources/openapi.yaml @@ -4,7 +4,7 @@ info: description: Lightweight API for Proxy internal communication. version: 1.0.0 servers: - - url: http://51.195.190.215:61101/api + - url: http://172.18.0.1:51101/api paths: /status: get: From 4cd77f3b1879a0e7f79c1df3fecbbe86f673d286 Mon Sep 17 00:00:00 2001 From: AMgr8 <153189709+AMgr8@users.noreply.github.com> Date: Sat, 25 Jul 2026 14:32:36 +0100 Subject: [PATCH 15/38] fixed issue --- plugin/src/main/java/org/btuk/proxy/Proxy.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugin/src/main/java/org/btuk/proxy/Proxy.java b/plugin/src/main/java/org/btuk/proxy/Proxy.java index 2cb45ef..2250eac 100644 --- a/plugin/src/main/java/org/btuk/proxy/Proxy.java +++ b/plugin/src/main/java/org/btuk/proxy/Proxy.java @@ -38,8 +38,6 @@ import java.util.UUID; import java.util.function.Consumer; -import org.btuk.proxy.core.ProxyController; - @Plugin(id = "proxy", name = "Proxy", version = "1.12.0-SNAPSHOT", url = "https://github.com/BTEUK/Proxy", description = "Proxy plugin, managed chat, discord and server related actions.", authors = {"ELgamer"}) public class Proxy { From 74a34b31531b8d42810147b5e4a75667a8a70f6a Mon Sep 17 00:00:00 2001 From: George112n <76963316+george112n@users.noreply.github.com> Date: Sat, 25 Jul 2026 14:47:17 +0100 Subject: [PATCH 16/38] Bump version --- api/pom.xml | 2 +- core/pom.xml | 2 +- database/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index b7691dd..41f94d2 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -6,7 +6,7 @@ org.btuk Proxy - 1.12.0-SNAPSHOT + 1.13.0-SNAPSHOT org.btuk.proxy diff --git a/core/pom.xml b/core/pom.xml index 6248470..9f00e77 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,7 +6,7 @@ org.btuk Proxy - 1.12.0-SNAPSHOT + 1.13.0-SNAPSHOT org.btuk.proxy diff --git a/database/pom.xml b/database/pom.xml index 1df1ae9..7ef8e1d 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -4,7 +4,7 @@ org.btuk Proxy - 1.12.0-SNAPSHOT + 1.13.0-SNAPSHOT org.btuk.proxy diff --git a/plugin/pom.xml b/plugin/pom.xml index 4c018b8..b76825c 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -7,7 +7,7 @@ org.btuk Proxy - 1.12.0-SNAPSHOT + 1.13.0-SNAPSHOT org.btuk.proxy diff --git a/pom.xml b/pom.xml index 75d7f3c..dff10c7 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.btuk Proxy - 1.12.0-SNAPSHOT + 1.13.0-SNAPSHOT pom Proxy From 2604a16d5efaa67de0fb7c4e48a87ec6f7b2b681 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 27 Jul 2026 19:00:11 +0100 Subject: [PATCH 17/38] bumped version in a couple more places. fixed an error that shouldn't have been happening. gemini magic. changed some pom things idk if they make a difference --- .../btuk/proxy/api/impl/BuildingsApiImpl.java | 4 +++ .../btuk/proxy/api/impl/PlayerApiImpl.java | 5 ++- .../btuk/proxy/api/impl/StatusApiImpl.java | 2 ++ .../org/btuk/proxy/api/server/ProxyApi.java | 33 ++++++++++++++++--- api/src/main/resources/openapi.yaml | 2 +- core/src/main/resources/proxy-config.yml | 2 +- plugin/pom.xml | 11 +++++-- .../src/main/java/org/btuk/proxy/Proxy.java | 2 +- 8 files changed, 51 insertions(+), 10 deletions(-) diff --git a/api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java b/api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java index df80fea..ca74811 100644 --- a/api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java +++ b/api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java @@ -1,5 +1,7 @@ package org.btuk.proxy.api.impl; +import jakarta.inject.Inject; +import jakarta.ws.rs.Path; import jakarta.ws.rs.core.Response; import org.btuk.proxy.api.BuildingsApi; import org.btuk.proxy.api.model.Building; @@ -17,10 +19,12 @@ import java.util.UUID; import java.util.stream.Collectors; +@Path("/buildings") public class BuildingsApiImpl implements BuildingsApi { private final GlobalSQL globalSQL; + @Inject public BuildingsApiImpl(GlobalSQL globalSQL) { this.globalSQL = globalSQL; } diff --git a/api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java b/api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java index a67946d..b01db6a 100644 --- a/api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java +++ b/api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java @@ -1,5 +1,7 @@ package org.btuk.proxy.api.impl; +import jakarta.inject.Inject; +import jakarta.ws.rs.Path; import jakarta.ws.rs.core.Response; import net.bteuk.network.lib.dto.DirectMessage; import net.bteuk.network.lib.utils.ChatUtils; @@ -16,12 +18,13 @@ import static net.bteuk.network.lib.enums.ChatChannels.GLOBAL; import static org.btuk.proxy.core.utils.Constants.SERVER_SENDER; - +@Path("/player") public class PlayerApiImpl implements PlayerApi { private final GlobalSQL globalSQL; private final ChatManager chatManager; + @Inject public PlayerApiImpl(GlobalSQL globalSQL, ChatManager chatManager) { this.globalSQL = globalSQL;this.chatManager = chatManager; } diff --git a/api/src/main/java/org/btuk/proxy/api/impl/StatusApiImpl.java b/api/src/main/java/org/btuk/proxy/api/impl/StatusApiImpl.java index 5433870..6326ba6 100644 --- a/api/src/main/java/org/btuk/proxy/api/impl/StatusApiImpl.java +++ b/api/src/main/java/org/btuk/proxy/api/impl/StatusApiImpl.java @@ -1,9 +1,11 @@ package org.btuk.proxy.api.impl; +import jakarta.ws.rs.Path; import jakarta.ws.rs.core.Response; import org.btuk.proxy.api.StatusApi; import org.btuk.proxy.api.model.Status; +@Path("/status") public class StatusApiImpl implements StatusApi { @Override diff --git a/api/src/main/java/org/btuk/proxy/api/server/ProxyApi.java b/api/src/main/java/org/btuk/proxy/api/server/ProxyApi.java index 6feadaa..5723b78 100644 --- a/api/src/main/java/org/btuk/proxy/api/server/ProxyApi.java +++ b/api/src/main/java/org/btuk/proxy/api/server/ProxyApi.java @@ -9,6 +9,7 @@ import org.glassfish.grizzly.http.server.HttpServer; import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; +import org.glassfish.jersey.internal.inject.AbstractBinder; import org.glassfish.jersey.server.ResourceConfig; import java.net.URI; @@ -43,10 +44,34 @@ public void start() { String baseUri = "http://0.0.0.0:" + apiPort + "/api/"; - ResourceConfig rc = new ResourceConfig() - .register(new StatusApiImpl()) - .register(new PlayerApiImpl(globalSQL,chatManager)) - .register(new BuildingsApiImpl(globalSQL)); + + ResourceConfig rc = new ResourceConfig(); + + // 1. Disable WADL warning + rc.property("jersey.config.server.wadl.disableWadl", true); + + // 2. Bind SQL and ChatManager dependencies for injection + rc.register(new AbstractBinder() { + @Override + protected void configure() { + bind(globalSQL).to(GlobalSQL.class); + bind(chatManager).to(ChatManager.class); + } + }); + + // 3. Register the implementation CLASSES (or scan the package) + rc.register(StatusApiImpl.class); + rc.register(PlayerApiImpl.class); + rc.register(BuildingsApiImpl.class); + +// ResourceConfig rc = new ResourceConfig() +// .property("jersey.config.server.wadl.disableWadl", true) +// .packages("org.btuk.proxy.api.impl"); + +// ResourceConfig rc = new ResourceConfig() +// .register(new StatusApiImpl()) +// .register(new PlayerApiImpl(globalSQL,chatManager)) +// .register(new BuildingsApiImpl(globalSQL)); try { server = GrizzlyHttpServerFactory.createHttpServer(URI.create(baseUri), rc); diff --git a/api/src/main/resources/openapi.yaml b/api/src/main/resources/openapi.yaml index f3c2dbd..3eb16fe 100644 --- a/api/src/main/resources/openapi.yaml +++ b/api/src/main/resources/openapi.yaml @@ -84,7 +84,7 @@ paths: responses: "201": description: Message Added - /players/online: + /player/online: get: tags: - Player diff --git a/core/src/main/resources/proxy-config.yml b/core/src/main/resources/proxy-config.yml index ad24a34..b63d77f 100644 --- a/core/src/main/resources/proxy-config.yml +++ b/core/src/main/resources/proxy-config.yml @@ -1,5 +1,5 @@ #Config version (do not update) -version: "1.12.0-SNAPSHOT" +version: "1.13.0-SNAPSHOT" #Bot token token: "insert token here" diff --git a/plugin/pom.xml b/plugin/pom.xml index b76825c..79f9864 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -30,7 +30,11 @@ false Proxy-${project.version} true - + + + io.netty:* + + *:* @@ -64,7 +68,10 @@ net/kyori/** org/jetbrains/** org/intellij/** - META-INF/** + META-INF/MANIFEST.MF + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA diff --git a/plugin/src/main/java/org/btuk/proxy/Proxy.java b/plugin/src/main/java/org/btuk/proxy/Proxy.java index 2250eac..d4b8a34 100644 --- a/plugin/src/main/java/org/btuk/proxy/Proxy.java +++ b/plugin/src/main/java/org/btuk/proxy/Proxy.java @@ -38,7 +38,7 @@ import java.util.UUID; import java.util.function.Consumer; -@Plugin(id = "proxy", name = "Proxy", version = "1.12.0-SNAPSHOT", +@Plugin(id = "proxy", name = "Proxy", version = "1.13.0-SNAPSHOT", url = "https://github.com/BTEUK/Proxy", description = "Proxy plugin, managed chat, discord and server related actions.", authors = {"ELgamer"}) public class Proxy { From f0b7bf5fd31b2d27cd7d62404e888c720bba5d0a Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sun, 2 Aug 2026 11:44:27 +0200 Subject: [PATCH 18/38] Update proxy to latest networklib version. --- .../btuk/proxy/api/impl/PlayerApiImpl.java | 6 +-- core/pom.xml | 2 +- .../org/btuk/proxy/core/chat/ChatHandler.java | 2 +- .../org/btuk/proxy/core/chat/ChatManager.java | 22 ++++---- .../btuk/proxy/core/chat/automod/AutoMod.java | 17 +++--- .../proxy/core/discord/BotChatListener.java | 5 +- .../org/btuk/proxy/core/discord/Discord.java | 41 +++++++------- .../core/discord/DiscordChatListener.java | 7 ++- .../core/discord/command/Playerlist.java | 11 ++-- .../btuk/proxy/core/server/ServerManager.java | 23 ++++---- .../proxy/core/socket/ProxySocketHandler.java | 24 +++++++-- .../proxy/core/tab/AbstractTabManager.java | 19 ++++--- .../org/btuk/proxy/core/tab/TabManager.java | 5 +- .../btuk/proxy/core/user/CoreUserManager.java | 2 +- .../java/org/btuk/proxy/core/user/User.java | 51 +++++++++--------- .../org/btuk/proxy/core/user/UserManager.java | 54 ++++++++++++------- database/pom.xml | 2 +- .../org/btuk/proxy/database/sql/PlotSQL.java | 4 +- plugin/pom.xml | 2 +- .../src/main/java/org/btuk/proxy/Proxy.java | 8 +-- .../java/org/btuk/proxy/ProxyController.java | 35 ++++++------ .../org/btuk/proxy/chat/ProxyChatHandler.java | 15 +++--- .../org/btuk/proxy/tab/ProxyTabManager.java | 17 +++--- pom.xml | 4 +- 24 files changed, 197 insertions(+), 181 deletions(-) diff --git a/api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java b/api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java index a67946d..99cb401 100644 --- a/api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java +++ b/api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java @@ -1,8 +1,8 @@ package org.btuk.proxy.api.impl; import jakarta.ws.rs.core.Response; -import net.bteuk.network.lib.dto.DirectMessage; -import net.bteuk.network.lib.utils.ChatUtils; +import org.btuk.network.lib.dto.DirectMessage; +import org.btuk.network.lib.utils.ChatUtils; import org.btuk.proxy.api.PlayerApi; import org.btuk.proxy.api.model.Message; import org.btuk.proxy.api.model.Player; @@ -14,7 +14,7 @@ import java.util.UUID; import java.util.stream.Collectors; -import static net.bteuk.network.lib.enums.ChatChannels.GLOBAL; +import static org.btuk.network.lib.enums.ChatChannels.GLOBAL; import static org.btuk.proxy.core.utils.Constants.SERVER_SENDER; public class PlayerApiImpl implements PlayerApi { diff --git a/core/pom.xml b/core/pom.xml index 9f00e77..46298d2 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -26,7 +26,7 @@ - com.github.BTEUK + com.github.BuildtheUK NetworkLib diff --git a/core/src/main/java/org/btuk/proxy/core/chat/ChatHandler.java b/core/src/main/java/org/btuk/proxy/core/chat/ChatHandler.java index 8280fdf..434d042 100644 --- a/core/src/main/java/org/btuk/proxy/core/chat/ChatHandler.java +++ b/core/src/main/java/org/btuk/proxy/core/chat/ChatHandler.java @@ -1,6 +1,6 @@ package org.btuk.proxy.core.chat; -import net.bteuk.network.lib.dto.AbstractTransferObject; +import org.btuk.network.lib.dto.AbstractTransferObject; import org.btuk.proxy.core.exceptions.ServerNotFoundException; diff --git a/core/src/main/java/org/btuk/proxy/core/chat/ChatManager.java b/core/src/main/java/org/btuk/proxy/core/chat/ChatManager.java index 8e99beb..abcf22b 100644 --- a/core/src/main/java/org/btuk/proxy/core/chat/ChatManager.java +++ b/core/src/main/java/org/btuk/proxy/core/chat/ChatManager.java @@ -1,26 +1,24 @@ package org.btuk.proxy.core.chat; -import net.bteuk.network.lib.dto.ChatMessage; -import net.bteuk.network.lib.dto.DirectMessage; -import net.bteuk.network.lib.dto.PrivateMessage; -import net.bteuk.network.lib.dto.ReplyMessage; -import net.bteuk.network.lib.utils.ChatUtils; - -import org.btuk.proxy.core.chat.automod.AutoMod; -import org.btuk.proxy.database.sql.GlobalSQL; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; - -import java.util.List; - +import org.btuk.network.lib.dto.ChatMessage; +import org.btuk.network.lib.dto.DirectMessage; +import org.btuk.network.lib.dto.PrivateMessage; +import org.btuk.network.lib.dto.ReplyMessage; +import org.btuk.network.lib.utils.ChatUtils; +import org.btuk.proxy.core.chat.automod.AutoMod; import org.btuk.proxy.core.user.CoreUserManager; import org.btuk.proxy.core.user.User; import org.btuk.proxy.core.utils.Analytics; import org.btuk.proxy.core.utils.Moderation; import org.btuk.proxy.core.utils.Time; +import org.btuk.proxy.database.sql.GlobalSQL; + +import java.util.List; -import static net.bteuk.network.lib.enums.ChatChannels.GLOBAL; +import static org.btuk.network.lib.enums.ChatChannels.GLOBAL; import static org.btuk.proxy.core.utils.Constants.DISCORD_SENDER; import static org.btuk.proxy.core.utils.Constants.SERVER_SENDER; diff --git a/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoMod.java b/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoMod.java index 00c9d28..e50823b 100644 --- a/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoMod.java +++ b/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoMod.java @@ -1,16 +1,10 @@ package org.btuk.proxy.core.chat.automod; import lombok.extern.java.Log; -import net.bteuk.network.lib.dto.DirectMessage; -import net.bteuk.network.lib.utils.ChatUtils; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; - -import java.time.Duration; -import java.util.Collections; -import java.util.List; -import java.util.Map; - +import org.btuk.network.lib.dto.DirectMessage; +import org.btuk.network.lib.utils.ChatUtils; import org.btuk.proxy.core.chat.ChatHandler; import org.btuk.proxy.core.config.Config; import org.btuk.proxy.core.discord.Discord; @@ -20,7 +14,12 @@ import org.btuk.proxy.core.utils.Moderation; import org.btuk.proxy.core.utils.Time; -import static net.bteuk.network.lib.enums.ChatChannels.GLOBAL; +import java.time.Duration; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.btuk.network.lib.enums.ChatChannels.GLOBAL; import static org.btuk.proxy.core.utils.Constants.SERVER_SENDER; /** diff --git a/core/src/main/java/org/btuk/proxy/core/discord/BotChatListener.java b/core/src/main/java/org/btuk/proxy/core/discord/BotChatListener.java index 87945af..03bbc68 100644 --- a/core/src/main/java/org/btuk/proxy/core/discord/BotChatListener.java +++ b/core/src/main/java/org/btuk/proxy/core/discord/BotChatListener.java @@ -1,16 +1,15 @@ package org.btuk.proxy.core.discord; import lombok.extern.java.Log; -import net.bteuk.network.lib.dto.DiscordLinking; import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import org.apache.commons.lang3.StringUtils; +import org.btuk.network.lib.dto.DiscordLinking; +import org.btuk.proxy.core.chat.ChatHandler; import java.util.List; -import org.btuk.proxy.core.chat.ChatHandler; - @Log public class BotChatListener extends ListenerAdapter { diff --git a/core/src/main/java/org/btuk/proxy/core/discord/Discord.java b/core/src/main/java/org/btuk/proxy/core/discord/Discord.java index a435367..8873692 100644 --- a/core/src/main/java/org/btuk/proxy/core/discord/Discord.java +++ b/core/src/main/java/org/btuk/proxy/core/discord/Discord.java @@ -3,20 +3,15 @@ import lombok.Getter; import lombok.Setter; import lombok.extern.java.Log; -import net.bteuk.network.lib.dto.ChatMessage; -import net.bteuk.network.lib.dto.DiscordDirectMessage; -import net.bteuk.network.lib.dto.DiscordEmbed; -import net.bteuk.network.lib.dto.DiscordLinking; -import net.bteuk.network.lib.dto.DiscordRole; - -import org.btuk.proxy.core.chat.automod.AutoModMatch; -import org.btuk.proxy.core.user.User; -import org.btuk.proxy.database.sql.GlobalSQL; -import org.btuk.proxy.database.sql.PlotSQL; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; -import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.entities.Activity; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.UserSnowflake; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.requests.GatewayIntent; import net.dv8tion.jda.api.utils.ChunkingFilter; @@ -28,6 +23,22 @@ import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.apache.commons.lang3.time.DurationFormatUtils; +import org.btuk.network.lib.dto.ChatMessage; +import org.btuk.network.lib.dto.DiscordDirectMessage; +import org.btuk.network.lib.dto.DiscordEmbed; +import org.btuk.network.lib.dto.DiscordLinking; +import org.btuk.network.lib.dto.DiscordRole; +import org.btuk.proxy.core.chat.ChatHandler; +import org.btuk.proxy.core.chat.ChatManager; +import org.btuk.proxy.core.chat.automod.AutoModMatch; +import org.btuk.proxy.core.config.Config; +import org.btuk.proxy.core.discord.command.CommandManager; +import org.btuk.proxy.core.scheduler.Scheduler; +import org.btuk.proxy.core.tab.TabManager; +import org.btuk.proxy.core.user.CoreUserManager; +import org.btuk.proxy.core.user.User; +import org.btuk.proxy.database.sql.GlobalSQL; +import org.btuk.proxy.database.sql.PlotSQL; import java.awt.Color; import java.nio.charset.StandardCharsets; @@ -39,14 +50,6 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -import org.btuk.proxy.core.chat.ChatHandler; -import org.btuk.proxy.core.chat.ChatManager; -import org.btuk.proxy.core.config.Config; -import org.btuk.proxy.core.discord.command.CommandManager; -import org.btuk.proxy.core.scheduler.Scheduler; -import org.btuk.proxy.core.tab.TabManager; -import org.btuk.proxy.core.user.CoreUserManager; - @Log public class Discord { diff --git a/core/src/main/java/org/btuk/proxy/core/discord/DiscordChatListener.java b/core/src/main/java/org/btuk/proxy/core/discord/DiscordChatListener.java index 8e075e1..c3673d2 100644 --- a/core/src/main/java/org/btuk/proxy/core/discord/DiscordChatListener.java +++ b/core/src/main/java/org/btuk/proxy/core/discord/DiscordChatListener.java @@ -1,9 +1,6 @@ package org.btuk.proxy.core.discord; import lombok.extern.java.Log; -import net.bteuk.network.lib.dto.ChatMessage; -import net.bteuk.network.lib.enums.ChatChannels; -import net.bteuk.network.lib.utils.ChatUtils; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.kyori.adventure.text.Component; @@ -11,7 +8,9 @@ import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.format.TextDecoration; import org.apache.commons.lang3.StringUtils; - +import org.btuk.network.lib.dto.ChatMessage; +import org.btuk.network.lib.enums.ChatChannels; +import org.btuk.network.lib.utils.ChatUtils; import org.btuk.proxy.core.chat.ChatManager; import static org.btuk.proxy.core.utils.Constants.DISCORD_SENDER; diff --git a/core/src/main/java/org/btuk/proxy/core/discord/command/Playerlist.java b/core/src/main/java/org/btuk/proxy/core/discord/command/Playerlist.java index e1eef47..2004a8f 100644 --- a/core/src/main/java/org/btuk/proxy/core/discord/command/Playerlist.java +++ b/core/src/main/java/org/btuk/proxy/core/discord/command/Playerlist.java @@ -1,20 +1,19 @@ package org.btuk.proxy.core.discord.command; -import net.bteuk.network.lib.dto.OnlineUser; -import net.bteuk.network.lib.dto.TabPlayer; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.btuk.network.lib.dto.OnlineUser; +import org.btuk.network.lib.dto.TabPlayer; +import org.btuk.proxy.core.tab.TabManager; +import org.btuk.proxy.core.user.CoreUserManager; +import org.btuk.proxy.core.user.User; import java.util.ArrayList; import java.util.Comparator; import java.util.Optional; import java.util.Set; -import org.btuk.proxy.core.tab.TabManager; -import org.btuk.proxy.core.user.CoreUserManager; -import org.btuk.proxy.core.user.User; - public class Playerlist extends AbstractCommand { private final CoreUserManager userManager; diff --git a/core/src/main/java/org/btuk/proxy/core/server/ServerManager.java b/core/src/main/java/org/btuk/proxy/core/server/ServerManager.java index e072d55..b2290cd 100644 --- a/core/src/main/java/org/btuk/proxy/core/server/ServerManager.java +++ b/core/src/main/java/org/btuk/proxy/core/server/ServerManager.java @@ -1,18 +1,10 @@ package org.btuk.proxy.core.server; import lombok.extern.java.Log; -import net.bteuk.network.lib.dto.OnlineUserRemove; -import net.bteuk.network.lib.dto.OnlineUsersReply; -import net.bteuk.network.lib.dto.ServerShutdown; -import net.bteuk.network.lib.dto.ServerStartup; -import org.btuk.proxy.database.sql.GlobalSQL; - -import java.util.List; -import java.util.Optional; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - +import org.btuk.network.lib.dto.OnlineUserRemove; +import org.btuk.network.lib.dto.OnlineUsersReply; +import org.btuk.network.lib.dto.ServerShutdown; +import org.btuk.network.lib.dto.ServerStartup; import org.btuk.proxy.core.chat.ChatHandler; import org.btuk.proxy.core.scheduler.Scheduler; import org.btuk.proxy.core.tab.TabManager; @@ -20,6 +12,13 @@ import org.btuk.proxy.core.user.User; import org.btuk.proxy.core.user.UserManager; import org.btuk.proxy.core.utils.Time; +import org.btuk.proxy.database.sql.GlobalSQL; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; @Log public class ServerManager { diff --git a/core/src/main/java/org/btuk/proxy/core/socket/ProxySocketHandler.java b/core/src/main/java/org/btuk/proxy/core/socket/ProxySocketHandler.java index c59fe66..588c69e 100644 --- a/core/src/main/java/org/btuk/proxy/core/socket/ProxySocketHandler.java +++ b/core/src/main/java/org/btuk/proxy/core/socket/ProxySocketHandler.java @@ -1,9 +1,27 @@ package org.btuk.proxy.core.socket; import lombok.extern.java.Log; -import net.bteuk.network.lib.dto.*; -import net.bteuk.network.lib.socket.SocketHandler; - +import org.btuk.network.lib.dto.AbstractTransferObject; +import org.btuk.network.lib.dto.ChatMessage; +import org.btuk.network.lib.dto.DirectMessage; +import org.btuk.network.lib.dto.DiscordDirectMessage; +import org.btuk.network.lib.dto.DiscordEmbed; +import org.btuk.network.lib.dto.DiscordLinking; +import org.btuk.network.lib.dto.DiscordRole; +import org.btuk.network.lib.dto.FocusEvent; +import org.btuk.network.lib.dto.ModerationEvent; +import org.btuk.network.lib.dto.MuteEvent; +import org.btuk.network.lib.dto.PlotMessage; +import org.btuk.network.lib.dto.PrivateMessage; +import org.btuk.network.lib.dto.ReplyMessage; +import org.btuk.network.lib.dto.ServerShutdown; +import org.btuk.network.lib.dto.ServerStartup; +import org.btuk.network.lib.dto.SwitchServerEvent; +import org.btuk.network.lib.dto.TeleportEvent; +import org.btuk.network.lib.dto.UserConnectRequest; +import org.btuk.network.lib.dto.UserDisconnect; +import org.btuk.network.lib.dto.UserUpdate; +import org.btuk.network.lib.socket.SocketHandler; import org.btuk.proxy.core.chat.ChatManager; import org.btuk.proxy.core.discord.Discord; import org.btuk.proxy.core.server.ServerManager; diff --git a/core/src/main/java/org/btuk/proxy/core/tab/AbstractTabManager.java b/core/src/main/java/org/btuk/proxy/core/tab/AbstractTabManager.java index 125ff8e..d1ab730 100644 --- a/core/src/main/java/org/btuk/proxy/core/tab/AbstractTabManager.java +++ b/core/src/main/java/org/btuk/proxy/core/tab/AbstractTabManager.java @@ -1,14 +1,20 @@ package org.btuk.proxy.core.tab; import lombok.Getter; -import net.bteuk.network.lib.dto.AddTeamEvent; -import net.bteuk.network.lib.dto.TabPlayer; -import net.bteuk.network.lib.utils.ChatUtils; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.text.minimessage.MiniMessage; +import org.btuk.network.lib.dto.AddTeamEvent; +import org.btuk.network.lib.dto.TabPlayer; +import org.btuk.network.lib.utils.ChatUtils; +import org.btuk.proxy.core.chat.ChatHandler; +import org.btuk.proxy.core.config.Config; +import org.btuk.proxy.core.player.Player; +import org.btuk.proxy.core.scheduler.Scheduler; +import org.btuk.proxy.core.user.CoreUserManager; +import org.btuk.proxy.core.user.User; import org.jetbrains.annotations.Nullable; import java.util.HashSet; @@ -17,13 +23,6 @@ import java.util.Set; import java.util.concurrent.TimeUnit; -import org.btuk.proxy.core.chat.ChatHandler; -import org.btuk.proxy.core.config.Config; -import org.btuk.proxy.core.player.Player; -import org.btuk.proxy.core.scheduler.Scheduler; -import org.btuk.proxy.core.user.CoreUserManager; -import org.btuk.proxy.core.user.User; - public abstract class AbstractTabManager implements TabManager { private final Config config; diff --git a/core/src/main/java/org/btuk/proxy/core/tab/TabManager.java b/core/src/main/java/org/btuk/proxy/core/tab/TabManager.java index c50ef3a..b552bb6 100644 --- a/core/src/main/java/org/btuk/proxy/core/tab/TabManager.java +++ b/core/src/main/java/org/btuk/proxy/core/tab/TabManager.java @@ -1,11 +1,10 @@ package org.btuk.proxy.core.tab; -import net.bteuk.network.lib.dto.TabPlayer; +import org.btuk.network.lib.dto.TabPlayer; +import org.btuk.proxy.core.user.User; import java.util.Optional; -import org.btuk.proxy.core.user.User; - public interface TabManager { void updatePlayerInTablistOfPlayer(User user, User userToUpdate); diff --git a/core/src/main/java/org/btuk/proxy/core/user/CoreUserManager.java b/core/src/main/java/org/btuk/proxy/core/user/CoreUserManager.java index 1145525..b8b13c2 100644 --- a/core/src/main/java/org/btuk/proxy/core/user/CoreUserManager.java +++ b/core/src/main/java/org/btuk/proxy/core/user/CoreUserManager.java @@ -1,6 +1,6 @@ package org.btuk.proxy.core.user; -import net.bteuk.network.lib.dto.OnlineUser; +import org.btuk.network.lib.dto.OnlineUser; import java.util.ArrayList; import java.util.Collections; diff --git a/core/src/main/java/org/btuk/proxy/core/user/User.java b/core/src/main/java/org/btuk/proxy/core/user/User.java index 7b39b29..eba294c 100644 --- a/core/src/main/java/org/btuk/proxy/core/user/User.java +++ b/core/src/main/java/org/btuk/proxy/core/user/User.java @@ -5,30 +5,37 @@ import lombok.Getter; import lombok.Setter; import lombok.extern.java.Log; -import net.bteuk.network.lib.dto.DirectMessage; -import net.bteuk.network.lib.dto.TeleportEvent; -import net.bteuk.network.lib.dto.UserConnectReply; -import net.bteuk.network.lib.dto.UserConnectRequest; -import net.bteuk.network.lib.enums.ChatChannels; -import net.bteuk.network.lib.enums.TeleportRequestType; -import net.bteuk.network.lib.utils.ChatUtils; - -import org.btuk.proxy.core.exceptions.ServerNotFoundException; -import org.btuk.proxy.core.utils.TeleportRequest; - +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.btuk.network.lib.dto.DirectMessage; +import org.btuk.network.lib.dto.TeleportEvent; +import org.btuk.network.lib.dto.UserConnectReply; +import org.btuk.network.lib.dto.UserConnectRequest; +import org.btuk.network.lib.enums.ChatChannels; +import org.btuk.network.lib.enums.TeleportRequestType; +import org.btuk.network.lib.utils.ChatUtils; +import org.btuk.proxy.core.chat.ChatHandler; import org.btuk.proxy.core.chat.automod.AutoMod; import org.btuk.proxy.core.chat.automod.AutoModFlag; import org.btuk.proxy.core.chat.automod.AutoModFlagRule; import org.btuk.proxy.core.chat.automod.AutoModMatch; import org.btuk.proxy.core.chat.automod.AutoModRule; +import org.btuk.proxy.core.exceptions.ServerNotFoundException; +import org.btuk.proxy.core.player.Player; +import org.btuk.proxy.core.scheduler.ScheduledTask; +import org.btuk.proxy.core.scheduler.Scheduler; +import org.btuk.proxy.core.scheduler.TaskStatus; +import org.btuk.proxy.core.tab.TabManager; +import org.btuk.proxy.core.utils.Analytics; +import org.btuk.proxy.core.utils.SwitchServer; +import org.btuk.proxy.core.utils.TeleportRequest; +import org.btuk.proxy.core.utils.Time; import org.btuk.proxy.database.dto.AutoModFlagDTO; import org.btuk.proxy.database.sql.GlobalSQL; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.format.Style; -import net.kyori.adventure.text.format.TextDecoration; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import java.io.IOException; import java.net.HttpURLConnection; @@ -47,16 +54,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import org.btuk.proxy.core.chat.ChatHandler; -import org.btuk.proxy.core.player.Player; -import org.btuk.proxy.core.scheduler.ScheduledTask; -import org.btuk.proxy.core.scheduler.Scheduler; -import org.btuk.proxy.core.scheduler.TaskStatus; -import org.btuk.proxy.core.tab.TabManager; -import org.btuk.proxy.core.utils.Analytics; -import org.btuk.proxy.core.utils.SwitchServer; -import org.btuk.proxy.core.utils.Time; - import static org.btuk.proxy.core.utils.Constants.SERVER_SENDER; /** diff --git a/core/src/main/java/org/btuk/proxy/core/user/UserManager.java b/core/src/main/java/org/btuk/proxy/core/user/UserManager.java index 4f8c9ae..02db842 100644 --- a/core/src/main/java/org/btuk/proxy/core/user/UserManager.java +++ b/core/src/main/java/org/btuk/proxy/core/user/UserManager.java @@ -2,37 +2,55 @@ import lombok.Getter; import lombok.extern.java.Log; -import net.bteuk.network.lib.dto.*; -import net.bteuk.network.lib.enums.ChatChannels; -import net.bteuk.network.lib.enums.ModerationAction; -import net.bteuk.network.lib.enums.TeleportRequestType; -import net.bteuk.network.lib.utils.ChatUtils; -import org.btuk.proxy.database.sql.GlobalSQL; -import org.btuk.proxy.database.sql.PlotSQL; -import org.btuk.proxy.database.sql.RegionSQL; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; - -import java.awt.Color; -import java.util.*; -import java.util.concurrent.TimeUnit; - -import org.btuk.proxy.core.chat.automod.AutoMod; -import org.btuk.proxy.core.discord.Discord; -import org.btuk.proxy.core.tab.TabManager; +import org.btuk.network.lib.dto.ChatMessage; +import org.btuk.network.lib.dto.DirectMessage; +import org.btuk.network.lib.dto.FocusEvent; +import org.btuk.network.lib.dto.ModerationEvent; +import org.btuk.network.lib.dto.MuteEvent; +import org.btuk.network.lib.dto.OnlineUser; +import org.btuk.network.lib.dto.OnlineUserAdd; +import org.btuk.network.lib.dto.OnlineUserRemove; +import org.btuk.network.lib.dto.PlotMessage; +import org.btuk.network.lib.dto.SwitchServerEvent; +import org.btuk.network.lib.dto.TeleportEvent; +import org.btuk.network.lib.dto.UserConnectReply; +import org.btuk.network.lib.dto.UserConnectRequest; +import org.btuk.network.lib.dto.UserDisconnect; +import org.btuk.network.lib.dto.UserRemove; +import org.btuk.network.lib.dto.UserUpdate; +import org.btuk.network.lib.enums.ChatChannels; +import org.btuk.network.lib.enums.ModerationAction; +import org.btuk.network.lib.enums.TeleportRequestType; +import org.btuk.network.lib.utils.ChatUtils; import org.btuk.proxy.core.chat.ChatHandler; import org.btuk.proxy.core.chat.ChatManager; +import org.btuk.proxy.core.chat.automod.AutoMod; +import org.btuk.proxy.core.discord.Discord; import org.btuk.proxy.core.exceptions.ErrorMessage; import org.btuk.proxy.core.exceptions.ServerNotFoundException; import org.btuk.proxy.core.player.PlayerManager; import org.btuk.proxy.core.scheduler.Scheduler; import org.btuk.proxy.core.server.CoreServerManager; +import org.btuk.proxy.core.tab.TabManager; import org.btuk.proxy.core.utils.Analytics; import org.btuk.proxy.core.utils.SwitchServer; import org.btuk.proxy.core.utils.Time; +import org.btuk.proxy.database.sql.GlobalSQL; +import org.btuk.proxy.database.sql.PlotSQL; +import org.btuk.proxy.database.sql.RegionSQL; + +import java.awt.Color; +import java.util.Optional; +import java.util.concurrent.TimeUnit; -import static net.bteuk.network.lib.enums.ChatChannels.GLOBAL; -import static org.btuk.proxy.core.utils.Constants.*; +import static org.btuk.network.lib.enums.ChatChannels.GLOBAL; +import static org.btuk.proxy.core.utils.Constants.JOIN_MESSAGE; +import static org.btuk.proxy.core.utils.Constants.LEAVE_MESSAGE; +import static org.btuk.proxy.core.utils.Constants.RECONNECT_MESSAGE; +import static org.btuk.proxy.core.utils.Constants.SERVER_SENDER; +import static org.btuk.proxy.core.utils.Constants.WELCOME_MESSAGE; /** * Class to manage the users on the network. diff --git a/database/pom.xml b/database/pom.xml index 7ef8e1d..19ec433 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -13,7 +13,7 @@ - com.github.BTEUK + com.github.BuildtheUK NetworkLib diff --git a/database/src/main/java/org/btuk/proxy/database/sql/PlotSQL.java b/database/src/main/java/org/btuk/proxy/database/sql/PlotSQL.java index 2274dbd..a277f92 100644 --- a/database/src/main/java/org/btuk/proxy/database/sql/PlotSQL.java +++ b/database/src/main/java/org/btuk/proxy/database/sql/PlotSQL.java @@ -1,8 +1,8 @@ package org.btuk.proxy.database.sql; import lombok.extern.java.Log; -import net.bteuk.network.lib.enums.PlotDifficulties; -import net.bteuk.network.lib.utils.Reviewing; +import org.btuk.network.lib.enums.PlotDifficulties; +import org.btuk.network.lib.utils.Reviewing; import org.btuk.proxy.database.sql.migration.AcceptData; import org.btuk.proxy.database.sql.migration.DenyData; import org.btuk.proxy.database.sql.migration.PlotSubmissions; diff --git a/plugin/pom.xml b/plugin/pom.xml index b76825c..54c1c54 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -57,7 +57,7 @@ - com.github.BTEUK:NetworkLib + com.github.BuildtheUK:NetworkLib com/fasterxml/** com/google/gson/** diff --git a/plugin/src/main/java/org/btuk/proxy/Proxy.java b/plugin/src/main/java/org/btuk/proxy/Proxy.java index 2250eac..328fdd5 100644 --- a/plugin/src/main/java/org/btuk/proxy/Proxy.java +++ b/plugin/src/main/java/org/btuk/proxy/Proxy.java @@ -10,21 +10,17 @@ import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.server.RegisteredServer; import lombok.Getter; -import net.bteuk.network.lib.socket.InputSocket; - +import org.btuk.network.lib.socket.InputSocket; import org.btuk.proxy.chat.ProxyChatHandler; - +import org.btuk.proxy.core.socket.ProxySocketHandler; import org.btuk.proxy.listener.CommandListener; import org.btuk.proxy.listener.ServerConnectListener; import org.btuk.proxy.player.ProxyPlayerManager; import org.btuk.proxy.scheduler.ProxyScheduler; import org.btuk.proxy.server.ProxyCoreServerManager; -import org.btuk.proxy.core.socket.ProxySocketHandler; - import org.btuk.proxy.tab.ProxyTabManager; import org.slf4j.Logger; - import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; diff --git a/plugin/src/main/java/org/btuk/proxy/ProxyController.java b/plugin/src/main/java/org/btuk/proxy/ProxyController.java index 698fe0e..56d5aaf 100644 --- a/plugin/src/main/java/org/btuk/proxy/ProxyController.java +++ b/plugin/src/main/java/org/btuk/proxy/ProxyController.java @@ -2,28 +2,11 @@ import lombok.Getter; import lombok.extern.java.Log; -import net.bteuk.network.lib.dto.OnlineUserRemove; - -import org.btuk.proxy.core.chat.automod.AutoMod; -import org.btuk.proxy.database.DatabaseInit; -import org.btuk.proxy.database.sql.GlobalSQL; -import org.btuk.proxy.database.sql.PlotSQL; -import org.btuk.proxy.database.sql.RegionSQL; - -import org.slf4j.bridge.SLF4JBridgeHandler; - -import javax.sql.DataSource; -import java.io.File; -import java.io.IOException; -import java.sql.SQLException; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; - +import org.btuk.network.lib.dto.OnlineUserRemove; import org.btuk.proxy.api.server.ProxyApi; import org.btuk.proxy.core.chat.ChatHandler; import org.btuk.proxy.core.chat.ChatManager; +import org.btuk.proxy.core.chat.automod.AutoMod; import org.btuk.proxy.core.config.Config; import org.btuk.proxy.core.discord.Discord; import org.btuk.proxy.core.discord.ReviewStatus; @@ -38,6 +21,20 @@ import org.btuk.proxy.core.utils.Analytics; import org.btuk.proxy.core.utils.Constants; import org.btuk.proxy.core.utils.Moderation; +import org.btuk.proxy.database.DatabaseInit; +import org.btuk.proxy.database.sql.GlobalSQL; +import org.btuk.proxy.database.sql.PlotSQL; +import org.btuk.proxy.database.sql.RegionSQL; +import org.slf4j.bridge.SLF4JBridgeHandler; + +import javax.sql.DataSource; +import java.io.File; +import java.io.IOException; +import java.sql.SQLException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import static java.awt.Color.RED; import static org.btuk.proxy.core.utils.Constants.LEAVE_MESSAGE; diff --git a/plugin/src/main/java/org/btuk/proxy/chat/ProxyChatHandler.java b/plugin/src/main/java/org/btuk/proxy/chat/ProxyChatHandler.java index 38baaf6..25e6f27 100644 --- a/plugin/src/main/java/org/btuk/proxy/chat/ProxyChatHandler.java +++ b/plugin/src/main/java/org/btuk/proxy/chat/ProxyChatHandler.java @@ -1,21 +1,20 @@ package org.btuk.proxy.chat; import lombok.extern.java.Log; -import net.bteuk.network.lib.dto.AbstractTransferObject; -import net.bteuk.network.lib.socket.OutputSocket; +import org.btuk.network.lib.dto.AbstractTransferObject; +import org.btuk.network.lib.socket.OutputSocket; +import org.btuk.proxy.core.chat.ChatHandler; +import org.btuk.proxy.core.config.Config; +import org.btuk.proxy.core.config.ConfigSocket; import org.btuk.proxy.core.exceptions.ServerNotFoundException; +import org.btuk.proxy.core.server.CoreServerManager; +import org.btuk.proxy.core.server.Server; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import org.btuk.proxy.core.chat.ChatHandler; -import org.btuk.proxy.core.config.Config; -import org.btuk.proxy.core.config.ConfigSocket; -import org.btuk.proxy.core.server.Server; -import org.btuk.proxy.core.server.CoreServerManager; - @Log public class ProxyChatHandler implements ChatHandler { diff --git a/plugin/src/main/java/org/btuk/proxy/tab/ProxyTabManager.java b/plugin/src/main/java/org/btuk/proxy/tab/ProxyTabManager.java index 6534f61..e8c03d7 100644 --- a/plugin/src/main/java/org/btuk/proxy/tab/ProxyTabManager.java +++ b/plugin/src/main/java/org/btuk/proxy/tab/ProxyTabManager.java @@ -3,13 +3,16 @@ import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.player.TabListEntry; import com.velocitypowered.api.util.GameProfile; -import net.bteuk.network.lib.dto.TabPlayer; - +import net.kyori.adventure.text.Component; +import org.btuk.network.lib.dto.TabPlayer; +import org.btuk.proxy.core.chat.ChatHandler; +import org.btuk.proxy.core.config.Config; +import org.btuk.proxy.core.player.Player; import org.btuk.proxy.core.scheduler.Scheduler; import org.btuk.proxy.core.tab.AbstractTabManager; +import org.btuk.proxy.core.user.CoreUserManager; +import org.btuk.proxy.core.user.User; import org.btuk.proxy.player.ProxyPlayer; - -import net.kyori.adventure.text.Component; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -17,12 +20,6 @@ import java.util.List; import java.util.Optional; -import org.btuk.proxy.core.chat.ChatHandler; -import org.btuk.proxy.core.config.Config; -import org.btuk.proxy.core.player.Player; -import org.btuk.proxy.core.user.CoreUserManager; -import org.btuk.proxy.core.user.User; - /** * Keeps track of all users and their tab information. * Sends updates to the servers when things change. diff --git a/pom.xml b/pom.xml index dff10c7..8ecfa3a 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ 3.11.0 3.6.1 - 955372d721 + 48f9c36fa5 3.5.0-SNAPSHOT @@ -86,7 +86,7 @@ - com.github.BTEUK + com.github.BuildtheUK NetworkLib ${networklib.version} From 9dac81b5ba2289536228b83a2c2f35151a39f5ce Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sun, 2 Aug 2026 12:00:12 +0200 Subject: [PATCH 19/38] Revert move of ProxyController to plugin module. --- .../org/btuk/proxy/api/impl/BuildingsApiImpl.java | 3 --- .../java/org/btuk/proxy/api/impl/PlayerApiImpl.java | 2 +- .../java/org/btuk/proxy/core}/ProxyController.java | 13 +++++++------ plugin/src/main/java/org/btuk/proxy/Proxy.java | 4 ++-- 4 files changed, 10 insertions(+), 12 deletions(-) rename {plugin/src/main/java/org/btuk/proxy => core/src/main/java/org/btuk/proxy/core}/ProxyController.java (96%) diff --git a/api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java b/api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java index df80fea..b07a4aa 100644 --- a/api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java +++ b/api/src/main/java/org/btuk/proxy/api/impl/BuildingsApiImpl.java @@ -25,9 +25,6 @@ public BuildingsApiImpl(GlobalSQL globalSQL) { this.globalSQL = globalSQL; } - - - @Override public Response getBuildingsByArea(Double minLat, Double maxLat, Double minLon, Double maxLon, UUID playerUuid) { String playerUuidStr = playerUuid != null ? playerUuid.toString() : null; diff --git a/api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java b/api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java index 99cb401..e2ecb8b 100644 --- a/api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java +++ b/api/src/main/java/org/btuk/proxy/api/impl/PlayerApiImpl.java @@ -63,7 +63,7 @@ public Response sendPlayerMessage(String playerID, Message message){ try { String messagePlainText = message.getMessage(); - DirectMessage m = new DirectMessage(GLOBAL.getChannelName(), playerID.toString(), SERVER_SENDER, ChatUtils.success(messagePlainText), true); + DirectMessage m = new DirectMessage(GLOBAL.getChannelName(), playerID, SERVER_SENDER, ChatUtils.success(messagePlainText), true); chatManager.sendDirectMessage(m); return Response.status(Response.Status.CREATED).build(); }catch (Exception e) diff --git a/plugin/src/main/java/org/btuk/proxy/ProxyController.java b/core/src/main/java/org/btuk/proxy/core/ProxyController.java similarity index 96% rename from plugin/src/main/java/org/btuk/proxy/ProxyController.java rename to core/src/main/java/org/btuk/proxy/core/ProxyController.java index 56d5aaf..8d0054f 100644 --- a/plugin/src/main/java/org/btuk/proxy/ProxyController.java +++ b/core/src/main/java/org/btuk/proxy/core/ProxyController.java @@ -1,4 +1,4 @@ -package org.btuk.proxy; +package org.btuk.proxy.core; import lombok.Getter; import lombok.extern.java.Log; @@ -99,8 +99,8 @@ public ProxyController(File dataFolder) { this.enabled = true; } - - public void start(ChatHandler chatHandler, Scheduler scheduler, CoreServerManager coreServerManager, PlayerManager playerManager, TabManager tabManager, Consumer socketInitializer) throws IOException { + public void start(ChatHandler chatHandler, Scheduler scheduler, CoreServerManager coreServerManager, PlayerManager playerManager, TabManager tabManager, + Consumer socketInitializer) throws IOException { if (!enabled) { log.severe("Proxy is not enabled, see previous logs for errors."); @@ -118,7 +118,8 @@ public void start(ChatHandler chatHandler, Scheduler scheduler, CoreServerManage AutoMod automod = new AutoMod(coreUserManager, new Config(dataFolder, AUTOMOD_CONFIG_NAME), moderation, discord, chatHandler, tabManager); ChatManager chatManager = new ChatManager(chatHandler, coreUserManager, analytics, globalSQL, moderation, automod); - this.userManager = new UserManager(coreUserManager, chatHandler, tabManager, globalSQL, plotSQL, regionSQL, coreServerManager, scheduler, chatManager, playerManager, analytics, discord, automod); + this.userManager = new UserManager(coreUserManager, chatHandler, tabManager, globalSQL, plotSQL, regionSQL, coreServerManager, scheduler, chatManager, playerManager, + analytics, discord, automod); ServerManager serverManager = new ServerManager(coreServerManager, scheduler, globalSQL, chatHandler, tabManager, coreUserManager, userManager); @@ -126,7 +127,7 @@ public void start(ChatHandler chatHandler, Scheduler scheduler, CoreServerManage new ReviewStatus(config, globalSQL, plotSQL, regionSQL, discord, scheduler); this.discord.addJDAEventListeners(chatManager, coreUserManager, tabManager, plotSQL); - this.proxyApi = new ProxyApi(config.getBoolean("api.enabled"), config.getInt("api.port"), globalSQL,chatManager); + this.proxyApi = new ProxyApi(config.getBoolean("api.enabled"), config.getInt("api.port"), globalSQL, chatManager); serverManager.initOnlineServers(); socketInitializer.accept(new ProxySocketHandler(chatManager, discord, userManager, serverManager, tabManager)); @@ -169,7 +170,7 @@ public void stop() { // Clear JDA listeners if (discord.getJda() != null) { - //Unregister listeners. + // Unregister listeners. discord.getJda().getEventManager().getRegisteredListeners().forEach(listener -> discord.getJda().getEventManager().unregister(listener)); } diff --git a/plugin/src/main/java/org/btuk/proxy/Proxy.java b/plugin/src/main/java/org/btuk/proxy/Proxy.java index 328fdd5..7702b86 100644 --- a/plugin/src/main/java/org/btuk/proxy/Proxy.java +++ b/plugin/src/main/java/org/btuk/proxy/Proxy.java @@ -51,7 +51,7 @@ public class Proxy { private HashMap lastServer; - private ProxyController proxyController; + private org.btuk.proxy.core.ProxyController proxyController; private String defaultServer; @@ -66,7 +66,7 @@ public Proxy(ProxyServer server, Logger logger) { @Subscribe public void onProxyInitialization(ProxyInitializeEvent event) throws IOException { - this.proxyController = new ProxyController(getDataFolder()); + this.proxyController = new org.btuk.proxy.core.ProxyController(getDataFolder()); this.defaultServer = proxyController.getConfig().getString("default_server"); From adf2d9eb78fd20917974f5619184819a7efffd33 Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sun, 2 Aug 2026 12:35:03 +0200 Subject: [PATCH 20/38] Added app module for the ProxyController to separate concerns. --- app/pom.xml | 106 ++++++++++++++++++ .../org.btuk.proxy.app}/ProxyController.java | 5 +- plugin/pom.xml | 4 + .../src/main/java/org/btuk/proxy/Proxy.java | 5 +- pom.xml | 12 +- 5 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 app/pom.xml rename {core/src/main/java/org/btuk/proxy/core => app/src/main/java/org.btuk.proxy.app}/ProxyController.java (97%) diff --git a/app/pom.xml b/app/pom.xml new file mode 100644 index 0000000..7109460 --- /dev/null +++ b/app/pom.xml @@ -0,0 +1,106 @@ + + + 4.0.0 + + org.btuk + Proxy + 1.13.0-SNAPSHOT + + + org.btuk.proxy + app + App + + + 21 + 21 + UTF-8 + + + + + org.btuk.proxy + api + + + org.btuk.proxy + core + + + org.btuk.proxy + database + + + + com.github.BuildtheUK + NetworkLib + + + + org.projectlombok + lombok + + + + org.apache.logging.log4j + log4j-core + provided + + + org.apache.logging.log4j + log4j-slf4j2-impl + provided + + + org.slf4j + jul-to-slf4j + + + + net.dv8tion + JDA + + + + net.kyori + adventure-text-serializer-plain + 4.16.0 + + + net.kyori + adventure-text-minimessage + 4.16.0 + + + + org.yaml + snakeyaml + + + + org.apache.commons + commons-lang3 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + + org.projectlombok + lombok + ${lombok.version} + + + + + + + \ No newline at end of file diff --git a/core/src/main/java/org/btuk/proxy/core/ProxyController.java b/app/src/main/java/org.btuk.proxy.app/ProxyController.java similarity index 97% rename from core/src/main/java/org/btuk/proxy/core/ProxyController.java rename to app/src/main/java/org.btuk.proxy.app/ProxyController.java index 8d0054f..c831741 100644 --- a/core/src/main/java/org/btuk/proxy/core/ProxyController.java +++ b/app/src/main/java/org.btuk.proxy.app/ProxyController.java @@ -1,4 +1,4 @@ -package org.btuk.proxy.core; +package org.btuk.proxy.app; import lombok.Getter; import lombok.extern.java.Log; @@ -37,7 +37,6 @@ import java.util.function.Consumer; import static java.awt.Color.RED; -import static org.btuk.proxy.core.utils.Constants.LEAVE_MESSAGE; /** * Controller of the core proxy functionality; can be enabled by the proxy or external plugins that want to use the proxy functions. @@ -149,7 +148,7 @@ public void stop() { coreUserManager.runForEachOnline(user -> { if (user.isOnline()) { - discord.sendConnectEmbed(LEAVE_MESSAGE, user.getName(), user.getUuid(), user.getPlayerSkin(), RED, (reply) -> { + discord.sendConnectEmbed(Constants.LEAVE_MESSAGE, user.getName(), user.getUuid(), user.getPlayerSkin(), RED, (reply) -> { users.decrementAndGet(); disconnectLatch.countDown(); }); diff --git a/plugin/pom.xml b/plugin/pom.xml index 54c1c54..350ad36 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -105,6 +105,10 @@ + + org.btuk.proxy + app + org.btuk.proxy core diff --git a/plugin/src/main/java/org/btuk/proxy/Proxy.java b/plugin/src/main/java/org/btuk/proxy/Proxy.java index 7702b86..d6093cc 100644 --- a/plugin/src/main/java/org/btuk/proxy/Proxy.java +++ b/plugin/src/main/java/org/btuk/proxy/Proxy.java @@ -11,6 +11,7 @@ import com.velocitypowered.api.proxy.server.RegisteredServer; import lombok.Getter; import org.btuk.network.lib.socket.InputSocket; +import org.btuk.proxy.app.ProxyController; import org.btuk.proxy.chat.ProxyChatHandler; import org.btuk.proxy.core.socket.ProxySocketHandler; import org.btuk.proxy.listener.CommandListener; @@ -51,7 +52,7 @@ public class Proxy { private HashMap lastServer; - private org.btuk.proxy.core.ProxyController proxyController; + private ProxyController proxyController; private String defaultServer; @@ -66,7 +67,7 @@ public Proxy(ProxyServer server, Logger logger) { @Subscribe public void onProxyInitialization(ProxyInitializeEvent event) throws IOException { - this.proxyController = new org.btuk.proxy.core.ProxyController(getDataFolder()); + this.proxyController = new ProxyController(getDataFolder()); this.defaultServer = proxyController.getConfig().getString("default_server"); diff --git a/pom.xml b/pom.xml index 8ecfa3a..ccba6df 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ api core plugin + app @@ -71,17 +72,22 @@ org.btuk.proxy - database + api ${project.version} org.btuk.proxy - api + app ${project.version} + + org.btuk.proxy + core + ${project.version} + org.btuk.proxy - core + database ${project.version} From 090192b1c2bb43e08a75d7d2508b607992532bc9 Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sun, 2 Aug 2026 12:35:53 +0200 Subject: [PATCH 21/38] Add app module to jitpack config. --- jitpack.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jitpack.yml b/jitpack.yml index 13ceb6c..f4386f4 100644 --- a/jitpack.yml +++ b/jitpack.yml @@ -1,4 +1,4 @@ jdk: - openjdk21 install: - - mvn install -Dmaven.javadoc.skip=true -DskipTests -pl :database,:core -am \ No newline at end of file + - mvn install -Dmaven.javadoc.skip=true -DskipTests -pl :database,:core,:app -am \ No newline at end of file From 7f3947a1106bf2450c77c9ab54b21fbd4a505485 Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sun, 2 Aug 2026 12:41:31 +0200 Subject: [PATCH 22/38] Downgrade plugin version to work with jitpack default maven version. --- api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/pom.xml b/api/pom.xml index 41f94d2..c94c997 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -16,7 +16,7 @@ UTF-8 7.4.0 - 3.6.0 + 3.5.0 3.1.0 3.1.5 4.0.2 From 875a7d5160ef19c423dd08cc2f116d020a81d0cd Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sun, 2 Aug 2026 13:04:20 +0200 Subject: [PATCH 23/38] Add maven wrapper to ensure correct maven version. --- .mvn/wrapper/maven-wrapper.properties | 3 +++ api/pom.xml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 .mvn/wrapper/maven-wrapper.properties diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..7c6b218 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,3 @@ +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip diff --git a/api/pom.xml b/api/pom.xml index c94c997..41f94d2 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -16,7 +16,7 @@ UTF-8 7.4.0 - 3.5.0 + 3.6.0 3.1.0 3.1.5 4.0.2 From c9f66b7ec5c9b3732a5a169b5f6e66ac8b767577 Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sun, 2 Aug 2026 13:05:05 +0200 Subject: [PATCH 24/38] Use maven wrapper in jitpack. --- jitpack.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jitpack.yml b/jitpack.yml index f4386f4..18cdc8e 100644 --- a/jitpack.yml +++ b/jitpack.yml @@ -1,4 +1,4 @@ jdk: - openjdk21 install: - - mvn install -Dmaven.javadoc.skip=true -DskipTests -pl :database,:core,:app -am \ No newline at end of file + - ./mvnw install -Dmaven.javadoc.skip=true -DskipTests -pl :database,:core,:app -am \ No newline at end of file From a938d2ddad60946f0f93313abec33e3a33c9e98c Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sun, 2 Aug 2026 13:06:35 +0200 Subject: [PATCH 25/38] Add mvnw --- mvnw | 295 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvnw.cmd | 189 +++++++++++++++++++++++++++++++++++ 2 files changed, 484 insertions(+) create mode 100644 mvnw create mode 100644 mvnw.cmd diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..bd8896b --- /dev/null +++ b/mvnw @@ -0,0 +1,295 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.4 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" + +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" + fi +fi + +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi + fi + done + set -f +fi + +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" +fi + +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..92450f9 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,189 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.4 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' + +$MAVEN_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" From d80e385b58211d61020674232540c1acd5e4c393 Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sun, 2 Aug 2026 13:08:35 +0200 Subject: [PATCH 26/38] Fix mvnw permission --- mvnw | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 mvnw diff --git a/mvnw b/mvnw old mode 100644 new mode 100755 From 1e5b50c6a0855cc308fecc5428175cf337e0bfa2 Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sat, 15 Aug 2026 13:55:48 +0200 Subject: [PATCH 27/38] Update server pinging to retry for 30 seconds. --- api/pom.xml | 10 ---- app/pom.xml | 10 ---- core/pom.xml | 10 ---- .../btuk/proxy/core/server/ServerManager.java | 52 +++++++++++-------- database/pom.xml | 10 ---- plugin/pom.xml | 4 +- .../src/main/java/org/btuk/proxy/Proxy.java | 1 - pom.xml | 41 +++++++++++---- 8 files changed, 63 insertions(+), 75 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 41f94d2..c314078 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -122,16 +122,6 @@ org.apache.maven.plugins maven-compiler-plugin - ${maven-compiler-plugin.version} - - - - org.projectlombok - lombok - ${lombok.version} - - - diff --git a/app/pom.xml b/app/pom.xml index 7109460..b24d3cf 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -90,16 +90,6 @@ org.apache.maven.plugins maven-compiler-plugin - ${maven-compiler-plugin.version} - - - - org.projectlombok - lombok - ${lombok.version} - - - diff --git a/core/pom.xml b/core/pom.xml index 46298d2..23aa6a5 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -82,16 +82,6 @@ org.apache.maven.plugins maven-compiler-plugin - ${maven-compiler-plugin.version} - - - - org.projectlombok - lombok - ${lombok.version} - - - diff --git a/core/src/main/java/org/btuk/proxy/core/server/ServerManager.java b/core/src/main/java/org/btuk/proxy/core/server/ServerManager.java index b2290cd..509f89f 100644 --- a/core/src/main/java/org/btuk/proxy/core/server/ServerManager.java +++ b/core/src/main/java/org/btuk/proxy/core/server/ServerManager.java @@ -15,7 +15,6 @@ import org.btuk.proxy.database.sql.GlobalSQL; import java.util.List; -import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -24,7 +23,7 @@ public class ServerManager { private final CoreServerManager coreServerManager; - + private final Scheduler scheduler; private final GlobalSQL globalSQL; private final ChatHandler chatHandler; @@ -39,6 +38,7 @@ public class ServerManager { public ServerManager(CoreServerManager coreServerManager, Scheduler scheduler, GlobalSQL globalSQL, ChatHandler chatHandler, TabManager tabManager, CoreUserManager coreUserManager, UserManager userManager) { this.coreServerManager = coreServerManager; + this.scheduler = scheduler; this.globalSQL = globalSQL; this.chatHandler = chatHandler; this.tabManager = tabManager; @@ -57,30 +57,30 @@ public void initOnlineServers() { } public void addServer(ServerStartup serverStartup) { - // It is possible the server is already set to online, this probably means it crashed, - // first clear all players that are 'connected' to this server and remove them. - Optional optionalServer = coreServerManager.getServers().stream().filter(server -> server.getName().equals(serverStartup.getServerName())).findFirst(); - optionalServer.ifPresent(this::removeServerDueToTimeout); + String serverName = serverStartup.getServerName(); + // It is possible the server was already set to online, this probably means it crashed. + // Even if the server is not currently tracked, there might be 'ghost' players. + cleanupServer(serverName); try { - Server server = coreServerManager.createServer(serverStartup.getServerName()); + Server server = coreServerManager.createServer(serverName); threadExecutor.submit(() -> addServerIfOnline(server)); } catch (RuntimeException e) { - log.warning("Unable to add server " + serverStartup.getServerName() + ", it can not be found."); + log.warning("Unable to add server " + serverName + ", it can not be found."); } } public void removeServer(ServerShutdown serverShutdown) { - Optional optionalServer = coreServerManager.getServers().stream().filter(server -> server.getName().equals(serverShutdown.getServerName())).findFirst(); - optionalServer.ifPresent(coreServerManager::removeServer); - - // Set the server offline in the database. - globalSQL.update("UPDATE server_data SET online=0 WHERE name='" + serverShutdown.getServerName() + "';"); + cleanupServer(serverShutdown.getServerName()); } private void addServerIfOnline(Server server) { + addServerIfOnline(server, 0); + } + + private void addServerIfOnline(Server server, int attempt) { // Skip if the server is already added. - if (coreServerManager.getServers().stream().anyMatch(server::equals)) { + if (coreServerManager.getServers().stream().anyMatch(s -> s.getName().equals(server.getName()))) { return; } if (server.canPing()) { @@ -94,25 +94,29 @@ private void addServerIfOnline(Server server) { tabManager.sendAddTeam(); } else { // The server is not online. - log.warning(String.format("Server " + server.getName() + " is not online.")); + if (attempt < 30) { + scheduler.createDelayedTask(() -> threadExecutor.submit(() -> addServerIfOnline(server, attempt + 1)), 1, TimeUnit.SECONDS); + } else { + log.warning(String.format("Server " + server.getName() + " is not online.")); + } } } private void pingServers() { coreServerManager.getServers().forEach(server -> threadExecutor.submit(() -> updatePing(server))); - // If any server has a ping of more than 120 seconds, set the server to offline and remove all online players that were connected to the server. + // If any server has a ping of more than 60 seconds, set the server to offline and remove all online players that were connected to the server. // This probably means the server crashed. - List offlineServers = coreServerManager.getServers().stream().filter(server -> server.getLastPing() < Time.currentTime() - 1000 * 120).toList(); + List offlineServers = coreServerManager.getServers().stream().filter(server -> server.getLastPing() < Time.currentTime() - 1000 * 60).toList(); offlineServers.forEach(this::removeServerDueToTimeout); } - private void removeServerDueToTimeout(Server server) { + private void cleanupServer(String serverName) { // Set the server offline in the database. - globalSQL.update("UPDATE server_data SET online=0 WHERE name='" + server.getName() + "';"); + globalSQL.update("UPDATE server_data SET online=0 WHERE name='" + serverName + "';"); // Remove all users connected to this server, // and also send a message to all other online servers to remove these users from their list. - List offlineServerUsers = coreUserManager.getUsersOnServer(server.getName()); + List offlineServerUsers = coreUserManager.getUsersOnServer(serverName); offlineServerUsers.forEach(user -> { OnlineUserRemove onlineUserRemove = new OnlineUserRemove(user.getUuid()); chatHandler.handle(onlineUserRemove); @@ -120,8 +124,12 @@ private void removeServerDueToTimeout(Server server) { } ); - // Remove server from the list. - coreServerManager.removeServer(server); + // Remove server from the list if it exists. + coreServerManager.getServer(serverName).ifPresent(coreServerManager::removeServer); + } + + private void removeServerDueToTimeout(Server server) { + cleanupServer(server.getName()); } private void updatePing(Server server) { diff --git a/database/pom.xml b/database/pom.xml index 19ec433..e73de01 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -42,16 +42,6 @@ org.apache.maven.plugins maven-compiler-plugin - ${maven-compiler-plugin.version} - - - - org.projectlombok - lombok - ${lombok.version} - - - diff --git a/plugin/pom.xml b/plugin/pom.xml index 350ad36..03e19e7 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -70,7 +70,8 @@ - + @@ -79,7 +80,6 @@ org.apache.maven.plugins maven-compiler-plugin - ${maven-compiler-plugin.version} diff --git a/plugin/src/main/java/org/btuk/proxy/Proxy.java b/plugin/src/main/java/org/btuk/proxy/Proxy.java index d6093cc..c3852f0 100644 --- a/plugin/src/main/java/org/btuk/proxy/Proxy.java +++ b/plugin/src/main/java/org/btuk/proxy/Proxy.java @@ -66,7 +66,6 @@ public Proxy(ProxyServer server, Logger logger) { @Subscribe public void onProxyInitialization(ProxyInitializeEvent event) throws IOException { - this.proxyController = new ProxyController(getDataFolder()); this.defaultServer = proxyController.getConfig().getString("default_server"); diff --git a/pom.xml b/pom.xml index ccba6df..813c04a 100644 --- a/pom.xml +++ b/pom.xml @@ -19,18 +19,18 @@ - 21 + 25 UTF-8 ${java-version} - 3.11.0 - 3.6.1 + 3.15.0 + 3.6.2 - 48f9c36fa5 + 5dee061bcb - 3.5.0-SNAPSHOT + 4.1.0-SNAPSHOT - 1.18.34 + 1.18.46 2.25.2 2.0.17 @@ -81,10 +81,10 @@ ${project.version} - org.btuk.proxy - core - ${project.version} - + org.btuk.proxy + core + ${project.version} + org.btuk.proxy database @@ -166,4 +166,25 @@ + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + + org.projectlombok + lombok + ${lombok.version} + + + + + + + \ No newline at end of file From 90ff77bf64de7cbcd7ffa8e219bc87f271daa2f2 Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sat, 15 Aug 2026 17:28:18 +0200 Subject: [PATCH 28/38] Add the /map command back to discord for displaying the progress map. Also add it as /progress and /progressmap. --- .gitignore | 115 ++++++++++++++++++ .../org/btuk/proxy/core/discord/Discord.java | 8 +- .../core/discord/command/CommandManager.java | 23 ++-- .../btuk/proxy/core/discord/command/Map.java | 56 ++++----- core/src/main/resources/proxy-config.yml | 4 +- 5 files changed, 164 insertions(+), 42 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c65bcf4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,115 @@ +# User-specific stuff +.idea/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +target/ + +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next + +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar +.flattened-pom.xml + +# Common working directory +run/ + +.junie diff --git a/core/src/main/java/org/btuk/proxy/core/discord/Discord.java b/core/src/main/java/org/btuk/proxy/core/discord/Discord.java index 8873692..afcda52 100644 --- a/core/src/main/java/org/btuk/proxy/core/discord/Discord.java +++ b/core/src/main/java/org/btuk/proxy/core/discord/Discord.java @@ -119,7 +119,7 @@ public Discord(Config config, GlobalSQL globalSQL, ChatHandler chatHandler, Sche this.moderatorChat = jda.getTextChannelById(moderatorChannel); //Load all members into cache. - chat.getGuild().loadMembers().onSuccess(members -> { + chat.getGuild().loadMembers().onSuccess(_ -> { log.info("Loaded all discord members into cache"); //Enable role syncing. @@ -135,7 +135,7 @@ public void addJDAEventListeners(ChatManager chatManager, CoreUserManager coreUs jda.addEventListener(new DiscordChatListener(this, chatManager, chatChannelId, staffChannelId)); jda.addEventListener(new BotChatListener(chatHandler, linking)); - CommandManager commandManager = new CommandManager(coreUserManager, tabManager, globalSQL, plotSQL); + CommandManager commandManager = new CommandManager(coreUserManager, tabManager, globalSQL, plotSQL, config); jda.addEventListener(commandManager); jda.getGuilds().forEach(commandManager::registerCommands); } @@ -341,7 +341,7 @@ public void addRole(long userId, long role_id, boolean sync) { if (!member.getRoles().contains(role)) { // If successful, resync if enabled. chat.getGuild().addRoleToMember(member, role).queue( - (user) -> { + _ -> { if (sync && hasRoles != null && giveRoles != null) { syncRoles(); } @@ -371,7 +371,7 @@ public void removeRole(long userId, long role_id, boolean sync) { // If the member does not have the role, add it. if (member.getRoles().contains(role)) { chat.getGuild().removeRoleFromMember(member, role).queue( - (user) -> { + _ -> { if (sync && hasRoles != null && giveRoles != null) { syncRoles(); } diff --git a/core/src/main/java/org/btuk/proxy/core/discord/command/CommandManager.java b/core/src/main/java/org/btuk/proxy/core/discord/command/CommandManager.java index 54c1fb5..c08c898 100644 --- a/core/src/main/java/org/btuk/proxy/core/discord/command/CommandManager.java +++ b/core/src/main/java/org/btuk/proxy/core/discord/command/CommandManager.java @@ -1,8 +1,5 @@ package org.btuk.proxy.core.discord.command; -import org.btuk.proxy.database.sql.GlobalSQL; -import org.btuk.proxy.database.sql.PlotSQL; - import lombok.extern.java.Log; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.events.guild.GuildJoinEvent; @@ -14,14 +11,16 @@ import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.OptionData; +import org.btuk.proxy.core.config.Config; +import org.btuk.proxy.core.tab.TabManager; +import org.btuk.proxy.core.user.CoreUserManager; +import org.btuk.proxy.database.sql.GlobalSQL; +import org.btuk.proxy.database.sql.PlotSQL; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; -import org.btuk.proxy.core.tab.TabManager; -import org.btuk.proxy.core.user.CoreUserManager; - /** * Manages all Discord commands. * Each command is stored in a map so when a command interaction is run it can be routed accordingly. @@ -39,11 +38,14 @@ public class CommandManager extends ListenerAdapter { private final PlotSQL plotSQL; - public CommandManager(CoreUserManager userManager, TabManager tabManager, GlobalSQL globalSQL, PlotSQL plotSQL) { + private final Config config; + + public CommandManager(CoreUserManager userManager, TabManager tabManager, GlobalSQL globalSQL, PlotSQL plotSQL, Config config) { this.userManager = userManager; this.tabManager = tabManager; this.globalSQL = globalSQL; this.plotSQL = plotSQL; + this.config = config; commands = new ArrayList<>(); } @@ -97,7 +99,12 @@ public void registerCommands(Guild guild) { //Create commands. commands.add(new Playerlist(userManager, tabManager, "playerlist", "List all online players on the Minecraft server.")); - //commands.add(new Map("map", "Sends a link to the UK progress map.")); // The progress map is no longer available. + String progressMap = config.getString("progress_map"); + if (progressMap != null && !progressMap.isBlank()) { + commands.add(new Map("map", "Sends a link to the UK progress map.", progressMap)); + commands.add(new Map("progress", "Sends a link to the UK progress map.", progressMap)); + commands.add(new Map("progressmap", "Sends a link to the UK progress map.", progressMap)); + } commands.add(new ClaimedPlots(globalSQL, plotSQL, "claimedplots", "List all plots that are currently claimed.", playerOption)); commands.add(new SubmittedPlots(globalSQL, plotSQL, "submittedplots", "List all plots that are currently submitted.", playerOption)); diff --git a/core/src/main/java/org/btuk/proxy/core/discord/command/Map.java b/core/src/main/java/org/btuk/proxy/core/discord/command/Map.java index 9d23928..7c0d58a 100644 --- a/core/src/main/java/org/btuk/proxy/core/discord/command/Map.java +++ b/core/src/main/java/org/btuk/proxy/core/discord/command/Map.java @@ -1,29 +1,27 @@ -//package org.btuk.proxy.core.discord.command; -// -//import org.btuk.proxy.Proxy; -//import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; -//import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; -// -//public class Map extends AbstractCommand { -// -// /** -// * Constructor, saved the name and description of the command. -// * Also registers the command in Discord. -// * @param name Name of the command -// * @param description Description of the command -// */ -// public Map(String name, String description) { -// super(name, description); -// } -// -// @Override -// public void onCommand(SlashCommandInteractionEvent event) { -// -// String playerListMessage = Proxy.getInstance().getConfig().getString("progress_map"); -// -// ReplyCallbackAction reply = event.reply(playerListMessage); -// reply = reply.setEphemeral(true); -// reply.queue(); -// -// } -//} \ No newline at end of file +package org.btuk.proxy.core.discord.command; + +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; + +public class Map extends AbstractCommand { + + private final String link; + + /** + * Constructor, saved the name and description of the command. + * Also registers the command in Discord. + * @param name Name of the command + * @param description Description of the command + */ + public Map(String name, String description, String link) { + super(name, description); + this.link = link; + } + + @Override + public void onCommand(SlashCommandInteractionEvent event) { + ReplyCallbackAction reply = event.reply(link); + reply = reply.setEphemeral(true); + reply.queue(); + } +} \ No newline at end of file diff --git a/core/src/main/resources/proxy-config.yml b/core/src/main/resources/proxy-config.yml index ad24a34..6602d62 100644 --- a/core/src/main/resources/proxy-config.yml +++ b/core/src/main/resources/proxy-config.yml @@ -86,4 +86,6 @@ tab: footer: "\nServer Info: /help\nMore Info: /discord" #Discord user activity playing status (your server name or IP) -DiscordPlaying: "server.net" \ No newline at end of file +DiscordPlaying: "server.net" + +progress_map: "https://progress.buildtheuk.org" \ No newline at end of file From bedc1a27282906365a7606b61a99181490e44d98 Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sun, 16 Aug 2026 13:51:44 +0200 Subject: [PATCH 29/38] Support multi-word phrases and handle punctuation in the auto mod rule matcher. --- core/pom.xml | 5 + .../btuk/proxy/core/chat/automod/AutoMod.java | 5 +- .../proxy/core/chat/automod/AutoModRule.java | 122 ++++++++++++------ .../core/chat/automod/CandidateWord.java | 15 +-- .../core/chat/automod/AutoModRuleTest.java | 59 +++++++++ 5 files changed, 149 insertions(+), 57 deletions(-) create mode 100644 core/src/test/java/org/btuk/proxy/core/chat/automod/AutoModRuleTest.java diff --git a/core/pom.xml b/core/pom.xml index 23aa6a5..76da412 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -75,6 +75,11 @@ org.apache.commons commons-lang3 + + org.junit.jupiter + junit-jupiter + test + diff --git a/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoMod.java b/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoMod.java index e50823b..2cbd378 100644 --- a/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoMod.java +++ b/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoMod.java @@ -17,7 +17,6 @@ import java.time.Duration; import java.util.Collections; import java.util.List; -import java.util.Map; import static org.btuk.network.lib.enums.ChatChannels.GLOBAL; import static org.btuk.proxy.core.utils.Constants.SERVER_SENDER; @@ -92,7 +91,7 @@ public boolean moderate(String sender, Component messageComponent) { * @return true if the message should be blocked */ private boolean checkMessage(User user, String message) { - Map candidateWords = AutoModRule.getCandidateWords(message); + List candidateWords = AutoModRule.getCandidateWords(message); boolean blockMessage = false; for (AutoModRule rule : autoModConfig.getRules()) { blockMessage |= checkRule(rule, candidateWords, user, message); @@ -122,7 +121,7 @@ private void checkUser(User user) { } } - private boolean checkRule(AutoModRule rule, Map candidateWords, User user, String message) { + private boolean checkRule(AutoModRule rule, List candidateWords, User user, String message) { List matches = rule.getMatches(candidateWords); if (matches.isEmpty()) { return false; diff --git a/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoModRule.java b/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoModRule.java index a69fcd5..168ff13 100644 --- a/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoModRule.java +++ b/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoModRule.java @@ -4,13 +4,13 @@ import java.text.Normalizer; import java.time.Duration; -import java.util.LinkedHashMap; +import java.util.ArrayList; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; public abstract class AutoModRule { @@ -28,10 +28,20 @@ public abstract class AutoModRule { public AutoModRule(String id, List flaggedWords, Duration duration) { this.id = id; - this.flaggedWords = flaggedWords.stream() - .map(AutoModRule::normalize) - .filter(word -> !word.isBlank()) - .collect(java.util.stream.Collectors.toUnmodifiableSet()); + java.util.Set normalizedSet = new java.util.HashSet<>(); + for (String word : flaggedWords) { + String normalized = normalize(word); + List tokens = new ArrayList<>(); + Matcher matcher = TOKEN_PATTERN.matcher(normalized); + while (matcher.find()) { + tokens.add(matcher.group()); + } + if (tokens.isEmpty()) continue; + + normalizedSet.add(String.join(" ", tokens)); + normalizedSet.add(String.join("", tokens)); + } + this.flaggedWords = java.util.Collections.unmodifiableSet(normalizedSet); this.duration = duration; } @@ -40,58 +50,90 @@ public AutoModRule(String id, List flaggedWords, Duration duration) { /** * Matches words against flagged words. * - * @param candidateWords map of candidate words keyed by normalized value + * @param candidateWords list of candidate words in order * @return list of matches */ - public List getMatches(Map candidateWords) { - return candidateWords.values().stream() - .filter(candidateWord -> flaggedWords.contains(candidateWord.normalized())) - .flatMap(candidateWord -> candidateWord.originals().stream() - .map(original -> new AutoModMatch(original, candidateWord.normalized()))) - .toList(); + public List getMatches(List candidateWords) { + List matches = new ArrayList<>(); + int n = candidateWords.size(); + for (int i = 0; i < n; i++) { + // Check single word and phrases up to 10 tokens + for (int len = 1; len <= 10 && i + len <= n; len++) { + List sub = candidateWords.subList(i, i + len); + + // Try joining with spaces + String normalizedPhrase = sub.stream() + .map(CandidateWord::normalized) + .collect(Collectors.joining(" ")); + + if (flaggedWords.contains(normalizedPhrase)) { + String originalPhrase = sub.stream() + .map(CandidateWord::original) + .collect(Collectors.joining(" ")); + matches.add(new AutoModMatch(originalPhrase, normalizedPhrase)); + continue; + } + + // Try joining without spaces to catch things like "f u c k" if "fuck" is flagged + String noSpacePhrase = sub.stream() + .map(CandidateWord::normalized) + .collect(Collectors.joining("")); + if (flaggedWords.contains(noSpacePhrase)) { + String originalPhrase = sub.stream() + .map(CandidateWord::original) + .collect(Collectors.joining("")); + matches.add(new AutoModMatch(originalPhrase, noSpacePhrase)); + } + } + } + return matches; } /** - * Gets a map of candidate words based on a message. + * Gets a list of candidate words based on a message. * * @param message the message to get candidates for - * @return map of candidate words keyed by normalized value + * @return list of candidate words in order */ - public static LinkedHashMap getCandidateWords(String message) { - LinkedHashMap candidates = new LinkedHashMap<>(); - - Matcher tokenMatcher = TOKEN_PATTERN.matcher(message); - while (tokenMatcher.find()) { - String originalToken = tokenMatcher.group(); - String normalizedToken = normalize(originalToken); - - addCandidate(candidates, normalizedToken, originalToken); - } + public static List getCandidateWords(String message) { + List candidates = new ArrayList<>(); Matcher chunkMatcher = NON_WHITESPACE_PATTERN.matcher(message); while (chunkMatcher.find()) { String originalChunk = chunkMatcher.group(); - - // Plain alphanumeric words are already handled by TOKEN_PATTERN, - // so only process chunks that contain punctuation/symbols. - if (originalChunk.chars().allMatch(Character::isLetterOrDigit)) { - continue; - } - String normalizedChunk = NON_ALPHANUMERIC.matcher(normalize(originalChunk)).replaceAll(""); - addCandidate(candidates, normalizedChunk, originalChunk); - } + if (normalizedChunk.isBlank()) continue; - return candidates; - } + // Try to see if this chunk contains multiple words + Matcher tokenMatcher = TOKEN_PATTERN.matcher(originalChunk); + List subTokens = new ArrayList<>(); + while (tokenMatcher.find()) { + subTokens.add(tokenMatcher.group()); + } - private static void addCandidate(LinkedHashMap candidates, String normalized, String original) { - if (normalized.isBlank() || original.isBlank()) { - return; + if (subTokens.size() > 1) { + // If it contains multi-character tokens, it's likely multiple words (e.g., "bad-word") + if (subTokens.stream().anyMatch(t -> t.length() > 1)) { + for (String sub : subTokens) { + candidates.add(new CandidateWord(normalize(sub), sub)); + } + // Also add the combined version if it's different from simple concatenation + String joinedSub = subTokens.stream().map(AutoModRule::normalize).collect(Collectors.joining("")); + if (!normalizedChunk.equals(joinedSub)) { + candidates.add(new CandidateWord(normalizedChunk, originalChunk)); + } + } else { + // All single letters, likely a single word broken up (e.g., "b.a.d") + candidates.add(new CandidateWord(normalizedChunk, originalChunk)); + } + } else { + // Single token or no sub-tokens (punctuation only) + candidates.add(new CandidateWord(normalizedChunk, originalChunk)); + } } - candidates.computeIfAbsent(normalized, CandidateWord::new).addOriginal(original); + return candidates; } private static String normalize(String input) { diff --git a/core/src/main/java/org/btuk/proxy/core/chat/automod/CandidateWord.java b/core/src/main/java/org/btuk/proxy/core/chat/automod/CandidateWord.java index b57d434..8bb43e3 100644 --- a/core/src/main/java/org/btuk/proxy/core/chat/automod/CandidateWord.java +++ b/core/src/main/java/org/btuk/proxy/core/chat/automod/CandidateWord.java @@ -1,17 +1,4 @@ package org.btuk.proxy.core.chat.automod; -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -public record CandidateWord(String normalized, List originals) { - - public CandidateWord(String normalized) { - this(normalized, new ArrayList<>()); - } - - public void addOriginal(String original) { - originals.add(original); - } +public record CandidateWord(String normalized, String original) { } \ No newline at end of file diff --git a/core/src/test/java/org/btuk/proxy/core/chat/automod/AutoModRuleTest.java b/core/src/test/java/org/btuk/proxy/core/chat/automod/AutoModRuleTest.java new file mode 100644 index 0000000..beaa868 --- /dev/null +++ b/core/src/test/java/org/btuk/proxy/core/chat/automod/AutoModRuleTest.java @@ -0,0 +1,59 @@ +package org.btuk.proxy.core.chat.automod; + +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class AutoModRuleTest { + + @Test + void testTooBadMatch() { + AutoModRule rule = new AutoModFlagRule("test", List.of("too bad"), 1, Duration.ZERO, true); + List candidates = AutoModRule.getCandidateWords("this is too bad"); + List matches = rule.getMatches(candidates); + assertFalse(matches.isEmpty(), "Should match 'too bad'"); + assertEquals("too bad", matches.getFirst().flaggedWord()); + assertEquals("too bad", matches.getFirst().messageWord()); + } + + @Test + void testTooBadLeetspeakMatch() { + AutoModRule rule = new AutoModFlagRule("test", List.of("too bad"), 1, Duration.ZERO, true); + List candidates = AutoModRule.getCandidateWords("this is t00 b.a.d"); + List matches = rule.getMatches(candidates); + assertFalse(matches.isEmpty(), "Should match 't00 b.a.d' against 'too bad'"); + assertEquals("too bad", matches.getFirst().flaggedWord()); + } + + @Test + void testPunctuationInFlaggedWord() { + // Test case where flagged word has punctuation + AutoModRule rule = new AutoModFlagRule("test", List.of("bad-word"), 1, Duration.ZERO, true); + List candidates = AutoModRule.getCandidateWords("you are a bad-word"); + List matches = rule.getMatches(candidates); + + assertFalse(matches.isEmpty(), "Should match 'bad-word' even if flagged as 'bad-word'"); + assertTrue(matches.stream().anyMatch(m -> m.flaggedWord().equals("badword") || m.flaggedWord().equals("bad word"))); + } + + @Test + void testDoubleSpaceInMessage() { + AutoModRule rule = new AutoModFlagRule("test", List.of("too bad"), 1, Duration.ZERO, true); + List candidates = AutoModRule.getCandidateWords("this is too bad"); + List matches = rule.getMatches(candidates); + assertFalse(matches.isEmpty(), "Should match 'too bad' (double space) against 'too bad'"); + } + + @Test + void testMixedPunctuationAndLeetspeak() { + AutoModRule rule = new AutoModFlagRule("test", List.of("bad word"), 1, Duration.ZERO, true); + List candidates = AutoModRule.getCandidateWords("you are b.a.d-w.0.r.d"); + List matches = rule.getMatches(candidates); + assertFalse(matches.isEmpty(), "Should match 'b.a.d-w.0.r.d' against 'bad word'"); + } +} From fdafdeca1f8bc353194657bcde884f24bec599b6 Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sun, 16 Aug 2026 14:09:32 +0200 Subject: [PATCH 30/38] Automod also applies to MC messages sent to discord. --- app/src/main/java/org.btuk.proxy.app/ProxyController.java | 4 ++-- .../main/java/org/btuk/proxy/core/chat/ChatManager.java | 8 +++++++- .../org/btuk/proxy/core/socket/ProxySocketHandler.java | 6 +----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org.btuk.proxy.app/ProxyController.java b/app/src/main/java/org.btuk.proxy.app/ProxyController.java index c831741..b7e9aab 100644 --- a/app/src/main/java/org.btuk.proxy.app/ProxyController.java +++ b/app/src/main/java/org.btuk.proxy.app/ProxyController.java @@ -115,7 +115,7 @@ public void start(ChatHandler chatHandler, Scheduler scheduler, CoreServerManage Moderation moderation = new Moderation(globalSQL); AutoMod automod = new AutoMod(coreUserManager, new Config(dataFolder, AUTOMOD_CONFIG_NAME), moderation, discord, chatHandler, tabManager); - ChatManager chatManager = new ChatManager(chatHandler, coreUserManager, analytics, globalSQL, moderation, automod); + ChatManager chatManager = new ChatManager(chatHandler, coreUserManager, analytics, globalSQL, moderation, automod, discord); this.userManager = new UserManager(coreUserManager, chatHandler, tabManager, globalSQL, plotSQL, regionSQL, coreServerManager, scheduler, chatManager, playerManager, analytics, discord, automod); @@ -129,7 +129,7 @@ public void start(ChatHandler chatHandler, Scheduler scheduler, CoreServerManage this.proxyApi = new ProxyApi(config.getBoolean("api.enabled"), config.getInt("api.port"), globalSQL, chatManager); serverManager.initOnlineServers(); - socketInitializer.accept(new ProxySocketHandler(chatManager, discord, userManager, serverManager, tabManager)); + socketInitializer.accept(new ProxySocketHandler(chatManager, discord, userManager, serverManager)); proxyApi.start(); diff --git a/core/src/main/java/org/btuk/proxy/core/chat/ChatManager.java b/core/src/main/java/org/btuk/proxy/core/chat/ChatManager.java index abcf22b..cd4e4c6 100644 --- a/core/src/main/java/org/btuk/proxy/core/chat/ChatManager.java +++ b/core/src/main/java/org/btuk/proxy/core/chat/ChatManager.java @@ -9,6 +9,7 @@ import org.btuk.network.lib.dto.ReplyMessage; import org.btuk.network.lib.utils.ChatUtils; import org.btuk.proxy.core.chat.automod.AutoMod; +import org.btuk.proxy.core.discord.Discord; import org.btuk.proxy.core.user.CoreUserManager; import org.btuk.proxy.core.user.User; import org.btuk.proxy.core.utils.Analytics; @@ -39,17 +40,20 @@ public class ChatManager { private final AutoMod autoMod; + private final Discord discord; + private static final List SERVER_USERS = List.of(new String[]{SERVER_SENDER, DISCORD_SENDER}); private static final String FOCUS_ENABLED_PRESET = "%s is in focus mode, unable to send message."; - public ChatManager(ChatHandler chatHandler, CoreUserManager userManager, Analytics analytics, GlobalSQL globalSQL, Moderation moderation, AutoMod autoMod) { + public ChatManager(ChatHandler chatHandler, CoreUserManager userManager, Analytics analytics, GlobalSQL globalSQL, Moderation moderation, AutoMod autoMod, Discord discord) { this.chatHandler = chatHandler; this.userManager = userManager; this.analytics = analytics; this.globalSQL = globalSQL; this.moderation = moderation; this.autoMod = autoMod; + this.discord = discord; } /** @@ -68,6 +72,8 @@ public void handle(ChatMessage chatMessage) { if (player) { analytics.addMessage(chatMessage.getSender(), Time.getDate(Time.currentTime())); } + // Send the message to discord. + discord.handle(chatMessage); } /** diff --git a/core/src/main/java/org/btuk/proxy/core/socket/ProxySocketHandler.java b/core/src/main/java/org/btuk/proxy/core/socket/ProxySocketHandler.java index 588c69e..0324333 100644 --- a/core/src/main/java/org/btuk/proxy/core/socket/ProxySocketHandler.java +++ b/core/src/main/java/org/btuk/proxy/core/socket/ProxySocketHandler.java @@ -25,7 +25,6 @@ import org.btuk.proxy.core.chat.ChatManager; import org.btuk.proxy.core.discord.Discord; import org.btuk.proxy.core.server.ServerManager; -import org.btuk.proxy.core.tab.TabManager; import org.btuk.proxy.core.user.UserManager; @Log @@ -35,14 +34,12 @@ public class ProxySocketHandler implements SocketHandler { private final Discord discord; private final UserManager userManager; private final ServerManager serverManager; - private final TabManager tabManager; - public ProxySocketHandler(ChatManager chatManager, Discord discord, UserManager userManager, ServerManager serverManager, TabManager tabManager) { + public ProxySocketHandler(ChatManager chatManager, Discord discord, UserManager userManager, ServerManager serverManager) { this.chatManager = chatManager; this.discord = discord; this.userManager = userManager; this.serverManager = serverManager; - this.tabManager = tabManager; } @Override @@ -51,7 +48,6 @@ public synchronized AbstractTransferObject handle(AbstractTransferObject abstrac switch (abstractTransferObject) { case ChatMessage chatMessage -> { chatManager.handle(chatMessage); - discord.handle(chatMessage); } case DirectMessage directMessage -> chatManager.handle(directMessage); case PrivateMessage privateMessage -> chatManager.handle(privateMessage); From f48cc241f62d9d8f92faf4afdceef9bc74694cda Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sun, 16 Aug 2026 20:20:30 +0200 Subject: [PATCH 31/38] Add /website command. --- .../core/discord/command/CommandManager.java | 5 ++++ .../proxy/core/discord/command/Website.java | 27 +++++++++++++++++++ core/src/main/resources/proxy-config.yml | 3 ++- 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/btuk/proxy/core/discord/command/Website.java diff --git a/core/src/main/java/org/btuk/proxy/core/discord/command/CommandManager.java b/core/src/main/java/org/btuk/proxy/core/discord/command/CommandManager.java index c08c898..fccf6c1 100644 --- a/core/src/main/java/org/btuk/proxy/core/discord/command/CommandManager.java +++ b/core/src/main/java/org/btuk/proxy/core/discord/command/CommandManager.java @@ -106,6 +106,11 @@ public void registerCommands(Guild guild) { commands.add(new Map("progressmap", "Sends a link to the UK progress map.", progressMap)); } + String websiteLink = config.getString("website"); + if (websiteLink != null && !websiteLink.isBlank()) { + commands.add(new Website("website", "Sends a link of the website", websiteLink)); + } + commands.add(new ClaimedPlots(globalSQL, plotSQL, "claimedplots", "List all plots that are currently claimed.", playerOption)); commands.add(new SubmittedPlots(globalSQL, plotSQL, "submittedplots", "List all plots that are currently submitted.", playerOption)); commands.add(new ActivePlots(globalSQL, plotSQL, "activeplots", "List all plots that are currently claimed or submitted.", playerOption)); diff --git a/core/src/main/java/org/btuk/proxy/core/discord/command/Website.java b/core/src/main/java/org/btuk/proxy/core/discord/command/Website.java new file mode 100644 index 0000000..098552f --- /dev/null +++ b/core/src/main/java/org/btuk/proxy/core/discord/command/Website.java @@ -0,0 +1,27 @@ +package org.btuk.proxy.core.discord.command; + +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; + +public class Website extends AbstractCommand { + + private final String link; + + /** + * Constructor, saved the name and description of the command. + * Also registers the command in Discord. + * @param name Name of the command + * @param description Description of the command + */ + public Website(String name, String description, String link) { + super(name, description); + this.link = link; + } + + @Override + public void onCommand(SlashCommandInteractionEvent event) { + ReplyCallbackAction reply = event.reply(link); + reply = reply.setEphemeral(true); + reply.queue(); + } +} \ No newline at end of file diff --git a/core/src/main/resources/proxy-config.yml b/core/src/main/resources/proxy-config.yml index a9db8d5..0350500 100644 --- a/core/src/main/resources/proxy-config.yml +++ b/core/src/main/resources/proxy-config.yml @@ -88,4 +88,5 @@ tab: #Discord user activity playing status (your server name or IP) DiscordPlaying: "server.net" -progress_map: "https://progress.buildtheuk.org" \ No newline at end of file +progress_map: "https://progress.buildtheuk.org" +website: "https://btuk.org" \ No newline at end of file From b2099847ff60d38fad99bd98e2f5161fb0cc78e6 Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Sun, 16 Aug 2026 21:57:21 +0200 Subject: [PATCH 32/38] Only send player messages to discord --- core/src/main/java/org/btuk/proxy/core/chat/ChatManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/btuk/proxy/core/chat/ChatManager.java b/core/src/main/java/org/btuk/proxy/core/chat/ChatManager.java index cd4e4c6..fa64c5f 100644 --- a/core/src/main/java/org/btuk/proxy/core/chat/ChatManager.java +++ b/core/src/main/java/org/btuk/proxy/core/chat/ChatManager.java @@ -73,7 +73,9 @@ public void handle(ChatMessage chatMessage) { analytics.addMessage(chatMessage.getSender(), Time.getDate(Time.currentTime())); } // Send the message to discord. - discord.handle(chatMessage); + if (player) { + discord.handle(chatMessage); + } } /** From 5716a7f4c0415d715dcacff110407258a5eb2866 Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Mon, 17 Aug 2026 21:53:28 +0200 Subject: [PATCH 33/38] Fix issue where player was seen as disconnected when restarting a server, the disconnect is now delayed by 1 second and checks if the player is switching server. --- .../btuk/proxy/core/server/ServerManager.java | 23 ++++++++++++------- .../java/org/btuk/proxy/core/user/User.java | 13 ++++------- .../org/btuk/proxy/core/user/UserManager.java | 11 ++++----- .../btuk/proxy/core/utils/SwitchServer.java | 1 + 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/core/src/main/java/org/btuk/proxy/core/server/ServerManager.java b/core/src/main/java/org/btuk/proxy/core/server/ServerManager.java index 509f89f..00986e6 100644 --- a/core/src/main/java/org/btuk/proxy/core/server/ServerManager.java +++ b/core/src/main/java/org/btuk/proxy/core/server/ServerManager.java @@ -1,6 +1,7 @@ package org.btuk.proxy.core.server; import lombok.extern.java.Log; + import org.btuk.network.lib.dto.OnlineUserRemove; import org.btuk.network.lib.dto.OnlineUsersReply; import org.btuk.network.lib.dto.ServerShutdown; @@ -114,15 +115,21 @@ private void cleanupServer(String serverName) { // Set the server offline in the database. globalSQL.update("UPDATE server_data SET online=0 WHERE name='" + serverName + "';"); - // Remove all users connected to this server, + // Remove all users connected to this server // and also send a message to all other online servers to remove these users from their list. - List offlineServerUsers = coreUserManager.getUsersOnServer(serverName); - offlineServerUsers.forEach(user -> { - OnlineUserRemove onlineUserRemove = new OnlineUserRemove(user.getUuid()); - chatHandler.handle(onlineUserRemove); - userManager.disconnectUser(user); - } - ); + // Delay this by a second so the switch server events have time to be processed. + scheduler.createDelayedTask(() -> { + List offlineServerUsers = coreUserManager.getUsersOnServer(serverName); + offlineServerUsers.forEach(user -> { + if (user.getSwitchServer() != null && user.getSwitchServer().getFromServer().equals(serverName)) { + log.info("User " + user.getName() + " is switching servers on shutdown."); + } else { + OnlineUserRemove onlineUserRemove = new OnlineUserRemove(user.getUuid()); + chatHandler.handle(onlineUserRemove); + userManager.disconnectUser(user); + } + }); + }, 1, TimeUnit.SECONDS); // Remove server from the list if it exists. coreServerManager.getServer(serverName).ifPresent(coreServerManager::removeServer); diff --git a/core/src/main/java/org/btuk/proxy/core/user/User.java b/core/src/main/java/org/btuk/proxy/core/user/User.java index eba294c..eb3fa63 100644 --- a/core/src/main/java/org/btuk/proxy/core/user/User.java +++ b/core/src/main/java/org/btuk/proxy/core/user/User.java @@ -132,10 +132,6 @@ public class User { @Getter private boolean focusEnabled; - @Getter - @Setter - private boolean blockNextDisconnect = false; - @Getter @Setter private int previousPlotSubmissionCount = 0; @@ -240,6 +236,10 @@ public void reconnect() { online = true; // Can't be afk on reconnect. afk = false; + cancelDisconnectTask(); + } + + public void cancelDisconnectTask() { if (disconnectTask != null && disconnectTask.getStatus() == TaskStatus.SCHEDULED) { disconnectTask.cancel(); } @@ -250,10 +250,7 @@ public void reconnect() { * Delete the user instance. */ public void delete() { - // If the disconnectTask is running cancel. - if (disconnectTask != null && disconnectTask.getStatus() == TaskStatus.SCHEDULED) { - disconnectTask.cancel(); - } + cancelDisconnectTask(); } public void mute(User user) { diff --git a/core/src/main/java/org/btuk/proxy/core/user/UserManager.java b/core/src/main/java/org/btuk/proxy/core/user/UserManager.java index 02db842..a2ac078 100644 --- a/core/src/main/java/org/btuk/proxy/core/user/UserManager.java +++ b/core/src/main/java/org/btuk/proxy/core/user/UserManager.java @@ -131,9 +131,9 @@ public void handleUserDisconnect(UserDisconnect disconnect) { return; } - if (user.isBlockNextDisconnect()) { - log.warning("User has already reconnected, cancelling disconnect."); - user.setBlockNextDisconnect(false); + // If the player is not on the server they disconnected from, it implies they switched server. + if (!user.getServer().equals(disconnect.getServer())) { + log.warning("User disconnect received but they are on a different server, cancelling disconnect."); return; } @@ -289,11 +289,8 @@ public User addUser(UserConnectRequest request) { switchServer.cancelTimeout(); user.setSwitchServer(null); + user.cancelDisconnectTask(); } else { - // If the user is still online, quickly cancel the disconnect event. - if (user.isOnline()) { - user.setBlockNextDisconnect(true); - } // Cancel disconnect task. user.reconnect(); diff --git a/core/src/main/java/org/btuk/proxy/core/utils/SwitchServer.java b/core/src/main/java/org/btuk/proxy/core/utils/SwitchServer.java index 20049d3..176ba56 100644 --- a/core/src/main/java/org/btuk/proxy/core/utils/SwitchServer.java +++ b/core/src/main/java/org/btuk/proxy/core/utils/SwitchServer.java @@ -29,6 +29,7 @@ public class SwitchServer { private final User user; + @Getter private final String fromServer; @Getter From 975521888b92f85b981a53180e90ef3fb585665f Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Tue, 18 Aug 2026 17:55:31 +0200 Subject: [PATCH 34/38] Review comments. --- .../btuk/proxy/core/chat/automod/AutoModRule.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoModRule.java b/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoModRule.java index 168ff13..3904f04 100644 --- a/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoModRule.java +++ b/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoModRule.java @@ -26,7 +26,10 @@ public abstract class AutoModRule { @Getter private final Duration duration; + private final int maxLength; + public AutoModRule(String id, List flaggedWords, Duration duration) { + int maxLength = 0; this.id = id; java.util.Set normalizedSet = new java.util.HashSet<>(); for (String word : flaggedWords) { @@ -39,10 +42,13 @@ public AutoModRule(String id, List flaggedWords, Duration duration) { if (tokens.isEmpty()) continue; normalizedSet.add(String.join(" ", tokens)); - normalizedSet.add(String.join("", tokens)); + String joined = String.join("", tokens); + maxLength = Math.max(maxLength, joined.length()); + normalizedSet.add(joined); } this.flaggedWords = java.util.Collections.unmodifiableSet(normalizedSet); this.duration = duration; + this.maxLength = maxLength; } public abstract boolean blockMessage(); @@ -57,8 +63,8 @@ public List getMatches(List candidateWords) { List matches = new ArrayList<>(); int n = candidateWords.size(); for (int i = 0; i < n; i++) { - // Check single word and phrases up to 10 tokens - for (int len = 1; len <= 10 && i + len <= n; len++) { + // Check single word and phrases up to maxLength tokens. + for (int len = 1; len <= maxLength && i + len <= n; len++) { List sub = candidateWords.subList(i, i + len); // Try joining with spaces @@ -74,7 +80,7 @@ public List getMatches(List candidateWords) { continue; } - // Try joining without spaces to catch things like "f u c k" if "fuck" is flagged + // Try joining without spaces to catch things like "b a d" if "bad" is flagged String noSpacePhrase = sub.stream() .map(CandidateWord::normalized) .collect(Collectors.joining("")); From b84561e34dbe0d45b53b958dc93f803031d0d63e Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Tue, 18 Aug 2026 19:10:29 +0200 Subject: [PATCH 35/38] Improve speed of getMatches method. --- .../proxy/core/chat/automod/AutoModRule.java | 82 ++++++++++++------- 1 file changed, 53 insertions(+), 29 deletions(-) diff --git a/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoModRule.java b/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoModRule.java index 3904f04..c7af2a2 100644 --- a/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoModRule.java +++ b/core/src/main/java/org/btuk/proxy/core/chat/automod/AutoModRule.java @@ -19,6 +19,8 @@ public abstract class AutoModRule { private static final Pattern NON_WHITESPACE_PATTERN = Pattern.compile("\\S+"); private static final Pattern NON_ALPHANUMERIC = Pattern.compile("[^\\p{L}\\p{N}]"); + private static final char SPACE = ' '; + private final Set flaggedWords; @Getter private final String id; @@ -61,34 +63,45 @@ public AutoModRule(String id, List flaggedWords, Duration duration) { */ public List getMatches(List candidateWords) { List matches = new ArrayList<>(); - int n = candidateWords.size(); - for (int i = 0; i < n; i++) { - // Check single word and phrases up to maxLength tokens. - for (int len = 1; len <= maxLength && i + len <= n; len++) { - List sub = candidateWords.subList(i, i + len); - - // Try joining with spaces - String normalizedPhrase = sub.stream() - .map(CandidateWord::normalized) - .collect(Collectors.joining(" ")); - - if (flaggedWords.contains(normalizedPhrase)) { - String originalPhrase = sub.stream() - .map(CandidateWord::original) - .collect(Collectors.joining(" ")); - matches.add(new AutoModMatch(originalPhrase, normalizedPhrase)); - continue; + int totalCandidates = candidateWords.size(); + for (int i = 0; i < totalCandidates; i++) { + StringBuilder sbNormalizedWithSpaces = new StringBuilder(); + StringBuilder sbNormalizedNoSpaces = new StringBuilder(); + + for (int j = i; j < totalCandidates; j++) { + CandidateWord cw = candidateWords.get(j); + String normalized = cw.normalized(); + + sbNormalizedNoSpaces.append(normalized); + if (sbNormalizedNoSpaces.length() > maxLength) { + break; } - // Try joining without spaces to catch things like "b a d" if "bad" is flagged - String noSpacePhrase = sub.stream() - .map(CandidateWord::normalized) - .collect(Collectors.joining("")); - if (flaggedWords.contains(noSpacePhrase)) { - String originalPhrase = sub.stream() - .map(CandidateWord::original) - .collect(Collectors.joining("")); - matches.add(new AutoModMatch(originalPhrase, noSpacePhrase)); + if (j > i) { + sbNormalizedWithSpaces.append(SPACE); + } + sbNormalizedWithSpaces.append(normalized); + + String phraseWithSpaces = sbNormalizedWithSpaces.toString(); + if (flaggedWords.contains(phraseWithSpaces)) { + StringBuilder sbOriginal = new StringBuilder(); + for (int k = i; k <= j; k++) { + if (k > i) { + sbOriginal.append(SPACE); + } + sbOriginal.append(candidateWords.get(k).original()); + } + matches.add(new AutoModMatch(sbOriginal.toString(), phraseWithSpaces)); + } else if (j > i) { + // Try joining without spaces to catch things like "b a d" if "bad" is flagged + String phraseNoSpaces = sbNormalizedNoSpaces.toString(); + if (flaggedWords.contains(phraseNoSpaces)) { + StringBuilder sbOriginal = new StringBuilder(); + for (int k = i; k <= j; k++) { + sbOriginal.append(candidateWords.get(k).original()); + } + matches.add(new AutoModMatch(sbOriginal.toString(), phraseNoSpaces)); + } } } } @@ -120,12 +133,23 @@ public static List getCandidateWords(String message) { if (subTokens.size() > 1) { // If it contains multi-character tokens, it's likely multiple words (e.g., "bad-word") - if (subTokens.stream().anyMatch(t -> t.length() > 1)) { + boolean hasMultiCharToken = false; + for (String token : subTokens) { + if (token.length() > 1) { + hasMultiCharToken = true; + break; + } + } + + if (hasMultiCharToken) { + StringBuilder joinedSubBuilder = new StringBuilder(); for (String sub : subTokens) { - candidates.add(new CandidateWord(normalize(sub), sub)); + String normalizedSub = normalize(sub); + candidates.add(new CandidateWord(normalizedSub, sub)); + joinedSubBuilder.append(normalizedSub); } // Also add the combined version if it's different from simple concatenation - String joinedSub = subTokens.stream().map(AutoModRule::normalize).collect(Collectors.joining("")); + String joinedSub = joinedSubBuilder.toString(); if (!normalizedChunk.equals(joinedSub)) { candidates.add(new CandidateWord(normalizedChunk, originalChunk)); } From 5d538c4efd6069e836a25f089404a354512ae15e Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Tue, 18 Aug 2026 20:39:02 +0200 Subject: [PATCH 36/38] Ensure no duplicate disconnect tasks exist but cancelling the previous before creating a new one. --- core/src/main/java/org/btuk/proxy/core/user/User.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/main/java/org/btuk/proxy/core/user/User.java b/core/src/main/java/org/btuk/proxy/core/user/User.java index eb3fa63..673a388 100644 --- a/core/src/main/java/org/btuk/proxy/core/user/User.java +++ b/core/src/main/java/org/btuk/proxy/core/user/User.java @@ -222,6 +222,8 @@ public void disconnect(Runnable runnable) { analytics.save(this, Time.getDate(time), time); online = false; + // Ensure no existing disconnect exists. + cancelDisconnectTask(); // Run a delayed task to remove the user. disconnectTask = scheduler.createDelayedTask(runnable, 5L, TimeUnit.MINUTES); } From 2385a3da2f8b2374b0a546e139d841f8a867ddbc Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Wed, 19 Aug 2026 21:06:52 +0200 Subject: [PATCH 37/38] Fix pom for maven compiler plugin. --- api/pom.xml | 4 ---- app/pom.xml | 9 --------- database/pom.xml | 9 --------- 3 files changed, 22 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index c314078..df3ad23 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -119,10 +119,6 @@ - - org.apache.maven.plugins - maven-compiler-plugin - diff --git a/app/pom.xml b/app/pom.xml index b24d3cf..db0e890 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -84,13 +84,4 @@ commons-lang3 - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - \ No newline at end of file diff --git a/database/pom.xml b/database/pom.xml index e73de01..194960b 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -36,13 +36,4 @@ 2.0.0-1.21.4 - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - \ No newline at end of file From 2749ad34067bf0159e386dbb5fb5067cb1676f03 Mon Sep 17 00:00:00 2001 From: LM-Wolfert Date: Wed, 19 Aug 2026 21:12:55 +0200 Subject: [PATCH 38/38] Fix java version in jitpack config. --- jitpack.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jitpack.yml b/jitpack.yml index 18cdc8e..fb27ac1 100644 --- a/jitpack.yml +++ b/jitpack.yml @@ -1,4 +1,4 @@ jdk: - - openjdk21 + - openjdk25 install: - ./mvnw install -Dmaven.javadoc.skip=true -DskipTests -pl :database,:core,:app -am \ No newline at end of file