diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java index 657e15ea6..e43640288 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java @@ -22,6 +22,7 @@ package com.seibel.distanthorizons.common.wrappers.chunk; import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper; import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper; import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.util.LodUtil; @@ -40,13 +41,11 @@ import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.levelgen.Heightmap; -import org.jetbrains.annotations.Nullable; - import java.util.*; -import java.util.concurrent.locks.ReentrantReadWriteLock; #if POST_MC_1_17_1 import net.minecraft.core.QuartPos; +import org.apache.logging.log4j.Logger; #endif #if POST_MC_1_20_1 @@ -57,6 +56,8 @@ import net.minecraft.core.SectionPos; public class ChunkWrapper implements IChunkWrapper { + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + /** useful for debugging, but can slow down chunk operations quite a bit due to being called every time. */ private static final boolean RUN_RELATIVE_POS_INDEX_VALIDATION = false; @@ -67,6 +68,8 @@ public class ChunkWrapper implements IChunkWrapper private final ILevelWrapper wrappedLevel; private boolean isDhLightCorrect = false; + /** only used when connected to a dedicated server */ + private boolean isMcClientLightingCorrect = false; private final byte[] blockLightArray; private final byte[] skyLightArray; @@ -76,8 +79,8 @@ public class ChunkWrapper implements IChunkWrapper private boolean useDhLighting; /** - * Due to vanilla `isClientLightReady()` not designed to be used by non-render thread, that value may return 'true' - * just before the light engine is ticked, (right after all light changes is marked to the engine to be processed). + * Due to vanilla `isClientLightReady()` not being designed for use by a non-render thread, it may return 'true' + * before the light engine has ticked, (right after all light changes is marked by the engine to be processed). * To fix this, on client-only mode, we mixin-redirect the `isClientLightReady()` so that after the call, it will * trigger a synchronous update of this flag here on all chunks that are wrapped.

