diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java index 343af2b65..9375da336 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java @@ -35,7 +35,8 @@ public abstract class AbstractPresetConfigEventHandler this.onConfigUiClosed()); + if (configGui != null) + configGui.addOnScreenChangeListener(() -> this.onConfigUiClosed()); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java index 14fe953bb..99558e014 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java @@ -10,6 +10,7 @@ import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.FullDataPointUtil; @@ -23,6 +24,7 @@ import org.apache.logging.log4j.Logger; import javax.annotation.CheckForNull; import java.io.*; +import java.util.function.Consumer; /** * This data source contains every datapoint over its given {@link DhSectionPos}. @@ -368,6 +370,27 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu } } + public void splitIntoChunkSizedAccessors(Consumer consumer) + { + LodUtil.assertTrue(sectionPos.sectionDetailLevel == DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, "Data source detail level must be at block detail level."); + + sectionPos.forEachChildAtLevel(LodUtil.CHUNK_DETAIL_LEVEL, childPos -> { + ChunkSizedFullDataAccessor accessor = new ChunkSizedFullDataAccessor(new DhChunkPos(childPos.sectionX, childPos.sectionZ)); + + int detailLevelDifference = sectionPos.sectionDetailLevel - childPos.sectionDetailLevel; + int childRelativeX = childPos.sectionX - sectionPos.sectionX * BitShiftUtil.powerOfTwo(detailLevelDifference); + int childRelativeZ = childPos.sectionZ - sectionPos.sectionZ * BitShiftUtil.powerOfTwo(detailLevelDifference); + + subView( + LodUtil.CHUNK_WIDTH, + childRelativeX * LodUtil.CHUNK_WIDTH, + childRelativeZ * LodUtil.CHUNK_WIDTH + ).shadowCopyTo(accessor); + + consumer.accept(accessor); + }); + } + //=====================// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/ChunkToLodBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/ChunkToLodBuilder.java index e49587a3d..aec487e74 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/ChunkToLodBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/ChunkToLodBuilder.java @@ -82,8 +82,6 @@ public class ChunkToLodBuilder implements AutoCloseable } else if (MC != null && !MC.playerExists()) { - // TODO handle server side properly - // MC hasn't finished loading (or is currently unloaded) // can be uncommented if tasks aren't being cleared correctly diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 9ab3eab32..1fc7fd7a8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -195,21 +195,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug if (chunkDataConsumer == null) return entry.future.cancel(false); - sectionPos.forEachChildAtLevel(LodUtil.CHUNK_DETAIL_LEVEL, childPos -> { - ChunkSizedFullDataAccessor accessor = new ChunkSizedFullDataAccessor(new DhChunkPos(childPos.sectionX, childPos.sectionZ)); - - int detailLevelDifference = sectionPos.sectionDetailLevel - childPos.sectionDetailLevel; - int childRelativeX = childPos.sectionX - sectionPos.sectionX * BitShiftUtil.powerOfTwo(detailLevelDifference); - int childRelativeZ = childPos.sectionZ - sectionPos.sectionZ * BitShiftUtil.powerOfTwo(detailLevelDifference); - - fullDataSource.subView( - LodUtil.CHUNK_WIDTH, - childRelativeX * LodUtil.CHUNK_WIDTH, - childRelativeZ * LodUtil.CHUNK_WIDTH - ).shadowCopyTo(accessor); - - chunkDataConsumer.accept(accessor); - }); + fullDataSource.splitIntoChunkSizedAccessors(chunkDataConsumer); } catch (ChannelException | RateLimitedException e) { 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 1438f7108..a0605fdac 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 @@ -1,6 +1,7 @@ package com.seibel.distanthorizons.core.level; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.fullDatafile.RemoteFullDataFileHandler; @@ -8,22 +9,30 @@ 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.network.NetworkClient; +import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; +import com.seibel.distanthorizons.core.network.messages.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.FullDataSourceUpdateMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; +import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import com.seibel.distanthorizons.coreapi.util.math.Mat4f; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; import javax.annotation.CheckForNull; import java.awt.*; +import java.io.IOException; import java.util.concurrent.CompletableFuture; /** The level used when connected to a server */ @@ -47,6 +56,8 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel @CheckForNull private final ClientNetworkState networkState; + @Nullable + private final ScopedNetworkEventSource eventSource; public final WorldGenModule worldGenModule; @@ -63,12 +74,41 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel this.networkState = networkState; this.worldGenModule = new WorldGenModule(dataFileHandler, this); + if (networkState != null) + { + this.eventSource = new ScopedNetworkEventSource<>(networkState.getClient()); + this.registerNetworkHandlers(); + } + else + { + this.eventSource = null; + } clientside = new ClientLevelModule(this); clientside.startRenderer(); LOGGER.info("Started DHLevel for " + this.levelWrapper + " with saves at " + this.saveStructure); } + private void registerNetworkHandlers() + { + assert this.eventSource != null; + + this.eventSource.registerHandler(FullDataSourceUpdateMessage.class, msg -> + { + try + { + CompleteFullDataSource fullDataSource = msg.getFullDataSource(this); + if (fullDataSource == null) return; + + fullDataSource.splitIntoChunkSizedAccessors(this::saveWrites); + } + catch (Exception e) + { + LOGGER.error("Error while updating full data source", e); + } + }); + } + //==============// // tick methods // //==============// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhLevel.java index 08fd32fd8..5fb8d2d86 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhLevel.java @@ -25,7 +25,7 @@ public abstract class DhLevel implements IDhLevel } @Override - public void updateChunkAsync(IChunkWrapper chunk) + public CompletableFuture updateChunkAsync(IChunkWrapper chunk) { CompletableFuture future = this.chunkToLodBuilder.tryGenerateData(chunk); if (future != null) @@ -44,6 +44,7 @@ public abstract class DhLevel implements IDhLevel new DhApiChunkModifiedEvent.EventParam(this.getLevelWrapper(), chunk.getChunkPos().x, chunk.getChunkPos().z)); }); } + return future; } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 6cd032a10..d5e533e17 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -6,6 +6,7 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedF 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.FullDataMetaFile; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.multiplayer.ServerPlayerState; @@ -15,10 +16,12 @@ 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.pos.DhBlockPos2D; +import com.seibel.distanthorizons.core.pos.DhChunkPos; 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.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; @@ -26,9 +29,7 @@ import com.seibel.distanthorizons.coreapi.util.math.Vec3d; import org.apache.logging.log4j.Logger; import javax.annotation.CheckForNull; -import java.util.Iterator; import java.util.Map; -import java.util.Set; import java.util.concurrent.*; public class DhServerLevel extends DhLevel implements IDhServerLevel @@ -40,7 +41,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel private final RemotePlayerConnectionHandler remotePlayerConnectionHandler; private final ScopedNetworkEventSource eventSource; - private final ConcurrentLinkedQueue worldGenLoopingQueue = new ConcurrentLinkedQueue<>(); + private final LinkedBlockingQueue worldGenLoopingQueue = new LinkedBlockingQueue<>(); private final ConcurrentMap incompleteDataSources = new ConcurrentHashMap<>(); private final ConcurrentMap fullDataRequests = new ConcurrentHashMap<>(); private final AppliedConfigState rateLimitConfig = new AppliedConfigState<>(Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit); @@ -66,12 +67,16 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel // TODO implement transparent message handling restriction by level // workaround: // ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); +// if (serverPlayerState == null) return; +// // if (serverPlayerState.serverPlayer.getLevel() != this.serverLevelWrapper) // return; this.eventSource.registerHandler(FullDataSourceRequestMessage.class, msg -> { ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); + if (serverPlayerState == null) return; + if (serverPlayerState.serverPlayer.getLevel() != this.serverLevelWrapper) return; @@ -106,6 +111,8 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel this.eventSource.registerHandler(GenTaskPriorityRequestMessage.class, msg -> { ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); + if (serverPlayerState == null) return; + if (serverPlayerState.serverPlayer.getLevel() != this.serverLevelWrapper) return; @@ -120,7 +127,9 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel if (entry == null) return; FullDataSourceRequestMessage requestMessage = entry.requestMessages.remove(msg.futureId); - remotePlayerConnectionHandler.getConnectedPlayer(msg).pendingFullDataRequests.decrementAndGet(); + ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); + if (serverPlayerState != null) + serverPlayerState.pendingFullDataRequests.decrementAndGet(); entry.requestCollectionSemaphore.acquireUninterruptibly(Short.MAX_VALUE); if (entry.requestMessages.isEmpty()) @@ -135,18 +144,12 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel public void addPlayer(IServerPlayerWrapper serverPlayer) { - synchronized (worldGenLoopingQueue) - { - this.worldGenLoopingQueue.add(serverPlayer); - } + this.worldGenLoopingQueue.add(serverPlayer); } public void removePlayer(IServerPlayerWrapper serverPlayer) { - synchronized (worldGenLoopingQueue) - { - this.worldGenLoopingQueue.remove(serverPlayer); - } + boolean ignored = this.worldGenLoopingQueue.remove(serverPlayer); } public void serverTick() @@ -189,6 +192,42 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel } } + @Override + public CompletableFuture updateChunkAsync(IChunkWrapper chunk) + { + DhSectionPos sectionPos = new DhSectionPos(chunk.getChunkPos()).convertToDetailLevel(DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); + FullDataMetaFile metaFile = this.serverside.dataFileHandler.getFileIfExist(sectionPos); + int prevChecksum = metaFile != null ? metaFile.baseMetaData.checksum : 0; + + CompletableFuture future = super.updateChunkAsync(chunk); + if (future == null) + return null; + + future.thenRun(() -> + { + if (metaFile == null || metaFile.baseMetaData.checksum == prevChecksum) + return; + + this.serverside.dataFileHandler.read(sectionPos).thenAccept(fullDataSource -> + { + if (!(fullDataSource instanceof CompleteFullDataSource)) + return; + CompleteFullDataSource completeSource = (CompleteFullDataSource) fullDataSource; + + for (IServerPlayerWrapper serverPlayer : worldGenLoopingQueue) + { + ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getPlayer(serverPlayer); + if (serverPlayerState == null) continue; + + if (chunk.getChunkPos().distance(new DhChunkPos(serverPlayer.getPosition())) <= serverPlayerState.config.renderDistance) + serverPlayerState.channelContext.writeAndFlush(new FullDataSourceUpdateMessage(completeSource, this)); + } + }); + }); + + return future; + } + @Override public void saveWrites(ChunkSizedFullDataAccessor data) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java index 688e7fe29..69ab18061 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java @@ -1,9 +1,11 @@ package com.seibel.distanthorizons.core.level; +import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import org.jetbrains.annotations.Nullable; import java.util.concurrent.CompletableFuture; @@ -20,7 +22,8 @@ public interface IDhLevel extends AutoCloseable */ ILevelWrapper getLevelWrapper(); - void updateChunkAsync(IChunkWrapper chunk); + @Nullable + CompletableFuture updateChunkAsync(IChunkWrapper chunk); IFullDataSourceProvider getFileHandler(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java index 41744ecfd..f1d4411b6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java @@ -2,7 +2,6 @@ package com.seibel.distanthorizons.core.multiplayer; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.network.IClientRequestHandler; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.network.messages.AckMessage; @@ -10,6 +9,7 @@ import com.seibel.distanthorizons.core.network.messages.HelloMessage; import com.seibel.distanthorizons.core.network.messages.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.messages.RemotePlayerConfigMessage; import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; import java.io.Closeable; import java.util.UUID; @@ -26,7 +26,7 @@ public class ClientNetworkState implements Closeable * Returns the client used by this instance.

