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/ReflectionMappingsInfo.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/ReflectionMappingsInfo.java index 811303325a..16a30a69bf 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 @@ -96,7 +96,12 @@ 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"; public static String ClientboundLevelChunkPacketDataBlockEntityInfo_y = "d"; @@ -111,12 +116,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"; @@ -125,4 +124,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/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 PALETTEDCONTAINER_CTOR = Arrays.stream(PalettedContainer.class.getConstructors()).filter(c -> c.getParameterCount() == 3).findFirst().get(); + 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 HashMap<>(); + + static { + try { + for (BlockEntityType nmsBlockEntityType : BuiltInRegistries.BLOCK_ENTITY_TYPE) { + Set validBlocks = (Set) BLOCK_ENTITY_TYPE_VALID_BLOCKS.invokeExact(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 +87,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,80 +110,267 @@ public static void copyPacketPaperPatch(ClientboundLevelChunkWithLightPacket new } } - public static ClientboundLevelChunkWithLightPacket handleMapChunkPacket(World world, ClientboundLevelChunkWithLightPacket originalPacket, int chunkX, int chunkZ, List blocks) { - 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); - 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; - } - } + 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 ClientboundLevelChunkPacketData$BlockEntityInfo + 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 = (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; + 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 new buf of the same size to write + FriendlyByteBuf rawChunkData = originalChunkPacket.getChunkData().getReadBuffer(); + FriendlyByteBuf newChunkData = new FriendlyByteBuf(Unpooled.buffer(rawChunkData.readableBytes())); + 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; + ClientboundLightUpdatePacketData lightData = copiedChunkPacket.getLightData(); + Long2ObjectMap sectionLightCache = new Long2ObjectOpenHashMap<>(); + // These are section coords, iterating through every chunk section + for (int y = minChunkY; y < maxChunkY; y++) { + // 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; + if (lightData.getBlockYMask().get(sectionIndex)) { + hasBlockLight = true; } - 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); - 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 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); - BlockState newState = getNMSState(block); - if (oldState.isAir() && !newState.isAir()) { - blockCount++; + if (lightData.getSkyYMask().get(sectionIndex)) { + hasSkyLight = true; + } + 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)) { + SectionPos sectionPos = SectionPos.of(chunkX, y, chunkZ); + 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 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 (!oldState.isSolidRender() || (blockEntityType == null && newState.isSolidRender())) { + continue; + } + 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, fakeBlockMap); + if (estimatedLights.block() > 0) { + DataLayer blockLights; + if (!hasBlockLight) { + blockLights = addLayer(lightData.getBlockUpdates(), blocksLit, lightData.getBlockYMask(), lightData.getEmptyBlockYMask(), sectionIndex); + hasBlockLight = true; } - else if (newState.isAir() && !oldState.isAir()) { - blockCount--; + else { + blockLights = new DataLayer(lightData.getBlockUpdates().get(blocksLit)); } - states.set(blockX, blockY, blockZ, newState); + blockLights.set(relativeX, relativeY, relativeZ, estimatedLights.block()); + block.lastBlockLight = estimatedLights.block(); + } + if (estimatedLights.sky() > 0) { + DataLayer skyLights; + if (!hasSkyLight) { + skyLights = addLayer(lightData.getSkyUpdates(), skyLit, lightData.getSkyYMask(), lightData.getEmptySkyYMask(), sectionIndex); + hasSkyLight = true; + } + else { + skyLights = new DataLayer(lightData.getSkyUpdates().get(skyLit)); + } + skyLights.set(relativeX, relativeY, relativeZ, estimatedLights.sky()); + block.lastSkyLight = estimatedLights.sky(); } } } - outputSerial.writeShort(blockCount); - states.write(outputSerial); - biomes.write(outputSerial); - } - byte[] outputBytes = outputSerial.array(); - CHUNKDATA_BUFFER_SETTER.invoke(packet, outputBytes); - CHUNKPACKET_CHUNKDATA_SETTER.invoke(duplicateCorePacket, packet); - return duplicateCorePacket; + } + if (hasBlockLight) { + blocksLit++; + } + if (hasSkyLight) { + skyLit++; + } + newChunkData.writeShort(blockCount); + states.write(newChunkData); + biomes.write(newChunkData); } - catch (Throwable ex) { - Debug.echoError(ex); + CHUNKDATA_BUFFER_SETTER.invokeExact(copiedChunkPacket.getChunkData(), newChunkData.array()); + 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 BlockLightData MAX_BLOCK_LIGHT = new BlockLightData(0, 15); + 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}, + {-1, 0, 0}, + {1, 0, 0}, + {0, 0, -1}, + {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, FakeBlock.FakeBlockMap fakeBlockMap) { + boolean isSkyBright = nmsWorld.getSkyDarken() == 0; + int maxSkyLight = getLight(skyLights, 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; + } + 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 (posOutOfSection(neighborX) || posOutOfSection(neighborZ)) { + if (blockLookups == null) { + blockLookups = new ArrayList<>(2); + } + blockLookups.add(new BlockPos(sectionPos.minBlockX() + neighborX, sectionPos.minBlockY() + neighborY, sectionPos.minBlockZ() + neighborZ)); + continue; + } + int blockLight = -1, skyLight = -1; + if (posOutOfSection(neighborY)) { + int adjacentSectionIndex = sectionIndex + yOffest; + boolean hasSkyLight = lightData.getSkyYMask().get(adjacentSectionIndex); + boolean hasBlockLight = lightData.getBlockYMask().get(adjacentSectionIndex); + if (!hasBlockLight && !hasSkyLight) { + continue; + } + 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 (blockLight == 15) { + return BlockLightData.MAX_BLOCK_LIGHT; + } + } + if (skyLight > maxSkyLight) { + maxSkyLight = skyLight; + } + if (blockLight > maxBlockLight) { + maxBlockLight = blockLight; + } } - return null; + if (blockLookups == null) { + return new BlockLightData(maxSkyLight, maxBlockLight); + } + for (BlockPos blockPos : blockLookups) { + SectionPos containingSection = SectionPos.of(blockPos); + SectionLightCache sectionLight = sectionLightsCache.computeIfAbsent(containingSection.asLong(), k -> { + 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 skyLight = getLight(sectionLight.skyLights(), 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; + } + if (blockLight > maxBlockLight) { + maxBlockLight = blockLight; + } + } + return new BlockLightData(maxSkyLight, maxBlockLight); + } + + 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 static boolean posOutOfSection(int relativePos) { + return relativePos < 0 || relativePos > 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 4938114cac..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 @@ -7,106 +7,110 @@ 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; +import java.lang.invoke.MethodHandle; import java.util.Arrays; import java.util.List; public class FakeBlocksPacketHandlers { public static void registerHandlers() { - DenizenNetworkManagerImpl.registerPacketHandler(ClientboundLevelChunkWithLightPacket.class, FakeBlocksPacketHandlers::processShowFakeForPacket); - DenizenNetworkManagerImpl.registerPacketHandler(ClientboundSectionBlocksUpdatePacket.class, FakeBlocksPacketHandlers::processShowFakeForPacket); - DenizenNetworkManagerImpl.registerPacketHandler(ClientboundBlockUpdatePacket.class, FakeBlocksPacketHandlers::processShowFakeForPacket); + DenizenNetworkManagerImpl.registerPacketHandler(ClientboundLevelChunkWithLightPacket.class, FakeBlocksPacketHandlers::processLevelChunkWithLightPacket); + DenizenNetworkManagerImpl.registerPacketHandler(ClientboundSectionBlocksUpdatePacket.class, FakeBlocksPacketHandlers::processSectionBlocksUpdatePacket); + 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 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; - } - ClientboundLevelChunkWithLightPacket newPacket = FakeBlockHelper.handleMapChunkPacket(networkManager.player.getBukkitEntity().getWorld(), (ClientboundLevelChunkWithLightPacket) packet, chunkX, chunkZ, blocks); - return newPacket; - } - 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 Throwable { + 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.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.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; + 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) { + statesArray[i] = FakeBlockHelper.getNMSState(block); + hasAny = true; } } - catch (Throwable ex) { - Debug.echoError(ex); + if (!hasAny) { + return sectionUpdatePacket; + } + return new ClientboundSectionBlocksUpdatePacket(coord, new ShortArraySet(originalOffsetArray), statesArray); + } + + 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) { + return new ClientboundBlockUpdatePacket(blockUpdatePacket.getPos(), FakeBlockHelper.getNMSState(block)); + } + 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; } + */ }