Merge in multi-dim support
This commit is contained in:
@@ -58,7 +58,7 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
public class ClientApi
|
||||
{
|
||||
public static boolean prefLoggerEnabled = false;
|
||||
public static List<WeakReference<SpamReducedLogger>> spamReducedLoggers
|
||||
public static final List<WeakReference<SpamReducedLogger>> spamReducedLoggers
|
||||
= Collections.synchronizedList(new LinkedList<WeakReference<SpamReducedLogger>>());
|
||||
|
||||
public static final ClientApi INSTANCE = new ClientApi();
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+5
-8
@@ -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<Void> future = CompletableFuture.completedFuture(null);
|
||||
|
||||
try {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<AbstractChunkPosWrapper> 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. <br>
|
||||
* <br>
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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() {}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<LodRegion>(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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Boolean> shinkGridEdge(MovableGridList<Boolean> target) {
|
||||
MovableGridList<Boolean> result = new MovableGridList<Boolean>(
|
||||
private static MovableCenteredGridList<Boolean> shinkGridEdge(MovableCenteredGridList<Boolean> target) {
|
||||
MovableCenteredGridList<Boolean> result = new MovableCenteredGridList<Boolean>(
|
||||
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<Boolean> chunkGrid = ClientApi.renderer.vanillaRenderedChunks;
|
||||
MovableCenteredGridList<Boolean> chunkGrid = ClientApi.renderer.vanillaRenderedChunks;
|
||||
if (CONFIG.client().graphics().advancedGraphics().getVanillaOverdraw() == VanillaOverdraw.BORDER) {
|
||||
chunkGrid = shinkGridEdge(chunkGrid);
|
||||
}
|
||||
|
||||
@@ -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<Boolean> vanillaRenderedChunks;
|
||||
public MovableCenteredGridList<Boolean> 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<Boolean>(
|
||||
vanillaRenderedChunks = new MovableCenteredGridList<Boolean>(
|
||||
chunkRenderDistance, chunkX, chunkZ);
|
||||
return true;
|
||||
}
|
||||
MovableGridList<Boolean> chunkList;
|
||||
MovableCenteredGridList<Boolean> chunkList;
|
||||
|
||||
boolean anyChanged = false;
|
||||
if (vanillaRenderedChunks == null || vanillaRenderedChunks.gridCentreToEdge != chunkRenderDistance ||
|
||||
vanillaRenderedChunks.getCenterX()!=chunkX || vanillaRenderedChunks.getCenterY()!=chunkZ) {
|
||||
chunkList = new MovableGridList<Boolean>(chunkRenderDistance, chunkX, chunkZ);
|
||||
chunkList = new MovableCenteredGridList<Boolean>(chunkRenderDistance, chunkX, chunkZ);
|
||||
anyChanged = true;
|
||||
} else {
|
||||
chunkList = vanillaRenderedChunks;
|
||||
|
||||
@@ -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<gridSize; y++) {
|
||||
for (int x=0; x<gridSize; x++) {
|
||||
nb[i] = transformer.transform(b[i],
|
||||
x+centerX-gridCentreToEdge, y+centerY-gridCentreToEdge);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
b = nb;
|
||||
}
|
||||
|
||||
public void flipBorder(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);
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
package com.seibel.lod.core.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GridList<T> extends ArrayList<T> implements List<T> {
|
||||
|
||||
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<T> subGrid(int gridCentreToEdge) {
|
||||
int centreIndex = size() / 2;
|
||||
GridList<T> subGrid = new GridList<T>(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();
|
||||
}
|
||||
}
|
||||
@@ -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<Boolean> vanillaRenderedChunks, int chunkX, int chunkZ)
|
||||
public static boolean isBorderChunk(MovableCenteredGridList<Boolean> vanillaRenderedChunks, int chunkX, int chunkZ)
|
||||
{
|
||||
for (LodDirection lodDirection : LodDirection.ADJ_DIRECTIONS)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<T> extends ArrayList<T> implements List<T> {
|
||||
public final int gridSize;
|
||||
|
||||
public ArrayGridList(int gridSize, BiFunction<Integer, Integer, T> 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<T> copy) {
|
||||
super(copy);
|
||||
gridSize = copy.gridSize;
|
||||
}
|
||||
|
||||
public ArrayGridList(ArrayGridList<T> 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<Integer, Integer, T> filler) {
|
||||
this.fill(null, filler);
|
||||
}
|
||||
public final void clear(Consumer<? super T> dealloc) {
|
||||
this.fill(dealloc, (x,y) -> null);
|
||||
}
|
||||
public final void fill(Consumer<? super T> dealloc,
|
||||
BiFunction<Integer, Integer, T> 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<Integer, Integer> consumer) {
|
||||
for (int y=0; y<gridSize; y++) {
|
||||
for (int x=0; x<gridSize; x++) {
|
||||
consumer.accept(x,y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void forEach(Consumer<? super T> 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();
|
||||
}
|
||||
}
|
||||
@@ -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<BoolType> {
|
||||
ArrayGridList<Integer> edgeCache = null;
|
||||
|
||||
public EdgeDistanceBooleanGrid(Iterator<Pos2D> 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<Integer> 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)<v ||
|
||||
grid.get(ox,oy-1)<v ||
|
||||
grid.get(ox+1, oy)<v ||
|
||||
grid.get(ox-1, oy)<v
|
||||
) {
|
||||
return true;
|
||||
} else {
|
||||
grid.set(ox,oy, v+1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//FIXME: This is slow and expensive. Use queue to make this skip recheck done pos
|
||||
private void computeEdgeCache() {
|
||||
if (edgeCache != null) return;
|
||||
|
||||
edgeCache = new ArrayGridList<Integer>(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 <T extends ArrayGridList<BoolType>> 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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+5
-6
@@ -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<T> extends ArrayList<T> implements List<T> {
|
||||
|
||||
private static final long serialVersionUID = 5366261085254591277L;
|
||||
@Deprecated // Replace with PosArrayGridList<T>
|
||||
public class MovableCenteredGridList<T> extends ArrayList<T> implements List<T> {
|
||||
|
||||
private int centerX;
|
||||
private int centerY;
|
||||
@@ -23,7 +22,7 @@ public class MovableGridList<T> extends ArrayList<T> implements List<T> {
|
||||
/*
|
||||
* WARNING: Not yet tested if its atomic. (non Thread safe)
|
||||
*/
|
||||
public MovableGridList(MovableGridList<T> other) {
|
||||
public MovableCenteredGridList(MovableCenteredGridList<T> other) {
|
||||
super(other);
|
||||
centerX = other.centerX;
|
||||
centerY = other.centerY;
|
||||
@@ -31,7 +30,7 @@ public class MovableGridList<T> extends ArrayList<T> implements List<T> {
|
||||
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;
|
||||
+23
-29
@@ -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<T> extends ArrayList<T> implements List<T> {
|
||||
|
||||
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> pos = new AtomicReference<Pos>();
|
||||
private AtomicReference<Pos2D> pos = new AtomicReference<Pos2D>();
|
||||
|
||||
private final int halfSize;
|
||||
private final int size;
|
||||
@@ -26,7 +20,7 @@ public class MovableGridRingList<T> extends ArrayList<T> implements List<T> {
|
||||
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<T> extends ArrayList<T> implements List<T> {
|
||||
}
|
||||
}
|
||||
|
||||
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<T> extends ArrayList<T> implements List<T> {
|
||||
// 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.x+size && y>=min.y && y<min.y+size);
|
||||
}
|
||||
private boolean _inRangeAquired(int x, int y, Pos min) {
|
||||
private boolean _inRangeAquired(int x, int y, Pos2D min) {
|
||||
return (x>=min.x && x<min.x+size && y>=min.y && y<min.y+size);
|
||||
}
|
||||
private T _getUnsafe(int x, int y) {
|
||||
@@ -88,11 +82,11 @@ public class MovableGridRingList<T> extends ArrayList<T> implements List<T> {
|
||||
|
||||
// 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<T> extends ArrayList<T> implements List<T> {
|
||||
|
||||
// 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<T> extends ArrayList<T> implements List<T> {
|
||||
|
||||
// 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<T> extends ArrayList<T> implements List<T> {
|
||||
}
|
||||
|
||||
public boolean move(int newCenterX, int newCenterY, Consumer<? super T> 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<T> extends ArrayList<T> implements List<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
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<T> extends ArrayList<T> implements List<T> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
Pos p = pos.get();
|
||||
Pos2D p = pos.get();
|
||||
return "MovabeGridRingList[" + p.x+halfSize + "," + p.y+halfSize + "] " + size + "*" + size + "[" + size() + "]";
|
||||
}
|
||||
|
||||
@@ -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<T> extends ArrayGridList<T> {
|
||||
|
||||
private int offsetX;
|
||||
private int offsetY;
|
||||
|
||||
/*
|
||||
* WARNING: Non Thread safe!
|
||||
*/
|
||||
public PosArrayGridList(int gridSize, int offsetX, int offsetY, BiFunction<Integer, Integer, T> 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<T> copy) {
|
||||
super(copy);
|
||||
offsetX = copy.offsetX;
|
||||
offsetY = copy.offsetY;
|
||||
}
|
||||
|
||||
public PosArrayGridList(PosArrayGridList<T> 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<Integer, Integer> consumer) {
|
||||
for (int y=offsetY; y<offsetY+gridSize; y++) {
|
||||
for (int x=offsetX; x<offsetX+gridSize; x++) {
|
||||
consumer.accept(x,y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getOffsetX() {
|
||||
return offsetX;
|
||||
}
|
||||
public int getOffsetY() {
|
||||
return offsetY;
|
||||
}
|
||||
|
||||
public boolean inRange(int x, int y) {
|
||||
return (x>=offsetX && x<offsetX+gridSize &&
|
||||
y>=offsetY && y<offsetY+gridSize);
|
||||
}
|
||||
|
||||
private T _directGet(int x, int y) {
|
||||
if (!inRange(x,y)) return null;
|
||||
return get(x,y);
|
||||
}
|
||||
|
||||
// Return false if haven't changed. Return true if it did
|
||||
public boolean move(int deltaX, int deltaY, Consumer<? super T> 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<newMinX || y<newMinY ||
|
||||
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() + "]";
|
||||
}
|
||||
}
|
||||
@@ -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. <br>
|
||||
* False if the generation must be run on Minecraft's server thread.
|
||||
*/
|
||||
boolean isWorldGeneratorSingleThreaded(DistanceGenerationMode distanceGenerationMode);
|
||||
|
||||
/**
|
||||
* @Returns True if BatchGeneration is implemented <br>
|
||||
* False if it is not supported
|
||||
*/
|
||||
boolean hasBatchGenerationImplementation();
|
||||
|
||||
/**
|
||||
* @Returns the number of generations call per thread.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+5
-12
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
-15
@@ -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();
|
||||
}
|
||||
-49
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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.
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
Reference in New Issue
Block a user