Fixed saving. Guarrantee save is complete on exit. Trashed some logging

Also, tried (keyword: tried) fixing the FAR_FIRST generation.
This commit is contained in:
tom lee
2022-01-06 00:07:19 +08:00
parent e71660eb41
commit 1a5fd87346
11 changed files with 207 additions and 275 deletions
@@ -81,7 +81,6 @@ public class EventApi
if (lodDim == null)
return;
// FIXME: This is in server thread. We shouldn't be accessing the client's renderer!
LodWorldGenerator.INSTANCE.queueGenerationRequests(lodDim, ApiShared.lodBuilder);
}
@@ -99,7 +98,7 @@ public class EventApi
public void worldSaveEvent()
{
ApiShared.lodWorld.saveAllDimensions();
ApiShared.lodWorld.saveAllDimensions(false); // Do an async save.
}
/** This is also called when a new dimension loads */
@@ -126,11 +125,12 @@ public class EventApi
ThreadMapUtil.clearMaps();
// ClientApi.renderer.markForCleanup();
// ClientApi.renderer.destroyBuffers();
new Thread(() -> checkIfDisconnectedFromServer()).start();
checkIfDisconnectedFromServer();
//new Thread(() -> checkIfDisconnectedFromServer()).start();
}
private void checkIfDisconnectedFromServer()
{
/*
try
{
// world unloading events are called before disconnecting from the server,
@@ -141,10 +141,10 @@ public class EventApi
{
// this should never happen, but just in case
e.printStackTrace();
}
}*/
if (MC.getWrappedClientWorld() == null || (!MC.connectedToServer() && !MC.hasSinglePlayerServer()))
//if (MC.getWrappedClientWorld() == null || (!MC.connectedToServer() && !MC.hasSinglePlayerServer()))
{
// the player just left the server
@@ -157,7 +157,6 @@ public class EventApi
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0);
ApiShared.lodWorld.deselectWorld();
// prevent issues related to the buffer builder
// breaking or retaining previous data when changing worlds.
ClientApi.renderer.destroyBuffers();
@@ -213,7 +212,6 @@ public class EventApi
RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - lodDim.getCenterRegionPosX(), playerRegionPos.z - lodDim.getCenterRegionPosZ());
if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0)
{
ApiShared.lodWorld.saveAllDimensions();
lodDim.move(worldRegionOffset);
//LOGGER.info("offset: " + worldRegionOffset.x + "," + worldRegionOffset.z + "\t center: " + lodDim.getCenterX() + "," + lodDim.getCenterZ());
}
@@ -236,12 +234,10 @@ public class EventApi
// do the dimensions need to change in size?
if (ApiShared.lodBuilder.defaultDimensionWidthInRegions != newWidth || recalculateWidths)
{
ApiShared.lodWorld.saveAllDimensions();
// update the dimensions to fit the new width
ApiShared.lodWorld.resizeDimensionRegionWidth(newWidth);
ApiShared.lodBuilder.defaultDimensionWidthInRegions = newWidth;
ClientApi.renderer.setupBuffers(ApiShared.lodWorld.getLodDimension(MC.getCurrentDimension()));
ClientApi.renderer.setupBuffers();
recalculateWidths = false;
//LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth );
@@ -78,7 +78,7 @@ public class LodBufferBuilderFactory
public void end(String source) {
timer = System.nanoTime() - timer;
if (timer> 16000000) { //16 ms
ClientApi.LOGGER.info("NOTE: "+source+" took "+Duration.ofNanos(timer)+"!");
ClientApi.LOGGER.debug("NOTE: "+source+" took "+Duration.ofNanos(timer)+"!");
}
}
@@ -372,7 +372,7 @@ public class LodBufferBuilderFactory
long buildTime = endTime - startTime;
long executeTime = executeEnd - executeStart;
if (enableLogging)
ClientApi.LOGGER.info("Thread Build("+nodeToRenderThreads.size()+"/"+(lodDim.getWidth()*lodDim.getWidth())+ (fullRegen ? "FULL" : "")+") time: " + buildTime + " ms" + '\n' +
ClientApi.LOGGER.debug("Thread Build("+nodeToRenderThreads.size()+"/"+(lodDim.getWidth()*lodDim.getWidth())+ (fullRegen ? "FULL" : "")+") time: " + buildTime + " ms" + '\n' +
"thread execute time: " + executeTime + " ms");
//================================//
@@ -400,7 +400,7 @@ public class LodBufferBuilderFactory
uploadBuffers(posToUpload);
long uploadTime = System.currentTimeMillis() - startUploadTime;
if (enableLogging)
ClientApi.LOGGER.info("Thread Upload time: " + uploadTime + " ms");
ClientApi.LOGGER.debug("Thread Upload time: " + uploadTime + " ms");
}
catch (Exception e)
{
@@ -840,7 +840,7 @@ public class LodBufferBuilderFactory
private boolean swapBuffers() {
bufferLock.lock();
ClientApi.LOGGER.info("Lod Swap Buffers");
ClientApi.LOGGER.debug("Lod Swap Buffers");
{
boolean shouldRegenBuff = true;
try
@@ -205,7 +205,7 @@ public class LodBuilder
posX = LevelPosUtil.convert((byte) 0, chunk.getChunkPosX() * 16 + startX, minDetailLevel);
posZ = LevelPosUtil.convert((byte) 0, chunk.getChunkPosZ() * 16 + startZ, minDetailLevel);
if (!lodDim.doesDataExist(minDetailLevel, posX, posZ)) {
lodDim.addVerticalData(minDetailLevel, posX, posZ, data, false);
lodDim.addVerticalData(minDetailLevel, posX, posZ, data);
lodDim.updateData(minDetailLevel, posX, posZ);
}
}
@@ -97,12 +97,21 @@ public class LodWorldGenerator
// TODO: Rename the config option
if (CONFIG.client().worldGenerator().getAllowUnstableFeatureGeneration()) {
if (experimentalWorldGenerator == null) {
experimentalWorldGenerator = WRAPPER_FACTORY.createExperimentalWorldGenerator(lodBuilder, lodDim, world);
try {
experimentalWorldGenerator = WRAPPER_FACTORY.createExperimentalWorldGenerator(lodBuilder, lodDim, world);
if (experimentalWorldGenerator == null) CONFIG.client().worldGenerator().setAllowUnstableFeatureGeneration(false);
} catch (RuntimeException e) {
// Exception may happen if world got unloaded unorderly
e.printStackTrace();
}
}
} else {
if (experimentalWorldGenerator != null) {
experimentalWorldGenerator.stop();
try {
experimentalWorldGenerator.stop();
} catch (RuntimeException e) {
e.printStackTrace();
}
experimentalWorldGenerator = null;
}
}
@@ -157,9 +166,7 @@ public class LodWorldGenerator
IWorldWrapper serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension);
PosToGenerateContainer posToGenerate = lodDim.getPosToGenerate(
maxChunkGenRequests,
playerPosX,
playerPosZ);
maxChunkGenRequests, playerPosX, playerPosZ, priority);
byte detailLevel;
int posX;
@@ -21,11 +21,15 @@ package com.seibel.lod.core.handlers;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.time.Duration;
import java.time.Instant;
import java.util.ConcurrentModificationException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream;
@@ -39,7 +43,7 @@ import com.seibel.lod.core.objects.lod.RegionPos;
import com.seibel.lod.core.objects.lod.VerticalLevelContainer;
import com.seibel.lod.core.util.LodThreadFactory;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.util.ThreadMapUtil;
/**
* This object handles creating LodRegions
@@ -84,9 +88,10 @@ public class LodDimensionFileHandler
* Allow saving asynchronously, but never try to save multiple regions
* at a time
*/
private final ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
private AtomicBoolean isFileWritingThreadRunning = new AtomicBoolean(false);
private ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
private ConcurrentHashMap<RegionPos, LodRegion> regionToSave = new ConcurrentHashMap<RegionPos, LodRegion>();
public LodDimensionFileHandler(File newSaveFolder, LodDimension newLodDimension)
@@ -112,7 +117,13 @@ public class LodDimensionFileHandler
*/
public LodRegion loadRegionFromFile(byte detailLevel, RegionPos regionPos, DistanceGenerationMode generationMode, VerticalQuality verticalQuality)
{
LodRegion region = new LodRegion((byte) (LodUtil.REGION_DETAIL_LEVEL+1), regionPos, generationMode, verticalQuality);
// Get one from the region hot cache
LodRegion region = regionToSave.get(regionPos);
if (region!=null && region.getMinDetailLevel()<=detailLevel &&
region.getGenerationMode().compareTo(generationMode)>=0 &&
region.getVerticalQuality().compareTo(verticalQuality)>=0)
return region; // The current hot cache to-be-saved region match our requirement.
region = new LodRegion((byte) (LodUtil.REGION_DETAIL_LEVEL+1), regionPos, generationMode, verticalQuality);
return loadRegionFromFile(detailLevel, region, generationMode, verticalQuality);
}
@@ -123,14 +134,12 @@ public class LodDimensionFileHandler
public LodRegion loadRegionFromFile(byte detailLevel, LodRegion region, DistanceGenerationMode generationMode, VerticalQuality verticalQuality)
{
if (region.getGenerationMode().compareTo(generationMode)<0 || region.getVerticalQuality().compareTo(verticalQuality)<0) {
//TODO: add flush and save region for old one
regionToSave.put(region.getRegionPos(), region); //FIXME: The hashMap key should prob be a {regionPos,VertQual} pair.
region = new LodRegion((byte) (LodUtil.REGION_DETAIL_LEVEL+1), region.getRegionPos(), generationMode, verticalQuality);
}
int regionX = region.regionPosX;
int regionZ = region.regionPosZ;
for (byte tempDetailLevel = (byte) (region.getMinDetailLevel()-1); tempDetailLevel >= detailLevel; tempDetailLevel--)
{
@@ -214,33 +223,88 @@ public class LodDimensionFileHandler
// Save to File //
//==============//
/** Save all dirty regions in this LodDimension to file */
public void saveDirtyRegionsToFileAsync()
{
fileWritingThreadPool.execute(saveDirtyRegionsThread);
public void addRegionsToSave(LodRegion r) {
regionToSave.put(r.getRegionPos(), r);
}
private final Thread saveDirtyRegionsThread = new Thread(() ->
/** Save all dirty regions in this LodDimension to file */
public void saveDirtyRegionsToFile(boolean blockUntilFinished)
{
try
for (int i = 0; i < lodDimension.getWidth(); i++)
{
for (int i = 0; i < lodDimension.getWidth(); i++)
for (int j = 0; j < lodDimension.getWidth(); j++)
{
for (int j = 0; j < lodDimension.getWidth(); j++)
LodRegion r = lodDimension.getRegionByArrayIndex(i, j);
if (r != null && r.needSaving)
{
if (lodDimension.GetIsRegionDirty(i, j) && lodDimension.getRegionByArrayIndex(i, j) != null)
{
saveRegionToFile(lodDimension.getRegionByArrayIndex(i, j));
lodDimension.SetIsRegionDirty(i, j, false);
}
r.needSaving = false;
regionToSave.put(r.getRegionPos(), r);
}
}
}
catch (Exception e)
{
e.printStackTrace();
trySaveRegionsToBeSaved();
if (blockUntilFinished) {
ClientApi.LOGGER.info("Blocking until lod file save finishes!");
try {
fileWritingThreadPool.shutdown();
boolean worked = fileWritingThreadPool.awaitTermination(30, TimeUnit.SECONDS);
if (!worked)
ClientApi.LOGGER.error("File writing timed out! File data may not be saved correctly and may cause corruptions!!!");
} catch (InterruptedException e) {
ClientApi.LOGGER.error("File writing wait is interrupted! File data may not be saved correctly and may cause corruptions!!!");
e.printStackTrace();
} finally {
fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
}
}
});
}
public void trySaveRegionsToBeSaved() {
if (regionToSave.isEmpty()) return;
// Use Memory order Acquire to acquire any memory changes on getting this boolean
// (Corresponding call is the this::writerMain(...)::...setRelease(false);)
boolean haventStarted = !isFileWritingThreadRunning.compareAndExchangeAcquire(false, true);
if (haventStarted) {
// We acquired the atomic lock.
fileWritingThreadPool.execute(this::writerMain);
}
}
private void writerMain() {
// Use Memory order Relaxed as no additional memory changes needed to be visible.
// (This is just a safety checks)
boolean isStarted = isFileWritingThreadRunning.getPlain();
if (!isStarted) throw new ConcurrentModificationException("WriterMain Triggered but the thead state is not started!?");
ClientApi.LOGGER.info("Lod File Writer started. To-be-written-regions: "+regionToSave.size());
Instant start = Instant.now();
// Note: Since regionToSave is a ConcurrentHashMap, and the .values() return one that support concurrency,
// this for loop should be safe and loop until all values are gone.
while (!regionToSave.isEmpty()) {
for (LodRegion r : regionToSave.values()) {
//Check if the data has been swapped out right under me. Otherwise remove it from the entry
if (!regionToSave.remove(r.getRegionPos(), r)) continue;
try
{
Instant i = Instant.now();
ClientApi.LOGGER.info("Lod: Saving Region "+r.getRegionPos());
saveRegionToFile(r);
Instant j = Instant.now();
Duration d = Duration.between(i, j);
ClientApi.LOGGER.info("Lod: Region "+r.getRegionPos()+" save finish. Took "+d);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
Instant end = Instant.now();
ClientApi.LOGGER.info("Lod File Writer completed. Took "+Duration.between(start, end));
// Use Memory order Release to release any memory changes on setting this boolean
// (Corresponding call is the this::saveRegions(...)::...compareAndExchangeAcquire(false, true);)
isFileWritingThreadRunning.setRelease(false);
}
/**
* Save a specific region to disk.<br>
@@ -256,7 +320,7 @@ public class LodDimensionFileHandler
{
// Get the old file
File oldFile = getRegionFile(region.regionPosX, region.regionPosZ, region.getGenerationMode(), detailLevel, region.getVerticalQuality());
ClientApi.LOGGER.debug("saving region [" + region.regionPosX + ", " + region.regionPosZ + "] to file.");
ClientApi.LOGGER.debug("saving region [" + region.regionPosX + ", " + region.regionPosZ + "] detail "+detailLevel+" to file.");
boolean isFileFullyGened = false;
// make sure the file and folder exists
@@ -31,7 +31,7 @@ public class PosToGenerateContainer
{
private final int playerPosX;
private final int playerPosZ;
private final byte farMinDetail;
public final byte farMinDetail;
private int nearSize;
private int farSize;
@@ -38,13 +38,13 @@ import com.seibel.lod.core.util.LodThreadFactory;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.util.SingletonHandler;
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.IMinecraftWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
//FIXME: Race condition on lodDim move/resize!
/**
* This object holds all loaded LOD regions
@@ -62,7 +62,6 @@ public class LodDimension
{
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
public final IDimensionTypeWrapper dimension;
@@ -77,11 +76,8 @@ public class LodDimension
public volatile LodRegion[][] regions;
/** stores if the region at the given x and z index needs to be saved to disk */
private volatile boolean[][] isRegionDirty;
/** stores if the region at the given x and z index needs to be regenerated */
// Use int because I need Tri state:
// 0: both buffer good. 1: the displaying buffer good. 2: both buffer bad.
private volatile int[][] regenRegionBuffer;
/**
* if true that means there are regions in this dimension
@@ -140,8 +136,6 @@ public class LodDimension
regions = new LodRegion[width][width];
isRegionDirty = new boolean[width][width];
regenRegionBuffer = new int[width][width];
center = new RegionPos(0, 0);
}
@@ -158,6 +152,7 @@ public class LodDimension
public synchronized void move(RegionPos regionOffset)
{
ClientApi.LOGGER.info("LodDim MOVE. Offset: "+regionOffset);
saveDirtyRegionsToFile(false); //async add dirty regions to be saved.
int xOffset = regionOffset.x;
int zOffset = regionOffset.z;
@@ -169,7 +164,6 @@ public class LodDimension
for (int x = 0; x < width; x++)
for (int z = 0; z < width; z++) {
regions[x][z] = null;
regenRegionBuffer[x][z] = 0;
}
// update the new center
center.x += xOffset;
@@ -187,14 +181,10 @@ public class LodDimension
{
for (int z = 0; z < width; z++)
{
if (x + xOffset < width) {
if (x + xOffset < width)
regions[x][z] = regions[x + xOffset][z];
regenRegionBuffer[x][z] = regenRegionBuffer[x + xOffset][z];
}
else {
else
regions[x][z] = null;
regenRegionBuffer[x][z] = 0;
}
}
}
}
@@ -205,14 +195,11 @@ public class LodDimension
{
for (int z = 0; z < width; z++)
{
if (x + xOffset >= 0) {
if (x + xOffset >= 0)
regions[x][z] = regions[x + xOffset][z];
regenRegionBuffer[x][z] = regenRegionBuffer[x + xOffset][z];
}
else {
else
regions[x][z] = null;
regenRegionBuffer[x][z] = 0;
}
}
}
}
@@ -225,14 +212,10 @@ public class LodDimension
{
for (int z = 0; z < width; z++)
{
if (z + zOffset < width) {
if (z + zOffset < width)
regions[x][z] = regions[x][z + zOffset];
regenRegionBuffer[x][z] = regenRegionBuffer[x][z + zOffset];
}
else {
else
regions[x][z] = null;
regenRegionBuffer[x][z] = 0;
}
}
}
}
@@ -243,15 +226,10 @@ public class LodDimension
{
for (int z = width - 1; z >= 0; z--)
{
if (z + zOffset >= 0) {
if (z + zOffset >= 0)
regions[x][z] = regions[x][z + zOffset];
regenRegionBuffer[x][z] = regenRegionBuffer[x][z + zOffset];
}
else {
else
regions[x][z] = null;
regenRegionBuffer[x][z] = 0;
}
}
}
}
}
@@ -378,18 +356,17 @@ public class LodDimension
byte minAllowedDetailLevel;
regionX = (x + center.x) - halfWidth;
regionZ = (z + center.z) - halfWidth;
if (regions[x][z] != null) {
LodRegion region = regions[x][z];
if (region != null) {
// check what detail level this region should be
// and cut it if it is higher then that
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ,
playerPosX, playerPosZ);
detail = DetailDistanceUtil.getTreeCutDetailFromDistance(minDistance);
minAllowedDetailLevel = DetailDistanceUtil.getCutLodDetail(detail);
if (regions[x][z].getMinDetailLevel() < minAllowedDetailLevel) {
regions[x][z].cutTree(minAllowedDetailLevel);
regenRegionBuffer[x][z] = 2;
if (region.getMinDetailLevel() < minAllowedDetailLevel) {
region.cutTree(minAllowedDetailLevel);
region.needRegenBuffer = 2;
regenDimensionBuffers = true;
}
}
@@ -436,12 +413,15 @@ public class LodDimension
boolean updated = false;
if (region == null) {
regions[x][z] = getRegionFromFile(regionPos, minDetail, generationMode, verticalQuality);
region = getRegionFromFile(regionPos, minDetail, generationMode, verticalQuality);
regions[x][z] = region;
updated = true;
} else if (region.getGenerationMode().compareTo(generationMode) < 0 ||
region.getVerticalQuality() != verticalQuality ||
region.getMinDetailLevel() > minDetail) {
regions[x][z] = getRegionFromFile(regions[x][z], minDetail, generationMode, verticalQuality);
// The 'getRegionFromFile' will flush and save the region if it returns a new one
region = getRegionFromFile(regions[x][z], minDetail, generationMode, verticalQuality);
regions[x][z] = region;
updated = true;
} else if (region.lastMaxDetailLevel != maxDetail) {
region.lastMaxDetailLevel = maxDetail;
@@ -449,9 +429,8 @@ public class LodDimension
} else if (region.lastMaxDetailLevel != region.getMinDetailLevel()) {
updated = true;
}
if (updated) {
regenRegionBuffer[x][z] = 2;
region.needRegenBuffer = 2;
regenDimensionBuffers = true;
}
});
@@ -462,55 +441,12 @@ public class LodDimension
cutAndExpandThread.execute(thread);
}
/**
* Use addVerticalData when possible.
* Add the given LOD to this dimension at the coordinate
* stored in the LOD. If an LOD already exists at the given
* coordinate it will be overwritten.
*/
public Boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, long data, boolean dontSave)
{
int regionPosX = LevelPosUtil.getRegion(detailLevel, posX);
int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ);
// don't continue if the region can't be saved
LodRegion region = getRegion(regionPosX, regionPosZ);
if (region == null)
return false;
boolean nodeAdded = region.addData(detailLevel, posX, posZ, verticalIndex, data);
// only save valid LODs to disk
if (!dontSave && fileHandler != null)
{
try
{
// mark the region as dirty, so it will be saved to disk
int xIndex = (regionPosX - center.x) + halfWidth;
int zIndex = (regionPosZ - center.z) + halfWidth;
isRegionDirty[xIndex][zIndex] = true;
regenRegionBuffer[xIndex][zIndex] = 2;
regenDimensionBuffers = true;
}
catch (ArrayIndexOutOfBoundsException e)
{
e.printStackTrace();
// If this happens, the method was probably
// called when the dimension was changing size.
// Hopefully this shouldn't be an issue.
}
}
return nodeAdded;
}
/**
* Add whole column of LODs to this dimension at the coordinate
* stored in the LOD. If an LOD already exists at the given
* coordinate it will be overwritten.
*/
public Boolean addVerticalData(byte detailLevel, int posX, int posZ, long[] data, boolean dontSave)
public Boolean addVerticalData(byte detailLevel, int posX, int posZ, long[] data)
{
int regionPosX = LevelPosUtil.getRegion(detailLevel, posX);
int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ);
@@ -521,44 +457,28 @@ public class LodDimension
return false;
boolean nodeAdded = region.addVerticalData(detailLevel, posX, posZ, data);
// only save valid LODs to disk
if (!dontSave && fileHandler != null)
{
try
{
// mark the region as dirty, so it will be saved to disk
int xIndex = (regionPosX - center.x) + halfWidth;
int zIndex = (regionPosZ - center.z) + halfWidth;
isRegionDirty[xIndex][zIndex] = true;
regenRegionBuffer[xIndex][zIndex] = 2;
regenDimensionBuffers = true;
}
catch (ArrayIndexOutOfBoundsException e)
{
e.printStackTrace();
// If this happens, the method was probably
// called when the dimension was changing size.
// Hopefully this shouldn't be an issue.
}
if (nodeAdded) {
region.needRegenBuffer = 2;
region.needSaving = true;
regenDimensionBuffers = true;
}
return nodeAdded;
}
/** marks the region at the given region position to have its buffer rebuilt */
public void markRegionBufferToRegen(int xRegion, int zRegion)
{
int xIndex = (xRegion - center.x) + halfWidth;
int zIndex = (zRegion - center.z) + halfWidth;
regenRegionBuffer[xIndex][zIndex] = 2;
LodRegion r = getRegion(xRegion,zRegion);
if (r!=null) {
r.needRegenBuffer = 2;
regenDimensionBuffers = true;
}
}
/**
* Returns every position that need to be generated based on the position of the player
*/
public PosToGenerateContainer getPosToGenerate(int maxDataToGenerate, int playerBlockPosX, int playerBlockPosZ)
public PosToGenerateContainer getPosToGenerate(int maxDataToGenerate, int playerBlockPosX, int playerBlockPosZ, GenerationPriority priority)
{
PosToGenerateContainer posToGenerate;
posToGenerate = new PosToGenerateContainer((byte) 8, maxDataToGenerate, playerBlockPosX, playerBlockPosZ);
@@ -566,7 +486,7 @@ public class LodDimension
//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);
lodRegion.getPosToGenerate(posToGenerate, playerBlockPosX, playerBlockPosZ, priority);
});
return posToGenerate;
}
@@ -656,8 +576,9 @@ public class LodDimension
LodRegion region = getRegion(xRegion, zRegion);
if (region == null)
return;
markRegionBufferToRegen(xRegion, zRegion);
region.clear(detailLevel, posX, posZ);
region.needRegenBuffer = 2;
regenDimensionBuffers = true;
}
/**
@@ -666,16 +587,12 @@ public class LodDimension
*/
public boolean getAndClearRegionNeedBufferRegen(int regionX, int regionZ)
{
//FIXME: Use actual atomics on regenRegionBuffer
//FIXME: Race condition on lodDim move/resize!
int xIndex = (regionX - center.x) + halfWidth;
int zIndex = (regionZ - center.z) + halfWidth;
if (xIndex < 0 || xIndex >= width || zIndex < 0 || zIndex >= width)
return false;
int i = regenRegionBuffer[xIndex][zIndex];
//FIXME: Use actual atomics on needRegenBuffer
LodRegion region = getRegion(regionX, regionZ);
if (region == null) return false;
int i = region.needRegenBuffer;
if (i > 0) {
regenRegionBuffer[xIndex][zIndex]--;
region.needRegenBuffer--;
return true;
}
return false;
@@ -696,11 +613,10 @@ public class LodDimension
int xRegion = LevelPosUtil.getRegion(detailLevel, posX);
int zRegion = LevelPosUtil.getRegion(detailLevel, posZ);
LodRegion region = getRegion(xRegion, zRegion);
if (region == null)
return;
markRegionBufferToRegen(xRegion, zRegion);
if (region == null) return;
region.updateArea(detailLevel, posX, posZ);
region.needRegenBuffer = 2;
regenDimensionBuffers = true;
}
/** Returns true if a region exists at the given LevelPos */
@@ -732,9 +648,10 @@ public class LodDimension
}
/** Save all dirty regions in this LodDimension to file. */
public void saveDirtyRegionsToFileAsync()
public void saveDirtyRegionsToFile(boolean blockUntilFinished)
{
fileHandler.saveDirtyRegionsToFileAsync();
if (fileHandler == null) return;
fileHandler.saveDirtyRegionsToFile(blockUntilFinished);
}
@@ -789,13 +706,6 @@ public class LodDimension
halfWidth = width/ 2;
regions = new LodRegion[width][width];
isRegionDirty = new boolean[width][width];
regenRegionBuffer = new int[width][width];
// populate isRegionDirty
for (int i = 0; i < width; i++)
for (int j = 0; j < width; j++)
isRegionDirty[i][j] = false;
}
@@ -818,14 +728,4 @@ public class LodDimension
}
return stringBuilder.toString();
}
public boolean GetIsRegionDirty(int i, int j)
{
return isRegionDirty[i][j];
}
public void SetIsRegionDirty(int i, int j, boolean val)
{
isRegionDirty[i][j] = val;
}
}
@@ -20,6 +20,7 @@
package com.seibel.lod.core.objects.lod;
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
import com.seibel.lod.core.enums.config.GenerationPriority;
import com.seibel.lod.core.enums.config.VerticalQuality;
import com.seibel.lod.core.objects.PosToGenerateContainer;
import com.seibel.lod.core.objects.PosToRenderContainer;
@@ -67,6 +68,9 @@ public class LodRegion
/** this region's z RegionPos */
public final int regionPosZ;
public volatile int needRegenBuffer = 2;
public volatile boolean needSaving = false;
public LodRegion(byte minDetailLevel, RegionPos regionPos, DistanceGenerationMode generationMode, VerticalQuality verticalQuality)
{
this.minDetailLevel = minDetailLevel;
@@ -81,73 +85,8 @@ public class LodRegion
{
dataContainer[lod] = new VerticalLevelContainer(lod);
}
boolean fileFound = false;
/*
preGeneratedChunkPos = new boolean[32 * 32];
if (MinecraftWrapper.INSTANCE.hasSinglePlayerServer() && LodConfig.CLIENT.worldGenerator.useExperimentalPreGenLoading.get())
{
File regionFileDirHead;
File regionFileDirParent;
// local world
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(MinecraftWrapper.INSTANCE.getCurrentDimension());
// provider needs a separate variable to prevent
// the compiler from complaining
StringBuilder string = new StringBuilder();
try
{
ServerChunkProvider provider = serverWorld.getChunkSource();
//System.out.println(provider.dataStorage.dataFolder);
regionFileDirHead = new File(provider.dataStorage.dataFolder.getCanonicalFile().getParentFile().toPath().toAbsolutePath().toString() + File.separatorChar + "region", "r." + regionPosZ + "." + regionPosX + ".mca");
if (regionFileDirHead.exists())
{
regionFileDirParent = regionFileDirHead.getParentFile();
//string.append(regionFileDirParent.toString());
string.append(regionFileDirHead);
RegionFile regionFile = new RegionFile(regionFileDirHead, regionFileDirParent, true);
for (int x = 0; x < 32; x++)
{
for (int z = 0; z < 32; z++)
{
preGeneratedChunkPos[x * 32 + z] = regionFile.doesChunkExist(new ChunkPos(regionPosX * 32 + x, regionPosZ * 32 + z));
}
}
string.append("region " + regionPosX + " " + regionPosZ + "\n");
for (int x = 0; x < 32; x++)
{
for (int z = 0; z < 32; z++)
{
//regionFile.doesChunkExist()
string.append(preGeneratedChunkPos[x * 32 + z] + "\t");
}
string.append("\n");
}
regionFile.close();
}
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println(string);
}*/
}
/** Return true if the chunk has been pregenerated in game */
//public boolean isChunkPreGenerated(int xChunkPos, int zChunkPos)
//{
// xChunkPos = LevelPosUtil.getRegionModule(LodUtil.CHUNK_DETAIL_LEVEL, xChunkPos);
// zChunkPos = LevelPosUtil.getRegionModule(LodUtil.CHUNK_DETAIL_LEVEL, zChunkPos);
// return preGeneratedChunkPos[xChunkPos * 32 + zChunkPos];
//}
/**
* Inserts the data point into the region.
* <p>
@@ -229,9 +168,9 @@ public class LodRegion
* TODO why don't we return the posToGenerate, it would make this easier to understand
*/
public void getPosToGenerate(PosToGenerateContainer posToGenerate,
int playerBlockPosX, int playerBlockPosZ)
int playerBlockPosX, int playerBlockPosZ, GenerationPriority priority)
{
getPosToGenerate(posToGenerate, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerBlockPosX, playerBlockPosZ);
getPosToGenerate(posToGenerate, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerBlockPosX, playerBlockPosZ, priority);
}
@@ -242,7 +181,7 @@ public class LodRegion
* TODO why don't we return the posToGenerate, it would make this easier to understand
*/
private void getPosToGenerate(PosToGenerateContainer posToGenerate, byte detailLevel,
int childOffsetPosX, int childOffsetPosZ, int playerPosX, int playerPosZ)
int childOffsetPosX, int childOffsetPosZ, int playerPosX, int playerPosZ, GenerationPriority priority)
{
// equivalent to 2^(...)
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
@@ -275,11 +214,14 @@ public class LodRegion
{
for (int x = 0; x <= 1; x++)
for (int z = 0; z <= 1; z++)
getPosToGenerate(posToGenerate, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ);
}
else
{
getPosToGenerate(posToGenerate, childDetailLevel, childPosX, childPosZ, playerPosX, playerPosZ);
getPosToGenerate(posToGenerate, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ, priority);
} else if (priority == GenerationPriority.FAR_FIRST && detailLevel == posToGenerate.farMinDetail) {
if (!doesDataExist(detailLevel, childOffsetPosX, childOffsetPosZ))
posToGenerate.addPosToGenerate(detailLevel, childOffsetPosX + regionPosX * size, childOffsetPosZ + regionPosZ * size);
else
getPosToGenerate(posToGenerate, childDetailLevel, childPosX, childPosZ, playerPosX, playerPosZ, priority);
} else {
getPosToGenerate(posToGenerate, childDetailLevel, childPosX, childPosZ, playerPosX, playerPosZ, priority);
}
}
}
@@ -92,6 +92,7 @@ public class LodWorld
public void deselectWorld()
{
worldName = NO_WORLD_LOADED;
saveAllDimensions(true); // Make sure all dims are saved. This will block threads
lodDimensions = null;
isWorldLoaded = false;
}
@@ -106,7 +107,8 @@ public class LodWorld
if (lodDimensions == null)
return;
lodDimensions.put(newDimension.dimension, newDimension);
LodDimension oldDim = lodDimensions.put(newDimension.dimension, newDimension);
if (oldDim != null) oldDim.saveDirtyRegionsToFile(true);
}
/**
@@ -129,7 +131,7 @@ public class LodWorld
if (lodDimensions == null)
return;
saveAllDimensions();
saveAllDimensions(true); //block until saving is done
for (IDimensionTypeWrapper key : lodDimensions.keySet())
lodDimensions.get(key).setRegionWidth(newRegionWidth);
@@ -138,7 +140,7 @@ public class LodWorld
/**
* Requests all dimensions save any dirty regions they may have.
*/
public void saveAllDimensions()
public void saveAllDimensions(boolean isBlocking)
{
if (lodDimensions == null)
return;
@@ -147,8 +149,10 @@ public class LodWorld
// but that requires a LodDimension.hasDirtyRegions() method or something similar
ClientApi.LOGGER.info("Saving LODs");
for (IDimensionTypeWrapper key : lodDimensions.keySet())
lodDimensions.get(key).saveDirtyRegionsToFileAsync();
for (IDimensionTypeWrapper key : lodDimensions.keySet()) {
lodDimensions.get(key).saveDirtyRegionsToFile(isBlocking);
}
//FIXME: This should block until file is saved.
}
@@ -82,10 +82,29 @@ public class RegionPos
.offset(LodUtil.CHUNK_WIDTH / 2, 0, LodUtil.CHUNK_WIDTH / 2);
}
@Override
public boolean equals(Object o) {
// If the object is compared with itself then return true
if (o == this) {
return true;
}
// Check if o is an instance of RegionPos or not
if (!(o instanceof RegionPos)) {
return false;
}
RegionPos c = (RegionPos) o;
return c.x==x &&c.z==z;
}
@Override
public String toString()
{
return "(" + x + "," + z + ")";
}
@Override
public int hashCode() {
return Long.hashCode((long)(x) << Integer.BYTES + z);
}
}
@@ -382,7 +382,7 @@ public class LodRenderer
}
/** Create all buffers that will be used. */
public void setupBuffers(LodDimension lodDim)
public void setupBuffers()
{
lodBufferBuilderFactory.allBuffersRequireReset = true;
}