* If you need to subscribe to any packet events, create an instance of {@link ScopedNetworkEventSource} using the returned instance. */ - public IClientRequestHandler getClient() { return this.client; } + public NetworkClient getClient() { return this.client; } /** * Constructs a new instance. diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java index 8b0d83c79..52048f097 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java @@ -10,6 +10,7 @@ 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 org.jetbrains.annotations.Nullable; import java.io.Closeable; import java.util.HashMap; @@ -69,11 +70,13 @@ public class RemotePlayerConnectionHandler implements Closeable return playersByConnection.values(); } + @Nullable public ServerPlayerState getConnectedPlayer(NetworkMessage msg) { return playersByConnection.get(msg.getChannelContext()); } + @Nullable public ServerPlayerState getPlayer(IServerPlayerWrapper serverPlayer) { return playersByUUID.get(serverPlayer.getUUID()); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/IClientRequestHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/IClientRequestHandler.java deleted file mode 100644 index a91b03798..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/IClientRequestHandler.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.seibel.distanthorizons.core.network; - -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; - -import java.util.concurrent.CompletableFuture; - -public interface IClientRequestHandler -{ - /** Indicates whether the client is initialized and not started connecting yet. */ - boolean isInitialState(); - /** Indicates whether the client is closed(-ing) and should not be used. */ - boolean isClosed(); - /** Indicates whether the connection is established and first message is sent. */ - boolean isReady(); - - /** Sends a new request. */ - CompletableFuture sendRequest(FutureTrackableNetworkMessage msg); -} - 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 8eb4e5a19..f59d9e751 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 @@ -19,7 +19,7 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; -public class NetworkClient extends NetworkEventSource implements IClientRequestHandler, AutoCloseable +public class NetworkClient extends NetworkEventSource implements AutoCloseable { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index 0d6daaa23..3e9837f56 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -5,8 +5,10 @@ import com.google.common.collect.Table; import com.google.common.collect.Tables; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.network.messages.CancelMessage; +import com.seibel.distanthorizons.core.network.messages.CloseEvent; import com.seibel.distanthorizons.core.network.messages.ExceptionMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.network.protocol.MessageRegistry; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import io.netty.channel.ChannelException; import io.netty.channel.ChannelHandlerContext; @@ -75,7 +77,13 @@ public abstract class NetworkEventSource public void registerHandler(Class handlerClass, Consumer handlerImplementation) { - this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> new HashSet<>()) + this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> + { + // Will throw if the handler class is not found + if (handlerClass != CloseEvent.class) + MessageRegistry.INSTANCE.getMessageId(handlerClass); + return new HashSet<>(); + }) .add((Consumer) handlerImplementation); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java index d8f550fe7..4f3ca1716 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java @@ -71,5 +71,9 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage { return fullDataSourceLoader.loadData(pos, new DhDataInputStream(inputStream), level); } + finally + { + dataBuffer.release(); + } } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceUpdateMessage.java new file mode 100644 index 000000000..e03ef2e0f --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceUpdateMessage.java @@ -0,0 +1,83 @@ +package com.seibel.distanthorizons.core.network.messages; + +import com.seibel.distanthorizons.core.dataObjects.fullData.loader.AbstractFullDataSourceLoader; +import com.seibel.distanthorizons.core.dataObjects.fullData.loader.CompleteFullDataSourceLoader; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; +import com.seibel.distanthorizons.core.level.DhServerLevel; +import com.seibel.distanthorizons.core.level.IDhLevel; +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.network.protocol.INetworkObject; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; +import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; + +import javax.annotation.Nullable; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class FullDataSourceUpdateMessage extends FutureTrackableNetworkMessage +{ + private CompleteFullDataSource fullDataSource; + private DhServerLevel level; + + private int levelHashCode; + private DhSectionPos sectionPos; + private CompleteFullDataSourceLoader fullDataSourceLoader; + private ByteBuf dataBuffer; + + public FullDataSourceUpdateMessage() {} + public FullDataSourceUpdateMessage(CompleteFullDataSource fullDataSource, DhServerLevel level) + { + this.fullDataSource = fullDataSource; + this.level = level; + + // TODO Multiverse support + this.levelHashCode = level.getLevelWrapper().getDimensionType().getDimensionName().hashCode(); + } + + @Override + public void encode0(ByteBuf out) throws IOException + { + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) + { + DhDataOutputStream dhOutputStream = new DhDataOutputStream(outputStream); + fullDataSource.writeToStream(dhOutputStream, level); + dhOutputStream.flush(); + + out.writeInt(levelHashCode); + fullDataSource.getSectionPos().encode(out); + out.writeByte(fullDataSource.getBinaryDataFormatVersion()); + out.writeInt(outputStream.size()); + out.writeBytes(outputStream.toByteArray()); + } + } + + @Override + public void decode0(ByteBuf in) + { + levelHashCode = in.readInt(); + sectionPos = INetworkObject.decodeStatic(DhSectionPos.zero(), in); + byte dataVersion = in.readByte(); + this.fullDataSourceLoader = (CompleteFullDataSourceLoader) AbstractFullDataSourceLoader.getLoader(CompleteFullDataSource.TYPE_ID, dataVersion); + this.dataBuffer = in.readBytes(in.readInt()); + } + + @Nullable + public CompleteFullDataSource getFullDataSource(IDhLevel level) throws IOException, InterruptedException + { + // TODO Multiverse support + if (levelHashCode != level.getLevelWrapper().getDimensionType().getDimensionName().hashCode()) + return null; + + try (ByteBufInputStream inputStream = new ByteBufInputStream(dataBuffer)) + { + return fullDataSourceLoader.loadData(sectionPos, new DhDataInputStream(inputStream), level); + } + finally + { + dataBuffer.release(); + } + } +} 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 d8cf5974a..9cd94f53f 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 @@ -35,9 +35,10 @@ public class MessageRegistry this.registerMessage(PlayerUUIDMessage.class, PlayerUUIDMessage::new); this.registerMessage(RemotePlayerConfigMessage.class, RemotePlayerConfigMessage::new); - // Full data requests + // Full data requests & updates this.registerMessage(FullDataSourceRequestMessage.class, FullDataSourceRequestMessage::new); this.registerMessage(FullDataSourceResponseMessage.class, FullDataSourceResponseMessage::new); + this.registerMessage(FullDataSourceUpdateMessage.class, FullDataSourceUpdateMessage::new); // Generation task prioritization this.registerMessage(GenTaskPriorityRequestMessage.class, GenTaskPriorityRequestMessage::new); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java index b8d12ea55..ff7d77be8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java @@ -19,6 +19,8 @@ package com.seibel.distanthorizons.core.pos; +import com.seibel.distanthorizons.coreapi.util.math.Vec3d; + import java.util.Objects; public class DhChunkPos @@ -48,6 +50,10 @@ public class DhChunkPos // >> 4 is the Same as div 16 this(blockPos.x >> 4, blockPos.z >> 4); } + public DhChunkPos(Vec3d pos) + { + this(((int)pos.x) >> 4, ((int)pos.z) >> 4); + } public DhChunkPos(long packed) { this(getX(packed), getZ(packed)); } @@ -72,6 +78,11 @@ public class DhChunkPos public long getLong() { return toLong(x, z); } + public double distance(DhChunkPos other) + { + return Math.sqrt(Math.pow(x - other.x, 2) + Math.pow(z - other.z, 2)); + } + @Override public boolean equals(Object obj) {