From 5305aaf6699b5a2b5d8d4ac00de3b92e15b4f50f Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Mon, 11 May 2026 23:40:36 +0200 Subject: [PATCH] Normalize block entity IDs and handle bundled packets - Normalize hidden block entity identifiers across versions - Add block break and tile entity handling for newer protocol versions - Preserve packet filtering through bundle packets and fix chunk section copying --- .../de/steamwar/techhider/ChunkHider18.java | 2 +- .../steamwar/techhider/ProtocolWrapper18.java | 11 ++ .../steamwar/techhider/ProtocolWrapper19.java | 4 +- .../de/steamwar/techhider/ChunkHider21.java | 63 +++++++++-- .../steamwar/techhider/ProtocolWrapper21.java | 104 ++++++++++++++++++ .../de/steamwar/techhider/ChunkHider9.java | 2 +- .../comphenix/tinyprotocol/TinyProtocol.java | 64 ++++++++++- .../src/de/steamwar/techhider/ChunkHider.java | 21 +++- .../de/steamwar/techhider/ProtocolUtils.java | 5 +- .../steamwar/techhider/ProtocolWrapper.java | 4 + .../src/de/steamwar/techhider/TechHider.java | 33 +++++- 11 files changed, 291 insertions(+), 22 deletions(-) create mode 100644 SpigotCore/SpigotCore_21/src/de/steamwar/techhider/ProtocolWrapper21.java diff --git a/SpigotCore/SpigotCore_18/src/de/steamwar/techhider/ChunkHider18.java b/SpigotCore/SpigotCore_18/src/de/steamwar/techhider/ChunkHider18.java index 3033feac..5aefac92 100644 --- a/SpigotCore/SpigotCore_18/src/de/steamwar/techhider/ChunkHider18.java +++ b/SpigotCore/SpigotCore_18/src/de/steamwar/techhider/ChunkHider18.java @@ -93,7 +93,7 @@ public class ChunkHider18 implements ChunkHider { private static final Reflection.Method getKey = Reflection.getTypedMethod(IRegistry.class, null, MinecraftKey.class, Object.class); private static final Reflection.Method getName = Reflection.getTypedMethod(MinecraftKey.class, null, String.class); protected boolean tileEntityVisible(Set hiddenBlockEntities, Object tile) { - return !hiddenBlockEntities.contains((String) getName.invoke(getKey.invoke(tileEntityTypes, entityType.get(tile)))); + return !hiddenBlockEntities.contains(TechHider.normalizeBlockEntityId((String) getName.invoke(getKey.invoke(tileEntityTypes, entityType.get(tile))))); } private void blocks(SectionHider section) { diff --git a/SpigotCore/SpigotCore_18/src/de/steamwar/techhider/ProtocolWrapper18.java b/SpigotCore/SpigotCore_18/src/de/steamwar/techhider/ProtocolWrapper18.java index 54f41d90..1d4c773e 100644 --- a/SpigotCore/SpigotCore_18/src/de/steamwar/techhider/ProtocolWrapper18.java +++ b/SpigotCore/SpigotCore_18/src/de/steamwar/techhider/ProtocolWrapper18.java @@ -20,8 +20,11 @@ package de.steamwar.techhider; import de.steamwar.Reflection; +import de.steamwar.core.Core; +import net.minecraft.core.IRegistry; import net.minecraft.core.SectionPosition; import net.minecraft.network.protocol.game.PacketPlayOutBlockBreak; +import net.minecraft.resources.MinecraftKey; import net.minecraft.world.level.block.entity.TileEntitySign; import net.minecraft.world.level.block.entity.TileEntityTypes; import net.minecraft.world.level.block.state.IBlockData; @@ -88,6 +91,14 @@ public class ProtocolWrapper18 implements ProtocolWrapper { return tileEntityType.get(packet) != signType; } + private static final IRegistry tileEntityTypes = Reflection.getField(Core.getVersion() > 18 ? Reflection.getClass("net.minecraft.core.registries.BuiltInRegistries") : IRegistry.class, IRegistry.class, 0, TileEntityTypes.class).get(null); + private static final Reflection.Method getKey = Reflection.getTypedMethod(IRegistry.class, null, MinecraftKey.class, Object.class); + private static final Reflection.Method getName = Reflection.getTypedMethod(MinecraftKey.class, null, String.class); + @Override + public String tileEntityDataType(Object packet) { + return TechHider.normalizeBlockEntityId((String) getName.invoke(getKey.invoke(tileEntityTypes, tileEntityType.get(packet)))); + } + @Override public BiFunction blockBreakHiderGenerator(Class blockBreakPacket, TechHider techHider) { return (p, packet) -> { diff --git a/SpigotCore/SpigotCore_19/src/de/steamwar/techhider/ProtocolWrapper19.java b/SpigotCore/SpigotCore_19/src/de/steamwar/techhider/ProtocolWrapper19.java index 2aded961..485d062f 100644 --- a/SpigotCore/SpigotCore_19/src/de/steamwar/techhider/ProtocolWrapper19.java +++ b/SpigotCore/SpigotCore_19/src/de/steamwar/techhider/ProtocolWrapper19.java @@ -19,6 +19,7 @@ package de.steamwar.techhider; +import de.steamwar.Reflection; import org.bukkit.entity.Player; import java.util.function.BiFunction; @@ -27,6 +28,7 @@ public class ProtocolWrapper19 extends ProtocolWrapper18 { @Override public BiFunction blockBreakHiderGenerator(Class blockBreakPacket, TechHider techHider) { - return null; + Reflection.Field blockBreakPosition = Reflection.getField(blockBreakPacket, TechHider.blockPosition, 0); + return (p, packet) -> techHider.getLocationEvaluator().checkBlockPos(p, blockBreakPosition.get(packet)) == TechHider.State.SKIP ? packet : null; } } diff --git a/SpigotCore/SpigotCore_21/src/de/steamwar/techhider/ChunkHider21.java b/SpigotCore/SpigotCore_21/src/de/steamwar/techhider/ChunkHider21.java index 1cc6c29c..fe5b6040 100644 --- a/SpigotCore/SpigotCore_21/src/de/steamwar/techhider/ChunkHider21.java +++ b/SpigotCore/SpigotCore_21/src/de/steamwar/techhider/ChunkHider21.java @@ -25,16 +25,20 @@ import io.netty.buffer.Unpooled; import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; import net.minecraft.util.SimpleBitStorage; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; import org.bukkit.entity.Player; import java.util.List; import java.util.Set; +import java.util.function.IntUnaryOperator; import java.util.function.BiFunction; import java.util.function.UnaryOperator; import java.util.stream.Collectors; public class ChunkHider21 implements ChunkHider { + private static final int DIRECT_BLOCK_BITS = Math.max(9, Integer.SIZE - Integer.numberOfLeadingZeros(Block.BLOCK_STATE_REGISTRY.size() - 1)); + @Override public Class mapChunkPacket() { return ClientboundLevelChunkWithLightPacket.class; @@ -96,33 +100,70 @@ public class ChunkHider21 implements ChunkHider { private static final Reflection.Method getKey = Reflection.getTypedMethod(registry, "getKey", resourceLocation, Object.class); private static final Reflection.Method getName = Reflection.getTypedMethod(resourceLocation, "getPath", String.class); protected boolean tileEntityVisible(Set hiddenBlockEntities, Object tile) { - return !hiddenBlockEntities.contains(getName.invoke(getKey.invoke(nameField.get(null), entityType.get(tile)))); + return !hiddenBlockEntities.contains(TechHider.normalizeBlockEntityId((String) getName.invoke(getKey.invoke(nameField.get(null), entityType.get(tile))))); } private void blocks(SectionHider section) { - section.copyBitsPerBlock(); + int sourceBitsPerBlock = section.readBitsPerBlock(); - boolean singleValued = section.getBitsPerBlock() == 0; + if(section.isSkipSection()) { + section.writeBitsPerBlock(sourceBitsPerBlock); + copyBlockStorage(section); + return; + } + + boolean singleValued = sourceBitsPerBlock == 0; if (singleValued) { int value = ProtocolUtils.readVarInt(section.getIn()); - ProtocolUtils.writeVarInt(section.getOut(), !section.isSkipSection() && section.getObfuscate().contains(value) ? section.getTarget() : value); + if(section.blockPrecise()) { + writeDirectBlocks(section, DIRECT_BLOCK_BITS, ignored -> value); + } else { + section.writeBitsPerBlock(sourceBitsPerBlock); + ProtocolUtils.writeVarInt(section.getOut(), section.getObfuscate().contains(value) ? section.getTarget() : value); + } return; - } else if (section.getBitsPerBlock() < 9) { + } else if (sourceBitsPerBlock < 9) { + if(section.blockPrecise()) { + int[] palette = section.readPalette(); + SimpleBitStorage values = new SimpleBitStorage(sourceBitsPerBlock, 4096, section.readNewDataArray(4096)); + writeDirectBlocks(section, DIRECT_BLOCK_BITS, pos -> palette[values.get(pos)]); + return; + } + + section.writeBitsPerBlock(sourceBitsPerBlock); // Indirect (paletted) storage – only present when bitsPerBlock < 9 in 1.21+ section.processPalette(); } - if (section.isSkipSection() || (!section.blockPrecise() && section.isPaletted())) { + if (!section.blockPrecise() && section.isPaletted()) { section.skipNewDataArray(4096); return; } - SimpleBitStorage values = new SimpleBitStorage(section.getBitsPerBlock(), 4096, section.readNewDataArray(4096)); + SimpleBitStorage values = new SimpleBitStorage(sourceBitsPerBlock, 4096, section.readNewDataArray(4096)); + writeDirectBlocks(section, sourceBitsPerBlock, values::get); + } + + private void copyBlockStorage(SectionHider section) { + if(section.getBitsPerBlock() == 0) { + section.copyVarInt(); + } else { + if(section.getBitsPerBlock() < 9) + section.skipPalette(); + + section.skipNewDataArray(4096); + } + } + + private void writeDirectBlocks(SectionHider section, int bitsPerBlock, IntUnaryOperator source) { + section.writeBitsPerBlock(bitsPerBlock); + SimpleBitStorage values = new SimpleBitStorage(bitsPerBlock, 4096); for (int y = 0; y < 16; y++) { for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++) { int pos = (((y * 16) + z) * 16) + x; + int value = source.applyAsInt(pos); TechHider.State test = section.test(x, y, z); @@ -130,15 +171,17 @@ public class ChunkHider21 implements ChunkHider { case SKIP: break; case CHECK: - if (!section.getObfuscate().contains(values.get(pos))) + if (!section.getObfuscate().contains(value)) break; case HIDE: - values.set(pos, section.getTarget()); + value = section.getTarget(); break; case HIDE_AIR: default: - values.set(pos, section.getAir()); + value = section.getAir(); } + + values.set(pos, value); } } } diff --git a/SpigotCore/SpigotCore_21/src/de/steamwar/techhider/ProtocolWrapper21.java b/SpigotCore/SpigotCore_21/src/de/steamwar/techhider/ProtocolWrapper21.java new file mode 100644 index 00000000..f670744c --- /dev/null +++ b/SpigotCore/SpigotCore_21/src/de/steamwar/techhider/ProtocolWrapper21.java @@ -0,0 +1,104 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import de.steamwar.Reflection; +import net.minecraft.core.BlockPos; +import net.minecraft.core.SectionPos; +import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.function.BiFunction; + +public class ProtocolWrapper21 implements ProtocolWrapper { + + private static final Reflection.Field multiBlockChangeChunk = Reflection.getField(TechHider.multiBlockChangePacket, SectionPos.class, 0); + private static final Reflection.Field multiBlockChangePos = Reflection.getField(TechHider.multiBlockChangePacket, short[].class, 0); + private static final Reflection.Field multiBlockChangeBlocks = Reflection.getField(TechHider.multiBlockChangePacket, BlockState[].class, 0); + + @Override + public BiFunction multiBlockChangeGenerator(TechHider techHider) { + return (p, packet) -> { + TechHider.LocationEvaluator locationEvaluator = techHider.getLocationEvaluator(); + SectionPos sectionPos = multiBlockChangeChunk.get(packet); + int chunkX = sectionPos.x(); + int chunkY = sectionPos.y(); + int chunkZ = sectionPos.z(); + if(locationEvaluator.skipChunkSection(p, chunkX, chunkY, chunkZ)) + return packet; + + packet = TechHider.multiBlockChangeCloner.apply(packet); + final short[] oldPos = multiBlockChangePos.get(packet); + final BlockState[] oldBlocks = multiBlockChangeBlocks.get(packet); + ArrayList poss = new ArrayList<>(oldPos.length); + ArrayList blocks = new ArrayList<>(oldPos.length); + for(int i = 0; i < oldPos.length; i++) { + short pos = oldPos[i]; + BlockState block = oldBlocks[i]; + switch(locationEvaluator.check(p, 16*chunkX + SectionPos.sectionRelativeX(pos), 16*chunkY + SectionPos.sectionRelativeY(pos), 16*chunkZ + SectionPos.sectionRelativeZ(pos))) { + case SKIP: + poss.add(pos); + blocks.add(block); + break; + case CHECK: + poss.add(pos); + blocks.add(techHider.iBlockDataHidden(block) ? (BlockState) techHider.getObfuscationTarget() : block); + break; + default: + break; + } + } + + if(blocks.isEmpty()) + return null; + + short[] newPos = new short[poss.size()]; + for(int i = 0; i < newPos.length; i++) + newPos[i] = poss.get(i); + + multiBlockChangePos.set(packet, newPos); + multiBlockChangeBlocks.set(packet, blocks.toArray(new BlockState[0])); + return packet; + }; + } + + private static final Reflection.Field tileEntityType = Reflection.getField(TechHider.tileEntityDataPacket, BlockEntityType.class, 0); + + @Override + public boolean unfilteredTileEntityDataAction(Object packet) { + BlockEntityType type = tileEntityType.get(packet); + return type != BlockEntityType.SIGN && type != BlockEntityType.HANGING_SIGN; + } + + @Override + public String tileEntityDataType(Object packet) { + return BlockEntityType.getKey(tileEntityType.get(packet)).getPath(); + } + + private static final Reflection.Field blockBreakPosition = Reflection.getField(ClientboundBlockDestructionPacket.class, BlockPos.class, 0); + + @Override + public BiFunction blockBreakHiderGenerator(Class blockBreakPacket, TechHider techHider) { + return (p, packet) -> techHider.getLocationEvaluator().checkBlockPos(p, blockBreakPosition.get(packet)) == TechHider.State.SKIP ? packet : null; + } +} diff --git a/SpigotCore/SpigotCore_9/src/de/steamwar/techhider/ChunkHider9.java b/SpigotCore/SpigotCore_9/src/de/steamwar/techhider/ChunkHider9.java index 71b47743..ab8119c0 100644 --- a/SpigotCore/SpigotCore_9/src/de/steamwar/techhider/ChunkHider9.java +++ b/SpigotCore/SpigotCore_9/src/de/steamwar/techhider/ChunkHider9.java @@ -54,7 +54,7 @@ public class ChunkHider9 extends ChunkHider8 { packet = mapChunkCloner.apply(packet); Set hiddenBlockEntities = techHider.getHiddenBlockEntities(); mapChunkBlockEntities.set(packet, ((List)mapChunkBlockEntities.get(packet)).stream().filter( - nbttag -> !hiddenBlockEntities.contains((String) nbtTagGetString.invoke(nbttag, "id")) + nbttag -> !hiddenBlockEntities.contains(TechHider.normalizeBlockEntityId((String) nbtTagGetString.invoke(nbttag, "id"))) ).collect(Collectors.toList())); int primaryBitMask = mapChunkBitMask.get(packet); diff --git a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java index cb4aca06..63d92ea0 100644 --- a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java +++ b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java @@ -23,6 +23,7 @@ import de.steamwar.Reflection; import de.steamwar.Reflection.Field; import de.steamwar.core.Core; import io.netty.channel.*; +import io.netty.util.ReferenceCountUtil; import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -37,6 +38,7 @@ import org.bukkit.plugin.Plugin; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiFunction; import java.util.logging.Level; @@ -57,17 +59,29 @@ public class TinyProtocol implements Listener { public static final Field networkManagers = Reflection.getField(serverConnection, List.class, 0, networkManager); private static final String HANDLER_NAME = "tiny-steamwar"; + private static final Class bundlePacket = getOptionalClass("net.minecraft.network.protocol.BundlePacket"); + private static final Class clientboundBundlePacket = getOptionalClass("net.minecraft.network.protocol.game.ClientboundBundlePacket"); + private static final Reflection.Method bundleSubPackets = bundlePacket == null ? null : Reflection.getTypedMethod(bundlePacket, null, Iterable.class); + private static final Reflection.Constructor clientboundBundleConstructor = clientboundBundlePacket == null ? null : Reflection.getConstructor(clientboundBundlePacket, Iterable.class); public static final TinyProtocol instance = new TinyProtocol(Core.getInstance()); public static void init() { //enforce init } + private static Class getOptionalClass(String className) { + try { + return Reflection.getClass(className); + } catch (IllegalArgumentException ignored) { + return null; + } + } + private final Plugin plugin; private final List connections; private boolean closed; - private final Map, List>> packetFilters = new HashMap<>(); + private final Map, List>> packetFilters = new ConcurrentHashMap<>(); @Getter private final Map playerInterceptors = new HashMap<>(); @@ -207,31 +221,51 @@ public class TinyProtocol implements Listener { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + Object originalMsg = msg; try { msg = filterPacket(player, msg); } catch (Exception e) { plugin.getLogger().log(Level.SEVERE, "Error during incoming packet processing", e); + msg = null; } if (msg != null) { super.channelRead(ctx, msg); + } else { + ReferenceCountUtil.release(originalMsg); } } @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + Object originalMsg = msg; try { msg = filterPacket(player, msg); } catch (Exception e) { plugin.getLogger().log(Level.SEVERE, "Error during outgoing packet processing", e); + msg = null; } if (msg != null) { super.write(ctx, msg, promise); + } else { + ReferenceCountUtil.release(originalMsg); + promise.trySuccess(); } } private Object filterPacket(Player player, Object packet) { + packet = filterSinglePacket(player, packet); + if(packet == null) + return null; + + if(clientboundBundlePacket != null && clientboundBundlePacket.isInstance(packet)) + return filterBundlePacket(player, packet); + + return packet; + } + + private Object filterSinglePacket(Player player, Object packet) { List> filters = packetFilters.getOrDefault(packet.getClass(), Collections.emptyList()); for(BiFunction filter : filters) { @@ -243,5 +277,33 @@ public class TinyProtocol implements Listener { return packet; } + + private Object filterBundlePacket(Player player, Object packet) { + if(bundleSubPackets == null || clientboundBundleConstructor == null) + throw new IllegalStateException("Cannot filter bundled packet " + packet.getClass().getName()); + + ArrayList filteredPackets = new ArrayList<>(); + boolean changed = false; + for(Object subPacket : (Iterable) bundleSubPackets.invoke(packet)) { + Object filteredPacket = filterPacket(player, subPacket); + if(filteredPacket == null) { + changed = true; + continue; + } + + if(filteredPacket != subPacket) + changed = true; + + filteredPackets.add(filteredPacket); + } + + if(!changed) + return packet; + + if(filteredPackets.isEmpty()) + return null; + + return clientboundBundleConstructor.invoke(filteredPackets); + } } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java index bdc8cdd3..e01f361a 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java @@ -92,7 +92,17 @@ public interface ChunkHider { } public void copyBitsPerBlock() { - bitsPerBlock = in.readByte(); + bitsPerBlock = in.readUnsignedByte(); + out.writeByte(bitsPerBlock); + } + + public int readBitsPerBlock() { + bitsPerBlock = in.readUnsignedByte(); + return bitsPerBlock; + } + + public void writeBitsPerBlock(int bitsPerBlock) { + this.bitsPerBlock = bitsPerBlock; out.writeByte(bitsPerBlock); } @@ -108,6 +118,15 @@ public interface ChunkHider { copyVarInt(); } + public int[] readPalette() { + int paletteLength = ProtocolUtils.readVarInt(in); + int[] palette = new int[paletteLength]; + for(int i = 0; i < paletteLength; i++) + palette[i] = ProtocolUtils.readVarInt(in); + + return palette; + } + public void processPalette() { if(skipSection) { skipPalette(); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java index a9677407..c876803f 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java @@ -98,10 +98,7 @@ public class ProtocolUtils { } public static int posToChunk(int c){ - int chunk = c / 16; - if(c < 0) - chunk--; - return chunk; + return Math.floorDiv(c, 16); } @Deprecated diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolWrapper.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolWrapper.java index aec71941..abece88f 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolWrapper.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolWrapper.java @@ -31,6 +31,10 @@ public interface ProtocolWrapper { boolean unfilteredTileEntityDataAction(Object packet); + default String tileEntityDataType(Object packet) { + return null; + } + BiFunction blockBreakHiderGenerator(Class blockBreakPacket, TechHider techHider); BiFunction multiBlockChangeGenerator(TechHider techHider); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 46e53bcf..b06959c2 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -27,6 +27,7 @@ import org.bukkit.Material; import org.bukkit.entity.Player; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.function.BiFunction; @@ -67,11 +68,12 @@ public class TechHider { private final Set obfuscateIds; @Getter private final Set hiddenBlockEntities; + private boolean enabled; public TechHider(LocationEvaluator locationEvaluator, Material obfuscationTarget, Set obfuscate, Set hiddenBlockEntities) { this.locationEvaluator = locationEvaluator; this.obfuscateIds = obfuscate.stream().flatMap(m -> BlockIds.impl.materialToAllIds(m).stream()).collect(Collectors.toSet()); - this.hiddenBlockEntities = hiddenBlockEntities; + this.hiddenBlockEntities = hiddenBlockEntities.stream().map(TechHider::normalizeBlockEntityId).collect(Collectors.toSet()); this.obfuscationTarget = getBlockDataByBlock.invoke(getBlockByMaterial.invoke(null, obfuscationTarget)); this.obfuscationTargetId = BlockIds.impl.materialToId(obfuscationTarget); @@ -81,9 +83,11 @@ public class TechHider { techhiders.put(multiBlockChangePacket, ProtocolWrapper.impl.multiBlockChangeGenerator(this)); techhiders.put(ChunkHider.impl.mapChunkPacket(), ChunkHider.impl.chunkHiderGenerator(this)); - if(Core.getVersion() > 12 && Core.getVersion() < 19) { + if(Core.getVersion() > 12) { Class blockBreakClass = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket"); - techhiders.put(blockBreakClass, ProtocolWrapper.impl.blockBreakHiderGenerator(blockBreakClass, this)); + BiFunction blockBreakHider = ProtocolWrapper.impl.blockBreakHiderGenerator(blockBreakClass, this); + if(blockBreakHider != null) + techhiders.put(blockBreakClass, blockBreakHider); } if(Core.getVersion() > 8){ @@ -94,10 +98,18 @@ public class TechHider { } public void enable() { + if(enabled) + return; + + enabled = true; techhiders.forEach(TinyProtocol.instance::addFilter); } public void disable() { + if(!enabled) + return; + + enabled = false; techhiders.forEach(TinyProtocol.instance::removeFilter); } @@ -142,6 +154,8 @@ public class TechHider { case SKIP: return packet; case CHECK: + if(isHiddenBlockEntity(ProtocolWrapper.impl.tileEntityDataType(packet))) + return null; if(ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet)) return packet; default: @@ -149,6 +163,19 @@ public class TechHider { } } + public boolean isHiddenBlockEntity(String id) { + return hiddenBlockEntities.contains(normalizeBlockEntityId(id)); + } + + public static String normalizeBlockEntityId(String id) { + if(id == null) + return ""; + + id = id.toLowerCase(Locale.ROOT); + int namespaceSeparator = id.indexOf(':'); + return namespaceSeparator < 0 ? id : id.substring(namespaceSeparator + 1); + } + public enum State { SKIP, CHECK,