diff --git a/src/main/java/com/seibel/lod/core/api/ClientApi.java b/src/main/java/com/seibel/lod/core/api/ClientApi.java index 6817f4812..063538699 100644 --- a/src/main/java/com/seibel/lod/core/api/ClientApi.java +++ b/src/main/java/com/seibel/lod/core/api/ClientApi.java @@ -58,7 +58,7 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; public class ClientApi { public static boolean prefLoggerEnabled = false; - public static List> spamReducedLoggers + public static final List> spamReducedLoggers = Collections.synchronizedList(new LinkedList>()); public static final ClientApi INSTANCE = new ClientApi(); diff --git a/src/main/java/com/seibel/lod/core/api/EventApi.java b/src/main/java/com/seibel/lod/core/api/EventApi.java index 9bfc2377b..064fda24a 100644 --- a/src/main/java/com/seibel/lod/core/api/EventApi.java +++ b/src/main/java/com/seibel/lod/core/api/EventApi.java @@ -19,14 +19,10 @@ package com.seibel.lod.core.api; -import org.lwjgl.glfw.GLFW; - import com.seibel.lod.core.api.ClientApi.LagSpikeCatcher; import com.seibel.lod.core.builders.lodBuilding.LodBuilder; -import com.seibel.lod.core.builders.worldGeneration.BatchGenerator; -import com.seibel.lod.core.builders.worldGeneration.LodWorldGenerator; +import com.seibel.lod.core.enums.worldGeneration.BatchGenerator; import com.seibel.lod.core.enums.WorldType; -import com.seibel.lod.core.enums.config.VerticalQuality; import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler; import com.seibel.lod.core.objects.lod.LodDimension; import com.seibel.lod.core.objects.lod.RegionPos; @@ -84,13 +80,9 @@ public class EventApi { if (CONFIG.client().worldGenerator().getEnableDistantGeneration()) { try { - if (VERSION_CONSTANTS.hasBatchGenerationImplementation()) { - if (batchGenerator == null) - batchGenerator = new BatchGenerator(ApiShared.lodBuilder, lodDim); - batchGenerator.queueGenerationRequests(lodDim, ApiShared.lodBuilder); - } else { - LodWorldGenerator.INSTANCE.queueGenerationRequests(lodDim, ApiShared.lodBuilder); - } + if (batchGenerator == null) + batchGenerator = new BatchGenerator(ApiShared.lodBuilder, lodDim); + batchGenerator.queueGenerationRequests(lodDim, ApiShared.lodBuilder); } catch (Exception e) { // Exception may happen if world got unloaded unorderly e.printStackTrace(); @@ -160,13 +152,9 @@ public class EventApi { ApiShared.isShuttingDown = true; // TODO Better report on when world gen is stuck and timeout - if (VERSION_CONSTANTS.hasBatchGenerationImplementation()) { - if (batchGenerator != null) - batchGenerator.stop(true); - batchGenerator = null; - } else { - LodWorldGenerator.INSTANCE.restartExecutorService(); - } + if (batchGenerator != null) + batchGenerator.stop(true); + batchGenerator = null; ApiShared.lodWorld.deselectWorld(); // This force a save and shutdown lodDim properly diff --git a/src/main/java/com/seibel/lod/core/builders/bufferBuilding/LodBufferBuilderFactory.java b/src/main/java/com/seibel/lod/core/builders/bufferBuilding/LodBufferBuilderFactory.java index d4c534e98..3fc13ebf7 100644 --- a/src/main/java/com/seibel/lod/core/builders/bufferBuilding/LodBufferBuilderFactory.java +++ b/src/main/java/com/seibel/lod/core/builders/bufferBuilding/LodBufferBuilderFactory.java @@ -25,16 +25,13 @@ import java.util.concurrent.locks.ReentrantLock; import com.seibel.lod.core.api.ApiShared; import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler; +import com.seibel.lod.core.objects.Pos2D; import com.seibel.lod.core.objects.lod.LodDimension; import com.seibel.lod.core.objects.lod.RegionPos; import com.seibel.lod.core.objects.opengl.RenderRegion; import com.seibel.lod.core.render.LodRenderer; -import com.seibel.lod.core.util.LevelPosUtil; -import com.seibel.lod.core.util.LodThreadFactory; -import com.seibel.lod.core.util.LodUtil; -import com.seibel.lod.core.util.MovableGridRingList; -import com.seibel.lod.core.util.SpamReducedLogger; -import com.seibel.lod.core.util.StatsMap; +import com.seibel.lod.core.util.*; +import com.seibel.lod.core.util.gridList.MovableGridRingList; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; @@ -209,8 +206,8 @@ public class LodBufferBuilderFactory { // int cullingRangeZ = Math.max((int)(1.5 * Math.abs(lastZ - playerZ)), // minCullingRange); - MovableGridRingList.Pos minPos = renderRegions.getMinInRange(); - MovableGridRingList.Pos maxPos = renderRegions.getMaxInRange(); + Pos2D minPos = renderRegions.getMinInRange(); + Pos2D maxPos = renderRegions.getMaxInRange(); CompletableFuture future = CompletableFuture.completedFuture(null); try { diff --git a/src/main/java/com/seibel/lod/core/builders/worldGeneration/BatchGenerator.java b/src/main/java/com/seibel/lod/core/builders/worldGeneration/BatchGenerator.java index a677d2604..4be3570ef 100644 --- a/src/main/java/com/seibel/lod/core/builders/worldGeneration/BatchGenerator.java +++ b/src/main/java/com/seibel/lod/core/builders/worldGeneration/BatchGenerator.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.seibel.lod.core.builders.worldGeneration; +package com.seibel.lod.core.enums.worldGeneration; import com.seibel.lod.core.api.ApiShared; import com.seibel.lod.core.builders.lodBuilding.LodBuilder; diff --git a/src/main/java/com/seibel/lod/core/builders/worldGeneration/LodWorldGenerator.java b/src/main/java/com/seibel/lod/core/builders/worldGeneration/LodWorldGenerator.java deleted file mode 100644 index fc8fe3b2e..000000000 --- a/src/main/java/com/seibel/lod/core/builders/worldGeneration/LodWorldGenerator.java +++ /dev/null @@ -1,364 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL v3 License. - * - * Copyright (C) 2020 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.core.builders.worldGeneration; - -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import com.seibel.lod.core.api.ApiShared; -import com.seibel.lod.core.builders.lodBuilding.LodBuilder; -import com.seibel.lod.core.enums.config.DistanceGenerationMode; -import com.seibel.lod.core.enums.config.GenerationPriority; -import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler; -import com.seibel.lod.core.objects.PosToGenerateContainer; -import com.seibel.lod.core.objects.lod.LodDimension; -import com.seibel.lod.core.util.LevelPosUtil; -import com.seibel.lod.core.util.LodThreadFactory; -import com.seibel.lod.core.util.LodUtil; -import com.seibel.lod.core.wrapperInterfaces.IVersionConstants; -import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory; -import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; -import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; -import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; -import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; -import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper; - -/** - * A singleton that handles all long distance LOD world generation. - * - * @author Leonardo Amato - * @author James Seibel - * @version 12-11-2021 - */ -public class LodWorldGenerator { - private static final IMinecraftClientWrapper MC = SingletonHandler.get(IMinecraftClientWrapper.class); - private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); - private static final IWrapperFactory WRAPPER_FACTORY = SingletonHandler.get(IWrapperFactory.class); - private static final IVersionConstants VERSION_CONSTANTS = SingletonHandler.get(IVersionConstants.class); - - /** - * This holds the thread used to create LOD generation requests off the main - * thread. - */ - private final ExecutorService mainGenThread = Executors - .newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " world generator", 1)); - private ExecutorService genSubThreads = Executors.newFixedThreadPool( - CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(), - new LodThreadFactory("Gen-Worker-Thread", 1)); - - /** we only want to queue up one generator thread at a time */ - private boolean generatorThreadRunning = false; - - /** - * This keeps track of how many chunk generation requests are on going. This is - * to limit how many chunks are queued at once. To prevent chunks from being - * generated for a long time in an area the player is no longer in. - */ - public AtomicInteger numberOfChunksWaitingToGenerate = new AtomicInteger(0); - - public final Set positionsWaitingToBeGenerated = new HashSet<>(); - - /** - * Singleton copy of this object - */ - public static final LodWorldGenerator INSTANCE = new LodWorldGenerator(); - - private LodWorldGenerator() { - } - - /** - * Queues up LodNodeGenWorkers for the given lodDimension. renderer needed so - * the LodNodeGenWorkers can flag that the buffers need to be rebuilt. - */ - public void queueGenerationRequests(LodDimension lodDim, LodBuilder lodBuilder) { - if (!CONFIG.client().worldGenerator().getEnableDistantGeneration()) - return; - - // TODO: This currently doesn't use the - // DetailDistanceUtil.getDistanceGenerationMode(int detail) to get the mode. - // This is fine currently since DistanceGenerationMode doesn't care about the - // detail level for now. - // However, If that was to be changed, This will need to be fixed. - DistanceGenerationMode mode = CONFIG.client().worldGenerator().getDistanceGenerationMode(); - final GenerationPriority priority; - if (CONFIG.client().worldGenerator().getGenerationPriority() == GenerationPriority.AUTO) - priority = MC.hasSinglePlayerServer() ? GenerationPriority.FAR_FIRST : GenerationPriority.NEAR_FIRST; - else - priority = CONFIG.client().worldGenerator().getGenerationPriority(); - - if (mode != DistanceGenerationMode.NONE && !generatorThreadRunning && MC.hasSinglePlayerServer()) { - // the thread is now running, don't queue up another thread - generatorThreadRunning = true; - - /** - * How many chunks to generate outside the player's view distance at one time. - * (or more specifically how many requests to make at one time). I multiply by 8 - * to make sure there is always a buffer of chunk requests, to make sure the CPU - * is always busy, and we can generate LODs as quickly as possible. - */ - int genRequestPerThread = VERSION_CONSTANTS.getWorldGenerationCountPerThread(); - int maxChunkGenRequests; - if (VERSION_CONSTANTS.isWorldGeneratorSingleThreaded(mode)) - maxChunkGenRequests = CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads() - * genRequestPerThread; - else - maxChunkGenRequests = genRequestPerThread; - - Runnable generatorFunc = (() -> { - try { - // round the player's block position down to the nearest chunk BlockPos - int playerPosX = MC.getPlayerBlockPos().getX(); - int playerPosZ = MC.getPlayerBlockPos().getZ(); - - // =======================================// - // fill in positionsWaitingToBeGenerated // - // =======================================// - - IWorldWrapper serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension); - - PosToGenerateContainer posToGenerate = lodDim.getPosToGenerate(maxChunkGenRequests, playerPosX, - playerPosZ, priority, mode); - - byte detailLevel; - int posX; - int posZ; - int nearIndex = 0; - int farIndex = 0; - - for (int i = 0; i < posToGenerate.getNumberOfPos(); i++) { - // I wish there was a way to compress this code, but I'm not aware of - // an easy way to do so. - - // add the near positions - if (nearIndex < posToGenerate.getNumberOfNearPos() - && posToGenerate.getNthDetail(nearIndex, true) != 0) { - detailLevel = (byte) (posToGenerate.getNthDetail(nearIndex, true) - 1); - posX = posToGenerate.getNthPosX(nearIndex, true); - posZ = posToGenerate.getNthPosZ(nearIndex, true); - nearIndex++; - - AbstractChunkPosWrapper chunkPos = WRAPPER_FACTORY.createChunkPos( - LevelPosUtil.getChunkPos(detailLevel, posX), - LevelPosUtil.getChunkPos(detailLevel, posZ)); - - // prevent generating the same chunk multiple times - if (positionsWaitingToBeGenerated.contains(chunkPos)) - continue; - - // don't add more to the generation queue then allowed - if (numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests) - break; - - positionsWaitingToBeGenerated.add(chunkPos); - numberOfChunksWaitingToGenerate.addAndGet(1); - queueWork(chunkPos, mode, lodBuilder, lodDim, serverWorld); - } - - // add the far positions - // But if priority is NEAR_FIRST, we only do that if near pos has ran out. - if ((nearIndex >= posToGenerate.getNumberOfNearPos() - || priority != GenerationPriority.NEAR_FIRST) - && farIndex < posToGenerate.getNumberOfFarPos() - && posToGenerate.getNthDetail(farIndex, false) != 0) { - detailLevel = (byte) (posToGenerate.getNthDetail(farIndex, false) - 1); - posX = posToGenerate.getNthPosX(farIndex, false); - posZ = posToGenerate.getNthPosZ(farIndex, false); - farIndex++; - - AbstractChunkPosWrapper chunkPos = WRAPPER_FACTORY.createChunkPos( - LevelPosUtil.getChunkPos(detailLevel, posX), - LevelPosUtil.getChunkPos(detailLevel, posZ)); - - // don't add more to the generation queue then allowed - if (numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests) - continue; - // break; - - // prevent generating the same chunk multiple times - if (positionsWaitingToBeGenerated.contains(chunkPos)) - continue; - - positionsWaitingToBeGenerated.add(chunkPos); - numberOfChunksWaitingToGenerate.addAndGet(1); - queueWork(chunkPos, mode, lodBuilder, lodDim, serverWorld); - } - } - - } catch (RuntimeException e) { - // this shouldn't ever happen, but just in case - e.printStackTrace(); - } finally { - generatorThreadRunning = false; - } - }); - - if (VERSION_CONSTANTS.isWorldGeneratorSingleThreaded(mode)) { - generatorFunc.run(); - } else { - mainGenThread.execute(generatorFunc); - } - } // if distanceGenerationMode != DistanceGenerationMode.NONE && - // !generatorThreadRunning - } // queueGenerationRequests - - private void queueWork(AbstractChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode, - LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper serverWorld) { - // just a few sanity checks - if (newPos == null) - throw new IllegalArgumentException("LodChunkGenWorker must have a non-null ChunkPos"); - - if (newLodBuilder == null) - throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodChunkBuilder"); - - if (newLodDimension == null) - throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodDimension"); - - if (serverWorld == null) - throw new IllegalArgumentException("LodChunkGenThread requires a non-null ServerWorld"); - - Runnable method = (() -> { - generateChunk(newPos, newGenerationMode, newLodBuilder, newLodDimension, serverWorld); - }); - - if (VERSION_CONSTANTS.isWorldGeneratorSingleThreaded(newGenerationMode)) { - // --Note: This is now using version constants-- - // if we are using FULL generation there is no reason - // to queue up a bunch of generation requests, - // because MC's internal server (as of 1.16.5) only - // responds with a single thread. And we don't - // want to cause more lag than necessary or queue up - // requests that may end up being unneeded. - // In 1.17+, world generation becomes completely single - // threaded. So to allow that, we check the boolean for - // whether the wrapper requires single thread - method.run(); - } else { - // Every other method can - // be done asynchronously - genSubThreads.execute(method); - } - - // useful for debugging -// ClientProxy.LOGGER.info(thread.lodDim.getNumberOfLods()); -// ClientProxy.LOGGER.info(genThreads.toString()); - } - - private void generateChunk(AbstractChunkPosWrapper pos, DistanceGenerationMode generationMode, - LodBuilder newLodBuilder, LodDimension lodDim, IWorldWrapper worldWrapper) { - // try - { - AbstractWorldGeneratorWrapper worldGenWrapper = WRAPPER_FACTORY.createWorldGenerator(newLodBuilder, lodDim, - worldWrapper); - // only generate LodChunks if they can - // be added to the current LodDimension - - if (lodDim.regionIsInRange(pos.getX() / LodUtil.REGION_WIDTH_IN_CHUNKS, - pos.getZ() / LodUtil.REGION_WIDTH_IN_CHUNKS)) { - switch (generationMode) { - case NONE: - // don't generate - break; - case BIOME_ONLY: - case BIOME_ONLY_SIMULATE_HEIGHT: - // fastest - worldGenWrapper.generateBiomesOnly(pos, generationMode); - break; - case SURFACE: - // faster - worldGenWrapper.generateSurface(pos); - break; - case FEATURES: - // fast - worldGenWrapper.generateFeatures(pos); - break; - case FULL: - // very slow - worldGenWrapper.generateFull(pos); - break; - } - -// boolean dataExistence = lodDim.doesDataExist(new LevelPos((byte) 3, pos.x, pos.z)); -// if (dataExistence) -// ClientProxy.LOGGER.info(pos.x + " " + pos.z + " Success!"); -// else -// ClientProxy.LOGGER.info(pos.x + " " + pos.z); - - // shows the pool size, active threads, queued tasks and completed tasks -// ClientProxy.LOGGER.info(genThreads.toString()); - - } // if in range - } - // catch (Exception e) - // { - // ApiShared.LOGGER.error(LodWorldGenerator.class.getSimpleName() + ": ran into - // an error: " + e.getMessage()); - // e.printStackTrace(); - // } - // finally - { - // decrement how many threads are running - LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.addAndGet(-1); - - // this position is no longer being generated - LodWorldGenerator.INSTANCE.positionsWaitingToBeGenerated.remove(pos); - } - }// run - - /** - * Stops the current genThreads if they are running and then recreates the - * Executor service.
- *
- *

- * This is done to clear any outstanding tasks that may exist after the player - * leaves their current world. If this isn't done unfinished tasks may be left - * in the queue preventing new LodChunks form being generated. - */ - public void restartExecutorService() { - - if (genSubThreads != null && !genSubThreads.isShutdown()) { - ApiShared.LOGGER.info("Blocking until generator sub threads terminated!!"); - try { - mainGenThread.shutdownNow(); - genSubThreads.shutdownNow(); - boolean worked = genSubThreads.awaitTermination(30, TimeUnit.SECONDS); - if (!worked) - ApiShared.LOGGER.error( - "Generator sub threads timed out! May cause crash on game exit due to cleanup failure."); - } catch (InterruptedException e) { - ApiShared.LOGGER.error( - "Generator sub threads shutdown is interrupted! May cause crash on game exit due to cleanup failure."); - e.printStackTrace(); - } finally { - genSubThreads = Executors.newFixedThreadPool( - CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(), - new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build()); - } - } - // Doing this instead of setting it to 0 because even if shutdown fail, it won't - // cause the int to underflow below 0 afterwards - numberOfChunksWaitingToGenerate = new AtomicInteger(0); - } - -} diff --git a/src/main/java/com/seibel/lod/core/dataFormat/DataMergeUtil.java b/src/main/java/com/seibel/lod/core/dataFormat/DataMergeUtil.java index 44f1ead7f..5a5d8470d 100644 --- a/src/main/java/com/seibel/lod/core/dataFormat/DataMergeUtil.java +++ b/src/main/java/com/seibel/lod/core/dataFormat/DataMergeUtil.java @@ -1,5 +1,6 @@ package com.seibel.lod.core.dataFormat; +@Deprecated //Unused public class DataMergeUtil { public static void shrinkArray(short[] array, int packetSize, int start, int length, int arraySize) diff --git a/src/main/java/com/seibel/lod/core/objects/BoolType.java b/src/main/java/com/seibel/lod/core/objects/BoolType.java new file mode 100644 index 000000000..a13b4a776 --- /dev/null +++ b/src/main/java/com/seibel/lod/core/objects/BoolType.java @@ -0,0 +1,7 @@ +package com.seibel.lod.core.objects; + +public final class BoolType { + public static final BoolType TRUE = new BoolType(); + public static final BoolType FALSE = null; + private BoolType() {} +} diff --git a/src/main/java/com/seibel/lod/core/objects/Pos2D.java b/src/main/java/com/seibel/lod/core/objects/Pos2D.java new file mode 100644 index 000000000..3dbc3672a --- /dev/null +++ b/src/main/java/com/seibel/lod/core/objects/Pos2D.java @@ -0,0 +1,10 @@ +package com.seibel.lod.core.objects; + +public final class Pos2D { + public final int x; + public final int y; + public Pos2D(int x, int y) { + this.x = x; + this.y = y; + } +} diff --git a/src/main/java/com/seibel/lod/core/objects/lod/LodDimension.java b/src/main/java/com/seibel/lod/core/objects/lod/LodDimension.java index 49b0b3996..6f80bffb8 100644 --- a/src/main/java/com/seibel/lod/core/objects/lod/LodDimension.java +++ b/src/main/java/com/seibel/lod/core/objects/lod/LodDimension.java @@ -28,10 +28,10 @@ import com.seibel.lod.core.enums.config.VerticalQuality; import com.seibel.lod.core.handlers.LodDimensionFileHandler; import com.seibel.lod.core.handlers.LodDimensionFileHelper; import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler; +import com.seibel.lod.core.objects.Pos2D; import com.seibel.lod.core.objects.PosToGenerateContainer; import com.seibel.lod.core.util.*; -import com.seibel.lod.core.util.MovableGridRingList.Pos; -import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory; +import com.seibel.lod.core.util.gridList.MovableGridRingList; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper; @@ -62,7 +62,6 @@ public class LodDimension { private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); private static final IMinecraftClientWrapper MC = SingletonHandler.get(IMinecraftClientWrapper.class); - private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class); public final IDimensionTypeWrapper dimension; @@ -164,21 +163,18 @@ public class LodDimension private void generateIteratorList() { iteratorList = null; - RegionPos[] list = new RegionPos[width * width]; + RegionPos[] list = new RegionPos[width*width]; int i = 0; - for (int ix = -halfWidth; ix <= halfWidth; ix++) - { - for (int iz = -halfWidth; iz <= halfWidth; iz++) - { + for (int ix=-halfWidth; ix<=halfWidth; ix++) { + for (int iz=-halfWidth; iz<=halfWidth; iz++) { list[i] = new RegionPos(ix, iz); i++; } } - Arrays.sort(list, (a, b) -> - { - double disSqrA = a.x * a.x + a.z * a.z; - double disSqrB = b.x * b.x + b.z * b.z; + Arrays.sort(list, (a, b) -> { + double disSqrA = a.x* a.x+ a.z* a.z; + double disSqrB = b.x* b.x+ b.z* b.z; return Double.compare(disSqrA, disSqrB); }); iteratorList = list; @@ -195,11 +191,11 @@ public class LodDimension */ public synchronized void move(RegionPos regionOffset) { - ApiShared.LOGGER.info("LodDim MOVE. Offset: " + regionOffset); + ApiShared.LOGGER.info("LodDim MOVE. Offset: "+regionOffset); saveDirtyRegionsToFile(false); //async add dirty regions to be saved. - Pos p = regions.getCenter(); - regions.move(p.x + regionOffset.x, p.y + regionOffset.z); - ApiShared.LOGGER.info("LodDim MOVE complete. Offset: " + regionOffset); + Pos2D p = regions.getCenter(); + regions.move(p.x+regionOffset.x, p.y+regionOffset.z); + ApiShared.LOGGER.info("LodDim MOVE complete. Offset: "+regionOffset); } @@ -237,8 +233,8 @@ public class LodDimension @Deprecated public LodRegion getRegionByArrayIndex(int xIndex, int zIndex) { - Pos p = regions.getMinInRange(); - return regions.get(p.x + xIndex, p.y + zIndex); + Pos2D p = regions.getMinInRange(); + return regions.get(p.x+xIndex, p.y+zIndex); } /** @@ -253,45 +249,36 @@ public class LodDimension regions[xIndex][zIndex] = newRegion; }*/ - public interface PosComsumer - { + public interface PosComsumer { void run(int x, int z); } - public void iterateWithSpiral(PosComsumer r) - { - int ox, oy, dx, dy; - ox = oy = dx = 0; - dy = -1; - int len = regions.getSize(); - int maxI = len * len; - int halfLen = len / 2; - for (int i = 0; i < maxI; i++) - { - if ((-halfLen <= ox) && (ox <= halfLen) && (-halfLen <= oy) && (oy <= halfLen)) - { - int x = ox + halfLen; - int z = oy + halfLen; - r.run(x, z); - } - if ((ox == oy) || ((ox < 0) && (ox == -oy)) || ((ox > 0) && (ox == 1 - oy))) - { - int temp = dx; - dx = -dy; - dy = temp; - } - ox += dx; - oy += dy; - } + public void iterateWithSpiral(PosComsumer r) { + int ox,oy,dx,dy; + ox = oy = dx = 0; + dy = -1; + int len = regions.getSize(); + int maxI = len*len; + int halfLen = len/2; + for(int i =0; i < maxI; i++){ + if ((-halfLen <= ox) && (ox <= halfLen) && (-halfLen <= oy) && (oy <= halfLen)){ + int x = ox+halfLen; + int z = oy+halfLen; + r.run(x, z); + } + if( (ox == oy) || ((ox < 0) && (ox == -oy)) || ((ox > 0) && (ox == 1-oy))){ + int temp = dx; + dx = -dy; + dy = temp; + } + ox += dx; + oy += dy; + } } - - public void iterateByDistance(PosComsumer r) - { - if (iteratorList == null) - return; - for (RegionPos relativePos : iteratorList) - { - r.run(relativePos.x + halfWidth, relativePos.z + halfWidth); + public void iterateByDistance(PosComsumer r) { + if (iteratorList==null) return; + for (RegionPos relativePos : iteratorList) { + r.run(relativePos.x+halfWidth, relativePos.z+halfWidth); } } @@ -305,49 +292,40 @@ public class LodDimension public void cutRegionNodesAsync(int playerPosX, int playerPosZ) { - if (isCutting) - return; + if (isCutting) return; isCutting = true; // don't run the tree cutter multiple times // for the same location - Runnable thread = () -> - { + Runnable thread = () -> { //ApiShared.LOGGER.info("LodDim cut Region: " + playerPosX + "," + playerPosZ); totalDirtiedRegions = 0; - Pos minPos = regions.getMinInRange(); + Pos2D minPos = regions.getMinInRange(); // go over every region in the dimension - iterateWithSpiral((int x, int z) -> - { + iterateWithSpiral((int x, int z) -> { double minDistance; byte detail; - LodRegion region = regions.get(x + minPos.x, z + minPos.y); - if (region != null && region.needSaving) - totalDirtiedRegions++; - if (region != null && !region.needSaving && region.isWriting.get() == 0) - { + LodRegion region = regions.get(x+minPos.x, z+minPos.y); + if (region != null && region.needSaving) totalDirtiedRegions++; + if (region != null && !region.needSaving && region.isWriting.get()==0) { // check what detail level this region should be // and cut it if it is higher then that - minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, x + minPos.x, z + minPos.y, + minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, x+minPos.x, z+minPos.y, playerPosX, playerPosZ); detail = DetailDistanceUtil.getDetailLevelFromDistance(minDistance); - if (region.getMinDetailLevel() < detail) - { - if (region.needSaving) - return; // FIXME: A crude attempt at lowering chance of race condition! + if (region.getMinDetailLevel() < detail) { + if (region.needSaving) return; // FIXME: A crude attempt at lowering chance of race condition! region.cutTree(detail); region.needSignalToRegenBuffer = true; } } - if (region != null && region.needSignalToRegenBuffer) - { + if (region != null && region.needSignalToRegenBuffer) { region.needSignalToRegenBuffer = false; - ClientApi.lodBufferBuilderFactory.setRegionNeedRegen(x + minPos.x, z + minPos.y); + ClientApi.lodBufferBuilderFactory.setRegionNeedRegen(x+minPos.x, z+minPos.y); } }); - if (totalDirtiedRegions > 8) - this.saveDirtyRegionsToFile(false); + if (totalDirtiedRegions > 8) this.saveDirtyRegionsToFile(false); dirtiedRegionsRoughCount = totalDirtiedRegions; //ApiShared.LOGGER.info("LodDim cut Region complete: " + playerPosX + "," + playerPosZ); isCutting = false; @@ -356,20 +334,15 @@ public class LodDimension }; cutAndExpandThread.execute(thread); } - + private boolean expandOrLoadPaused = false; - /** Either expands or loads all regions in the rendered LOD area */ - public void expandOrLoadRegionsAsync(int playerPosX, int playerPosZ) - { - - if (isExpanding) - return; + public void expandOrLoadRegionsAsync(int playerPosX, int playerPosZ) { + + if (isExpanding) return; // If we have less than 20% or 128MB ram left. Don't expend. - if (expandOrLoadPaused) - { - if (LodUtil.checkRamUsage(0.2, 128)) - { + if (expandOrLoadPaused) { + if (LodUtil.checkRamUsage(0.2, 128)) { ApiShared.LOGGER.info("Enough ram for expandOrLoadThread. Restarting..."); expandOrLoadPaused = false; } @@ -379,21 +352,18 @@ public class LodDimension VerticalQuality verticalQuality = CONFIG.client().graphics().quality().getVerticalQuality(); DropoffQuality dropoffQuality = CONFIG.client().graphics().quality().getDropoffQuality(); if (dropoffQuality == DropoffQuality.AUTO) - dropoffQuality = CONFIG.client().graphics().quality().getLodChunkRenderDistance() < 128 ? DropoffQuality.SMOOTH_DROPOFF : DropoffQuality.PERFORMANCE_FOCUSED; + dropoffQuality = CONFIG.client().graphics().quality().getLodChunkRenderDistance() < 128 ? + DropoffQuality.SMOOTH_DROPOFF : DropoffQuality.PERFORMANCE_FOCUSED; int dropoffSwitch = dropoffQuality.fastModeSwitch; // don't run the expander multiple times // for the same location - Runnable thread = () -> - { + Runnable thread = () -> { //ApiShared.LOGGER.info("LodDim expend Region: " + playerPosX + "," + playerPosZ); - Pos minPos = regions.getMinInRange(); - iterateWithSpiral((int x, int z) -> - { - if (!expandOrLoadPaused && !LodUtil.checkRamUsage(0.02, 64)) - { + Pos2D minPos = regions.getMinInRange(); + iterateWithSpiral((int x, int z) -> { + if (!expandOrLoadPaused && !LodUtil.checkRamUsage(0.02, 64)) { Runtime.getRuntime().gc(); - if (!LodUtil.checkRamUsage(0.2, 128)) - { + if (!LodUtil.checkRamUsage(0.2, 128)) { ApiShared.LOGGER.warn("Not enough ram for expandOrLoadThread. Pausing until Ram is freed..."); // We have less than 10% or 64MB ram left. Don't expend. expandOrLoadPaused = true; @@ -412,21 +382,19 @@ public class LodDimension regionZ = z + minPos.y; final RegionPos regionPos = new RegionPos(regionX, regionZ); region = regions.get(regionX, regionZ); - if (region != null && region.isWriting.get() != 0) - return; // FIXME: A crude attempt at lowering chance of race condition! - + if (region != null && region.isWriting.get()!=0) return; // FIXME: A crude attempt at lowering chance of race condition! + minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX, playerPosZ); maxDistance = LevelPosUtil.maxDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX, playerPosZ); { - double debugRPosX = LevelPosUtil.convert(LodUtil.REGION_DETAIL_LEVEL, regionX, (byte) 0) + LodUtil.REGION_WIDTH / 2; - double debugRPosZ = LevelPosUtil.convert(LodUtil.REGION_DETAIL_LEVEL, regionZ, (byte) 0) + LodUtil.REGION_WIDTH / 2; + double debugRPosX = LevelPosUtil.convert(LodUtil.REGION_DETAIL_LEVEL, regionX, (byte) 0) + LodUtil.REGION_WIDTH/2; + double debugRPosZ = LevelPosUtil.convert(LodUtil.REGION_DETAIL_LEVEL, regionZ, (byte) 0) + LodUtil.REGION_WIDTH/2; double deltaRPosX = debugRPosX - playerPosX; double deltaRPosZ = debugRPosZ - playerPosZ; - double debugDistance = Math.sqrt(deltaRPosX * deltaRPosX + deltaRPosZ * deltaRPosZ); - if (minDistance > debugDistance || maxDistance < debugDistance || minDistance > maxDistance) - { + double debugDistance = Math.sqrt(deltaRPosX*deltaRPosX + deltaRPosZ*deltaRPosZ); + if (minDistance > debugDistance || maxDistance < debugDistance || minDistance > maxDistance) { ApiShared.LOGGER.error("MinDistance/MaxDistance is WRONG!!! minDist: [{}], maxDist: [{}], centerDist: [{}]\n" + "At center block pos: {} {}, region pos: {}", minDistance, maxDistance, debugDistance, debugRPosX, debugRPosZ, regionPos); @@ -436,50 +404,39 @@ public class LodDimension minDetail = DetailDistanceUtil.getDetailLevelFromDistance(minDistance); maxDetail = DetailDistanceUtil.getDetailLevelFromDistance(maxDistance); boolean updated = false; - if (region == null) - { - if ((!expandOrLoadPaused)) - { + if (region == null) { + if ((!expandOrLoadPaused)) { region = getRegionFromFile(regionPos, minDetail, verticalQuality); regions.set(regionX, regionZ, region); updated = true; } - } - else if (region.getVerticalQuality() != verticalQuality || - region.getMinDetailLevel() > minDetail) - { + } else if (region.getVerticalQuality() != verticalQuality || + region.getMinDetailLevel() > minDetail) { // The 'getRegionFromFile' will flush and save the region if it returns a new one - if ((!expandOrLoadPaused)) - { + if ((!expandOrLoadPaused)) { region = getRegionFromFile(region, minDetail, verticalQuality); regions.set(regionX, regionZ, region); updated = true; } - } - else if (minDetail <= dropoffSwitch && region.lastMaxDetailLevel != maxDetail) - { + } else if (minDetail <= dropoffSwitch && region.lastMaxDetailLevel != maxDetail) { region.lastMaxDetailLevel = maxDetail; updated = true; - } - else if (minDetail <= dropoffSwitch && region.lastMaxDetailLevel != region.getMinDetailLevel()) - { + } else if (minDetail <= dropoffSwitch && region.lastMaxDetailLevel != region.getMinDetailLevel()) { updated = true; } - if (updated) - { + if (updated) { region.needSignalToRegenBuffer = true; region.needRecheckGenPoint = true; } - if (region != null && region.needSignalToRegenBuffer) - { + if (region != null && region.needSignalToRegenBuffer) { region.needSignalToRegenBuffer = false; - ClientApi.lodBufferBuilderFactory.setRegionNeedRegen(x + minPos.x, z + minPos.y); + ClientApi.lodBufferBuilderFactory.setRegionNeedRegen(x+minPos.x, z+minPos.y); } }); //ApiShared.LOGGER.info("LodDim expend Region complete: " + playerPosX + "," + playerPosZ); isExpanding = false; }; - + cutAndExpandThread.execute(thread); } @@ -514,39 +471,34 @@ public class LodDimension // This ensures that we don't spawn way too much regions without finish flushing them first. //if (dirtiedRegionsRoughCount > 16) return posToGenerate; - GenerationPriority allowedPriority = dirtiedRegionsRoughCount > 12 ? GenerationPriority.NEAR_FIRST : priority; - Pos minPos = regions.getMinInRange(); - iterateByDistance((int x, int z) -> - { - boolean isCloseRange = (Math.abs(x - halfWidth) + Math.abs(z - halfWidth) <= 2); + GenerationPriority allowedPriority = dirtiedRegionsRoughCount>12 ? GenerationPriority.NEAR_FIRST : priority; + Pos2D minPos = regions.getMinInRange(); + iterateByDistance((int x, int z) -> { + boolean isCloseRange = (Math.abs(x-halfWidth)+Math.abs(z-halfWidth)<=2); //boolean isCloseRange = true; //All of this is handled directly by the region, which scan every pos from top to bottom of the quad tree - LodRegion lodRegion = regions.get(minPos.x + x, minPos.y + z); + LodRegion lodRegion = regions.get(minPos.x+x, minPos.y+z); - if (lodRegion != null && lodRegion.needRecheckGenPoint) - { + if (lodRegion != null && lodRegion.needRecheckGenPoint) { int nearCount = posToGenerate.getNumberOfNearPos(); int farCount = posToGenerate.getNumberOfFarPos(); boolean checkForFlag = (nearCount < posToGenerate.getMaxNumberOfNearPos() && farCount < posToGenerate.getMaxNumberOfFarPos()); - if (checkForFlag) - { + if (checkForFlag) { lodRegion.needRecheckGenPoint = false; } lodRegion.getPosToGenerate(posToGenerate, playerBlockPosX, playerBlockPosZ, allowedPriority, genMode, isCloseRange); - if (checkForFlag) - { - if (nearCount != posToGenerate.getNumberOfNearPos() || farCount != posToGenerate.getNumberOfFarPos()) - { + if (checkForFlag) { + if (nearCount != posToGenerate.getNumberOfNearPos() || farCount != posToGenerate.getNumberOfFarPos()) { lodRegion.needRecheckGenPoint = true; } } } }); - return posToGenerate; + return posToGenerate; } - + /** * Determines how many vertical LODs could be used * for the given region at the given detail level @@ -632,12 +584,11 @@ public class LodDimension { if (detailLevel > LodUtil.REGION_DETAIL_LEVEL) throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max."); - + int xRegion = LevelPosUtil.getRegion(detailLevel, posX); int zRegion = LevelPosUtil.getRegion(detailLevel, posZ); LodRegion region = getRegion(xRegion, zRegion); - if (region == null) - return; + if (region == null) return; region.updateArea(detailLevel, posX, posZ); } @@ -654,23 +605,23 @@ public class LodDimension */ public LodRegion getRegionFromFile(RegionPos regionPos, byte detailLevel, VerticalQuality verticalQuality) { - return fileHandler != null ? fileHandler.loadRegionFromFile(detailLevel, regionPos, verticalQuality) : new LodRegion(detailLevel, regionPos, verticalQuality); + return fileHandler != null ? fileHandler.loadRegionFromFile(detailLevel, regionPos, verticalQuality) : + new LodRegion(detailLevel, regionPos, verticalQuality); } - /** * Loads the region at the given region from file, * if a file exists for that region. */ public LodRegion getRegionFromFile(LodRegion existingRegion, byte detailLevel, VerticalQuality verticalQuality) { - return fileHandler != null ? fileHandler.loadRegionFromFile(detailLevel, existingRegion, verticalQuality) : new LodRegion(detailLevel, existingRegion.getRegionPos(), verticalQuality); + return fileHandler != null ? fileHandler.loadRegionFromFile(detailLevel, existingRegion, verticalQuality) : + new LodRegion(detailLevel, existingRegion.getRegionPos(), verticalQuality); } /** Save all dirty regions in this LodDimension to file. */ public void saveDirtyRegionsToFile(boolean blockUntilFinished) { - if (fileHandler == null) - return; + if (fileHandler == null) return; fileHandler.saveDirtyRegionsToFile(blockUntilFinished); } @@ -709,9 +660,8 @@ public class LodDimension return regions.getCenter().y; } - public RegionPos getCenterRegionPos() - { - Pos p = regions.getCenter(); + public RegionPos getCenterRegionPos() { + Pos2D p = regions.getCenter(); return new RegionPos(p.x, p.y); } @@ -728,19 +678,17 @@ public class LodDimension public void setRegionWidth(int newWidth) { width = newWidth; - halfWidth = width / 2; - Pos p = regions.getCenter(); + halfWidth = width/ 2; + Pos2D p = regions.getCenter(); regions = new MovableGridRingList(halfWidth, p.x, p.y); generateIteratorList(); } private final SpamReducedLogger ramLogger = new SpamReducedLogger(1); - public void dumpRamUsage() { - if (!ramLogger.canMaybeLog()) - return; - int regionCount = width * width; + if (!ramLogger.canMaybeLog()) return; + int regionCount = width*width; ramLogger.info("Dumping Ram Usage for LodDim in {} with {} regions...", dimension.getDimensionName(), regionCount); int nonNullRegionCount = 0; int dirtiedRegionCount = 0; @@ -748,25 +696,18 @@ public class LodDimension long totalUsage = 0; int[] detailCount = new int[LodUtil.DETAIL_OPTIONS]; long[] detailUsage = new long[LodUtil.DETAIL_OPTIONS]; - for (LodRegion r : regions) - { - if (r == null) - continue; + for (LodRegion r : regions) { + if (r==null) continue; nonNullRegionCount++; - if (r.needSaving) - dirtiedRegionCount++; - if (r.isWriting.get() != 0) - writingRegionCount++; + if (r.needSaving) dirtiedRegionCount++; + if (r.isWriting.get() != 0) writingRegionCount++; LevelContainer[] container = r.debugGetDataContainers().clone(); - if (container == null || container.length != LodUtil.DETAIL_OPTIONS) - { + if (container == null || container.length != LodUtil.DETAIL_OPTIONS) { ApiShared.LOGGER.warn("DumpRamUsage encountered an invalid region!"); continue; } - for (int i = 0; i < LodUtil.DETAIL_OPTIONS; i++) - { - if (container[i] == null) - continue; + for (int i = 0; i < LodUtil.DETAIL_OPTIONS; i++) { + if (container[i] == null) continue; detailCount[i]++; long byteUsage = container[i].getRoughRamUsage(); detailUsage[i] += byteUsage; @@ -777,8 +718,7 @@ public class LodDimension ramLogger.info("Non Null Regions: [{}], Dirtied Regions: [{}], Writing Regions: [{}], Bytes: [{}]", nonNullRegionCount, dirtiedRegionCount, writingRegionCount, new UnitBytes(totalUsage)); ramLogger.info("------------------------------------------------"); - for (int i = 0; i < LodUtil.DETAIL_OPTIONS; i++) - { + for (int i = 0; i < LodUtil.DETAIL_OPTIONS; i++) { ramLogger.info("DETAIL {}: Containers: [{}], Bytes: [{}]", i, detailCount[i], new UnitBytes(detailUsage[i])); } ramLogger.info("================================================"); @@ -789,7 +729,7 @@ public class LodDimension @Override public String toString() { - return "[Dim = " + dimension.getDimensionName() + ", Region = " + regions + "]"; + return "[Dim = "+dimension.getDimensionName()+", Region = "+regions+"]"; } public String toDetailString() @@ -799,18 +739,14 @@ public class LodDimension stringBuilder.append(regions.toDetailString()); return stringBuilder.toString(); } - - public void shutdown() - { + + public void shutdown() { cutAndExpandThread.shutdown(); - try - { + try { boolean worked = cutAndExpandThread.awaitTermination(5, TimeUnit.SECONDS); if (!worked) ApiShared.LOGGER.error("Cut And Expend threads timed out! May cause crash on game exit due to cleanup failure."); - } - catch (InterruptedException e) - { + } catch (InterruptedException e) { ApiShared.LOGGER.error("Cut And Expend threads shutdown is interrupted! May cause crash on game exit due to cleanup failure: ", e); } diff --git a/src/main/java/com/seibel/lod/core/objects/lod/VerticalLevelContainer.java b/src/main/java/com/seibel/lod/core/objects/lod/VerticalLevelContainer.java index 9d9306bb3..596436474 100644 --- a/src/main/java/com/seibel/lod/core/objects/lod/VerticalLevelContainer.java +++ b/src/main/java/com/seibel/lod/core/objects/lod/VerticalLevelContainer.java @@ -91,8 +91,7 @@ public class VerticalLevelContainer implements LevelContainer private void forceWriteVerticalData(long[] data, int posX, int posZ) { int index = posX * size * verticalSize + posZ * verticalSize; - for (int verticalIndex = 0; verticalIndex < verticalSize; verticalIndex++) - dataContainer[index + verticalIndex] = data[verticalIndex]; + if (verticalSize >= 0) System.arraycopy(data, 0, dataContainer, index + 0, verticalSize); } @Override diff --git a/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java b/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java index ad2816ecd..26b5c12f7 100644 --- a/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java +++ b/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java @@ -21,7 +21,6 @@ import com.seibel.lod.core.objects.PosToRenderContainer; import com.seibel.lod.core.objects.lod.LodDimension; import com.seibel.lod.core.objects.lod.LodRegion; import com.seibel.lod.core.objects.lod.RegionPos; -import com.seibel.lod.core.objects.math.Mat4f; import com.seibel.lod.core.objects.math.Vec3d; import com.seibel.lod.core.objects.math.Vec3f; import com.seibel.lod.core.render.GLProxy; @@ -31,7 +30,7 @@ import com.seibel.lod.core.util.DataPointUtil; import com.seibel.lod.core.util.DetailDistanceUtil; import com.seibel.lod.core.util.LevelPosUtil; import com.seibel.lod.core.util.LodUtil; -import com.seibel.lod.core.util.MovableGridList; +import com.seibel.lod.core.util.gridList.MovableCenteredGridList; import com.seibel.lod.core.util.StatsMap; import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; @@ -243,8 +242,8 @@ public class RenderRegion implements AutoCloseable { 1, 1} }; - private static MovableGridList shinkGridEdge(MovableGridList target) { - MovableGridList result = new MovableGridList( + private static MovableCenteredGridList shinkGridEdge(MovableCenteredGridList target) { + MovableCenteredGridList result = new MovableCenteredGridList( target.gridCentreToEdge-1, target.getCenterX(), target.getCenterY()); int chunkGridMinX = target.getCenterX() - target.gridCentreToEdge; int chunkGridMinZ = target.getCenterY() - target.gridCentreToEdge; @@ -274,7 +273,7 @@ public class RenderRegion implements AutoCloseable // position PosToRenderContainer posToRender = new PosToRenderContainer(minDetail, region.regionPosX, region.regionPosZ); region.getPosToRender(posToRender, playerX, playerZ); - MovableGridList chunkGrid = ClientApi.renderer.vanillaRenderedChunks; + MovableCenteredGridList chunkGrid = ClientApi.renderer.vanillaRenderedChunks; if (CONFIG.client().graphics().advancedGraphics().getVanillaOverdraw() == VanillaOverdraw.BORDER) { chunkGrid = shinkGridEdge(chunkGrid); } diff --git a/src/main/java/com/seibel/lod/core/render/LodRenderer.java b/src/main/java/com/seibel/lod/core/render/LodRenderer.java index 0c3efefe6..02cc7f5ae 100644 --- a/src/main/java/com/seibel/lod/core/render/LodRenderer.java +++ b/src/main/java/com/seibel/lod/core/render/LodRenderer.java @@ -24,7 +24,10 @@ import java.time.Duration; import java.util.Set; import java.util.concurrent.TimeUnit; +import com.seibel.lod.core.objects.Pos2D; import com.seibel.lod.core.util.*; +import com.seibel.lod.core.util.gridList.MovableCenteredGridList; +import com.seibel.lod.core.util.gridList.MovableGridRingList; import org.lwjgl.opengl.GL32; import com.seibel.lod.core.api.ApiShared; @@ -32,7 +35,6 @@ import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory; import com.seibel.lod.core.enums.rendering.DebugMode; import com.seibel.lod.core.enums.rendering.FogColorMode; import com.seibel.lod.core.enums.rendering.FogDistance; -import com.seibel.lod.core.handlers.IReflectionHandler; import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler; import com.seibel.lod.core.objects.lod.LodDimension; import com.seibel.lod.core.objects.math.Mat4f; @@ -119,7 +121,7 @@ public class LodRenderer * This HashSet contains every chunk that Vanilla Minecraft * is going to render */ - public MovableGridList vanillaRenderedChunks; + public MovableCenteredGridList vanillaRenderedChunks; public int vanillaRenderedChunksCenterX; public int vanillaRenderedChunksCenterZ; public int vanillaRenderedChunksRefreshTimer; @@ -354,7 +356,7 @@ public class LodRenderer int halfLen = len/2; for(int i =0; i < maxI; i++){ if ((-halfLen <= ox) && (ox <= halfLen) && (-halfLen <= oy) && (oy <= halfLen)){ - MovableGridRingList.Pos pos = regions.getCenter(); + Pos2D pos = regions.getCenter(); int regionX = ox+pos.x; int regionZ = oy+pos.y; { @@ -527,16 +529,16 @@ public class LodRenderer // if the player is high enough, draw all LODs IWorldWrapper world = MC.getWrappedClientWorld(); if (lastUpdatedPos.getY() > world.getHeight()-world.getMinHeight()) { - vanillaRenderedChunks = new MovableGridList( + vanillaRenderedChunks = new MovableCenteredGridList( chunkRenderDistance, chunkX, chunkZ); return true; } - MovableGridList chunkList; + MovableCenteredGridList chunkList; boolean anyChanged = false; if (vanillaRenderedChunks == null || vanillaRenderedChunks.gridCentreToEdge != chunkRenderDistance || vanillaRenderedChunks.getCenterX()!=chunkX || vanillaRenderedChunks.getCenterY()!=chunkZ) { - chunkList = new MovableGridList(chunkRenderDistance, chunkX, chunkZ); + chunkList = new MovableCenteredGridList(chunkRenderDistance, chunkX, chunkZ); anyChanged = true; } else { chunkList = vanillaRenderedChunks; diff --git a/src/main/java/com/seibel/lod/core/util/BooleanMovableGridList.java b/src/main/java/com/seibel/lod/core/util/BooleanMovableGridList.java deleted file mode 100644 index adea98107..000000000 --- a/src/main/java/com/seibel/lod/core/util/BooleanMovableGridList.java +++ /dev/null @@ -1,245 +0,0 @@ -package com.seibel.lod.core.util; - -/*Layout: - * 0,1,2, - * 3,4,5, - * 6,7,8 - */ - -public class BooleanMovableGridList { - - private int centerX; - private int centerY; - - public final int gridCentreToEdge; - public final int gridSize; - private boolean[] b; - - public BooleanMovableGridList(int gridCentreToEdge, int centerX, int centerY) { - gridSize = gridCentreToEdge * 2 + 1; - this.gridCentreToEdge = gridCentreToEdge; - this.centerX = centerX; - this.centerY = centerY; - clear(); - } - - public void clear() { - b = new boolean[gridSize*gridSize]; - } - - public int getCenterX() {return centerX;} - public int getCenterY() {return centerY;} - - private void assertIndex(int ix, int iy) { - if (ix<0 || ix>=gridSize || iy<0 || iy>=gridSize) - throw new IndexOutOfBoundsException("BooleanMovableGridList index position out of bound"); - } - - public boolean isInBound(int x, int y) { - x = x-centerX+gridCentreToEdge; - y = y-centerY+gridCentreToEdge; - return !(x<0 || x>=gridSize || y<0 || y>=gridSize); - } - - // return onFail if x,y is outside of the grid - public boolean get(int x, int y) { - x = x-centerX+gridCentreToEdge; - y = y-centerY+gridCentreToEdge; - return _getDirect(x,y); - } - - // return false if x,y is outside of the grid - public void set(int x, int y, boolean t) { - x = x-centerX+gridCentreToEdge; - y = y-centerY+gridCentreToEdge; - _setDirect(x,y,t); - } - - // return onFail if x,y is outside of the grid - // Otherwise, return the new value (for chaining) - public boolean setAndGet(int x, int y, boolean t) { - x = x-centerX+gridCentreToEdge; - y = y-centerY+gridCentreToEdge; - _setDirect(x,y,t); - return t; - } - // return null if x,y is outside of the grid - // Otherwise, return the old value - public boolean swap(int x, int y, boolean t, boolean onFail) { - x = x-centerX+gridCentreToEdge; - y = y-centerY+gridCentreToEdge; - return _swapDirect(x,y, t); - } - - private boolean _getDirect(int x, int y) { - assertIndex(x,y); - return b[x + y * gridSize]; - } - private void _setDirect(int x, int y, boolean t) { - assertIndex(x,y); - b[x + y * gridSize] = t; - } - private boolean _swapDirect(int x, int y, boolean t) { - assertIndex(x,y); - boolean r = b[x + y * gridSize]; - b[x + y * gridSize] = t; - return r; - } - - interface BoolTransformer { - boolean transform(boolean oldValue, int x, int y); - } - - // Transform the list via the function. The data can still be accessed - // inside the function, and the returned value will not be applied - // until all elements have done the transform. - public void twoStageTransform(BoolTransformer transformer) { - boolean[] nb = new boolean[b.length]; - int i=0; - for (int y=0; y { - if (v!= valueToBeFlipped) return v; - boolean r = false; - r |= (isInBound(x - 1, y) && get(x - 1, y) == !valueToBeFlipped); - r |= (isInBound(x, y - 1) && get(x, y - 1) == !valueToBeFlipped); - r |= (isInBound(x + 1, y) && get(x + 1, y) == !valueToBeFlipped); - r |= (isInBound(x, y + 1) && get(x, y + 1) == !valueToBeFlipped); - return r != valueToBeFlipped; - }; - twoStageTransform(tran); - } - public void flipBorderCorner(boolean valueToBeFlipped) { - BoolTransformer tran = (v, x, y) -> { - if (v!= valueToBeFlipped) return v; - boolean r = false; - r |= (isInBound(x - 1, y) && get(x - 1, y) == !valueToBeFlipped); - r |= (isInBound(x, y - 1) && get(x, y - 1) == !valueToBeFlipped); - r |= (isInBound(x + 1, y) && get(x + 1, y) == !valueToBeFlipped); - r |= (isInBound(x, y + 1) && get(x, y + 1) == !valueToBeFlipped); - r |= (isInBound(x - 1, y - 1) && get(x - 1, y - 1) == !valueToBeFlipped); - r |= (isInBound(x + 1, y - 1) && get(x + 1, y - 1) == !valueToBeFlipped); - r |= (isInBound(x + 1, y + 1) && get(x + 1, y + 1) == !valueToBeFlipped); - r |= (isInBound(x - 1, y + 1) && get(x - 1, y + 1) == !valueToBeFlipped); - return r != valueToBeFlipped; - }; - twoStageTransform(tran); - } - public void flipBorderCorner(boolean valueToBeFlipped, int range) { - BoolTransformer tran = (v, x, y) -> { - if (v!= valueToBeFlipped) return v; - boolean r = false; - for (int dx=-range;dx<=range;dx++) - for (int dy=-range;dy<=range;dy++) - r |= (isInBound(x + dx, y + dy) && get(x + dx, y + dy) == !valueToBeFlipped); - return r != valueToBeFlipped; - }; - twoStageTransform(tran); - } - - - // Return false if haven't changed. Return true if it did - public boolean move(int newCenterX, int newCenterY) { - return move(newCenterX, newCenterY, false); - } - - // Return false if haven't changed. Return true if it did - public boolean move(int newCenterX, int newCenterY, boolean value) { - if (centerX == newCenterX && centerY == newCenterY) return false; - int deltaX = newCenterX - centerX; - int deltaY = newCenterY - centerY; - - // if the x or z offset is equal to or greater than - // the total width, just delete the current data - // and update the centerX and/or centerZ - if (Math.abs(deltaX) >= gridSize || Math.abs(deltaY) >= gridSize) - { - clear(); - // update the new center - centerX = newCenterX; - centerY = newCenterY; - return true; - } - centerX = newCenterX; - centerY = newCenterY; - - // X - if (deltaX >= 0 && deltaY >= 0) - { - // move everything over to the left-up (as the center moves to the right-down) - for (int x = 0; x < gridSize; x++) - { - for (int y = 0; y < gridSize; y++) - { - _setDirect(x, y, _getDirect(x+deltaX, y+deltaY)); - } - } - } - else if (deltaX < 0 && deltaY >= 0) - { - // move everything over to the right-up (as the center moves to the left-down) - for (int x = gridSize - 1; x >= 0; x--) - { - for (int y = 0; y < gridSize; y++) - { - _setDirect(x, y, _getDirect(x+deltaX, y+deltaY)); - } - } - } - else if (deltaX >= 0)// && deltaY < 0) - { - // move everything over to the left-down (as the center moves to the right-up) - for (int x = 0; x < gridSize; x++) - { - for (int y = gridSize - 1; y >= 0; y--) - { - _setDirect(x, y, _getDirect(x+deltaX, y+deltaY)); - } - } - } - else //if (deltaX < 0 && deltaY < 0) - { - // move everything over to the right-down (as the center moves to the left-up) - for (int x = gridSize - 1; x >= 0; x--) - { - for (int y = gridSize - 1; y >= 0; y--) - { - _setDirect(x, y, _getDirect(x+deltaX, y+deltaY)); - } - } - } - return true; - } - - - @Override - public String toString() { - return "MovableGridList[" + centerX + "," + centerY + "] " + gridSize + "*" + gridSize + "[" + b.length + "]"; - } - - public String toDetailString() { - StringBuilder str = new StringBuilder("\n"); - int i = 0; - str.append(this); - str.append("\n"); - for (boolean t : b) { - str.append(t ? "#" : "."); - i++; - if (i % gridSize == 0) { - str.append("\n"); - } //else { - //str.append(", "); - //} - } - return str.toString(); - } -} diff --git a/src/main/java/com/seibel/lod/core/util/DataPointUtil.java b/src/main/java/com/seibel/lod/core/util/DataPointUtil.java index adb6d117c..19e24a940 100644 --- a/src/main/java/com/seibel/lod/core/util/DataPointUtil.java +++ b/src/main/java/com/seibel/lod/core/util/DataPointUtil.java @@ -239,18 +239,15 @@ public class DataPointUtil isVoid(dataPoint) + " " + doesItExist(dataPoint) + '\n'; } - + public static void shrinkArray(short[] array, int packetSize, int start, int length, int arraySize) { start *= packetSize; length *= packetSize; arraySize *= packetSize; - for (int i = 0; i < arraySize - start; i++) - { - array[start + i] = array[start + length + i]; - //remove comment to not leave garbage at the end - //array[start + packetSize + i] = 0; - } + //remove comment to not leave garbage at the end + //array[start + packetSize + i] = 0; + if (arraySize - start >= 0) System.arraycopy(array, start + length + 0, array, start + 0, arraySize - start); } public static void extendArray(short[] array, int packetSize, int start, int length, int arraySize) diff --git a/src/main/java/com/seibel/lod/core/util/GridList.java b/src/main/java/com/seibel/lod/core/util/GridList.java deleted file mode 100644 index 8517aef2c..000000000 --- a/src/main/java/com/seibel/lod/core/util/GridList.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.seibel.lod.core.util; - -import java.util.ArrayList; -import java.util.List; - -public class GridList extends ArrayList implements List { - - public static class Pos { - public int x; - public int y; - - public Pos(int xx, int yy) { - x = xx; - y = yy; - } - } - - private static final long serialVersionUID = 1585978374811888116L; - public final int gridCentreToEdge; - public final int gridSize; - - public GridList(int gridCentreToEdge) { - super((gridCentreToEdge * 2 + 1) * (gridCentreToEdge * 2 + 1)); - gridSize = gridCentreToEdge * 2 + 1; - this.gridCentreToEdge = gridCentreToEdge; - } - - public final T getOffsetOf(int index, int x, int y) { - return get(index + x + y * gridSize); - } - - public final int offsetOf(int index, int x, int y) { - return index + x + y * gridSize; - } - - public final Pos posOf(int index) { - return new Pos(index % gridSize, index / gridSize); - } - - public final int calculateOffset(int x, int y) { - return x + y * gridSize; - } - - public final GridList subGrid(int gridCentreToEdge) { - int centreIndex = size() / 2; - GridList subGrid = new GridList(gridCentreToEdge); - for (int oy = -gridCentreToEdge; oy <= gridCentreToEdge; oy++) { - int begin = offsetOf(centreIndex, -gridCentreToEdge, oy); - int end = offsetOf(centreIndex, gridCentreToEdge, oy); - subGrid.addAll(this.subList(begin, end + 1)); - } - // System.out.println("========================================\n"+ - // this.toDetailString() + "\nTOOOOOOOOOOOOO\n"+subGrid.toDetailString()+ - // "==========================================\n"); - return subGrid; - } - - @Override - public String toString() { - return "GridList " + gridSize + "*" + gridSize + "[" + size() + "]"; - } - - public String toDetailString() { - StringBuilder str = new StringBuilder("\n"); - int i = 0; - for (T t : this) { - str.append(t.toString()); - str.append(", "); - i++; - if (i % gridSize == 0) { - str.append("\n"); - } - } - return str.toString(); - } -} diff --git a/src/main/java/com/seibel/lod/core/util/LodUtil.java b/src/main/java/com/seibel/lod/core/util/LodUtil.java index 6f6abfc6f..fbf7be529 100644 --- a/src/main/java/com/seibel/lod/core/util/LodUtil.java +++ b/src/main/java/com/seibel/lod/core/util/LodUtil.java @@ -34,6 +34,7 @@ import com.seibel.lod.core.objects.lod.LodDimension; import com.seibel.lod.core.objects.lod.RegionPos; import com.seibel.lod.core.objects.opengl.DefaultLodVertexFormats; import com.seibel.lod.core.objects.opengl.LodVertexFormat; +import com.seibel.lod.core.util.gridList.MovableCenteredGridList; import com.seibel.lod.core.wrapperInterfaces.IVersionConstants; import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory; import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; @@ -478,7 +479,7 @@ public class LodUtil } return false; } - public static boolean isBorderChunk(MovableGridList vanillaRenderedChunks, int chunkX, int chunkZ) + public static boolean isBorderChunk(MovableCenteredGridList vanillaRenderedChunks, int chunkX, int chunkZ) { for (LodDirection lodDirection : LodDirection.ADJ_DIRECTIONS) { diff --git a/src/main/java/com/seibel/lod/core/util/UnitBytes.java b/src/main/java/com/seibel/lod/core/util/UnitBytes.java index 9add9e62b..7a25c62ac 100644 --- a/src/main/java/com/seibel/lod/core/util/UnitBytes.java +++ b/src/main/java/com/seibel/lod/core/util/UnitBytes.java @@ -32,15 +32,15 @@ public class UnitBytes long v = value; StringBuilder str = new StringBuilder(); long GB = byteToGB(v); - if (GB != 0) str.append(GB+ "GB "); + if (GB != 0) str.append(GB).append("GB "); v -= GBToByte(GB); long MB = byteToMB(v); - if (MB != 0) str.append(MB+ "MB "); + if (MB != 0) str.append(MB).append("MB "); v -= MBToByte(MB); long KB = byteToKB(v); - if (KB != 0) str.append(KB+ "KB "); + if (KB != 0) str.append(KB).append("KB "); v -= KBToByte(KB); - str.append(v+"B"); + str.append(v).append("B"); return str.toString(); } } diff --git a/src/main/java/com/seibel/lod/core/util/gridList/ArrayGridList.java b/src/main/java/com/seibel/lod/core/util/gridList/ArrayGridList.java new file mode 100644 index 000000000..791c2878b --- /dev/null +++ b/src/main/java/com/seibel/lod/core/util/gridList/ArrayGridList.java @@ -0,0 +1,114 @@ +package com.seibel.lod.core.util.gridList; + +import com.seibel.lod.core.objects.Pos2D; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; + +public class ArrayGridList extends ArrayList implements List { + public final int gridSize; + + public ArrayGridList(int gridSize, BiFunction filler) { + super((gridSize) * (gridSize)); + this.gridSize = gridSize; + this.forEachPos((x, y) -> super.add(filler.apply(x,y))); + } + + public ArrayGridList(int gridSize) { + this(gridSize, (x,y) -> null); + } + + public ArrayGridList(ArrayGridList copy) { + super(copy); + gridSize = copy.gridSize; + } + + public ArrayGridList(ArrayGridList from, int minR, int maxR) { + super(maxR-minR); + if (minR > maxR) throw new IndexOutOfBoundsException("minR greater than maxR"); + if (minR < 0) throw new IndexOutOfBoundsException("minR less than 0"); + if (maxR > from.gridSize) throw new IndexOutOfBoundsException("maxR greater than gridSize"); + gridSize = maxR-minR; + for (int oy = minR; oy < maxR; oy++) { + int begin = minR + oy * from.gridSize; + int end = maxR + oy * from.gridSize; + super.addAll(from.subList(begin, end)); + } + if (super.size() != gridSize*gridSize) throw new IllegalStateException("subgrid clone failure"); + // System.out.println("========================================\n"+ + // this.toDetailString() + "\nTOOOOOOOOOOOOO\n"+subGrid.toDetailString()+ + // "==========================================\n"); + } + + protected int _indexOf(int x, int y) { + return x + y * gridSize; + } + + public final T get(Pos2D pos) { + return get(pos.x, pos.y); + } + public final T set(Pos2D pos, T e) { + return set(pos.x, pos.y, e); + } + public T get(int x, int y) { + return get(_indexOf(x,y)); + } + public T set(int x, int y, T e) { + return set(_indexOf(x, y), e); + } + + public final void clear() { + this.clear(null); + } + public final void fill(BiFunction filler) { + this.fill(null, filler); + } + public final void clear(Consumer dealloc) { + this.fill(dealloc, (x,y) -> null); + } + public final void fill(Consumer dealloc, + BiFunction filler) { + this.forEachPos((x, y) -> { + T t = this.set(x, y, filler.apply(x, y)); + if (t!=null) dealloc.accept(t); + }); + } + + public void forEachPos(BiConsumer consumer) { + for (int y=0; y consumer) { + super.forEach(consumer); + } + + @Override + public String toString() { + return getClass().toString() + " " + gridSize + "*" + gridSize + "[" + size() + "]"; + } + + public String toDetailString() { + StringBuilder str = new StringBuilder("\n"); + int i = 0; + str.append(this); + str.append("\n"); + for (T t : this) { + + str.append(t!=null ? t.toString() : "NULL"); + str.append(", "); + i++; + if (i % gridSize == 0) { + str.append("\n"); + } + } + return str.toString(); + } +} diff --git a/src/main/java/com/seibel/lod/core/util/gridList/EdgeDistanceBooleanGrid.java b/src/main/java/com/seibel/lod/core/util/gridList/EdgeDistanceBooleanGrid.java new file mode 100644 index 000000000..8df2de130 --- /dev/null +++ b/src/main/java/com/seibel/lod/core/util/gridList/EdgeDistanceBooleanGrid.java @@ -0,0 +1,70 @@ +package com.seibel.lod.core.util.gridList; + +import com.seibel.lod.core.objects.BoolType; +import com.seibel.lod.core.objects.Pos2D; + +import java.util.Iterator; +import java.util.function.IntPredicate; + +public class EdgeDistanceBooleanGrid extends PosArrayGridList { + ArrayGridList edgeCache = null; + + public EdgeDistanceBooleanGrid(Iterator posIter, int offsetX, int offsetY, int gridSize) { + super(gridSize, offsetX, offsetY); + while (posIter.hasNext()) { + Pos2D p = posIter.next(); + this.set(p, BoolType.TRUE); + } + } + + // Return false if it is indeed updated + private static boolean updatePos(ArrayGridList grid, int ox, int oy) { + if (grid.get(ox,oy) < 0) return true; + if (ox==0 || oy==0 || ox==grid.gridSize-1 || oy==grid.gridSize-1) { + return true; + } + + int v = grid.get(ox,oy); + if ( + grid.get(ox,oy+1)(gridSize, (ox, oy) -> { + BoolType b = get(ox+getOffsetX(), oy+getOffsetY()); + return b==null ? -1 : 0; + }); + + final boolean[] isDone = {false}; + while (!isDone[0]) { + isDone[0] = true; + edgeCache.forEachPos( (ox, oy) -> { + isDone[0] &= updatePos(edgeCache, ox, oy); + }); + } + } + + // 0 means right on the edge, while 1 means 1 ceil away. Uses Manhattan Distance + public > void flagAllWithDistance(T list, IntPredicate predicate) { + computeEdgeCache(); + edgeCache.forEachPos((ox, oy) -> { + int v = edgeCache.get(ox, oy); + if (v<0 || !predicate.test(v)) return; + list.set(ox + getOffsetX(), oy + getOffsetY(), BoolType.TRUE); + }); + } + + +} diff --git a/src/main/java/com/seibel/lod/core/util/MovableGridList.java b/src/main/java/com/seibel/lod/core/util/gridList/MovableCenteredGridList.java similarity index 95% rename from src/main/java/com/seibel/lod/core/util/MovableGridList.java rename to src/main/java/com/seibel/lod/core/util/gridList/MovableCenteredGridList.java index 6d2326d66..40b56770a 100644 --- a/src/main/java/com/seibel/lod/core/util/MovableGridList.java +++ b/src/main/java/com/seibel/lod/core/util/gridList/MovableCenteredGridList.java @@ -1,4 +1,4 @@ -package com.seibel.lod.core.util; +package com.seibel.lod.core.util.gridList; import java.util.ArrayList; import java.util.List; @@ -10,9 +10,8 @@ import java.util.function.Consumer; * 6,7,8 */ -public class MovableGridList extends ArrayList implements List { - - private static final long serialVersionUID = 5366261085254591277L; +@Deprecated // Replace with PosArrayGridList +public class MovableCenteredGridList extends ArrayList implements List { private int centerX; private int centerY; @@ -23,7 +22,7 @@ public class MovableGridList extends ArrayList implements List { /* * WARNING: Not yet tested if its atomic. (non Thread safe) */ - public MovableGridList(MovableGridList other) { + public MovableCenteredGridList(MovableCenteredGridList other) { super(other); centerX = other.centerX; centerY = other.centerY; @@ -31,7 +30,7 @@ public class MovableGridList extends ArrayList implements List { gridSize = other.gridSize; } - public MovableGridList(int gridCentreToEdge, int centerX, int centerY) { + public MovableCenteredGridList(int gridCentreToEdge, int centerX, int centerY) { super((gridCentreToEdge * 2 + 1) * (gridCentreToEdge * 2 + 1)); gridSize = gridCentreToEdge * 2 + 1; this.gridCentreToEdge = gridCentreToEdge; diff --git a/src/main/java/com/seibel/lod/core/util/MovableGridRingList.java b/src/main/java/com/seibel/lod/core/util/gridList/MovableGridRingList.java similarity index 84% rename from src/main/java/com/seibel/lod/core/util/MovableGridRingList.java rename to src/main/java/com/seibel/lod/core/util/gridList/MovableGridRingList.java index efd8cbb2c..980c61fac 100644 --- a/src/main/java/com/seibel/lod/core/util/MovableGridRingList.java +++ b/src/main/java/com/seibel/lod/core/util/gridList/MovableGridRingList.java @@ -1,4 +1,6 @@ -package com.seibel.lod.core.util; +package com.seibel.lod.core.util.gridList; + +import com.seibel.lod.core.objects.Pos2D; import java.util.ArrayList; import java.util.List; @@ -8,15 +10,7 @@ import java.util.function.Consumer; public class MovableGridRingList extends ArrayList implements List { - private static final long serialVersionUID = -7743190533384530134L; - - public static class Pos { - public final int x; - public final int y; - Pos(int x, int y) {this.x=x; this.y=y;} - } - - private AtomicReference pos = new AtomicReference(); + private AtomicReference pos = new AtomicReference(); private final int halfSize; private final int size; @@ -26,7 +20,7 @@ public class MovableGridRingList extends ArrayList implements List { super((halfSize * 2 + 1) * (halfSize * 2 + 1)); size = halfSize * 2 + 1; this.halfSize = halfSize; - pos.set(new Pos(centerX-halfSize, centerY-halfSize)); + pos.set(new Pos2D(centerX-halfSize, centerY-halfSize)); clear(); } @@ -53,16 +47,16 @@ public class MovableGridRingList extends ArrayList implements List { } } - public Pos getCenter() { - Pos bottom = pos.get(); - return new Pos(bottom.x+halfSize, bottom.y+halfSize); + public Pos2D getCenter() { + Pos2D bottom = pos.get(); + return new Pos2D(bottom.x+halfSize, bottom.y+halfSize); } - public Pos getMinInRange() { + public Pos2D getMinInRange() { return pos.get(); } - public Pos getMaxInRange() { - Pos bottom = pos.get(); - return new Pos(bottom.x+size-1, bottom.y+size-1); + public Pos2D getMaxInRange() { + Pos2D bottom = pos.get(); + return new Pos2D(bottom.x+size-1, bottom.y+size-1); } public int getSize() {return size;} public int getHalfSize() {return halfSize;} @@ -70,10 +64,10 @@ public class MovableGridRingList extends ArrayList implements List { // WARNNING! Be careful with race condition! // The grid may get moved after this query! public boolean inRange(int x, int y) { - Pos min = pos.get(); + Pos2D min = pos.get(); return (x>=min.x && x=min.y && y=min.x && x=min.y && y extends ArrayList implements List { // return null if x,y is outside of the grid public T get(int x, int y) { - Pos min = pos.get(); + Pos2D min = pos.get(); if (!_inRangeAquired(x, y, min)) return null; moveLock.readLock().lock(); try { - Pos newMin = pos.get(); + Pos2D newMin = pos.get(); // Use EXECT compare here if (min!=newMin) if (!_inRangeAquired(x, y, newMin)) return null; @@ -104,11 +98,11 @@ public class MovableGridRingList extends ArrayList implements List { // return false if x,y is outside of the grid public boolean set(int x, int y, T t) { - Pos min = pos.get(); + Pos2D min = pos.get(); if (!_inRangeAquired(x, y, min)) return false; moveLock.readLock().lock(); try { - Pos newMin = pos.get(); + Pos2D newMin = pos.get(); // Use EXECT compare here if (min!=newMin) if (!_inRangeAquired(x, y, newMin)) return false; @@ -121,11 +115,11 @@ public class MovableGridRingList extends ArrayList implements List { // return input t if x,y is outside of the grid public T swap(int x, int y, T t) { - Pos min = pos.get(); + Pos2D min = pos.get(); if (!_inRangeAquired(x, y, min)) return t; moveLock.readLock().lock(); try { - Pos newMin = pos.get(); + Pos2D newMin = pos.get(); // Use EXECT compare here if (min!=newMin) if (!_inRangeAquired(x, y, newMin)) return t; @@ -165,7 +159,7 @@ public class MovableGridRingList extends ArrayList implements List { } public boolean move(int newCenterX, int newCenterY, Consumer d) { - Pos cPos = pos.get(); + Pos2D cPos = pos.get(); int newMinX = newCenterX - halfSize; int newMinY = newCenterY - halfSize; if (cPos.x == newMinX && cPos.y == newMinY) @@ -193,7 +187,7 @@ public class MovableGridRingList extends ArrayList implements List { } } } - pos.set(new Pos(newMinX, newMinY)); + pos.set(new Pos2D(newMinX, newMinY)); return true; } finally { moveLock.writeLock().unlock(); @@ -202,7 +196,7 @@ public class MovableGridRingList extends ArrayList implements List { @Override public String toString() { - Pos p = pos.get(); + Pos2D p = pos.get(); return "MovabeGridRingList[" + p.x+halfSize + "," + p.y+halfSize + "] " + size + "*" + size + "[" + size() + "]"; } diff --git a/src/main/java/com/seibel/lod/core/util/gridList/PosArrayGridList.java b/src/main/java/com/seibel/lod/core/util/gridList/PosArrayGridList.java new file mode 100644 index 000000000..7d9a8ac8e --- /dev/null +++ b/src/main/java/com/seibel/lod/core/util/gridList/PosArrayGridList.java @@ -0,0 +1,158 @@ +package com.seibel.lod.core.util.gridList; + +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; + +/*Layout: + * 0,1,2, + * 3,4,5, + * 6,7,8 + */ + +public class PosArrayGridList extends ArrayGridList { + + private int offsetX; + private int offsetY; + + /* + * WARNING: Non Thread safe! + */ + public PosArrayGridList(int gridSize, int offsetX, int offsetY, BiFunction filler) { + super(gridSize, filler); + this.offsetX = offsetX; + this.offsetY = offsetY; + } + + public PosArrayGridList(int gridSize, int offsetX, int offsetY) { + this(gridSize, offsetX, offsetY, (x,y) -> null); + } + + public PosArrayGridList(PosArrayGridList copy) { + super(copy); + offsetX = copy.offsetX; + offsetY = copy.offsetY; + } + + public PosArrayGridList(PosArrayGridList source, int minR, int maxR) { + super(source, minR, maxR); + } + + @Override + protected int _indexOf(int x, int y) { + return (x-offsetX) + (y-offsetY) * gridSize; + } + + @Override + public void forEachPos(BiConsumer consumer) { + for (int y=offsetY; y=offsetX && x=offsetY && y dealloc) { + if (deltaX==0 && deltaY==0) return false; + + // if the x or z offset is equal to or greater than + // the total width, just delete the current data + // and update the centerX and/or centerZ + if (Math.abs(deltaX) >= gridSize || Math.abs(deltaY) >= gridSize) + { + clear(dealloc); + // update the new center + offsetX += deltaX; + offsetY += deltaY; + return true; + } + int newMinX = offsetX + deltaX; + int newMinY = offsetY + deltaY; + int newMaxX = newMinX + gridSize; + int newMaxY = newMinY + gridSize; + + // Dealloc stuff + if (dealloc != null) + forEachPos((x,y) -> { + if (x=newMaxX || y>=newMaxY) { + T t = get(x,y); + if (t!=null) dealloc.accept(t); + } + }); + + offsetX = newMinX; + offsetY = newMinY; + + // X + if (deltaX >= 0 && deltaY >= 0) + { + + // move everything over to the left-up (as the center moves to the right-down) + for (int x = newMinX; x < newMaxX; x++) + { + for (int y = newMinY; y < newMaxY; y++) + { + set(x, y, _directGet(x+deltaX, y+deltaY)); + } + } + } + else if (deltaX < 0 && deltaY >= 0) + { + // move everything over to the right-up (as the center moves to the left-down) + for (int x = newMaxX - 1; x >= newMinX; x--) + { + for (int y = newMinY; y < newMaxY; y++) + { + set(x, y, _directGet(x+deltaX, y+deltaY)); + } + } + } + else if (deltaX >= 0) // && deltaY < 0) + { + // move everything over to the left-down (as the center moves to the right-up) + for (int x = newMinX; x < newMaxX; x++) + { + for (int y = newMaxY - 1; y >= newMinY; y--) + { + set(x, y, _directGet(x+deltaX, y+deltaY)); + } + } + } + else //if (deltaX < 0 && deltaY < 0) + { + // move everything over to the right-down (as the center moves to the left-up) + for (int x = newMaxX - 1; x >= newMinX; x--) + { + for (int y = newMaxY - 1; y >= newMinY; y--) + { + set(x, y, _directGet(x+deltaX, y+deltaY)); + } + } + } + return true; + } + + @Override + public String toString() { + return getClass().toString() + "[" + offsetX + "," + offsetY + "] " + gridSize + "*" + gridSize + "[" + size() + "]"; + } +} diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/IVersionConstants.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/IVersionConstants.java index 3e610815b..887c07562 100644 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/IVersionConstants.java +++ b/src/main/java/com/seibel/lod/core/wrapperInterfaces/IVersionConstants.java @@ -15,19 +15,6 @@ public interface IVersionConstants extends IBindable { /** @returns the minimum height blocks can be generated */ int getMinimumWorldHeight(); - /** - * @Returns True if the given DistanceGenerationMode can be run on our own - * thread.
- * False if the generation must be run on Minecraft's server thread. - */ - boolean isWorldGeneratorSingleThreaded(DistanceGenerationMode distanceGenerationMode); - - /** - * @Returns True if BatchGeneration is implemented
- * False if it is not supported - */ - boolean hasBatchGenerationImplementation(); - /** * @Returns the number of generations call per thread. */ diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/IWrapperFactory.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/IWrapperFactory.java index 402048a1b..029665030 100644 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/IWrapperFactory.java +++ b/src/main/java/com/seibel/lod/core/wrapperInterfaces/IWrapperFactory.java @@ -26,7 +26,6 @@ import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvionmentWrapper; -import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper; /** * This handles creating abstract wrapper objects. @@ -45,9 +44,6 @@ public interface IWrapperFactory extends IBindable AbstractChunkPosWrapper createChunkPos(AbstractChunkPosWrapper newChunkPos); AbstractChunkPosWrapper createChunkPos(AbstractBlockPosWrapper blockPos); - AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, - IWorldWrapper worldWrapper); - AbstractBatchGenerationEnvionmentWrapper createBatchGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper); } diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java index a4577c892..2e1d5c814 100644 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java +++ b/src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java @@ -604,32 +604,32 @@ public interface ILodConfigWrapperSingleton extends IBindable + " Only generate the biomes and use the biome's \n" + " grass color, water color, or snow color. \n" + " Doesn't generate height, everything is shown at sea level. \n" - + " " + multiOrSingleThreadText(versionConstants, DistanceGenerationMode.BIOME_ONLY) + " - Fastest (2-5 ms) \n" + + " - Fastest (2-5 ms) \n" + "\n" + " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT + " \n" + " Same as " + DistanceGenerationMode.BIOME_ONLY + ", except instead \n" + " of always using sea level as the LOD height \n" + " different biome types (mountain, ocean, forest, etc.) \n" + " use predetermined heights to simulate having height data. \n" - + " " + multiOrSingleThreadText(versionConstants, DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT) + " - Fastest (2-5 ms) \n" + + " - Fastest (2-5 ms) \n" + "\n" + " " + DistanceGenerationMode.SURFACE + " \n" + " Generate the world surface, \n" + " this does NOT include trees, \n" + " or structures. \n" - + " " + multiOrSingleThreadText(versionConstants, DistanceGenerationMode.SURFACE) + " - Faster (10-20 ms) \n" + + " - Faster (10-20 ms) \n" + "\n" + " " + DistanceGenerationMode.FEATURES + " \n" + " Generate everything except structures. \n" + " WARNING: This may cause world generation bugs or instability! \n" - + " " + multiOrSingleThreadText(versionConstants, DistanceGenerationMode.FEATURES) + " - Fast (15-20 ms) \n" + + " - Fast (15-20 ms) \n" + "\n" + " " + DistanceGenerationMode.FULL + " \n" + " Ask the local server to generate/load each chunk. \n" + " This will show player made structures, which can \n" + " be useful if you are adding the mod to a pre-existing world. \n" + " This is the most compatible, but causes server/simulation lag. \n" - + " " + multiOrSingleThreadText(versionConstants, DistanceGenerationMode.FULL) + " - Slow (15-50 ms, with spikes up to 200 ms) \n" + + " - Slow (15-50 ms, with spikes up to 200 ms) \n" + "\n" + " The multithreaded options may increase CPU load significantly (while generating) \n" + " depending on how many world generation threads you have allocated. \n"; @@ -704,13 +704,6 @@ public interface ILodConfigWrapperSingleton extends IBindable + " This wont't affect performance."; BlocksToAvoid getBlocksToAvoid(); void setBlockToAvoid(BlocksToAvoid newBlockToAvoid); - - - /** description helper method */ - static String multiOrSingleThreadText(IVersionConstants versionConstants, DistanceGenerationMode distanceGenerationMode) - { - return versionConstants.isWorldGeneratorSingleThreaded(distanceGenerationMode) ? "Singlethreaded" : "Multithreaded"; - } } diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractExperimentalWorldGeneratorWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractExperimentalWorldGeneratorWrapper.java deleted file mode 100644 index 5fd2b2351..000000000 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractExperimentalWorldGeneratorWrapper.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.seibel.lod.core.wrapperInterfaces.worldGeneration; - -import com.seibel.lod.core.builders.lodBuilding.LodBuilder; -import com.seibel.lod.core.objects.lod.LodDimension; -import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; - -public abstract class AbstractExperimentalWorldGeneratorWrapper { - public AbstractExperimentalWorldGeneratorWrapper(LodBuilder newLodBuilder, LodDimension newLodDimension, - IWorldWrapper worldWrapper) { - } - - public abstract void queueGenerationRequests(LodDimension lodDim, LodBuilder lodBuilder); - - public abstract void stop(); -} diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractWorldGeneratorWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractWorldGeneratorWrapper.java deleted file mode 100644 index e09a220cb..000000000 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractWorldGeneratorWrapper.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL v3 License. - * - * Copyright (C) 2020 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.core.wrapperInterfaces.worldGeneration; - -import com.seibel.lod.core.builders.lodBuilding.LodBuilder; -import com.seibel.lod.core.enums.config.DistanceGenerationMode; -import com.seibel.lod.core.objects.lod.LodDimension; -import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; -import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; - -/** - * This is used for generating chunks in a variety of detail and threading - * levels. - *

- * Abstract instead of an interface, so we can define its constructors. - * - * @author James Seibel - * @version 11-20-2021 - */ -public abstract class AbstractWorldGeneratorWrapper { - public AbstractWorldGeneratorWrapper(LodBuilder newLodBuilder, LodDimension newLodDimension, - IWorldWrapper worldWrapper) { - } - - public abstract void generateBiomesOnly(AbstractChunkPosWrapper pos, DistanceGenerationMode generationMode); - - public abstract void generateSurface(AbstractChunkPosWrapper pos); - - public abstract void generateFeatures(AbstractChunkPosWrapper pos); - - public abstract void generateFull(AbstractChunkPosWrapper pos); -}