From 283f4c978298771f9b488474b737c8ba877de1a2 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 20 Aug 2023 20:23:28 +0500 Subject: [PATCH 01/11] Fix incorrect use of checksum stream (for some reason each section goes twice through this method) --- .../core/file/metaData/AbstractMetaDataContainerFile.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/metaData/AbstractMetaDataContainerFile.java b/core/src/main/java/com/seibel/distanthorizons/core/file/metaData/AbstractMetaDataContainerFile.java index 1248de29d..5a5a0390e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/metaData/AbstractMetaDataContainerFile.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/metaData/AbstractMetaDataContainerFile.java @@ -215,10 +215,11 @@ public abstract class AbstractMetaDataContainerFile { fileChannel.position(METADATA_SIZE_IN_BYTES); - try (DhDataOutputStream compressedOut = new DhDataOutputStream(Channels.newOutputStream(fileChannel)); - CheckedOutputStream checkedOut = new CheckedOutputStream(compressedOut, new Adler32())) // TODO: Is Adler32 ok? + try (CheckedOutputStream checkedOut = new CheckedOutputStream(Channels.newOutputStream(fileChannel), new Adler32()); // TODO: Is Adler32 ok? + DhDataOutputStream compressedOut = new DhDataOutputStream(checkedOut)) { dataWriterFunc.writeBufferToFile(compressedOut); + compressedOut.flush(); this.baseMetaData.checksum = (int) checkedOut.getChecksum().getValue(); } From c0e0412de9b13aa24bb4c01b8d3c9bf4b022f868 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sun, 20 Aug 2023 14:14:44 -0500 Subject: [PATCH 02/11] Add world gen task count to the F3 menu --- .../core/generation/WorldGenerationQueue.java | 13 ++ .../core/level/ClientLevelModule.java | 3 +- .../core/level/DhClientServerLevel.java | 8 +- .../core/level/ServerLevelModule.java | 117 +++++++++++------- 4 files changed, 90 insertions(+), 51 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index a23442827..68eac6257 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -544,6 +544,14 @@ public class WorldGenerationQueue implements Closeable, IDebugRenderable + //=========// + // getters // + //=========// + + public int getWaitingTaskCount() { return this.waitingTasks.size(); } + public int getInProgressTaskCount() { return this.inProgressGenTasksByLodPos.size(); } + + //==========// // shutdown // //==========// @@ -675,6 +683,11 @@ public class WorldGenerationQueue implements Closeable, IDebugRenderable } + + //=======// + // debug // + //=======// + @Override public void debugRender(DebugRenderer r) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java index 5fbd2435e..34ab6ae82 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java @@ -202,7 +202,8 @@ public class ClientLevelModule implements Closeable ClientRenderState.close(); } } - f3Message.close(); + + this.f3Message.close(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java index dbe06fbc0..89ff0b1f7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java @@ -34,6 +34,8 @@ public class DhClientServerLevel extends DhLevel implements IDhClientLevel, IDhS private final IServerLevelWrapper serverLevelWrapper; public IClientLevelWrapper clientLevelWrapper; + + public DhClientServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper) { if (saveStructure.getFullDataFolder(serverLevelWrapper).mkdirs()) @@ -41,11 +43,13 @@ public class DhClientServerLevel extends DhLevel implements IDhClientLevel, IDhS LOGGER.warn("unable to create data folder."); } this.serverLevelWrapper = serverLevelWrapper; - serverside = new ServerLevelModule(this, serverLevelWrapper, saveStructure); - clientside = new ClientLevelModule(this); + this.serverside = new ServerLevelModule(this, serverLevelWrapper, saveStructure); + this.clientside = new ClientLevelModule(this); LOGGER.info("Started " + DhClientServerLevel.class.getSimpleName() + " for " + serverLevelWrapper + " with saves at " + saveStructure); } + + //==============// // tick methods // //==============// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java index 4fa32e696..420a66dee 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java @@ -8,6 +8,7 @@ import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.generation.BatchGenerator; import com.seibel.distanthorizons.core.generation.WorldGenerationQueue; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.WorldGeneratorInjector; @@ -18,53 +19,18 @@ import java.util.concurrent.atomic.AtomicReference; public class ServerLevelModule { - private static class WorldGenState - { - public final WorldGenerationQueue worldGenerationQueue; - WorldGenState(IDhServerLevel level) - { - IDhApiWorldGenerator worldGenerator = WorldGeneratorInjector.INSTANCE.get(level.getLevelWrapper()); - if (worldGenerator == null) - { - // no override generator is bound, use the Core world generator - worldGenerator = new BatchGenerator(level); - // binding the core generator won't prevent other mods from binding their own generators - // since core world generator's should have the lowest override priority - WorldGeneratorInjector.INSTANCE.bind(level.getLevelWrapper(), worldGenerator); - } - this.worldGenerationQueue = new WorldGenerationQueue(worldGenerator); - } - - CompletableFuture closeAsync(boolean doInterrupt) - { - return this.worldGenerationQueue.startClosing(true, doInterrupt) - .exceptionally(ex -> - { - LOGGER.error("Error closing generation queue", ex); - return null; - } - ).thenRun(this.worldGenerationQueue::close) - .exceptionally(ex -> - { - LOGGER.error("Error closing world gen", ex); - return null; - }); - } - - public void tick(DhBlockPos2D targetPosForGeneration) - { - worldGenerationQueue.runCurrentGenTasksUntilBusy(targetPosForGeneration); - } - - } - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + public final IServerLevelWrapper levelWrapper; public final IDhServerLevel parent; public final AbstractSaveStructure saveStructure; public final GeneratedFullDataFileHandler dataFileHandler; public final AppliedConfigState worldGeneratorEnabledConfig; + private final AtomicReference worldGenStateRef = new AtomicReference<>(); + private final F3Screen.DynamicMessage worldGenF3Message; + + public ServerLevelModule(IDhServerLevel parent, IServerLevelWrapper levelWrapper, AbstractSaveStructure saveStructure) { @@ -73,13 +39,30 @@ public class ServerLevelModule this.saveStructure = saveStructure; this.dataFileHandler = new GeneratedFullDataFileHandler(parent, saveStructure); this.worldGeneratorEnabledConfig = new AppliedConfigState<>(Config.Client.Advanced.WorldGenerator.enableDistantGeneration); + + this.worldGenF3Message = new F3Screen.DynamicMessage(() -> + { + WorldGenState worldGenState = this.worldGenStateRef.get(); + if (worldGenState != null) + { + int waiting = worldGenState.worldGenerationQueue.getWaitingTaskCount(); + int inProgress = worldGenState.worldGenerationQueue.getInProgressTaskCount(); + + return "World Gen Tasks: "+waiting+", (in progress: "+inProgress+")"; + } + else + { + return "World Gen Disabled"; + } + }); } + + //==============// // tick methods // //==============// - public void startWorldGen() { // create the new world generator @@ -131,6 +114,8 @@ public class ServerLevelModule } } + + //===============// // data handling // //===============// @@ -154,19 +139,55 @@ public class ServerLevelModule worldGenState.closeAsync(true).join(); //TODO: Make it async. } } - dataFileHandler.close(); + + this.dataFileHandler.close(); + this.worldGenF3Message.close(); } + //================// + // helper classes // + //================// - //=======================// - // misc helper functions // - //=======================// - - public void dumpRamUsage() + private static class WorldGenState { - //TODO + public final WorldGenerationQueue worldGenerationQueue; + WorldGenState(IDhServerLevel level) + { + IDhApiWorldGenerator worldGenerator = WorldGeneratorInjector.INSTANCE.get(level.getLevelWrapper()); + if (worldGenerator == null) + { + // no override generator is bound, use the Core world generator + worldGenerator = new BatchGenerator(level); + // binding the core generator won't prevent other mods from binding their own generators + // since core world generator's should have the lowest override priority + WorldGeneratorInjector.INSTANCE.bind(level.getLevelWrapper(), worldGenerator); + } + this.worldGenerationQueue = new WorldGenerationQueue(worldGenerator); + } + + CompletableFuture closeAsync(boolean doInterrupt) + { + return this.worldGenerationQueue.startClosing(true, doInterrupt) + .exceptionally(ex -> + { + LOGGER.error("Error closing generation queue", ex); + return null; + } + ).thenRun(this.worldGenerationQueue::close) + .exceptionally(ex -> + { + LOGGER.error("Error closing world gen", ex); + return null; + }); + } + + public void tick(DhBlockPos2D targetPosForGeneration) + { + worldGenerationQueue.runCurrentGenTasksUntilBusy(targetPosForGeneration); + } + } } From f57c52b2d08f430155f5619cb76472e8bf67d13b Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sun, 20 Aug 2023 14:47:22 -0500 Subject: [PATCH 03/11] add ILevelWrapper.onUnload() --- .../core/world/DhClientServerWorld.java | 40 +++++++++++-------- .../core/world/DhClientWorld.java | 4 +- .../core/world/DhServerWorld.java | 11 +++++ .../world/ILevelWrapper.java | 8 +--- 4 files changed, 39 insertions(+), 24 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java index 6dd3317c9..90cac6a2d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java @@ -19,9 +19,9 @@ import java.util.concurrent.ExecutorService; public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWorld, IDhServerWorld { - private final HashMap levelObjMap; - private final HashSet dhLevels; - public final LocalSaveStructure saveStructure; + private final HashMap levelWrapperByDhLevel = new HashMap<>(); + private final HashSet dhLevels = new HashSet<>(); + public final LocalSaveStructure saveStructure = new LocalSaveStructure(); public ExecutorService dhTickerThread = ThreadUtil.makeSingleThreadPool("DH Client Server World Ticker Thread", 2); public EventLoop eventLoop = new EventLoop(this.dhTickerThread, this::_clientTick); //TODO: Rate-limit the loop @@ -30,12 +30,13 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor + //=============// + // constructor // + //=============// + public DhClientServerWorld() { super(EWorldEnvironment.Client_Server); - this.saveStructure = new LocalSaveStructure(); - this.levelObjMap = new HashMap<>(); - this.dhLevels = new HashSet<>(); LOGGER.info("Started DhWorld of type " + this.environment); @@ -44,12 +45,16 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor + //=========// + // methods // + //=========// + @Override public DhClientServerLevel getOrLoadLevel(ILevelWrapper wrapper) { if (wrapper instanceof IServerLevelWrapper) { - return this.levelObjMap.computeIfAbsent(wrapper, (levelWrapper) -> + return this.levelWrapperByDhLevel.computeIfAbsent(wrapper, (levelWrapper) -> { File levelFile = this.saveStructure.getLevelFolder(levelWrapper); LodUtil.assertTrue(levelFile != null); @@ -60,7 +65,7 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor } else { - return this.levelObjMap.computeIfAbsent(wrapper, (levelWrapper) -> + return this.levelWrapperByDhLevel.computeIfAbsent(wrapper, (levelWrapper) -> { IClientLevelWrapper clientLevelWrapper = (IClientLevelWrapper) levelWrapper; IServerLevelWrapper serverLevelWrapper = clientLevelWrapper.tryGetServerSideWrapper(); @@ -68,7 +73,7 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor LodUtil.assertTrue(clientLevelWrapper.getDimensionType().equals(serverLevelWrapper.getDimensionType()), "tryGetServerSideWrapper returned a level for a different dimension. ClientLevelWrapper dim: " + clientLevelWrapper.getDimensionType().getDimensionName() + " ServerLevelWrapper dim: " + serverLevelWrapper.getDimensionType().getDimensionName()); - DhClientServerLevel level = this.levelObjMap.get(serverLevelWrapper); + DhClientServerLevel level = this.levelWrapperByDhLevel.get(serverLevelWrapper); if (level == null) { return null; @@ -81,7 +86,7 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor } @Override - public DhClientServerLevel getLevel(ILevelWrapper wrapper) { return this.levelObjMap.get(wrapper); } + public DhClientServerLevel getLevel(ILevelWrapper wrapper) { return this.levelWrapperByDhLevel.get(wrapper); } @Override public Iterable getAllLoadedLevels() { return this.dhLevels; } @@ -89,21 +94,23 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor @Override public void unloadLevel(ILevelWrapper wrapper) { - if (this.levelObjMap.containsKey(wrapper)) + if (this.levelWrapperByDhLevel.containsKey(wrapper)) { if (wrapper instanceof IServerLevelWrapper) { - LOGGER.info("Unloading level " + this.levelObjMap.get(wrapper)); - DhClientServerLevel clientServerLevel = this.levelObjMap.remove(wrapper); - this.dhLevels.remove(clientServerLevel); + LOGGER.info("Unloading level " + this.levelWrapperByDhLevel.get(wrapper)); + wrapper.onUnload(); + + DhClientServerLevel clientServerLevel = this.levelWrapperByDhLevel.remove(wrapper); clientServerLevel.close(); + this.dhLevels.remove(clientServerLevel); } else { // If the level wrapper is a Client Level Wrapper, then that means the client side leaves the level, // but note that the server side still has the level loaded. So, we don't want to unload the level, // we just want to stop rendering it. - this.levelObjMap.remove(wrapper).stopRenderer(); // Ignore resource warning. The level obj is referenced elsewhere. + this.levelWrapperByDhLevel.remove(wrapper).stopRenderer(); // Ignore resource warning. The level obj is referenced elsewhere. } } } @@ -137,10 +144,11 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor for (DhClientServerLevel level : this.dhLevels) { LOGGER.info("Unloading level " + level.getServerLevelWrapper().getDimensionType().getDimensionName()); + level.clientLevelWrapper.onUnload(); level.close(); } - this.levelObjMap.clear(); + this.levelWrapperByDhLevel.clear(); this.eventLoop.close(); LOGGER.info("Closed DhWorld of type " + this.environment); } 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 209f8905a..ec0a6d877 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 @@ -30,8 +30,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld // private final NetworkClient networkClient; - // TODO why does this executor have 2 threads? - public ExecutorService dhTickerThread = ThreadUtil.makeSingleThreadPool("DH Client World Ticker Thread", 2); + public ExecutorService dhTickerThread = ThreadUtil.makeSingleThreadPool("DH Client World Ticker Thread"); public EventLoop eventLoop = new EventLoop(this.dhTickerThread, this::_clientTick); @@ -137,6 +136,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld if (this.levels.containsKey(wrapper)) { LOGGER.info("Unloading level " + this.levels.get(wrapper)); + wrapper.onUnload(); this.levels.remove(wrapper).close(); } } 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 fb2387413..eeff453fd 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 @@ -31,6 +31,10 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld + //==============// + // constructors // + //==============// + public DhServerWorld() { super(EWorldEnvironment.Server_Only); @@ -94,6 +98,12 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld // }); } + + + //=========// + // methods // + //=========// + public void addPlayer(IServerPlayerWrapper serverPlayer) { //this.playersByUUID.put(serverPlayer.getUUID(), new RemotePlayer(serverPlayer)); @@ -149,6 +159,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld if (this.levels.containsKey(wrapper)) { LOGGER.info("Unloading level {} ", this.levels.get(wrapper)); + wrapper.onUnload(); this.levels.remove(wrapper).close(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java index 0bc41d07b..8c660fccd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java @@ -39,10 +39,6 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable @Override IDhApiDimensionTypeWrapper getDimensionType(); - int getBlockLight(int x, int y, int z); - - int getSkyLight(int x, int y, int z); - @Override boolean hasCeiling(); @@ -65,7 +61,7 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable @Deprecated IBiomeWrapper getBiome(DhBlockPos pos); - // TODO implement onUnload - // necessary so ChunkToLodBuilder can have its cache cleared after the level closes + /** Fired when the level is being unloaded. Doesn't unload the level. */ + void onUnload(); } From 2964acfc8cc0558435989c2dd4aa4b6c81e0c292 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sun, 20 Aug 2023 15:06:59 -0500 Subject: [PATCH 04/11] Hide unused multiplayer settings --- .../distanthorizons/core/config/Config.java | 6 ++++-- .../core/world/DhClientWorld.java | 20 +++++++++---------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 99e1c1c73..92fb6b80a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -734,7 +734,8 @@ public class Config + "") .build(); - public static ConfigEntry enableMultiverseNetworking = new ConfigEntry.Builder() + // not currently implemented + private static ConfigEntry enableMultiverseNetworking = new ConfigEntry.Builder() .set(true) .comment("" + "If true Distant Horizons will attempt to communicate with the connected \n" @@ -742,7 +743,8 @@ public class Config + "") .build(); - public static ConfigEntry enableServerNetworking = new ConfigEntry.Builder() + // not currently implemented + private static ConfigEntry enableServerNetworking = new ConfigEntry.Builder() .set(false) .comment("" + "Attention: this is only for developers and hasn't been implemented.\n" 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 ec0a6d877..b2402ee57 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 @@ -46,16 +46,16 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld this.saveStructure = new ClientOnlySaveStructure(); this.levels = new ConcurrentHashMap<>(); - if (Config.Client.Advanced.Multiplayer.enableServerNetworking.get()) - { - // TODO server specific configs -// this.networkClient = new NetworkClient(MC_CLIENT.getCurrentServerIp(), 25049); - this.registerNetworkHandlers(); - } - else - { -// this.networkClient = null; - } + //if (Config.Client.Advanced.Multiplayer.enableServerNetworking.get()) + //{ + // // TODO server specific configs + // this.networkClient = new NetworkClient(MC_CLIENT.getCurrentServerIp(), 25049); + // this.registerNetworkHandlers(); + //} + //else + //{ + // this.networkClient = null; + //} LOGGER.info("Started DhWorld of type " + this.environment); } From 35f5bce14939606d9301fc722a73a2b0fd713081 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sun, 20 Aug 2023 17:59:58 -0500 Subject: [PATCH 05/11] Clean up World Gen Threads after termination --- .../core/generation/WorldGenerationQueue.java | 12 +++++++++-- .../RateLimitedThreadPoolExecutor.java | 21 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index 68eac6257..20338b160 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -18,6 +18,7 @@ import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.objects.DhThreadFactory; +import com.seibel.distanthorizons.core.util.objects.RateLimitedThreadPoolExecutor; import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; @@ -34,7 +35,7 @@ public class WorldGenerationQueue implements Closeable, IDebugRenderable { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - public static final DhThreadFactory THREAD_FACTORY = new DhThreadFactory(ThreadUtil.THREAD_NAME_PREFIX + "Gen-Worker-Thread", Thread.MIN_PRIORITY); + public static final DhThreadFactory THREAD_FACTORY = new DhThreadFactory(ThreadUtil.THREAD_NAME_PREFIX + "World-Gen-Worker-Thread", Thread.MIN_PRIORITY); private final IDhApiWorldGenerator generator; @@ -73,7 +74,7 @@ public class WorldGenerationQueue implements Closeable, IDebugRenderable private final HashMap alreadyGeneratedPosHashSet = new HashMap<>(MAX_ALREADY_GENERATED_COUNT); private final Queue alreadyGeneratedPosQueue = new LinkedList<>(); - private static ExecutorService worldGeneratorThreadPool; + private static RateLimitedThreadPoolExecutor worldGeneratorThreadPool; private static ConfigChangeListener configListener; @@ -527,6 +528,7 @@ public class WorldGenerationQueue implements Closeable, IDebugRenderable } worldGeneratorThreadPool = ThreadUtil.makeRateLimitedThreadPool(threadPoolSize, THREAD_FACTORY, Config.Client.Advanced.MultiThreading.runTimeRatioForWorldGenerationThreads); + worldGeneratorThreadPool.setOnTerminatedEventHandler(WorldGenerationQueue::onWorldGenThreadPoolTerminated); } /** @@ -542,6 +544,12 @@ public class WorldGenerationQueue implements Closeable, IDebugRenderable } } + private static void onWorldGenThreadPoolTerminated() + { + LOGGER.debug("World generator thread pool terminated. Suggesting the JVM runs a garbage collection to clean up any loose world generation objects..."); + System.gc(); + } + //=========// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/RateLimitedThreadPoolExecutor.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/RateLimitedThreadPoolExecutor.java index a732842f9..ee77c442c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/RateLimitedThreadPoolExecutor.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/RateLimitedThreadPoolExecutor.java @@ -15,6 +15,8 @@ public class RateLimitedThreadPoolExecutor extends ThreadPoolExecutor /** How long it took this thread to run its last task */ private final ThreadLocal lastRunDurationNanoTimeRef = ThreadLocal.withInitial(() -> -1L); + private Runnable onTerminatedEventHandler = null; + //==============// @@ -63,4 +65,23 @@ public class RateLimitedThreadPoolExecutor extends ThreadPoolExecutor this.lastRunDurationNanoTimeRef.set(System.nanoTime() - this.runStartNanoTimeRef.get()); } + @Override + protected void terminated() + { + super.terminated(); + if (this.onTerminatedEventHandler != null) + { + this.onTerminatedEventHandler.run(); + } + } + + + + //==============// + // custom logic // + //==============// + + /** only one event handler can be present at a time */ + public void setOnTerminatedEventHandler(Runnable runnable) { this.onTerminatedEventHandler = runnable; } + } \ No newline at end of file From d5f3ac9fb8902542743f925440475be71a4521e2 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sun, 20 Aug 2023 18:11:12 -0500 Subject: [PATCH 06/11] Fix renderer crash when enabling earth curvature --- core/src/main/resources/shaders/curve.vert | 54 +++++++------------ core/src/main/resources/shaders/standard.vert | 2 + 2 files changed, 22 insertions(+), 34 deletions(-) diff --git a/core/src/main/resources/shaders/curve.vert b/core/src/main/resources/shaders/curve.vert index 5db053d1e..03876a40c 100644 --- a/core/src/main/resources/shaders/curve.vert +++ b/core/src/main/resources/shaders/curve.vert @@ -8,6 +8,8 @@ out vec4 vertexColor; out vec3 vertexWorldPos; out float vertexYPos; +uniform bool whiteWorld; + uniform mat4 combinedMatrix; uniform vec3 modelOffset; uniform float worldYOffset; @@ -19,6 +21,8 @@ uniform float mircoOffset; uniform float earthRadius; /** + * TODO in the future this and standard.vert should be merged together to prevent inconsistencies between the two + * * Vertex Shader * * author: James Seibel @@ -51,43 +55,25 @@ void main() vertexWorldPos.x += mx; vertexWorldPos.y += my; vertexWorldPos.z += mz; - - #if 0 - // Old (disabled) vertex transformation logic - Leetom - - // Calculate the vertex pos due to curvature of the earth - // We use spherical coordinates to calculate the vertex position - //if (vertexWorldPos.x == 0.0 && vertexWorldPos.z == 0.0) - //{ - // // In the center. No curvature needed - //} - //else - //{ - float theta = atan(vertexWorldPos.z, vertexWorldPos.x); // in radians (-pi, pi) - float trueY = earthRadius + vertexWorldPos.y; // true Y position, or height - float phi = sqrt(vertexWorldPos.z * vertexWorldPos.z + vertexWorldPos.x * vertexWorldPos.x) / trueY; - // Convert spherical coordinates to cartesian coordinates - vertexWorldPos.x = trueY * sin(phi) * cos(theta); - vertexWorldPos.z = trueY * sin(phi) * sin(theta); - vertexWorldPos.y = trueY * cos(phi) - earthRadius; - //} - - #else - // new vertex transformation logic - stduhpf - - float localRadius = earthRadius + vertexYPos;// vertexWorldPos.y + cameraPosition.y - Center_Y; - - float phi = length(vertexWorldPos.xz) / localRadius; - - vertexWorldPos.y += (cos(phi) - 1.0) * localRadius; - vertexWorldPos.xz = vertexWorldPos.xz * sin(phi) / phi; - #endif - + + + // vertex transformation logic - stduhpf + float localRadius = earthRadius + vertexYPos; + float phi = length(vertexWorldPos.xz) / localRadius; + vertexWorldPos.y += (cos(phi) - 1.0) * localRadius; + vertexWorldPos.xz = vertexWorldPos.xz * sin(phi) / phi; + + uint lights = meta & 0xFFu; float light2 = (mod(float(lights), 16.0) + 0.5) / 16.0; float light = (float(lights / 16u) + 0.5) / 16.0; - vertexColor = color * vec4(texture(lightMap, vec2(light, light2)).xyz, 1.0); - + vertexColor = vec4(texture(lightMap, vec2(light, light2)).xyz, 1.0); + + if (!whiteWorld) + { + vertexColor *= color; + } + gl_Position = combinedMatrix * vec4(vertexWorldPos, 1.0); } diff --git a/core/src/main/resources/shaders/standard.vert b/core/src/main/resources/shaders/standard.vert index c7e446cf8..316559b6e 100644 --- a/core/src/main/resources/shaders/standard.vert +++ b/core/src/main/resources/shaders/standard.vert @@ -20,6 +20,8 @@ uniform float mircoOffset; /** + * TODO in the future this and curve.vert should be merged together to prevent inconsistencies between the two + * * Vertex Shader * * author: James Seibel From 3b14bd655c743fc9a02c80b942accdbbcfc9d138 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sun, 20 Aug 2023 18:22:10 -0500 Subject: [PATCH 07/11] Add a world curvature listener and limiter --- .../distanthorizons/core/config/Config.java | 3 ++ .../WorldCurvatureConfigEventHandler.java | 47 +++++++++++++++++++ .../assets/distanthorizons/lang/en_us.json | 2 +- 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/WorldCurvatureConfigEventHandler.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 92fb6b80a..a59355d57 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -28,8 +28,10 @@ import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGenerat import com.seibel.distanthorizons.core.config.eventHandlers.QuickRenderToggleConfigEventHandler; import com.seibel.distanthorizons.core.config.eventHandlers.RenderCacheConfigEventHandler; import com.seibel.distanthorizons.core.config.eventHandlers.UnsafeValuesConfigListener; +import com.seibel.distanthorizons.core.config.eventHandlers.WorldCurvatureConfigEventHandler; import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPresetConfigEventHandler; import com.seibel.distanthorizons.core.config.eventHandlers.presets.RenderQualityPresetConfigEventHandler; +import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; import com.seibel.distanthorizons.core.config.types.*; import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryAppearance; import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryPerformance; @@ -575,6 +577,7 @@ public class Config + "Note: Due to current limitations, the min value is 50 \n" + "and the max value is 5000. Any values outside this range \n" + "will be set to 0 (disabled).") + .addListener(WorldCurvatureConfigEventHandler.INSTANCE) .build(); public static ConfigEntry lodBias = new ConfigEntry.Builder() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/WorldCurvatureConfigEventHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/WorldCurvatureConfigEventHandler.java new file mode 100644 index 000000000..0fdea7e48 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/WorldCurvatureConfigEventHandler.java @@ -0,0 +1,47 @@ +package com.seibel.distanthorizons.core.config.eventHandlers; + +import com.seibel.distanthorizons.api.DhApi; +import com.seibel.distanthorizons.api.enums.config.ELodShading; +import com.seibel.distanthorizons.api.enums.config.EMaxHorizontalResolution; +import com.seibel.distanthorizons.api.enums.config.EVerticalQuality; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.config.listeners.IConfigListener; + +import java.util.Timer; +import java.util.TimerTask; + +/** + * Listens to the config and will automatically + * clear the current render cache if certain settings are changed.

+ * + * Note: if additional settings should clear the render cache, add those to this listener, don't create a new listener + */ +public class WorldCurvatureConfigEventHandler implements IConfigListener +{ + public static WorldCurvatureConfigEventHandler INSTANCE = new WorldCurvatureConfigEventHandler(); + + private static final int MIN_VALID_CURVE_VALUE = 50; + + + /** private since we only ever need one handler at a time */ + private WorldCurvatureConfigEventHandler() { } + + + + @Override + public void onConfigValueSet() + { + int curveRatio = Config.Client.Advanced.Graphics.AdvancedGraphics.earthCurveRatio.get(); + if (curveRatio > 0 && curveRatio < MIN_VALID_CURVE_VALUE) + { + // shouldn't update the UI, otherwise we may end up fighting the user + Config.Client.Advanced.Graphics.AdvancedGraphics.earthCurveRatio.set(MIN_VALID_CURVE_VALUE); + } + + } + + @Override + public void onUiModify() { /* do nothing, we only care about modified config values */ } + + +} diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 106798640..49441406b 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -257,7 +257,7 @@ "distanthorizons.config.client.advanced.graphics.advancedGraphics.earthCurveRatio": "Earth Curve Ratio §6(EXPERIMENTAL)§r", "distanthorizons.config.client.advanced.graphics.advancedGraphics.earthCurveRatio.@tooltip": - "A value of 1 is equivalent to the curvature of Earth in real life.", + "A value of 1 is equivalent to the curvature of Earth in real life. \nThe minimum accepted value is 50 and the maximum value is 5000. \nEverything between 1 and 49 will be rounded up to 50.", "distanthorizons.config.client.advanced.graphics.advancedGraphics.lodBias": "LOD Bias §6(Affects vanilla terrain)§r", "distanthorizons.config.client.advanced.graphics.advancedGraphics.lodBias.@tooltip": From 786c445a915df45a7e58c0825747767202c7c0ce Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sun, 20 Aug 2023 18:38:21 -0500 Subject: [PATCH 08/11] Add a profiler section for debug wireframe rendering --- .../core/render/renderer/DebugRenderer.java | 2 -- .../core/render/renderer/LodRenderer.java | 10 +++++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java index add3f6330..b8ad72317 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java @@ -329,8 +329,6 @@ public class DebugRenderer public void render(Mat4f transform) { - if (!Config.Client.Advanced.Debugging.debugWireframeRendering.get()) return; - transform_this_frame = transform; Vec3d cam = MC_RENDER.getCameraExactPosition(); camf = new Vec3f((float) cam.x, (float) cam.y, (float) cam.z); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java index 9a9c46c5f..ce8721347 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java @@ -301,7 +301,15 @@ public class LodRenderer GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0); this.shaderProgram.unbind(); - DebugRenderer.INSTANCE.render(modelViewProjectionMatrix); + + if (Config.Client.Advanced.Debugging.debugWireframeRendering.get()) + { + profiler.popPush("Debug wireframes"); + // Note: this can be very slow if a lot of boxes are being rendered + DebugRenderer.INSTANCE.render(modelViewProjectionMatrix); + profiler.popPush("LOD cleanup"); + } + GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT); minecraftGlState.restore(); From 1e05972e3cac8431cd5e54d9209bad800b552fc5 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Mon, 21 Aug 2023 07:46:51 -0500 Subject: [PATCH 09/11] Fix potential null pointers in DhWorld shutdown --- .../distanthorizons/core/level/DhClientServerLevel.java | 7 +------ .../distanthorizons/core/world/DhClientServerWorld.java | 9 ++++++++- .../seibel/distanthorizons/core/world/DhClientWorld.java | 9 +++++++++ .../seibel/distanthorizons/core/world/DhServerWorld.java | 8 ++++++++ 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java index 89ff0b1f7..52e627541 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java @@ -32,7 +32,6 @@ public class DhClientServerLevel extends DhLevel implements IDhClientLevel, IDhS public final ClientLevelModule clientside; private final IServerLevelWrapper serverLevelWrapper; - public IClientLevelWrapper clientLevelWrapper; @@ -109,11 +108,7 @@ public class DhClientServerLevel extends DhLevel implements IDhClientLevel, IDhS clientside.startRenderer(); } - public void stopRenderer() - { - clientside.stopRenderer(); - clientLevelWrapper = null; - } + public void stopRenderer() { this.clientside.stopRenderer(); } //================// // level handling // diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java index 90cac6a2d..150983118 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java @@ -144,7 +144,14 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor for (DhClientServerLevel level : this.dhLevels) { LOGGER.info("Unloading level " + level.getServerLevelWrapper().getDimensionType().getDimensionName()); - level.clientLevelWrapper.onUnload(); + + // level wrapper shouldn't be null, but just in case + IServerLevelWrapper serverLevelWrapper = level.getServerLevelWrapper(); + if (serverLevelWrapper != null) + { + serverLevelWrapper.onUnload(); + } + level.close(); } 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 b2402ee57..2b39ff96c 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 @@ -15,6 +15,7 @@ import com.seibel.distanthorizons.core.util.objects.EventLoop; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import java.io.File; import java.util.concurrent.CompletableFuture; @@ -167,6 +168,14 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld for (DhClientLevel dhClientLevel : this.levels.values()) { LOGGER.info("Unloading level " + dhClientLevel.getLevelWrapper().getDimensionType().getDimensionName()); + + // level wrapper shouldn't be null, but just in case + IClientLevelWrapper clientLevelWrapper = dhClientLevel.getClientLevelWrapper(); + if (clientLevelWrapper != null) + { + clientLevelWrapper.onUnload(); + } + dhClientLevel.close(); } 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 eeff453fd..144c3c0ae 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 @@ -182,6 +182,14 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld for (DhServerLevel level : this.levels.values()) { LOGGER.info("Unloading level " + level.getLevelWrapper().getDimensionType().getDimensionName()); + + // level wrapper shouldn't be null, but just in case + IServerLevelWrapper serverLevelWrapper = level.getServerLevelWrapper(); + if (serverLevelWrapper != null) + { + serverLevelWrapper.onUnload(); + } + level.close(); } From 4ec4b2f8dd6ebc7914cca82a742d05b610334f96 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Mon, 21 Aug 2023 22:06:33 -0500 Subject: [PATCH 10/11] Reduce SSAO noise --- core/src/main/resources/shaders/ssao/ao.frag | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/core/src/main/resources/shaders/ssao/ao.frag b/core/src/main/resources/shaders/ssao/ao.frag index 33be6a37d..9538f54c4 100644 --- a/core/src/main/resources/shaders/ssao/ao.frag +++ b/core/src/main/resources/shaders/ssao/ao.frag @@ -46,16 +46,17 @@ void main() { vec3 samplePos = vec3(0.0) + (TBN * gKernel[i]); samplePos = viewPos + samplePos * gSampleRad; - + vec4 offset = gProj * vec4(samplePos + viewPos, 1.0); offset.xy /= offset.w; offset.xy = offset.xy * HALF_2 + HALF_2; - + float geometryDepth = calcViewPosition(offset.xy).z; - + float rangeCheck = smoothstep(0.0, 1.0, gSampleRad / abs(viewPos.z - geometryDepth)); - occlusion_factor += float(geometryDepth >= samplePos.z + 0.05) * rangeCheck; - + // the number added to the samplePos.z can be used to reduce noise in the SSAO application at the cost of reducing the overall affect + occlusion_factor += float(geometryDepth >= samplePos.z + 1.0) * rangeCheck; + } float visibility_factor = 1.0 - (occlusion_factor / MAX_KERNEL_SIZE); From 794e9afc10e33e4f72849ff7a560ff596622789e Mon Sep 17 00:00:00 2001 From: James Seibel Date: Tue, 22 Aug 2023 07:45:25 -0500 Subject: [PATCH 11/11] Fix file saving --- .../AbstractMetaDataContainerFile.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/metaData/AbstractMetaDataContainerFile.java b/core/src/main/java/com/seibel/distanthorizons/core/file/metaData/AbstractMetaDataContainerFile.java index 5a5a0390e..bf50944cf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/metaData/AbstractMetaDataContainerFile.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/metaData/AbstractMetaDataContainerFile.java @@ -215,17 +215,19 @@ public abstract class AbstractMetaDataContainerFile { fileChannel.position(METADATA_SIZE_IN_BYTES); - try (CheckedOutputStream checkedOut = new CheckedOutputStream(Channels.newOutputStream(fileChannel), new Adler32()); // TODO: Is Adler32 ok? - DhDataOutputStream compressedOut = new DhDataOutputStream(checkedOut)) - { - dataWriterFunc.writeBufferToFile(compressedOut); - compressedOut.flush(); - this.baseMetaData.checksum = (int) checkedOut.getChecksum().getValue(); - } + // the order of these streams is important, otherwise the checksum won't be calculated + CheckedOutputStream checkedOut = new CheckedOutputStream(Channels.newOutputStream(fileChannel), new Adler32()); + // normally a DhStream should be the topmost stream to prevent closing the stream accidentally, but since this stream will be closed immediately after writing anyway, it won't be an issue + DhDataOutputStream compressedOut = new DhDataOutputStream(checkedOut); + + // write the contained data + dataWriterFunc.writeBufferToFile(compressedOut); + compressedOut.flush(); + this.baseMetaData.checksum = (int) checkedOut.getChecksum().getValue(); - fileChannel.position(0); // Write metadata + fileChannel.position(0); ByteBuffer buffer = ByteBuffer.allocate(METADATA_SIZE_IN_BYTES); buffer.putInt(METADATA_IDENTITY_BYTES); buffer.putInt(this.pos.sectionX);