feat: Implement syncing of DhSectionPos for chunk requests/responses

This commit is contained in:
Steveplays28
2023-07-19 16:48:20 +02:00
parent 79408c081a
commit 9a0c9e9b7d
7 changed files with 99 additions and 50 deletions
@@ -10,6 +10,7 @@ import com.seibel.distanthorizons.core.network.messages.ChunkRequestMessage;
import com.seibel.distanthorizons.core.network.messages.ChunkResponseMessage;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import java.nio.file.FileAlreadyExistsException;
import java.util.concurrent.CompletableFuture;
public class RemoteFullDataFileHandler extends FullDataFileHandler
@@ -25,14 +26,11 @@ public class RemoteFullDataFileHandler extends FullDataFileHandler
@Override
public CompletableFuture<IFullDataSource> read(DhSectionPos pos) {
// TODO read and force update somehow instead ????
// TODO: LOD data file updating is probably incomplete
return super.read(pos).thenCompose((fullDataSource) -> {
if (fullDataSource != null) {
return CompletableFuture.completedFuture(fullDataSource);
}
CompletableFuture<ChunkResponseMessage> responseFuture = chunkRequestTracker.sendRequest(networkClient.getChannel(), new ChunkRequestMessage());
FullDataMetaFile metaFile = this.getLoadOrMakeFile(pos, true);
CompletableFuture<ChunkResponseMessage> responseFuture = chunkRequestTracker.sendRequest(networkClient.getChannel(), new ChunkRequestMessage(pos));
return onDataFileUpdate(fullDataSource, metaFile, iFullDataSource -> {}, iFullDataSource -> true);
});
}
@@ -7,17 +7,23 @@ import io.netty.buffer.ByteBuf;
public class ChunkRequestMessage implements IFutureTrackableNetworkMessage<DhSectionPos>
{
public DhSectionPos dhSectionPos;
public ChunkRequestMessage() {}
public ChunkRequestMessage(DhSectionPos dhSectionPos) {
this.dhSectionPos = dhSectionPos;
}
@Override public DhSectionPos getRequestKey() { return dhSectionPos; }
@Override
public void encode(ByteBuf out)
public void encode(ByteBuf out)
{
}
@Override
public void decode(ByteBuf in)
public void decode(ByteBuf in)
{
}
@@ -7,12 +7,18 @@ import io.netty.buffer.ByteBuf;
public class ChunkResponseMessage implements IFutureTrackableNetworkMessage<DhSectionPos>
{
public DhSectionPos dhSectionPos;
public ChunkResponseMessage() {}
public ChunkResponseMessage(DhSectionPos dhSectionPos) {
this.dhSectionPos = dhSectionPos;
}
@Override public DhSectionPos getRequestKey() { return dhSectionPos; }
@Override public void encode(ByteBuf out)
{
}
@Override public void decode(ByteBuf in)
@@ -1,8 +1,10 @@
package com.seibel.distanthorizons.core.pos;
import com.seibel.distanthorizons.core.enums.ELodDirection;
import com.seibel.distanthorizons.core.network.protocol.INetworkObject;
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
import io.netty.buffer.ByteBuf;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -19,7 +21,7 @@ import java.util.function.Consumer;
* @author Leetom
* @version 2022-11-6
*/
public class DhSectionPos implements Comparable<DhSectionPos>
public class DhSectionPos implements Comparable<DhSectionPos>, INetworkObject
{
/**
* The lowest detail level a Section position can hold.
@@ -32,12 +34,12 @@ public class DhSectionPos implements Comparable<DhSectionPos>
public final static byte SECTION_REGION_DETAIL_LEVEL = SECTION_MINIMUM_DETAIL_LEVEL + LodUtil.REGION_DETAIL_LEVEL;
public final byte sectionDetailLevel;
public byte sectionDetailLevel;
/** in a sectionDetailLevel grid */
public final int sectionX;
public int sectionX;
/** in a sectionDetailLevel grid */
public final int sectionZ;
public int sectionZ;
@@ -258,4 +260,18 @@ public class DhSectionPos implements Comparable<DhSectionPos>
Integer.hashCode(this.sectionX) ^ // XOR
Integer.hashCode(this.sectionZ);
}
@Override
public void encode(ByteBuf out) {
out.writeByte(this.sectionDetailLevel);
out.writeInt(this.sectionX);
out.writeInt(this.sectionZ);
}
@Override
public void decode(ByteBuf in) {
this.sectionDetailLevel = in.readByte();
this.sectionX = in.readInt();
this.sectionZ = in.readInt();
}
}
@@ -9,6 +9,8 @@ import com.seibel.distanthorizons.core.network.messages.*;
import com.seibel.distanthorizons.core.network.messages.PlayerUUIDMessage;
import com.seibel.distanthorizons.core.network.messages.RemotePlayerConfigMessage;
import com.seibel.distanthorizons.core.network.objects.RemotePlayer;
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.util.ThreadUtil;
import com.seibel.distanthorizons.core.util.objects.EventLoop;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
@@ -64,7 +66,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
networkClient.registerAckHandler(RemotePlayerConfigMessage.class, ctx -> {
// TODO Actually request chunks
ctx.writeAndFlush(new ChunkRequestMessage());
ctx.writeAndFlush(new ChunkRequestMessage(new DhSectionPos(new DhBlockPos2D(0, 0))));
});
}
@@ -2,6 +2,8 @@ package com.seibel.distanthorizons.core.world;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile;
import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider;
import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure;
import com.seibel.distanthorizons.core.level.DhServerLevel;
import com.seibel.distanthorizons.core.level.IDhLevel;
@@ -9,6 +11,7 @@ import com.seibel.distanthorizons.core.network.NetworkServer;
import com.seibel.distanthorizons.core.network.messages.*;
import com.seibel.distanthorizons.core.network.messages.ChunkRequestMessage;
import com.seibel.distanthorizons.core.network.objects.RemotePlayer;
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
@@ -16,6 +19,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import io.netty.channel.ChannelHandlerContext;
import java.io.File;
import java.nio.file.FileAlreadyExistsException;
import java.util.HashMap;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@@ -24,32 +28,32 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
{
private final HashMap<IServerLevelWrapper, DhServerLevel> levels;
public final LocalSaveStructure saveStructure;
private final NetworkServer networkServer;
private final HashMap<UUID, RemotePlayer> playersByUUID;
private final BiMap<ChannelHandlerContext, RemotePlayer> playersByConnection;
public DhServerWorld()
{
super(EWorldEnvironment.Server_Only);
this.saveStructure = new LocalSaveStructure();
this.levels = new HashMap<>();
// 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();
LOGGER.info("Started "+DhServerWorld.class.getSimpleName()+" of type "+this.environment);
}
private void registerNetworkHandlers()
{
this.networkServer.registerHandler(CloseMessage.class, (closeMessage, channelContext) ->
this.networkServer.registerHandler(CloseMessage.class, (closeMessage, channelContext) ->
{
RemotePlayer dhPlayer = this.playersByConnection.remove(channelContext);
if (dhPlayer != null)
@@ -57,43 +61,57 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
dhPlayer.channelContext = null;
}
});
this.networkServer.registerHandler(PlayerUUIDMessage.class, (playerUUIDMessage, channelContext) ->
this.networkServer.registerHandler(PlayerUUIDMessage.class, (playerUUIDMessage, channelContext) ->
{
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);
channelContext.writeAndFlush(new AckMessage(PlayerUUIDMessage.class));
});
this.networkServer.registerHandler(RemotePlayerConfigMessage.class, (dhRemotePlayerConfigMessage, channelContext) ->
this.networkServer.registerHandler(RemotePlayerConfigMessage.class, (dhRemotePlayerConfigMessage, channelContext) ->
{
// TODO Take notice of received payload and possibly echo back a constrained version
channelContext.writeAndFlush(new AckMessage(RemotePlayerConfigMessage.class));
});
this.networkServer.registerHandler(ChunkRequestMessage.class, (msg, ctx) ->
{
LOGGER.info("RequestChunksMessage");
if (msg.dhSectionPos == null) {
LOGGER.warn("RequestChunksMessage received with null msg.dhSectionPos");
return;
}
LOGGER.info("RequestChunksMessage received at pos ({}, {}) with detail level {}", msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ, msg.dhSectionPos.sectionDetailLevel);
// hasReceivedChunkRequest should be false somewhere ???
// to avoid sending updates until client says at least something about its state
// TODO: Get level from the player that sent the packet
DhServerLevel level = this.getLevel(playersByConnection.get(ctx).serverPlayer.getLevel());
// TODO: Add level to packet
level.serverside.worldGenTick(new DhBlockPos2D(msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ));
// Send chunk response message back
ctx.writeAndFlush(new ChunkResponseMessage(msg.dhSectionPos));
});
}
public void addPlayer(IServerPlayerWrapper serverPlayer)
{
this.playersByUUID.put(serverPlayer.getUUID(), new RemotePlayer(serverPlayer));
@@ -107,7 +125,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
this.networkServer.disconnectClient(channelContext, "You are being disconnected.");
}
}
@Override
public DhServerLevel getOrLoadLevel(ILevelWrapper wrapper)
{
@@ -115,7 +133,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
{
return null;
}
return this.levels.computeIfAbsent((IServerLevelWrapper) wrapper, (serverLevelWrapper) ->
{
File levelFile = this.saveStructure.getLevelFolder(wrapper);
@@ -123,7 +141,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
return new DhServerLevel(this.saveStructure, serverLevelWrapper);
});
}
@Override
public DhServerLevel getLevel(ILevelWrapper wrapper)
{
@@ -131,13 +149,13 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
{
return null;
}
return this.levels.get(wrapper);
}
@Override
public Iterable<? extends IDhLevel> getAllLoadedLevels() { return this.levels.values(); }
@Override
public void unloadLevel(ILevelWrapper wrapper)
{
@@ -145,24 +163,24 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
{
return;
}
if (this.levels.containsKey(wrapper))
{
LOGGER.info("Unloading level {} ", this.levels.get(wrapper));
this.levels.remove(wrapper).close();
}
}
public void serverTick() { this.levels.values().forEach(DhServerLevel::serverTick); }
public void doWorldGen() { this.levels.values().forEach(DhServerLevel::doWorldGen); }
@Override
public CompletableFuture<Void> saveAndFlush()
{
return CompletableFuture.allOf(this.levels.values().stream().map(DhServerLevel::saveAsync).toArray(CompletableFuture[]::new));
}
@Override
public void close()
{
@@ -173,9 +191,9 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
LOGGER.info("Unloading level " + level.getLevelWrapper().getDimensionType().getDimensionName());
level.close();
}
this.levels.clear();
LOGGER.info("Closed DhWorld of type "+this.environment);
}
}
@@ -1,10 +1,13 @@
package com.seibel.distanthorizons.core.wrapperInterfaces.misc;
import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import java.util.UUID;
public interface IServerPlayerWrapper extends IDhApiUnsafeWrapper
public interface IServerPlayerWrapper extends IDhApiUnsafeWrapper
{
UUID getUUID();
IServerLevelWrapper getLevel();
}