From 3fdc1dabe8008ba284287014b263564646bfa0d8 Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Sat, 12 Jul 2025 23:55:39 +0100 Subject: [PATCH 01/21] Initial cleanups & optimizations --- .../nms/v1_21/ReflectionMappingsInfo.java | 4 + .../network/handlers/FakeBlockHelper.java | 142 ++++++++++-------- .../packet/FakeBlocksPacketHandlers.java | 2 +- 3 files changed, 87 insertions(+), 61 deletions(-) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/ReflectionMappingsInfo.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/ReflectionMappingsInfo.java index 91b55ad7f6..0fbee1ebbe 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/ReflectionMappingsInfo.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/ReflectionMappingsInfo.java @@ -98,6 +98,7 @@ public class ReflectionMappingsInfo { public static String ClientboundSetPassengersPacket_passengers = "c"; // net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$BlockEntityInfo + public static String ClientboundLevelChunkPacketDataBlockEntityInfo_create_method = "a"; public static String ClientboundLevelChunkPacketDataBlockEntityInfo_packedXZ = "c"; public static String ClientboundLevelChunkPacketDataBlockEntityInfo_y = "d"; @@ -126,4 +127,7 @@ public class ReflectionMappingsInfo { // net.minecraft.stats.ServerRecipeBook public static String ServerRecipeBook_addHighlight_method = "e"; + + // net.minecraft.world.level.block.entity.BlockEntityType + public static String BlockEntityType_validBlocks = "Y"; } diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java index ff41dfa046..286b092508 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java @@ -8,10 +8,11 @@ import com.denizenscript.denizencore.utilities.debugging.Debug; import io.netty.buffer.Unpooled; import net.minecraft.core.Registry; +import net.minecraft.core.SectionPos; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; import net.minecraft.world.level.biome.Biome; @@ -22,38 +23,56 @@ import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.PalettedContainer; +import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_21_R5.CraftRegistry; import org.bukkit.craftbukkit.v1_21_R5.CraftWorld; -import org.bukkit.craftbukkit.v1_21_R5.block.CraftBlockStates; +import org.bukkit.craftbukkit.v1_21_R5.block.CraftBlockType; import org.bukkit.craftbukkit.v1_21_R5.block.data.CraftBlockData; +import org.bukkit.craftbukkit.v1_21_R5.util.CraftLocation; import java.lang.invoke.MethodHandle; import java.lang.reflect.Constructor; import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.*; public class FakeBlockHelper { public static Field CHUNKDATA_BLOCK_ENTITIES = ReflectionHelper.getFields(ClientboundLevelChunkPacketData.class).getFirstOfType(List.class); + public static Class CHUNKDATA_BLOCKENTITYINFO_CLASS = ClientboundLevelChunkPacketData.class.getDeclaredClasses()[0]; public static MethodHandle CHUNKDATA_BLOCK_ENTITY_CONSTRUCTOR = ReflectionHelper.getConstructor(ClientboundLevelChunkPacketData.class.getDeclaredClasses()[0], int.class, int.class, BlockEntityType.class, CompoundTag.class); + public static final MethodHandle CHUNK_DATA_BLOCK_ENTITY_CREATE = ReflectionHelper.getMethodHandle(CHUNKDATA_BLOCKENTITYINFO_CLASS, ReflectionMappingsInfo.ClientboundLevelChunkPacketDataBlockEntityInfo_create_method, BlockEntity.class); public static MethodHandle CHUNKDATA_BUFFER_SETTER = ReflectionHelper.getFinalSetterForFirstOfType(ClientboundLevelChunkPacketData.class, byte[].class); - public static Class CHUNKDATA_BLOCKENTITYINFO_CLASS = ClientboundLevelChunkPacketData.class.getDeclaredClasses()[0]; public static Field CHUNKDATA_BLOCKENTITYINFO_PACKEDXZ = ReflectionHelper.getFields(CHUNKDATA_BLOCKENTITYINFO_CLASS).get(ReflectionMappingsInfo.ClientboundLevelChunkPacketDataBlockEntityInfo_packedXZ); public static Field CHUNKDATA_BLOCKENTITYINFO_Y = ReflectionHelper.getFields(CHUNKDATA_BLOCKENTITYINFO_CLASS).get(ReflectionMappingsInfo.ClientboundLevelChunkPacketDataBlockEntityInfo_y); public static MethodHandle CHUNKPACKET_CHUNKDATA_SETTER = ReflectionHelper.getFinalSetterForFirstOfType(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkPacketData.class); public static Constructor PALETTEDCONTAINER_CTOR = Arrays.stream(PalettedContainer.class.getConstructors()).filter(c -> c.getParameterCount() == 3).findFirst().get(); + public static Field BLOCK_ENTITY_TYPE_VALID_BLOCKS = ReflectionHelper.getFields(BlockEntityType.class).get(ReflectionMappingsInfo.BlockEntityType_validBlocks, Set.class); + + public static final Map> MATERIAL_BLOCK_ENTITY_TYPES = new EnumMap<>(Material.class); + + static { + try { + for (BlockEntityType nmsBlockEntityType : BuiltInRegistries.BLOCK_ENTITY_TYPE) { + Set validBlocks = (Set) BLOCK_ENTITY_TYPE_VALID_BLOCKS.get(nmsBlockEntityType); + for (Block validBlock : validBlocks) { + MATERIAL_BLOCK_ENTITY_TYPES.put(CraftBlockType.minecraftToBukkit(validBlock), nmsBlockEntityType); + } + } + } + catch (Throwable e) { + throw new RuntimeException(e); + } + } public static BlockState getNMSState(FakeBlock block) { return ((CraftBlockData) block.material.getModernData()).getState(); } - public static boolean anyBlocksInSection(List blocks, int y) { - int minY = y << 4; - int maxY = (y << 4) + 16; - for (FakeBlock block : blocks) { + public static boolean anyBlocksInSection(List blocksInChunk, int y) { + int minY = SectionPos.sectionToBlockCoord(y); + int maxY = minY + 16; + for (FakeBlock block : blocksInChunk) { int blockY = block.location.getBlockY(); if (blockY >= minY && blockY < maxY) { return true; @@ -65,7 +84,7 @@ public static boolean anyBlocksInSection(List blocks, int y) { public static Field PAPER_CHUNK_READY; public static boolean tryPaperPatch = true; - public static void copyPacketPaperPatch(ClientboundLevelChunkWithLightPacket newPacket, ClientboundLevelChunkWithLightPacket oldPacket) { + public static void copyPacketPaperPatch(ClientboundLevelChunkWithLightPacket newPacket) { if (!Denizen.supportsPaper || !tryPaperPatch) { return; } @@ -88,57 +107,54 @@ public static void copyPacketPaperPatch(ClientboundLevelChunkWithLightPacket new } } - public static ClientboundLevelChunkWithLightPacket handleMapChunkPacket(World world, ClientboundLevelChunkWithLightPacket originalPacket, int chunkX, int chunkZ, List blocks) { + public static ClientboundLevelChunkWithLightPacket handleMapChunkPacket(World world, ClientboundLevelChunkWithLightPacket originalChunkPacket, int chunkX, int chunkZ, List blocksInChunk, FakeBlock.FakeBlockMap fakeBlockMap) { try { - ClientboundLevelChunkWithLightPacket duplicateCorePacket = DenizenNetworkManagerImpl.copyPacket(originalPacket, ClientboundLevelChunkWithLightPacket.STREAM_CODEC); - copyPacketPaperPatch(duplicateCorePacket, originalPacket); - RegistryFriendlyByteBuf copier = new RegistryFriendlyByteBuf(Unpooled.buffer(), CraftRegistry.getMinecraftRegistry()); - originalPacket.getChunkData().write(copier); - ClientboundLevelChunkPacketData packet = new ClientboundLevelChunkPacketData(copier, chunkX, chunkZ); - FriendlyByteBuf serial = originalPacket.getChunkData().getReadBuffer(); - FriendlyByteBuf outputSerial = new FriendlyByteBuf(Unpooled.buffer(serial.readableBytes())); - List blockEntities = new ArrayList((List) CHUNKDATA_BLOCK_ENTITIES.get(originalPacket.getChunkData())); - CHUNKDATA_BLOCK_ENTITIES.set(packet, blockEntities); - for (int i = 0; i < blockEntities.size(); i++) { - Object blockEnt = blockEntities.get(i); + ClientboundLevelChunkWithLightPacket copiedChunkPacket = DenizenNetworkManagerImpl.copyPacket(originalChunkPacket, ClientboundLevelChunkWithLightPacket.STREAM_CODEC); + copyPacketPaperPatch(copiedChunkPacket); + // A list of block entities sent with the chunk data + List blockEntities = (List) CHUNKDATA_BLOCK_ENTITIES.get(copiedChunkPacket.getChunkData()); + LocationTag location = new LocationTag(world, 0, 0, 0); + ListIterator blockEntitiesIterator = blockEntities.listIterator(); + while (blockEntitiesIterator.hasNext()) { + Object blockEnt = blockEntitiesIterator.next(); int xz = CHUNKDATA_BLOCKENTITYINFO_PACKEDXZ.getInt(blockEnt); int y = CHUNKDATA_BLOCKENTITYINFO_Y.getInt(blockEnt); - int x = (chunkX << 4) + ((xz >> 4) & 15); - int z = (chunkZ << 4) + (xz & 15); - for (FakeBlock block : blocks) { - LocationTag loc = block.location; - if (loc.getBlockX() == x && loc.getBlockY() == y && loc.getBlockZ() == z && block.material != null) { - BlockEntity newBlockEnt = CraftBlockStates.createNewTileEntity(block.material.getMaterial()); - Object newData = CHUNKDATA_BLOCK_ENTITY_CONSTRUCTOR.invoke(xz, y, newBlockEnt.getType(), newBlockEnt.getUpdateTag(CraftRegistry.getMinecraftRegistry())); - blockEntities.set(i, newData); - break; - } + int relativeX = SectionPos.sectionRelative(xz >> 4); + int relativeZ = SectionPos.sectionRelative(xz); + int x = SectionPos.sectionToBlockCoord(chunkX) + relativeX; + int z = SectionPos.sectionToBlockCoord(chunkZ) + relativeZ; + location.setX(x); + location.setY(y); + location.setZ(z); + if (fakeBlockMap.byLocation.containsKey(location)) { + blockEntitiesIterator.remove(); } } + // Get the original chunk data to read, and a buf of the same size to write + FriendlyByteBuf rawChunkData = originalChunkPacket.getChunkData().getReadBuffer(); + FriendlyByteBuf newChunkData = new FriendlyByteBuf(Unpooled.buffer(rawChunkData.readableBytes())); int worldMinY = world.getMinHeight(); int worldMaxY = world.getMaxHeight(); - int minChunkY = worldMinY >> 4; - int maxChunkY = worldMaxY >> 4; - Registry biomeRegistry = ((CraftWorld) world).getHandle().registryAccess().lookupOrThrow(Registries.BIOME); + int minChunkY = SectionPos.blockToSectionCoord(worldMinY); + int maxChunkY = SectionPos.blockToSectionCoord(worldMaxY); + Registry biomeRegistry = CraftRegistry.getMinecraftRegistry(Registries.BIOME); + // These are section coords, iterating through every chunk section for (int y = minChunkY; y < maxChunkY; y++) { - int blockCount = serial.readShort(); - // reflected constructors as workaround for spigot remapper bug - Mojang "IdMap" became Spigot "IRegistry" but should be "Registry" - PalettedContainer states = (PalettedContainer) PALETTEDCONTAINER_CTOR.newInstance(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES); - states.read(serial); - PalettedContainer biomes = (PalettedContainer) PALETTEDCONTAINER_CTOR.newInstance(biomeRegistry, biomeRegistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES); - biomes.read(serial); - if (anyBlocksInSection(blocks, y)) { - int minY = y << 4; - int maxY = (y << 4) + 16; - for (FakeBlock block : blocks) { + int blockCount = rawChunkData.readShort(); + PalettedContainer states = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES); + states.read(rawChunkData); + PalettedContainer biomes = new PalettedContainer<>(biomeRegistry, biomeRegistry.getValueOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES); + biomes.read(rawChunkData); + if (anyBlocksInSection(blocksInChunk, y)) { + int minY = SectionPos.sectionToBlockCoord(y); + int maxY = minY + 16; + for (FakeBlock block : blocksInChunk) { int blockY = block.location.getBlockY(); if (blockY >= minY && blockY < maxY && block.material != null) { - int blockX = block.location.getBlockX(); - int blockZ = block.location.getBlockZ(); - blockX -= (blockX >> 4) * 16; - blockY -= (blockY >> 4) * 16; - blockZ -= (blockZ >> 4) * 16; - BlockState oldState = states.get(blockX, blockY, blockZ); + int relativeX = SectionPos.sectionRelative(block.location.getBlockX()); + int relativeY = SectionPos.sectionRelative(blockY); + int relativeZ = SectionPos.sectionRelative(block.location.getBlockZ()); + BlockState oldState = states.get(relativeX, relativeY, relativeZ); BlockState newState = getNMSState(block); if (oldState.isAir() && !newState.isAir()) { blockCount++; @@ -146,18 +162,24 @@ public static ClientboundLevelChunkWithLightPacket handleMapChunkPacket(World wo else if (newState.isAir() && !oldState.isAir()) { blockCount--; } - states.set(blockX, blockY, blockZ, newState); + states.set(relativeX, relativeY, relativeZ, newState); + BlockEntityType nmsBlockEntityType = MATERIAL_BLOCK_ENTITY_TYPES.get(block.material.getMaterial()); + if (nmsBlockEntityType == null) { + continue; + } + BlockEntity createdBlockEntity = nmsBlockEntityType.create(CraftLocation.toBlockPosition(block.location), newState); + createdBlockEntity.setLevel(((CraftWorld) world).getHandle()); + Object packetBlockEntityData = CHUNK_DATA_BLOCK_ENTITY_CREATE.invoke(createdBlockEntity); + blockEntities.add(packetBlockEntityData); } } } - outputSerial.writeShort(blockCount); - states.write(outputSerial); - biomes.write(outputSerial); + newChunkData.writeShort(blockCount); + states.write(newChunkData); + biomes.write(newChunkData); } - byte[] outputBytes = outputSerial.array(); - CHUNKDATA_BUFFER_SETTER.invoke(packet, outputBytes); - CHUNKPACKET_CHUNKDATA_SETTER.invoke(duplicateCorePacket, packet); - return duplicateCorePacket; + CHUNKDATA_BUFFER_SETTER.invoke(copiedChunkPacket.getChunkData(), newChunkData.array()); + return copiedChunkPacket; } catch (Throwable ex) { Debug.echoError(ex); diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java index 4938114cac..52ed0f191d 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java @@ -47,7 +47,7 @@ public static Packet processShowFakeForPacket(DenizenN if (blocks == null || blocks.isEmpty()) { return packet; } - ClientboundLevelChunkWithLightPacket newPacket = FakeBlockHelper.handleMapChunkPacket(networkManager.player.getBukkitEntity().getWorld(), (ClientboundLevelChunkWithLightPacket) packet, chunkX, chunkZ, blocks); + ClientboundLevelChunkWithLightPacket newPacket = FakeBlockHelper.handleMapChunkPacket(networkManager.player.getBukkitEntity().getWorld(), (ClientboundLevelChunkWithLightPacket) packet, chunkX, chunkZ, blocks, map); return newPacket; } else if (packet instanceof ClientboundSectionBlocksUpdatePacket sectionBlocksUpdatePacket) { From b4b800668b3a1f005b35a93b41dd38e81930846a Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Sat, 12 Jul 2025 23:57:54 +0100 Subject: [PATCH 02/21] Remove unnecessary `try` --- .../network/handlers/FakeBlockHelper.java | 138 +++++++++--------- 1 file changed, 66 insertions(+), 72 deletions(-) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java index 286b092508..a01beda1ce 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java @@ -107,83 +107,77 @@ public static void copyPacketPaperPatch(ClientboundLevelChunkWithLightPacket new } } - public static ClientboundLevelChunkWithLightPacket handleMapChunkPacket(World world, ClientboundLevelChunkWithLightPacket originalChunkPacket, int chunkX, int chunkZ, List blocksInChunk, FakeBlock.FakeBlockMap fakeBlockMap) { - try { - ClientboundLevelChunkWithLightPacket copiedChunkPacket = DenizenNetworkManagerImpl.copyPacket(originalChunkPacket, ClientboundLevelChunkWithLightPacket.STREAM_CODEC); - copyPacketPaperPatch(copiedChunkPacket); - // A list of block entities sent with the chunk data - List blockEntities = (List) CHUNKDATA_BLOCK_ENTITIES.get(copiedChunkPacket.getChunkData()); - LocationTag location = new LocationTag(world, 0, 0, 0); - ListIterator blockEntitiesIterator = blockEntities.listIterator(); - while (blockEntitiesIterator.hasNext()) { - Object blockEnt = blockEntitiesIterator.next(); - int xz = CHUNKDATA_BLOCKENTITYINFO_PACKEDXZ.getInt(blockEnt); - int y = CHUNKDATA_BLOCKENTITYINFO_Y.getInt(blockEnt); - int relativeX = SectionPos.sectionRelative(xz >> 4); - int relativeZ = SectionPos.sectionRelative(xz); - int x = SectionPos.sectionToBlockCoord(chunkX) + relativeX; - int z = SectionPos.sectionToBlockCoord(chunkZ) + relativeZ; - location.setX(x); - location.setY(y); - location.setZ(z); - if (fakeBlockMap.byLocation.containsKey(location)) { - blockEntitiesIterator.remove(); - } + public static ClientboundLevelChunkWithLightPacket handleMapChunkPacket(World world, ClientboundLevelChunkWithLightPacket originalChunkPacket, int chunkX, int chunkZ, List blocksInChunk, FakeBlock.FakeBlockMap fakeBlockMap) throws Throwable { + ClientboundLevelChunkWithLightPacket copiedChunkPacket = DenizenNetworkManagerImpl.copyPacket(originalChunkPacket, ClientboundLevelChunkWithLightPacket.STREAM_CODEC); + copyPacketPaperPatch(copiedChunkPacket); + // A list of block entities sent with the chunk data + List blockEntities = (List) CHUNKDATA_BLOCK_ENTITIES.get(copiedChunkPacket.getChunkData()); + LocationTag location = new LocationTag(world, 0, 0, 0); + ListIterator blockEntitiesIterator = blockEntities.listIterator(); + while (blockEntitiesIterator.hasNext()) { + Object blockEnt = blockEntitiesIterator.next(); + int xz = CHUNKDATA_BLOCKENTITYINFO_PACKEDXZ.getInt(blockEnt); + int y = CHUNKDATA_BLOCKENTITYINFO_Y.getInt(blockEnt); + int relativeX = SectionPos.sectionRelative(xz >> 4); + int relativeZ = SectionPos.sectionRelative(xz); + int x = SectionPos.sectionToBlockCoord(chunkX) + relativeX; + int z = SectionPos.sectionToBlockCoord(chunkZ) + relativeZ; + location.setX(x); + location.setY(y); + location.setZ(z); + if (fakeBlockMap.byLocation.containsKey(location)) { + blockEntitiesIterator.remove(); } - // Get the original chunk data to read, and a buf of the same size to write - FriendlyByteBuf rawChunkData = originalChunkPacket.getChunkData().getReadBuffer(); - FriendlyByteBuf newChunkData = new FriendlyByteBuf(Unpooled.buffer(rawChunkData.readableBytes())); - int worldMinY = world.getMinHeight(); - int worldMaxY = world.getMaxHeight(); - int minChunkY = SectionPos.blockToSectionCoord(worldMinY); - int maxChunkY = SectionPos.blockToSectionCoord(worldMaxY); - Registry biomeRegistry = CraftRegistry.getMinecraftRegistry(Registries.BIOME); - // These are section coords, iterating through every chunk section - for (int y = minChunkY; y < maxChunkY; y++) { - int blockCount = rawChunkData.readShort(); - PalettedContainer states = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES); - states.read(rawChunkData); - PalettedContainer biomes = new PalettedContainer<>(biomeRegistry, biomeRegistry.getValueOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES); - biomes.read(rawChunkData); - if (anyBlocksInSection(blocksInChunk, y)) { - int minY = SectionPos.sectionToBlockCoord(y); - int maxY = minY + 16; - for (FakeBlock block : blocksInChunk) { - int blockY = block.location.getBlockY(); - if (blockY >= minY && blockY < maxY && block.material != null) { - int relativeX = SectionPos.sectionRelative(block.location.getBlockX()); - int relativeY = SectionPos.sectionRelative(blockY); - int relativeZ = SectionPos.sectionRelative(block.location.getBlockZ()); - BlockState oldState = states.get(relativeX, relativeY, relativeZ); - BlockState newState = getNMSState(block); - if (oldState.isAir() && !newState.isAir()) { - blockCount++; - } - else if (newState.isAir() && !oldState.isAir()) { - blockCount--; - } - states.set(relativeX, relativeY, relativeZ, newState); - BlockEntityType nmsBlockEntityType = MATERIAL_BLOCK_ENTITY_TYPES.get(block.material.getMaterial()); - if (nmsBlockEntityType == null) { - continue; - } - BlockEntity createdBlockEntity = nmsBlockEntityType.create(CraftLocation.toBlockPosition(block.location), newState); - createdBlockEntity.setLevel(((CraftWorld) world).getHandle()); - Object packetBlockEntityData = CHUNK_DATA_BLOCK_ENTITY_CREATE.invoke(createdBlockEntity); - blockEntities.add(packetBlockEntityData); + } + // Get the original chunk data to read, and a buf of the same size to write + FriendlyByteBuf rawChunkData = originalChunkPacket.getChunkData().getReadBuffer(); + FriendlyByteBuf newChunkData = new FriendlyByteBuf(Unpooled.buffer(rawChunkData.readableBytes())); + int worldMinY = world.getMinHeight(); + int worldMaxY = world.getMaxHeight(); + int minChunkY = SectionPos.blockToSectionCoord(worldMinY); + int maxChunkY = SectionPos.blockToSectionCoord(worldMaxY); + Registry biomeRegistry = CraftRegistry.getMinecraftRegistry(Registries.BIOME); + // These are section coords, iterating through every chunk section + for (int y = minChunkY; y < maxChunkY; y++) { + int blockCount = rawChunkData.readShort(); + PalettedContainer states = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES); + states.read(rawChunkData); + PalettedContainer biomes = new PalettedContainer<>(biomeRegistry, biomeRegistry.getValueOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES); + biomes.read(rawChunkData); + if (anyBlocksInSection(blocksInChunk, y)) { + int minY = SectionPos.sectionToBlockCoord(y); + int maxY = minY + 16; + for (FakeBlock block : blocksInChunk) { + int blockY = block.location.getBlockY(); + if (blockY >= minY && blockY < maxY && block.material != null) { + int relativeX = SectionPos.sectionRelative(block.location.getBlockX()); + int relativeY = SectionPos.sectionRelative(blockY); + int relativeZ = SectionPos.sectionRelative(block.location.getBlockZ()); + BlockState oldState = states.get(relativeX, relativeY, relativeZ); + BlockState newState = getNMSState(block); + if (oldState.isAir() && !newState.isAir()) { + blockCount++; + } + else if (newState.isAir() && !oldState.isAir()) { + blockCount--; } + states.set(relativeX, relativeY, relativeZ, newState); + BlockEntityType nmsBlockEntityType = MATERIAL_BLOCK_ENTITY_TYPES.get(block.material.getMaterial()); + if (nmsBlockEntityType == null) { + continue; + } + BlockEntity createdBlockEntity = nmsBlockEntityType.create(CraftLocation.toBlockPosition(block.location), newState); + createdBlockEntity.setLevel(((CraftWorld) world).getHandle()); + Object packetBlockEntityData = CHUNK_DATA_BLOCK_ENTITY_CREATE.invoke(createdBlockEntity); + blockEntities.add(packetBlockEntityData); } } - newChunkData.writeShort(blockCount); - states.write(newChunkData); - biomes.write(newChunkData); } - CHUNKDATA_BUFFER_SETTER.invoke(copiedChunkPacket.getChunkData(), newChunkData.array()); - return copiedChunkPacket; - } - catch (Throwable ex) { - Debug.echoError(ex); + newChunkData.writeShort(blockCount); + states.write(newChunkData); + biomes.write(newChunkData); } - return null; + CHUNKDATA_BUFFER_SETTER.invoke(copiedChunkPacket.getChunkData(), newChunkData.array()); + return copiedChunkPacket; } } From 78306a0437f0713eca6710076d81ea0500b8e651 Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Sun, 13 Jul 2025 00:27:59 +0100 Subject: [PATCH 03/21] Cleanup reflection --- .../nms/v1_21/ReflectionMappingsInfo.java | 10 +++---- .../network/handlers/FakeBlockHelper.java | 29 ++++++++----------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/ReflectionMappingsInfo.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/ReflectionMappingsInfo.java index 0fbee1ebbe..18d5f6e051 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/ReflectionMappingsInfo.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/ReflectionMappingsInfo.java @@ -97,6 +97,10 @@ public class ReflectionMappingsInfo { // net.minecraft.network.protocol.game.ClientboundSetPassengersPacket public static String ClientboundSetPassengersPacket_passengers = "c"; + // net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData + public static String ClientboundLevelChunkPacketData_buffer = "d"; + public static String ClientboundLevelChunkPacketData_blockEntitiesData = "e"; + // net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$BlockEntityInfo public static String ClientboundLevelChunkPacketDataBlockEntityInfo_create_method = "a"; public static String ClientboundLevelChunkPacketDataBlockEntityInfo_packedXZ = "c"; @@ -113,12 +117,6 @@ public class ReflectionMappingsInfo { // net.minecraft.tags.TagNetworkSerialization$NetworkPayload public static String TagNetworkSerializationNetworkPayload_tags = "b"; - // net.minecraft.core.HolderSet$Named - public static String HolderSetNamed_bind_method = "b"; - - // net.minecraft.core.Holder$Reference - public static String HolderReference_bindTags_method = "a"; - // net.minecraft.server.level.ServerLevel public static String ServerLevel_sleepStatus = "R"; diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java index a01beda1ce..61a876b8af 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java @@ -11,7 +11,6 @@ import net.minecraft.core.SectionPos; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; @@ -32,29 +31,25 @@ import org.bukkit.craftbukkit.v1_21_R5.util.CraftLocation; import java.lang.invoke.MethodHandle; -import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.*; public class FakeBlockHelper { - public static Field CHUNKDATA_BLOCK_ENTITIES = ReflectionHelper.getFields(ClientboundLevelChunkPacketData.class).getFirstOfType(List.class); - public static Class CHUNKDATA_BLOCKENTITYINFO_CLASS = ClientboundLevelChunkPacketData.class.getDeclaredClasses()[0]; - public static MethodHandle CHUNKDATA_BLOCK_ENTITY_CONSTRUCTOR = ReflectionHelper.getConstructor(ClientboundLevelChunkPacketData.class.getDeclaredClasses()[0], int.class, int.class, BlockEntityType.class, CompoundTag.class); - public static final MethodHandle CHUNK_DATA_BLOCK_ENTITY_CREATE = ReflectionHelper.getMethodHandle(CHUNKDATA_BLOCKENTITYINFO_CLASS, ReflectionMappingsInfo.ClientboundLevelChunkPacketDataBlockEntityInfo_create_method, BlockEntity.class); - public static MethodHandle CHUNKDATA_BUFFER_SETTER = ReflectionHelper.getFinalSetterForFirstOfType(ClientboundLevelChunkPacketData.class, byte[].class); - public static Field CHUNKDATA_BLOCKENTITYINFO_PACKEDXZ = ReflectionHelper.getFields(CHUNKDATA_BLOCKENTITYINFO_CLASS).get(ReflectionMappingsInfo.ClientboundLevelChunkPacketDataBlockEntityInfo_packedXZ); - public static Field CHUNKDATA_BLOCKENTITYINFO_Y = ReflectionHelper.getFields(CHUNKDATA_BLOCKENTITYINFO_CLASS).get(ReflectionMappingsInfo.ClientboundLevelChunkPacketDataBlockEntityInfo_y); - public static MethodHandle CHUNKPACKET_CHUNKDATA_SETTER = ReflectionHelper.getFinalSetterForFirstOfType(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkPacketData.class); - public static Constructor PALETTEDCONTAINER_CTOR = Arrays.stream(PalettedContainer.class.getConstructors()).filter(c -> c.getParameterCount() == 3).findFirst().get(); - public static Field BLOCK_ENTITY_TYPE_VALID_BLOCKS = ReflectionHelper.getFields(BlockEntityType.class).get(ReflectionMappingsInfo.BlockEntityType_validBlocks, Set.class); + public static final MethodHandle CHUNKDATA_BLOCK_ENTITIES = ReflectionHelper.getFields(ClientboundLevelChunkPacketData.class).getGetter(ReflectionMappingsInfo.ClientboundLevelChunkPacketData_blockEntitiesData, List.class); + public static final MethodHandle CHUNKDATA_BUFFER_SETTER = ReflectionHelper.getFields(ClientboundLevelChunkPacketData.class).getSetter(ReflectionMappingsInfo.ClientboundLevelChunkPacketData_buffer, byte[].class); + public static final Class CHUNKDATA_BLOCKENTITYINFO_CLASS = ClientboundLevelChunkPacketData.class.getDeclaredClasses()[0]; + public static final MethodHandle CHUNKDATA_BLOCK_ENTITY_CREATE = ReflectionHelper.getMethodHandle(CHUNKDATA_BLOCKENTITYINFO_CLASS, ReflectionMappingsInfo.ClientboundLevelChunkPacketDataBlockEntityInfo_create_method, BlockEntity.class); + public static final MethodHandle CHUNKDATA_BLOCKENTITYINFO_PACKEDXZ = ReflectionHelper.getFields(CHUNKDATA_BLOCKENTITYINFO_CLASS).getGetter(ReflectionMappingsInfo.ClientboundLevelChunkPacketDataBlockEntityInfo_packedXZ); + public static final MethodHandle CHUNKDATA_BLOCKENTITYINFO_Y = ReflectionHelper.getFields(CHUNKDATA_BLOCKENTITYINFO_CLASS).getGetter(ReflectionMappingsInfo.ClientboundLevelChunkPacketDataBlockEntityInfo_y); + public static final MethodHandle BLOCK_ENTITY_TYPE_VALID_BLOCKS = ReflectionHelper.getFields(BlockEntityType.class).getGetter(ReflectionMappingsInfo.BlockEntityType_validBlocks, Set.class); public static final Map> MATERIAL_BLOCK_ENTITY_TYPES = new EnumMap<>(Material.class); static { try { for (BlockEntityType nmsBlockEntityType : BuiltInRegistries.BLOCK_ENTITY_TYPE) { - Set validBlocks = (Set) BLOCK_ENTITY_TYPE_VALID_BLOCKS.get(nmsBlockEntityType); + Set validBlocks = (Set) BLOCK_ENTITY_TYPE_VALID_BLOCKS.invokeExact(nmsBlockEntityType); for (Block validBlock : validBlocks) { MATERIAL_BLOCK_ENTITY_TYPES.put(CraftBlockType.minecraftToBukkit(validBlock), nmsBlockEntityType); } @@ -111,13 +106,13 @@ public static ClientboundLevelChunkWithLightPacket handleMapChunkPacket(World wo ClientboundLevelChunkWithLightPacket copiedChunkPacket = DenizenNetworkManagerImpl.copyPacket(originalChunkPacket, ClientboundLevelChunkWithLightPacket.STREAM_CODEC); copyPacketPaperPatch(copiedChunkPacket); // A list of block entities sent with the chunk data - List blockEntities = (List) CHUNKDATA_BLOCK_ENTITIES.get(copiedChunkPacket.getChunkData()); + List blockEntities = (List) CHUNKDATA_BLOCK_ENTITIES.invoke(copiedChunkPacket.getChunkData()); LocationTag location = new LocationTag(world, 0, 0, 0); ListIterator blockEntitiesIterator = blockEntities.listIterator(); while (blockEntitiesIterator.hasNext()) { Object blockEnt = blockEntitiesIterator.next(); - int xz = CHUNKDATA_BLOCKENTITYINFO_PACKEDXZ.getInt(blockEnt); - int y = CHUNKDATA_BLOCKENTITYINFO_Y.getInt(blockEnt); + int xz = (int) CHUNKDATA_BLOCKENTITYINFO_PACKEDXZ.invoke(blockEnt); + int y = (int) CHUNKDATA_BLOCKENTITYINFO_Y.invoke(blockEnt); int relativeX = SectionPos.sectionRelative(xz >> 4); int relativeZ = SectionPos.sectionRelative(xz); int x = SectionPos.sectionToBlockCoord(chunkX) + relativeX; @@ -168,7 +163,7 @@ else if (newState.isAir() && !oldState.isAir()) { } BlockEntity createdBlockEntity = nmsBlockEntityType.create(CraftLocation.toBlockPosition(block.location), newState); createdBlockEntity.setLevel(((CraftWorld) world).getHandle()); - Object packetBlockEntityData = CHUNK_DATA_BLOCK_ENTITY_CREATE.invoke(createdBlockEntity); + Object packetBlockEntityData = CHUNKDATA_BLOCK_ENTITY_CREATE.invoke(createdBlockEntity); blockEntities.add(packetBlockEntityData); } } From 06d6df9a0df790ed3b98519ec3c47d9aeddec324 Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Thu, 17 Jul 2025 16:01:41 +0100 Subject: [PATCH 04/21] Initial work on fake block light levels --- .../network/handlers/FakeBlockHelper.java | 144 ++++++++++++++++++ .../packet/FakeBlocksPacketHandlers.java | 3 +- 2 files changed, 145 insertions(+), 2 deletions(-) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java index 61a876b8af..9723a3ca96 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java @@ -1,12 +1,17 @@ package com.denizenscript.denizen.nms.v1_21.impl.network.handlers; import com.denizenscript.denizen.Denizen; +import com.denizenscript.denizen.nms.NMSHandler; import com.denizenscript.denizen.nms.v1_21.ReflectionMappingsInfo; import com.denizenscript.denizen.objects.LocationTag; import com.denizenscript.denizen.utilities.blocks.FakeBlock; +import com.denizenscript.denizencore.objects.core.ColorTag; import com.denizenscript.denizencore.utilities.ReflectionHelper; import com.denizenscript.denizencore.utilities.debugging.Debug; import io.netty.buffer.Unpooled; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; import net.minecraft.core.SectionPos; import net.minecraft.core.registries.BuiltInRegistries; @@ -14,6 +19,9 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; +import net.minecraft.network.protocol.game.ClientboundLightUpdatePacketData; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.block.Block; @@ -21,7 +29,10 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.DataLayer; import net.minecraft.world.level.chunk.PalettedContainer; +import net.minecraft.world.level.lighting.LevelLightEngine; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_21_R5.CraftRegistry; @@ -132,8 +143,20 @@ public static ClientboundLevelChunkWithLightPacket handleMapChunkPacket(World wo int minChunkY = SectionPos.blockToSectionCoord(worldMinY); int maxChunkY = SectionPos.blockToSectionCoord(worldMaxY); Registry biomeRegistry = CraftRegistry.getMinecraftRegistry(Registries.BIOME); + int blocksLit = 0, skyLit = 0; // These are section coords, iterating through every chunk section + ClientboundLightUpdatePacketData lightData = copiedChunkPacket.getLightData(); + Long2ObjectMap sectionLightCache = new Long2ObjectOpenHashMap<>(); for (int y = minChunkY; y < maxChunkY; y++) { + SectionPos sectionPos = SectionPos.of(chunkX, y, chunkZ); + int sectionIndex = y + Math.abs(minChunkY) + 1; + boolean hasSky = false, hasBlock = false; + if (lightData.getBlockYMask().get(sectionIndex)) { + hasBlock = true; + } + if (lightData.getSkyYMask().get(sectionIndex)) { + hasSky = true; + } int blockCount = rawChunkData.readShort(); PalettedContainer states = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES); states.read(rawChunkData); @@ -165,9 +188,29 @@ else if (newState.isAir() && !oldState.isAir()) { createdBlockEntity.setLevel(((CraftWorld) world).getHandle()); Object packetBlockEntityData = CHUNKDATA_BLOCK_ENTITY_CREATE.invoke(createdBlockEntity); blockEntities.add(packetBlockEntityData); + DataLayer blockLights; + if (!hasBlock) { + Debug.log(">>>>>>>>>>>>>>>>>>> No light data, adding"); + blockLights = new DataLayer(); + lightData.getBlockUpdates().add(blocksLit, blockLights.getData()); + lightData.getBlockYMask().set(sectionIndex); + lightData.getEmptyBlockYMask().clear(sectionIndex); + hasBlock = true; + } + else { + blockLights = new DataLayer(lightData.getBlockUpdates().get(blocksLit)); + } + DataLayer skyLights = hasSky ? new DataLayer(lightData.getSkyUpdates().get(skyLit)) : new DataLayer(); + blockLights.set(relativeX, relativeY, relativeZ, getEstimatedLightLevel(relativeX, relativeY, relativeZ, blockLights, skyLights, lightData, world, sectionPos, sectionLightCache, sectionIndex, blocksLit, skyLit)); } } } + if (hasBlock) { + blocksLit++; + } + if (hasSky) { + skyLit++; + } newChunkData.writeShort(blockCount); states.write(newChunkData); biomes.write(newChunkData); @@ -175,4 +218,105 @@ else if (newState.isAir() && !oldState.isAir()) { CHUNKDATA_BUFFER_SETTER.invoke(copiedChunkPacket.getChunkData(), newChunkData.array()); return copiedChunkPacket; } + + public static final int[][] directions = { + {0, -1, 0}, + {0, 1, 0}, + {-1, 0, 0}, + {1, 0, 0}, + {0, 0, -1}, + {0, 0, 1} + }; + + public static int getEstimatedLightLevel(int relativeX, int relativeY, int relativeZ, DataLayer blockLights, DataLayer skyLights, ClientboundLightUpdatePacketData lightPacket, World world, SectionPos sectionPos, Long2ObjectMap sectionLightsCache, int sectionIndex, int blocksLit, int skyLit) { + int maxLight = Math.max(blockLights.get(relativeX, relativeY, relativeZ), skyLights.get(relativeX, relativeY, relativeZ)); + if (maxLight == 15) { + return 15; + } + List blockLookups = null; + for (int[] direction : directions) { + int yOffest = direction[1]; + int neighborX = relativeX + direction[0]; + int neighborY = relativeY + yOffest; + int neighborZ = relativeZ + direction[2]; + if (coordOutOfSection(neighborX) || coordOutOfSection(neighborZ)) { + if (blockLookups == null) { + blockLookups = new ArrayList<>(2); + } + blockLookups.add(new BlockPos(sectionPos.minBlockX() + neighborX, sectionPos.minBlockY() + neighborY, sectionPos.minBlockZ() + neighborZ)); + continue; + } + int light; + if (coordOutOfSection(neighborY)) { + int adjacentSectionIndex = sectionIndex + yOffest; + boolean hasSkyLights = lightPacket.getSkyYMask().get(adjacentSectionIndex); + boolean hasBlockLights = lightPacket.getBlockYMask().get(adjacentSectionIndex); + if (!hasBlockLights && !hasSkyLights) { + Debug.log("No lights in adjacent section"); + continue; + } + int wrappedNeighborY = SectionPos.sectionRelative(neighborY); + light = Math.max( + hasSkyLights ? new DataLayer(lightPacket.getSkyUpdates().get(skyLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ) : 0, + hasBlockLights ? new DataLayer(lightPacket.getBlockUpdates().get(blocksLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ) : 0 + ); + Debug.log("Adjacent packet light: " + light); + } + else { + light = Math.max(blockLights.get(neighborX, neighborY, neighborZ), skyLights.get(neighborX, neighborY, neighborZ)); + Debug.log("Packet light: " + light); + } + if (light == 15) { + return 15; + } + if (light > maxLight) { + maxLight = light; + } + } + if (blockLookups == null) { + return maxLight; + } + ServerLevel nmsWorld = ((CraftWorld) world).getHandle(); + for (BlockPos blockPos : blockLookups) { + SectionPos containingSection = SectionPos.of(blockPos); + SectionLightCache sectionLight = sectionLightsCache.computeIfAbsent(containingSection.asLong(), k -> { + Debug.log("Getting section to cache"); + LevelLightEngine lightEngine = nmsWorld.getLightEngine(); + DataLayer sectionBlockLights = lightEngine.getLayerListener(LightLayer.BLOCK).getDataLayerData(containingSection); + DataLayer sectionSkyLights = lightEngine.getLayerListener(LightLayer.SKY).getDataLayerData(containingSection); + return new SectionLightCache(sectionBlockLights, sectionSkyLights); + }); + int light = sectionLight.getLight(blockPos); + Bukkit.getScheduler().runTaskLater(Denizen.getInstance(), () -> { + NMSHandler.packetHelper.showDebugTestMarker(Bukkit.getOnlinePlayers().iterator().next(), CraftLocation.toBukkit(blockPos), ColorTag.valueOf("red", null), "", 4000); + }, 1); + Debug.log("Block light: " + light); + if (light == 15) { + return 15; + } + if (light > maxLight) { + maxLight = light; + } + } + return maxLight; + } + + public record SectionLightCache(DataLayer blockLights, DataLayer skyLights) { + + public int getLight(BlockPos blockPos) { + int relativeX = SectionPos.sectionRelative(blockPos.getX()); + int relativeY = SectionPos.sectionRelative(blockPos.getY()); + int relativeZ = SectionPos.sectionRelative(blockPos.getZ()); + int skyLight = skyLights != null ? skyLights.get(relativeX, relativeY, relativeZ) : 0; + if (skyLight == 15) { + return skyLight; + } + int blockLight = blockLights != null ? blockLights.get(relativeX, relativeY, relativeZ) : 0; + return Math.max(skyLight, blockLight); + } + } + + public static boolean coordOutOfSection(int relativeCoord) { + return relativeCoord < 0 || relativeCoord > 15; + } } diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java index 52ed0f191d..f8b31d4052 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java @@ -47,8 +47,7 @@ public static Packet processShowFakeForPacket(DenizenN if (blocks == null || blocks.isEmpty()) { return packet; } - ClientboundLevelChunkWithLightPacket newPacket = FakeBlockHelper.handleMapChunkPacket(networkManager.player.getBukkitEntity().getWorld(), (ClientboundLevelChunkWithLightPacket) packet, chunkX, chunkZ, blocks, map); - return newPacket; + return FakeBlockHelper.handleMapChunkPacket(networkManager.player.getBukkitEntity().getWorld(), (ClientboundLevelChunkWithLightPacket) packet, chunkX, chunkZ, blocks, map); } else if (packet instanceof ClientboundSectionBlocksUpdatePacket sectionBlocksUpdatePacket) { FakeBlock.FakeBlockMap map = FakeBlock.blocks.get(networkManager.player.getUUID()); From d23b77218c5a769b697863f2c9dbfed0888101e3 Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Thu, 17 Jul 2025 16:33:58 +0100 Subject: [PATCH 05/21] Minor optimization to `max` handling --- .../network/handlers/FakeBlockHelper.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java index 9723a3ca96..2f0180bce4 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java @@ -229,7 +229,7 @@ else if (newState.isAir() && !oldState.isAir()) { }; public static int getEstimatedLightLevel(int relativeX, int relativeY, int relativeZ, DataLayer blockLights, DataLayer skyLights, ClientboundLightUpdatePacketData lightPacket, World world, SectionPos sectionPos, Long2ObjectMap sectionLightsCache, int sectionIndex, int blocksLit, int skyLit) { - int maxLight = Math.max(blockLights.get(relativeX, relativeY, relativeZ), skyLights.get(relativeX, relativeY, relativeZ)); + int maxLight = maxLight(relativeX, relativeY, relativeZ, blockLights, skyLights); if (maxLight == 15) { return 15; } @@ -256,14 +256,16 @@ public static int getEstimatedLightLevel(int relativeX, int relativeY, int relat continue; } int wrappedNeighborY = SectionPos.sectionRelative(neighborY); - light = Math.max( - hasSkyLights ? new DataLayer(lightPacket.getSkyUpdates().get(skyLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ) : 0, - hasBlockLights ? new DataLayer(lightPacket.getBlockUpdates().get(blocksLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ) : 0 - ); + int skyLight = hasSkyLights ? new DataLayer(lightPacket.getSkyUpdates().get(skyLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ) : 0; + if (skyLight == 15 || !hasBlockLights) { + return skyLight; + } + int blockLight = new DataLayer(lightPacket.getBlockUpdates().get(blocksLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ); + light = Math.max(skyLight, blockLight); Debug.log("Adjacent packet light: " + light); } else { - light = Math.max(blockLights.get(neighborX, neighborY, neighborZ), skyLights.get(neighborX, neighborY, neighborZ)); + light = maxLight(neighborX, neighborY, neighborZ, blockLights, skyLights); Debug.log("Packet light: " + light); } if (light == 15) { @@ -301,6 +303,15 @@ public static int getEstimatedLightLevel(int relativeX, int relativeY, int relat return maxLight; } + public static int maxLight(int relativeX, int relativeY, int relativeZ, DataLayer blockLightData, DataLayer skyLightData) { + int skyLight = skyLightData.get(relativeX, relativeY, relativeZ); + if (skyLight == 15) { + return 15; + } + int blockLight = blockLightData.get(relativeX, relativeY, relativeZ); + return Math.max(skyLight, blockLight); + } + public record SectionLightCache(DataLayer blockLights, DataLayer skyLights) { public int getLight(BlockPos blockPos) { From 0a660dfa3ccbea82c3f17bac4ad4aa0cd68e8c9e Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Thu, 17 Jul 2025 16:35:19 +0100 Subject: [PATCH 06/21] Pass in NMS world --- .../nms/v1_21/impl/network/handlers/FakeBlockHelper.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java index 2f0180bce4..e5362bf056 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java @@ -185,7 +185,8 @@ else if (newState.isAir() && !oldState.isAir()) { continue; } BlockEntity createdBlockEntity = nmsBlockEntityType.create(CraftLocation.toBlockPosition(block.location), newState); - createdBlockEntity.setLevel(((CraftWorld) world).getHandle()); + ServerLevel nmsWorld = ((CraftWorld) world).getHandle(); + createdBlockEntity.setLevel(nmsWorld); Object packetBlockEntityData = CHUNKDATA_BLOCK_ENTITY_CREATE.invoke(createdBlockEntity); blockEntities.add(packetBlockEntityData); DataLayer blockLights; @@ -201,7 +202,7 @@ else if (newState.isAir() && !oldState.isAir()) { blockLights = new DataLayer(lightData.getBlockUpdates().get(blocksLit)); } DataLayer skyLights = hasSky ? new DataLayer(lightData.getSkyUpdates().get(skyLit)) : new DataLayer(); - blockLights.set(relativeX, relativeY, relativeZ, getEstimatedLightLevel(relativeX, relativeY, relativeZ, blockLights, skyLights, lightData, world, sectionPos, sectionLightCache, sectionIndex, blocksLit, skyLit)); + blockLights.set(relativeX, relativeY, relativeZ, getEstimatedLightLevel(relativeX, relativeY, relativeZ, blockLights, skyLights, lightData, nmsWorld, sectionPos, sectionLightCache, sectionIndex, blocksLit, skyLit)); } } } @@ -228,7 +229,7 @@ else if (newState.isAir() && !oldState.isAir()) { {0, 0, 1} }; - public static int getEstimatedLightLevel(int relativeX, int relativeY, int relativeZ, DataLayer blockLights, DataLayer skyLights, ClientboundLightUpdatePacketData lightPacket, World world, SectionPos sectionPos, Long2ObjectMap sectionLightsCache, int sectionIndex, int blocksLit, int skyLit) { + public static int getEstimatedLightLevel(int relativeX, int relativeY, int relativeZ, DataLayer blockLights, DataLayer skyLights, ClientboundLightUpdatePacketData lightPacket, ServerLevel nmsWorld, SectionPos sectionPos, Long2ObjectMap sectionLightsCache, int sectionIndex, int blocksLit, int skyLit) { int maxLight = maxLight(relativeX, relativeY, relativeZ, blockLights, skyLights); if (maxLight == 15) { return 15; @@ -278,7 +279,6 @@ public static int getEstimatedLightLevel(int relativeX, int relativeY, int relat if (blockLookups == null) { return maxLight; } - ServerLevel nmsWorld = ((CraftWorld) world).getHandle(); for (BlockPos blockPos : blockLookups) { SectionPos containingSection = SectionPos.of(blockPos); SectionLightCache sectionLight = sectionLightsCache.computeIfAbsent(containingSection.asLong(), k -> { From a56183031978df4df30d61f152a9c8cd00052e06 Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Thu, 17 Jul 2025 18:25:47 +0100 Subject: [PATCH 07/21] Account for sky darkness --- .../network/handlers/FakeBlockHelper.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java index e5362bf056..fa5bffabbb 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java @@ -230,7 +230,8 @@ else if (newState.isAir() && !oldState.isAir()) { }; public static int getEstimatedLightLevel(int relativeX, int relativeY, int relativeZ, DataLayer blockLights, DataLayer skyLights, ClientboundLightUpdatePacketData lightPacket, ServerLevel nmsWorld, SectionPos sectionPos, Long2ObjectMap sectionLightsCache, int sectionIndex, int blocksLit, int skyLit) { - int maxLight = maxLight(relativeX, relativeY, relativeZ, blockLights, skyLights); + int ambientDarkness = nmsWorld.getSkyDarken(); + int maxLight = maxLight(relativeX, relativeY, relativeZ, blockLights, skyLights, ambientDarkness); if (maxLight == 15) { return 15; } @@ -257,7 +258,7 @@ public static int getEstimatedLightLevel(int relativeX, int relativeY, int relat continue; } int wrappedNeighborY = SectionPos.sectionRelative(neighborY); - int skyLight = hasSkyLights ? new DataLayer(lightPacket.getSkyUpdates().get(skyLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ) : 0; + int skyLight = hasSkyLights ? new DataLayer(lightPacket.getSkyUpdates().get(skyLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ) - ambientDarkness : 0; if (skyLight == 15 || !hasBlockLights) { return skyLight; } @@ -266,7 +267,7 @@ public static int getEstimatedLightLevel(int relativeX, int relativeY, int relat Debug.log("Adjacent packet light: " + light); } else { - light = maxLight(neighborX, neighborY, neighborZ, blockLights, skyLights); + light = maxLight(neighborX, neighborY, neighborZ, blockLights, skyLights, ambientDarkness); Debug.log("Packet light: " + light); } if (light == 15) { @@ -288,7 +289,7 @@ public static int getEstimatedLightLevel(int relativeX, int relativeY, int relat DataLayer sectionSkyLights = lightEngine.getLayerListener(LightLayer.SKY).getDataLayerData(containingSection); return new SectionLightCache(sectionBlockLights, sectionSkyLights); }); - int light = sectionLight.getLight(blockPos); + int light = sectionLight.getLight(blockPos, ambientDarkness); Bukkit.getScheduler().runTaskLater(Denizen.getInstance(), () -> { NMSHandler.packetHelper.showDebugTestMarker(Bukkit.getOnlinePlayers().iterator().next(), CraftLocation.toBukkit(blockPos), ColorTag.valueOf("red", null), "", 4000); }, 1); @@ -303,8 +304,8 @@ public static int getEstimatedLightLevel(int relativeX, int relativeY, int relat return maxLight; } - public static int maxLight(int relativeX, int relativeY, int relativeZ, DataLayer blockLightData, DataLayer skyLightData) { - int skyLight = skyLightData.get(relativeX, relativeY, relativeZ); + public static int maxLight(int relativeX, int relativeY, int relativeZ, DataLayer blockLightData, DataLayer skyLightData, int ambientDarkness) { + int skyLight = skyLightData.get(relativeX, relativeY, relativeZ) - ambientDarkness; if (skyLight == 15) { return 15; } @@ -314,15 +315,15 @@ public static int maxLight(int relativeX, int relativeY, int relativeZ, DataLaye public record SectionLightCache(DataLayer blockLights, DataLayer skyLights) { - public int getLight(BlockPos blockPos) { + public int getLight(BlockPos blockPos, int ambientDarkness) { int relativeX = SectionPos.sectionRelative(blockPos.getX()); int relativeY = SectionPos.sectionRelative(blockPos.getY()); int relativeZ = SectionPos.sectionRelative(blockPos.getZ()); - int skyLight = skyLights != null ? skyLights.get(relativeX, relativeY, relativeZ) : 0; - if (skyLight == 15) { + int skyLight = skyLights != null ? skyLights.get(relativeX, relativeY, relativeZ) - ambientDarkness : 0; + if (skyLight == 15 || blockLights == null) { return skyLight; } - int blockLight = blockLights != null ? blockLights.get(relativeX, relativeY, relativeZ) : 0; + int blockLight = blockLights.get(relativeX, relativeY, relativeZ); return Math.max(skyLight, blockLight); } } From 9bf7492d8591b52b695b968fa062aa136e528e32 Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Thu, 17 Jul 2025 22:13:50 +0100 Subject: [PATCH 08/21] Set both block and sky light for smoother visuals --- .../network/handlers/FakeBlockHelper.java | 136 ++++++++++-------- 1 file changed, 78 insertions(+), 58 deletions(-) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java index fa5bffabbb..7681e6892e 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java @@ -189,20 +189,32 @@ else if (newState.isAir() && !oldState.isAir()) { createdBlockEntity.setLevel(nmsWorld); Object packetBlockEntityData = CHUNKDATA_BLOCK_ENTITY_CREATE.invoke(createdBlockEntity); blockEntities.add(packetBlockEntityData); - DataLayer blockLights; - if (!hasBlock) { - Debug.log(">>>>>>>>>>>>>>>>>>> No light data, adding"); - blockLights = new DataLayer(); - lightData.getBlockUpdates().add(blocksLit, blockLights.getData()); - lightData.getBlockYMask().set(sectionIndex); - lightData.getEmptyBlockYMask().clear(sectionIndex); - hasBlock = true; + BlockLightData estimatedLights = getEstimatedLightLevel(relativeX, relativeY, relativeZ, + getLayer(hasBlock, lightData.getBlockUpdates(), blocksLit), getLayer(hasSky, lightData.getSkyUpdates(), skyLit), + lightData, nmsWorld, sectionPos, sectionLightCache, sectionIndex, blocksLit, skyLit); + Debug.log("Final data: " + estimatedLights); + if (estimatedLights.block() > 0) { + DataLayer blockLights; + if (!hasBlock) { + blockLights = addLayer(lightData.getBlockUpdates(), blocksLit, lightData.getBlockYMask(), lightData.getEmptyBlockYMask(), sectionIndex); + hasBlock = true; + } + else { + blockLights = new DataLayer(lightData.getBlockUpdates().get(blocksLit)); + } + blockLights.set(relativeX, relativeY, relativeZ, estimatedLights.block()); } - else { - blockLights = new DataLayer(lightData.getBlockUpdates().get(blocksLit)); + if (estimatedLights.sky() > 0) { + DataLayer skyLights; + if (!hasSky) { + skyLights = addLayer(lightData.getSkyUpdates(), skyLit, lightData.getSkyYMask(), lightData.getEmptySkyYMask(), sectionIndex); + hasSky = true; + } + else { + skyLights = new DataLayer(lightData.getSkyUpdates().get(skyLit)); + } + skyLights.set(relativeX, relativeY, relativeZ, estimatedLights.sky()); } - DataLayer skyLights = hasSky ? new DataLayer(lightData.getSkyUpdates().get(skyLit)) : new DataLayer(); - blockLights.set(relativeX, relativeY, relativeZ, getEstimatedLightLevel(relativeX, relativeY, relativeZ, blockLights, skyLights, lightData, nmsWorld, sectionPos, sectionLightCache, sectionIndex, blocksLit, skyLit)); } } } @@ -220,6 +232,20 @@ else if (newState.isAir() && !oldState.isAir()) { return copiedChunkPacket; } + public static DataLayer addLayer(List layers, int index, BitSet litSections, BitSet unlitSections, int sectionIndex) { + DataLayer newLayer = new DataLayer(); + layers.add(index, newLayer.getData()); + litSections.set(sectionIndex); + unlitSections.clear(sectionIndex); + return newLayer; + } + + public static DataLayer getLayer(boolean has, List layers, int index) { + return has ? new DataLayer(layers.get(index)) : null; + } + + public record BlockLightData(int sky, int block) {} + public static final int[][] directions = { {0, -1, 0}, {0, 1, 0}, @@ -229,12 +255,10 @@ else if (newState.isAir() && !oldState.isAir()) { {0, 0, 1} }; - public static int getEstimatedLightLevel(int relativeX, int relativeY, int relativeZ, DataLayer blockLights, DataLayer skyLights, ClientboundLightUpdatePacketData lightPacket, ServerLevel nmsWorld, SectionPos sectionPos, Long2ObjectMap sectionLightsCache, int sectionIndex, int blocksLit, int skyLit) { - int ambientDarkness = nmsWorld.getSkyDarken(); - int maxLight = maxLight(relativeX, relativeY, relativeZ, blockLights, skyLights, ambientDarkness); - if (maxLight == 15) { - return 15; - } + public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY, int relativeZ, DataLayer blockLights, DataLayer skyLights, ClientboundLightUpdatePacketData lightPacket, ServerLevel nmsWorld, SectionPos sectionPos, Long2ObjectMap sectionLightsCache, int sectionIndex, int blocksLit, int skyLit) { + int ambientDarkness = 0; + int maxSky = getLight(skyLights, relativeX, relativeY, relativeZ); + int maxBlock = getLight(blockLights, relativeX, relativeY, relativeZ); List blockLookups = null; for (int[] direction : directions) { int yOffest = direction[1]; @@ -248,7 +272,7 @@ public static int getEstimatedLightLevel(int relativeX, int relativeY, int relat blockLookups.add(new BlockPos(sectionPos.minBlockX() + neighborX, sectionPos.minBlockY() + neighborY, sectionPos.minBlockZ() + neighborZ)); continue; } - int light; + int blockLight = -1, skyLight = -1; if (coordOutOfSection(neighborY)) { int adjacentSectionIndex = sectionIndex + yOffest; boolean hasSkyLights = lightPacket.getSkyYMask().get(adjacentSectionIndex); @@ -258,27 +282,28 @@ public static int getEstimatedLightLevel(int relativeX, int relativeY, int relat continue; } int wrappedNeighborY = SectionPos.sectionRelative(neighborY); - int skyLight = hasSkyLights ? new DataLayer(lightPacket.getSkyUpdates().get(skyLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ) - ambientDarkness : 0; - if (skyLight == 15 || !hasBlockLights) { - return skyLight; + if (hasSkyLights) { + skyLight = new DataLayer(lightPacket.getSkyUpdates().get(skyLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ) - ambientDarkness; + } + if (hasBlockLights) { + blockLight = new DataLayer(lightPacket.getBlockUpdates().get(blocksLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ); } - int blockLight = new DataLayer(lightPacket.getBlockUpdates().get(blocksLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ); - light = Math.max(skyLight, blockLight); - Debug.log("Adjacent packet light: " + light); + Debug.log("Adjacent packet light, Block(" + blockLight + ") Sky(" + skyLight + ')'); } else { - light = maxLight(neighborX, neighborY, neighborZ, blockLights, skyLights, ambientDarkness); - Debug.log("Packet light: " + light); + skyLight = getLight(skyLights, neighborX, neighborY, neighborZ); + blockLight = getLight(blockLights, neighborX, neighborY, neighborZ); + Debug.log("Packet light, Block(" + blockLight + ") Sky(" + skyLight + ')'); } - if (light == 15) { - return 15; + if (skyLight > maxSky) { + maxSky = skyLight; } - if (light > maxLight) { - maxLight = light; + if (blockLight > maxBlock) { + maxBlock = blockLight; } } if (blockLookups == null) { - return maxLight; + return new BlockLightData(maxSky, maxBlock); } for (BlockPos blockPos : blockLookups) { SectionPos containingSection = SectionPos.of(blockPos); @@ -289,42 +314,37 @@ public static int getEstimatedLightLevel(int relativeX, int relativeY, int relat DataLayer sectionSkyLights = lightEngine.getLayerListener(LightLayer.SKY).getDataLayerData(containingSection); return new SectionLightCache(sectionBlockLights, sectionSkyLights); }); - int light = sectionLight.getLight(blockPos, ambientDarkness); + int skyLight = sectionLight.getSkyLight(blockPos, ambientDarkness); + int blockLight = sectionLight.getBlockLight(blockPos); + if (skyLight > maxSky) { + maxSky = skyLight; + } + if (blockLight > maxBlock) { + maxBlock = blockLight; + } Bukkit.getScheduler().runTaskLater(Denizen.getInstance(), () -> { NMSHandler.packetHelper.showDebugTestMarker(Bukkit.getOnlinePlayers().iterator().next(), CraftLocation.toBukkit(blockPos), ColorTag.valueOf("red", null), "", 4000); }, 1); - Debug.log("Block light: " + light); - if (light == 15) { - return 15; - } - if (light > maxLight) { - maxLight = light; - } } - return maxLight; + return new BlockLightData(maxSky, maxBlock); } - public static int maxLight(int relativeX, int relativeY, int relativeZ, DataLayer blockLightData, DataLayer skyLightData, int ambientDarkness) { - int skyLight = skyLightData.get(relativeX, relativeY, relativeZ) - ambientDarkness; - if (skyLight == 15) { - return 15; - } - int blockLight = blockLightData.get(relativeX, relativeY, relativeZ); - return Math.max(skyLight, blockLight); + public static int getLight(DataLayer lightData, BlockPos blockPos) { + return lightData != null ? lightData.get(SectionPos.sectionRelative(blockPos.getX()), SectionPos.sectionRelative(blockPos.getY()), SectionPos.sectionRelative(blockPos.getZ())) : -1; + } + + public static int getLight(DataLayer lightData, int relativeX, int relativeY, int relativeZ) { + return lightData != null ? lightData.get(relativeX, relativeY, relativeZ) : -1; } public record SectionLightCache(DataLayer blockLights, DataLayer skyLights) { - public int getLight(BlockPos blockPos, int ambientDarkness) { - int relativeX = SectionPos.sectionRelative(blockPos.getX()); - int relativeY = SectionPos.sectionRelative(blockPos.getY()); - int relativeZ = SectionPos.sectionRelative(blockPos.getZ()); - int skyLight = skyLights != null ? skyLights.get(relativeX, relativeY, relativeZ) - ambientDarkness : 0; - if (skyLight == 15 || blockLights == null) { - return skyLight; - } - int blockLight = blockLights.get(relativeX, relativeY, relativeZ); - return Math.max(skyLight, blockLight); + public int getSkyLight(BlockPos blockPos, int ambientDarkness) { + return getLight(skyLights, blockPos) - ambientDarkness; + } + + public int getBlockLight(BlockPos blockPos) { + return getLight(blockLights, blockPos); } } From 515940fa15edb583803d76df247b43fbeec13e22 Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Thu, 17 Jul 2025 22:16:09 +0100 Subject: [PATCH 09/21] No need for ambient darkness --- .../impl/network/handlers/FakeBlockHelper.java | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java index 7681e6892e..bd122da8dd 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java @@ -256,7 +256,6 @@ public record BlockLightData(int sky, int block) {} }; public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY, int relativeZ, DataLayer blockLights, DataLayer skyLights, ClientboundLightUpdatePacketData lightPacket, ServerLevel nmsWorld, SectionPos sectionPos, Long2ObjectMap sectionLightsCache, int sectionIndex, int blocksLit, int skyLit) { - int ambientDarkness = 0; int maxSky = getLight(skyLights, relativeX, relativeY, relativeZ); int maxBlock = getLight(blockLights, relativeX, relativeY, relativeZ); List blockLookups = null; @@ -283,7 +282,7 @@ public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY } int wrappedNeighborY = SectionPos.sectionRelative(neighborY); if (hasSkyLights) { - skyLight = new DataLayer(lightPacket.getSkyUpdates().get(skyLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ) - ambientDarkness; + skyLight = new DataLayer(lightPacket.getSkyUpdates().get(skyLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ); } if (hasBlockLights) { blockLight = new DataLayer(lightPacket.getBlockUpdates().get(blocksLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ); @@ -314,8 +313,8 @@ public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY DataLayer sectionSkyLights = lightEngine.getLayerListener(LightLayer.SKY).getDataLayerData(containingSection); return new SectionLightCache(sectionBlockLights, sectionSkyLights); }); - int skyLight = sectionLight.getSkyLight(blockPos, ambientDarkness); - int blockLight = sectionLight.getBlockLight(blockPos); + int skyLight = getLight(sectionLight.skyLights(), blockPos); + int blockLight = getLight(sectionLight.blockLights(), blockPos); if (skyLight > maxSky) { maxSky = skyLight; } @@ -337,16 +336,7 @@ public static int getLight(DataLayer lightData, int relativeX, int relativeY, in return lightData != null ? lightData.get(relativeX, relativeY, relativeZ) : -1; } - public record SectionLightCache(DataLayer blockLights, DataLayer skyLights) { - - public int getSkyLight(BlockPos blockPos, int ambientDarkness) { - return getLight(skyLights, blockPos) - ambientDarkness; - } - - public int getBlockLight(BlockPos blockPos) { - return getLight(blockLights, blockPos); - } - } + public record SectionLightCache(DataLayer blockLights, DataLayer skyLights) {} public static boolean coordOutOfSection(int relativeCoord) { return relativeCoord < 0 || relativeCoord > 15; From f2110006d8ee4563a013d997d29046cc501f91ec Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Fri, 18 Jul 2025 20:22:14 +0100 Subject: [PATCH 10/21] Bring back some short-circuiting --- .../network/handlers/FakeBlockHelper.java | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java index bd122da8dd..858cbef6cf 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java @@ -244,7 +244,10 @@ public static DataLayer getLayer(boolean has, List layers, int index) { return has ? new DataLayer(layers.get(index)) : null; } - public record BlockLightData(int sky, int block) {} + public record BlockLightData(int sky, int block) { + public static final BlockLightData MAX_BLOCK_LIGHT = new BlockLightData(0, 15); + public static final BlockLightData MAX_SKY_LIGHT = new BlockLightData(15, 0); + } public static final int[][] directions = { {0, -1, 0}, @@ -256,8 +259,15 @@ public record BlockLightData(int sky, int block) {} }; public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY, int relativeZ, DataLayer blockLights, DataLayer skyLights, ClientboundLightUpdatePacketData lightPacket, ServerLevel nmsWorld, SectionPos sectionPos, Long2ObjectMap sectionLightsCache, int sectionIndex, int blocksLit, int skyLit) { + boolean isSkyBright = nmsWorld.getSkyDarken() == 0; int maxSky = getLight(skyLights, relativeX, relativeY, relativeZ); int maxBlock = getLight(blockLights, relativeX, relativeY, relativeZ); + if (isSkyBright && maxSky == 15) { + return BlockLightData.MAX_SKY_LIGHT; + } + if (maxBlock == 15) { + return BlockLightData.MAX_BLOCK_LIGHT; + } List blockLookups = null; for (int[] direction : directions) { int yOffest = direction[1]; @@ -277,7 +287,6 @@ public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY boolean hasSkyLights = lightPacket.getSkyYMask().get(adjacentSectionIndex); boolean hasBlockLights = lightPacket.getBlockYMask().get(adjacentSectionIndex); if (!hasBlockLights && !hasSkyLights) { - Debug.log("No lights in adjacent section"); continue; } int wrappedNeighborY = SectionPos.sectionRelative(neighborY); @@ -287,12 +296,16 @@ public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY if (hasBlockLights) { blockLight = new DataLayer(lightPacket.getBlockUpdates().get(blocksLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ); } - Debug.log("Adjacent packet light, Block(" + blockLight + ") Sky(" + skyLight + ')'); } else { skyLight = getLight(skyLights, neighborX, neighborY, neighborZ); blockLight = getLight(blockLights, neighborX, neighborY, neighborZ); - Debug.log("Packet light, Block(" + blockLight + ") Sky(" + skyLight + ')'); + } + if (isSkyBright && skyLight == 15) { + return BlockLightData.MAX_SKY_LIGHT; + } + if (blockLight == 15) { + return BlockLightData.MAX_BLOCK_LIGHT; } if (skyLight > maxSky) { maxSky = skyLight; @@ -307,7 +320,7 @@ public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY for (BlockPos blockPos : blockLookups) { SectionPos containingSection = SectionPos.of(blockPos); SectionLightCache sectionLight = sectionLightsCache.computeIfAbsent(containingSection.asLong(), k -> { - Debug.log("Getting section to cache"); + Debug.log(">>>>>>>> Getting section to cache"); LevelLightEngine lightEngine = nmsWorld.getLightEngine(); DataLayer sectionBlockLights = lightEngine.getLayerListener(LightLayer.BLOCK).getDataLayerData(containingSection); DataLayer sectionSkyLights = lightEngine.getLayerListener(LightLayer.SKY).getDataLayerData(containingSection); @@ -315,6 +328,12 @@ public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY }); int skyLight = getLight(sectionLight.skyLights(), blockPos); int blockLight = getLight(sectionLight.blockLights(), blockPos); + if (isSkyBright && skyLight == 15) { + return BlockLightData.MAX_SKY_LIGHT; + } + if (blockLight == 15) { + return BlockLightData.MAX_BLOCK_LIGHT; + } if (skyLight > maxSky) { maxSky = skyLight; } From 15d8371a4d3835c57f09c07a844b3e83576f54f1 Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Thu, 24 Jul 2025 20:15:23 +0100 Subject: [PATCH 11/21] Handle all semi-transparent blocks' lighting --- .../network/handlers/FakeBlockHelper.java | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java index 858cbef6cf..b36f7d88a6 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java @@ -1,11 +1,9 @@ package com.denizenscript.denizen.nms.v1_21.impl.network.handlers; import com.denizenscript.denizen.Denizen; -import com.denizenscript.denizen.nms.NMSHandler; import com.denizenscript.denizen.nms.v1_21.ReflectionMappingsInfo; import com.denizenscript.denizen.objects.LocationTag; import com.denizenscript.denizen.utilities.blocks.FakeBlock; -import com.denizenscript.denizencore.objects.core.ColorTag; import com.denizenscript.denizencore.utilities.ReflectionHelper; import com.denizenscript.denizencore.utilities.debugging.Debug; import io.netty.buffer.Unpooled; @@ -32,7 +30,6 @@ import net.minecraft.world.level.chunk.DataLayer; import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.lighting.LevelLightEngine; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_21_R5.CraftRegistry; @@ -180,19 +177,19 @@ else if (newState.isAir() && !oldState.isAir()) { blockCount--; } states.set(relativeX, relativeY, relativeZ, newState); - BlockEntityType nmsBlockEntityType = MATERIAL_BLOCK_ENTITY_TYPES.get(block.material.getMaterial()); - if (nmsBlockEntityType == null) { + BlockEntityType blockEntityType = MATERIAL_BLOCK_ENTITY_TYPES.get(block.material.getMaterial()); + if (blockEntityType != null) { + BlockEntity createdBlockEntity = blockEntityType.create(CraftLocation.toBlockPosition(block.location), newState); + createdBlockEntity.setLevel(((CraftWorld) world).getHandle()); + Object packetBlockEntityData = CHUNKDATA_BLOCK_ENTITY_CREATE.invoke(createdBlockEntity); + blockEntities.add(packetBlockEntityData); + } + if (blockEntityType == null && newState.isSolidRender()) { continue; } - BlockEntity createdBlockEntity = nmsBlockEntityType.create(CraftLocation.toBlockPosition(block.location), newState); - ServerLevel nmsWorld = ((CraftWorld) world).getHandle(); - createdBlockEntity.setLevel(nmsWorld); - Object packetBlockEntityData = CHUNKDATA_BLOCK_ENTITY_CREATE.invoke(createdBlockEntity); - blockEntities.add(packetBlockEntityData); BlockLightData estimatedLights = getEstimatedLightLevel(relativeX, relativeY, relativeZ, getLayer(hasBlock, lightData.getBlockUpdates(), blocksLit), getLayer(hasSky, lightData.getSkyUpdates(), skyLit), - lightData, nmsWorld, sectionPos, sectionLightCache, sectionIndex, blocksLit, skyLit); - Debug.log("Final data: " + estimatedLights); + lightData, ((CraftWorld) world).getHandle(), sectionPos, sectionLightCache, sectionIndex, blocksLit, skyLit); if (estimatedLights.block() > 0) { DataLayer blockLights; if (!hasBlock) { @@ -274,7 +271,7 @@ public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY int neighborX = relativeX + direction[0]; int neighborY = relativeY + yOffest; int neighborZ = relativeZ + direction[2]; - if (coordOutOfSection(neighborX) || coordOutOfSection(neighborZ)) { + if (posOutOfSection(neighborX) || posOutOfSection(neighborZ)) { if (blockLookups == null) { blockLookups = new ArrayList<>(2); } @@ -282,7 +279,7 @@ public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY continue; } int blockLight = -1, skyLight = -1; - if (coordOutOfSection(neighborY)) { + if (posOutOfSection(neighborY)) { int adjacentSectionIndex = sectionIndex + yOffest; boolean hasSkyLights = lightPacket.getSkyYMask().get(adjacentSectionIndex); boolean hasBlockLights = lightPacket.getBlockYMask().get(adjacentSectionIndex); @@ -320,7 +317,6 @@ public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY for (BlockPos blockPos : blockLookups) { SectionPos containingSection = SectionPos.of(blockPos); SectionLightCache sectionLight = sectionLightsCache.computeIfAbsent(containingSection.asLong(), k -> { - Debug.log(">>>>>>>> Getting section to cache"); LevelLightEngine lightEngine = nmsWorld.getLightEngine(); DataLayer sectionBlockLights = lightEngine.getLayerListener(LightLayer.BLOCK).getDataLayerData(containingSection); DataLayer sectionSkyLights = lightEngine.getLayerListener(LightLayer.SKY).getDataLayerData(containingSection); @@ -340,9 +336,6 @@ public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY if (blockLight > maxBlock) { maxBlock = blockLight; } - Bukkit.getScheduler().runTaskLater(Denizen.getInstance(), () -> { - NMSHandler.packetHelper.showDebugTestMarker(Bukkit.getOnlinePlayers().iterator().next(), CraftLocation.toBukkit(blockPos), ColorTag.valueOf("red", null), "", 4000); - }, 1); } return new BlockLightData(maxSky, maxBlock); } @@ -357,7 +350,7 @@ public static int getLight(DataLayer lightData, int relativeX, int relativeY, in public record SectionLightCache(DataLayer blockLights, DataLayer skyLights) {} - public static boolean coordOutOfSection(int relativeCoord) { - return relativeCoord < 0 || relativeCoord > 15; + public static boolean posOutOfSection(int relativePos) { + return relativePos < 0 || relativePos > 15; } } From 47c2988cc25b9f410dc7940b4a97612f97455d7a Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Sun, 27 Jul 2025 13:00:52 +0100 Subject: [PATCH 12/21] Naming, comments --- .../network/handlers/FakeBlockHelper.java | 81 +++++++++---------- 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java index b36f7d88a6..b18b4da774 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java @@ -113,7 +113,7 @@ public static void copyPacketPaperPatch(ClientboundLevelChunkWithLightPacket new public static ClientboundLevelChunkWithLightPacket handleMapChunkPacket(World world, ClientboundLevelChunkWithLightPacket originalChunkPacket, int chunkX, int chunkZ, List blocksInChunk, FakeBlock.FakeBlockMap fakeBlockMap) throws Throwable { ClientboundLevelChunkWithLightPacket copiedChunkPacket = DenizenNetworkManagerImpl.copyPacket(originalChunkPacket, ClientboundLevelChunkWithLightPacket.STREAM_CODEC); copyPacketPaperPatch(copiedChunkPacket); - // A list of block entities sent with the chunk data + // A list of ClientboundLevelChunkPacketData$BlockEntityInfo List blockEntities = (List) CHUNKDATA_BLOCK_ENTITIES.invoke(copiedChunkPacket.getChunkData()); LocationTag location = new LocationTag(world, 0, 0, 0); ListIterator blockEntitiesIterator = blockEntities.listIterator(); @@ -132,27 +132,26 @@ public static ClientboundLevelChunkWithLightPacket handleMapChunkPacket(World wo blockEntitiesIterator.remove(); } } - // Get the original chunk data to read, and a buf of the same size to write + // Get the original chunk data to read, and a new buf of the same size to write FriendlyByteBuf rawChunkData = originalChunkPacket.getChunkData().getReadBuffer(); FriendlyByteBuf newChunkData = new FriendlyByteBuf(Unpooled.buffer(rawChunkData.readableBytes())); - int worldMinY = world.getMinHeight(); - int worldMaxY = world.getMaxHeight(); - int minChunkY = SectionPos.blockToSectionCoord(worldMinY); - int maxChunkY = SectionPos.blockToSectionCoord(worldMaxY); + final int minChunkY = SectionPos.blockToSectionCoord(world.getMinHeight()); + final int maxChunkY = SectionPos.blockToSectionCoord(world.getMaxHeight()); Registry biomeRegistry = CraftRegistry.getMinecraftRegistry(Registries.BIOME); int blocksLit = 0, skyLit = 0; - // These are section coords, iterating through every chunk section ClientboundLightUpdatePacketData lightData = copiedChunkPacket.getLightData(); Long2ObjectMap sectionLightCache = new Long2ObjectOpenHashMap<>(); + // These are section coords, iterating through every chunk section for (int y = minChunkY; y < maxChunkY; y++) { SectionPos sectionPos = SectionPos.of(chunkX, y, chunkZ); + // The light data counts up from 0 instead of minChunkY, and has a buffer of 1 extra section above and below the world (hence + 1) int sectionIndex = y + Math.abs(minChunkY) + 1; - boolean hasSky = false, hasBlock = false; + boolean hasSkyLight = false, hasBlockLight = false; if (lightData.getBlockYMask().get(sectionIndex)) { - hasBlock = true; + hasBlockLight = true; } if (lightData.getSkyYMask().get(sectionIndex)) { - hasSky = true; + hasSkyLight = true; } int blockCount = rawChunkData.readShort(); PalettedContainer states = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES); @@ -188,13 +187,13 @@ else if (newState.isAir() && !oldState.isAir()) { continue; } BlockLightData estimatedLights = getEstimatedLightLevel(relativeX, relativeY, relativeZ, - getLayer(hasBlock, lightData.getBlockUpdates(), blocksLit), getLayer(hasSky, lightData.getSkyUpdates(), skyLit), + getLayer(hasBlockLight, lightData.getBlockUpdates(), blocksLit), getLayer(hasSkyLight, lightData.getSkyUpdates(), skyLit), lightData, ((CraftWorld) world).getHandle(), sectionPos, sectionLightCache, sectionIndex, blocksLit, skyLit); if (estimatedLights.block() > 0) { DataLayer blockLights; - if (!hasBlock) { + if (!hasBlockLight) { blockLights = addLayer(lightData.getBlockUpdates(), blocksLit, lightData.getBlockYMask(), lightData.getEmptyBlockYMask(), sectionIndex); - hasBlock = true; + hasBlockLight = true; } else { blockLights = new DataLayer(lightData.getBlockUpdates().get(blocksLit)); @@ -203,9 +202,9 @@ else if (newState.isAir() && !oldState.isAir()) { } if (estimatedLights.sky() > 0) { DataLayer skyLights; - if (!hasSky) { + if (!hasSkyLight) { skyLights = addLayer(lightData.getSkyUpdates(), skyLit, lightData.getSkyYMask(), lightData.getEmptySkyYMask(), sectionIndex); - hasSky = true; + hasSkyLight = true; } else { skyLights = new DataLayer(lightData.getSkyUpdates().get(skyLit)); @@ -215,10 +214,10 @@ else if (newState.isAir() && !oldState.isAir()) { } } } - if (hasBlock) { + if (hasBlockLight) { blocksLit++; } - if (hasSky) { + if (hasSkyLight) { skyLit++; } newChunkData.writeShort(blockCount); @@ -246,6 +245,8 @@ public record BlockLightData(int sky, int block) { public static final BlockLightData MAX_SKY_LIGHT = new BlockLightData(15, 0); } + public record SectionLightCache(DataLayer blockLights, DataLayer skyLights) {} + public static final int[][] directions = { {0, -1, 0}, {0, 1, 0}, @@ -255,14 +256,14 @@ public record BlockLightData(int sky, int block) { {0, 0, 1} }; - public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY, int relativeZ, DataLayer blockLights, DataLayer skyLights, ClientboundLightUpdatePacketData lightPacket, ServerLevel nmsWorld, SectionPos sectionPos, Long2ObjectMap sectionLightsCache, int sectionIndex, int blocksLit, int skyLit) { + public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY, int relativeZ, DataLayer blockLights, DataLayer skyLights, ClientboundLightUpdatePacketData lightData, ServerLevel nmsWorld, SectionPos sectionPos, Long2ObjectMap sectionLightsCache, int sectionIndex, int blocksLit, int skyLit) { boolean isSkyBright = nmsWorld.getSkyDarken() == 0; - int maxSky = getLight(skyLights, relativeX, relativeY, relativeZ); - int maxBlock = getLight(blockLights, relativeX, relativeY, relativeZ); - if (isSkyBright && maxSky == 15) { + int maxSkyLight = getLight(skyLights, relativeX, relativeY, relativeZ); + int maxBlockLight = getLight(blockLights, relativeX, relativeY, relativeZ); + if (isSkyBright && maxSkyLight == 15) { return BlockLightData.MAX_SKY_LIGHT; } - if (maxBlock == 15) { + if (maxBlockLight == 15) { return BlockLightData.MAX_BLOCK_LIGHT; } List blockLookups = null; @@ -281,17 +282,17 @@ public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY int blockLight = -1, skyLight = -1; if (posOutOfSection(neighborY)) { int adjacentSectionIndex = sectionIndex + yOffest; - boolean hasSkyLights = lightPacket.getSkyYMask().get(adjacentSectionIndex); - boolean hasBlockLights = lightPacket.getBlockYMask().get(adjacentSectionIndex); - if (!hasBlockLights && !hasSkyLights) { + boolean hasSkyLight = lightData.getSkyYMask().get(adjacentSectionIndex); + boolean hasBlockLight = lightData.getBlockYMask().get(adjacentSectionIndex); + if (!hasBlockLight && !hasSkyLight) { continue; } int wrappedNeighborY = SectionPos.sectionRelative(neighborY); - if (hasSkyLights) { - skyLight = new DataLayer(lightPacket.getSkyUpdates().get(skyLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ); + if (hasSkyLight) { + skyLight = new DataLayer(lightData.getSkyUpdates().get(skyLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ); } - if (hasBlockLights) { - blockLight = new DataLayer(lightPacket.getBlockUpdates().get(blocksLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ); + if (hasBlockLight) { + blockLight = new DataLayer(lightData.getBlockUpdates().get(blocksLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ); } } else { @@ -304,15 +305,15 @@ public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY if (blockLight == 15) { return BlockLightData.MAX_BLOCK_LIGHT; } - if (skyLight > maxSky) { - maxSky = skyLight; + if (skyLight > maxSkyLight) { + maxSkyLight = skyLight; } - if (blockLight > maxBlock) { - maxBlock = blockLight; + if (blockLight > maxBlockLight) { + maxBlockLight = blockLight; } } if (blockLookups == null) { - return new BlockLightData(maxSky, maxBlock); + return new BlockLightData(maxSkyLight, maxBlockLight); } for (BlockPos blockPos : blockLookups) { SectionPos containingSection = SectionPos.of(blockPos); @@ -330,14 +331,14 @@ public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY if (blockLight == 15) { return BlockLightData.MAX_BLOCK_LIGHT; } - if (skyLight > maxSky) { - maxSky = skyLight; + if (skyLight > maxSkyLight) { + maxSkyLight = skyLight; } - if (blockLight > maxBlock) { - maxBlock = blockLight; + if (blockLight > maxBlockLight) { + maxBlockLight = blockLight; } } - return new BlockLightData(maxSky, maxBlock); + return new BlockLightData(maxSkyLight, maxBlockLight); } public static int getLight(DataLayer lightData, BlockPos blockPos) { @@ -348,8 +349,6 @@ public static int getLight(DataLayer lightData, int relativeX, int relativeY, in return lightData != null ? lightData.get(relativeX, relativeY, relativeZ) : -1; } - public record SectionLightCache(DataLayer blockLights, DataLayer skyLights) {} - public static boolean posOutOfSection(int relativePos) { return relativePos < 0 || relativePos > 15; } From 54b908c55b6a27162ca8409bc4addc7a5e46bcef Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Sun, 27 Jul 2025 18:11:20 +0100 Subject: [PATCH 13/21] Skip lighting if the existing state isn't solid --- .../nms/v1_21/impl/network/handlers/FakeBlockHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java index b18b4da774..deba5a6b61 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java @@ -183,7 +183,7 @@ else if (newState.isAir() && !oldState.isAir()) { Object packetBlockEntityData = CHUNKDATA_BLOCK_ENTITY_CREATE.invoke(createdBlockEntity); blockEntities.add(packetBlockEntityData); } - if (blockEntityType == null && newState.isSolidRender()) { + if (!oldState.isSolidRender() || (blockEntityType == null && newState.isSolidRender())) { continue; } BlockLightData estimatedLights = getEstimatedLightLevel(relativeX, relativeY, relativeZ, From 97e44bcd3924e7a2edffd408ed1154161eb61475 Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Mon, 28 Jul 2025 14:39:33 +0100 Subject: [PATCH 14/21] Split up fake block packet handlers --- .../handlers/DenizenNetworkManagerImpl.java | 4 +- .../packet/FakeBlocksPacketHandlers.java | 155 +++++++++--------- 2 files changed, 82 insertions(+), 77 deletions(-) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/DenizenNetworkManagerImpl.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/DenizenNetworkManagerImpl.java index bcde3c8c85..7841537489 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/DenizenNetworkManagerImpl.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/DenizenNetworkManagerImpl.java @@ -66,7 +66,7 @@ public static > T copyPacket(T original, StreamCodec> { - Packet handlePacket(DenizenNetworkManagerImpl networkManager, T packet) throws Exception; + Packet handlePacket(DenizenNetworkManagerImpl networkManager, T packet) throws Throwable; } public static final Map>, List>> packetHandlers = new HashMap<>(); @@ -376,7 +376,7 @@ public Packet processPacketHandlersFor(Packet processShowFakeForPacket(DenizenNetworkManagerImpl networkManager, Packet packet) { + public static ClientboundLevelChunkWithLightPacket processLevelChunkWithLightPacket(DenizenNetworkManagerImpl networkManager, ClientboundLevelChunkWithLightPacket chunkPacket) throws Throwable { if (FakeBlock.blocks.isEmpty()) { - return packet; + return chunkPacket; } - try { - if (packet instanceof ClientboundLevelChunkWithLightPacket) { - FakeBlock.FakeBlockMap map = FakeBlock.blocks.get(networkManager.player.getUUID()); - if (map == null) { - return packet; - } - int chunkX = ((ClientboundLevelChunkWithLightPacket) packet).getX(); - int chunkZ = ((ClientboundLevelChunkWithLightPacket) packet).getZ(); - ChunkCoordinate chunkCoord = new ChunkCoordinate(chunkX, chunkZ, networkManager.player.level().getWorld().getName()); - List blocks = FakeBlock.getFakeBlocksFor(networkManager.player.getUUID(), chunkCoord); - if (blocks == null || blocks.isEmpty()) { - return packet; - } - return FakeBlockHelper.handleMapChunkPacket(networkManager.player.getBukkitEntity().getWorld(), (ClientboundLevelChunkWithLightPacket) packet, chunkX, chunkZ, blocks, map); - } - else if (packet instanceof ClientboundSectionBlocksUpdatePacket sectionBlocksUpdatePacket) { - FakeBlock.FakeBlockMap map = FakeBlock.blocks.get(networkManager.player.getUUID()); - if (map == null) { - return sectionBlocksUpdatePacket; - } - SectionPos coord = (SectionPos) SECTIONPOS_MULTIBLOCKCHANGE.get(sectionBlocksUpdatePacket); - ChunkCoordinate coordinateDenizen = new ChunkCoordinate(coord.getX(), coord.getZ(), networkManager.player.level().getWorld().getName()); - if (!map.byChunk.containsKey(coordinateDenizen)) { - return sectionBlocksUpdatePacket; - } - ClientboundSectionBlocksUpdatePacket newPacket = DenizenNetworkManagerImpl.copyPacket(sectionBlocksUpdatePacket, ClientboundSectionBlocksUpdatePacket.STREAM_CODEC); - LocationTag location = new LocationTag(networkManager.player.level().getWorld(), 0, 0, 0); - short[] originalOffsetArray = (short[])OFFSETARRAY_MULTIBLOCKCHANGE.get(newPacket); - BlockState[] originalDataArray = (BlockState[])BLOCKARRAY_MULTIBLOCKCHANGE.get(newPacket); - short[] offsetArray = Arrays.copyOf(originalOffsetArray, originalOffsetArray.length); - BlockState[] dataArray = Arrays.copyOf(originalDataArray, originalDataArray.length); - OFFSETARRAY_MULTIBLOCKCHANGE.set(newPacket, offsetArray); - BLOCKARRAY_MULTIBLOCKCHANGE.set(newPacket, dataArray); - for (int i = 0; i < offsetArray.length; i++) { - short offset = offsetArray[i]; - BlockPos pos = coord.relativeToBlockPos(offset); - location.setX(pos.getX()); - location.setY(pos.getY()); - location.setZ(pos.getZ()); - FakeBlock block = map.byLocation.get(location); - if (block != null) { - dataArray[i] = FakeBlockHelper.getNMSState(block); - } - } - return newPacket; - } - else if (packet instanceof ClientboundBlockUpdatePacket) { - BlockPos pos = ((ClientboundBlockUpdatePacket) packet).getPos(); - LocationTag loc = new LocationTag(networkManager.player.level().getWorld(), pos.getX(), pos.getY(), pos.getZ()); - FakeBlock block = FakeBlock.getFakeBlockFor(networkManager.player.getUUID(), loc); - if (block != null) { - ClientboundBlockUpdatePacket newPacket = new ClientboundBlockUpdatePacket(((ClientboundBlockUpdatePacket) packet).getPos(), FakeBlockHelper.getNMSState(block)); - return newPacket; - } - } - else if (packet instanceof ClientboundBlockChangedAckPacket) { - // TODO: 1.19: Can no longer determine what block this packet is for. Would have to track separately? Possibly from the inbound packet rather than the outbound one. - /* - ClientboundBlockChangedAckPacket origPack = (ClientboundBlockChangedAckPacket) packet; - BlockPos pos = origPack.pos(); - LocationTag loc = new LocationTag(player.getLevel().getWorld(), pos.getX(), pos.getY(), pos.getZ()); - FakeBlock block = FakeBlock.getFakeBlockFor(player.getUUID(), loc); - if (block != null) { - ClientboundBlockChangedAckPacket newPacket = new ClientboundBlockChangedAckPacket(origPack.pos(), FakeBlockHelper.getNMSState(block), origPack.action(), false); - oldManager.send(newPacket, genericfuturelistener); - return true; - }*/ + FakeBlock.FakeBlockMap map = FakeBlock.blocks.get(networkManager.player.getUUID()); + if (map == null) { + return chunkPacket; + } + int chunkX = chunkPacket.getX(); + int chunkZ = chunkPacket.getZ(); + ChunkCoordinate chunkCoord = new ChunkCoordinate(chunkX, chunkZ, networkManager.player.level().getWorld().getName()); + List blocks = FakeBlock.getFakeBlocksFor(networkManager.player.getUUID(), chunkCoord); + if (blocks == null || blocks.isEmpty()) { + return chunkPacket; + } + return FakeBlockHelper.handleMapChunkPacket(networkManager.player.getBukkitEntity().getWorld(), chunkPacket, chunkX, chunkZ, blocks, map); + } + + public static ClientboundSectionBlocksUpdatePacket processSectionBlocksUpdatePacket(DenizenNetworkManagerImpl networkManager, ClientboundSectionBlocksUpdatePacket sectionUpdatePacket) throws IllegalAccessException { + if (FakeBlock.blocks.isEmpty()) { + return sectionUpdatePacket; + } + FakeBlock.FakeBlockMap map = FakeBlock.blocks.get(networkManager.player.getUUID()); + if (map == null) { + return sectionUpdatePacket; + } + SectionPos coord = (SectionPos) SECTIONPOS_MULTIBLOCKCHANGE.get(sectionUpdatePacket); + ChunkCoordinate coordinateDenizen = new ChunkCoordinate(coord.getX(), coord.getZ(), networkManager.player.level().getWorld().getName()); + if (!map.byChunk.containsKey(coordinateDenizen)) { + return sectionUpdatePacket; + } + ClientboundSectionBlocksUpdatePacket newPacket = DenizenNetworkManagerImpl.copyPacket(sectionUpdatePacket, ClientboundSectionBlocksUpdatePacket.STREAM_CODEC); + LocationTag location = new LocationTag(networkManager.player.level().getWorld(), 0, 0, 0); + short[] originalOffsetArray = (short[])OFFSETARRAY_MULTIBLOCKCHANGE.get(newPacket); + BlockState[] originalDataArray = (BlockState[])BLOCKARRAY_MULTIBLOCKCHANGE.get(newPacket); + short[] offsetArray = Arrays.copyOf(originalOffsetArray, originalOffsetArray.length); + BlockState[] dataArray = Arrays.copyOf(originalDataArray, originalDataArray.length); + OFFSETARRAY_MULTIBLOCKCHANGE.set(newPacket, offsetArray); + BLOCKARRAY_MULTIBLOCKCHANGE.set(newPacket, dataArray); + for (int i = 0; i < offsetArray.length; i++) { + short offset = offsetArray[i]; + BlockPos pos = coord.relativeToBlockPos(offset); + location.setX(pos.getX()); + location.setY(pos.getY()); + location.setZ(pos.getZ()); + FakeBlock block = map.byLocation.get(location); + if (block != null) { + dataArray[i] = FakeBlockHelper.getNMSState(block); } } - catch (Throwable ex) { - Debug.echoError(ex); + return newPacket; + } + + public static ClientboundBlockUpdatePacket processBlockUpdatePacket(DenizenNetworkManagerImpl networkManager, ClientboundBlockUpdatePacket blockUpdatePacket) { + if (FakeBlock.blocks.isEmpty()) { + return blockUpdatePacket; + } + BlockPos pos = blockUpdatePacket.getPos(); + LocationTag loc = new LocationTag(networkManager.player.level().getWorld(), pos.getX(), pos.getY(), pos.getZ()); + FakeBlock block = FakeBlock.getFakeBlockFor(networkManager.player.getUUID(), loc); + if (block != null) { + ClientboundBlockUpdatePacket newPacket = new ClientboundBlockUpdatePacket(blockUpdatePacket.getPos(), FakeBlockHelper.getNMSState(block)); + return newPacket; + } + return blockUpdatePacket; + } + + // TODO: 1.19: Can no longer determine what block this packet is for. Would have to track separately? Possibly from the inbound packet rather than the outbound one. + /* + public static ClientboundBlockChangedAckPacket processBlockChangedAckPacket(DenizenNetworkManagerImpl networkManager, ClientboundBlockChangedAckPacket blockChangedAckPacket) { + if (FakeBlock.blocks.isEmpty()) { + return blockChangedAckPacket; + } + BlockPos pos = blockChangedAckPacket.pos(); + LocationTag loc = new LocationTag(player.getLevel().getWorld(), pos.getX(), pos.getY(), pos.getZ()); + FakeBlock block = FakeBlock.getFakeBlockFor(player.getUUID(), loc); + if (block != null) { + ClientboundBlockChangedAckPacket newPacket = new ClientboundBlockChangedAckPacket(blockChangedAckPacket.pos(), FakeBlockHelper.getNMSState(block), blockChangedAckPacket.action(), false); + oldManager.send(newPacket, genericfuturelistener); + return true; } - return packet; } + */ } From 2024dbf25782304bc8b6055f05cc4f38acebe2c7 Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Mon, 28 Jul 2025 16:11:45 +0100 Subject: [PATCH 15/21] Cleanup fake block handlers --- .../packet/FakeBlocksPacketHandlers.java | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java index e7e76b3723..c74d60d2c7 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java @@ -7,11 +7,12 @@ import com.denizenscript.denizen.utilities.blocks.ChunkCoordinate; import com.denizenscript.denizen.utilities.blocks.FakeBlock; import com.denizenscript.denizencore.utilities.ReflectionHelper; -import com.denizenscript.denizencore.utilities.debugging.Debug; +import it.unimi.dsi.fastutil.shorts.ShortArraySet; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; -import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.*; +import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; +import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; +import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket; import net.minecraft.world.level.block.state.BlockState; import java.lang.reflect.Field; @@ -61,26 +62,21 @@ public static ClientboundSectionBlocksUpdatePacket processSectionBlocksUpdatePac if (!map.byChunk.containsKey(coordinateDenizen)) { return sectionUpdatePacket; } - ClientboundSectionBlocksUpdatePacket newPacket = DenizenNetworkManagerImpl.copyPacket(sectionUpdatePacket, ClientboundSectionBlocksUpdatePacket.STREAM_CODEC); + short[] originalOffsetArray = (short[])OFFSETARRAY_MULTIBLOCKCHANGE.get(sectionUpdatePacket); + BlockState[] originalStatesArray = (BlockState[])BLOCKARRAY_MULTIBLOCKCHANGE.get(sectionUpdatePacket); + BlockState[] statesArray = Arrays.copyOf(originalStatesArray, originalStatesArray.length); LocationTag location = new LocationTag(networkManager.player.level().getWorld(), 0, 0, 0); - short[] originalOffsetArray = (short[])OFFSETARRAY_MULTIBLOCKCHANGE.get(newPacket); - BlockState[] originalDataArray = (BlockState[])BLOCKARRAY_MULTIBLOCKCHANGE.get(newPacket); - short[] offsetArray = Arrays.copyOf(originalOffsetArray, originalOffsetArray.length); - BlockState[] dataArray = Arrays.copyOf(originalDataArray, originalDataArray.length); - OFFSETARRAY_MULTIBLOCKCHANGE.set(newPacket, offsetArray); - BLOCKARRAY_MULTIBLOCKCHANGE.set(newPacket, dataArray); - for (int i = 0; i < offsetArray.length; i++) { - short offset = offsetArray[i]; - BlockPos pos = coord.relativeToBlockPos(offset); - location.setX(pos.getX()); - location.setY(pos.getY()); - location.setZ(pos.getZ()); + for (int i = 0; i < originalOffsetArray.length; i++) { + short offset = originalOffsetArray[i]; + location.setX(coord.relativeToBlockX(offset)); + location.setY(coord.relativeToBlockY(offset)); + location.setZ(coord.relativeToBlockZ(offset)); FakeBlock block = map.byLocation.get(location); if (block != null) { - dataArray[i] = FakeBlockHelper.getNMSState(block); + statesArray[i] = FakeBlockHelper.getNMSState(block); } } - return newPacket; + return new ClientboundSectionBlocksUpdatePacket(coord, new ShortArraySet(originalOffsetArray), statesArray); } public static ClientboundBlockUpdatePacket processBlockUpdatePacket(DenizenNetworkManagerImpl networkManager, ClientboundBlockUpdatePacket blockUpdatePacket) { @@ -91,8 +87,7 @@ public static ClientboundBlockUpdatePacket processBlockUpdatePacket(DenizenNetwo LocationTag loc = new LocationTag(networkManager.player.level().getWorld(), pos.getX(), pos.getY(), pos.getZ()); FakeBlock block = FakeBlock.getFakeBlockFor(networkManager.player.getUUID(), loc); if (block != null) { - ClientboundBlockUpdatePacket newPacket = new ClientboundBlockUpdatePacket(blockUpdatePacket.getPos(), FakeBlockHelper.getNMSState(block)); - return newPacket; + return new ClientboundBlockUpdatePacket(blockUpdatePacket.getPos(), FakeBlockHelper.getNMSState(block)); } return blockUpdatePacket; } From 365d4b6eded8ea2ea1cae9b315199945bc5569bd Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Mon, 28 Jul 2025 16:53:51 +0100 Subject: [PATCH 16/21] Don't make new section blocks packet unless needed --- .../network/handlers/packet/FakeBlocksPacketHandlers.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java index c74d60d2c7..e5e10d49b2 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java @@ -66,6 +66,7 @@ public static ClientboundSectionBlocksUpdatePacket processSectionBlocksUpdatePac BlockState[] originalStatesArray = (BlockState[])BLOCKARRAY_MULTIBLOCKCHANGE.get(sectionUpdatePacket); BlockState[] statesArray = Arrays.copyOf(originalStatesArray, originalStatesArray.length); LocationTag location = new LocationTag(networkManager.player.level().getWorld(), 0, 0, 0); + boolean hasAny = false; for (int i = 0; i < originalOffsetArray.length; i++) { short offset = originalOffsetArray[i]; location.setX(coord.relativeToBlockX(offset)); @@ -74,8 +75,12 @@ public static ClientboundSectionBlocksUpdatePacket processSectionBlocksUpdatePac FakeBlock block = map.byLocation.get(location); if (block != null) { statesArray[i] = FakeBlockHelper.getNMSState(block); + hasAny = true; } } + if (!hasAny) { + return sectionUpdatePacket; + } return new ClientboundSectionBlocksUpdatePacket(coord, new ShortArraySet(originalOffsetArray), statesArray); } From 19c450b23c003fd4ce49de2077433fa1a10f842a Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Mon, 28 Jul 2025 17:20:42 +0100 Subject: [PATCH 17/21] Section block packets: use `MethodHandle`s --- .../packet/FakeBlocksPacketHandlers.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java index e5e10d49b2..7f513acd96 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/packet/FakeBlocksPacketHandlers.java @@ -15,7 +15,7 @@ import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket; import net.minecraft.world.level.block.state.BlockState; -import java.lang.reflect.Field; +import java.lang.invoke.MethodHandle; import java.util.Arrays; import java.util.List; @@ -27,9 +27,9 @@ public static void registerHandlers() { DenizenNetworkManagerImpl.registerPacketHandler(ClientboundBlockUpdatePacket.class, FakeBlocksPacketHandlers::processBlockUpdatePacket); } - public static Field SECTIONPOS_MULTIBLOCKCHANGE = ReflectionHelper.getFields(ClientboundSectionBlocksUpdatePacket.class).get(ReflectionMappingsInfo.ClientboundSectionBlocksUpdatePacket_sectionPos, SectionPos.class); - public static Field OFFSETARRAY_MULTIBLOCKCHANGE = ReflectionHelper.getFields(ClientboundSectionBlocksUpdatePacket.class).get(ReflectionMappingsInfo.ClientboundSectionBlocksUpdatePacket_positions, short[].class); - public static Field BLOCKARRAY_MULTIBLOCKCHANGE = ReflectionHelper.getFields(ClientboundSectionBlocksUpdatePacket.class).get(ReflectionMappingsInfo.ClientboundSectionBlocksUpdatePacket_states, BlockState[].class); + public static final MethodHandle SECTIONPOS_MULTIBLOCKCHANGE = ReflectionHelper.getFields(ClientboundSectionBlocksUpdatePacket.class).getGetter(ReflectionMappingsInfo.ClientboundSectionBlocksUpdatePacket_sectionPos, SectionPos.class); + public static final MethodHandle OFFSETARRAY_MULTIBLOCKCHANGE = ReflectionHelper.getFields(ClientboundSectionBlocksUpdatePacket.class).getGetter(ReflectionMappingsInfo.ClientboundSectionBlocksUpdatePacket_positions, short[].class); + public static final MethodHandle BLOCKARRAY_MULTIBLOCKCHANGE = ReflectionHelper.getFields(ClientboundSectionBlocksUpdatePacket.class).getGetter(ReflectionMappingsInfo.ClientboundSectionBlocksUpdatePacket_states, BlockState[].class); public static ClientboundLevelChunkWithLightPacket processLevelChunkWithLightPacket(DenizenNetworkManagerImpl networkManager, ClientboundLevelChunkWithLightPacket chunkPacket) throws Throwable { if (FakeBlock.blocks.isEmpty()) { @@ -49,7 +49,7 @@ public static ClientboundLevelChunkWithLightPacket processLevelChunkWithLightPac return FakeBlockHelper.handleMapChunkPacket(networkManager.player.getBukkitEntity().getWorld(), chunkPacket, chunkX, chunkZ, blocks, map); } - public static ClientboundSectionBlocksUpdatePacket processSectionBlocksUpdatePacket(DenizenNetworkManagerImpl networkManager, ClientboundSectionBlocksUpdatePacket sectionUpdatePacket) throws IllegalAccessException { + public static ClientboundSectionBlocksUpdatePacket processSectionBlocksUpdatePacket(DenizenNetworkManagerImpl networkManager, ClientboundSectionBlocksUpdatePacket sectionUpdatePacket) throws Throwable { if (FakeBlock.blocks.isEmpty()) { return sectionUpdatePacket; } @@ -57,13 +57,13 @@ public static ClientboundSectionBlocksUpdatePacket processSectionBlocksUpdatePac if (map == null) { return sectionUpdatePacket; } - SectionPos coord = (SectionPos) SECTIONPOS_MULTIBLOCKCHANGE.get(sectionUpdatePacket); + SectionPos coord = (SectionPos) SECTIONPOS_MULTIBLOCKCHANGE.invokeExact(sectionUpdatePacket); ChunkCoordinate coordinateDenizen = new ChunkCoordinate(coord.getX(), coord.getZ(), networkManager.player.level().getWorld().getName()); if (!map.byChunk.containsKey(coordinateDenizen)) { return sectionUpdatePacket; } - short[] originalOffsetArray = (short[])OFFSETARRAY_MULTIBLOCKCHANGE.get(sectionUpdatePacket); - BlockState[] originalStatesArray = (BlockState[])BLOCKARRAY_MULTIBLOCKCHANGE.get(sectionUpdatePacket); + short[] originalOffsetArray = (short[]) OFFSETARRAY_MULTIBLOCKCHANGE.invokeExact(sectionUpdatePacket); + BlockState[] originalStatesArray = (BlockState[]) BLOCKARRAY_MULTIBLOCKCHANGE.invokeExact(sectionUpdatePacket); BlockState[] statesArray = Arrays.copyOf(originalStatesArray, originalStatesArray.length); LocationTag location = new LocationTag(networkManager.player.level().getWorld(), 0, 0, 0); boolean hasAny = false; From 4d00a6e97509aff1c32f2ed41b96edebc2eeba36 Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Tue, 29 Jul 2025 15:38:59 +0100 Subject: [PATCH 18/21] Minor change --- .../nms/v1_21/impl/network/handlers/FakeBlockHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java index deba5a6b61..23bb735dd7 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java @@ -143,7 +143,6 @@ public static ClientboundLevelChunkWithLightPacket handleMapChunkPacket(World wo Long2ObjectMap sectionLightCache = new Long2ObjectOpenHashMap<>(); // These are section coords, iterating through every chunk section for (int y = minChunkY; y < maxChunkY; y++) { - SectionPos sectionPos = SectionPos.of(chunkX, y, chunkZ); // The light data counts up from 0 instead of minChunkY, and has a buffer of 1 extra section above and below the world (hence + 1) int sectionIndex = y + Math.abs(minChunkY) + 1; boolean hasSkyLight = false, hasBlockLight = false; @@ -159,6 +158,7 @@ public static ClientboundLevelChunkWithLightPacket handleMapChunkPacket(World wo PalettedContainer biomes = new PalettedContainer<>(biomeRegistry, biomeRegistry.getValueOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES); biomes.read(rawChunkData); if (anyBlocksInSection(blocksInChunk, y)) { + SectionPos sectionPos = SectionPos.of(chunkX, y, chunkZ); int minY = SectionPos.sectionToBlockCoord(y); int maxY = minY + 16; for (FakeBlock block : blocksInChunk) { From 29b3a87e59d0f6c54301a542a71c0d4c5b19b6a3 Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Wed, 24 Sep 2025 00:46:31 +0100 Subject: [PATCH 19/21] Reflection fixup --- .../nms/v1_21/impl/network/handlers/FakeBlockHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java index 23bb735dd7..74b1ec1912 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java @@ -224,7 +224,7 @@ else if (newState.isAir() && !oldState.isAir()) { states.write(newChunkData); biomes.write(newChunkData); } - CHUNKDATA_BUFFER_SETTER.invoke(copiedChunkPacket.getChunkData(), newChunkData.array()); + CHUNKDATA_BUFFER_SETTER.invokeExact(copiedChunkPacket.getChunkData(), newChunkData.array()); return copiedChunkPacket; } From 6acdb14a95589bf4baf773aca375e69cb52e884c Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Sat, 27 Sep 2025 11:16:10 +0100 Subject: [PATCH 20/21] Persist light through fake blocks between sections --- .../denizen/utilities/blocks/FakeBlock.java | 2 + .../network/handlers/FakeBlockHelper.java | 41 ++++++++++++++----- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/plugin/src/main/java/com/denizenscript/denizen/utilities/blocks/FakeBlock.java b/plugin/src/main/java/com/denizenscript/denizen/utilities/blocks/FakeBlock.java index 5efa823896..87cf588e40 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/utilities/blocks/FakeBlock.java +++ b/plugin/src/main/java/com/denizenscript/denizen/utilities/blocks/FakeBlock.java @@ -75,6 +75,8 @@ public static List getFakeBlocksFor(UUID id, ChunkCoordinate chunkCoo public final ChunkCoordinate chunkCoord; public MaterialTag material; public BukkitTask currentTask = null; + public int lastBlockLight = -1; + public int lastSkyLight = -1; private FakeBlock(PlayerTag player, LocationTag location) { this.player = player; diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java index 74b1ec1912..433632bac2 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java @@ -188,7 +188,7 @@ else if (newState.isAir() && !oldState.isAir()) { } BlockLightData estimatedLights = getEstimatedLightLevel(relativeX, relativeY, relativeZ, getLayer(hasBlockLight, lightData.getBlockUpdates(), blocksLit), getLayer(hasSkyLight, lightData.getSkyUpdates(), skyLit), - lightData, ((CraftWorld) world).getHandle(), sectionPos, sectionLightCache, sectionIndex, blocksLit, skyLit); + lightData, ((CraftWorld) world).getHandle(), sectionPos, sectionLightCache, sectionIndex, blocksLit, skyLit, fakeBlockMap); if (estimatedLights.block() > 0) { DataLayer blockLights; if (!hasBlockLight) { @@ -199,6 +199,7 @@ else if (newState.isAir() && !oldState.isAir()) { blockLights = new DataLayer(lightData.getBlockUpdates().get(blocksLit)); } blockLights.set(relativeX, relativeY, relativeZ, estimatedLights.block()); + block.lastBlockLight = estimatedLights.block(); } if (estimatedLights.sky() > 0) { DataLayer skyLights; @@ -210,6 +211,7 @@ else if (newState.isAir() && !oldState.isAir()) { skyLights = new DataLayer(lightData.getSkyUpdates().get(skyLit)); } skyLights.set(relativeX, relativeY, relativeZ, estimatedLights.sky()); + block.lastSkyLight = estimatedLights.sky(); } } } @@ -256,13 +258,13 @@ public record SectionLightCache(DataLayer blockLights, DataLayer skyLights) {} {0, 0, 1} }; - public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY, int relativeZ, DataLayer blockLights, DataLayer skyLights, ClientboundLightUpdatePacketData lightData, ServerLevel nmsWorld, SectionPos sectionPos, Long2ObjectMap sectionLightsCache, int sectionIndex, int blocksLit, int skyLit) { + public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY, int relativeZ, DataLayer blockLights, DataLayer skyLights, ClientboundLightUpdatePacketData lightData, ServerLevel nmsWorld, SectionPos sectionPos, Long2ObjectMap sectionLightsCache, int sectionIndex, int blocksLit, int skyLit, FakeBlock.FakeBlockMap fakeBlockMap) { boolean isSkyBright = nmsWorld.getSkyDarken() == 0; int maxSkyLight = getLight(skyLights, relativeX, relativeY, relativeZ); - int maxBlockLight = getLight(blockLights, relativeX, relativeY, relativeZ); if (isSkyBright && maxSkyLight == 15) { return BlockLightData.MAX_SKY_LIGHT; } + int maxBlockLight = getLight(blockLights, relativeX, relativeY, relativeZ); if (maxBlockLight == 15) { return BlockLightData.MAX_BLOCK_LIGHT; } @@ -290,20 +292,26 @@ public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY int wrappedNeighborY = SectionPos.sectionRelative(neighborY); if (hasSkyLight) { skyLight = new DataLayer(lightData.getSkyUpdates().get(skyLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ); + if (isSkyBright && skyLight == 15) { + return BlockLightData.MAX_SKY_LIGHT; + } } if (hasBlockLight) { blockLight = new DataLayer(lightData.getBlockUpdates().get(blocksLit + yOffest)).get(neighborX, wrappedNeighborY, neighborZ); + if (blockLight == 15) { + return BlockLightData.MAX_BLOCK_LIGHT; + } } } else { skyLight = getLight(skyLights, neighborX, neighborY, neighborZ); + if (isSkyBright && skyLight == 15) { + return BlockLightData.MAX_SKY_LIGHT; + } blockLight = getLight(blockLights, neighborX, neighborY, neighborZ); - } - if (isSkyBright && skyLight == 15) { - return BlockLightData.MAX_SKY_LIGHT; - } - if (blockLight == 15) { - return BlockLightData.MAX_BLOCK_LIGHT; + if (blockLight == 15) { + return BlockLightData.MAX_BLOCK_LIGHT; + } } if (skyLight > maxSkyLight) { maxSkyLight = skyLight; @@ -324,13 +332,26 @@ public static BlockLightData getEstimatedLightLevel(int relativeX, int relativeY return new SectionLightCache(sectionBlockLights, sectionSkyLights); }); int skyLight = getLight(sectionLight.skyLights(), blockPos); - int blockLight = getLight(sectionLight.blockLights(), blockPos); if (isSkyBright && skyLight == 15) { return BlockLightData.MAX_SKY_LIGHT; } + int blockLight = getLight(sectionLight.blockLights(), blockPos); if (blockLight == 15) { return BlockLightData.MAX_BLOCK_LIGHT; } + FakeBlock fakeBlock = fakeBlockMap.byLocation.get(new LocationTag(nmsWorld.getWorld(), blockPos.getX(), blockPos.getY(), blockPos.getZ())); + if (fakeBlock.lastSkyLight > skyLight) { + skyLight = fakeBlock.lastSkyLight; + if (isSkyBright && skyLight == 15) { + return BlockLightData.MAX_SKY_LIGHT; + } + } + if (fakeBlock.lastBlockLight > blockLight) { + blockLight = fakeBlock.lastBlockLight; + if (blockLight == 15) { + return BlockLightData.MAX_BLOCK_LIGHT; + } + } if (skyLight > maxSkyLight) { maxSkyLight = skyLight; } From 46fedf5584afa5e4e7070c39377eb4afef6d46ec Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Sat, 27 Sep 2025 11:23:09 +0100 Subject: [PATCH 21/21] No `EnumMap` --- .../nms/v1_21/impl/network/handlers/FakeBlockHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java index 433632bac2..1b0f11f0d0 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/impl/network/handlers/FakeBlockHelper.java @@ -52,7 +52,7 @@ public class FakeBlockHelper { public static final MethodHandle CHUNKDATA_BLOCKENTITYINFO_Y = ReflectionHelper.getFields(CHUNKDATA_BLOCKENTITYINFO_CLASS).getGetter(ReflectionMappingsInfo.ClientboundLevelChunkPacketDataBlockEntityInfo_y); public static final MethodHandle BLOCK_ENTITY_TYPE_VALID_BLOCKS = ReflectionHelper.getFields(BlockEntityType.class).getGetter(ReflectionMappingsInfo.BlockEntityType_validBlocks, Set.class); - public static final Map> MATERIAL_BLOCK_ENTITY_TYPES = new EnumMap<>(Material.class); + public static final Map> MATERIAL_BLOCK_ENTITY_TYPES = new HashMap<>(); static { try {