refactor internal server world gen
This commit is contained in:
+28
-10
@@ -21,6 +21,7 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.seibel.distanthorizons.api.DhApi;
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
||||
@@ -36,7 +37,10 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
|
||||
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
|
||||
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
|
||||
import com.seibel.distanthorizons.core.util.threading.PriorityTaskPicker;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.IBatchGeneratorEnvironmentWrapper;
|
||||
@@ -46,6 +50,7 @@ import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepBiomes;
|
||||
@@ -86,6 +91,11 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
|
||||
.fileLevelConfig(Config.Common.Logging.logWorldGenEventToFile)
|
||||
.build();
|
||||
|
||||
public static final DhLogger RATE_LIMITED_LOGGER = new DhLoggerBuilder()
|
||||
.name("LOD World Gen")
|
||||
.maxCountPerSecond(1)
|
||||
.build();
|
||||
|
||||
@NotNull
|
||||
public static final ImmutableMap<EDhApiWorldGenerationStep, Integer> WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP;
|
||||
public static final int MAX_WORLD_GEN_CHUNK_BORDER_NEEDED;
|
||||
@@ -284,6 +294,14 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
|
||||
// We handle this later, although that handling would need to change if the gen size ever changes.
|
||||
LodUtil.assertTrue(genEvent.widthInChunks % 2 == 0, "Generation events are expected to be an evan number of chunks wide.");
|
||||
|
||||
if (!DhApi.isDhThread()
|
||||
&& ModInfo.IS_DEV_BUILD)
|
||||
{
|
||||
throw new IllegalStateException("Batch world generation should be called from one of DH's world gen thread. Current thread: ["+Thread.currentThread().getName()+"]");
|
||||
}
|
||||
|
||||
|
||||
|
||||
int borderSize = MAX_WORLD_GEN_CHUNK_BORDER_NEEDED;
|
||||
// genEvent.size - 1 converts the even width size to an odd number for MC compatability
|
||||
int refSize = (genEvent.widthInChunks - 1) + (borderSize * 2);
|
||||
@@ -308,7 +326,7 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
|
||||
// futures to handle getting empty chunks
|
||||
CompletableFuture<?>[] readFutures =
|
||||
// the extra radius of 8 is to account for structure references which need a chunk radius of 8
|
||||
ChunkPosGenStream.getStream(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 8)
|
||||
ChunkPosGenStream.getStream(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 8)// TODO
|
||||
.map((chunkPos) -> this.chunkFileReader.createEmptyOrPreExistingChunkAsync(chunkPos.x, chunkPos.z, chunkSkyLightingByDhPos, chunkBlockLightingByDhPos, generatedChunkByDhPos))
|
||||
.toArray(CompletableFuture[]::new);
|
||||
|
||||
@@ -433,7 +451,7 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
|
||||
// submit generated chunks //
|
||||
//=========================//
|
||||
|
||||
Iterator<ChunkPos> iterator = ChunkPosGenStream.getStream(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0).iterator();
|
||||
Iterator<ChunkPos> iterator = ChunkPosGenStream.getIterator(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0);
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
ChunkPos pos = iterator.next();
|
||||
@@ -478,7 +496,6 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
|
||||
if (chunk instanceof ProtoChunk)
|
||||
{
|
||||
ProtoChunk protoChunk = ((ProtoChunk) chunk);
|
||||
|
||||
protoChunk.setLightEngine(region.getLightEngine());
|
||||
}
|
||||
});
|
||||
@@ -525,13 +542,13 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
|
||||
return;
|
||||
}
|
||||
|
||||
throwIfThreadInterrupted();
|
||||
// caves can generally be ignored since they aren't generally visible from far away
|
||||
if (step == EDhApiWorldGenerationStep.CARVERS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// throwIfThreadInterrupted();
|
||||
// // caves can generally be ignored since they aren't generally visible from far away
|
||||
// if (step == EDhApiWorldGenerationStep.CARVERS)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
throwIfThreadInterrupted();
|
||||
this.stepFeatures.generateGroup(genEvent.threadedParam, region, GetCutoutFrom(chunkWrappersToGenerate, EDhApiWorldGenerationStep.FEATURES));
|
||||
}
|
||||
@@ -574,6 +591,7 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
|
||||
{
|
||||
DhLightingEngine.INSTANCE.bakeChunkBlockLighting(centerChunk, iChunkWrapperList, maxSkyLight);
|
||||
}
|
||||
// centerChunk.setIsDhBlockLightCorrect(true);
|
||||
|
||||
this.dhServerLevel.updateBeaconBeamsForChunk(centerChunk, iChunkWrapperList);
|
||||
}
|
||||
|
||||
+5
-1
@@ -2,6 +2,7 @@ package com.seibel.distanthorizons.common.wrappers.worldGeneration;
|
||||
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Spliterator;
|
||||
import java.util.Spliterators;
|
||||
import java.util.function.Consumer;
|
||||
@@ -11,10 +12,13 @@ import java.util.stream.StreamSupport;
|
||||
public class ChunkPosGenStream
|
||||
{
|
||||
|
||||
public static Iterator<ChunkPos> getIterator(int genMinX, int genMinZ, int width, int extraRadius)
|
||||
{ return getStream(genMinX, genMinZ, width, extraRadius).iterator(); }
|
||||
/** @param extraRadius in both the positive and negative directions */
|
||||
public static Stream<ChunkPos> getStream(int genMinX, int genMinZ, int width, int extraRadius)
|
||||
{ return StreamSupport.stream(new InclusiveChunkPosIterator(genMinX, genMinZ, width, extraRadius), false); }
|
||||
public static class InclusiveChunkPosIterator extends Spliterators.AbstractSpliterator<ChunkPos>
|
||||
|
||||
private static class InclusiveChunkPosIterator extends Spliterators.AbstractSpliterator<ChunkPos>
|
||||
{
|
||||
private final int minX;
|
||||
private final int minZ;
|
||||
|
||||
+7
@@ -107,6 +107,13 @@ public class ChunkFileReader implements AutoCloseable
|
||||
ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
|
||||
DhChunkPos dhChunkPos = new DhChunkPos(chunkX, chunkZ);
|
||||
|
||||
// if (true)
|
||||
// {
|
||||
// ChunkAccess newChunk = CreateEmptyChunk(this.params.level, chunkPos);
|
||||
// generatedChunkByDhPos.put(dhChunkPos, newChunk);
|
||||
// return CompletableFuture.completedFuture(newChunk);
|
||||
// }
|
||||
|
||||
if (generatedChunkByDhPos.containsKey(dhChunkPos))
|
||||
{
|
||||
return CompletableFuture.completedFuture(generatedChunkByDhPos.get(dhChunkPos));
|
||||
|
||||
+156
-135
@@ -1,19 +1,24 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.internalServer;
|
||||
|
||||
import com.seibel.distanthorizons.api.DhApi;
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ChunkPosGenStream;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.GenerationEvent;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.GlobalWorldGenParams;
|
||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
|
||||
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
|
||||
import com.seibel.distanthorizons.core.level.IDhServerLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IC2meAccessor;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ChunkLevel;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.TicketType;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
@@ -23,8 +28,6 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class InternalServerGenerator
|
||||
@@ -39,6 +42,8 @@ public class InternalServerGenerator
|
||||
.fileLevelConfig(Config.Common.Logging.logWorldGenChunkLoadEventToFile)
|
||||
.build();
|
||||
|
||||
private static final IC2meAccessor C2ME_ACCESSOR = ModAccessorInjector.INSTANCE.get(IC2meAccessor.class);
|
||||
|
||||
#if MC_VER < MC_1_21_5
|
||||
private static final TicketType<ChunkPos> DH_SERVER_GEN_TICKET = TicketType.create("dh_server_gen_ticket", Comparator.comparingLong(ChunkPos::toLong));
|
||||
#elif MC_VER < MC_1_21_9
|
||||
@@ -47,6 +52,8 @@ public class InternalServerGenerator
|
||||
private static final TicketType DH_SERVER_GEN_TICKET = new TicketType(/* timeout, 0 = disabled*/0L, /* flags */TicketType.FLAG_LOADING);
|
||||
#endif
|
||||
|
||||
private static boolean c2meMissingWarningLogged = false;
|
||||
|
||||
|
||||
private final GlobalWorldGenParams params;
|
||||
private final IDhServerLevel dhServerLevel;
|
||||
@@ -69,183 +76,197 @@ public class InternalServerGenerator
|
||||
// generation //
|
||||
//============//
|
||||
|
||||
public void generateChunksViaInternalServer(GenerationEvent genEvent) throws InterruptedException
|
||||
public void generateChunksViaInternalServer(GenerationEvent genEvent)
|
||||
{
|
||||
LinkedBlockingQueue<Runnable> runnableQueue = new LinkedBlockingQueue<>();
|
||||
this.runValidation();
|
||||
|
||||
Map<DhChunkPos, ChunkWrapper> chunkWrappersByDhPos = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
|
||||
|
||||
//===================================//
|
||||
// create generation queue runnables //
|
||||
//===================================//
|
||||
|
||||
// request each chunk pos from the server
|
||||
CompletableFuture<?>[] requestFutures =
|
||||
ChunkPosGenStream.getStream(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0)
|
||||
.map(chunkPos ->
|
||||
{
|
||||
return requestChunkFromServerAsync(this.params.level, chunkPos, true)
|
||||
.whenCompleteAsync((chunk, throwable) ->
|
||||
{
|
||||
// unwrap the CompletionException if necessary
|
||||
Throwable actualThrowable = throwable;
|
||||
while (actualThrowable instanceof CompletionException)
|
||||
{
|
||||
actualThrowable = actualThrowable.getCause();
|
||||
}
|
||||
|
||||
if (throwable != null)
|
||||
{
|
||||
CHUNK_LOAD_LOGGER.warn("DistantHorizons: Couldn't load chunk [" + chunkPos + "] from server, error: [" + actualThrowable.getMessage() + "].", actualThrowable);
|
||||
}
|
||||
|
||||
if (chunk != null)
|
||||
{
|
||||
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, this.dhServerLevel.getLevelWrapper());
|
||||
chunkWrappersByDhPos.put(new DhChunkPos(chunkPos.x, chunkPos.z), chunkWrapper);
|
||||
}
|
||||
}, runnableQueue::add);
|
||||
})
|
||||
.toArray(CompletableFuture[]::new);
|
||||
|
||||
// handle each generated chunk
|
||||
CompletableFuture<Void> processGeneratedChunksFuture =
|
||||
CompletableFuture.allOf(requestFutures)
|
||||
.whenCompleteAsync((voidObj, throwable) ->
|
||||
{
|
||||
// generate chunk lighting using DH's lighting engine
|
||||
int maxSkyLight = this.dhServerLevel.getServerLevelWrapper().hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT;
|
||||
|
||||
ArrayList<IChunkWrapper> generatedChunks = new ArrayList<>(chunkWrappersByDhPos.values());
|
||||
for (IChunkWrapper iChunkWrapper : generatedChunks)
|
||||
{
|
||||
((ChunkWrapper) iChunkWrapper).recalculateDhHeightMapsIfNeeded();
|
||||
|
||||
// pre-generated chunks should have lighting but new ones won't
|
||||
if (!iChunkWrapper.isDhBlockLightingCorrect())
|
||||
{
|
||||
DhLightingEngine.INSTANCE.bakeChunkBlockLighting(iChunkWrapper, generatedChunks, maxSkyLight);
|
||||
}
|
||||
|
||||
this.dhServerLevel.updateBeaconBeamsForChunk(iChunkWrapper, generatedChunks);
|
||||
}
|
||||
|
||||
for (IChunkWrapper iChunkWrapper : generatedChunks)
|
||||
{
|
||||
genEvent.resultConsumer.accept(iChunkWrapper);
|
||||
}
|
||||
}, runnableQueue::add)
|
||||
.whenCompleteAsync((unused, throwable) ->
|
||||
{
|
||||
// cleanup
|
||||
// release the generated chunks
|
||||
|
||||
Iterator<ChunkPos> iterator = ChunkPosGenStream.getStream(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0).iterator();
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
ChunkPos chunkPos = iterator.next();
|
||||
releaseChunkFromServer(this.params.level, chunkPos, true);
|
||||
}
|
||||
});
|
||||
|
||||
processGeneratedChunksFuture.whenCompleteAsync((unused, throwable) -> { }, runnableQueue::add); // trigger wakeup
|
||||
|
||||
|
||||
|
||||
//===============//
|
||||
// run each step //
|
||||
//===============//
|
||||
|
||||
while (!processGeneratedChunksFuture.isDone())
|
||||
try
|
||||
{
|
||||
try
|
||||
//=====================//
|
||||
// create gen requests //
|
||||
//=====================//
|
||||
|
||||
ArrayList<CompletableFuture<ChunkAccess>> getChunkFutureList = new ArrayList<>();
|
||||
{
|
||||
Runnable command = runnableQueue.poll(1, TimeUnit.SECONDS);
|
||||
if (command != null)
|
||||
Iterator<ChunkPos> chunkPosIterator = ChunkPosGenStream.getIterator(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0);
|
||||
while (chunkPosIterator.hasNext())
|
||||
{
|
||||
command.run();
|
||||
ChunkPos chunkPos = chunkPosIterator.next();
|
||||
|
||||
CompletableFuture<ChunkAccess> requestChunkFuture =
|
||||
this.requestChunkFromServerAsync(chunkPos)
|
||||
// log errors if necessary
|
||||
.whenCompleteAsync(
|
||||
(chunk, throwable) ->
|
||||
{
|
||||
// unwrap the CompletionException if necessary
|
||||
Throwable actualThrowable = throwable;
|
||||
while (actualThrowable instanceof CompletionException)
|
||||
{
|
||||
actualThrowable = actualThrowable.getCause();
|
||||
}
|
||||
|
||||
if (actualThrowable != null)
|
||||
{
|
||||
// ignore expected shutdown exceptions
|
||||
boolean isShutdownException =
|
||||
ExceptionUtil.isShutdownException(actualThrowable)
|
||||
|| actualThrowable.getMessage().contains("Unloaded chunk");
|
||||
if (!isShutdownException)
|
||||
{
|
||||
CHUNK_LOAD_LOGGER.warn("DistantHorizons: Couldn't load chunk [" + chunkPos + "] from server, error: [" + actualThrowable.getMessage() + "].", actualThrowable);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
getChunkFutureList.add(requestChunkFuture);
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
|
||||
|
||||
|
||||
//==============================//
|
||||
// wait for generation requests //
|
||||
//==============================//
|
||||
|
||||
// Join-ing each thread will prevent DH from working on anything else
|
||||
// but will also prevent over-queuing world gen tasks.
|
||||
// If C2ME is present the CPU will still be well utilized.
|
||||
|
||||
ArrayList<IChunkWrapper> chunkWrappers = new ArrayList<>();
|
||||
for (int i = 0; i < getChunkFutureList.size(); i++)
|
||||
{
|
||||
// interrupted, release chunk to server
|
||||
Iterator<ChunkPos> iterator = ChunkPosGenStream.getStream(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0).iterator();
|
||||
while (iterator.hasNext())
|
||||
CompletableFuture<ChunkAccess> getChunkFuture = getChunkFutureList.get(i);
|
||||
ChunkAccess chunk = getChunkFuture.join();
|
||||
if (chunk != null)
|
||||
{
|
||||
ChunkPos chunkPos = iterator.next();
|
||||
releaseChunkFromServer(this.params.level, chunkPos, true);
|
||||
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, this.dhServerLevel.getLevelWrapper());
|
||||
chunkWrappers.add(chunkWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==========================//
|
||||
// process generated chunks //
|
||||
//==========================//
|
||||
|
||||
int maxSkyLight = this.dhServerLevel.getServerLevelWrapper().hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT;
|
||||
for (int i = 0; i < chunkWrappers.size(); i++)
|
||||
{
|
||||
ChunkWrapper chunkWrapper = (ChunkWrapper)chunkWrappers.get(i);
|
||||
chunkWrapper.recalculateDhHeightMapsIfNeeded();
|
||||
|
||||
// pre-generated chunks should have lighting but new ones won't
|
||||
if (!chunkWrapper.isDhBlockLightingCorrect())
|
||||
{
|
||||
DhLightingEngine.INSTANCE.bakeChunkBlockLighting(chunkWrapper, chunkWrappers, maxSkyLight);
|
||||
}
|
||||
|
||||
throw e;
|
||||
this.dhServerLevel.updateBeaconBeamsForChunk(chunkWrapper, chunkWrappers);
|
||||
genEvent.resultConsumer.accept(chunkWrapper);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// release all chunks from the server to prevent out of memory issues
|
||||
Iterator<ChunkPos> chunkPosIterator = ChunkPosGenStream.getIterator(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0);
|
||||
while (chunkPosIterator.hasNext())
|
||||
{
|
||||
ChunkPos chunkPos = chunkPosIterator.next();
|
||||
this.releaseChunkFromServer(this.params.level, chunkPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
/** @param generateUpToFeatures if false this generate the chunk up to "FULL" status */
|
||||
private static CompletableFuture<ChunkAccess> requestChunkFromServerAsync(ServerLevel level, ChunkPos pos, boolean generateUpToFeatures)
|
||||
private void runValidation()
|
||||
{
|
||||
// DH thread check
|
||||
if (!DhApi.isDhThread()
|
||||
&& ModInfo.IS_DEV_BUILD)
|
||||
{
|
||||
throw new IllegalStateException("Internal server generation should be called from one of DH's world gen thread. Current thread: ["+Thread.currentThread().getName()+"]");
|
||||
}
|
||||
|
||||
|
||||
// C2ME present?
|
||||
if (C2ME_ACCESSOR == null
|
||||
&& !c2meMissingWarningLogged)
|
||||
{
|
||||
c2meMissingWarningLogged = true;
|
||||
|
||||
String c2meWarning = "C2ME missing, \n" +
|
||||
"low CPU usage and slow world gen speeds expected. \n" +
|
||||
"DH is set to use MC's internal server for world gen \n" +
|
||||
"this mode is less efficient unless a mod like C2ME is present."
|
||||
;
|
||||
|
||||
if (Config.Common.Logging.Warning.showSlowWorldGenSettingWarnings.get())
|
||||
{
|
||||
String message =
|
||||
// orange text
|
||||
"\u00A76" + "Distant Horizons: slow world gen." + "\u00A7r\n" +
|
||||
c2meWarning;
|
||||
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
||||
}
|
||||
|
||||
LOGGER.warn(c2meWarning);
|
||||
}
|
||||
}
|
||||
private CompletableFuture<ChunkAccess> requestChunkFromServerAsync(ChunkPos chunkPos)
|
||||
{
|
||||
return CompletableFuture.supplyAsync(() ->
|
||||
{
|
||||
int chunkLevel;
|
||||
#if MC_VER <= MC_1_19_4
|
||||
// 33 is equivalent to FULL Chunk
|
||||
chunkLevel = generateUpToFeatures ? 33 + ChunkStatus.getDistance(ChunkStatus.FEATURES) : 33;
|
||||
#else
|
||||
// 33 is equivalent to FULL Chunk
|
||||
chunkLevel = generateUpToFeatures ? ChunkLevel.byStatus(ChunkStatus.FEATURES) : 33;
|
||||
#endif
|
||||
ServerLevel level = this.params.level;
|
||||
|
||||
#if MC_VER < MC_1_21_5
|
||||
int chunkLevel = 33; // 33 is equivalent to FULL Chunk
|
||||
level.getChunkSource().distanceManager.addTicket(DH_SERVER_GEN_TICKET, pos, chunkLevel, pos);
|
||||
#else
|
||||
level.getChunkSource().addTicketWithRadius(DH_SERVER_GEN_TICKET, pos, 0);
|
||||
level.getChunkSource().addTicketWithRadius(DH_SERVER_GEN_TICKET, chunkPos, 0);
|
||||
#endif
|
||||
level.getChunkSource().distanceManager.runAllUpdates(level.getChunkSource().chunkMap); // probably not the most optimal to run updates here, but fast enough
|
||||
ChunkHolder holder = level.getChunkSource().chunkMap.getUpdatingChunkIfPresent(pos.toLong());
|
||||
if (holder == null)
|
||||
|
||||
// probably not the most optimal to run updates here, but fast enough
|
||||
level.getChunkSource().distanceManager.runAllUpdates(level.getChunkSource().chunkMap);
|
||||
|
||||
ChunkHolder chunkHolder = level.getChunkSource().chunkMap.getUpdatingChunkIfPresent(chunkPos.toLong());
|
||||
if (chunkHolder == null)
|
||||
{
|
||||
throw new IllegalStateException("No chunk holder after ticket has been added");
|
||||
throw new IllegalStateException("No chunk chunkHolder for pos ["+chunkPos+"] after ticket has been added.");
|
||||
}
|
||||
|
||||
#if MC_VER <= MC_1_20_4
|
||||
return holder.getOrScheduleFuture(ChunkStatus.FEATURES, level.getChunkSource().chunkMap)
|
||||
return chunkHolder.getOrScheduleFuture(ChunkStatus.FEATURES, level.getChunkSource().chunkMap)
|
||||
.thenApply(result -> result.left().orElseThrow(() -> new RuntimeException(result.right().get().toString()))); // can throw if the server is shutting down
|
||||
#elif MC_VER <= MC_1_20_6
|
||||
return holder.getOrScheduleFuture(ChunkStatus.FEATURES, level.getChunkSource().chunkMap)
|
||||
return chunkHolder.getOrScheduleFuture(ChunkStatus.FEATURES, level.getChunkSource().chunkMap)
|
||||
.thenApply(result -> result.orElseThrow(() -> new RuntimeException(result.toString()))); // can throw if the server is shutting down
|
||||
#else
|
||||
return holder.scheduleChunkGenerationTask(ChunkStatus.FEATURES, level.getChunkSource().chunkMap)
|
||||
return chunkHolder.scheduleChunkGenerationTask(ChunkStatus.FEATURES, level.getChunkSource().chunkMap)
|
||||
.thenApply(result -> result.orElseThrow(() -> new RuntimeException(result.getError()))); // can throw if the server is shutting down
|
||||
#endif
|
||||
|
||||
}, level.getChunkSource().chunkMap.mainThreadExecutor)
|
||||
.thenCompose(Function.identity());
|
||||
}, this.params.level.getChunkSource().chunkMap.mainThreadExecutor)
|
||||
.thenCompose(Function.identity());
|
||||
}
|
||||
/** @param chunkWasGeneratedUpToFeatures if false this assumes the chunk was generated to "FULL" status */
|
||||
private static void releaseChunkFromServer(ServerLevel level, ChunkPos pos, boolean chunkWasGeneratedUpToFeatures)
|
||||
/**
|
||||
* mitigates out of memory issues in the vanilla chunk system. <br>
|
||||
* See: https://github.com/pop4959/Chunky/pull/383
|
||||
*/
|
||||
private void releaseChunkFromServer(ServerLevel level, ChunkPos pos)
|
||||
{
|
||||
level.getChunkSource().chunkMap.mainThreadExecutor.execute(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
int chunkLevel;
|
||||
#if MC_VER <= MC_1_19_4
|
||||
// 33 is equivalent to FULL Chunk
|
||||
chunkLevel = chunkWasGeneratedUpToFeatures ? 33 + ChunkStatus.getDistance(ChunkStatus.FEATURES) : 33;
|
||||
#else
|
||||
// 33 is equivalent to FULL Chunk
|
||||
chunkLevel = chunkWasGeneratedUpToFeatures ? ChunkLevel.byStatus(ChunkStatus.FEATURES) : 33;
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_21_5
|
||||
int chunkLevel = 33; // 33 is equivalent to FULL Chunk
|
||||
level.getChunkSource().distanceManager.removeTicket(DH_SERVER_GEN_TICKET, pos, chunkLevel, pos);
|
||||
#else
|
||||
level.getChunkSource().removeTicketWithRadius(DH_SERVER_GEN_TICKET, pos, 0);
|
||||
#endif
|
||||
|
||||
// mitigate OOM issues in vanilla chunk system: see https://github.com/pop4959/Chunky/pull/383
|
||||
level.getChunkSource().chunkMap.tick(() -> false);
|
||||
|
||||
#if MC_VER > MC_1_16_5
|
||||
level.entityManager.tick();
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user