Fix issues with generation filling up RAM

This commit is contained in:
tom lee
2022-01-16 17:33:20 +08:00
parent f665a8b8f6
commit be5b5de170
4 changed files with 86 additions and 91 deletions
@@ -79,7 +79,7 @@ public class LodBuilder
* How wide LodDimensions should be in regions <br>
* Is automatically set before the first frame in ClientProxy.
*/
public int defaultDimensionWidthInRegions = 0;
public int defaultDimensionWidthInRegions = 1;
//public static final boolean useExperimentalLighting = true;
@@ -150,52 +150,6 @@ public class LodBuilder
});
lodGenThreadPool.execute(thread);
}
public void generateLodNodeDirect(IChunkWrapper chunk, LodWorld lodWorld, IDimensionTypeWrapper dim, DistanceGenerationMode generationMode, boolean override)
{
if (lodWorld == null || lodWorld.getIsWorldNotLoaded())
return;
// don't try to create an LOD object
// if for some reason we aren't
// given a valid chunk object
if (chunk == null)
return;
//noinspection GrazieInspection
try
{
// we need a loaded client world in order to
// get the textures for blocks
if (MC.getWrappedClientWorld() == null)
return;
// don't try to generate LODs if the user isn't in the world anymore
// (this happens a lot when the user leaves a world/server)
if (!MC.hasSinglePlayerServer() && !MC.connectedToServer())
return;
// make sure the dimension exists
LodDimension lodDim;
if (lodWorld.getLodDimension(dim) == null)
{
lodDim = new LodDimension(dim, lodWorld, defaultDimensionWidthInRegions);
lodWorld.addLodDimension(lodDim);
}
else
{
lodDim = lodWorld.getLodDimension(dim);
}
generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(generationMode), override);
}
catch (IllegalArgumentException | NullPointerException e)
{
e.printStackTrace();
// if the world changes while LODs are being generated
// they will throw errors as they try to access things that no longer
// exist.
}
}
/**
* Creates a LodNode for a chunk in the given world.
@@ -56,7 +56,7 @@ public class PosToGenerateContainer
// TODO what is going on in this method?
public void addPosToGenerate(byte detailLevel, int posX, int posZ)
public void addPosToGenerate(byte detailLevel, int posX, int posZ, boolean sort)
{
int distance = LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ);
int index;
@@ -67,25 +67,22 @@ public class PosToGenerateContainer
if (farSize < farPosToGenerate.length)
farSize++;
index = farSize - 1;
while (index > 0 && LevelPosUtil.compareDistance(distance, farPosToGenerate[index - 1][3]) <= 0)
{
farPosToGenerate[index][0] = farPosToGenerate[index - 1][0];
farPosToGenerate[index][1] = farPosToGenerate[index - 1][1];
farPosToGenerate[index][2] = farPosToGenerate[index - 1][2];
farPosToGenerate[index][3] = farPosToGenerate[index - 1][3];
index--;
}
if (index != farSize - 1 || farSize != farPosToGenerate.length)
{
farPosToGenerate[index][0] = detailLevel + 1;
farPosToGenerate[index][1] = posX;
farPosToGenerate[index][2] = posZ;
farPosToGenerate[index][3] = distance;
if (sort) {
while (index > 0 && LevelPosUtil.compareDistance(distance, farPosToGenerate[index - 1][3]) <= 0)
{
farPosToGenerate[index][0] = farPosToGenerate[index - 1][0];
farPosToGenerate[index][1] = farPosToGenerate[index - 1][1];
farPosToGenerate[index][2] = farPosToGenerate[index - 1][2];
farPosToGenerate[index][3] = farPosToGenerate[index - 1][3];
index--;
}
}
farPosToGenerate[index][0] = detailLevel + 1;
farPosToGenerate[index][1] = posX;
farPosToGenerate[index][2] = posZ;
farPosToGenerate[index][3] = distance;
}
else
{
@@ -95,26 +92,27 @@ public class PosToGenerateContainer
nearSize++;
index = nearSize - 1;
while (index > 0 && LevelPosUtil.compareDistance(distance, nearPosToGenerate[index - 1][3]) <= 0)
{
nearPosToGenerate[index][0] = nearPosToGenerate[index - 1][0];
nearPosToGenerate[index][1] = nearPosToGenerate[index - 1][1];
nearPosToGenerate[index][2] = nearPosToGenerate[index - 1][2];
nearPosToGenerate[index][3] = nearPosToGenerate[index - 1][3];
index--;
}
if (index != nearSize - 1 || nearSize != nearPosToGenerate.length)
{
nearPosToGenerate[index][0] = detailLevel + 1;
nearPosToGenerate[index][1] = posX;
nearPosToGenerate[index][2] = posZ;
nearPosToGenerate[index][3] = distance;
if (sort) {
while (index > 0 && LevelPosUtil.compareDistance(distance, nearPosToGenerate[index - 1][3]) <= 0)
{
nearPosToGenerate[index][0] = nearPosToGenerate[index - 1][0];
nearPosToGenerate[index][1] = nearPosToGenerate[index - 1][1];
nearPosToGenerate[index][2] = nearPosToGenerate[index - 1][2];
nearPosToGenerate[index][3] = nearPosToGenerate[index - 1][3];
index--;
}
}
nearPosToGenerate[index][0] = detailLevel + 1;
nearPosToGenerate[index][1] = posX;
nearPosToGenerate[index][2] = posZ;
nearPosToGenerate[index][3] = distance;
}
}
public boolean isFull() {
return nearSize == nearPosToGenerate.length && farSize == farPosToGenerate.length;
}
public int getNumberOfPos()
@@ -21,6 +21,8 @@ package com.seibel.lod.core.objects.lod;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -75,6 +77,8 @@ public class LodDimension
// which is a safer way to get the width then directly asking the arrays
/** stores all the regions in this dimension */
public volatile LodRegion[][] regions;
//NOTE: This list pos is relative to center
private volatile RegionPos[] iteratorList = null;
/** stores if the region at the given x and z index needs to be saved to disk */
/** stores if the region at the given x and z index needs to be regenerated */
@@ -95,6 +99,7 @@ public class LodDimension
private final ExecutorService cutAndExpandThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - Cut and Expand"));
/**
* Creates the dimension centered at (0,0)
* @param newWidth in regions
@@ -139,6 +144,28 @@ public class LodDimension
regions = new LodRegion[width][width];
center = new RegionPos(0, 0);
generateIteratorList();
}
private void generateIteratorList() {
iteratorList = null;
RegionPos[] list = new RegionPos[width*width];
int i = 0;
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) -> {
RegionPos posA = (RegionPos)a;
RegionPos posB = (RegionPos)b;
double disSqrA = posA.x*posA.x+posA.z*posA.z;
double disSqrB = posB.x*posB.x+posB.z*posB.z;
return Double.compare(disSqrA, disSqrB);
});
iteratorList = list;
}
@@ -333,12 +360,21 @@ public class LodDimension
oy += dy;
}
}
public void iterateByDistance(PosComsumer r) {
if (iteratorList==null) return;
for (RegionPos relativePos : iteratorList) {
r.run(relativePos.x+halfWidth, relativePos.z+halfWidth);
}
}
/**
* Deletes nodes that are a higher detail then necessary, freeing
* up memory.
*/
private int totalDirtiedRegions = 0;
public void cutRegionNodesAsync(int playerPosX, int playerPosZ)
{
if (isCutting) return;
@@ -347,7 +383,7 @@ public class LodDimension
// for the same location
Runnable thread = () -> {
//ClientApi.LOGGER.info("LodDim cut Region: " + playerPosX + "," + playerPosZ);
totalDirtiedRegions = 0;
// go over every region in the dimension
iterateWithSpiral((int x, int z) -> {
int regionX;
@@ -357,6 +393,7 @@ public class LodDimension
regionX = (x + center.x) - halfWidth;
regionZ = (z + center.z) - halfWidth;
LodRegion region = regions[x][z];
if (region != null && region.needSaving) totalDirtiedRegions++;
if (region != null && !region.needSaving && region.isWriting==0) {
// check what detail level this region should be
// and cut it if it is higher then that
@@ -371,8 +408,12 @@ public class LodDimension
}
}
});
if (totalDirtiedRegions > 8) this.saveDirtyRegionsToFile(false);
//ClientApi.LOGGER.info("LodDim cut Region complete: " + playerPosX + "," + playerPosZ);
isCutting = false;
// See if we need to save and flush some data out.
};
cutAndExpandThread.execute(thread);
}
@@ -485,11 +526,12 @@ public class LodDimension
{
PosToGenerateContainer posToGenerate;
posToGenerate = new PosToGenerateContainer((byte) 8, maxDataToGenerate, playerBlockPosX, playerBlockPosZ);
iterateWithSpiral((int x, int z) -> {
iterateByDistance((int x, int z) -> {
if (posToGenerate.isFull()) return;
//All of this is handled directly by the region, which scan every pos from top to bottom of the quad tree
LodRegion lodRegion = regions[x][z];
if (lodRegion != null)
lodRegion.getPosToGenerate(posToGenerate, playerBlockPosX, playerBlockPosZ, priority);
lodRegion.getPosToGenerate(posToGenerate, playerBlockPosX, playerBlockPosZ, priority, (Math.abs(x)+Math.abs(z)<2));
});
return posToGenerate;
}
@@ -698,6 +740,7 @@ public class LodDimension
halfWidth = width/ 2;
regions = new LodRegion[width][width];
generateIteratorList();
}
@@ -166,8 +166,8 @@ public class LodRegion {
* understand
*/
public void getPosToGenerate(PosToGenerateContainer posToGenerate, int playerBlockPosX, int playerBlockPosZ,
GenerationPriority priority) {
getPosToGenerate(posToGenerate, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerBlockPosX, playerBlockPosZ, priority);
GenerationPriority priority, boolean shouldSort) {
getPosToGenerate(posToGenerate, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerBlockPosX, playerBlockPosZ, priority, shouldSort);
}
@@ -179,7 +179,7 @@ public class LodRegion {
* understand
*/
private void getPosToGenerate(PosToGenerateContainer posToGenerate, byte detailLevel, int childOffsetPosX,
int childOffsetPosZ, int playerPosX, int playerPosZ, GenerationPriority priority) {
int childOffsetPosZ, int playerPosX, int playerPosZ, GenerationPriority priority, boolean shouldSort) {
// equivalent to 2^(...)
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
@@ -198,24 +198,24 @@ public class LodRegion {
if (targetDetailLevel == detailLevel) {
if (!doesDataExist(detailLevel, childOffsetPosX, childOffsetPosZ))
posToGenerate.addPosToGenerate(detailLevel, childOffsetPosX + regionPosX * size,
childOffsetPosZ + regionPosZ * size);
childOffsetPosZ + regionPosZ * size, shouldSort);
} else {
if (priority == GenerationPriority.FAR_FIRST && detailLevel >= posToGenerate.farMinDetail
&& !doesDataExist(detailLevel, childOffsetPosX, childOffsetPosZ)) {
posToGenerate.addPosToGenerate(detailLevel, childOffsetPosX + regionPosX * size,
childOffsetPosZ + regionPosZ * size);
childOffsetPosZ + regionPosZ * size, shouldSort);
} else if (detailLevel > LodUtil.CHUNK_DETAIL_LEVEL) {
for (int x = 0; x <= 1; x++)
for (int z = 0; z <= 1; z++)
getPosToGenerate(posToGenerate, childDetailLevel, childPosX + x, childPosZ + z, playerPosX,
playerPosZ, priority);
playerPosZ, priority, shouldSort);
} else {
// we want at max one request per chunk (since the world generator creates
// chunks).
// So for lod smaller than a chunk, only recurse down
// the top right child
getPosToGenerate(posToGenerate, childDetailLevel, childPosX, childPosZ, playerPosX, playerPosZ,
priority);
priority, shouldSort);
}
}
}