Add config for a few slow features

This commit is contained in:
s809
2023-09-21 21:00:23 +05:00
parent d325a69e3f
commit 877a89d17a
20 changed files with 285 additions and 135 deletions
@@ -755,6 +755,8 @@ public class Config
public static class Multiplayer
{
public static ConfigCategory serverNetworking = new ConfigCategory.Builder().set(ServerNetworking.class).build();
public static ConfigEntry<EServerFolderNameMode> serverFolderNameMode = new ConfigEntry.Builder<EServerFolderNameMode>()
.set(EServerFolderNameMode.NAME_ONLY)
.comment(""
@@ -800,29 +802,46 @@ public class Config
+ "")
.build();
// not currently implemented
public static ConfigEntry<Boolean> enableServerNetworking = new ConfigEntry.Builder<Boolean>()
.set(false)
.comment(""
+ "Attention: \n"
+ " 1. This feature is not fully implemented. \n"
+ " 2. If you really want to use it, enable it only on trusted server/with trusted players. \n"
+ "\n"
+ "If true Distant Horizons will attempt to communicate with the connected \n"
+ "server in order to load LODs outside your vanilla render distance. \n"
+ "\n"
+ "Note: This requires DH to be installed on the server in order to function. \n"
+ "")
.build();
public static ConfigEntry<Integer> serverNetworkingRateLimit = new ConfigEntry.Builder<Integer>()
.setMinDefaultMax(1, 20, 100)
.comment(""
+ "Limits the amount of sent/processed LOD requests concurrently. \n"
+ "\n"
+ "Note: Server can set its own rate limit. \n"
+ "")
.build();
// TODO Write strings
public static class ServerNetworking
{
public static ConfigEntry<Boolean> enableServerNetworking = new ConfigEntry.Builder<Boolean>()
.set(true)
.comment(""
+ "Attention: \n"
+ " 1. This feature is not fully implemented. \n"
+ " 2. If you really want to use it, enable it only on trusted server/with trusted players. \n"
+ "\n"
+ "If true Distant Horizons will attempt to communicate with the connected \n"
+ "server in order to load LODs outside your vanilla render distance. \n"
+ "\n"
+ "Note: This requires DH to be installed on the server in order to function. \n"
+ "")
.build();
public static ConfigEntry<Integer> requestRateLimit = new ConfigEntry.Builder<Integer>()
.setMinDefaultMax(1, 20, 100)
.comment(""
+ "Limits the amount of sent/processed LOD requests concurrently. \n"
+ "\n"
+ "Note: Server can set its own rate limit. \n"
+ "")
.build();
public static ConfigEntry<Boolean> enableRealTimeUpdates = new ConfigEntry.Builder<Boolean>()
.set(false)
.comment(""
+ "Enables real time updates from server."
+ "")
.build();
public static ConfigEntry<Boolean> enablePostRelogUpdate = new ConfigEntry.Builder<Boolean>()
.set(false)
.comment(""
+ "Enables updating of LODs after relog."
+ "")
.build();
}
}
@@ -1081,6 +1100,7 @@ public class Config
}
// TODO write strings
public static class Debugging
{
public static ConfigCategory debugWireframeRendering = new ConfigCategory.Builder().set(DebugWireframeRendering.class).build();
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.core.file.fullDatafile;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource;
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
@@ -26,12 +27,12 @@ import com.seibel.distanthorizons.core.generation.IWorldGenerationQueue;
import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState;
import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState;
import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException;
import com.seibel.distanthorizons.core.network.exceptions.InvalidSectionPosException;
import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException;
import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryRequestMessage;
import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryResponseMessage;
import com.seibel.distanthorizons.core.pos.DhLodPos;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
@@ -144,11 +145,10 @@ public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler
@Override
public FullDataMetaFile getFileIfExist(DhSectionPos pos)
{
// This feature is broken - same data may produce different hashes, apparently
if (true)
if (this.networkState == null || !this.isFileUnloaded(pos))
return super.getFileIfExist(pos);
if (this.networkState == null || !this.isFileUnloaded(pos))
if (!this.networkState.config.postRelogUpdateEnabled)
return super.getFileIfExist(pos);
FullDataMetaFile metaFile = super.getFileIfExist(pos);
@@ -8,7 +8,8 @@ import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult;
import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState;
import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState;
import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException;
import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException;
import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage;
import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceResponseMessage;
@@ -178,13 +179,8 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug
waitingTasks.remove(sectionPos);
LOGGER.debug("FullDataSourceResponseMessage " + sectionPos);
CompleteFullDataSource fullDataSource = response.getFullDataSource(sectionPos, level);
// FIXME Add dimension context to request instead
// Check is dimension has been switched - received data may no longer be relevant
if (fullDataSource == null)
throw new CancellationException();
Consumer<ChunkSizedFullDataAccessor> chunkDataConsumer = entry.tracker.getChunkDataConsumer();
// FIXME Why keeping a reference in first place
@@ -193,6 +189,10 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug
fullDataSource.splitIntoChunkSizedAccessors(chunkDataConsumer);
}
catch (InvalidLevelException ignored)
{
// We're too late
}
catch (ChannelException | RateLimitedException e)
{
if (e instanceof RateLimitedException)
@@ -26,7 +26,7 @@ import com.seibel.distanthorizons.core.file.fullDatafile.RemoteFullDataFileHandl
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
import com.seibel.distanthorizons.core.generation.WorldRemoteGenerationQueue;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState;
import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState;
import com.seibel.distanthorizons.core.network.NetworkClient;
import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource;
import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataPartialUpdateMessage;
@@ -19,7 +19,6 @@
package com.seibel.distanthorizons.core.level;
import com.seibel.distanthorizons.core.config.AppliedConfigState;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource;
@@ -28,13 +27,13 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.I
import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile;
import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider;
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
import com.seibel.distanthorizons.core.multiplayer.ServerPlayerState;
import com.seibel.distanthorizons.core.multiplayer.RemotePlayerConnectionHandler;
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState;
import com.seibel.distanthorizons.core.multiplayer.server.RemotePlayerConnectionHandler;
import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource;
import com.seibel.distanthorizons.core.network.NetworkServer;
import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException;
import com.seibel.distanthorizons.core.network.exceptions.InvalidSectionPosException;
import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException;
import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException;
import com.seibel.distanthorizons.core.network.messages.base.CancelMessage;
import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage;
import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceResponseMessage;
@@ -72,7 +71,6 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel
private final LinkedBlockingQueue<IServerPlayerWrapper> worldGenLoopingQueue = new LinkedBlockingQueue<>();
private final ConcurrentMap<DhSectionPos, IncompleteDataSourceEntry> incompleteDataSources = new ConcurrentHashMap<>();
private final ConcurrentMap<Long, IncompleteDataSourceEntry> fullDataRequests = new ConcurrentHashMap<>();
private final AppliedConfigState<Integer> rateLimitConfig = new AppliedConfigState<>(Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit);
public DhServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper, RemotePlayerConnectionHandler remotePlayerConnectionHandler)
@@ -94,10 +92,10 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel
{
this.eventSource.registerHandler(FullDataSourceRequestMessage.class, remotePlayerConnectionHandler.currentLevelOnly(this, (msg, serverPlayerState) ->
{
if (serverPlayerState.pendingFullDataRequests.incrementAndGet() > rateLimitConfig.get())
if (serverPlayerState.pendingFullDataRequests.incrementAndGet() > serverPlayerState.config.getFullDataRequestRateLimit())
{
serverPlayerState.pendingFullDataRequests.decrementAndGet();
msg.sendResponse(new RateLimitedException("Max concurrent requests: " + rateLimitConfig.get()));
msg.sendResponse(new RateLimitedException("Max concurrent requests: " + serverPlayerState.config.getFullDataRequestRateLimit()));
return;
}
@@ -110,7 +108,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel
});
return newEntry;
});
// If this fails, current entry is being drained and need create another one
// If this fails, current entry is being drained and need to create another one
if (entry.requestCollectionSemaphore.tryAcquire())
{
fullDataRequests.put(msg.futureId, entry);
@@ -130,6 +128,12 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel
this.eventSource.registerHandler(FullDataChangeSummaryRequestMessage.class, remotePlayerConnectionHandler.currentLevelOnly(this, (msg, serverPlayerState) ->
{
if (!Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate.get())
{
msg.sendResponse(new RequestRejectedException("Operation is disabled from config."));
return;
}
// Load files and check checksums
HashSet<DhSectionPos> changedPosList = new HashSet<>();
for (Map.Entry<DhSectionPos, Integer> entry : msg.checksums.entrySet())
@@ -226,11 +230,16 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel
if (future == null)
return null;
if (!Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get())
return future;
future.thenAccept(chunkSizedFullDataAccessor ->
{
for (ServerPlayerState serverPlayerState : remotePlayerConnectionHandler.getConnectedPlayers())
{
if (chunk.getChunkPos().distance(new DhChunkPos(serverPlayerState.serverPlayer.getPosition())) <= serverPlayerState.config.renderDistance)
if (serverPlayerState.config.isRealTimeUpdatesEnabled()) continue;
if (chunk.getChunkPos().distance(new DhChunkPos(serverPlayerState.serverPlayer.getPosition())) <= serverPlayerState.config.getRenderDistance())
serverPlayerState.channelContext.writeAndFlush(new FullDataPartialUpdateMessage(chunkSizedFullDataAccessor, this));
}
});
@@ -1,34 +0,0 @@
package com.seibel.distanthorizons.core.multiplayer;
import com.seibel.distanthorizons.core.network.protocol.INetworkObject;
import io.netty.buffer.ByteBuf;
public class MultiplayerConfig implements INetworkObject
{
public int renderDistance;
public int fullDataRequestRateLimit;
@Override
public void encode(ByteBuf out)
{
out.writeInt(this.renderDistance);
out.writeInt(this.fullDataRequestRateLimit);
}
@Override
public void decode(ByteBuf in)
{
this.renderDistance = in.readInt();
this.fullDataRequestRateLimit = in.readInt();
}
@Override public String toString()
{
return "MultiplayerConfig{" +
"renderDistance=" + renderDistance +
", fullDataRequestRateLimit=" + fullDataRequestRateLimit +
'}';
}
}
@@ -1,7 +1,8 @@
package com.seibel.distanthorizons.core.multiplayer;
package com.seibel.distanthorizons.core.multiplayer.client;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig;
import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener;
import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource;
import com.seibel.distanthorizons.core.network.NetworkClient;
import com.seibel.distanthorizons.core.network.messages.base.AckMessage;
@@ -20,6 +21,7 @@ public class ClientNetworkState implements Closeable
private final NetworkClient client;
private final UUID playerUUID;
public MultiplayerConfig config = new MultiplayerConfig();
private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged);
/**
* Returns the client used by this instance. <p>
@@ -31,12 +33,13 @@ public class ClientNetworkState implements Closeable
* Constructs a new instance.
*
* @param networkClient Client to use. It is assumed that this client will be at full control by this instance.
* @param playerUUID UUID of a player connected
* @param playerUUID UUID of a player connected.
*/
public ClientNetworkState(NetworkClient networkClient, UUID playerUUID)
{
this.client = networkClient;
this.playerUUID = playerUUID;
this.registerNetworkHandlers();
this.client.startConnecting();
}
@@ -48,23 +51,28 @@ public class ClientNetworkState implements Closeable
LOGGER.info("Connected to server: "+helloMessage.getChannelContext().channel().remoteAddress());
this.getClient().sendRequest(new PlayerUUIDMessage(playerUUID), AckMessage.class)
.thenCompose(ack -> this.getClient().sendRequest(new RemotePlayerConfigMessage(new MultiplayerConfig()
{{
renderDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistance.get();
fullDataRequestRateLimit = Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit.get();
}}), RemotePlayerConfigMessage.class))
.thenAccept(msg -> {
this.config = msg.payload;
})
.thenAccept(ack -> this.getClient().sendMessage(new RemotePlayerConfigMessage(new MultiplayerConfig())))
.exceptionally(throwable -> {
LOGGER.error("Error while fetching server's config", throwable);
return null;
});
});
this.client.registerHandler(RemotePlayerConfigMessage.class, msg ->
{
LOGGER.info("Connection config was changed: " + msg.payload);
this.config = (MultiplayerConfig) msg.payload;
});
}
private void onConfigChanged()
{
this.getClient().sendMessage(new RemotePlayerConfigMessage(new MultiplayerConfig()));
}
public void close()
{
this.configChangeListener.close();
this.client.close();
}
}
@@ -0,0 +1,23 @@
package com.seibel.distanthorizons.core.multiplayer.config;
import com.seibel.distanthorizons.core.network.protocol.INetworkObject;
import io.netty.buffer.ByteBuf;
public abstract class AbstractMultiplayerConfig implements INetworkObject
{
public abstract int getRenderDistance();
public abstract int getFullDataRequestRateLimit();
public abstract boolean isRealTimeUpdatesEnabled();
public abstract boolean isPostRelogUpdateEnabled();
@Override
public void encode(ByteBuf out)
{
out.writeInt(this.getRenderDistance());
out.writeInt(this.getFullDataRequestRateLimit());
out.writeBoolean(this.isRealTimeUpdatesEnabled());
out.writeBoolean(this.isPostRelogUpdateEnabled());
}
}
@@ -0,0 +1,39 @@
package com.seibel.distanthorizons.core.multiplayer.config;
import com.seibel.distanthorizons.core.config.Config;
import io.netty.buffer.ByteBuf;
public class MultiplayerConfig extends AbstractMultiplayerConfig
{
public int renderDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistance.get();
@Override public int getRenderDistance() { return renderDistance; }
public int fullDataRequestRateLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.requestRateLimit.get();
@Override public int getFullDataRequestRateLimit() { return fullDataRequestRateLimit; }
public boolean realTimeUpdatesEnabled = Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get();
@Override public boolean isRealTimeUpdatesEnabled() { return realTimeUpdatesEnabled; }
public boolean postRelogUpdateEnabled = Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate.get();
@Override public boolean isPostRelogUpdateEnabled() { return postRelogUpdateEnabled; }
@Override
public void decode(ByteBuf in)
{
this.renderDistance = in.readInt();
this.fullDataRequestRateLimit = in.readInt();
this.realTimeUpdatesEnabled = in.readBoolean();
this.postRelogUpdateEnabled = in.readBoolean();
}
@Override public String toString()
{
return "MultiplayerConfig{" +
"renderDistance=" + renderDistance +
", fullDataRequestRateLimit=" + fullDataRequestRateLimit +
", realTimeUpdatesEnabled=" + realTimeUpdatesEnabled +
", postRelogUpdatesEnabled=" + postRelogUpdateEnabled +
'}';
}
}
@@ -0,0 +1,33 @@
package com.seibel.distanthorizons.core.multiplayer.config;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
import java.io.Closeable;
public class MultiplayerConfigChangeListener implements Closeable
{
private final ConfigChangeListener<Integer> renderDistance;
private final ConfigChangeListener<Integer> rateLimit;
private final ConfigChangeListener<Boolean> enableRealTimeUpdates;
private final ConfigChangeListener<Boolean> enablePostRelogUpdate;
public MultiplayerConfigChangeListener(Runnable runnable)
{
renderDistance = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistance, ignored -> runnable.run());
rateLimit = new ConfigChangeListener<>(Config.Client.Advanced.Multiplayer.ServerNetworking.requestRateLimit, ignored -> runnable.run());
enableRealTimeUpdates = new ConfigChangeListener<>(Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates, ignored -> runnable.run());
enablePostRelogUpdate = new ConfigChangeListener<>(Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate, ignored -> runnable.run());
}
@Override
public void close()
{
renderDistance.close();
rateLimit.close();
enableRealTimeUpdates.close();
enablePostRelogUpdate.close();
}
}
@@ -1,14 +1,17 @@
package com.seibel.distanthorizons.core.multiplayer;
package com.seibel.distanthorizons.core.multiplayer.server;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.seibel.distanthorizons.core.level.DhServerLevel;
import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig;
import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener;
import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource;
import com.seibel.distanthorizons.core.network.NetworkServer;
import com.seibel.distanthorizons.core.network.messages.base.ILevelRelatedMessage;
import com.seibel.distanthorizons.core.network.messages.base.AckMessage;
import com.seibel.distanthorizons.core.network.messages.base.CloseEvent;
import com.seibel.distanthorizons.core.network.messages.session.PlayerUUIDMessage;
import com.seibel.distanthorizons.core.network.messages.session.RemotePlayerConfigMessage;
import com.seibel.distanthorizons.core.network.protocol.NetworkMessage;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import io.netty.channel.ChannelHandlerContext;
@@ -26,6 +29,8 @@ public class RemotePlayerConnectionHandler implements Closeable
private final HashMap<UUID, ServerPlayerState> playersByUUID = new HashMap<>();
private final BiMap<ChannelHandlerContext, ServerPlayerState> playersByConnection = HashBiMap.create();
private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged);
public NetworkServer server() { return this.eventSource.parent; }
public RemotePlayerConnectionHandler(NetworkServer networkServer)
@@ -59,6 +64,12 @@ public class RemotePlayerConnectionHandler implements Closeable
playerUUIDMessage.sendResponse(new AckMessage());
});
this.eventSource.registerHandler(RemotePlayerConfigMessage.class, this.connectedPlayersOnly((remotePlayerConfigMessage, serverPlayerState) ->
{
serverPlayerState.config.clientConfig = (MultiplayerConfig) remotePlayerConfigMessage.payload;
serverPlayerState.channelContext.writeAndFlush(new RemotePlayerConfigMessage(serverPlayerState.config));
}));
this.eventSource.registerHandler(CloseEvent.class, closeEvent ->
{
ServerPlayerState dhPlayer = this.playersByConnection.remove(closeEvent.getChannelContext());
@@ -69,6 +80,12 @@ public class RemotePlayerConnectionHandler implements Closeable
});
}
private void onConfigChanged()
{
for (ServerPlayerState serverPlayerState : this.getConnectedPlayers())
serverPlayerState.channelContext.writeAndFlush(new RemotePlayerConfigMessage(serverPlayerState.config));
}
public <T extends NetworkMessage> Consumer<T> connectedPlayersOnly(BiConsumer<T, ServerPlayerState> next)
{
return msg ->
@@ -131,6 +148,7 @@ public class RemotePlayerConnectionHandler implements Closeable
@Override
public void close()
{
this.configChangeListener.close();
this.eventSource.close();
this.server().close();
}
@@ -1,4 +1,4 @@
package com.seibel.distanthorizons.core.multiplayer;
package com.seibel.distanthorizons.core.multiplayer.server;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import io.netty.channel.ChannelHandlerContext;
@@ -8,8 +8,9 @@ import java.util.concurrent.atomic.AtomicInteger;
public class ServerPlayerState
{
public IServerPlayerWrapper serverPlayer;
public MultiplayerConfig config;
public ChannelHandlerContext channelContext;
public ServersideMultiplayerConfig config = new ServersideMultiplayerConfig();
public final AtomicInteger pendingFullDataRequests = new AtomicInteger();
@@ -0,0 +1,44 @@
package com.seibel.distanthorizons.core.multiplayer.server;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.multiplayer.config.AbstractMultiplayerConfig;
import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig;
import io.netty.buffer.ByteBuf;
/**
* Used for constraining the client config to the server config.
*/
public class ServersideMultiplayerConfig extends AbstractMultiplayerConfig
{
public MultiplayerConfig clientConfig = new MultiplayerConfig();
@Override
public int getRenderDistance()
{
return Math.min(clientConfig.renderDistance, Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistance.get());
}
@Override
public int getFullDataRequestRateLimit()
{
return Math.min(clientConfig.fullDataRequestRateLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.requestRateLimit.get());
}
@Override
public boolean isRealTimeUpdatesEnabled()
{
return clientConfig.realTimeUpdatesEnabled && Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get();
}
@Override
public boolean isPostRelogUpdateEnabled() {
return clientConfig.postRelogUpdateEnabled && Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate.get();
}
@Override
public void decode(ByteBuf in)
{
throw new UnsupportedOperationException("Decoding is not supported for server-only class.");
}
}
@@ -26,6 +26,7 @@ import com.seibel.distanthorizons.core.network.messages.base.HelloMessage;
import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage;
import com.seibel.distanthorizons.core.network.protocol.MessageHandler;
import com.seibel.distanthorizons.core.network.protocol.NetworkChannelInitializer;
import com.seibel.distanthorizons.core.network.protocol.NetworkMessage;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
@@ -131,7 +132,7 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable
return;
}
channel.writeAndFlush(new HelloMessage());
sendMessage(new HelloMessage());
ready = true;
});
@@ -168,6 +169,11 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable
});
}
public final void sendMessage(NetworkMessage msg)
{
this.channel.writeAndFlush(msg);
}
public final <TResponse extends FutureTrackableNetworkMessage> CompletableFuture<TResponse> sendRequest(FutureTrackableNetworkMessage msg, Class<TResponse> responseClass)
{
return this.sendRequest(this.channel.pipeline().context(MessageHandler.class), msg, responseClass);
@@ -0,0 +1,10 @@
package com.seibel.distanthorizons.core.network.exceptions;
public class RequestRejectedException extends Exception
{
public RequestRejectedException(String message)
{
super(message);
}
}
@@ -22,6 +22,7 @@ package com.seibel.distanthorizons.core.network.messages.base;
import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException;
import com.seibel.distanthorizons.core.network.exceptions.InvalidSectionPosException;
import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException;
import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException;
import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage;
import io.netty.buffer.ByteBuf;
@@ -36,6 +37,7 @@ public class ExceptionMessage extends FutureTrackableNetworkMessage
add(RateLimitedException.class);
add(InvalidLevelException.class);
add(InvalidSectionPosException.class);
add(RequestRejectedException.class);
}};
public Exception exception;
@@ -73,10 +73,8 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage
this.dataBuffer = in.readBytes(in.readInt());
}
@Nullable
public CompleteFullDataSource getFullDataSource(DhSectionPos pos, IDhLevel level) throws IOException, InterruptedException
{
try (ByteBufInputStream inputStream = new ByteBufInputStream(dataBuffer))
{
return fullDataSourceLoader.loadData(pos, new DhDataInputStream(inputStream), level);
@@ -19,23 +19,24 @@
package com.seibel.distanthorizons.core.network.messages.session;
import com.seibel.distanthorizons.core.multiplayer.MultiplayerConfig;
import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage;
import com.seibel.distanthorizons.core.multiplayer.config.AbstractMultiplayerConfig;
import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig;
import com.seibel.distanthorizons.core.network.protocol.INetworkObject;
import com.seibel.distanthorizons.core.network.protocol.NetworkMessage;
import io.netty.buffer.ByteBuf;
public class RemotePlayerConfigMessage extends FutureTrackableNetworkMessage
public class RemotePlayerConfigMessage extends NetworkMessage
{
public MultiplayerConfig payload;
public AbstractMultiplayerConfig payload;
public RemotePlayerConfigMessage() { }
public RemotePlayerConfigMessage(MultiplayerConfig payload) { this.payload = payload; }
public RemotePlayerConfigMessage(AbstractMultiplayerConfig payload) { this.payload = payload; }
@Override
public void encode0(ByteBuf out) { this.payload.encode(out); }
public void encode(ByteBuf out) { this.payload.encode(out); }
@Override
public void decode0(ByteBuf in) { this.payload = INetworkObject.decodeStatic(new MultiplayerConfig(), in); }
public void decode(ByteBuf in) { this.payload = INetworkObject.decodeStatic(new MultiplayerConfig(), in); }
@Override public String toString()
{
@@ -24,7 +24,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
import com.seibel.distanthorizons.core.level.DhClientLevel;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState;
import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState;
import com.seibel.distanthorizons.core.network.NetworkClient;
import com.seibel.distanthorizons.core.util.ThreadUtil;
import com.seibel.distanthorizons.core.util.objects.EventLoop;
@@ -63,7 +63,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
this.saveStructure = new ClientOnlySaveStructure();
this.levels = new ConcurrentHashMap<>();
if (Config.Client.Advanced.Multiplayer.enableServerNetworking.get())
if (Config.Client.Advanced.Multiplayer.ServerNetworking.enableServerNetworking.get())
{
// TODO server specific configs
NetworkClient networkClient = new NetworkClient(MC_CLIENT.getCurrentServerIp(), 25049);
@@ -19,15 +19,11 @@
package com.seibel.distanthorizons.core.world;
import com.seibel.distanthorizons.core.config.AppliedConfigState;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure;
import com.seibel.distanthorizons.core.level.DhServerLevel;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.multiplayer.ServerPlayerState;
import com.seibel.distanthorizons.core.multiplayer.RemotePlayerConnectionHandler;
import com.seibel.distanthorizons.core.multiplayer.server.RemotePlayerConnectionHandler;
import com.seibel.distanthorizons.core.network.NetworkServer;
import com.seibel.distanthorizons.core.network.messages.session.RemotePlayerConfigMessage;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
@@ -41,10 +37,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
{
private final HashMap<IServerLevelWrapper, DhServerLevel> levels;
public final LocalSaveStructure saveStructure;
private final RemotePlayerConnectionHandler remotePlayerConnectionHandler;
private final AppliedConfigState<Integer> rateLimitConfig = new AppliedConfigState<>(Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit);
//==============//
@@ -61,21 +54,9 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
// TODO move to global payload once server specific configs are implemented
NetworkServer networkServer = new NetworkServer(25049);
this.remotePlayerConnectionHandler = new RemotePlayerConnectionHandler(networkServer);
this.registerNetworkHandlers();
LOGGER.info("Started "+DhServerWorld.class.getSimpleName()+" of type "+this.environment);
}
private void registerNetworkHandlers()
{
this.remotePlayerConnectionHandler.server().registerHandler(RemotePlayerConfigMessage.class, remotePlayerConfigMessage ->
{
this.remotePlayerConnectionHandler.getConnectedPlayer(remotePlayerConfigMessage).config = remotePlayerConfigMessage.payload;
remotePlayerConfigMessage.payload.fullDataRequestRateLimit = Math.min(rateLimitConfig.get(), remotePlayerConfigMessage.payload.fullDataRequestRateLimit);
remotePlayerConfigMessage.sendResponse(remotePlayerConfigMessage);
});
}
@@ -95,8 +76,8 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
}
public void changePlayerLevel(IServerPlayerWrapper player, IServerLevelWrapper origin, IServerLevelWrapper dest)
{
this.getLevel(origin).removePlayer(player);
this.getLevel(dest).addPlayer(player);
this.getLevel(origin).removePlayer(player);
}
@Override
@@ -147,15 +128,6 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
public void serverTick() {
this.levels.values().forEach(DhServerLevel::serverTick);
if (rateLimitConfig.pollNewValue())
{
for (ServerPlayerState serverPlayerState : this.remotePlayerConnectionHandler.getConnectedPlayers())
{
serverPlayerState.config.fullDataRequestRateLimit = rateLimitConfig.get();
serverPlayerState.channelContext.writeAndFlush(new RemotePlayerConfigMessage(serverPlayerState.config));
}
}
}
public void doWorldGen() {