Fix world gen for extreme render distances
This commit is contained in:
+33
-7
@@ -24,7 +24,9 @@ import com.seibel.distanthorizons.core.file.ISourceProvider;
|
|||||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||||
import com.seibel.distanthorizons.core.sql.repo.FullDataRepo;
|
import com.seibel.distanthorizons.core.sql.repo.FullDataRepo;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,16 +38,40 @@ public interface IFullDataSourceProvider extends ISourceProvider<NewFullDataSour
|
|||||||
CompletableFuture<NewFullDataSource> getAsync(DhSectionPos pos);
|
CompletableFuture<NewFullDataSource> getAsync(DhSectionPos pos);
|
||||||
NewFullDataSource get(DhSectionPos pos);
|
NewFullDataSource get(DhSectionPos pos);
|
||||||
|
|
||||||
/**
|
|
||||||
* If this provider has the ability to create (world gen) or get (networking)
|
|
||||||
* missing data sources this method will queue the given position
|
|
||||||
* for generation or retrieval.
|
|
||||||
*/
|
|
||||||
void queuePositionForGenerationOrRetrievalIfNecessary(DhSectionPos pos);
|
|
||||||
|
|
||||||
CompletableFuture<Void> updateDataSourceAsync(NewFullDataSource chunkData);
|
CompletableFuture<Void> updateDataSourceAsync(NewFullDataSource chunkData);
|
||||||
|
|
||||||
/** @return -1 if this provider never has unsaved data sources */
|
/** @return -1 if this provider never has unsaved data sources */
|
||||||
default int getUnsavedDataSourceCount() { return -1; }
|
default int getUnsavedDataSourceCount() { return -1; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// retrieval (world gen) //
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true this {@link IFullDataSourceProvider} can generate or retrieve
|
||||||
|
* {@link NewFullDataSource}'s that aren't currently in the database.
|
||||||
|
*/
|
||||||
|
default boolean canRetrieveMissingDataSources() { return false; }
|
||||||
|
|
||||||
|
/** @return null if it was unable to generate any positions, an empty array if all positions were generated */
|
||||||
|
@Nullable
|
||||||
|
default ArrayList<DhSectionPos> getPositionsToRetrieve(DhSectionPos pos) { return null; }
|
||||||
|
/**
|
||||||
|
* Returns how many positions could potentially be generated for this position assuming the position is empty.
|
||||||
|
* Used when estimating the total number of retrieval requests.
|
||||||
|
*/
|
||||||
|
default int getMaxPossibleRetrievalPositionCountForPos(DhSectionPos pos) { return -1; }
|
||||||
|
|
||||||
|
/** @return true if the position was queued, false if not */
|
||||||
|
default boolean queuePositionForRetrieval(DhSectionPos genPos) { return false; }
|
||||||
|
/**
|
||||||
|
* @return false if the provider isn't accepting new requests,
|
||||||
|
* this can be due to having a full queue or some other
|
||||||
|
* limiting factor.
|
||||||
|
*/
|
||||||
|
default boolean canQueueRetrieval() { return false; }
|
||||||
|
|
||||||
|
/** Can be used to display how many total retrieval requests might be available. */
|
||||||
|
default void setTotalRetrievalPositionCount(int newCount) { }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -127,7 +127,7 @@ public class NewFullDataFileHandler
|
|||||||
@Override
|
@Override
|
||||||
protected NewFullDataSource createNewDataSourceFromExistingDtos(DhSectionPos pos)
|
protected NewFullDataSource createNewDataSourceFromExistingDtos(DhSectionPos pos)
|
||||||
{
|
{
|
||||||
// TODO maybe just set children update flags to true?
|
// TODO maybe just set children update flags to true?
|
||||||
return NewFullDataSource.createEmpty(pos);
|
return NewFullDataSource.createEmpty(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+101
-88
@@ -31,13 +31,15 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|||||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||||
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
|
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
|
||||||
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
||||||
|
import com.seibel.distanthorizons.core.sql.repo.NewFullDataSourceRepo;
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
|
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||||
|
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -46,18 +48,14 @@ public class NewGeneratedFullDataFileHandler extends NewFullDataFileHandler impl
|
|||||||
{
|
{
|
||||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||||
|
|
||||||
private final AtomicReference<IWorldGenerationQueue> worldGenQueueRef = new AtomicReference<>(null);
|
public static final int MAX_WORLD_GEN_REQUESTS_PER_THREAD = 20;
|
||||||
|
|
||||||
|
|
||||||
|
private final AtomicReference<IWorldGenerationQueue> worldGenQueueRef = new AtomicReference<>(null);
|
||||||
private final ArrayList<IOnWorldGenCompleteListener> onWorldGenTaskCompleteListeners = new ArrayList<>();
|
private final ArrayList<IOnWorldGenCompleteListener> onWorldGenTaskCompleteListeners = new ArrayList<>();
|
||||||
|
|
||||||
protected final DelayedFullDataSourceSaveCache delayedFullDataSourceSaveCache = new DelayedFullDataSourceSaveCache(this::onDataSourceSave, 5_000);
|
protected final DelayedFullDataSourceSaveCache delayedFullDataSourceSaveCache = new DelayedFullDataSourceSaveCache(this::onDataSourceSave, 5_000);
|
||||||
|
|
||||||
// TODO name better
|
|
||||||
// this is just the list of section pos that have had their world generation
|
|
||||||
// calculated and queued this session.
|
|
||||||
@Deprecated
|
|
||||||
private final Set<DhSectionPos> genHandledPosSet = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
//=============//
|
||||||
@@ -68,29 +66,6 @@ public class NewGeneratedFullDataFileHandler extends NewFullDataFileHandler impl
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===========//
|
|
||||||
// overrides //
|
|
||||||
//===========//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public NewFullDataSource get(DhSectionPos pos) { return this.get(pos, true); }
|
|
||||||
public NewFullDataSource get(DhSectionPos pos, boolean runWorldGenCheck)
|
|
||||||
{
|
|
||||||
NewFullDataSource dataSource = super.get(pos);
|
|
||||||
|
|
||||||
if (runWorldGenCheck)
|
|
||||||
{
|
|
||||||
this.tryQueueSection(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
return dataSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void queuePositionForGenerationOrRetrievalIfNecessary(DhSectionPos pos) { this.tryQueueSection(pos); }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================//
|
//==================//
|
||||||
// generation queue //
|
// generation queue //
|
||||||
//==================//
|
//==================//
|
||||||
@@ -106,11 +81,7 @@ public class NewGeneratedFullDataFileHandler extends NewFullDataFileHandler impl
|
|||||||
LOGGER.info("Set world gen queue for level ["+this.level+"].");
|
LOGGER.info("Set world gen queue for level ["+this.level+"].");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearGenerationQueue()
|
public void clearGenerationQueue() { this.worldGenQueueRef.set(null); }
|
||||||
{
|
|
||||||
this.worldGenQueueRef.set(null);
|
|
||||||
this.genHandledPosSet.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Can be used to remove positions that are outside the player's render distance. */
|
/** Can be used to remove positions that are outside the player's render distance. */
|
||||||
public void removeGenRequestIf(Function<DhSectionPos, Boolean> removeIf)
|
public void removeGenRequestIf(Function<DhSectionPos, Boolean> removeIf)
|
||||||
@@ -122,14 +93,6 @@ public class NewGeneratedFullDataFileHandler extends NewFullDataFileHandler impl
|
|||||||
{
|
{
|
||||||
worldGenQueue.removeGenRequestIf(removeIf);
|
worldGenQueue.removeGenRequestIf(removeIf);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.genHandledPosSet.forEach((pos) ->
|
|
||||||
{
|
|
||||||
if (removeIf.apply(pos))
|
|
||||||
{
|
|
||||||
this.genHandledPosSet.remove(pos);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -191,61 +154,92 @@ public class NewGeneratedFullDataFileHandler extends NewFullDataFileHandler impl
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
//===================================//
|
||||||
// helper methods //
|
// world gen (data source retrieval) //
|
||||||
//================//
|
//===================================//
|
||||||
|
|
||||||
/** does nothing if this section or one of it's parents has already been queued */
|
@Override
|
||||||
public void tryQueueSection(DhSectionPos pos)
|
public boolean canRetrieveMissingDataSources() { return true; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTotalRetrievalPositionCount(int newCount)
|
||||||
{
|
{
|
||||||
IWorldGenerationQueue tempWorldGenQueue = this.worldGenQueueRef.get();
|
IWorldGenerationQueue worldGenQueue = this.worldGenQueueRef.get();
|
||||||
if (tempWorldGenQueue == null)
|
if (worldGenQueue != null)
|
||||||
{
|
{
|
||||||
return;
|
worldGenQueue.setEstimatedTotalTaskCount(newCount);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (this.genHandledPosSet.contains(pos))
|
|
||||||
|
@Override
|
||||||
|
public boolean canQueueRetrieval()
|
||||||
|
{
|
||||||
|
IWorldGenerationQueue worldGenQueue = this.worldGenQueueRef.get();
|
||||||
|
if (worldGenQueue == null)
|
||||||
{
|
{
|
||||||
return;
|
// we can't queue anything if the world generator isn't set up yet
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ThreadPoolExecutor updateExecutor = ThreadPoolUtil.getUpdatePropagatorExecutor();
|
||||||
AtomicBoolean positionAlreadyHandled = new AtomicBoolean(false);
|
if (updateExecutor == null || updateExecutor.getQueue().size() >= MAX_UPDATE_TASK_COUNT)
|
||||||
pos.forEachPosUpToDetailLevel(NewFullDataFileHandler.TOP_SECTION_DETAIL_LEVEL, (parentPos) ->
|
|
||||||
{
|
{
|
||||||
if (!positionAlreadyHandled.get())
|
// don't queue additional world gen requests if the updater is behind
|
||||||
{
|
return false;
|
||||||
if (this.genHandledPosSet.contains(parentPos))
|
|
||||||
{
|
|
||||||
positionAlreadyHandled.set(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (positionAlreadyHandled.get())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
this.genHandledPosSet.add(pos);
|
|
||||||
|
|
||||||
|
|
||||||
|
// don't queue additional world gen requests beyond the max allotted
|
||||||
|
int maxQueueCount = MAX_WORLD_GEN_REQUESTS_PER_THREAD * Config.Client.Advanced.MultiThreading.numberOfWorldGenerationThreads.get();
|
||||||
|
return worldGenQueue.getWaitingTaskCount() < maxQueueCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean queuePositionForRetrieval(DhSectionPos genPos)
|
||||||
|
{
|
||||||
|
IWorldGenerationQueue worldGenQueue = this.worldGenQueueRef.get();
|
||||||
|
if (worldGenQueue == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// get the un-generated pos list
|
GenTask genTask = new GenTask(genPos);
|
||||||
byte minGeneratorSectionDetailLevel = (byte) (tempWorldGenQueue.highestDataDetail() + DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
|
CompletableFuture<WorldGenResult> worldGenFuture = worldGenQueue.submitGenTask(genPos, (byte) (genPos.getDetailLevel() - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL), genTask);
|
||||||
|
worldGenFuture.whenComplete((genTaskResult, ex) -> this.onWorldGenTaskComplete(genTaskResult, ex));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArrayList<DhSectionPos> getPositionsToRetrieve(DhSectionPos pos)
|
||||||
|
{
|
||||||
|
IWorldGenerationQueue worldGenQueue = this.worldGenQueueRef.get();
|
||||||
|
if (worldGenQueue == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO based on the column generation step, only check children that are un-generated
|
||||||
|
ArrayList<DhSectionPos> generationList = new ArrayList<>();
|
||||||
|
byte minGeneratorSectionDetailLevel = (byte) (worldGenQueue.highestDataDetail() + DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
|
||||||
pos.forEachChildAtDetailLevel(minGeneratorSectionDetailLevel, (genPos) ->
|
pos.forEachChildAtDetailLevel(minGeneratorSectionDetailLevel, (genPos) ->
|
||||||
{
|
{
|
||||||
IWorldGenerationQueue worldGenQueue = this.worldGenQueueRef.get();
|
if (!this.repo.existsWithKey(genPos))
|
||||||
if (worldGenQueue == null)
|
|
||||||
{
|
{
|
||||||
return;
|
// nothing exists for this position, it needs generation
|
||||||
|
generationList.add(genPos);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (this.repo.existsWithKey(genPos))
|
|
||||||
{
|
{
|
||||||
// TODO only pull in the generation steps
|
byte[] columnGenerationSteps = ((NewFullDataSourceRepo)this.repo).getColumnGenerationStepForPos(genPos);
|
||||||
NewFullDataSource potentialDataSource = this.get(genPos, false);
|
if (columnGenerationSteps == null)
|
||||||
|
{
|
||||||
|
// shouldn't happen, but just in case
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
EDhApiWorldGenerationStep currentMinWorldGenStep = EDhApiWorldGenerationStep.LIGHT;
|
EDhApiWorldGenerationStep currentMinWorldGenStep = EDhApiWorldGenerationStep.LIGHT;
|
||||||
checkWorldGenLoop:
|
checkWorldGenLoop:
|
||||||
@@ -254,7 +248,7 @@ public class NewGeneratedFullDataFileHandler extends NewFullDataFileHandler impl
|
|||||||
for (int z = 0; z < NewFullDataSource.WIDTH; z++)
|
for (int z = 0; z < NewFullDataSource.WIDTH; z++)
|
||||||
{
|
{
|
||||||
int index = NewFullDataSource.relativePosToIndex(x, z);
|
int index = NewFullDataSource.relativePosToIndex(x, z);
|
||||||
byte genStepValue = potentialDataSource.columnGenerationSteps[index];
|
byte genStepValue = columnGenerationSteps[index];
|
||||||
|
|
||||||
if (genStepValue < currentMinWorldGenStep.value)
|
if (genStepValue < currentMinWorldGenStep.value)
|
||||||
{
|
{
|
||||||
@@ -275,19 +269,38 @@ public class NewGeneratedFullDataFileHandler extends NewFullDataFileHandler impl
|
|||||||
|
|
||||||
if (currentMinWorldGenStep != EDhApiWorldGenerationStep.EMPTY)
|
if (currentMinWorldGenStep != EDhApiWorldGenerationStep.EMPTY)
|
||||||
{
|
{
|
||||||
// no world gen needed
|
// no world gen needed for this position
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generationList.add(genPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// queue each new gen task
|
|
||||||
GenTask genTask = new GenTask(genPos);
|
|
||||||
CompletableFuture<WorldGenResult> worldGenFuture = tempWorldGenQueue.submitGenTask(genPos, (byte) (genPos.getDetailLevel() - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL), genTask);
|
|
||||||
worldGenFuture.whenComplete((genTaskResult, ex) -> this.onWorldGenTaskComplete(genTaskResult, ex));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return generationList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxPossibleRetrievalPositionCountForPos(DhSectionPos pos)
|
||||||
|
{
|
||||||
|
IWorldGenerationQueue worldGenQueue = this.worldGenQueueRef.get();
|
||||||
|
if (worldGenQueue == null)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int minGeneratorSectionDetailLevel = worldGenQueue.highestDataDetail() + DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||||
|
int detailLevelDiff = pos.getDetailLevel() - minGeneratorSectionDetailLevel;
|
||||||
|
|
||||||
|
return BitShiftUtil.powerOfTwo(detailLevelDiff);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=======//
|
||||||
|
// debug //
|
||||||
|
//=======//
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void debugRender(DebugRenderer renderer)
|
public void debugRender(DebugRenderer renderer)
|
||||||
{
|
{
|
||||||
|
|||||||
+14
-10
@@ -47,6 +47,7 @@ public class RenderSourceFileHandler extends AbstractLegacyDataSourceHandler<Col
|
|||||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||||
|
|
||||||
private final F3Screen.NestedMessage threadPoolMsg;
|
private final F3Screen.NestedMessage threadPoolMsg;
|
||||||
|
private int totalRetrievalPositionCount = 0;
|
||||||
|
|
||||||
public final IFullDataSourceProvider fullDataSourceProvider;
|
public final IFullDataSourceProvider fullDataSourceProvider;
|
||||||
|
|
||||||
@@ -73,10 +74,6 @@ public class RenderSourceFileHandler extends AbstractLegacyDataSourceHandler<Col
|
|||||||
@Override
|
@Override
|
||||||
public ColumnRenderSource get(DhSectionPos pos)
|
public ColumnRenderSource get(DhSectionPos pos)
|
||||||
{
|
{
|
||||||
// call the full data provider to make sure the full data is up to date
|
|
||||||
// and any necessary world generation has been queued/completed
|
|
||||||
this.fullDataSourceProvider.queuePositionForGenerationOrRetrievalIfNecessary(pos);
|
|
||||||
|
|
||||||
return super.get(pos);
|
return super.get(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +116,19 @@ public class RenderSourceFileHandler extends AbstractLegacyDataSourceHandler<Col
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=====================//
|
||||||
|
// extension overrides //
|
||||||
|
//=====================//
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Void> updateDataSourceAsync(NewFullDataSource inputDataSource)
|
||||||
|
{
|
||||||
|
// TODO once the legacy data provider has been replaced this can be removed
|
||||||
|
this.updateDataSourceAtPos(inputDataSource.getSectionPos(), inputDataSource);
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=========//
|
//=========//
|
||||||
// F3 menu //
|
// F3 menu //
|
||||||
@@ -148,12 +158,6 @@ public class RenderSourceFileHandler extends AbstractLegacyDataSourceHandler<Col
|
|||||||
return lines.toArray(new String[0]);
|
return lines.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompletableFuture<Void> updateDataSourceAsync(NewFullDataSource inputDataSource)
|
|
||||||
{
|
|
||||||
this.updateDataSourceAtPos(inputDataSource.getSectionPos(), inputDataSource);
|
|
||||||
return CompletableFuture.completedFuture(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//=====================//
|
//=====================//
|
||||||
|
|||||||
+4
@@ -45,6 +45,10 @@ public interface IWorldGenerationQueue extends Closeable
|
|||||||
int getWaitingTaskCount();
|
int getWaitingTaskCount();
|
||||||
int getInProgressTaskCount();
|
int getInProgressTaskCount();
|
||||||
|
|
||||||
|
/** used for rendering to the F3 menu */
|
||||||
|
int getEstimatedTotalTaskCount();
|
||||||
|
void setEstimatedTotalTaskCount(int newEstimate);
|
||||||
|
|
||||||
CompletableFuture<Void> startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning);
|
CompletableFuture<Void> startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning);
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
|
|||||||
+17
-8
@@ -67,13 +67,8 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender
|
|||||||
|
|
||||||
/** largest numerical detail level allowed */
|
/** largest numerical detail level allowed */
|
||||||
public final byte lowestDataDetail;
|
public final byte lowestDataDetail;
|
||||||
@Override
|
|
||||||
public byte lowestDataDetail() { return this.lowestDataDetail; }
|
|
||||||
|
|
||||||
/** smallest numerical detail level allowed */
|
/** smallest numerical detail level allowed */
|
||||||
public final byte highestDataDetail;
|
public final byte highestDataDetail;
|
||||||
@Override
|
|
||||||
public byte highestDataDetail() { return this.highestDataDetail; }
|
|
||||||
|
|
||||||
|
|
||||||
/** If not null this generator is in the process of shutting down */
|
/** If not null this generator is in the process of shutting down */
|
||||||
@@ -96,6 +91,9 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender
|
|||||||
private final HashMap<DhSectionPos, StackTraceElement[]> alreadyGeneratedPosHashSet = new HashMap<>(MAX_ALREADY_GENERATED_COUNT);
|
private final HashMap<DhSectionPos, StackTraceElement[]> alreadyGeneratedPosHashSet = new HashMap<>(MAX_ALREADY_GENERATED_COUNT);
|
||||||
private final Queue<DhSectionPos> alreadyGeneratedPosQueue = new LinkedList<>();
|
private final Queue<DhSectionPos> alreadyGeneratedPosQueue = new LinkedList<>();
|
||||||
|
|
||||||
|
/** just used for rendering to the F3 menu */
|
||||||
|
private int estimatedTotalTaskCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
//==============//
|
||||||
@@ -498,13 +496,24 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=========//
|
//===================//
|
||||||
// getters //
|
// getters / setters //
|
||||||
//=========//
|
//===================//
|
||||||
|
|
||||||
public int getWaitingTaskCount() { return this.waitingTasks.size(); }
|
public int getWaitingTaskCount() { return this.waitingTasks.size(); }
|
||||||
public int getInProgressTaskCount() { return this.inProgressGenTasksByLodPos.size(); }
|
public int getInProgressTaskCount() { return this.inProgressGenTasksByLodPos.size(); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte lowestDataDetail() { return this.lowestDataDetail; }
|
||||||
|
@Override
|
||||||
|
public byte highestDataDetail() { return this.highestDataDetail; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getEstimatedTotalTaskCount() { return this.estimatedTotalTaskCount; }
|
||||||
|
@Override
|
||||||
|
public void setEstimatedTotalTaskCount(int newEstimate) { this.estimatedTotalTaskCount = newEstimate; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==========//
|
//==========//
|
||||||
// shutdown //
|
// shutdown //
|
||||||
|
|||||||
@@ -316,7 +316,7 @@ public class ClientLevelModule implements Closeable, AbstractNewDataSourceHandle
|
|||||||
this.quadtree = new LodQuadTree(dhClientLevel, Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH * 2,
|
this.quadtree = new LodQuadTree(dhClientLevel, Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH * 2,
|
||||||
// initial position is (0,0) just in case the player hasn't loaded in yet, the tree will be moved once the level starts ticking
|
// initial position is (0,0) just in case the player hasn't loaded in yet, the tree will be moved once the level starts ticking
|
||||||
0, 0,
|
0, 0,
|
||||||
this.renderSourceFileHandler);
|
this.renderSourceFileHandler.fullDataSourceProvider, this.renderSourceFileHandler);
|
||||||
|
|
||||||
RenderBufferHandler renderBufferHandler = new RenderBufferHandler(this.quadtree);
|
RenderBufferHandler renderBufferHandler = new RenderBufferHandler(this.quadtree);
|
||||||
this.renderer = new LodRenderer(renderBufferHandler);
|
this.renderer = new LodRenderer(renderBufferHandler);
|
||||||
|
|||||||
@@ -53,8 +53,9 @@ public class WorldGenModule implements Closeable
|
|||||||
{
|
{
|
||||||
int waitingCount = worldGenState.worldGenerationQueue.getWaitingTaskCount();
|
int waitingCount = worldGenState.worldGenerationQueue.getWaitingTaskCount();
|
||||||
int inProgressCount = worldGenState.worldGenerationQueue.getInProgressTaskCount();
|
int inProgressCount = worldGenState.worldGenerationQueue.getInProgressTaskCount();
|
||||||
|
int totalCountEstimate = worldGenState.worldGenerationQueue.getEstimatedTotalTaskCount();
|
||||||
|
|
||||||
return "World Gen Tasks: "+waitingCount+", (in progress: "+inProgressCount+")";
|
return "World Gen Tasks: "+waitingCount+"/"+totalCountEstimate+", (in progress: "+inProgressCount+")";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -224,6 +224,12 @@ public class DhSectionPos
|
|||||||
return (centerBlockPos * BitShiftUtil.powerOfTwo(this.detailLevel)) + positionOffset;
|
return (centerBlockPos * BitShiftUtil.powerOfTwo(this.detailLevel)) + positionOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getManhattanBlockDistance(DhBlockPos2D blockPos)
|
||||||
|
{
|
||||||
|
return Math.abs(this.getCenterBlockPosX() - blockPos.x)
|
||||||
|
+ Math.abs(this.getCenterBlockPosZ() - blockPos.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================//
|
//==================//
|
||||||
|
|||||||
@@ -23,20 +23,25 @@ import com.seibel.distanthorizons.api.enums.config.EHorizontalQuality;
|
|||||||
import com.seibel.distanthorizons.core.config.Config;
|
import com.seibel.distanthorizons.core.config.Config;
|
||||||
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
|
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
|
||||||
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
|
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
|
||||||
|
import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider;
|
||||||
import com.seibel.distanthorizons.core.file.renderfile.IRenderSourceProvider;
|
import com.seibel.distanthorizons.core.file.renderfile.IRenderSourceProvider;
|
||||||
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
|
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
|
||||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
|
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||||
import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode;
|
import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode;
|
||||||
import com.seibel.distanthorizons.core.util.objects.quadTree.QuadTree;
|
import com.seibel.distanthorizons.core.util.objects.quadTree.QuadTree;
|
||||||
import com.seibel.distanthorizons.coreapi.util.MathUtil;
|
import com.seibel.distanthorizons.coreapi.util.MathUtil;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,8 +53,13 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
|||||||
public static final byte TREE_LOWEST_DETAIL_LEVEL = ColumnRenderSource.SECTION_SIZE_OFFSET;
|
public static final byte TREE_LOWEST_DETAIL_LEVEL = ColumnRenderSource.SECTION_SIZE_OFFSET;
|
||||||
|
|
||||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||||
|
/** there should only ever be one {@link LodQuadTree} so having the thread static should be fine */
|
||||||
|
private static final ThreadPoolExecutor FULL_DATA_RETRIEVAL_QUEUE_THREAD = ThreadUtil.makeSingleThreadPool("QuadTree Full Data Retrieval Queue Populator");
|
||||||
|
private static final int WORLD_GEN_QUEUE_UPDATE_DELAY_IN_MS = 1_000;
|
||||||
|
|
||||||
|
|
||||||
public final int blockRenderDistanceDiameter;
|
public final int blockRenderDistanceDiameter;
|
||||||
|
private final IFullDataSourceProvider fullDataSourceProvider;
|
||||||
private final IRenderSourceProvider renderSourceProvider;
|
private final IRenderSourceProvider renderSourceProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -60,6 +70,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
|||||||
private final IDhClientLevel level; //FIXME: Proper hierarchy to remove this reference!
|
private final IDhClientLevel level; //FIXME: Proper hierarchy to remove this reference!
|
||||||
private final ConfigChangeListener<EHorizontalQuality> horizontalScaleChangeListener;
|
private final ConfigChangeListener<EHorizontalQuality> horizontalScaleChangeListener;
|
||||||
private final ReentrantLock treeReadWriteLock = new ReentrantLock();
|
private final ReentrantLock treeReadWriteLock = new ReentrantLock();
|
||||||
|
private final AtomicBoolean fullDataRetrievalQueueRunning = new AtomicBoolean(false);
|
||||||
|
|
||||||
/** the smallest numerical detail level number that can be rendered */
|
/** the smallest numerical detail level number that can be rendered */
|
||||||
private byte maxRenderDetailLevel;
|
private byte maxRenderDetailLevel;
|
||||||
@@ -80,12 +91,14 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
|||||||
public LodQuadTree(
|
public LodQuadTree(
|
||||||
IDhClientLevel level, int viewDiameterInBlocks,
|
IDhClientLevel level, int viewDiameterInBlocks,
|
||||||
int initialPlayerBlockX, int initialPlayerBlockZ,
|
int initialPlayerBlockX, int initialPlayerBlockZ,
|
||||||
IRenderSourceProvider provider)
|
IFullDataSourceProvider fullDataSourceProvider,
|
||||||
|
IRenderSourceProvider renderSourceProvider)
|
||||||
{
|
{
|
||||||
super(viewDiameterInBlocks, new DhBlockPos2D(initialPlayerBlockX, initialPlayerBlockZ), TREE_LOWEST_DETAIL_LEVEL);
|
super(viewDiameterInBlocks, new DhBlockPos2D(initialPlayerBlockX, initialPlayerBlockZ), TREE_LOWEST_DETAIL_LEVEL);
|
||||||
|
|
||||||
this.level = level;
|
this.level = level;
|
||||||
this.renderSourceProvider = provider;
|
this.fullDataSourceProvider = fullDataSourceProvider;
|
||||||
|
this.renderSourceProvider = renderSourceProvider;
|
||||||
this.blockRenderDistanceDiameter = viewDiameterInBlocks;
|
this.blockRenderDistanceDiameter = viewDiameterInBlocks;
|
||||||
|
|
||||||
this.horizontalScaleChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.horizontalQuality, (newHorizontalScale) -> this.onHorizontalQualityChange());
|
this.horizontalScaleChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.horizontalQuality, (newHorizontalScale) -> this.onHorizontalQualityChange());
|
||||||
@@ -163,6 +176,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
|||||||
|
|
||||||
|
|
||||||
// walk through each root node
|
// walk through each root node
|
||||||
|
ArrayList<LodRenderSection> nodesNeedingRetrieval = new ArrayList<>();
|
||||||
Iterator<DhSectionPos> rootPosIterator = this.rootNodePosIterator();
|
Iterator<DhSectionPos> rootPosIterator = this.rootNodePosIterator();
|
||||||
while (rootPosIterator.hasNext())
|
while (rootPosIterator.hasNext())
|
||||||
{
|
{
|
||||||
@@ -174,14 +188,23 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
|||||||
}
|
}
|
||||||
|
|
||||||
QuadNode<LodRenderSection> rootNode = this.getNode(rootPos);
|
QuadNode<LodRenderSection> rootNode = this.getNode(rootPos);
|
||||||
this.recursivelyUpdateRenderSectionNode(playerPos, rootNode, rootNode, rootNode.sectionPos, false);
|
this.recursivelyUpdateRenderSectionNode(playerPos, rootNode, rootNode, rootNode.sectionPos, false, nodesNeedingRetrieval);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// full data retrieval (world gen)
|
||||||
|
if (!this.fullDataRetrievalQueueRunning.get())
|
||||||
|
{
|
||||||
|
this.fullDataRetrievalQueueRunning.set(true);
|
||||||
|
FULL_DATA_RETRIEVAL_QUEUE_THREAD.execute(() -> this.queueFullDataRetrievalTasks(playerPos, nodesNeedingRetrieval));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/** @return whether the current position is able to render (note: not if it IS rendering, just if it is ABLE to.) */
|
/** @return whether the current position is able to render (note: not if it IS rendering, just if it is ABLE to.) */
|
||||||
private boolean recursivelyUpdateRenderSectionNode(
|
private boolean recursivelyUpdateRenderSectionNode(
|
||||||
DhBlockPos2D playerPos,
|
DhBlockPos2D playerPos,
|
||||||
QuadNode<LodRenderSection> rootNode, QuadNode<LodRenderSection> quadNode, DhSectionPos sectionPos,
|
QuadNode<LodRenderSection> rootNode, QuadNode<LodRenderSection> quadNode, DhSectionPos sectionPos,
|
||||||
boolean parentRenderSectionIsEnabled)
|
boolean parentRenderSectionIsEnabled,
|
||||||
|
ArrayList<LodRenderSection> nodesNeedingRetrieval)
|
||||||
{
|
{
|
||||||
//===============================//
|
//===============================//
|
||||||
// node and render section setup //
|
// node and render section setup //
|
||||||
@@ -237,7 +260,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
|||||||
DhSectionPos childPos = childPosIterator.next();
|
DhSectionPos childPos = childPosIterator.next();
|
||||||
QuadNode<LodRenderSection> childNode = rootNode.getNode(childPos);
|
QuadNode<LodRenderSection> childNode = rootNode.getNode(childPos);
|
||||||
|
|
||||||
boolean childSectionLoaded = this.recursivelyUpdateRenderSectionNode(playerPos, rootNode, childNode, childPos, canThisPosRender || parentRenderSectionIsEnabled);
|
boolean childSectionLoaded = this.recursivelyUpdateRenderSectionNode(playerPos, rootNode, childNode, childPos, canThisPosRender || parentRenderSectionIsEnabled, nodesNeedingRetrieval);
|
||||||
allChildrenSectionsAreLoaded = childSectionLoaded && allChildrenSectionsAreLoaded;
|
allChildrenSectionsAreLoaded = childSectionLoaded && allChildrenSectionsAreLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,7 +282,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
|||||||
DhSectionPos childPos = childPosIterator.next();
|
DhSectionPos childPos = childPosIterator.next();
|
||||||
QuadNode<LodRenderSection> childNode = rootNode.getNode(childPos);
|
QuadNode<LodRenderSection> childNode = rootNode.getNode(childPos);
|
||||||
|
|
||||||
boolean childSectionLoaded = this.recursivelyUpdateRenderSectionNode(playerPos, rootNode, childNode, childPos, parentRenderSectionIsEnabled);
|
boolean childSectionLoaded = this.recursivelyUpdateRenderSectionNode(playerPos, rootNode, childNode, childPos, parentRenderSectionIsEnabled, nodesNeedingRetrieval);
|
||||||
allChildrenSectionsAreLoaded = childSectionLoaded && allChildrenSectionsAreLoaded;
|
allChildrenSectionsAreLoaded = childSectionLoaded && allChildrenSectionsAreLoaded;
|
||||||
}
|
}
|
||||||
if (!allChildrenSectionsAreLoaded)
|
if (!allChildrenSectionsAreLoaded)
|
||||||
@@ -276,6 +299,9 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
|||||||
// TODO this should only equal the expected detail level, the (expectedDetailLevel-1) is a temporary fix to prevent corners from being cut out
|
// TODO this should only equal the expected detail level, the (expectedDetailLevel-1) is a temporary fix to prevent corners from being cut out
|
||||||
else if (sectionPos.getDetailLevel() == expectedDetailLevel || sectionPos.getDetailLevel() == expectedDetailLevel - 1)
|
else if (sectionPos.getDetailLevel() == expectedDetailLevel || sectionPos.getDetailLevel() == expectedDetailLevel - 1)
|
||||||
{
|
{
|
||||||
|
// this is the detail level we want to render //
|
||||||
|
|
||||||
|
|
||||||
/* Can be uncommented to easily debug a single render section. */
|
/* Can be uncommented to easily debug a single render section. */
|
||||||
/* Don't forget the disableRendering() at the bottom though. */
|
/* Don't forget the disableRendering() at the bottom though. */
|
||||||
//if (sectionPos.getDetailLevel() == 10
|
//if (sectionPos.getDetailLevel() == 10
|
||||||
@@ -285,9 +311,9 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
|||||||
// sectionPos.getZ() == -4
|
// sectionPos.getZ() == -4
|
||||||
// ))
|
// ))
|
||||||
{
|
{
|
||||||
// this is the detail level we want to render //
|
|
||||||
// prepare this section for rendering
|
// prepare this section for rendering
|
||||||
renderSection.loadRenderSource(this.renderSourceProvider, this.level); // TODO this should fire for the lowest detail level first, wait for it to finish then fire the next highest to prevent waiting forever for 2 million chunk section to finish sampling everything
|
// TODO this should fire for the lowest detail level first to improve loading speed
|
||||||
|
renderSection.loadRenderSource(this.renderSourceProvider, this.level);
|
||||||
|
|
||||||
// wait for the parent to disable before enabling this section, so we don't overdraw/overlap render sections
|
// wait for the parent to disable before enabling this section, so we don't overdraw/overlap render sections
|
||||||
if (!parentRenderSectionIsEnabled && renderSection.canRenderNow())
|
if (!parentRenderSectionIsEnabled && renderSection.canRenderNow())
|
||||||
@@ -308,6 +334,11 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!renderSection.isFullyGenerated())
|
||||||
|
{
|
||||||
|
nodesNeedingRetrieval.add(renderSection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//else
|
//else
|
||||||
//{
|
//{
|
||||||
@@ -400,7 +431,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
|||||||
*/
|
*/
|
||||||
public void clearRenderDataCache()
|
public void clearRenderDataCache()
|
||||||
{
|
{
|
||||||
if (this.treeReadWriteLock.tryLock())
|
if (this.treeReadWriteLock.tryLock()) // TODO make async, can lock render thread
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -451,14 +482,70 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=================================//
|
||||||
|
// full data retrieval (world gen) //
|
||||||
|
//=================================//
|
||||||
|
|
||||||
|
private void queueFullDataRetrievalTasks(DhBlockPos2D playerPos, ArrayList<LodRenderSection> nodesNeedingRetrieval)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// add a slight delay since we don't need to check the world gen queue every tick
|
||||||
|
Thread.sleep(WORLD_GEN_QUEUE_UPDATE_DELAY_IN_MS);
|
||||||
|
|
||||||
|
// sort the nodes from nearest to farthest
|
||||||
|
nodesNeedingRetrieval.sort((a, b) ->
|
||||||
|
{
|
||||||
|
int aDist = a.pos.getManhattanBlockDistance(playerPos);
|
||||||
|
int bDist = b.pos.getManhattanBlockDistance(playerPos);
|
||||||
|
return Integer.compare(aDist, bDist);
|
||||||
|
});
|
||||||
|
|
||||||
|
// add retrieval tasks to the queue
|
||||||
|
for (int i = 0; i < nodesNeedingRetrieval.size(); i++)
|
||||||
|
{
|
||||||
|
LodRenderSection renderSection = nodesNeedingRetrieval.get(i);
|
||||||
|
if (!this.fullDataSourceProvider.canQueueRetrieval())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSection.tryQueuingMissingLodRetrieval(this.fullDataSourceProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate an estimate for the max number of tasks for the queue
|
||||||
|
int totalWorldGenCount = 0;
|
||||||
|
for (int i = 0; i < nodesNeedingRetrieval.size(); i++)
|
||||||
|
{
|
||||||
|
LodRenderSection renderSection = nodesNeedingRetrieval.get(i);
|
||||||
|
if (!renderSection.missingPositionsCalculated())
|
||||||
|
{
|
||||||
|
// may be higher than the actual amount
|
||||||
|
totalWorldGenCount += this.fullDataSourceProvider.getMaxPossibleRetrievalPositionCountForPos(renderSection.pos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
totalWorldGenCount += renderSection.ungeneratedPositionCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.fullDataSourceProvider.setTotalRetrievalPositionCount(totalWorldGenCount);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LOGGER.error("Unexpected error: "+e.getMessage(), e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
this.fullDataRetrievalQueueRunning.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//==================//
|
//==================//
|
||||||
// config listeners //
|
// config listeners //
|
||||||
//==================//
|
//==================//
|
||||||
|
|
||||||
private void onHorizontalQualityChange()
|
private void onHorizontalQualityChange() { this.clearRenderDataCache(); }
|
||||||
{
|
|
||||||
this.clearRenderDataCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import com.seibel.distanthorizons.core.config.Config;
|
|||||||
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
|
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
|
||||||
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.ColumnRenderBufferBuilder;
|
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.ColumnRenderBufferBuilder;
|
||||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
||||||
|
import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider;
|
||||||
import com.seibel.distanthorizons.core.file.renderfile.IRenderSourceProvider;
|
import com.seibel.distanthorizons.core.file.renderfile.IRenderSourceProvider;
|
||||||
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
@@ -36,6 +37,7 @@ import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
@@ -84,6 +86,10 @@ public class LodRenderSection implements IDebugRenderable
|
|||||||
|
|
||||||
private final QuadTree<LodRenderSection> parentQuadTree;
|
private final QuadTree<LodRenderSection> parentQuadTree;
|
||||||
|
|
||||||
|
private boolean missingPositionsCalculated = false;
|
||||||
|
/** should be an empty array if no positions need to be generated */
|
||||||
|
private ArrayList<DhSectionPos> missingGenerationPos = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
//=============//
|
||||||
@@ -407,6 +413,56 @@ public class LodRenderSection implements IDebugRenderable
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=================================//
|
||||||
|
// full data retrieval (world gen) //
|
||||||
|
//=================================//
|
||||||
|
|
||||||
|
public boolean isFullyGenerated() { return this.missingPositionsCalculated && this.missingGenerationPos.size() == 0; }
|
||||||
|
public boolean missingPositionsCalculated() { return this.missingPositionsCalculated; }
|
||||||
|
public int ungeneratedPositionCount() { return (this.missingGenerationPos != null) ? this.missingGenerationPos.size() : 0; }
|
||||||
|
|
||||||
|
public void tryQueuingMissingLodRetrieval(IFullDataSourceProvider fullDataSourceProvider)
|
||||||
|
{
|
||||||
|
if (fullDataSourceProvider.canRetrieveMissingDataSources() && fullDataSourceProvider.canQueueRetrieval())
|
||||||
|
{
|
||||||
|
// calculate the missing positions if not already done
|
||||||
|
if (!this.missingPositionsCalculated)
|
||||||
|
{
|
||||||
|
this.missingGenerationPos = fullDataSourceProvider.getPositionsToRetrieve(this.pos);
|
||||||
|
if (this.missingGenerationPos != null)
|
||||||
|
{
|
||||||
|
this.missingPositionsCalculated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the missing positions were found, queue them
|
||||||
|
if (this.missingGenerationPos != null)
|
||||||
|
{
|
||||||
|
// queue from last to first to prevent shifting the array unnecessarily
|
||||||
|
for (int i = this.missingGenerationPos.size() - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (!fullDataSourceProvider.canQueueRetrieval())
|
||||||
|
{
|
||||||
|
// the data source provider isn't accepting any more jobs
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
DhSectionPos pos = this.missingGenerationPos.remove(i);
|
||||||
|
boolean positionQueued = fullDataSourceProvider.queuePositionForRetrieval(pos);
|
||||||
|
if (!positionQueued)
|
||||||
|
{
|
||||||
|
// shouldn't normally happen, but just in case
|
||||||
|
this.missingGenerationPos.add(pos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
//==============//
|
||||||
// base methods //
|
// base methods //
|
||||||
//==============//
|
//==============//
|
||||||
|
|||||||
+20
@@ -209,6 +209,26 @@ public class NewFullDataSourceRepo extends AbstractDhRepo<DhSectionPos, NewFullD
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return null if nothing exists for this position */
|
||||||
|
public byte[] getColumnGenerationStepForPos(DhSectionPos pos)
|
||||||
|
{
|
||||||
|
int detailLevel = pos.getDetailLevel() - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||||
|
|
||||||
|
Map<String, Object> resultMap = this.queryDictionaryFirst(
|
||||||
|
"select ColumnGenerationStep " +
|
||||||
|
"from "+this.getTableName()+" " +
|
||||||
|
"WHERE DetailLevel = "+detailLevel+" AND PosX = "+pos.getX()+" AND PosZ = "+pos.getZ());
|
||||||
|
|
||||||
|
if (resultMap != null)
|
||||||
|
{
|
||||||
|
return (byte[]) resultMap.get("ColumnGenerationStep");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user