start world gen refactoring

This commit is contained in:
James Seibel
2025-12-17 22:39:23 -06:00
parent b1b0642fbe
commit 6d98c9cb84
9 changed files with 135 additions and 116 deletions
@@ -207,10 +207,10 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
}
@Override
public boolean canQueueRetrieval() { return this.canQueueRetrieval(false); }
public boolean canQueueRetrieval(boolean pruneWaitingTasksAboveLimit)
public boolean canQueueRetrievalNow() { return this.canQueueRetrievalNow(false); }
public boolean canQueueRetrievalNow(boolean pruneWaitingTasksAboveLimit)
{
if (!super.canQueueRetrieval())
if (!super.canQueueRetrievalNow())
{
return false;
}
@@ -275,7 +275,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
if (pruneWaitingTasksAboveLimit)
{
AtomicInteger tasksToCancel = new AtomicInteger(-availableTaskSlots + 1);
worldGenQueue.removeRetrievalRequestIf(x -> tasksToCancel.getAndDecrement() > 0);
worldGenQueue.removeRetrievalRequestIf(taskPos -> tasksToCancel.getAndDecrement() > 0);
}
else
{
@@ -382,7 +382,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
LongArrayList generationList = new LongArrayList();
byte lowestGeneratorDetailLevel = (byte) Math.min(
worldGenQueue.lowestDataDetail() + DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL,
worldGenQueue.lowestDataDetail() + DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL,
DhSectionPos.getDetailLevel(pos));
DhSectionPos.forEachChildAtDetailLevel(pos, lowestGeneratorDetailLevel, (genPos) ->
@@ -74,7 +74,7 @@ public class RemoteFullDataSourceProvider extends GeneratedFullDataSourceProvide
//==================//
@Override
public boolean canQueueRetrieval() { return this.canQueueRetrieval(true); }
public boolean canQueueRetrievalNow() { return this.canQueueRetrievalNow(true); }
@Override
@Nullable
@@ -48,7 +48,6 @@ import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
/**
* Handles reading/writing {@link FullDataSourceV2}
@@ -360,7 +359,7 @@ public class FullDataSourceProviderV2 implements IDebugRenderable, AutoCloseable
* to the beginning of your override.
* Otherwise, parent retrieval limits will be ignored.
*/
public boolean canQueueRetrieval()
public boolean canQueueRetrievalNow()
{
// Retrieval shouldn't happen while an unknown number of
// legacy data sources are present.
@@ -369,13 +368,13 @@ public class FullDataSourceProviderV2 implements IDebugRenderable, AutoCloseable
}
/**
* @return null if this provider can't generate any positions and
* @return null if this provider can't generate any positions or
* an empty array if all positions were generated
*/
@Nullable
public LongArrayList getPositionsToRetrieve(Long pos) { return null; }
/** @return true if the position was queued, false if not */
/** @return null if the position couldn't be queued */
@Nullable
public CompletableFuture<WorldGenResult> queuePositionForRetrieval(Long genPos) { return null; }
@@ -12,6 +12,7 @@ import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.WorldGenUtil;
import com.seibel.distanthorizons.core.util.objects.Pair;
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
import com.seibel.distanthorizons.core.logging.DhLogger;
@@ -59,41 +60,43 @@ public class RemoteWorldRetrievalQueue extends AbstractFullDataNetworkRequestQue
long generationStartMsTime = System.currentTimeMillis();
return super.submitRequest(sectionPos, fullDataSource -> {
Objects.requireNonNull(tracker.getDataSourceConsumer()).accept(fullDataSource);
fullDataSource.close();
})
.thenApply(requestResult ->
return super.submitRequest(sectionPos, /* client timestamp */null,
(fullDataSource) ->
{
tracker.getDataSourceConsumer().accept(fullDataSource);
fullDataSource.close(); // TODO can cause issues if the above is async
})
.thenApply((ERequestResult requestResult) ->
{
long totalGenTimeInMs = System.currentTimeMillis() - generationStartMsTime;
int chunkWidth = DhSectionPos.getChunkWidth(sectionPos);
int chunkCount = chunkWidth * chunkWidth;
double timePerChunk = (double) totalGenTimeInMs / (double) chunkCount;
this.rollingAverageChunkGenTimeInMs.add(timePerChunk);
switch (requestResult)
{
long totalGenTimeInMs = System.currentTimeMillis() - generationStartMsTime;
int chunkWidth = DhSectionPos.getChunkWidth(sectionPos);
int chunkCount = chunkWidth * chunkWidth;
double timePerChunk = (double)totalGenTimeInMs / (double)chunkCount;
this.rollingAverageChunkGenTimeInMs.add(timePerChunk);
switch (requestResult)
{
case SUCCEEDED:
return WorldGenResult.CreateSuccess(sectionPos);
case FAILED:
return WorldGenResult.CreateFail();
case REQUIRES_SPLITTING:
List<CompletableFuture<WorldGenResult>> childFutures = new ArrayList<>(4);
DhSectionPos.forEachChild(sectionPos, childPos -> {
tracker.shouldGenerateSplitChild(childPos).thenAccept(shouldGenerate -> {
if (shouldGenerate)
{
childFutures.add(this.submitRetrievalTask(childPos, requiredDataDetail, tracker));
}
});
case SUCCEEDED:
return WorldGenResult.CreateSuccess(sectionPos);
case FAILED:
return WorldGenResult.CreateFail();
case REQUIRES_SPLITTING:
List<CompletableFuture<WorldGenResult>> childFutures = new ArrayList<>(4);
DhSectionPos.forEachChild(sectionPos, childPos -> {
tracker.shouldGenerateSplitChild(childPos).thenAccept(shouldGenerate -> {
if (shouldGenerate)
{
childFutures.add(this.submitRetrievalTask(childPos, requiredDataDetail, tracker));
}
});
return WorldGenResult.CreateSplit(childFutures);
}
LodUtil.assertNotReach("Unexpected and unhandled request response result: ["+requestResult+"]");
return WorldGenResult.CreateFail();
});
});
return WorldGenResult.CreateSplit(childFutures);
}
LodUtil.assertNotReach("Unexpected and unhandled request response result: [" + requestResult + "]");
return WorldGenResult.CreateFail();
});
}
@Override
@@ -161,7 +161,12 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
{
if (removeIf.accept(genPos))
{
this.waitingTasks.remove(genPos);
WorldGenTask removedTask = this.waitingTasks.remove(genPos);
if (removedTask != null)
{
// cancel tasks so any waiting future steps can be triggered
removedTask.future.cancel(true);
}
}
});
}
@@ -556,7 +561,8 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
// shutdown //
//==========//
@Override public CompletableFuture<Void> startClosingAsync(boolean cancelCurrentGeneration, boolean alsoInterruptRunning)
@Override
public CompletableFuture<Void> startClosingAsync(boolean cancelCurrentGeneration, boolean alsoInterruptRunning)
{
LOGGER.info("Closing world gen queue");
this.queueingThread.shutdownNow();
@@ -31,7 +31,6 @@ import java.util.function.Consumer;
*/
public interface IWorldGenTaskTracker
{
@Nullable
Consumer<FullDataSourceV2> getDataSourceConsumer();
CompletableFuture<Boolean> shouldGenerateSplitChild(long pos);
@@ -33,7 +33,6 @@ import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
@@ -119,8 +118,6 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
// request submitting //
//====================//
public CompletableFuture<ERequestResult> submitRequest(long sectionPos, Consumer<FullDataSourceV2> dataSourceConsumer)
{ return this.submitRequest(sectionPos, null, dataSourceConsumer); }
public CompletableFuture<ERequestResult> submitRequest(long sectionPos, @Nullable Long clientTimestamp, Consumer<FullDataSourceV2> dataSourceConsumer)
{
if (this.succeededPositions.contains(sectionPos))
@@ -133,14 +130,15 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
return CompletableFuture.completedFuture(ERequestResult.REQUIRES_SPLITTING);
}
AtomicBoolean added = new AtomicBoolean(false);
RequestQueueEntry entry = this.waitingTasksBySectionPos.compute(sectionPos, (pos, existingQueueEntry) ->
{
// ignore already queued tasks
if (existingQueueEntry != null)
{
return existingQueueEntry;
}
RequestQueueEntry newEntry = new RequestQueueEntry(dataSourceConsumer, clientTimestamp);
newEntry.future.whenComplete((requestResult, throwable) ->
{
@@ -167,15 +165,9 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
}
});
added.set(true);
return newEntry;
});
if (!added.get())
{
return CompletableFuture.completedFuture(ERequestResult.FAILED);
}
return entry.future;
}
@@ -221,31 +213,31 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
}
long sectionPos = mapEntry.getKey();
RequestQueueEntry entry = mapEntry.getValue();
RequestQueueEntry requestEntry = mapEntry.getValue();
if (!this.isSectionAllowedToGenerate(sectionPos, targetPos))
{
entry.future.cancel(false);
requestEntry.future.cancel(false);
this.pendingTasksSemaphore.release();
return;
}
if (!this.onBeforeRequest(sectionPos, entry.future))
if (!this.onBeforeRequest(sectionPos, requestEntry.future))
{
this.pendingTasksSemaphore.release();
return;
}
Long offsetEntryTimestamp = entry.updateTimestamp != null
? entry.updateTimestamp + this.networkState.getServerTimeOffset()
Long offsetEntryTimestamp = requestEntry.updateTimestamp != null
? requestEntry.updateTimestamp + this.networkState.getServerTimeOffset()
: null;
CompletableFuture<FullDataSourceResponseMessage> dataSourceFuture = this.networkState.getSession().sendRequest(
CompletableFuture<FullDataSourceResponseMessage> dataSourceNetworkFuture = this.networkState.getSession().sendRequest(
new FullDataSourceRequestMessage(this.level.getLevelWrapper(), sectionPos, offsetEntryTimestamp),
FullDataSourceResponseMessage.class
);
entry.networkDataSourceFuture = dataSourceFuture;
dataSourceFuture.handle((response, throwable) ->
requestEntry.networkDataSourceFuture = dataSourceNetworkFuture;
dataSourceNetworkFuture.handle((response, throwable) ->
{
this.pendingTasksSemaphore.release();
@@ -265,6 +257,7 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
dataSourceDto.applyToChildren = DhSectionPos.getDetailLevel(dataSourceDto.pos) > DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL;
dataSourceDto.applyToParent = DhSectionPos.getDetailLevel(dataSourceDto.pos) < DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL + 12;
// TODO what thread is this currently running on? Does saving need to be run async?
AbstractExecutorService executor = ThreadPoolUtil.getNetworkCompressionExecutor();
if (executor == null)
{
@@ -280,7 +273,7 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
this.level.updateBeaconBeamsForSectionPos(dataSourceDto.pos, response.payload.beaconBeams);
FullDataSourceV2 fullDataSource = dataSourceDto.createDataSource(this.level.getLevelWrapper(), null);
entry.dataSourceConsumer.accept(fullDataSource);
requestEntry.dataSourceConsumer.accept(fullDataSource);
}
catch (Exception e)
{
@@ -299,16 +292,16 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
}
catch (SectionRequiresSplittingException ignored)
{
return entry.future.complete(ERequestResult.REQUIRES_SPLITTING);
return requestEntry.future.complete(ERequestResult.REQUIRES_SPLITTING);
}
catch (SessionClosedException | CancellationException ignored)
{
return entry.future.cancel(false);
return requestEntry.future.cancel(false);
}
catch (RequestRejectedException e)
{
LOGGER.info("Request rejected by the server: " + e.getMessage());
return entry.future.complete(ERequestResult.FAILED);
return requestEntry.future.complete(ERequestResult.FAILED);
}
catch (RateLimitedException e)
{
@@ -317,34 +310,34 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
// Skip all requests for 1 second
this.rateLimiter.acquireAll();
entry.networkDataSourceFuture = null;
requestEntry.networkDataSourceFuture = null;
return null;
}
catch (RequestOutOfRangeException e)
{
LOGGER.debug("Out of range, re-queueing task [" + DhSectionPos.toString(sectionPos) + "]: " + e.getMessage());
entry.networkDataSourceFuture = null;
requestEntry.networkDataSourceFuture = null;
return null;
}
catch (Throwable e)
{
entry.retryAttempts--;
LOGGER.error("Error while fetching full data source, attempts left: {} / {}", entry.retryAttempts, MAX_RETRY_ATTEMPTS, e);
requestEntry.retryAttempts--;
LOGGER.error("Unexpected error ["+e.getMessage()+"] while fetching full data source, attempts left: ["+requestEntry.retryAttempts+"] / ["+MAX_RETRY_ATTEMPTS+"]", e);
// Retry logic
if (entry.retryAttempts > 0)
if (requestEntry.retryAttempts > 0)
{
entry.networkDataSourceFuture = null;
requestEntry.networkDataSourceFuture = null;
return null;
}
else
{
return entry.future.complete(ERequestResult.FAILED);
return requestEntry.future.complete(ERequestResult.FAILED);
}
}
return entry.future.complete(ERequestResult.SUCCEEDED);
return requestEntry.future.complete(ERequestResult.SUCCEEDED);
});
}
@@ -366,13 +359,11 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
if (removeIf.accept(pos))
{
LOGGER.debug("Removing request [" + mapEntry.getKey() + "]...");
entry.future.cancel(false);
if (entry.networkDataSourceFuture != null)
{
entry.networkDataSourceFuture.cancel(false);
}
entry.future.cancel(false);
}
}
}
@@ -20,7 +20,6 @@
package com.seibel.distanthorizons.core.render;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.file.fullDatafile.V2.FullDataSourceProviderV2;
@@ -34,7 +33,6 @@ import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
import com.seibel.distanthorizons.core.render.renderer.generic.BeaconRenderHandler;
import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.PerfRecorder;
import com.seibel.distanthorizons.core.util.ThreadUtil;
import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode;
import com.seibel.distanthorizons.core.util.objects.quadTree.QuadTree;
@@ -46,7 +44,6 @@ import org.jetbrains.annotations.Nullable;
import javax.annotation.WillNotClose;
import java.awt.*;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -73,7 +70,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
* This is a {@link ConcurrentLinkedQueue} because new sections can be added to this list via the world generator threads.
*/
private final ConcurrentLinkedQueue<Long> sectionsToReload = new ConcurrentLinkedQueue<>();
private final IDhClientLevel level; //FIXME: Proper hierarchy to remove this reference!
private final IDhClientLevel level;
private final ReentrantLock treeReadWriteLock = new ReentrantLock();
private final AtomicBoolean fullDataRetrievalQueueRunning = new AtomicBoolean(false);
@@ -106,9 +103,10 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
//==============//
// constructors //
//==============//
//=============//
// constructor //
//=============//
//region constructor
public LodQuadTree(
IDhClientLevel level, int viewDiameterInBlocks,
@@ -128,11 +126,14 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
}
//endregion constructor
//=============//
// tick update //
//=============//
//region tick update
/**
* This function updates the quadTree based on the playerPos and the current game configs (static and global)
@@ -143,19 +144,18 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
{
if (this.level == null)
{
// the level hasn't finished loading yet
// TODO sometimes null pointers still happen, when logging back into a world (maybe the old level isn't null but isn't valid either?)
// the quad tree was created before a level reference was created
return;
}
// this shouldn't be updated while the tree is being iterated through
this.updateDetailLevelVariables();
// don't traverse the tree if it is being modified
if (this.treeReadWriteLock.tryLock())
{
// this shouldn't be updated while the tree is being iterated through
this.updateDetailLevelVariables();
try
{
// recenter if necessary, removing out of bounds sections
@@ -210,6 +210,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
}
QuadNode<LodRenderSection> rootNode = this.getNode(rootPos);
LodUtil.assertTrue(rootNode != null, "All root nodes should have been created by this point.");
this.recursivelyUpdateRenderSectionNode(playerPos, rootNode, rootNode, rootNode.sectionPos, false, nodesNeedingRetrieval, nodesNeedingLoading);
}
@@ -217,7 +218,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
// queue full data retrieval (world gen) requests if needed
if (nodesNeedingRetrieval.size() != 0
&& !this.fullDataRetrievalQueueRunning.get()
&& this.fullDataSourceProvider.canQueueRetrieval())
&& this.fullDataSourceProvider.canQueueRetrievalNow())
{
this.fullDataRetrievalQueueRunning.set(true);
FULL_DATA_RETRIEVAL_QUEUE_THREAD.execute(() -> this.queueFullDataRetrievalTasks(playerPos, nodesNeedingRetrieval));
@@ -368,13 +369,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
// prepare this section for rendering
if (!renderSection.gpuUploadInProgress()
&& renderSection.bufferContainer == null
// TODO this is commented out since some users reported LODs refusing to
// load at their expected higher-detail levels
// this check is specifically for N-sized world generators where the higher quality
// data source may not exist yet, this is done to prevent holes while waiting for said generator
//&& renderSection.getFullDataSourceExists()
)
&& renderSection.bufferContainer == null)
{
nodesNeedingLoading.add(renderSection);
}
@@ -453,9 +448,6 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
LodRenderSection renderSection = this.getValue(pos);
if (renderSection != null)
{
// this data source may now exist
renderSection.updateFullDataSourceExists();
if (renderSection.canRender())
{
if (renderSection.gpuUploadInProgress()
@@ -495,11 +487,14 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
}
}
//endregion tick update
//====================//
// detail level logic //
//====================//
//region detail level logic
/**
* This method will compute the detail level based on player position and section pos
@@ -553,11 +548,14 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
this.minRootRenderDetailLevel = (byte) Math.max(minSectionDetailLevel, this.maxLeafRenderDetailLevel); // respect the user's selected max resolution if it is lower detail (IE they want 2x2 block, but minSectionDetailLevel is specifically for 1x1 block render resolution)
}
//endregion detail level logic
//=============//
// render data //
//=============//
//==========================//
// external render requests //
//==========================//
//region external render requests
/**
* Re-creates the color, render data.
@@ -620,29 +618,38 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
}
}
//endregion external render requests
//=================================//
// full data retrieval (world gen) //
//=================================//
//region world gen
private void queueFullDataRetrievalTasks(DhBlockPos2D playerPos, HashSet<LodRenderSection> nodesNeedingRetrieval)
{
try
{
// sort the nodes from nearest to farthest
ArrayList<LodRenderSection> nodeList = new ArrayList<>(nodesNeedingRetrieval);
nodeList.sort((a, b) ->
ArrayList<LodRenderSection> renderSectionList = new ArrayList<>(nodesNeedingRetrieval);
renderSectionList.sort((renderSectionA, renderSectionB) ->
{
int aDist = DhSectionPos.getManhattanBlockDistance(a.pos, playerPos);
int bDist = DhSectionPos.getManhattanBlockDistance(b.pos, playerPos);
int aDist = DhSectionPos.getManhattanBlockDistance(renderSectionA.pos, playerPos);
int bDist = DhSectionPos.getManhattanBlockDistance(renderSectionB.pos, playerPos);
return Integer.compare(aDist, bDist);
});
// add retrieval tasks to the queue
for (int i = 0; i < nodeList.size(); i++)
//==================================//
// add retrieval tasks to the queue //
//==================================//
for (int i = 0; i < renderSectionList.size(); i++)
{
LodRenderSection renderSection = nodeList.get(i);
if (!this.fullDataSourceProvider.canQueueRetrieval())
LodRenderSection renderSection = renderSectionList.get(i);
if (!this.fullDataSourceProvider.canQueueRetrievalNow())
{
break;
}
@@ -650,12 +657,18 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
renderSection.tryQueuingMissingLodRetrieval();
}
//==========================//
// calc task count estimate //
//==========================//
// calculate an estimate for the max number of chunks for the queue
int totalWorldGenChunkCount = 0;
int totalWorldGenTaskCount = 0;
for (int i = 0; i < nodeList.size(); i++)
for (int i = 0; i < renderSectionList.size(); i++)
{
LodRenderSection renderSection = nodeList.get(i);
LodRenderSection renderSection = renderSectionList.get(i);
if (!renderSection.missingPositionsCalculated())
{
// chunk count
@@ -688,11 +701,14 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
}
}
//endregion world gen
//===========//
// debugging //
//===========//
//region debugging
@Override
public void debugRender(DebugRenderer debugRenderer)
@@ -739,11 +755,14 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
}
}
//endregion debugging
//==============//
// base methods //
//==============//
//region base methods
@Override
public void close()
@@ -783,6 +802,8 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
LOGGER.info("Finished shutting down LodQuadTree");
}
//endregion base methods
}
@@ -42,7 +42,7 @@ import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
import com.seibel.distanthorizons.core.render.renderer.generic.BeaconRenderHandler;
import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo;
import com.seibel.distanthorizons.core.util.WorldGenUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.threading.PriorityTaskPicker;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;