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:
@@ -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 );
|
||||
|
||||
+4
-4
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user