diff --git a/common/src/main/java/io/github/steveplays28/noisium/extension/world/chunk/NoisiumWorldChunkExtension.java b/common/src/main/java/io/github/steveplays28/noisium/extension/world/chunk/NoisiumWorldChunkExtension.java new file mode 100644 index 0000000..ff4cc9a --- /dev/null +++ b/common/src/main/java/io/github/steveplays28/noisium/extension/world/chunk/NoisiumWorldChunkExtension.java @@ -0,0 +1,9 @@ +package io.github.steveplays28.noisium.extension.world.chunk; + +import java.util.BitSet; + +public interface NoisiumWorldChunkExtension { + BitSet noisium$getBlockLightBits(); + + BitSet noisium$getSkyLightBits(); +} diff --git a/common/src/main/java/io/github/steveplays28/noisium/mixin/server/world/ServerChunkManagerMixin.java b/common/src/main/java/io/github/steveplays28/noisium/mixin/server/world/ServerChunkManagerMixin.java index 98b7b4a..f15ed0e 100644 --- a/common/src/main/java/io/github/steveplays28/noisium/mixin/server/world/ServerChunkManagerMixin.java +++ b/common/src/main/java/io/github/steveplays28/noisium/mixin/server/world/ServerChunkManagerMixin.java @@ -1,6 +1,7 @@ package io.github.steveplays28.noisium.mixin.server.world; import io.github.steveplays28.noisium.extension.world.server.NoisiumServerWorldExtension; +import io.github.steveplays28.noisium.server.world.chunk.event.NoisiumServerChunkEvent; import io.github.steveplays28.noisium.server.world.event.NoisiumServerTickEvent; import io.github.steveplays28.noisium.util.world.chunk.networking.packet.PacketUtil; import net.minecraft.entity.Entity; @@ -11,6 +12,8 @@ import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.math.ChunkSectionPos; +import net.minecraft.world.LightType; import net.minecraft.world.World; import net.minecraft.world.chunk.WorldChunk; import org.spongepowered.asm.mixin.Mixin; @@ -42,7 +45,8 @@ public abstract class ServerChunkManagerMixin { @Inject(method = "getWorldChunk", at = @At(value = "HEAD"), cancellable = true) private void noisium$getWorldChunkFromNoisiumServerWorldChunkManager(int chunkX, int chunkZ, CallbackInfoReturnable cir) { ((NoisiumServerWorldExtension) this.getWorld()).noisium$getServerWorldChunkManager().getChunkAsync( - new ChunkPos(chunkX, chunkZ)).whenComplete((worldChunk, throwable) -> cir.setReturnValue(worldChunk)); + new ChunkPos(chunkX, chunkZ) + ).whenComplete((worldChunk, throwable) -> cir.setReturnValue(worldChunk)); } // TODO: Don't send this packet to players out of range, to save on bandwidth @@ -102,4 +106,10 @@ public abstract class ServerChunkManagerMixin { PacketUtil.sendPacketToPlayers(serverWorld.getPlayers(), new BlockUpdateS2CPacket(blockPos, serverWorld.getBlockState(blockPos))); ci.cancel(); } + + @Inject(method = "onLightUpdate", at = @At(value = "HEAD"), cancellable = true) + private void noisium$updateLightingViaNoisiumServerWorldChunkManager(LightType lightType, ChunkSectionPos chunkSectionPos, CallbackInfo ci) { + NoisiumServerChunkEvent.LIGHT_UPDATE.invoker().onLightUpdate(lightType, chunkSectionPos); + ci.cancel(); + } } diff --git a/common/src/main/java/io/github/steveplays28/noisium/mixin/world/chunk/WorldChunkMixin.java b/common/src/main/java/io/github/steveplays28/noisium/mixin/world/chunk/WorldChunkMixin.java new file mode 100644 index 0000000..fb2919b --- /dev/null +++ b/common/src/main/java/io/github/steveplays28/noisium/mixin/world/chunk/WorldChunkMixin.java @@ -0,0 +1,26 @@ +package io.github.steveplays28.noisium.mixin.world.chunk; + +import io.github.steveplays28.noisium.extension.world.chunk.NoisiumWorldChunkExtension; +import net.minecraft.world.chunk.WorldChunk; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +import java.util.BitSet; + +@Mixin(WorldChunk.class) +public class WorldChunkMixin implements NoisiumWorldChunkExtension { + @Unique + private final BitSet noisium$blockLightBits = new BitSet(); + @Unique + private final BitSet noisium$skyLightBits = new BitSet(); + + @Override + public BitSet noisium$getBlockLightBits() { + return noisium$blockLightBits; + } + + @Override + public BitSet noisium$getSkyLightBits() { + return noisium$skyLightBits; + } +} diff --git a/common/src/main/java/io/github/steveplays28/noisium/server/player/NoisiumServerPlayerBlockUpdater.java b/common/src/main/java/io/github/steveplays28/noisium/server/player/NoisiumServerPlayerBlockUpdater.java index d278c21..51bd82a 100644 --- a/common/src/main/java/io/github/steveplays28/noisium/server/player/NoisiumServerPlayerBlockUpdater.java +++ b/common/src/main/java/io/github/steveplays28/noisium/server/player/NoisiumServerPlayerBlockUpdater.java @@ -16,7 +16,7 @@ public NoisiumServerPlayerBlockUpdater() { BlockEvent.BREAK.register((world, blockPos, blockState, player, xp) -> onBlockBreak(world, blockPos)); } - private EventResult onBlockBreak(@NotNull World world, BlockPos blockPos) { + private EventResult onBlockBreak(@NotNull World world, @NotNull BlockPos blockPos) { if (world.isClient()) { return EventResult.pass(); } diff --git a/common/src/main/java/io/github/steveplays28/noisium/server/world/NoisiumServerWorldChunkManager.java b/common/src/main/java/io/github/steveplays28/noisium/server/world/NoisiumServerWorldChunkManager.java index 9a13256..8daa6a7 100644 --- a/common/src/main/java/io/github/steveplays28/noisium/server/world/NoisiumServerWorldChunkManager.java +++ b/common/src/main/java/io/github/steveplays28/noisium/server/world/NoisiumServerWorldChunkManager.java @@ -3,8 +3,8 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.mojang.datafixers.DataFixer; import io.github.steveplays28.noisium.Noisium; +import io.github.steveplays28.noisium.extension.world.chunk.NoisiumWorldChunkExtension; import io.github.steveplays28.noisium.mixin.accessor.NoiseChunkGeneratorAccessor; -import io.github.steveplays28.noisium.server.world.chunk.ServerChunkData; import io.github.steveplays28.noisium.server.world.chunk.event.NoisiumServerChunkEvent; import io.github.steveplays28.noisium.util.world.chunk.ChunkUtil; import net.minecraft.entity.EntityType; @@ -15,9 +15,11 @@ import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.ChunkSectionPos; import net.minecraft.util.math.MathHelper; -import net.minecraft.world.*; +import net.minecraft.world.ChunkRegion; +import net.minecraft.world.ChunkSerializer; +import net.minecraft.world.Heightmap; +import net.minecraft.world.LightType; import net.minecraft.world.chunk.*; -import net.minecraft.world.chunk.light.LightSourceView; import net.minecraft.world.gen.GenerationStep; import net.minecraft.world.gen.chunk.Blender; import net.minecraft.world.gen.chunk.ChunkGenerator; @@ -39,14 +41,14 @@ // TODO: Fix canTickBlockEntities() check // The check needs to be changed to point to the server world's isChunkLoaded() method // TODO: Implement chunk ticking -public class NoisiumServerWorldChunkManager implements ChunkProvider { +public class NoisiumServerWorldChunkManager { private final ServerWorld serverWorld; private final ChunkGenerator chunkGenerator; private final PointOfInterestStorage pointOfInterestStorage; private final VersionedChunkStorage versionedChunkStorage; private final NoiseConfig noiseConfig; private final Executor threadPoolExecutor; - private final Map loadedWorldChunks; + private final Map loadedWorldChunks; public NoisiumServerWorldChunkManager(@NotNull ServerWorld serverWorld, @NotNull ChunkGenerator chunkGenerator, @NotNull Path worldDirectoryPath, DataFixer dataFixer) { this.serverWorld = serverWorld; @@ -70,44 +72,13 @@ public NoisiumServerWorldChunkManager(@NotNull ServerWorld serverWorld, @NotNull serverWorld.getRegistryManager().getWrapperOrThrow(RegistryKeys.NOISE_PARAMETERS), serverWorld.getSeed() ); } - } - - @Override - public BlockView getWorld() { - return serverWorld; - } - @Nullable - @Override - public LightSourceView getChunk(int chunkX, int chunkZ) { - return getChunk(new ChunkPos(chunkX, chunkZ)); - } - - @Override - public void onLightUpdate(LightType lightType, ChunkSectionPos chunkSectionPosition) { - var lightingProvider = serverWorld.getLightingProvider(); - var chunkPos = chunkSectionPosition.toChunkPos(); - var chunkSectionYPosition = chunkSectionPosition.getSectionY(); - int bottomY = lightingProvider.getBottomY(); - int topY = lightingProvider.getTopY(); - - if (chunkSectionYPosition >= bottomY && chunkSectionYPosition <= topY) { - int yDifference = chunkSectionYPosition - bottomY; - var serverChunkData = loadedWorldChunks.get(chunkPos); - var skyLightBits = serverChunkData.skyLightBits(); - var blockLightBits = serverChunkData.skyLightBits(); - - if (lightType == LightType.SKY) { - skyLightBits.set(yDifference); - } else { - blockLightBits.set(yDifference); - } - ChunkUtil.sendLightUpdateToPlayers(serverWorld.getPlayers(), lightingProvider, chunkPos, skyLightBits, blockLightBits); - } + NoisiumServerChunkEvent.LIGHT_UPDATE.register(this::onLightUpdateAsync); } /** - * Loads the chunk at the specified position, returning the loaded chunk when done. Returns the chunk from the loadedChunks cache if available. + * Loads the chunk at the specified position, returning the loaded chunk when done. + * Returns the chunk from the {@link NoisiumServerWorldChunkManager#loadedWorldChunks} cache if available. * This method is ran asynchronously. * * @param chunkPos The position at which to load the chunk. @@ -115,7 +86,7 @@ public void onLightUpdate(LightType lightType, ChunkSectionPos chunkSectionPosit */ public @NotNull CompletableFuture getChunkAsync(ChunkPos chunkPos) { if (loadedWorldChunks.containsKey(chunkPos)) { - return CompletableFuture.completedFuture(loadedWorldChunks.get(chunkPos).worldChunk()); + return CompletableFuture.completedFuture(loadedWorldChunks.get(chunkPos)); } return CompletableFuture.supplyAsync(() -> { @@ -136,28 +107,29 @@ public void onLightUpdate(LightType lightType, ChunkSectionPos chunkSectionPosit fetchedWorldChunk.addChunkTickSchedulers(serverWorld); fetchedWorldChunk.loadEntities(); - loadedWorldChunks.put(chunkPos, new ServerChunkData(fetchedWorldChunk, (short) 0, new BitSet(), new BitSet())); + loadedWorldChunks.put(chunkPos, fetchedWorldChunk); NoisiumServerChunkEvent.WORLD_CHUNK_GENERATED.invoker().onWorldChunkGenerated(fetchedWorldChunk); }); } /** - * Loads the chunk at the specified position, returning the loaded chunk when done. Returns the chunk from the loadedChunks cache if available. - * WARNING: This method blocks the server thread. Prefer using {@link NoisiumServerWorldChunkManager#getChunk(int, int)} instead. + * Loads the chunk at the specified position, returning the loaded {@link WorldChunk} when done. + * Returns the chunk from the {@link NoisiumServerWorldChunkManager#loadedWorldChunks} cache if available. + * WARNING: This method blocks the server thread. Prefer using {@link NoisiumServerWorldChunkManager#getChunkAsync} instead. * - * @param chunkPos The position at which to load the chunk. - * @return The loaded chunk. + * @param chunkPos The position at which to load the {@link WorldChunk}. + * @return The loaded {@link WorldChunk}. */ - public @Nullable WorldChunk getChunk(ChunkPos chunkPos) { + public @NotNull WorldChunk getChunk(ChunkPos chunkPos) { if (loadedWorldChunks.containsKey(chunkPos)) { - return loadedWorldChunks.get(chunkPos).worldChunk(); + return loadedWorldChunks.get(chunkPos); } var fetchedNbtData = getNbtDataAtChunkPosition(chunkPos); if (fetchedNbtData == null) { // TODO: Schedule ProtoChunk worldgen and update loadedWorldChunks incrementally during worldgen steps var fetchedWorldChunk = new WorldChunk(serverWorld, generateChunk(chunkPos), null); - loadedWorldChunks.put(chunkPos, new ServerChunkData(fetchedWorldChunk, (short) 0, new BitSet(), new BitSet())); + loadedWorldChunks.put(chunkPos, fetchedWorldChunk); NoisiumServerChunkEvent.WORLD_CHUNK_GENERATED.invoker().onWorldChunkGenerated(fetchedWorldChunk); return fetchedWorldChunk; } @@ -169,7 +141,7 @@ public void onLightUpdate(LightType lightType, ChunkSectionPos chunkSectionPosit fetchedWorldChunk.addChunkTickSchedulers(serverWorld); fetchedWorldChunk.loadEntities(); - loadedWorldChunks.put(chunkPos, new ServerChunkData(fetchedWorldChunk, (short) 0, new BitSet(), new BitSet())); + loadedWorldChunks.put(chunkPos, fetchedWorldChunk); NoisiumServerChunkEvent.WORLD_CHUNK_GENERATED.invoker().onWorldChunkGenerated(fetchedWorldChunk); return fetchedWorldChunk; } @@ -216,6 +188,38 @@ public void onLightUpdate(LightType lightType, ChunkSectionPos chunkSectionPosit return chunks; } + /** + * Updates the chunk's lighting at the specified {@link ChunkSectionPos}. + * This method is ran asynchronously. + * + * @param lightType The {@link LightType} that should be updated for this {@link WorldChunk}. + * @param chunkSectionPosition The {@link ChunkSectionPos} of the {@link WorldChunk}. + */ + private void onLightUpdateAsync(@NotNull LightType lightType, @NotNull ChunkSectionPos chunkSectionPosition) { + CompletableFuture.runAsync(() -> { + var lightingProvider = serverWorld.getLightingProvider(); + int bottomY = lightingProvider.getBottomY(); + int topY = lightingProvider.getTopY(); + var chunkSectionYPosition = chunkSectionPosition.getSectionY(); + if (chunkSectionYPosition < bottomY || chunkSectionYPosition > topY) { + return; + } + + var chunkPosition = chunkSectionPosition.toChunkPos(); + var worldChunk = (NoisiumWorldChunkExtension) getChunk(chunkPosition); + var skyLightBits = worldChunk.noisium$getBlockLightBits(); + var blockLightBits = worldChunk.noisium$getSkyLightBits(); + int chunkSectionYPositionDifference = chunkSectionYPosition - bottomY; + + if (lightType == LightType.SKY) { + skyLightBits.set(chunkSectionYPositionDifference); + } else { + blockLightBits.set(chunkSectionYPositionDifference); + } + ChunkUtil.sendLightUpdateToPlayers(serverWorld.getPlayers(), lightingProvider, chunkPosition, skyLightBits, blockLightBits); + }, threadPoolExecutor); + } + private @Nullable NbtCompound getNbtDataAtChunkPosition(ChunkPos chunkPos) { try { var fetchedNbtCompoundOptionalFuture = versionedChunkStorage.getNbt(chunkPos).get(); diff --git a/common/src/main/java/io/github/steveplays28/noisium/server/world/chunk/ServerChunkData.java b/common/src/main/java/io/github/steveplays28/noisium/server/world/chunk/ServerChunkData.java deleted file mode 100644 index c46523f..0000000 --- a/common/src/main/java/io/github/steveplays28/noisium/server/world/chunk/ServerChunkData.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.steveplays28.noisium.server.world.chunk; - -import net.minecraft.world.chunk.WorldChunk; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.BitSet; - -public record ServerChunkData(@Nullable WorldChunk worldChunk, @NotNull Short blockUpdates, @NotNull BitSet blockLightBits, - @NotNull BitSet skyLightBits) {} diff --git a/common/src/main/java/io/github/steveplays28/noisium/server/world/chunk/event/NoisiumServerChunkEvent.java b/common/src/main/java/io/github/steveplays28/noisium/server/world/chunk/event/NoisiumServerChunkEvent.java index cf65d12..40d3d84 100644 --- a/common/src/main/java/io/github/steveplays28/noisium/server/world/chunk/event/NoisiumServerChunkEvent.java +++ b/common/src/main/java/io/github/steveplays28/noisium/server/world/chunk/event/NoisiumServerChunkEvent.java @@ -2,7 +2,10 @@ import dev.architectury.event.Event; import dev.architectury.event.EventFactory; +import net.minecraft.util.math.ChunkSectionPos; +import net.minecraft.world.LightType; import net.minecraft.world.chunk.WorldChunk; +import org.jetbrains.annotations.NotNull; public interface NoisiumServerChunkEvent { /** @@ -10,6 +13,11 @@ public interface NoisiumServerChunkEvent { */ Event WORLD_CHUNK_GENERATED = EventFactory.createLoop(); + /** + * @see WorldChunkGenerated + */ + Event LIGHT_UPDATE = EventFactory.createLoop(); + @FunctionalInterface interface WorldChunkGenerated { /** @@ -19,4 +27,15 @@ interface WorldChunkGenerated { */ void onWorldChunkGenerated(WorldChunk worldChunk); } + + @FunctionalInterface + interface LightUpdate { + /** + * Invoked before a {@link WorldChunk} has had a light update processed by {@link io.github.steveplays28.noisium.server.world.NoisiumServerWorldChunkManager}. + * + * @param lightType The {@link LightType} of the {@link WorldChunk}. + * @param chunkSectionPosition The {@link ChunkSectionPos} of the {@link WorldChunk}. + */ + void onLightUpdate(@NotNull LightType lightType, @NotNull ChunkSectionPos chunkSectionPosition); + } } diff --git a/common/src/main/java/io/github/steveplays28/noisium/util/world/chunk/ChunkUtil.java b/common/src/main/java/io/github/steveplays28/noisium/util/world/chunk/ChunkUtil.java index 99a6e21..61decda 100644 --- a/common/src/main/java/io/github/steveplays28/noisium/util/world/chunk/ChunkUtil.java +++ b/common/src/main/java/io/github/steveplays28/noisium/util/world/chunk/ChunkUtil.java @@ -71,7 +71,7 @@ public static void sendWorldChunksToPlayerAsync(@NotNull ServerWorld serverWorld * @param blockLightBits The blocklight {@link BitSet}. */ @SuppressWarnings("ForLoopReplaceableByForEach") - public static void sendLightUpdateToPlayers(@NotNull List players, LightingProvider lightingProvider, ChunkPos chunkPos, BitSet skyLightBits, BitSet blockLightBits) { + public static void sendLightUpdateToPlayers(@NotNull List players, @NotNull LightingProvider lightingProvider, @NotNull ChunkPos chunkPos, @NotNull BitSet skyLightBits, @NotNull BitSet blockLightBits) { for (int i = 0; i < players.size(); i++) { players.get(i).networkHandler.sendPacket(new LightUpdateS2CPacket(chunkPos, lightingProvider, skyLightBits, blockLightBits)); } @@ -85,7 +85,7 @@ public static void sendLightUpdateToPlayers(@NotNull List pl * @param blockState The {@link BlockState} at the specified {@link BlockPos} of the block update that should be sent to the {@link List} of players. */ @SuppressWarnings("ForLoopReplaceableByForEach") - public static void sendBlockUpdateToPlayers(@NotNull List players, @NotNull BlockPos blockPos, BlockState blockState) { + public static void sendBlockUpdateToPlayers(@NotNull List players, @NotNull BlockPos blockPos, @NotNull BlockState blockState) { for (int i = 0; i < players.size(); i++) { players.get(i).networkHandler.sendPacket(new BlockUpdateS2CPacket(blockPos, blockState)); } diff --git a/common/src/main/resources/noisium-common.mixins.json b/common/src/main/resources/noisium-common.mixins.json index bea69ee..e344606 100644 --- a/common/src/main/resources/noisium-common.mixins.json +++ b/common/src/main/resources/noisium-common.mixins.json @@ -19,7 +19,8 @@ "server.world.ServerWorldMixin", "server.world.ThreadedAnvilChunkStorageMixin", "world.WorldAccessMixin", - "world.WorldMixin" + "world.WorldMixin", + "world.chunk.WorldChunkMixin" ], "client": [ "client.gui.hud.DebugHudMixin"