diff --git a/gradle.properties b/gradle.properties index 0b964a7c..15316895 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ minecraft_version=1.16.4 yarn_mappings=1.16.4+build.7 loader_version=0.10.8 # Mod Properties -mod_version=0.0.2-RC1 +mod_version=0.0.2-RC2 maven_group=ca.spottedleaf.starlight archives_base_name=starlight # Dependencies diff --git a/src/main/java/ca/spottedleaf/starlight/common/light/SkyStarLightEngine.java b/src/main/java/ca/spottedleaf/starlight/common/light/SkyStarLightEngine.java index ba5f872e..5853ec46 100644 --- a/src/main/java/ca/spottedleaf/starlight/common/light/SkyStarLightEngine.java +++ b/src/main/java/ca/spottedleaf/starlight/common/light/SkyStarLightEngine.java @@ -3,6 +3,7 @@ import ca.spottedleaf.starlight.common.blockstate.ExtendedAbstractBlockState; import ca.spottedleaf.starlight.common.chunk.ExtendedChunk; import ca.spottedleaf.starlight.common.chunk.ExtendedChunkSection; +import it.unimi.dsi.fastutil.shorts.ShortCollection; import net.minecraft.block.BlockState; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; @@ -280,7 +281,7 @@ protected final void initNibbleForLitChunk(final SWMRNibbleArray currNibble, fin protected final void rewriteNibbleCacheForSkylight(final Chunk chunk) { for (int index = 0, max = this.nibbleCache.length; index < max; ++index) { final SWMRNibbleArray nibble = this.nibbleCache[index]; - if (nibble == null || nibble.isNullNibbleUpdating()) { + if (nibble != null && nibble.isNullNibbleUpdating()) { // stop propagation in these areas this.nibbleCache[index] = null; } @@ -372,6 +373,19 @@ protected boolean canUseChunk(final Chunk chunk) { && (this.isClientSide ? ((ExtendedChunk)chunk).getEmptinessMap()[ExtendedChunk.getEmptinessMapIndex(0, 0)] != null : chunk.isLightOn()); } + @Override + protected void checkChunkEdges(final ChunkProvider lightAccess, final Chunk chunk, final int fromSection, + final int toSection) { + this.rewriteNibbleCacheForSkylight(chunk); + super.checkChunkEdges(lightAccess, chunk, fromSection, toSection); + } + + @Override + protected void checkChunkEdges(ChunkProvider lightAccess, Chunk chunk, ShortCollection sections) { + this.rewriteNibbleCacheForSkylight(chunk); + super.checkChunkEdges(lightAccess, chunk, sections); + } + @Override protected void checkBlock(final int worldX, final int worldY, final int worldZ) { // blocks can change opacity @@ -389,8 +403,7 @@ protected void checkBlock(final int worldX, final int worldY, final int worldZ) ((worldX + (worldZ << 6) + (worldY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) | (currentLevel & 0xFL) << (6 + 6 + 16) | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) - | FLAG_HAS_SIDED_TRANSPARENT_BLOCKS // don't know if the block is conditionally transparent - | (FLAG_RECHECK_LEVEL); // this source might be set up to decrease + | FLAG_HAS_SIDED_TRANSPARENT_BLOCKS; // don't know if the block is conditionally transparent } else { this.setLightLevel(worldX, worldY, worldZ, 0); } @@ -429,6 +442,10 @@ protected void propagateBlockChanges(final ChunkProvider lightAccess, final Chun } } + // note: light sets are delayed while processing skylight source changes due to how + // nibbles are initialised, as we want to avoid clobbering nibble values so what when + // below nibbles are initialised they aren't reading from partially modified nibbles + // now we can recalculate the sources for the changed columns for (int index = 0; index < (16 * 16); ++index) { final int maxY = this.heightMapBlockChange[index]; @@ -442,7 +459,8 @@ protected void propagateBlockChanges(final ChunkProvider lightAccess, final Chun final int columnZ = (index >>> 4) | (chunkZ << 4); // try and propagate from the above y - final int maxPropagationY = this.tryPropagateSkylight(world, columnX, maxY, columnZ, true); + // delay light set until after processing all sources to setup + final int maxPropagationY = this.tryPropagateSkylight(world, columnX, maxY, columnZ, true, true); // maxPropagationY is now the highest block that could not be propagated to @@ -474,16 +492,21 @@ protected void propagateBlockChanges(final ChunkProvider lightAccess, final Chun break; } + // delay light set until after processing all sources to setup this.decreaseQueue[this.decreaseQueueInitialLength++] = ((columnX + (columnZ << 6) + (currY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) | (15L << (6 + 6 + 16)) - | (propagateDirection << (6 + 6 + 16 + 4)) - | FLAG_FORCE_WRITE; // overwriting the nibble value affects init of dummy nibbles + | (propagateDirection << (6 + 6 + 16 + 4)); // do not set transparent blocks for the same reason we don't in the checkBlock method } } } + // delayed light sets are processed here, and must be processed before checkBlock as checkBlock reads + // immediate light value + this.processDelayedIncreases(); + this.processDelayedDecreases(); + for (final BlockPos pos : positions) { this.checkBlock(pos.getX(), pos.getY(), pos.getZ()); } @@ -693,7 +716,7 @@ protected void lightChunk(final ChunkProvider lightAccess, final Chunk chunk, fi this.increaseQueueInitialLength = queueLength; // Just in case there's a conditionally transparent block at the top. - this.tryPropagateSkylight(world, worldX, heightMapC, worldZ, false); + this.tryPropagateSkylight(world, worldX, heightMapC, worldZ, false, false); } } } // else: apparently the chunk is empty @@ -716,8 +739,48 @@ protected void lightChunk(final ChunkProvider lightAccess, final Chunk chunk, fi } } + protected final void processDelayedIncreases() { + // copied from performLightIncrease + final long[] queue = this.increaseQueue; + final int decodeOffsetX = -this.encodeOffsetX; + final int decodeOffsetY = -this.encodeOffsetY; + final int decodeOffsetZ = -this.encodeOffsetZ; + + for (int i = 0, len = this.increaseQueueInitialLength; i < len; ++i) { + final long queueValue = queue[i]; + + final int posX = ((int)queueValue & 63) + decodeOffsetX; + final int posZ = (((int)queueValue >>> 6) & 63) + decodeOffsetZ; + final int posY = (((int)queueValue >>> 12) & ((1 << 16) - 1)) + decodeOffsetY; + final int propagatedLightLevel = (int)((queueValue >>> (6 + 6 + 16)) & 0xF); + + this.setLightLevel(posX, posY, posZ, propagatedLightLevel); + } + } + + protected final void processDelayedDecreases() { + // copied from performLightDecrease + final long[] queue = this.decreaseQueue; + final int decodeOffsetX = -this.encodeOffsetX; + final int decodeOffsetY = -this.encodeOffsetY; + final int decodeOffsetZ = -this.encodeOffsetZ; + + for (int i = 0, len = this.decreaseQueueInitialLength; i < len; ++i) { + final long queueValue = queue[i]; + + final int posX = ((int)queueValue & 63) + decodeOffsetX; + final int posZ = (((int)queueValue >>> 6) & 63) + decodeOffsetZ; + final int posY = (((int)queueValue >>> 12) & ((1 << 16) - 1)) + decodeOffsetY; + + this.setLightLevel(posX, posY, posZ, 0); + } + } + + // delaying the light set is useful for block changes since they need to worry about initialising nibblearrays + // while also queueing light at the same time (initialising nibblearrays might depend on nibbles above, so + // clobbering the light values will result in broken propagation) protected final int tryPropagateSkylight(final BlockView world, final int worldX, int startY, final int worldZ, - final boolean extrudeInitialised) { + final boolean extrudeInitialised, final boolean delayLightSet) { final BlockPos.Mutable mutablePos = this.mutablePos3; final int encodeOffset = this.coordinateOffset; final long propagateDirection = AxisDirection.POSITIVE_Y.everythingButThisDirection; // just don't check upwards. @@ -765,14 +828,14 @@ protected final int tryPropagateSkylight(final BlockView world, final int worldX } // most of the time it falls here. // add to propagate + // light set delayed until we determine if this nibble section is null this.increaseQueue[this.increaseQueueInitialLength++] = ((worldX + (worldZ << 6) + (startY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) | (15L << (6 + 6 + 16)) // we know we're at full lit here - | (propagateDirection << (6 + 6 + 16 + 4)) - | FLAG_FORCE_WRITE; // overwriting the nibble value affects init of dummy nibbles + | (propagateDirection << (6 + 6 + 16 + 4)); } else { mutablePos.set(worldX, startY, worldZ); - long flags = FLAG_FORCE_WRITE; // overwriting the nibble value affects init of dummy nibbles + long flags = 0L; if (((ExtendedAbstractBlockState)current).isConditionallyFullOpaque()) { final VoxelShape cullingFace = current.getCullingFace(world, mutablePos, AxisDirection.POSITIVE_Y.nms); @@ -789,6 +852,7 @@ protected final int tryPropagateSkylight(final BlockView world, final int worldX break; } + // light set delayed until we determine if this nibble section is null this.increaseQueue[this.increaseQueueInitialLength++] = ((worldX + (worldZ << 6) + (startY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) | (15L << (6 + 6 + 16)) // we know we're at full lit here @@ -812,6 +876,8 @@ protected final int tryPropagateSkylight(final BlockView world, final int worldX // make sure this is marked as AIR above = AIR_BLOCK_STATE; + } else if (!delayLightSet) { + this.setLightLevel(worldX, startY, worldZ, 15); } } diff --git a/src/main/java/ca/spottedleaf/starlight/common/light/StarLightEngine.java b/src/main/java/ca/spottedleaf/starlight/common/light/StarLightEngine.java index d32dd5e8..fde15b6e 100644 --- a/src/main/java/ca/spottedleaf/starlight/common/light/StarLightEngine.java +++ b/src/main/java/ca/spottedleaf/starlight/common/light/StarLightEngine.java @@ -2,6 +2,8 @@ import ca.spottedleaf.starlight.common.blockstate.ExtendedAbstractBlockState; import ca.spottedleaf.starlight.common.util.IntegerUtil; +import it.unimi.dsi.fastutil.shorts.ShortCollection; +import it.unimi.dsi.fastutil.shorts.ShortIterator; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; import net.minecraft.util.math.BlockPos; @@ -394,109 +396,125 @@ public final void blocksChangedInChunk(final ChunkProvider lightAccess, final in protected abstract void checkBlock(final int worldX, final int worldY, final int worldZ); - // subclasses should not initialise caches, as this will always be done by the super call - // subclasses should not invoke updateVisible, as this will always be done by the super call - // verifies that light levels on this chunks edges are consistent with this chunk's neighbours - // edges. if they are not, they are decreased (effectively performing the logic in checkBlock). - // This does not resolve skylight source problems. - protected final void checkChunkEdges(final ChunkProvider lightAccess, final Chunk chunk, final int fromSection, final int toSection) { - final ChunkPos chunkPos = chunk.getPos(); - final int chunkX = chunkPos.x; - final int chunkZ = chunkPos.z; + protected void checkChunkEdge(final ChunkProvider lightAccess, final Chunk chunk, + final int chunkX, final int chunkY, final int chunkZ) { + final SWMRNibbleArray currNibble = this.getNibbleFromCache(chunkX, chunkY, chunkZ); + if (currNibble == null) { + return; + } - for (int currSectionY = toSection; currSectionY >= fromSection; --currSectionY) { - final SWMRNibbleArray currNibble = this.getNibbleFromCache(chunkX, currSectionY, chunkZ); - for (final AxisDirection direction : ONLY_HORIZONTAL_DIRECTIONS) { - final int neighbourOffX = direction.x; - final int neighbourOffZ = direction.z; + for (final AxisDirection direction : ONLY_HORIZONTAL_DIRECTIONS) { + final int neighbourOffX = direction.x; + final int neighbourOffZ = direction.z; - final SWMRNibbleArray neighbourNibble = this.getNibbleFromCache(chunkX + neighbourOffX, - currSectionY, chunkZ + neighbourOffZ); + final SWMRNibbleArray neighbourNibble = this.getNibbleFromCache(chunkX + neighbourOffX, + chunkY, chunkZ + neighbourOffZ); - if (neighbourNibble == null || neighbourNibble.isNullNibbleUpdating()) { - continue; - } + if (neighbourNibble == null || neighbourNibble.isNullNibbleUpdating()) { + continue; + } - if (this.skylightPropagator && currNibble.isNullNibbleUpdating()) { - // TODO in what situation does this happen? Pretty sure it's erroneous. - continue; - } + if (!currNibble.isInitialisedUpdating() && !neighbourNibble.isInitialisedUpdating()) { + // both are zero, nothing to check. + continue; + } - if (!currNibble.isInitialisedUpdating() && !neighbourNibble.isInitialisedUpdating()) { - // both are zero, nothing to check. - continue; - } + final int incX; + final int incZ; + final int startX; + final int startZ; - final int incX; - final int incZ; - final int startX; - final int startZ; + if (neighbourOffX != 0) { + // x direction + incX = 0; + incZ = 1; - if (neighbourOffX != 0) { - // x direction - incX = 0; - incZ = 1; + if (direction.x < 0) { + // negative + startX = chunkX << 4; + } else { + startX = chunkX << 4 | 15; + } + startZ = chunkZ << 4; + } else { + // z direction + incX = 1; + incZ = 0; - if (direction.x < 0) { - // negative - startX = chunkX << 4; - } else { - startX = chunkX << 4 | 15; - } + if (neighbourOffZ < 0) { + // negative startZ = chunkZ << 4; } else { - // z direction - incX = 1; - incZ = 0; - - if (neighbourOffZ < 0) { - // negative - startZ = chunkZ << 4; - } else { - startZ = chunkZ << 4 | 15; - } - startX = chunkX << 4; + startZ = chunkZ << 4 | 15; } + startX = chunkX << 4; + } - for (int currY = currSectionY << 4, maxY = currY | 15; currY <= maxY; ++currY) { - for (int i = 0, currX = startX, currZ = startZ; i < 16; ++i, currX += incX, currZ += incZ) { - final int neighbourX = currX + neighbourOffX; - final int neighbourZ = currZ + neighbourOffZ; + for (int currY = chunkY << 4, maxY = currY | 15; currY <= maxY; ++currY) { + for (int i = 0, currX = startX, currZ = startZ; i < 16; ++i, currX += incX, currZ += incZ) { + final int neighbourX = currX + neighbourOffX; + final int neighbourZ = currZ + neighbourOffZ; + + final int currentLevel = currNibble.getUpdating((currX & 15) | + ((currZ & 15)) << 4 | + ((currY & 15) << 8) + ); + final int neighbourLevel = neighbourNibble.getUpdating((neighbourX & 15) | + ((neighbourZ & 15)) << 4 | + ((currY & 15) << 8) + ); + + if (currentLevel == neighbourLevel && (currentLevel == 0 || currentLevel == 15)) { + // nothing to check here + continue; + } - final int currentLevel = currNibble.getUpdating((currX & 15) | - ((currZ & 15)) << 4 | - ((currY & 15) << 8) - ); - final int neighbourLevel = neighbourNibble.getUpdating((neighbourX & 15) | - ((neighbourZ & 15)) << 4 | - ((currY & 15) << 8) - ); + if (Math.abs(currentLevel - neighbourLevel) == 1) { + final BlockState currentBlock = this.getBlockState(currX, currY, currZ); + final BlockState neighbourBlock = this.getBlockState(neighbourX, currY, neighbourZ); - if (currentLevel == neighbourLevel && (currentLevel == 0 || currentLevel == 15)) { - // nothing to check here + final int currentOpacity = ((ExtendedAbstractBlockState)currentBlock).getOpacityIfCached(); + final int neighbourOpacity = ((ExtendedAbstractBlockState)neighbourBlock).getOpacityIfCached(); + if (currentOpacity == 0 || currentOpacity == 1 || + neighbourOpacity == 0 || neighbourOpacity == 1) { + // looks good continue; } - - if (Math.abs(currentLevel - neighbourLevel) == 1) { - final BlockState currentBlock = this.getBlockState(currX, currY, currZ); - final BlockState neighbourBlock = this.getBlockState(neighbourX, currY, neighbourZ); - - final int currentOpacity = ((ExtendedAbstractBlockState)currentBlock).getOpacityIfCached(); - final int neighbourOpacity = ((ExtendedAbstractBlockState)neighbourBlock).getOpacityIfCached(); - if (currentOpacity == 0 || currentOpacity == 1 || - neighbourOpacity == 0 || neighbourOpacity == 1) { - // looks good - continue; - } - } - - // setup queue, it looks like something could be inconsistent - this.checkBlock(currX, currY, currZ); - this.checkBlock(neighbourX, currY, neighbourZ); } + + // setup queue, it looks like something could be inconsistent + this.checkBlock(currX, currY, currZ); + this.checkBlock(neighbourX, currY, neighbourZ); } } } + } + + protected void checkChunkEdges(final ChunkProvider lightAccess, final Chunk chunk, final ShortCollection sections) { + final ChunkPos chunkPos = chunk.getPos(); + final int chunkX = chunkPos.x; + final int chunkZ = chunkPos.z; + + for (final ShortIterator iterator = sections.iterator(); iterator.hasNext();) { + this.checkChunkEdge(lightAccess, chunk, chunkX, iterator.nextShort(), chunkZ); + } + + this.performLightDecrease(lightAccess); + } + + // subclasses should not initialise caches, as this will always be done by the super call + // subclasses should not invoke updateVisible, as this will always be done by the super call + // verifies that light levels on this chunks edges are consistent with this chunk's neighbours + // edges. if they are not, they are decreased (effectively performing the logic in checkBlock). + // This does not resolve skylight source problems. + protected void checkChunkEdges(final ChunkProvider lightAccess, final Chunk chunk, final int fromSection, final int toSection) { + final ChunkPos chunkPos = chunk.getPos(); + final int chunkX = chunkPos.x; + final int chunkZ = chunkPos.z; + + for (int currSectionY = toSection; currSectionY >= fromSection; --currSectionY) { + this.checkChunkEdge(lightAccess, chunk, chunkX, currSectionY, chunkZ); + } this.performLightDecrease(lightAccess); } @@ -641,7 +659,7 @@ protected abstract void handleEmptySectionChanges(final ChunkProvider lightAcces final Boolean[] emptinessChanges, final boolean unlit); public final void checkChunkEdges(final ChunkProvider lightAccess, final int chunkX, final int chunkZ) { - this.setupCaches(lightAccess, chunkX * 16 + 7, 128, chunkZ * 16 + 7, false); + this.setupCaches(lightAccess, chunkX * 16 + 7, 128, chunkZ * 16 + 7, true); try { final Chunk chunk = this.getChunkInCache(chunkX, chunkZ); if (chunk == null) { @@ -654,6 +672,20 @@ public final void checkChunkEdges(final ChunkProvider lightAccess, final int chu } } + public final void checkChunkEdges(final ChunkProvider lightAccess, final int chunkX, final int chunkZ, final ShortCollection sections) { + this.setupCaches(lightAccess, chunkX * 16 + 7, 128, chunkZ * 16 + 7, true); + try { + final Chunk chunk = this.getChunkInCache(chunkX, chunkZ); + if (chunk == null) { + return; + } + this.checkChunkEdges(lightAccess, chunk, sections); + this.updateVisible(lightAccess); + } finally { + this.destroyCaches(); + } + } + // subclasses should not initialise caches, as this will always be done by the super call // subclasses should not invoke updateVisible, as this will always be done by the super call // needsEdgeChecks applies when possibly loading vanilla data, which means we need to validate the current @@ -732,13 +764,10 @@ protected final void relightChunk(final ChunkProvider lightAccess, final Chunk c // lower (6 + 6 + 16) = 28 bits: encoded coordinate position (x | (z << 6) | (y << (6 + 6)))) // next 4 bits: propagated light level (0, 15] // next 6 bits: propagation direction bitset - // next 23 bits: unused - // last 3 bits: state flags + // next 24 bits: unused + // last 4 bits: state flags // state flags: // whether the propagation must set the current position's light value (0 if decrease, propagated light level if increase) - // used when wanting to delay the setting of a level - // this will also re-schedule the queued value, so it will be shuffled to the end of the queue - protected static final long FLAG_FORCE_WRITE = Long.MIN_VALUE >>> 2; // whether the propagation needs to check if its current level is equal to the expected level // used only in increase propagation protected static final long FLAG_RECHECK_LEVEL = Long.MIN_VALUE >>> 1; @@ -789,11 +818,6 @@ protected final void performLightIncrease(final ChunkProvider lightAccess) { continue; } } - if ((queueValue & FLAG_FORCE_WRITE) != 0L) { - this.setLightLevel(posX, posY, posZ, propagatedLightLevel); - queue[queueLength++] = queueValue ^ FLAG_FORCE_WRITE; - continue; - } if ((queueValue & FLAG_HAS_SIDED_TRANSPARENT_BLOCKS) == 0L) { // we don't need to worry about our state here. @@ -968,12 +992,6 @@ protected final void performLightDecrease(final ChunkProvider lightAccess) { final int propagatedLightLevel = (int)((queueValue >>> (6 + 6 + 16)) & 0xF); final AxisDirection[] checkDirections = OLD_CHECK_DIRECTIONS[(int)((queueValue >>> (6 + 6 + 16 + 4)) & 63)]; - if ((queueValue & FLAG_FORCE_WRITE) != 0L) { - this.setLightLevel(posX, posY, posZ, 0); - queue[queueLength++] = queueValue ^ FLAG_FORCE_WRITE; - continue; - } - if ((queueValue & FLAG_HAS_SIDED_TRANSPARENT_BLOCKS) == 0L) { // we don't need to worry about our state here. for (final AxisDirection propagate : checkDirections) { diff --git a/src/main/java/ca/spottedleaf/starlight/common/light/StarLightInterface.java b/src/main/java/ca/spottedleaf/starlight/common/light/StarLightInterface.java index 6c02fa3e..15a15a0b 100644 --- a/src/main/java/ca/spottedleaf/starlight/common/light/StarLightInterface.java +++ b/src/main/java/ca/spottedleaf/starlight/common/light/StarLightInterface.java @@ -5,6 +5,7 @@ import ca.spottedleaf.starlight.common.world.ExtendedWorld; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.shorts.ShortCollection; import net.minecraft.server.world.ChunkTicketType; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; @@ -13,7 +14,6 @@ import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.ChunkNibbleArray; import net.minecraft.world.chunk.ChunkProvider; -import net.minecraft.world.chunk.ChunkSection; import net.minecraft.world.chunk.ChunkStatus; import net.minecraft.world.chunk.light.ChunkLightingView; import java.util.ArrayDeque; @@ -342,18 +342,52 @@ public void relightChunk(final int chunkX, final int chunkZ) { } public void checkChunkEdges(final int chunkX, final int chunkZ) { + this.checkSkyEdges(chunkX, chunkZ); + this.checkBlockEdges(chunkX, chunkZ); + } + + public void checkSkyEdges(final int chunkX, final int chunkZ) { final SkyStarLightEngine skyEngine = this.getSkyLightEngine(); - final BlockStarLightEngine blockEngine = this.getBlockLightEngine(); try { if (skyEngine != null) { skyEngine.checkChunkEdges(this.lightAccess, chunkX, chunkZ); } + } finally { + this.releaseSkyLightEngine(skyEngine); + } + } + + public void checkBlockEdges(final int chunkX, final int chunkZ) { + final BlockStarLightEngine blockEngine = this.getBlockLightEngine(); + try { if (blockEngine != null) { blockEngine.checkChunkEdges(this.lightAccess, chunkX, chunkZ); } + } finally { + this.releaseBlockLightEngine(blockEngine); + } + } + + public void checkSkyEdges(final int chunkX, final int chunkZ, final ShortCollection sections) { + final SkyStarLightEngine skyEngine = this.getSkyLightEngine(); + + try { + if (skyEngine != null) { + skyEngine.checkChunkEdges(this.lightAccess, chunkX, chunkZ, sections); + } } finally { this.releaseSkyLightEngine(skyEngine); + } + } + + public void checkBlockEdges(final int chunkX, final int chunkZ, final ShortCollection sections) { + final BlockStarLightEngine blockEngine = this.getBlockLightEngine(); + try { + if (blockEngine != null) { + blockEngine.checkChunkEdges(this.lightAccess, chunkX, chunkZ, sections); + } + } finally { this.releaseBlockLightEngine(blockEngine); } } diff --git a/src/main/java/ca/spottedleaf/starlight/mixin/world/ClientWorldMixin.java b/src/main/java/ca/spottedleaf/starlight/mixin/client/world/ClientWorldMixin.java similarity index 96% rename from src/main/java/ca/spottedleaf/starlight/mixin/world/ClientWorldMixin.java rename to src/main/java/ca/spottedleaf/starlight/mixin/client/world/ClientWorldMixin.java index 8cb5fc2f..66043eb1 100644 --- a/src/main/java/ca/spottedleaf/starlight/mixin/world/ClientWorldMixin.java +++ b/src/main/java/ca/spottedleaf/starlight/mixin/client/world/ClientWorldMixin.java @@ -1,4 +1,4 @@ -package ca.spottedleaf.starlight.mixin.world; +package ca.spottedleaf.starlight.mixin.client.world; import ca.spottedleaf.starlight.common.world.ExtendedWorld; import net.fabricmc.api.EnvType; diff --git a/src/main/java/ca/spottedleaf/starlight/mixin/blockstate/AbstractBlockStateMixin.java b/src/main/java/ca/spottedleaf/starlight/mixin/common/blockstate/AbstractBlockStateMixin.java similarity index 97% rename from src/main/java/ca/spottedleaf/starlight/mixin/blockstate/AbstractBlockStateMixin.java rename to src/main/java/ca/spottedleaf/starlight/mixin/common/blockstate/AbstractBlockStateMixin.java index 0b9566c6..eebcad2d 100644 --- a/src/main/java/ca/spottedleaf/starlight/mixin/blockstate/AbstractBlockStateMixin.java +++ b/src/main/java/ca/spottedleaf/starlight/mixin/common/blockstate/AbstractBlockStateMixin.java @@ -1,4 +1,4 @@ -package ca.spottedleaf.starlight.mixin.blockstate; +package ca.spottedleaf.starlight.mixin.common.blockstate; import ca.spottedleaf.starlight.common.blockstate.ExtendedAbstractBlockState; import com.google.common.collect.ImmutableMap; diff --git a/src/main/java/ca/spottedleaf/starlight/mixin/chunk/ChunkMixin.java b/src/main/java/ca/spottedleaf/starlight/mixin/common/chunk/ChunkMixin.java similarity index 80% rename from src/main/java/ca/spottedleaf/starlight/mixin/chunk/ChunkMixin.java rename to src/main/java/ca/spottedleaf/starlight/mixin/common/chunk/ChunkMixin.java index fdba416f..94913df9 100644 --- a/src/main/java/ca/spottedleaf/starlight/mixin/chunk/ChunkMixin.java +++ b/src/main/java/ca/spottedleaf/starlight/mixin/common/chunk/ChunkMixin.java @@ -1,4 +1,4 @@ -package ca.spottedleaf.starlight.mixin.chunk; +package ca.spottedleaf.starlight.mixin.common.chunk; import ca.spottedleaf.starlight.common.chunk.ExtendedChunk; import net.minecraft.world.chunk.Chunk; diff --git a/src/main/java/ca/spottedleaf/starlight/mixin/chunk/ChunkSectionMixin.java b/src/main/java/ca/spottedleaf/starlight/mixin/common/chunk/ChunkSectionMixin.java similarity index 99% rename from src/main/java/ca/spottedleaf/starlight/mixin/chunk/ChunkSectionMixin.java rename to src/main/java/ca/spottedleaf/starlight/mixin/common/chunk/ChunkSectionMixin.java index 5c7beb86..ea72c301 100644 --- a/src/main/java/ca/spottedleaf/starlight/mixin/chunk/ChunkSectionMixin.java +++ b/src/main/java/ca/spottedleaf/starlight/mixin/common/chunk/ChunkSectionMixin.java @@ -1,4 +1,4 @@ -package ca.spottedleaf.starlight.mixin.chunk; +package ca.spottedleaf.starlight.mixin.common.chunk; import ca.spottedleaf.starlight.common.blockstate.ExtendedAbstractBlockState; import ca.spottedleaf.starlight.common.chunk.ExtendedChunkSection; diff --git a/src/main/java/ca/spottedleaf/starlight/mixin/chunk/EmptyChunkMixin.java b/src/main/java/ca/spottedleaf/starlight/mixin/common/chunk/EmptyChunkMixin.java similarity index 95% rename from src/main/java/ca/spottedleaf/starlight/mixin/chunk/EmptyChunkMixin.java rename to src/main/java/ca/spottedleaf/starlight/mixin/common/chunk/EmptyChunkMixin.java index 23daecad..4f9b1e95 100644 --- a/src/main/java/ca/spottedleaf/starlight/mixin/chunk/EmptyChunkMixin.java +++ b/src/main/java/ca/spottedleaf/starlight/mixin/common/chunk/EmptyChunkMixin.java @@ -1,4 +1,4 @@ -package ca.spottedleaf.starlight.mixin.chunk; +package ca.spottedleaf.starlight.mixin.common.chunk; import ca.spottedleaf.starlight.common.chunk.ExtendedChunk; import ca.spottedleaf.starlight.common.light.SWMRNibbleArray; diff --git a/src/main/java/ca/spottedleaf/starlight/mixin/chunk/ProtoChunkMixin.java b/src/main/java/ca/spottedleaf/starlight/mixin/common/chunk/ProtoChunkMixin.java similarity index 97% rename from src/main/java/ca/spottedleaf/starlight/mixin/chunk/ProtoChunkMixin.java rename to src/main/java/ca/spottedleaf/starlight/mixin/common/chunk/ProtoChunkMixin.java index 1e4c00af..43fed1dc 100644 --- a/src/main/java/ca/spottedleaf/starlight/mixin/chunk/ProtoChunkMixin.java +++ b/src/main/java/ca/spottedleaf/starlight/mixin/common/chunk/ProtoChunkMixin.java @@ -1,4 +1,4 @@ -package ca.spottedleaf.starlight.mixin.chunk; +package ca.spottedleaf.starlight.mixin.common.chunk; import ca.spottedleaf.starlight.common.light.StarLightEngine; import ca.spottedleaf.starlight.common.chunk.ExtendedChunk; diff --git a/src/main/java/ca/spottedleaf/starlight/mixin/chunk/ReadOnlyChunkMixin.java b/src/main/java/ca/spottedleaf/starlight/mixin/common/chunk/ReadOnlyChunkMixin.java similarity index 96% rename from src/main/java/ca/spottedleaf/starlight/mixin/chunk/ReadOnlyChunkMixin.java rename to src/main/java/ca/spottedleaf/starlight/mixin/common/chunk/ReadOnlyChunkMixin.java index 45c87ea2..9972268a 100644 --- a/src/main/java/ca/spottedleaf/starlight/mixin/chunk/ReadOnlyChunkMixin.java +++ b/src/main/java/ca/spottedleaf/starlight/mixin/common/chunk/ReadOnlyChunkMixin.java @@ -1,4 +1,4 @@ -package ca.spottedleaf.starlight.mixin.chunk; +package ca.spottedleaf.starlight.mixin.common.chunk; import ca.spottedleaf.starlight.common.light.SWMRNibbleArray; import ca.spottedleaf.starlight.common.chunk.ExtendedChunk; diff --git a/src/main/java/ca/spottedleaf/starlight/mixin/chunk/WorldChunkMixin.java b/src/main/java/ca/spottedleaf/starlight/mixin/common/chunk/WorldChunkMixin.java similarity index 98% rename from src/main/java/ca/spottedleaf/starlight/mixin/chunk/WorldChunkMixin.java rename to src/main/java/ca/spottedleaf/starlight/mixin/common/chunk/WorldChunkMixin.java index 22bc105f..bda2f4c0 100644 --- a/src/main/java/ca/spottedleaf/starlight/mixin/chunk/WorldChunkMixin.java +++ b/src/main/java/ca/spottedleaf/starlight/mixin/common/chunk/WorldChunkMixin.java @@ -1,4 +1,4 @@ -package ca.spottedleaf.starlight.mixin.chunk; +package ca.spottedleaf.starlight.mixin.common.chunk; import ca.spottedleaf.starlight.common.light.SWMRNibbleArray; import ca.spottedleaf.starlight.common.light.StarLightEngine; diff --git a/src/main/java/ca/spottedleaf/starlight/mixin/lightengine/LightingProviderMixin.java b/src/main/java/ca/spottedleaf/starlight/mixin/common/lightengine/LightingProviderMixin.java similarity index 60% rename from src/main/java/ca/spottedleaf/starlight/mixin/lightengine/LightingProviderMixin.java rename to src/main/java/ca/spottedleaf/starlight/mixin/common/lightengine/LightingProviderMixin.java index 6f064e58..63498905 100644 --- a/src/main/java/ca/spottedleaf/starlight/mixin/lightengine/LightingProviderMixin.java +++ b/src/main/java/ca/spottedleaf/starlight/mixin/common/lightengine/LightingProviderMixin.java @@ -1,4 +1,4 @@ -package ca.spottedleaf.starlight.mixin.lightengine; +package ca.spottedleaf.starlight.mixin.common.lightengine; import ca.spottedleaf.starlight.common.light.StarLightInterface; import ca.spottedleaf.starlight.common.light.StarLightLightingProvider; @@ -6,7 +6,10 @@ import ca.spottedleaf.starlight.common.chunk.ExtendedChunk; import ca.spottedleaf.starlight.common.light.SWMRNibbleArray; import ca.spottedleaf.starlight.common.light.StarLightEngine; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.shorts.ShortOpenHashSet; +import net.minecraft.server.world.ServerLightingProvider; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.ChunkSectionPos; @@ -14,7 +17,6 @@ import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.ChunkNibbleArray; import net.minecraft.world.chunk.ChunkProvider; -import net.minecraft.world.chunk.ChunkSection; import net.minecraft.world.chunk.light.ChunkLightingView; import net.minecraft.world.chunk.light.LightingProvider; import net.minecraft.world.chunk.light.LightingView; @@ -24,6 +26,7 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import java.util.Iterator; @Mixin(LightingProvider.class) public abstract class LightingProviderMixin implements LightingView, StarLightLightingProvider { @@ -36,6 +39,10 @@ public final StarLightInterface getLightEngine() { return this.lightEngine; } + /** + * + * TODO since this is a constructor inject, check on update for new constructors + */ @Inject( method = "", at = @At("TAIL") ) @@ -45,6 +52,7 @@ public void construct(final ChunkProvider chunkProvider, final boolean hasBlockL } /** + * @reason Route to new light engine * @author Spottedleaf */ @Overwrite @@ -53,6 +61,7 @@ public void checkBlock(final BlockPos pos) { } /** + * @reason Avoid messing with vanilla light engine state * @author Spottedleaf */ @Overwrite @@ -61,6 +70,7 @@ public void addLightSource(final BlockPos pos, final int level) { } /** + * @reason Route to new light engine * @author Spottedleaf */ @Overwrite @@ -70,17 +80,52 @@ public boolean hasUpdates() { } /** + * @reason Hook into new light engine for light updates * @author Spottedleaf */ @Overwrite public int doLightUpdates(final int maxUpdateCount, final boolean doSkylight, final boolean skipEdgeLightPropagation) { // replace impl - final boolean hadUpdates = this.hasUpdates(); - this.lightEngine.propagateChanges(); - return hadUpdates ? 1 : 0; + if ((Object)this instanceof ServerLightingProvider) { + // serverside + final boolean hadUpdates = this.hasUpdates(); + this.lightEngine.propagateChanges(); + return hadUpdates ? 1 : 0; + } else { + // clientside + final boolean hadUpdates = this.hasUpdates() || !this.queuedChunkLoads.isEmpty(); + for (final Iterator> iterator = this.queuedChunkLoads.long2ObjectEntrySet().fastIterator(); iterator.hasNext();) { + final Long2ObjectMap.Entry entry = iterator.next(); + final long coordinate = entry.getLongKey(); + final Boolean[] emptiness = entry.getValue(); + iterator.remove(); + + this.lightEngine.loadInChunk(CoordinateUtils.getChunkX(coordinate), CoordinateUtils.getChunkZ(coordinate), emptiness); + } + for (final Iterator> iterator = this.queuedEdgeChecksSky.long2ObjectEntrySet().fastIterator(); iterator.hasNext();) { + final Long2ObjectMap.Entry entry = iterator.next(); + final long coordinate = entry.getLongKey(); + final ShortOpenHashSet sections = entry.getValue(); + iterator.remove(); + + this.lightEngine.checkSkyEdges(CoordinateUtils.getChunkX(coordinate), CoordinateUtils.getChunkZ(coordinate), sections); + } + for (final Iterator> iterator = this.queuedEdgeChecksBlock.long2ObjectEntrySet().fastIterator(); iterator.hasNext();) { + final Long2ObjectMap.Entry entry = iterator.next(); + final long coordinate = entry.getLongKey(); + final ShortOpenHashSet sections = entry.getValue(); + iterator.remove(); + + this.lightEngine.checkBlockEdges(CoordinateUtils.getChunkX(coordinate), CoordinateUtils.getChunkZ(coordinate), sections); + } + + this.lightEngine.propagateChanges(); + return hadUpdates ? 1 : 0; + } } /** + * @reason New light engine hook for handling empty section changes * @author Spottedleaf */ @Overwrite @@ -94,7 +139,17 @@ public void setSectionStatus(final ChunkSectionPos pos, final boolean notReady) @Unique protected final Long2ObjectOpenHashMap skyLight = new Long2ObjectOpenHashMap<>(); + @Unique + protected final Long2ObjectOpenHashMap queuedChunkLoads = new Long2ObjectOpenHashMap<>(); + + @Unique + protected final Long2ObjectOpenHashMap queuedEdgeChecksBlock = new Long2ObjectOpenHashMap<>(); + + @Unique + protected final Long2ObjectOpenHashMap queuedEdgeChecksSky = new Long2ObjectOpenHashMap<>(); + /** + * @reason Replace hook clientside for enabling light data, we use it for loading in light data previously queued * @author Spottedleaf */ @Overwrite @@ -110,15 +165,18 @@ public void setColumnEnabled(final ChunkPos pos, final boolean lightEnabled) { ((ExtendedChunk)chunk).setSkyNibbles(skyNibbles); } - // TODO queue this shit, yo - this.getLightEngine().loadInChunk(pos.x, pos.z, StarLightEngine.getEmptySectionsForChunk(chunk)); + this.queuedChunkLoads.put(CoordinateUtils.getChunkKey(pos), StarLightEngine.getEmptySectionsForChunk(chunk)); } else if (!lightEnabled) { this.blockLight.remove(CoordinateUtils.getChunkKey(pos)); this.skyLight.remove(CoordinateUtils.getChunkKey(pos)); + this.queuedChunkLoads.remove(CoordinateUtils.getChunkKey(pos)); + this.queuedEdgeChecksBlock.remove(CoordinateUtils.getChunkKey(pos)); + this.queuedEdgeChecksSky.remove(CoordinateUtils.getChunkKey(pos)); } } /** + * @reason Replace light views with our own that hook into the new light engine instead of vanilla's * @author Spottedleaf */ @Overwrite @@ -127,16 +185,17 @@ public ChunkLightingView get(final LightType lightType) { } /** + * @reason Data is loaded in differently on the client * @author Spottedleaf */ @Overwrite public void enqueueSectionData(final LightType lightType, final ChunkSectionPos pos, final ChunkNibbleArray nibble, - final boolean bl) { + final boolean trustEdges) { // data storage changed with new light impl final Chunk chunk = this.getLightEngine().getAnyChunkNow(pos.getX(), pos.getZ()); switch (lightType) { case BLOCK: { - SWMRNibbleArray[] blockNibbles = this.blockLight.computeIfAbsent(CoordinateUtils.getChunkKey(pos), (long keyInMap) -> { + final SWMRNibbleArray[] blockNibbles = this.blockLight.computeIfAbsent(CoordinateUtils.getChunkKey(pos), (final long keyInMap) -> { return StarLightEngine.getFilledEmptyLight(); }); @@ -146,7 +205,7 @@ public void enqueueSectionData(final LightType lightType, final ChunkSectionPos } else if (nibble.isUninitialized()) { replacement = new SWMRNibbleArray(); } else { - replacement = new SWMRNibbleArray(nibble.asByteArray()); + replacement = new SWMRNibbleArray(nibble.asByteArray().clone()); // make sure we don't write to the parameter later } blockNibbles[pos.getY() + 1] = replacement; @@ -158,7 +217,7 @@ public void enqueueSectionData(final LightType lightType, final ChunkSectionPos break; } case SKY: { - SWMRNibbleArray[] skyNibbles = this.skyLight.computeIfAbsent(CoordinateUtils.getChunkKey(pos), (long keyInMap) -> { + final SWMRNibbleArray[] skyNibbles = this.skyLight.computeIfAbsent(CoordinateUtils.getChunkKey(pos), (final long keyInMap) -> { return StarLightEngine.getFilledEmptyLight(); }); @@ -168,7 +227,7 @@ public void enqueueSectionData(final LightType lightType, final ChunkSectionPos } else if (nibble.isUninitialized()) { replacement = new SWMRNibbleArray(); } else { - replacement = new SWMRNibbleArray(nibble.asByteArray()); + replacement = new SWMRNibbleArray(nibble.asByteArray().clone()); // make sure we don't write to the parameter later } skyNibbles[pos.getY() + 1] = replacement; @@ -183,6 +242,7 @@ public void enqueueSectionData(final LightType lightType, final ChunkSectionPos } /** + * @reason Avoid messing with the vanilla light engine state * @author Spottedleaf */ @Overwrite @@ -191,6 +251,7 @@ public void setRetainData(final ChunkPos pos, final boolean retainData) { } /** + * @reason Need to use our own hooks for retrieving light data * @author Spottedleaf */ @Overwrite diff --git a/src/main/java/ca/spottedleaf/starlight/mixin/lightengine/ServerLightingProviderMixin.java b/src/main/java/ca/spottedleaf/starlight/mixin/common/lightengine/ServerLightingProviderMixin.java similarity index 66% rename from src/main/java/ca/spottedleaf/starlight/mixin/lightengine/ServerLightingProviderMixin.java rename to src/main/java/ca/spottedleaf/starlight/mixin/common/lightengine/ServerLightingProviderMixin.java index e78a8571..14a0eae6 100644 --- a/src/main/java/ca/spottedleaf/starlight/mixin/lightengine/ServerLightingProviderMixin.java +++ b/src/main/java/ca/spottedleaf/starlight/mixin/common/lightengine/ServerLightingProviderMixin.java @@ -1,17 +1,14 @@ -package ca.spottedleaf.starlight.mixin.lightengine; +package ca.spottedleaf.starlight.mixin.common.lightengine; import ca.spottedleaf.starlight.common.light.StarLightEngine; import ca.spottedleaf.starlight.common.light.StarLightInterface; import ca.spottedleaf.starlight.common.light.StarLightLightingProvider; -import net.minecraft.server.world.ChunkTaskPrioritySystem; import net.minecraft.server.world.ServerLightingProvider; import net.minecraft.server.world.ServerWorld; import net.minecraft.server.world.ThreadedAnvilChunkStorage; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.ChunkSectionPos; -import net.minecraft.util.thread.MessageListener; -import net.minecraft.util.thread.TaskExecutor; import net.minecraft.world.LightType; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.ChunkNibbleArray; @@ -25,9 +22,6 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.IntSupplier; @Mixin(ServerLightingProvider.class) public abstract class ServerLightingProviderMixin extends LightingProvider implements StarLightLightingProvider { @@ -36,54 +30,18 @@ public abstract class ServerLightingProviderMixin extends LightingProvider imple @Shadow private ThreadedAnvilChunkStorage chunkStorage; - @Final - @Shadow - private MessageListener> executor; - - @Final - @Shadow - private AtomicBoolean ticking; - - @Final - @Shadow - private TaskExecutor processor; - @Final @Shadow private static Logger LOGGER; - @Unique - protected final ConcurrentLinkedQueue preTasks = new ConcurrentLinkedQueue<>(); - - @Unique - protected final ConcurrentLinkedQueue postTasks = new ConcurrentLinkedQueue<>(); - - @Unique - private void enqueue(final int x, final int z, final Runnable task) { - this.enqueue(x, z, task, false); - } - - @Unique - private void enqueue(final int x, final int z, final Runnable task, final boolean postTask) { - this.enqueue(x, z, this.chunkStorage.getCompletedLevelSupplier(ChunkPos.toLong(x, z)), postTask, task); - } - - @Unique - private void enqueue(final int x, final int z, final IntSupplier completedLevelSupplier, final boolean postTask, - final Runnable task) { - this.executor.send(ChunkTaskPrioritySystem.createMessage(() -> { - if (postTask) { - this.postTasks.add(task); - } else { - this.preTasks.add(task); - } - }, ChunkPos.toLong(x, z), completedLevelSupplier)); - } + @Shadow + protected abstract void enqueue(final int x, final int z, final ServerLightingProvider.Stage stage, final Runnable task); public ServerLightingProviderMixin(final ChunkProvider chunkProvider, final boolean hasBlockLight, final boolean hasSkyLight) { super(chunkProvider, hasBlockLight, hasSkyLight); } + @Unique private long workTicketCounts = 0L; @Unique @@ -92,6 +50,14 @@ private void queueTaskForSection(final int chunkX, final int chunkY, final int c final ServerWorld world = (ServerWorld)this.getLightEngine().getWorld(); + if (!world.getChunkManager().threadedAnvilChunkStorage.mainThreadExecutor.isOnThread()) { + // this is not safe to run off-main, re-schedule + world.getChunkManager().threadedAnvilChunkStorage.mainThreadExecutor.execute(() -> { + this.queueTaskForSection(chunkX, chunkY, chunkZ, runnable); + }); + return; + } + Chunk center = this.getLightEngine().getAnyChunkNow(chunkX, chunkZ); if (center == null || !center.getStatus().isAtLeast(ChunkStatus.LIGHT) || !center.isLightOn()) { // do not accept updates in unlit chunks @@ -109,17 +75,19 @@ private void queueTaskForSection(final int chunkX, final int chunkY, final int c world.getChunkManager().addTicket(StarLightInterface.CHUNK_WORK_TICKET, new ChunkPos(chunkX, chunkZ), 0, ticketId); - this.enqueue(chunkX, chunkZ, () -> { + this.enqueue(chunkX, chunkZ, ServerLightingProvider.Stage.PRE_UPDATE, () -> { runnable.run(); - this.enqueue(chunkX, chunkZ, () -> { + this.enqueue(chunkX, chunkZ, ServerLightingProvider.Stage.POST_UPDATE, () -> { world.getChunkManager().threadedAnvilChunkStorage.mainThreadExecutor.execute(() -> { world.getChunkManager().removeTicket(StarLightInterface.CHUNK_WORK_TICKET, new ChunkPos(chunkX, chunkZ), 0, ticketId); }); - }, true); + }); }); } /** + * @reason Redirect scheduling call away from the vanilla light engine, as well as enforce + * that chunk neighbours are loaded before the processing can occur * @author Spottedleaf */ @Overwrite @@ -131,14 +99,16 @@ public void checkBlock(final BlockPos pos) { } /** + * @reason Avoid messing with the vanilla light engine state * @author Spottedleaf */ @Overwrite - public void updateChunkStatus(final ChunkPos pos) { - // Do nothing, we don't care - } + public void updateChunkStatus(final ChunkPos pos) {} /** + * @reason Redirect to schedule for our own logic, as well as ensure 1 radius neighbours + * are loaded + * Note: Our scheduling logic will discard this call if the chunk is not lit, unloaded, or not at LIGHT stage yet. * @author Spottedleaf */ @Overwrite @@ -149,6 +119,7 @@ public void setSectionStatus(final ChunkSectionPos pos, final boolean notReady) } /** + * @reason Avoid messing with the vanilla light engine state * @author Spottedleaf */ @Overwrite @@ -157,15 +128,18 @@ public void setColumnEnabled(final ChunkPos pos, final boolean lightEnabled) { } /** + * @reason Light data is now attached to chunks, and this means we need to hook into chunk loading logic + * to load the data rather than rely on this call. This call also would mess with the vanilla light engine state. * @author Spottedleaf */ @Overwrite public void enqueueSectionData(final LightType lightType, final ChunkSectionPos pos, final ChunkNibbleArray nibbles, final boolean bl) { - // hook for loading light data is changed, as chunk is no longer loaded at this stage + // load hooks inside ChunkSerializer } /** + * @reason Avoid messing with the vanilla light engine state * @author Spottedleaf */ @Overwrite @@ -174,34 +148,7 @@ public void setRetainData(final ChunkPos pos, final boolean retainData) { } /** - * @author Spottedleaf - */ - @Overwrite - public void tick() { - if ((!this.preTasks.isEmpty() || !this.postTasks.isEmpty() || super.hasUpdates()) && this.ticking.compareAndSet(false, true)) { - this.processor.send(() -> { - this.runTasks(); - this.ticking.set(false); - this.tick(); - }); - } - } - /** - * @author Spottedleaf - */ - @Overwrite - private void runTasks() { - Runnable task; - while ((task = this.preTasks.poll()) != null) { - task.run(); - } - this.getLightEngine().propagateChanges(); - while ((task = this.postTasks.poll()) != null) { - task.run(); - } - } - - /** + * @reason Route to new logic to either light or just load the data * @author Spottedleaf */ @Overwrite @@ -220,7 +167,7 @@ public CompletableFuture light(final Chunk chunk, final boolean lit) { this.chunkStorage.releaseLightTicket(chunkPos); return chunk; }, (runnable) -> { - this.enqueue(chunkPos.x, chunkPos.z, runnable); + this.enqueue(chunkPos.x, chunkPos.z, ServerLightingProvider.Stage.PRE_UPDATE, runnable); }).whenComplete((final Chunk c, final Throwable throwable) -> { if (throwable != null) { LOGGER.fatal("Failed to light chunk " + chunkPos, throwable); diff --git a/src/main/java/ca/spottedleaf/starlight/mixin/world/ChunkSerializerMixin.java b/src/main/java/ca/spottedleaf/starlight/mixin/common/world/ChunkSerializerMixin.java similarity index 91% rename from src/main/java/ca/spottedleaf/starlight/mixin/world/ChunkSerializerMixin.java rename to src/main/java/ca/spottedleaf/starlight/mixin/common/world/ChunkSerializerMixin.java index 92063b06..f6282fd5 100644 --- a/src/main/java/ca/spottedleaf/starlight/mixin/world/ChunkSerializerMixin.java +++ b/src/main/java/ca/spottedleaf/starlight/mixin/common/world/ChunkSerializerMixin.java @@ -1,4 +1,4 @@ -package ca.spottedleaf.starlight.mixin.world; +package ca.spottedleaf.starlight.mixin.common.world; import ca.spottedleaf.starlight.common.light.StarLightEngine; import ca.spottedleaf.starlight.common.chunk.ExtendedChunk; @@ -25,7 +25,8 @@ public abstract class ChunkSerializerMixin { private static final int STARLIGHT_LIGHT_VERSION = 1; - private static final String UNINITIALISED_SKYLIGHT_TAG = "starlight.skylight_"; + private static final String UNINITIALISED_SKYLIGHT_TAG = "starlight.skylight_uninit"; + private static final String STARLIGHT_VERSION_TAG = "starlight.light_version"; /** * Overwrites vanilla's light data with our own. @@ -51,7 +52,7 @@ private static void saveLightHook(final ServerWorld world, final Chunk chunk, fi // diff start - store our tag for whether light data is init'd if (lit) { level.putBoolean("isLightOn", false); - level.putInt("starlight.light_versiom", STARLIGHT_LIGHT_VERSION); + level.putInt(STARLIGHT_VERSION_TAG, STARLIGHT_LIGHT_VERSION); } // diff end - store our tag for whether light data is init'd ChunkStatus status = ChunkStatus.byId(level.getString("Status")); @@ -90,10 +91,8 @@ private static void saveLightHook(final ServerWorld world, final Chunk chunk, fi } if (skyNibble != null) { - // for skylight compatibility with vanilla, we store our data in a different key. if (skyNibble.isUninitialized()) { - // different key lets use do this - section.putBoolean("starlight.skylight_uninit", true); + section.putBoolean(UNINITIALISED_SKYLIGHT_TAG, true); } else { // we store under the same key so mod programs editing nbt // can still read the data, hopefully. @@ -140,7 +139,7 @@ private static void loadLightHook(final ServerWorld world, final StructureManage // start copy from from the original method CompoundTag levelTag = tag.getCompound("Level"); - boolean lit = levelTag.getInt("starlight.light_versiom") == STARLIGHT_LIGHT_VERSION; ret.setLightOn(lit); // diff - override lit with our value + boolean lit = levelTag.getInt(STARLIGHT_VERSION_TAG) == STARLIGHT_LIGHT_VERSION; ret.setLightOn(lit); // diff - override lit with our value boolean canReadSky = world.getDimension().hasSkyLight(); ChunkStatus status = ChunkStatus.byId(tag.getCompound("Level").getString("Status")); if (lit && status.isAtLeast(ChunkStatus.LIGHT)) { // diff - we add the status check here @@ -156,7 +155,6 @@ private static void loadLightHook(final ServerWorld world, final StructureManage } if (canReadSky) { - // for skylight compatibility with vanilla, we store our data in a different key. if (sectionData.contains("SkyLight", 7)) { // we store under the same key so mod programs editing nbt // can still read the data, hopefully. @@ -164,7 +162,7 @@ private static void loadLightHook(final ServerWorld world, final StructureManage // is forced to re-light them if it encounters our data. It's too much of a burden // to try and maintain compatibility with a broken and inferior skylight management system. skyNibbles[y - minSection] = new SWMRNibbleArray(sectionData.getByteArray("SkyLight").clone()); // clone for data safety - } else if (sectionData.getBoolean("starlight.skylight_uninit")) { + } else if (sectionData.getBoolean(UNINITIALISED_SKYLIGHT_TAG)) { skyNibbles[y - minSection] = new SWMRNibbleArray(); } } diff --git a/src/main/java/ca/spottedleaf/starlight/mixin/world/ServerWorldMixin.java b/src/main/java/ca/spottedleaf/starlight/mixin/common/world/ServerWorldMixin.java similarity index 97% rename from src/main/java/ca/spottedleaf/starlight/mixin/world/ServerWorldMixin.java rename to src/main/java/ca/spottedleaf/starlight/mixin/common/world/ServerWorldMixin.java index 36647083..f8a89f5a 100644 --- a/src/main/java/ca/spottedleaf/starlight/mixin/world/ServerWorldMixin.java +++ b/src/main/java/ca/spottedleaf/starlight/mixin/common/world/ServerWorldMixin.java @@ -1,4 +1,4 @@ -package ca.spottedleaf.starlight.mixin.world; +package ca.spottedleaf.starlight.mixin.common.world; import ca.spottedleaf.starlight.common.util.CoordinateUtils; import ca.spottedleaf.starlight.common.world.ExtendedWorld; diff --git a/src/main/java/ca/spottedleaf/starlight/mixin/world/WorldMixin.java b/src/main/java/ca/spottedleaf/starlight/mixin/common/world/WorldMixin.java similarity index 84% rename from src/main/java/ca/spottedleaf/starlight/mixin/world/WorldMixin.java rename to src/main/java/ca/spottedleaf/starlight/mixin/common/world/WorldMixin.java index 8050a002..626b0b3b 100644 --- a/src/main/java/ca/spottedleaf/starlight/mixin/world/WorldMixin.java +++ b/src/main/java/ca/spottedleaf/starlight/mixin/common/world/WorldMixin.java @@ -1,4 +1,4 @@ -package ca.spottedleaf.starlight.mixin.world; +package ca.spottedleaf.starlight.mixin.common.world; import ca.spottedleaf.starlight.common.world.ExtendedWorld; import net.minecraft.world.World; diff --git a/src/main/resources/starlight.accesswidener b/src/main/resources/starlight.accesswidener index b6e8b7ec..99e8e03c 100644 --- a/src/main/resources/starlight.accesswidener +++ b/src/main/resources/starlight.accesswidener @@ -23,4 +23,8 @@ accessible field net/minecraft/server/world/ThreadedAnvilChunkStorage mainThread accessible method net/minecraft/server/world/ThreadedAnvilChunkStorage getCurrentChunkHolder (J)Lnet/minecraft/server/world/ChunkHolder; accessible method net/minecraft/server/world/ThreadedAnvilChunkStorage getChunkHolder (J)Lnet/minecraft/server/world/ChunkHolder; accessible method net/minecraft/server/world/ThreadedAnvilChunkStorage getCompletedLevelSupplier (J)Ljava/util/function/IntSupplier; -accessible method net/minecraft/server/world/ThreadedAnvilChunkStorage releaseLightTicket (Lnet/minecraft/util/math/ChunkPos;)V \ No newline at end of file +accessible method net/minecraft/server/world/ThreadedAnvilChunkStorage releaseLightTicket (Lnet/minecraft/util/math/ChunkPos;)V + + +# ServerLightingProvider +accessible class net/minecraft/server/world/ServerLightingProvider$Stage diff --git a/src/main/resources/starlight.mixins.json b/src/main/resources/starlight.mixins.json index 815e83f5..04f5e4a1 100644 --- a/src/main/resources/starlight.mixins.json +++ b/src/main/resources/starlight.mixins.json @@ -4,21 +4,21 @@ "package": "ca.spottedleaf.starlight.mixin", "compatibilityLevel": "JAVA_8", "mixins": [ - "blockstate.AbstractBlockStateMixin", - "chunk.ChunkMixin", - "chunk.ChunkSectionMixin", - "chunk.EmptyChunkMixin", - "chunk.ProtoChunkMixin", - "chunk.ReadOnlyChunkMixin", - "chunk.WorldChunkMixin", - "lightengine.LightingProviderMixin", - "lightengine.ServerLightingProviderMixin", - "world.ChunkSerializerMixin", - "world.ServerWorldMixin", - "world.WorldMixin" + "common.blockstate.AbstractBlockStateMixin", + "common.chunk.ChunkMixin", + "common.chunk.ChunkSectionMixin", + "common.chunk.EmptyChunkMixin", + "common.chunk.ProtoChunkMixin", + "common.chunk.ReadOnlyChunkMixin", + "common.chunk.WorldChunkMixin", + "common.lightengine.LightingProviderMixin", + "common.lightengine.ServerLightingProviderMixin", + "common.world.ChunkSerializerMixin", + "common.world.ServerWorldMixin", + "common.world.WorldMixin" ], "client": [ - "world.ClientWorldMixin" + "client.world.ClientWorldMixin", ], "injectors": { "defaultRequire": 1