diff --git a/src/main/java/me/jellysquid/mods/phosphor/mixin/chunk/MixinProtoChunk.java b/src/main/java/me/jellysquid/mods/phosphor/mixin/chunk/MixinProtoChunk.java new file mode 100644 index 00000000..68130394 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/phosphor/mixin/chunk/MixinProtoChunk.java @@ -0,0 +1,62 @@ +package me.jellysquid.mods.phosphor.mixin.chunk; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.chunk.ChunkSection; +import net.minecraft.world.chunk.ChunkStatus; +import net.minecraft.world.chunk.ProtoChunk; +import net.minecraft.world.chunk.light.LightingProvider; + +@Mixin(ProtoChunk.class) +public abstract class MixinProtoChunk { + @Shadow + public abstract LightingProvider getLightingProvider(); + + @Shadow + public abstract ChunkStatus getStatus(); + + @Unique + private static final ChunkStatus PRE_LIGHT = ChunkStatus.LIGHT.getPrevious(); + + @Inject( + method = "setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Z)Lnet/minecraft/block/BlockState;", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/chunk/ChunkSection;setBlockState(IIILnet/minecraft/block/BlockState;)Lnet/minecraft/block/BlockState;" + ), + locals = LocalCapture.CAPTURE_FAILHARD + ) + private void addLightmap(final BlockPos pos, final BlockState state, final boolean moved, final CallbackInfoReturnable ci, final int x, final int y, final int z, final ChunkSection section) { + if (this.getStatus().isAtLeast(PRE_LIGHT) && ChunkSection.isEmpty(section)) { + this.getLightingProvider().updateSectionStatus(pos, false); + } + } + + @Inject( + method = "setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Z)Lnet/minecraft/block/BlockState;", + slice = @Slice( + from = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/chunk/ChunkSection;setBlockState(IIILnet/minecraft/block/BlockState;)Lnet/minecraft/block/BlockState;" + ) + ), + at = @At( + value = "RETURN", + ordinal = 0 + ), + locals = LocalCapture.CAPTURE_FAILHARD + ) + private void removeLightmap(final BlockPos pos, final BlockState state, final boolean moved, final CallbackInfoReturnable ci, final int x, final int y, final int z, final ChunkSection section) { + if (this.getStatus().isAtLeast(PRE_LIGHT) && ChunkSection.isEmpty(section)) { + this.getLightingProvider().updateSectionStatus(pos, true); + } + } +} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mixin/chunk/MixinWorldChunk.java b/src/main/java/me/jellysquid/mods/phosphor/mixin/chunk/MixinWorldChunk.java index 57548732..f521e27c 100644 --- a/src/main/java/me/jellysquid/mods/phosphor/mixin/chunk/MixinWorldChunk.java +++ b/src/main/java/me/jellysquid/mods/phosphor/mixin/chunk/MixinWorldChunk.java @@ -5,10 +5,13 @@ import net.minecraft.util.math.ChunkPos; import net.minecraft.world.chunk.ChunkSection; import net.minecraft.world.chunk.WorldChunk; +import net.minecraft.world.chunk.light.LightingProvider; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; import java.util.ArrayList; import java.util.List; @@ -67,4 +70,14 @@ public Stream getLightSourcesStream() { return list.stream(); } + + @Redirect( + method = "setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Z)Lnet/minecraft/block/BlockState;", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/chunk/light/LightingProvider;updateSectionStatus(Lnet/minecraft/util/math/BlockPos;Z)V" + ) + ) + private void disablelLightmapHandling(final LightingProvider lightingProvider, final BlockPos pos, final boolean status) { + } } diff --git a/src/main/java/me/jellysquid/mods/phosphor/mixin/chunk/light/MixinServerLightingProvider.java b/src/main/java/me/jellysquid/mods/phosphor/mixin/chunk/light/MixinServerLightingProvider.java new file mode 100644 index 00000000..6fb2f31a --- /dev/null +++ b/src/main/java/me/jellysquid/mods/phosphor/mixin/chunk/light/MixinServerLightingProvider.java @@ -0,0 +1,58 @@ +package me.jellysquid.mods.phosphor.mixin.chunk.light; + +import java.util.function.IntSupplier; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import net.minecraft.server.world.ServerLightingProvider; +import net.minecraft.util.Util; +import net.minecraft.util.math.ChunkSectionPos; +import net.minecraft.world.chunk.ChunkProvider; +import net.minecraft.world.chunk.light.LightingProvider; + +@Mixin(ServerLightingProvider.class) +public abstract class MixinServerLightingProvider extends LightingProvider +{ + private MixinServerLightingProvider(final ChunkProvider chunkProvider, final boolean hasBlockLight, final boolean hasSkyLight) { + super(chunkProvider, hasBlockLight, hasSkyLight); + } + + @Shadow + protected abstract void enqueue(int x, int z, IntSupplier completedLevelSupplier, ServerLightingProvider.Stage stage, Runnable task); + + @Shadow + protected abstract void enqueue(int x, int z, ServerLightingProvider.Stage stage, Runnable task); + + /** + * @author PhiPro + * @reason Re-implement + */ + @Overwrite + public void updateSectionStatus(final ChunkSectionPos pos, final boolean empty) + { + if (empty) { + // Schedule after light updates have been carried out + this.enqueue(pos.getSectionX(), pos.getSectionZ(), ServerLightingProvider.Stage.POST_UPDATE, Util.debugRunnable(() -> { + super.updateSectionStatus(pos, true); + }, + () -> "updateSectionStatus " + pos + " " + true + )); + } else { + // Schedule before light updates are carried out + this.enqueue(pos.getSectionX(), pos.getSectionZ(), () -> 0, ServerLightingProvider.Stage.PRE_UPDATE, Util.debugRunnable(() -> { + super.updateSectionStatus(pos, false); + }, + () -> "updateSectionStatus " + pos + " " + false + )); + + // Schedule another version in POST_UPDATE to achieve reliable final state + this.enqueue(pos.getSectionX(), pos.getSectionZ(), ServerLightingProvider.Stage.POST_UPDATE, Util.debugRunnable(() -> { + super.updateSectionStatus(pos, false); + }, + () -> "updateSectionStatus " + pos + " " + false + )); + } + } +} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mixin/world/MixinWorld.java b/src/main/java/me/jellysquid/mods/phosphor/mixin/world/MixinWorld.java new file mode 100644 index 00000000..5984f91a --- /dev/null +++ b/src/main/java/me/jellysquid/mods/phosphor/mixin/world/MixinWorld.java @@ -0,0 +1,48 @@ +package me.jellysquid.mods.phosphor.mixin.world; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.WorldAccess; +import net.minecraft.world.chunk.ChunkSection; +import net.minecraft.world.chunk.WorldChunk; + +@Mixin(World.class) +public abstract class MixinWorld implements WorldAccess { + @Inject( + method = "setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;II)Z", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/chunk/WorldChunk;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Z)Lnet/minecraft/block/BlockState;" + ), + locals = LocalCapture.CAPTURE_FAILHARD + ) + private void addLightmap(final BlockPos pos, final BlockState state, final int flags, final int maxUpdateDepth, final CallbackInfoReturnable ci, final WorldChunk chunk) { + if (ChunkSection.isEmpty(chunk.getSectionArray()[pos.getY() >> 4])) { + this.getChunkManager().getLightingProvider().updateSectionStatus(pos, false); + } + } + + @Inject( + method = "setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;II)Z", + slice = @Slice( + from = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/chunk/WorldChunk;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Z)Lnet/minecraft/block/BlockState;" + ) + ), + at = @At("RETURN"), + locals = LocalCapture.CAPTURE_FAILHARD + ) + private void removeLightmap(final BlockPos pos, final BlockState state, final int flags, final int maxUpdateDepth, final CallbackInfoReturnable ci, final WorldChunk chunk) { + if (ChunkSection.isEmpty(chunk.getSectionArray()[pos.getY() >> 4])) { + this.getChunkManager().getLightingProvider().updateSectionStatus(pos, true); + } + } +} diff --git a/src/main/resources/phosphor.accesswidener b/src/main/resources/phosphor.accesswidener index 12dce3f4..a4083a37 100644 --- a/src/main/resources/phosphor.accesswidener +++ b/src/main/resources/phosphor.accesswidener @@ -2,6 +2,8 @@ accessWidener v1 named accessible class net/minecraft/block/AbstractBlock$AbstractBlockState$ShapeCache +accessible class net/minecraft/server/world/ServerLightingProvider$Stage + accessible method net/minecraft/world/chunk/light/LightStorage hasLight (J)Z accessible method net/minecraft/world/chunk/light/SkyLightStorage method_15565 (J)Z accessible method net/minecraft/world/chunk/light/SkyLightStorage isAboveMinHeight (I)Z diff --git a/src/main/resources/phosphor.mixins.json b/src/main/resources/phosphor.mixins.json index 6d4ac910..f1120e86 100644 --- a/src/main/resources/phosphor.mixins.json +++ b/src/main/resources/phosphor.mixins.json @@ -15,7 +15,10 @@ "chunk.light.MixinLevelPropagator", "chunk.light.MixinLightStorage", "chunk.light.MixinSkyLightStorage", - "chunk.light.MixinSkyLightStorageData" + "chunk.light.MixinSkyLightStorageData", + "chunk.light.MixinServerLightingProvider", + "world.MixinWorld", + "chunk.MixinProtoChunk" ], "injectors": { "defaultRequire": 1