Attempt to fix dimension switching

This commit is contained in:
s809
2023-08-01 17:04:40 +05:00
parent 781b588980
commit 2251cd4c25
9 changed files with 277 additions and 185 deletions
@@ -171,5 +171,14 @@ public class ServerApi
((DhServerWorld) serverWorld).removePlayer(player);
}
}
public void serverPlayerLevelChangeEvent(IServerPlayerWrapper player, IServerLevelWrapper origin, IServerLevelWrapper dest)
{
IDhServerWorld serverWorld = SharedApi.getIDhServerWorld();
if (serverWorld instanceof DhServerWorld) // TODO add support for DhClientServerWorld's (lan worlds) as well
{
LOGGER.debug("Player changed level: " + player.getUUID());
((DhServerWorld) serverWorld).changePlayerLevel(player, origin, dest);
}
}
}
@@ -24,7 +24,7 @@ import io.netty.channel.ChannelException;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -84,17 +84,19 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue
private void sendNewRequest(DhBlockPos2D targetPos)
{
DhSectionPos sectionPos = waitingTasks.keySet().stream().reduce(null, (a, b)
if (!pendingTasksSemaphore.tryAcquire())
return;
DhSectionPos sectionPos = Objects.requireNonNull(waitingTasks.keySet().stream().reduce(null, (a, b)
-> a != null
&& a.getCenter().getCenterBlockPos().distSquared(targetPos)
< b.getCenter().getCenterBlockPos().distSquared(targetPos)
? a : b);
? a : b));
WorldGenQueueEntry entry = waitingTasks.remove(sectionPos);
pendingTasksSemaphore.acquireUninterruptibly();
eventSource.parent.<FullDataSourceResponseMessage>sendRequest(new FullDataSourceRequestMessage(sectionPos))
.handle((response, throwable) -> {
.handle((response, throwable) ->
{
pendingTasksSemaphore.release();
finishedRequests.incrementAndGet();
@@ -132,7 +134,8 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue
finishedRequests.decrementAndGet();
waitingTasks.put(sectionPos, entry);
}
catch (ChannelException e) {
catch (ChannelException e)
{
finishedRequests.decrementAndGet();
waitingTasks.put(sectionPos, entry);
}
@@ -88,8 +88,8 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel
public void doWorldGen()
{
worldGeneratorEnabledConfig.pollNewValue();
boolean isClientWorking = networkClient != null && networkClient.isWorking();
boolean shouldDoWorldGen = worldGeneratorEnabledConfig.get() && isClientWorking && clientside.isRendering();
boolean isClientUsable = networkClient != null && !networkClient.isClosed();
boolean shouldDoWorldGen = worldGeneratorEnabledConfig.get() && isClientUsable && clientside.isRendering();
boolean isWorldGenRunning = worldGenModule.isWorldGenRunning();
if (shouldDoWorldGen && !isWorldGenRunning)
{
@@ -1,26 +1,53 @@
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;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource;
import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataFileHandler;
import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider;
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
import com.seibel.distanthorizons.core.multiplayer.RemotePlayer;
import com.seibel.distanthorizons.core.multiplayer.RemotePlayerConnectionHandler;
import com.seibel.distanthorizons.core.network.ChildNetworkEventSource;
import com.seibel.distanthorizons.core.network.NetworkServer;
import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException;
import com.seibel.distanthorizons.core.network.messages.FullDataSourceRequestMessage;
import com.seibel.distanthorizons.core.network.messages.FullDataSourceResponseMessage;
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
import com.seibel.distanthorizons.core.pos.DhLodPos;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.world.DhServerWorld;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import com.seibel.distanthorizons.coreapi.util.math.Vec3d;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.CompletableFuture;
import javax.annotation.CheckForNull;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.*;
public class DhServerLevel extends DhLevel implements IDhServerLevel
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public final ServerLevelModule serverside;
private final IServerLevelWrapper serverLevelWrapper;
public DhServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper)
private final RemotePlayerConnectionHandler remotePlayerConnectionHandler;
private final ChildNetworkEventSource<NetworkServer> eventSource;
private final ConcurrentLinkedQueue<IServerPlayerWrapper> worldGenLoopingQueue = new ConcurrentLinkedQueue<>();
private final ConcurrentMap<DhSectionPos, IncompleteDataSourceEntry> incompleteDataSources = new ConcurrentHashMap<>();
private final AppliedConfigState<Integer> rateLimitConfig = new AppliedConfigState<>(Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit);
public DhServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper, RemotePlayerConnectionHandler remotePlayerConnectionHandler)
{
if (saveStructure.getFullDataFolder(serverLevelWrapper).mkdirs())
{
@@ -29,11 +56,97 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel
this.serverLevelWrapper = serverLevelWrapper;
serverside = new ServerLevelModule(this, serverLevelWrapper, saveStructure);
LOGGER.info("Started DHLevel for {} with saves at {}", serverLevelWrapper, saveStructure);
this.remotePlayerConnectionHandler = remotePlayerConnectionHandler;
this.eventSource = new ChildNetworkEventSource<>(remotePlayerConnectionHandler.eventSource);
this.registerNetworkHandlers();
}
private void registerNetworkHandlers()
{
this.eventSource.registerHandler(FullDataSourceRequestMessage.class, msg ->
{
RemotePlayer remotePlayer = remotePlayerConnectionHandler.getConnectedPlayer(msg);
if (remotePlayer.serverPlayer.getLevel() != this.serverLevelWrapper)
return;
LOGGER.info("FullDataSourceRequestMessage received at pos ({}, {}) with detail level {}", msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ, msg.dhSectionPos.sectionDetailLevel);
if (remotePlayer.pendingFullDataRequests.incrementAndGet() > rateLimitConfig.get())
{
remotePlayer.pendingFullDataRequests.decrementAndGet();
msg.sendResponse(new RateLimitedException("Max concurrent requests: "+rateLimitConfig.get()));
return;
}
while (true)
{
IncompleteDataSourceEntry entry = incompleteDataSources.computeIfAbsent(msg.dhSectionPos, pos -> {
IncompleteDataSourceEntry newEntry = new IncompleteDataSourceEntry();
serverside.dataFileHandler.read(msg.dhSectionPos).thenAccept(fullDataSource -> {
newEntry.fullDataSource = fullDataSource;
});
return newEntry;
});
// If this fails, current entry is being drained and need create another one
if (entry.requestCollectionSemaphore.tryAcquire())
{
entry.requestMessages.add(msg);
entry.requestCollectionSemaphore.release();
break;
}
}
});
}
public void addPlayer(IServerPlayerWrapper serverPlayer)
{
synchronized (worldGenLoopingQueue)
{
this.worldGenLoopingQueue.add(serverPlayer);
}
}
public void removePlayer(IServerPlayerWrapper serverPlayer)
{
synchronized (worldGenLoopingQueue)
{
this.worldGenLoopingQueue.remove(serverPlayer);
}
}
public void serverTick()
{
chunkToLodBuilder.tick();
for (Iterator<IncompleteDataSourceEntry> it = incompleteDataSources.values().iterator(); it.hasNext(); )
{
IncompleteDataSourceEntry entry = it.next();
if (entry.fullDataSource == null) continue;
if (entry.fullDataSource instanceof IIncompleteFullDataSource)
{
IIncompleteFullDataSource incompleteSource = (IIncompleteFullDataSource) entry.fullDataSource;
if (!incompleteSource.hasBeenPromoted()) continue;
entry.fullDataSource = incompleteSource.tryPromotingToCompleteDataSource();
}
LodUtil.assertTrue(entry.fullDataSource instanceof CompleteFullDataSource, "Invalid full data source");
it.remove();
// This semaphore is intentionally acquired forever
entry.requestCollectionSemaphore.acquireUninterruptibly(Short.MAX_VALUE);
CompleteFullDataSource completeSource = (CompleteFullDataSource) entry.fullDataSource;
for (FullDataSourceRequestMessage msg : entry.requestMessages)
{
RemotePlayer remotePlayer = remotePlayerConnectionHandler.getConnectedPlayer(msg);
if (remotePlayer == null) continue;
remotePlayer.pendingFullDataRequests.decrementAndGet();
msg.sendResponse(new FullDataSourceResponseMessage(completeSource, this));
}
}
}
@Override
@@ -77,14 +190,20 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel
// stop world gen
serverside.worldGenModule.stopWorldGen(serverside.dataFileHandler);
}
}
public void doWorldGen(DhBlockPos2D pos) {
this.doWorldGen();
if (serverside.worldGenModule.isWorldGenRunning())
{
serverside.worldGenModule.worldGenTick(pos);
IServerPlayerWrapper firstPlayer;
synchronized (worldGenLoopingQueue)
{
firstPlayer = this.worldGenLoopingQueue.poll();
if (firstPlayer == null)
return;
this.worldGenLoopingQueue.add(firstPlayer);
}
Vec3d position = firstPlayer.getPosition();
serverside.worldGenModule.worldGenTick(new DhBlockPos2D((int) position.x, (int) position.z));
}
}
@@ -106,4 +225,12 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel
public void onWorldGenTaskComplete(DhSectionPos pos) {
//TODO: Send packet to client
}
private static class IncompleteDataSourceEntry
{
@CheckForNull
public IFullDataSource fullDataSource;
public final Set<FullDataSourceRequestMessage> requestMessages = ConcurrentHashMap.newKeySet();
public final Semaphore requestCollectionSemaphore = new Semaphore(Short.MAX_VALUE, true);
}
}
@@ -0,0 +1,89 @@
package com.seibel.distanthorizons.core.multiplayer;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.seibel.distanthorizons.core.network.ChildNetworkEventSource;
import com.seibel.distanthorizons.core.network.NetworkServer;
import com.seibel.distanthorizons.core.network.messages.AckMessage;
import com.seibel.distanthorizons.core.network.messages.CloseMessage;
import com.seibel.distanthorizons.core.network.messages.PlayerUUIDMessage;
import com.seibel.distanthorizons.core.network.protocol.NetworkMessage;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import io.netty.channel.ChannelHandlerContext;
import java.util.HashMap;
import java.util.UUID;
public class RemotePlayerConnectionHandler
{
public final ChildNetworkEventSource<NetworkServer> eventSource;
private final HashMap<UUID, RemotePlayer> playersByUUID = new HashMap<>();
private final BiMap<ChannelHandlerContext, RemotePlayer> playersByConnection = HashBiMap.create();
public RemotePlayerConnectionHandler(NetworkServer networkServer)
{
this.eventSource = new ChildNetworkEventSource<>(networkServer);
this.registerNetworkHandlers();
}
private void registerNetworkHandlers()
{
this.eventSource.registerHandler(PlayerUUIDMessage.class, playerUUIDMessage ->
{
ChannelHandlerContext channelContext = playerUUIDMessage.getChannelContext();
RemotePlayer dhPlayer = this.playersByUUID.get(playerUUIDMessage.playerUUID);
if (dhPlayer == null)
{
this.eventSource.parent.disconnectClient(channelContext, "Player is not logged in.");
return;
}
if (dhPlayer.channelContext != null)
{
this.eventSource.parent.disconnectClient(channelContext, "Another connection is already in use.");
return;
}
dhPlayer.channelContext = channelContext;
this.playersByConnection.put(channelContext, dhPlayer);
playerUUIDMessage.sendResponse(new AckMessage());
});
this.eventSource.registerHandler(CloseMessage.class, closeMessage ->
{
RemotePlayer dhPlayer = this.playersByConnection.remove(closeMessage.getChannelContext());
if (dhPlayer != null)
{
dhPlayer.channelContext = null;
}
});
}
public Iterable<RemotePlayer> getConnectedPlayers()
{
return playersByConnection.values();
}
public RemotePlayer getConnectedPlayer(NetworkMessage msg)
{
return playersByConnection.get(msg.getChannelContext());
}
public void mcPlayerJoined(IServerPlayerWrapper serverPlayer)
{
this.playersByUUID.put(serverPlayer.getUUID(), new RemotePlayer(serverPlayer));
}
public void mcPlayerLeft(IServerPlayerWrapper serverPlayer)
{
RemotePlayer dhPlayer = this.playersByUUID.remove(serverPlayer.getUUID());
ChannelHandlerContext channelContext = this.playersByConnection.inverse().remove(dhPlayer);
if (channelContext != null)
{
this.eventSource.parent.disconnectClient(channelContext, "You are being disconnected.");
}
}
}
@@ -14,6 +14,10 @@ public final class ChildNetworkEventSource<TParent extends NetworkEventSource> e
{
this.parent = parent;
}
public ChildNetworkEventSource(ChildNetworkEventSource<TParent> child)
{
this.parent = child.parent;
}
@Override
public <T extends NetworkMessage> void registerHandler(Class<T> handlerClass, Consumer<T> handlerImplementation)
@@ -7,7 +7,6 @@ import com.seibel.distanthorizons.core.network.messages.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;
@@ -26,18 +25,13 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable
private enum EConnectionState
{
NOT_CONNECTING,
INITIAL,
OPEN,
RECONNECT,
RECONNECT_FORCE,
CLOSE_WAIT,
CLOSED
}
private static final Set<EConnectionState> workingStates = EnumSet.of(
EConnectionState.OPEN,
EConnectionState.RECONNECT,
EConnectionState.RECONNECT_FORCE
);
private static final Set<EConnectionState> closedStates = EnumSet.of(
EConnectionState.CLOSE_WAIT,
EConnectionState.CLOSED
@@ -50,9 +44,7 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable
private final InetSocketAddress address;
/** Indicates whether the client is initialized and not started connecting yet. */
public boolean isNotConnecting() { return this.connectionState == EConnectionState.NOT_CONNECTING; }
/** Indicates whether the client is working or in auto-recoverable state. */
public boolean isWorking() { return workingStates.contains(this.connectionState); }
public boolean isInitialState() { return this.connectionState == EConnectionState.INITIAL; }
/** Indicates whether the client is closed(-ing) and should not be used. */
public boolean isClosed() { return closedStates.contains(this.connectionState); }
private boolean isReady;
@@ -66,7 +58,7 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new NetworkChannelInitializer(new MessageHandler(this::handleMessage)));
private EConnectionState connectionState = EConnectionState.NOT_CONNECTING;
private EConnectionState connectionState = EConnectionState.INITIAL;
private Channel channel;
private int reconnectAttempts = FAILURE_RECONNECT_ATTEMPTS;
@@ -98,7 +90,7 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable
public void startConnecting()
{
if (!isNotConnecting()) return;
if (!isInitialState()) return;
this.connect();
}
@@ -143,9 +143,9 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
public void clientTick() { this.eventLoop.tick(); }
public void doWorldGen() {
if (networkClient != null && networkClient.isNotConnecting())
networkClient.startConnecting();
this.levels.values().forEach(DhClientLevel::doWorldGen);
if (networkClient != null && networkClient.isInitialState())
networkClient.startConnecting();
}
@Override
@@ -1,33 +1,22 @@
package com.seibel.distanthorizons.core.world;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.seibel.distanthorizons.core.config.AppliedConfigState;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource;
import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataFileHandler;
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.network.NetworkServer;
import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException;
import com.seibel.distanthorizons.core.network.messages.*;
import com.seibel.distanthorizons.core.multiplayer.RemotePlayer;
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.multiplayer.RemotePlayerConnectionHandler;
import com.seibel.distanthorizons.core.network.NetworkServer;
import com.seibel.distanthorizons.core.network.messages.RemotePlayerConfigMessage;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import com.seibel.distanthorizons.coreapi.util.math.Vec3d;
import io.netty.channel.ChannelHandlerContext;
import javax.annotation.CheckForNull;
import java.io.File;
import java.util.*;
import java.util.concurrent.*;
import java.util.HashMap;
import java.util.concurrent.CompletableFuture;
public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
{
@@ -35,14 +24,11 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
public final LocalSaveStructure saveStructure;
private final NetworkServer networkServer;
private final HashMap<UUID, RemotePlayer> playersByUUID;
private final BiMap<ChannelHandlerContext, RemotePlayer> playersByConnection;
private final ConcurrentLinkedQueue<IServerPlayerWrapper> worldGenLoopingQueue = new ConcurrentLinkedQueue<>();
private final ConcurrentMap<DhSectionPos, IncompleteDataSourceEntry> incompleteDataSources = new ConcurrentHashMap<>();
private final RemotePlayerConnectionHandler remotePlayerConnectionHandler;
private final AppliedConfigState<Integer> rateLimitConfig = new AppliedConfigState<>(Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit);
public DhServerWorld()
{
super(EWorldEnvironment.Server_Only);
@@ -52,103 +38,35 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
// TODO move to global payload once server specific configs are implemented
this.networkServer = new NetworkServer(25049);
this.playersByUUID = new HashMap<>();
this.playersByConnection = HashBiMap.create();
this.registerNetworkHandlers();
this.remotePlayerConnectionHandler = new RemotePlayerConnectionHandler(networkServer);
LOGGER.info("Started "+DhServerWorld.class.getSimpleName()+" of type "+this.environment);
}
private void registerNetworkHandlers()
{
this.networkServer.registerHandler(CloseMessage.class, closeMessage ->
{
RemotePlayer dhPlayer = this.playersByConnection.remove(closeMessage.getChannelContext());
if (dhPlayer != null)
{
dhPlayer.channelContext = null;
}
});
this.networkServer.registerHandler(PlayerUUIDMessage.class, playerUUIDMessage ->
{
ChannelHandlerContext channelContext = playerUUIDMessage.getChannelContext();
RemotePlayer dhPlayer = this.playersByUUID.get(playerUUIDMessage.playerUUID);
if (dhPlayer == null)
{
this.networkServer.disconnectClient(channelContext, "Player is not logged in.");
return;
}
if (dhPlayer.channelContext != null)
{
this.networkServer.disconnectClient(channelContext, "Another connection is already in use.");
return;
}
dhPlayer.channelContext = channelContext;
this.playersByConnection.put(channelContext, dhPlayer);
playerUUIDMessage.sendResponse(new AckMessage());
});
this.networkServer.registerHandler(RemotePlayerConfigMessage.class, remotePlayerConfigMessage ->
{
remotePlayerConfigMessage.payload.fullDataRequestRateLimit = Math.min(rateLimitConfig.get(), remotePlayerConfigMessage.payload.fullDataRequestRateLimit);
remotePlayerConfigMessage.sendResponse(remotePlayerConfigMessage);
});
// This should be at DhServerLevel I guess
this.networkServer.registerHandler(FullDataSourceRequestMessage.class, msg ->
{
LOGGER.info("FullDataSourceRequestMessage received at pos ({}, {}) with detail level {}", msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ, msg.dhSectionPos.sectionDetailLevel);
RemotePlayer remotePlayer = playersByConnection.get(msg.getChannelContext());
if (remotePlayer.pendingFullDataRequests.incrementAndGet() > rateLimitConfig.get())
{
remotePlayer.pendingFullDataRequests.decrementAndGet();
msg.sendResponse(new RateLimitedException("Max concurrent requests: "+rateLimitConfig.get()));
return;
}
DhServerLevel level = this.getLevel(remotePlayer.serverPlayer.getLevel());
GeneratedFullDataFileHandler handler = level.serverside.dataFileHandler;
while (true)
{
IncompleteDataSourceEntry entry = incompleteDataSources.computeIfAbsent(msg.dhSectionPos, pos -> {
IncompleteDataSourceEntry newEntry = new IncompleteDataSourceEntry();
handler.read(msg.dhSectionPos).thenAccept(fullDataSource -> {
newEntry.fullDataSource = fullDataSource;
});
return newEntry;
});
// If this fails, current entry is being drained and need create another one
if (entry.requestCollectionSemaphore.tryAcquire())
{
entry.requestMessages.add(msg);
entry.requestCollectionSemaphore.release();
break;
}
}
});
}
public void addPlayer(IServerPlayerWrapper serverPlayer)
{
this.playersByUUID.put(serverPlayer.getUUID(), new RemotePlayer(serverPlayer));
this.worldGenLoopingQueue.add(serverPlayer);
this.remotePlayerConnectionHandler.mcPlayerJoined(serverPlayer);
this.getLevel(serverPlayer.getLevel()).addPlayer(serverPlayer);
}
public void removePlayer(IServerPlayerWrapper serverPlayer)
{
this.worldGenLoopingQueue.remove(serverPlayer);
RemotePlayer dhPlayer = this.playersByUUID.remove(serverPlayer.getUUID());
ChannelHandlerContext channelContext = this.playersByConnection.inverse().remove(dhPlayer);
if (channelContext != null)
{
this.networkServer.disconnectClient(channelContext, "You are being disconnected.");
}
this.getLevel(serverPlayer.getLevel()).removePlayer(serverPlayer);
this.remotePlayerConnectionHandler.mcPlayerLeft(serverPlayer);
}
public void changePlayerLevel(IServerPlayerWrapper player, IServerLevelWrapper origin, IServerLevelWrapper dest)
{
this.getLevel(origin).removePlayer(player);
this.getLevel(dest).addPlayer(player);
}
@Override
@@ -163,7 +81,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
{
File levelFile = this.saveStructure.getLevelFolder(wrapper);
LodUtil.assertTrue(levelFile != null);
return new DhServerLevel(this.saveStructure, serverLevelWrapper);
return new DhServerLevel(this.saveStructure, serverLevelWrapper, this.remotePlayerConnectionHandler);
});
}
@@ -201,58 +119,16 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
if (rateLimitConfig.pollNewValue())
{
for (RemotePlayer remotePlayer : playersByConnection.values())
for (RemotePlayer remotePlayer : this.remotePlayerConnectionHandler.getConnectedPlayers())
{
remotePlayer.payload.fullDataRequestRateLimit = rateLimitConfig.get();
remotePlayer.channelContext.writeAndFlush(new RemotePlayerConfigMessage(remotePlayer.payload));
}
}
for (Iterator<IncompleteDataSourceEntry> it = incompleteDataSources.values().iterator(); it.hasNext(); )
{
IncompleteDataSourceEntry entry = it.next();
if (entry.fullDataSource == null) continue;
if (entry.fullDataSource instanceof IIncompleteFullDataSource)
{
IIncompleteFullDataSource incompleteSource = (IIncompleteFullDataSource) entry.fullDataSource;
if (!incompleteSource.hasBeenPromoted()) continue;
entry.fullDataSource = incompleteSource.tryPromotingToCompleteDataSource();
}
LodUtil.assertTrue(entry.fullDataSource instanceof CompleteFullDataSource, "Invalid full data source");
it.remove();
// This semaphore is intentionally acquired forever
entry.requestCollectionSemaphore.acquireUninterruptibly(Short.MAX_VALUE);
CompleteFullDataSource completeSource = (CompleteFullDataSource) entry.fullDataSource;
for (FullDataSourceRequestMessage msg : entry.requestMessages)
{
RemotePlayer remotePlayer = playersByConnection.get(msg.getChannelContext());
if (remotePlayer == null) continue;
remotePlayer.pendingFullDataRequests.decrementAndGet();
DhServerLevel level = this.getLevel(remotePlayer.serverPlayer.getLevel());
msg.sendResponse(new FullDataSourceResponseMessage(completeSource, level));
}
}
}
public void doWorldGen() {
this.levels.values().forEach(level -> {
// TODO Deal with dimensions and dimension switches
IServerPlayerWrapper firstPlayer = this.worldGenLoopingQueue.poll();
if (firstPlayer == null) {
level.doWorldGen();
return;
}
this.worldGenLoopingQueue.add(firstPlayer);
Vec3d position = firstPlayer.getPosition();
level.doWorldGen(new DhBlockPos2D((int) position.x, (int) position.z));
});
this.levels.values().forEach(DhServerLevel::doWorldGen);
}
@Override
@@ -275,13 +151,5 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
this.levels.clear();
LOGGER.info("Closed DhWorld of type "+this.environment);
}
private static class IncompleteDataSourceEntry
{
@CheckForNull
public IFullDataSource fullDataSource;
public final Set<FullDataSourceRequestMessage> requestMessages = ConcurrentHashMap.newKeySet();
public final Semaphore requestCollectionSemaphore = new Semaphore(Short.MAX_VALUE, true);
}
}