From a2ed6005e66b35258bbfbbe8df129f1d965c384d Mon Sep 17 00:00:00 2001 From: DaPorkchop_ Date: Wed, 29 Dec 2021 17:56:42 +0100 Subject: [PATCH] support the cubic chunks async terrain gen api and periodically unload generated cubes --- .../CCAsyncBlockAccessImpl.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/main/java/net/daporkchop/fp2/compat/cc/asyncblockaccess/CCAsyncBlockAccessImpl.java b/src/main/java/net/daporkchop/fp2/compat/cc/asyncblockaccess/CCAsyncBlockAccessImpl.java index ebab83256..eb0ac9b43 100644 --- a/src/main/java/net/daporkchop/fp2/compat/cc/asyncblockaccess/CCAsyncBlockAccessImpl.java +++ b/src/main/java/net/daporkchop/fp2/compat/cc/asyncblockaccess/CCAsyncBlockAccessImpl.java @@ -26,6 +26,7 @@ import io.github.opencubicchunks.cubicchunks.api.world.ICubeProviderServer; import io.github.opencubicchunks.cubicchunks.api.world.ICubicWorldServer; import io.github.opencubicchunks.cubicchunks.api.world.storage.ICubicStorage; +import io.github.opencubicchunks.cubicchunks.api.worldgen.ICubeGenerator; import io.github.opencubicchunks.cubicchunks.core.asm.mixin.ICubicWorldInternal; import io.github.opencubicchunks.cubicchunks.core.server.chunkio.AsyncBatchingCubeIO; import io.github.opencubicchunks.cubicchunks.core.server.chunkio.ICubeIO; @@ -38,6 +39,7 @@ import net.daporkchop.fp2.compat.cc.cube.CubeWithoutWorld; import net.daporkchop.fp2.compat.vanilla.IBiomeAccess; import net.daporkchop.fp2.compat.vanilla.IBlockHeightAccess; +import net.daporkchop.fp2.mode.api.ctx.IFarWorldServer; import net.daporkchop.fp2.server.worldlistener.IWorldChangeListener; import net.daporkchop.fp2.server.worldlistener.WorldChangeListenerManager; import net.daporkchop.fp2.util.datastructure.Datastructures; @@ -61,11 +63,13 @@ import net.minecraft.world.WorldType; import net.minecraft.world.biome.Biome; import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.IChunkProvider; import net.minecraft.world.chunk.storage.ExtendedBlockStorage; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.stream.Stream; @@ -82,6 +86,7 @@ public class CCAsyncBlockAccessImpl implements IAsyncBlockAccess, IWorldChangeLi protected final WorldServer world; protected final ICubeIO io; protected final ICubicStorage storage; + protected final ICubeGenerator generator; protected final ColumnCache columns = new ColumnCache(); protected final CubeCache cubes = new CubeCache(); @@ -91,10 +96,13 @@ public class CCAsyncBlockAccessImpl implements IAsyncBlockAccess, IWorldChangeLi protected final NDimensionalIntSegtreeSet columnsExistCache; protected final NDimensionalIntSegtreeSet cubesExistCache; + protected final AtomicInteger generatedCount = new AtomicInteger(); + public CCAsyncBlockAccessImpl(@NonNull WorldServer world) { this.world = world; this.io = ((ICubeProviderInternal.Server) ((ICubicWorldInternal) world).getCubeCache()).getCubeIO(); this.storage = PUnsafe.getObject(AsyncBatchingCubeIO.class.cast(this.io), ASYNCBATCHINGCUBEIO_STORAGE_OFFSET); + this.generator = ((ICubicWorldServer) world).getCubeGenerator(); this.emptyStorage = new ExtendedBlockStorage(0, world.provider.hasSkyLight()); @@ -175,6 +183,18 @@ public void onCubeSaved(@NonNull World world, int cubeX, int cubeY, int cubeZ, @ this.cubes.notifyUpdate(new CubePos(cubeX, cubeY, cubeZ), nbt); } + @Override + public void onTickEnd() { + //if 8192 things have been generated, run a full chunk gc! + // if we don't do this, the gc will only be triggered when a player has been moving for more than some number of ticks. the player might just be standing still + // while waiting for terrain to load in, so we need to do this to prevent a memory leak. + if (this.generatedCount.get() >= 8192) { + this.generatedCount.lazySet(0); + + ((ICubicWorldServer) this.world).unloadOldCubes(); + } + } + @Override public boolean anyColumnIntersects(int tileX, int tileZ, int level) { return this.columnsExistCache.containsAny(level, tileX, tileZ); @@ -262,12 +282,18 @@ protected IColumn loadFromDisk(@NonNull ChunkPos key, @NonNull Object param) { @Override protected void triggerGeneration(@NonNull ChunkPos key, @NonNull Object param) { + //spin until the generator reports that it's ready + while (CCAsyncBlockAccessImpl.this.generator.pollAsyncColumnGenerator(key.x, key.z) != ICubeGenerator.GeneratorReadyState.READY) { + PorkUtil.sleep(1L); + } + //load and immediately save column on server thread ThreadingHelper.scheduleTaskInWorldThread(CCAsyncBlockAccessImpl.this.world, () -> { Chunk column = ((ICubicWorldServer) CCAsyncBlockAccessImpl.this.world) .getCubeCache().getColumn(key.x, key.z, ICubeProviderServer.Requirement.POPULATE); if (column != null && !column.isEmpty()) { CCAsyncBlockAccessImpl.this.io.saveColumn(column); + CCAsyncBlockAccessImpl.this.generatedCount.incrementAndGet(); } }).join(); } @@ -302,12 +328,19 @@ protected ICube loadFromDisk(@NonNull CubePos key, @NonNull Chunk param) { @Override protected void triggerGeneration(@NonNull CubePos key, @NonNull Chunk param) { + //spin until the generator reports that it's ready + while (CCAsyncBlockAccessImpl.this.generator.pollAsyncCubeGenerator(key.getX(), key.getY(), key.getZ()) != ICubeGenerator.GeneratorReadyState.READY + || CCAsyncBlockAccessImpl.this.generator.pollAsyncCubePopulator(key.getX(), key.getY(), key.getZ()) != ICubeGenerator.GeneratorReadyState.READY) { + PorkUtil.sleep(1L); + } + ThreadingHelper.scheduleTaskInWorldThread(CCAsyncBlockAccessImpl.this.world, () -> { //TODO: save column as well if needed ICube cube = ((ICubicWorldServer) CCAsyncBlockAccessImpl.this.world) .getCubeCache().getCube(key.getX(), key.getY(), key.getZ(), ICubeProviderServer.Requirement.LIGHT); if (cube != null && cube.isInitialLightingDone()) { CCAsyncBlockAccessImpl.this.io.saveCube((Cube) cube); + CCAsyncBlockAccessImpl.this.generatedCount.incrementAndGet(); } }).join(); }