Add global bandwidth limit setting

This commit is contained in:
s809
2025-09-26 21:45:10 +05:00
parent 73324c71ec
commit b312582ce4
8 changed files with 76 additions and 18 deletions
@@ -1722,11 +1722,19 @@ public class Config
// Common
public static ConfigEntry<Integer> maxDataTransferSpeed = new ConfigEntry.Builder<Integer>()
.setChatCommandName("common.maxDataTransferSpeed")
public static ConfigEntry<Integer> playerBandwidthLimit = new ConfigEntry.Builder<Integer>()
.setChatCommandName("common.playerBandwidthLimit")
.setMinDefaultMax(0, 500, 1000000 /* 1 GB/s */)
.comment(""
+ "Maximum speed for uploading LODs to the clients, in KB/s.\n"
+ "Maximum per-player speed for uploading LODs to the clients, in KB/s.\n"
+ "Value of 0 disables the limit."
+ "")
.build();
public static ConfigEntry<Integer> globalBandwidthLimit = new ConfigEntry.Builder<Integer>()
.setChatCommandName("common.globalBandwidthLimit")
.setMinDefaultMax(0, 0, 10000000 /* 10 GB/s */)
.comment(""
+ "Maximum global speed for uploading LODs to the clients, in KB/s.\n"
+ "Value of 0 disables the limit."
+ "")
.build();
@@ -153,7 +153,7 @@ public class ClientNetworkState implements Closeable
if (Config.Server.enableAdaptiveTransferSpeed.get())
{
sessionConfig.constrainValue(Config.Server.maxDataTransferSpeed, this.congestionControl.getDesiredRate());
sessionConfig.constrainValue(Config.Server.playerBandwidthLimit, this.congestionControl.getDesiredRate());
}
if (blocking)
@@ -45,7 +45,7 @@ public class SessionConfig implements INetworkObject
registerConfigEntry(Config.Server.maxSyncOnLoadRequestDistance, Math::min);
registerConfigEntry(Config.Server.syncOnLoadRateLimit, Math::min);
registerConfigEntry(Config.Server.maxDataTransferSpeed, (x, y) -> {
registerConfigEntry(Config.Server.playerBandwidthLimit, (x, y) -> {
if (x == 0 && y == 0)
{
return 0;
@@ -80,7 +80,7 @@ public class SessionConfig implements INetworkObject
public int getMaxSyncOnLoadDistance() { return this.getValue(Config.Server.maxSyncOnLoadRequestDistance); }
public int getSyncOnLoginRateLimit() { return this.getValue(Config.Server.syncOnLoadRateLimit); }
public int getMaxDataTransferSpeed() { return this.getValue(Config.Server.maxDataTransferSpeed); }
public int getPlayerBandwidthLimit() { return this.getValue(Config.Server.playerBandwidthLimit); }
@@ -8,7 +8,6 @@ import io.netty.buffer.ByteBuf;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.*;
public class FullDataPayloadSender implements AutoCloseable
@@ -26,11 +25,14 @@ public class FullDataPayloadSender implements AutoCloseable
private final IntSupplier maxKBpsSupplier;
private final ConcurrentLinkedQueue<PendingTransfer> transferQueue = new ConcurrentLinkedQueue<>();
private final SharedBandwidthLimit sharedBandwidthLimit;
public FullDataPayloadSender(NetworkSession session, IntSupplier maxKBpsSupplier)
public FullDataPayloadSender(NetworkSession session, IntSupplier maxKBpsSupplier, SharedBandwidthLimit sharedBandwidthLimit)
{
this.session = session;
this.maxKBpsSupplier = maxKBpsSupplier;
this.sharedBandwidthLimit = sharedBandwidthLimit;
UPLOAD_TIMER.scheduleAtFixedRate(this.tickTimerTask, 0, 1000 / TICK_RATE);
}
@@ -48,11 +50,16 @@ public class FullDataPayloadSender implements AutoCloseable
private void tick()
{
int convertedMaxRate = this.maxKBpsSupplier.getAsInt();
convertedMaxRate = convertedMaxRate > 0 ? convertedMaxRate : Integer.MAX_VALUE / 1000;
int bandwidthShare = this.sharedBandwidthLimit.getBandwidthShare();
int maxPlayerRate = Math.min(this.maxKBpsSupplier.getAsInt(), bandwidthShare);
// + 1 to account for rounding errors on values of < 4
int bytesToSend = (convertedMaxRate * 1000) / TICK_RATE + 1;
int bytesToSend = maxPlayerRate > 0
? (maxPlayerRate * 1000) / TICK_RATE + 1
: Integer.MAX_VALUE;
this.sharedBandwidthLimit.setSenderActive(this, bytesToSend > 0);
while (bytesToSend > 0)
{
PendingTransfer pendingTransfer = this.transferQueue.peek();
@@ -0,0 +1,36 @@
package com.seibel.distanthorizons.core.multiplayer.fullData;
import com.seibel.distanthorizons.core.config.Config;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class SharedBandwidthLimit
{
private final Set<FullDataPayloadSender> senders = Collections.newSetFromMap(new ConcurrentHashMap<>());
public void setSenderActive(FullDataPayloadSender sender, boolean active)
{
if (active)
{
this.senders.add(sender);
}
else
{
this.senders.remove(sender);
}
}
public int getBandwidthShare()
{
int globalBandwidthLimit = Config.Server.globalBandwidthLimit.get();
if (globalBandwidthLimit == 0)
{
globalBandwidthLimit = Integer.MAX_VALUE;
}
return globalBandwidthLimit / Math.max(this.senders.size(), 1);
}
}
@@ -5,6 +5,7 @@ import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
import com.seibel.distanthorizons.core.level.AbstractDhServerLevel;
import com.seibel.distanthorizons.core.multiplayer.config.SessionConfig;
import com.seibel.distanthorizons.core.multiplayer.fullData.FullDataPayloadSender;
import com.seibel.distanthorizons.core.multiplayer.fullData.SharedBandwidthLimit;
import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageInternalEvent;
import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage;
import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
@@ -48,10 +49,10 @@ public class ServerPlayerState implements Closeable
// constructors //
//==============//
public ServerPlayerState(IServerPlayerWrapper serverPlayer)
public ServerPlayerState(IServerPlayerWrapper serverPlayer, SharedBandwidthLimit sharedBandwidthLimit)
{
this.networkSession = new NetworkSession(serverPlayer);
this.fullDataPayloadSender = new FullDataPayloadSender(this.networkSession, this.sessionConfig::getMaxDataTransferSpeed);
this.fullDataPayloadSender = new FullDataPayloadSender(this.networkSession, this.sessionConfig::getPlayerBandwidthLimit, sharedBandwidthLimit);
this.networkSession.registerHandler(SessionConfigMessage.class, (sessionConfigMessage) ->
{
@@ -1,5 +1,6 @@
package com.seibel.distanthorizons.core.multiplayer.server;
import com.seibel.distanthorizons.core.multiplayer.fullData.SharedBandwidthLimit;
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import org.jetbrains.annotations.Nullable;
@@ -16,6 +17,7 @@ public class ServerPlayerStateManager
private final ConcurrentMap<IServerPlayerWrapper, ServerPlayerState> connectedPlayerStateByPlayerWrapper = new ConcurrentHashMap<>();
private final ConcurrentMap<IServerPlayerWrapper, MessageQueueState> messageQueueByPlayerWrapper = new ConcurrentHashMap<>();
private final SharedBandwidthLimit sharedBandwidthLimit = new SharedBandwidthLimit();
//========================//
@@ -24,7 +26,7 @@ public class ServerPlayerStateManager
public ServerPlayerState registerJoinedPlayer(IServerPlayerWrapper serverPlayer)
{
ServerPlayerState playerState = new ServerPlayerState(serverPlayer);
ServerPlayerState playerState = new ServerPlayerState(serverPlayer, this.sharedBandwidthLimit);
this.connectedPlayerStateByPlayerWrapper.put(serverPlayer, playerState);
return playerState;
}
@@ -750,10 +750,14 @@
"distanthorizons.config.server.maxSyncOnLoadRequestDistance.@tooltip":
"Defines the distance allowed to be synchronized around the player.\nShould be the same or larger than maxGenerationRequestDistance in most cases.",
"distanthorizons.config.server.maxDataTransferSpeed":
"Maximum Data Transfer Speed, KB/s",
"distanthorizons.config.server.maxDataTransferSpeed.@tooltip":
"Maximum speed for uploading LODs to the clients, in KB/s.\nValue of 0 disables the limit.",
"distanthorizons.config.server.playerBandwidthLimit":
"Per-player Bandwidth Limit, KB/s",
"distanthorizons.config.server.playerBandwidthLimit.@tooltip":
"Maximum per-player speed for uploading LODs to the clients, in KB/s.\nValue of 0 disables the limit.",
"distanthorizons.config.server.globalBandwidthLimit":
"Global Bandwidth Limit, KB/s",
"distanthorizons.config.server.globalBandwidthLimit.@tooltip":
"Maximum global speed for uploading LODs to the clients, in KB/s.\nValue of 0 disables the limit.",
"distanthorizons.config.server.enableAdaptiveTransferSpeed":
"Enable Adaptive Transfer Speed",
"distanthorizons.config.server.enableAdaptiveTransferSpeed.@tooltip":