* @@ -86,8 +89,9 @@ public class ChunkWrapper implements IChunkWrapper * visible when a chunk is re-wrapped later.
* (Also, thread safety done via a reader writer lock) */ - private final static WeakHashMap chunksToUpdateClientLightReady = new WeakHashMap<>(); // TODO this is never cleared - private final static ReentrantReadWriteLock weakMapLock = new ReentrantReadWriteLock(); + private static ArrayList chunksToUpdateClientLightReadyWriter = new ArrayList<>(300); + /** two arrays are used to prevent concurrency issues and the need to use a read/write lock. */ + private static ArrayList chunksToUpdateClientLightReadyReader = new ArrayList<>(300); @@ -111,9 +115,7 @@ public class ChunkWrapper implements IChunkWrapper this.blockLightArray = new byte[LodUtil.CHUNK_WIDTH * LodUtil.CHUNK_WIDTH * (this.getHeight() + 1)]; this.skyLightArray = new byte[LodUtil.CHUNK_WIDTH * LodUtil.CHUNK_WIDTH * (this.getHeight() + 1)]; - weakMapLock.writeLock().lock(); - chunksToUpdateClientLightReady.put(chunk, false); - weakMapLock.writeLock().unlock(); + chunksToUpdateClientLightReadyWriter.add(this); } @@ -218,14 +220,14 @@ public class ChunkWrapper implements IChunkWrapper LevelChunk levelChunk = (LevelChunk) this.chunk; if (levelChunk.getLevel().isClientSide()) { - weakMapLock.readLock().lock(); - boolean fixedIsClientLightReady = chunksToUpdateClientLightReady.get(this.chunk); - weakMapLock.readLock().unlock(); - return fixedIsClientLightReady; + // connected to a server + return this.isMcClientLightingCorrect; + } + else + { + // in a single player world + return this.chunk.isLightCorrect() && levelChunk.loaded; } - - // called when in single player or in dedicated server, and the chunk is a level chunk (active) - return this.chunk.isLightCorrect() && levelChunk.loaded; } else { @@ -395,14 +397,37 @@ public class ChunkWrapper implements IChunkWrapper } #endif - // Should be called after client light updates are triggered. - private static boolean updateClientLightReady(ChunkAccess chunk, boolean oldValue) + + public static void syncedUpdateClientLightStatus() { - if (chunk instanceof LevelChunk && ((LevelChunk) chunk).getLevel() instanceof ClientLevel) + #if PRE_MC_1_18_2 + // TODO: Check what to do in 1.18.1 and older + #else + + // swap the buffers + ArrayList temp = chunksToUpdateClientLightReadyReader; + chunksToUpdateClientLightReadyReader = chunksToUpdateClientLightReadyWriter; + chunksToUpdateClientLightReadyWriter = temp; + + // update the chunks client lighting + for (ChunkWrapper chunkWrapper : chunksToUpdateClientLightReadyReader) { - LevelChunk levelChunk = (LevelChunk) chunk; + chunkWrapper.updateIsClientLightingCorrect(); + } + // remove the processed chunks. + // FIXME sometimes chunks will be processed slightly early and will be removed even if they have valid lighting, this can cause holes in the world when flying around. + chunksToUpdateClientLightReadyReader.clear(); + + #endif + } + /** Should be called after client light updates are triggered. */ + private void updateIsClientLightingCorrect() + { + if (this.chunk instanceof LevelChunk && ((LevelChunk) this.chunk).getLevel() instanceof ClientLevel) + { + LevelChunk levelChunk = (LevelChunk) this.chunk; ClientChunkCache clientChunkCache = ((ClientLevel) levelChunk.getLevel()).getChunkSource(); - return clientChunkCache.getChunkForLighting(chunk.getPos().x, chunk.getPos().z) != null && + this.isMcClientLightingCorrect = clientChunkCache.getChunkForLighting(this.chunk.getPos().x, this.chunk.getPos().z) != null && #if MC_1_16_5 || MC_1_17_1 levelChunk.isLightCorrect(); #elif PRE_MC_1_20_1 @@ -411,27 +436,6 @@ public class ChunkWrapper implements IChunkWrapper checkLightSectionsOnChunk(levelChunk, levelChunk.getLevel().getLightEngine()); #endif } - else - { - return oldValue; - } - } - - public static void syncedUpdateClientLightStatus() - { - #if PRE_MC_1_18_2 - // TODO: Check what to do in 1.18.1, or in other versions - #else - weakMapLock.writeLock().lock(); - try - { - chunksToUpdateClientLightReady.replaceAll(ChunkWrapper::updateClientLightReady); - } - finally - { - weakMapLock.writeLock().unlock(); - } - #endif } diff --git a/coreSubProjects b/coreSubProjects index 025484d5b..87572246a 160000 --- a/coreSubProjects +++ b/coreSubProjects @@ -1 +1 @@ -Subproject commit 025484d5b4ba1341e7d16ba93c3fc50fb108c0de +Subproject commit 87572246a6323c6e5a35d8131552dde845d28195 diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricClientProxy.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricClientProxy.java index 9d0cfe32e..b1ca87de6 100644 --- a/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricClientProxy.java +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricClientProxy.java @@ -131,7 +131,7 @@ public class FabricClientProxy LOGGER.trace("attack block at blockPos: " + blockPos); IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level); - ClientApi.INSTANCE.clientChunkLoadEvent( + ClientApi.INSTANCE.clientChunkBlockChangedEvent( new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel ); @@ -158,7 +158,7 @@ public class FabricClientProxy LOGGER.trace("use block at blockPos: " + hitResult.getBlockPos()); IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level); - ClientApi.INSTANCE.clientChunkLoadEvent( + ClientApi.INSTANCE.clientChunkBlockChangedEvent( new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel ); diff --git a/forge/src/main/java/com/seibel/distanthorizons/forge/ForgeClientProxy.java b/forge/src/main/java/com/seibel/distanthorizons/forge/ForgeClientProxy.java index 8732fbcdb..c13e7a704 100644 --- a/forge/src/main/java/com/seibel/distanthorizons/forge/ForgeClientProxy.java +++ b/forge/src/main/java/com/seibel/distanthorizons/forge/ForgeClientProxy.java @@ -191,7 +191,7 @@ public class ForgeClientProxy if (chunk != null) { IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level); - ClientApi.INSTANCE.clientChunkLoadEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel); + ClientApi.INSTANCE.clientChunkBlockChangedEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel); } } }