Enforce LOD request concurrency limit.

This commit is contained in:
Jim C K Flaten committed 2026-07-22 22:50:35 +02:00
1 parent bda0d067ee
commit 9feff5efa5
7 files changed
+73 -14

No files matched your search

@@ -18,6 +18,7 @@
package no.jckf.dhsupport.core;
import no.jckf.dhsupport.core.configuration.Configuration;
import no.jckf.dhsupport.core.configuration.DhsConfig;
import no.jckf.dhsupport.core.database.models.LodModel;
import no.jckf.dhsupport.core.dataobject.SectionPosition;
@@ -68,7 +69,12 @@ public class PreGenerator implements Runnable
public void run()
{
int rateLimit = this.world.getConfig().getInt(DhsConfig.FULL_DATA_REQUEST_CONCURRENCY_LIMIT);
Configuration worldConfig = this.world.getConfig();
// Fall back to the old key in case the server's config file is for an older plugin version.
int rateLimit = worldConfig.has(DhsConfig.CLIENT_LOD_REQUEST_CONCURRENCY_LIMIT)
? worldConfig.getInt(DhsConfig.CLIENT_LOD_REQUEST_CONCURRENCY_LIMIT)
: worldConfig.getInt(DhsConfig.FULL_DATA_REQUEST_CONCURRENCY_LIMIT);
int currentX = this.centerX;
int currentZ = this.centerZ;
@@ -118,4 +118,9 @@ public class Configuration
{
return (int) this.variables.compute(key, (k, v) -> v == null ? 1 : (int) v + 1);
}
public int decrement(String key)
{
return (int) this.variables.compute(key, (k, v) -> v == null ? -1 : (int) v - 1);
}
}
@@ -32,8 +32,15 @@ public abstract class DhsConfig
public static String DISTANT_GENERATION_ENABLED = "distant_generation_enabled";
/**
* @deprecated Replaced by CLIENT_LOD_REQUEST_CONCURRENCY_LIMIT.
*/
public static String FULL_DATA_REQUEST_CONCURRENCY_LIMIT = "full_data_request_concurrency_limit";
public static String CLIENT_LOD_REQUEST_CONCURRENCY_LIMIT = "client_lod_request_concurrency_limit";
public static String SERVER_LOD_REQUEST_CONCURRENCY_LIMIT = "server_lod_request_concurrency_limit";
public static String REAL_TIME_UPDATES_ENABLED = "real_time_updates_enabled";
public static String REAL_TIME_UPDATE_RADIUS = "real_time_update_radius";
@@ -30,6 +30,7 @@ import no.jckf.dhsupport.core.world.WorldInterface;
import org.bukkit.Bukkit;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
public class LodHandler
{
@@ -39,6 +40,8 @@ public class LodHandler
protected PluginMessageHandler pluginMessageHandler;
protected AtomicInteger requestsInFlight = new AtomicInteger(0);
public LodHandler(DhSupport dhSupport, PluginMessageHandler pluginMessageHandler)
{
this.dhSupport = dhSupport;
@@ -100,7 +103,35 @@ public class LodHandler
return;
}
int clientRateLimit = playerConfig.getInt(DhsConfig.CLIENT_LOD_REQUEST_CONCURRENCY_LIMIT, 0);
int serverRateLimit = dhsConfig.getInt(DhsConfig.SERVER_LOD_REQUEST_CONCURRENCY_LIMIT, 0);
boolean exceedsClientRateLimit = playerConfig.increment("active-requests") > clientRateLimit;
boolean exceedsServerRateLimit = serverRateLimit > 0 && this.requestsInFlight.incrementAndGet() > serverRateLimit;
if (exceedsClientRateLimit || exceedsServerRateLimit) {
if (serverRateLimit > 0) {
this.requestsInFlight.decrementAndGet();
}
playerConfig.decrement("active-requests");
ExceptionMessage exceptionMessage = new ExceptionMessage();
exceptionMessage.isResponseTo(requestMessage);
exceptionMessage.setTypeId(ExceptionMessage.TYPE_RATE_LIMITED);
exceptionMessage.setMessage("Rate limited by the server");
this.pluginMessageHandler.sendPluginMessage(requestMessage.getSender(), exceptionMessage);
return;
}
this.dhSupport.getLod(worldUuid, position)
.whenComplete((lodModel, exception) -> {
if (serverRateLimit > 0) {
this.requestsInFlight.decrementAndGet();
}
playerConfig.decrement("active-requests");
})
.thenAccept((lodModel) -> {
if (lodModel == null) {
ExceptionMessage exceptionMessage = new ExceptionMessage();
@@ -129,6 +129,11 @@ public class PlayerConfigHandler
keepValue = false;
}
// If no value was found under the new key, read the old key to see if the server's config file is for an older plugin version.
if (key.equals(DhsConfig.CLIENT_LOD_REQUEST_CONCURRENCY_LIMIT) && dhsValue == null) {
dhsValue = dhsConfig.get(DhsConfig.FULL_DATA_REQUEST_CONCURRENCY_LIMIT);
}
// TODO: This is ugly. Move to comparator closures like in DH.
if (key.equals(DhsConfig.BORDER_CENTER_X)) {
keepValue = world.getWorldBorderX();
@@ -31,7 +31,7 @@ public class RemotePlayerConfigMessage extends PluginMessage
DhsConfig.BORDER_CENTER_X,
DhsConfig.BORDER_CENTER_Z,
DhsConfig.BORDER_RADIUS,
DhsConfig.FULL_DATA_REQUEST_CONCURRENCY_LIMIT,
DhsConfig.CLIENT_LOD_REQUEST_CONCURRENCY_LIMIT,
DhsConfig.REAL_TIME_UPDATES_ENABLED,
DhsConfig.REAL_TIME_UPDATE_RADIUS,
DhsConfig.LOGIN_DATA_SYNC_ENABLED,
@@ -44,7 +44,7 @@ public class RemotePlayerConfigMessage extends PluginMessage
protected boolean distantGenerationEnabled;
protected int fullDataRequestConcurrencyLimit;
protected int clientLodRequestConcurrencyLimit;
protected boolean realTimeUpdatesEnabled;
@@ -84,14 +84,14 @@ public class RemotePlayerConfigMessage extends PluginMessage
return this.distantGenerationEnabled;
}
public void setFullDataRequestConcurrencyLimit(int limit)
public void setClientLodRequestConcurrencyLimit(int limit)
{
this.fullDataRequestConcurrencyLimit = limit;
this.clientLodRequestConcurrencyLimit = limit;
}
public int getFullDataRequestConcurrencyLimit()
public int getClientLodRequestConcurrencyLimit()
{
return this.fullDataRequestConcurrencyLimit;
return this.clientLodRequestConcurrencyLimit;
}
public void setRealTimeUpdatesEnabled(boolean enabled)
@@ -194,7 +194,7 @@ public class RemotePlayerConfigMessage extends PluginMessage
encoder.writeInt(this.borderCenterZ);
encoder.writeInt(this.borderRadius);
encoder.writeInt(this.fullDataRequestConcurrencyLimit);
encoder.writeInt(this.clientLodRequestConcurrencyLimit);
encoder.writeBoolean(this.realTimeUpdatesEnabled);
encoder.writeInt(this.realTimeUpdateRadius);
@@ -216,7 +216,7 @@ public class RemotePlayerConfigMessage extends PluginMessage
this.borderCenterZ = decoder.readInt();
this.borderRadius = decoder.readInt();
this.fullDataRequestConcurrencyLimit = decoder.readInt();
this.clientLodRequestConcurrencyLimit = decoder.readInt();
this.realTimeUpdatesEnabled = decoder.readBoolean();
this.realTimeUpdateRadius = decoder.readInt();
@@ -241,7 +241,7 @@ public class RemotePlayerConfigMessage extends PluginMessage
this.borderCenterZ = borderCenterZ == null ? 0 : borderCenterZ;
this.borderRadius = borderRadius == null ? 0 : borderRadius;
this.fullDataRequestConcurrencyLimit = config.getInt(DhsConfig.FULL_DATA_REQUEST_CONCURRENCY_LIMIT);
this.clientLodRequestConcurrencyLimit = config.getInt(DhsConfig.CLIENT_LOD_REQUEST_CONCURRENCY_LIMIT);
this.realTimeUpdatesEnabled = config.getBool(DhsConfig.REAL_TIME_UPDATES_ENABLED);
this.realTimeUpdateRadius = config.getInt(DhsConfig.REAL_TIME_UPDATE_RADIUS);
@@ -264,7 +264,7 @@ public class RemotePlayerConfigMessage extends PluginMessage
config.set(DhsConfig.BORDER_CENTER_Z, this.borderCenterZ);
config.set(DhsConfig.BORDER_RADIUS, this.borderRadius);
config.set(DhsConfig.FULL_DATA_REQUEST_CONCURRENCY_LIMIT, this.fullDataRequestConcurrencyLimit);
config.set(DhsConfig.CLIENT_LOD_REQUEST_CONCURRENCY_LIMIT, this.clientLodRequestConcurrencyLimit);
config.set(DhsConfig.REAL_TIME_UPDATES_ENABLED, this.realTimeUpdatesEnabled);
config.set(DhsConfig.REAL_TIME_UPDATE_RADIUS, this.realTimeUpdateRadius);
+8 -3
View File
@@ -14,10 +14,15 @@ render_distance: 1024
# Default: true
distant_generation_enabled: true
# Number of parallel LOD requests per client.
# Maximum number of concurrent LOD requests per client.
# Required.
# Default: 20
full_data_request_concurrency_limit: 20
client_lod_request_concurrency_limit: 20
# Maximum concurrent LOD requests server-wide, across all players. 0 = unlimited.
# Optional.
# Default: 0
#server_lod_request_concurrency_limit: 100
# Send LOD updates to players in real-time?
# Required.
@@ -235,4 +240,4 @@ scheduler_threads: 4
#debug: true
# Don't touch :)
config_version: 10
config_version: 11