Fix gen tasks sometimes not submitting after LOD level changes

This commit is contained in:
s809
2024-12-03 21:10:58 +05:00
parent 56db5d7e1a
commit 585a288f68
6 changed files with 51 additions and 32 deletions
@@ -21,7 +21,6 @@ package com.seibel.distanthorizons.core.file.fullDatafile;
import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV1; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV1;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
@@ -41,7 +40,6 @@ import com.seibel.distanthorizons.core.sql.repo.FullDataSourceV2Repo;
import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.ThreadUtil;
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException; import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.world.EWorldEnvironment;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import it.unimi.dsi.fastutil.longs.LongArrayList; import it.unimi.dsi.fastutil.longs.LongArrayList;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@@ -606,7 +604,7 @@ public class FullDataSourceProviderV2
/** @return true if the position was queued, false if not */ /** @return true if the position was queued, false if not */
@Nullable @Nullable
public CompletableFuture<WorldGenResult> queuePositionForRetrieval(Long genPos) { return null; } public CompletableFuture<WorldGenResult> queuePositionForRetrieval(long genPos, boolean allowAboveMaxGenRequests) { return null; }
/** does nothing if the given position isn't present in the queue */ /** does nothing if the given position isn't present in the queue */
public void removeRetrievalRequestIf(DhSectionPos.ICancelablePrimitiveLongConsumer removeIf) { } public void removeRetrievalRequestIf(DhSectionPos.ICancelablePrimitiveLongConsumer removeIf) { }
@@ -63,10 +63,10 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
* TODO this should be dynamically allocated based on CPU load * TODO this should be dynamically allocated based on CPU load
* and abilities. * and abilities.
*/ */
public static final int MAX_WORLD_GEN_REQUESTS_PER_THREAD = 20; public static final int MAX_WORLD_GEN_REQUESTS_PER_THREAD = 20;
private final AtomicReference<IFullDataSourceRetrievalQueue> worldGenQueueRef = new AtomicReference<>(null); protected final AtomicReference<IFullDataSourceRetrievalQueue> 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);
@@ -173,8 +173,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
} }
IFullDataSourceRetrievalQueue worldGenQueue = this.worldGenQueueRef.get(); if (this.worldGenQueueRef.get() == null)
if (worldGenQueue == null)
{ {
// we can't queue anything if the world generator isn't set up yet // we can't queue anything if the world generator isn't set up yet
return false; return false;
@@ -211,13 +210,11 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
return false; return false;
} }
return true;
// don't queue additional world gen requests beyond the max allotted count
return worldGenQueue.getWaitingTaskCount() < maxQueueCount;
} }
@Override @Override
public CompletableFuture<WorldGenResult> queuePositionForRetrieval(Long genPos) public CompletableFuture<WorldGenResult> queuePositionForRetrieval(long genPos, boolean allowAboveMaxGenRequests)
{ {
IFullDataSourceRetrievalQueue worldGenQueue = this.worldGenQueueRef.get(); IFullDataSourceRetrievalQueue worldGenQueue = this.worldGenQueueRef.get();
if (worldGenQueue == null) if (worldGenQueue == null)
@@ -225,6 +222,15 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
return null; return null;
} }
if (!allowAboveMaxGenRequests)
{
int maxQueueCount = MAX_WORLD_GEN_REQUESTS_PER_THREAD * Config.Common.MultiThreading.numberOfWorldGenerationThreads.get();
if (worldGenQueue.getWaitingTaskCount() >= maxQueueCount)
{
return null;
}
}
WorldGenTaskTracker genTaskTracker = new WorldGenTaskTracker(genPos); WorldGenTaskTracker genTaskTracker = new WorldGenTaskTracker(genPos);
CompletableFuture<WorldGenResult> worldGenFuture = worldGenQueue.submitRetrievalTask(genPos, (byte) (DhSectionPos.getDetailLevel(genPos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL), genTaskTracker); CompletableFuture<WorldGenResult> worldGenFuture = worldGenQueue.submitRetrievalTask(genPos, (byte) (DhSectionPos.getDetailLevel(genPos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL), genTaskTracker);
worldGenFuture.whenComplete((genTaskResult, ex) -> worldGenFuture.whenComplete((genTaskResult, ex) ->
@@ -20,18 +20,20 @@
package com.seibel.distanthorizons.core.file.fullDatafile; package com.seibel.distanthorizons.core.file.fullDatafile;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.file.structure.ISaveStructure; import com.seibel.distanthorizons.core.file.structure.ISaveStructure;
import com.seibel.distanthorizons.core.generation.RemoteWorldRetrievalQueue; import com.seibel.distanthorizons.core.generation.RemoteWorldRetrievalQueue;
import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult;
import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.level.WorldGenModule; import com.seibel.distanthorizons.core.level.WorldGenModule;
import com.seibel.distanthorizons.core.multiplayer.client.SyncOnLoadRequestQueue; import com.seibel.distanthorizons.core.multiplayer.client.SyncOnLoadRequestQueue;
import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/** /**
@@ -40,6 +42,8 @@ import java.util.concurrent.ConcurrentHashMap;
*/ */
public class RemoteFullDataSourceProvider extends GeneratedFullDataSourceProvider public class RemoteFullDataSourceProvider extends GeneratedFullDataSourceProvider
{ {
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
@Nullable @Nullable
private final SyncOnLoadRequestQueue syncOnLoadRequestQueue; private final SyncOnLoadRequestQueue syncOnLoadRequestQueue;
private final Set<Long> visitedPositions = ConcurrentHashMap.newKeySet(); private final Set<Long> visitedPositions = ConcurrentHashMap.newKeySet();
@@ -123,6 +127,19 @@ public class RemoteFullDataSourceProvider extends GeneratedFullDataSourceProvide
} }
@Override
public CompletableFuture<WorldGenResult> queuePositionForRetrieval(long genPos, boolean allowAboveMaxGenRequests)
{
RemoteWorldRetrievalQueue worldGenQueue = (RemoteWorldRetrievalQueue) this.worldGenQueueRef.get();
if (worldGenQueue == null)
{
return null;
}
return super.queuePositionForRetrieval(genPos, worldGenQueue.isPosCloserThanFarthestWaiting(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()), genPos));
}
//==========// //==========//
// shutdown // // shutdown //
@@ -183,7 +183,7 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
Map.Entry<Long, RequestQueueEntry> mapEntry = this.waitingTasksBySectionPos Map.Entry<Long, RequestQueueEntry> mapEntry = this.waitingTasksBySectionPos
.entrySet().stream() .entrySet().stream()
.filter(task -> task.getValue().networkDataSourceFuture == null) .filter(task -> task.getValue().networkDataSourceFuture == null)
.min(Comparator.comparingInt(x -> posDistanceSquared(targetPos, x.getKey()))) .min(Comparator.comparingInt(x -> DhSectionPos.getChebyshevSignedBlockDistance(x.getKey(), targetPos)))
.orElse(null); .orElse(null);
if (mapEntry == null) if (mapEntry == null)
@@ -305,6 +305,19 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
} }
public boolean isPosCloserThanFarthestWaiting(DhBlockPos2D targetPos, long pos)
{
Long farthestPos = this.waitingTasksBySectionPos
.keySet().stream()
.max(Comparator.comparingInt(x -> DhSectionPos.getChebyshevSignedBlockDistance(x, targetPos)))
.orElse(null);
if (farthestPos == null)
{
return true;
}
return DhSectionPos.getChebyshevSignedBlockDistance(pos, targetPos) <= DhSectionPos.getChebyshevSignedBlockDistance(farthestPos, targetPos);
}
//=========================================// //=========================================//
@@ -404,15 +417,6 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
//================//
// helper methods //
//================//
protected static int posDistanceSquared(DhBlockPos2D targetPos, long pos)
{ return (int) DhSectionPos.getCenterBlockPos(pos).distSquared(targetPos); }
//================// //================//
// helper classes // // helper classes //
//================// //================//
@@ -246,7 +246,7 @@ public class FullDataSourceRequestHandler
else else
{ {
LOGGER.info("sending - queueing [" + DhSectionPos.toString(pos) + "]"); LOGGER.info("sending - queueing [" + DhSectionPos.toString(pos) + "]");
this.fullDataSourceProvider().queuePositionForRetrieval(pos); this.fullDataSourceProvider().queuePositionForRetrieval(pos, true);
} }
}); });
} }
@@ -475,14 +475,8 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
// queue from last to first to prevent shifting the array unnecessarily // queue from last to first to prevent shifting the array unnecessarily
for (int i = this.missingGenerationPos.size() - 1; i >= 0; i--) for (int i = this.missingGenerationPos.size() - 1; i >= 0; i--)
{ {
if (!this.fullDataSourceProvider.canQueueRetrieval())
{
// the data source provider isn't accepting any more jobs
break;
}
long pos = this.missingGenerationPos.removeLong(i); long pos = this.missingGenerationPos.removeLong(i);
boolean positionQueued = (this.fullDataSourceProvider.queuePositionForRetrieval(pos) != null); boolean positionQueued = (this.fullDataSourceProvider.queuePositionForRetrieval(pos, false) != null);
if (!positionQueued) if (!positionQueued)
{ {
// shouldn't normally happen, but just in case // shouldn't normally happen, but just in case