diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index d24e70beb..656108692 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -1,50 +1,44 @@ package com.seibel.distanthorizons.core.file.fullDatafile; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkClient; +import com.seibel.distanthorizons.core.network.future.NetworkRequestTracker; +import com.seibel.distanthorizons.core.network.messages.ChunkRequestMessage; +import com.seibel.distanthorizons.core.network.messages.ChunkResponseMessage; import com.seibel.distanthorizons.core.pos.DhSectionPos; -import io.netty.channel.ChannelHandlerContext; import java.util.concurrent.CompletableFuture; public class RemoteFullDataFileHandler extends FullDataFileHandler { - private final Multimap chunkRequests = HashMultimap.create(); - - public RemoteFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure, ChildNetworkEventSource eventSource) { + private final NetworkClient networkClient; + private final NetworkRequestTracker chunkRequestTracker; + + public RemoteFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure, NetworkClient networkClient) { super(level, saveStructure); - this.registerNetworkHandlers(eventSource); - } - - private void registerNetworkHandlers(ChildNetworkEventSource eventSource) { - //eventSource.registerHandler(); + this.networkClient = networkClient; + this.chunkRequestTracker = new NetworkRequestTracker<>(networkClient, ChunkResponseMessage.class); } @Override public CompletableFuture read(DhSectionPos pos) { // TODO read and force update somehow instead ???? - return super.read(pos).handle((fullDataSource, throwable) -> { - if (fullDataSource == null) { - + return super.read(pos).thenCompose((fullDataSource) -> { + if (fullDataSource != null) { + return CompletableFuture.completedFuture(fullDataSource); } - return fullDataSource; + CompletableFuture responseFuture = chunkRequestTracker.sendRequest(networkClient.getChannel(), new ChunkRequestMessage()); + }); } @Override public void close() { super.close(); - - - } - - private static class ChunkRequest { - + chunkRequestTracker.close(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 367e59444..42a9f8045 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -32,11 +32,11 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel // constructor // //=============// - public DhClientLevel(AbstractSaveStructure saveStructure, IClientLevelWrapper clientLevelWrapper, ChildNetworkEventSource eventSource) + public DhClientLevel(AbstractSaveStructure saveStructure, IClientLevelWrapper clientLevelWrapper, NetworkClient networkClient) { this.levelWrapper = clientLevelWrapper; this.saveStructure = saveStructure; - dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure, eventSource); + dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure, networkClient); clientside = new ClientLevelModule(this); clientside.startRenderer(); LOGGER.info("Started DHLevel for "+this.levelWrapper+" with saves at "+this.saveStructure); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index b35586bdf..468da601b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -134,6 +134,10 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable this.channel.disconnect(); } + public Channel getChannel() { + return channel; + } + @Override public void close() { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/future/IFutureTrackableNetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/future/IFutureTrackableNetworkMessage.java new file mode 100644 index 000000000..be3514034 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/future/IFutureTrackableNetworkMessage.java @@ -0,0 +1,8 @@ +package com.seibel.distanthorizons.core.network.future; + +import com.seibel.distanthorizons.core.network.protocol.INetworkMessage; + +public interface IFutureTrackableNetworkMessage> extends INetworkMessage +{ + TKey getRequestKey(); +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/future/NetworkRequestTracker.java b/core/src/main/java/com/seibel/distanthorizons/core/network/future/NetworkRequestTracker.java new file mode 100644 index 000000000..9eaffcd82 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/future/NetworkRequestTracker.java @@ -0,0 +1,73 @@ +package com.seibel.distanthorizons.core.network.future; + +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; +import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; +import com.seibel.distanthorizons.core.network.NetworkEventSource; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundInvoker; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +public class NetworkRequestTracker + , TKey extends Comparable> + implements AutoCloseable +{ + private final ChildNetworkEventSource eventSource; + + private final Set knownChannels = new HashSet<>(); + private final Table> pendingFutures = HashBasedTable.create(); + + public NetworkRequestTracker( + NetworkEventSource eventSource, + Class responseClass) + { + this.eventSource = new ChildNetworkEventSource<>(eventSource); + registerNetworkHandlers(responseClass); + } + + private void registerNetworkHandlers(Class responseClass) + { + this.eventSource.registerHandler(responseClass, (msg, ctx) -> { + CompletableFuture future = pendingFutures.remove(ctx.channel(), msg.getRequestKey()); + if (future != null) { + future.complete(msg); + } + }); + } + + public CompletableFuture sendRequest(Channel channel, IFutureTrackableNetworkMessage msg) + { + if (knownChannels.add(channel)) + { + channel.closeFuture().addListener(closeFuture -> + { + pendingFutures.row(channel).values().removeIf(responseFuture -> + { + responseFuture.completeExceptionally(closeFuture.cause()); + return true; + }); + }); + } + + CompletableFuture responseFuture = new CompletableFuture<>(); + pendingFutures.put(channel, msg.getRequestKey(), responseFuture); + + channel.writeAndFlush(msg).addListener(writeFuture -> { + if (writeFuture.cause() != null) { + responseFuture.completeExceptionally(writeFuture.cause()); + } + }); + return responseFuture; + } + + @Override public void close() + { + this.eventSource.close(); + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java new file mode 100644 index 000000000..0c9a4d7c5 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java @@ -0,0 +1,24 @@ +package com.seibel.distanthorizons.core.network.messages; + +import com.seibel.distanthorizons.core.network.future.IFutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import io.netty.buffer.ByteBuf; + +public class ChunkRequestMessage implements IFutureTrackableNetworkMessage +{ + public DhSectionPos dhSectionPos; + + @Override public DhSectionPos getRequestKey() { return dhSectionPos; } + + @Override + public void encode(ByteBuf out) + { + + } + + @Override + public void decode(ByteBuf in) + { + + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkResponseMessage.java new file mode 100644 index 000000000..cc2fa1a0b --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkResponseMessage.java @@ -0,0 +1,22 @@ +package com.seibel.distanthorizons.core.network.messages; + +import com.seibel.distanthorizons.core.network.future.IFutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import io.netty.buffer.ByteBuf; + +public class ChunkResponseMessage implements IFutureTrackableNetworkMessage +{ + public DhSectionPos dhSectionPos; + + @Override public DhSectionPos getRequestKey() { return dhSectionPos; } + + @Override public void encode(ByteBuf out) + { + + } + + @Override public void decode(ByteBuf in) + { + + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RequestChunksMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RequestChunksMessage.java deleted file mode 100644 index 15312601e..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RequestChunksMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.seibel.distanthorizons.core.network.messages; - -import com.seibel.distanthorizons.core.network.protocol.INetworkMessage; -import io.netty.buffer.ByteBuf; - -public class RequestChunksMessage implements INetworkMessage -{ - - @Override - public void encode(ByteBuf out) - { - - } - - @Override - public void decode(ByteBuf in) - { - - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java index ea952c4ad..0344e272f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java @@ -29,7 +29,7 @@ public class MessageRegistry this.registerMessage(AckMessage.class, AckMessage::new); this.registerMessage(PlayerUUIDMessage.class, PlayerUUIDMessage::new); this.registerMessage(RemotePlayerConfigMessage.class, RemotePlayerConfigMessage::new); - this.registerMessage(RequestChunksMessage.class, RequestChunksMessage::new); + this.registerMessage(ChunkRequestMessage.class, ChunkRequestMessage::new); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java index ae9edde62..87d87b9bd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java @@ -3,6 +3,7 @@ package com.seibel.distanthorizons.core.pos; import com.seibel.distanthorizons.core.enums.ELodDirection; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import com.seibel.distanthorizons.core.util.LodUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.function.Consumer; @@ -18,7 +19,7 @@ import java.util.function.Consumer; * @author Leetom * @version 2022-11-6 */ -public class DhSectionPos +public class DhSectionPos implements Comparable { /** * The lowest detail level a Section position can hold. @@ -238,6 +239,18 @@ public class DhSectionPos this.sectionZ == that.sectionZ; } + @Override public int compareTo(@NotNull DhSectionPos o) + { + if (this.sectionDetailLevel != o.sectionDetailLevel) + return this.sectionDetailLevel - o.sectionDetailLevel; + if (this.sectionX != o.sectionX) + return this.sectionX - o.sectionX; + if (this.sectionZ != o.sectionZ) + return this.sectionZ - o.sectionZ; + + return 0; + } + @Override public int hashCode() { @@ -245,5 +258,4 @@ public class DhSectionPos Integer.hashCode(this.sectionX) ^ // XOR Integer.hashCode(this.sectionZ); } - } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index 17e9ef3b0..e2fa70bdf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -4,7 +4,6 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.level.DhClientLevel; -import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.network.messages.*; import com.seibel.distanthorizons.core.network.messages.PlayerUUIDMessage; @@ -65,7 +64,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld networkClient.registerAckHandler(RemotePlayerConfigMessage.class, ctx -> { // TODO Actually request chunks - ctx.writeAndFlush(new RequestChunksMessage()); + ctx.writeAndFlush(new ChunkRequestMessage()); }); } @@ -86,7 +85,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld return null; } - return new DhClientLevel(this.saveStructure, clientLevelWrapper, new ChildNetworkEventSource<>(networkClient)); + return new DhClientLevel(this.saveStructure, clientLevelWrapper, networkClient); }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index dbbea1b33..ab76ee2e3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -7,7 +7,7 @@ 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.messages.*; -import com.seibel.distanthorizons.core.network.messages.RequestChunksMessage; +import com.seibel.distanthorizons.core.network.messages.ChunkRequestMessage; import com.seibel.distanthorizons.core.network.objects.RemotePlayer; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; @@ -86,7 +86,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld channelContext.writeAndFlush(new AckMessage(RemotePlayerConfigMessage.class)); }); - this.networkServer.registerHandler(RequestChunksMessage.class, (msg, ctx) -> + this.networkServer.registerHandler(ChunkRequestMessage.class, (msg, ctx) -> { LOGGER.info("RequestChunksMessage"); // hasReceivedChunkRequest should be false somewhere ???