diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index e0370955..70649d41 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -10,6 +10,8 @@ import java.util.function.BiFunction; import java.util.function.ToDoubleFunction; import java.util.function.ToIntFunction; +import it.unimi.dsi.fastutil.ints.IntList; +import net.minecraft.world.level.block.entity.BlockEntityType; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; @@ -174,12 +176,15 @@ import net.minecraft.world.level.block.state.BlockState; public abstract class TechHider { private final Set>> bypassingPackets; private final Map>, BiFunction, Packet>> packetProcessors; - private final TinyProtocol interceptor; - private final BlockState blockUsedForObfuscation; + + private final Block blockUsedForObfuscation; + private final BlockState blockStateUsedForObfuscation; // TODO handle packet bundle - public TechHider(Plugin plugin, BlockState blockUsedForObfuscation) { + public TechHider(Plugin plugin, Block blockUsedForObfuscation) { this.blockUsedForObfuscation = blockUsedForObfuscation; + this.blockStateUsedForObfuscation = blockUsedForObfuscation.defaultBlockState(); + this.bypassingPackets = new HashSet<>(List.of( // --- 5.1.x Login Protocol --- ClientboundLoginDisconnectPacket.class, // 5.1.1 Disconnect @@ -306,55 +311,52 @@ public abstract class TechHider { // --- Entity packets - // 7.1.2 Spawn Entity: entity type and position can reveal hidden contraptions. - processors.put(ClientboundAddEntityPacket.class, this.buildEntityWithPositionProcessor(ClientboundAddEntityPacket::getId, ClientboundAddEntityPacket::getX, ClientboundAddEntityPacket::getY, ClientboundAddEntityPacket::getZ)); + processors.put(ClientboundAddEntityPacket.class, (p, packet) -> this.processAddEntityPacket(p, (ClientboundAddEntityPacket) packet)); // 7.1.3 Entity Animation: entity id based signal, keep blocked until entity visibility is modeled. - processors.put(ClientboundAnimatePacket.class, this.buildEntityProcessor(ClientboundAnimatePacket::getId)); + processors.put(ClientboundAnimatePacket.class, this.buildEntityPacketProcessor(ClientboundAnimatePacket::getId)); // 7.1.35 Entity Event: entity id based signal. - processors.put(ClientboundEntityEventPacket.class, this.buildEntityProcessor(ClientboundEntityEventPacket::getEventId)); + processors.put(ClientboundEntityEventPacket.class, this.buildEntityPacketProcessor(ClientboundEntityEventPacket::getEventId)); // 7.1.36 Teleport Entity: entity id and absolute position. - processors.put(ClientboundTeleportEntityPacket.class, this.buildEntityProcessor(ClientboundTeleportEntityPacket::id)); + processors.put(ClientboundTeleportEntityPacket.class, this.buildEntityPacketProcessor(ClientboundTeleportEntityPacket::id)); // 7.1.42 Hurt Animation: entity id based signal. - processors.put(ClientboundHurtAnimationPacket.class, this.buildEntityProcessor(ClientboundHurtAnimationPacket::id)); + processors.put(ClientboundHurtAnimationPacket.class, this.buildEntityPacketProcessor(ClientboundHurtAnimationPacket::id)); // 7.1.100 Set Entity Velocity: entity id and movement. - processors.put(ClientboundSetEntityMotionPacket.class, this.buildEntityProcessor(ClientboundSetEntityMotionPacket::getId)); + processors.put(ClientboundSetEntityMotionPacket.class, this.buildEntityPacketProcessor(ClientboundSetEntityMotionPacket::getId)); // 7.1.101 Set Equipment: entity equipment can reveal wargear. - processors.put(ClientboundSetEquipmentPacket.class, this.buildEntityProcessor(ClientboundSetEquipmentPacket::getEntity)); + processors.put(ClientboundSetEquipmentPacket.class, this.buildEntityPacketProcessor(ClientboundSetEquipmentPacket::getEntity)); // 7.1.106 Set Passengers: entity relationship signal. - processors.put(ClientboundSetPassengersPacket.class, this.buildEntityProcessor(ClientboundSetPassengersPacket::getVehicle)); + processors.put(ClientboundSetPassengersPacket.class, this.buildEntityPacketProcessor(ClientboundSetPassengersPacket::getVehicle)); // 7.1.130 Update Attributes: entity id and attribute state. - processors.put(ClientboundUpdateAttributesPacket.class, this.buildEntityProcessor(ClientboundUpdateAttributesPacket::getEntityId)); + processors.put(ClientboundUpdateAttributesPacket.class, this.buildEntityPacketProcessor(ClientboundUpdateAttributesPacket::getEntityId)); // 7.1.131 Entity Effect: entity id and effect state. - processors.put(ClientboundUpdateMobEffectPacket.class, this.buildEntityProcessor(ClientboundUpdateMobEffectPacket::getEntityId)); + processors.put(ClientboundUpdateMobEffectPacket.class, this.buildEntityPacketProcessor(ClientboundUpdateMobEffectPacket::getEntityId)); // 7.1.77 Remove Entity Effect: entity id and effect state. - processors.put(ClientboundRemoveMobEffectPacket.class, this.buildEntityProcessor(ClientboundRemoveMobEffectPacket::entityId)); + processors.put(ClientboundRemoveMobEffectPacket.class, this.buildEntityPacketProcessor(ClientboundRemoveMobEffectPacket::entityId)); // 7.1.98 Set Entity Metadata: entity state can reveal blocks/items/displays. - processors.put(ClientboundSetEntityDataPacket.class, this.buildEntityProcessor(ClientboundSetEntityDataPacket::id)); + processors.put(ClientboundSetEntityDataPacket.class, this.buildEntityPacketProcessor(ClientboundSetEntityDataPacket::id)); // 7.1.26 Damage Event: entity ids and damage source location can leak hidde activity. - processors.put(ClientboundDamageEventPacket.class, tossPacket); + processors.put(ClientboundDamageEventPacket.class, this.buildEntityPacketProcessor(ClientboundDamageEventPacket::entityId)); // 7.1.52/53/55 Update Entity Position/Rotation: entity id and movement signal. - processors.put(ClientboundMoveEntityPacket.class, tossPacket); + processors.put(ClientboundMoveEntityPacket.class, (p, packet) -> this.processMoveEntityPacket(p, (ClientboundMoveEntityPacket) packet)); // 7.1.82 Set Head Rotation: entity id and rotation. - processors.put(ClientboundRotateHeadPacket.class, tossPacket); + processors.put(ClientboundRotateHeadPacket.class, (p, packet) -> this.processRotateHeadPacket(p, (ClientboundRotateHeadPacket) packet)); // 7.1.76 Remove Entities: entity id visibility side channel. processors.put(ClientboundRemoveEntitiesPacket.class, tossPacket); // 7.1.99 Link Entities: entity relationship signal. - processors.put(ClientboundSetEntityLinkPacket.class, tossPacket); + processors.put(ClientboundSetEntityLinkPacket.class, (p, packet) -> this.processSetEntityLinkPacket(p, (ClientboundSetEntityLinkPacket) packet)); // --- Block entity packets --- - // 7.1.7 Block Entity Data: currently filtered by block position and allowed - // tile-entity actions. + // 7.1.7 Block Entity Data: currently filtered by block position and allowed tile-entity actions. processors.put(ClientboundBlockEntityDataPacket.class, (p, packet) -> processBlockEntityDataPacket(p, (ClientboundBlockEntityDataPacket) packet)); // 7.1.122 Tag Query Response: block-entity query result. processors.put(ClientboundTagQueryPacket.class, tossPacket); // --- Block packets --- - // 7.1.6 Set Block Destroy Stage: block position and mining progress can reveal - // activity. - processors.put(ClientboundBlockDestructionPacket.class, tossPacket); + // 7.1.6 Set Block Destroy Stage: block position and mining progress can reveal activity. + processors.put(ClientboundBlockDestructionPacket.class, (p, packet) -> proccessBlockDestructionPacket(p, (ClientboundBlockDestructionPacket) packet)); // 7.1.8 Block Action: currently filtered by block position. processors.put(ClientboundBlockEventPacket.class, (p, packet) -> processBlockEventPacket(p, (ClientboundBlockEventPacket) packet)); - // 7.1.9 Block Update: currently filtered/obfuscated by block position and block - // id. + // 7.1.9 Block Update: currently filtered/obfuscated by block position and block id. processors.put(ClientboundBlockUpdatePacket.class, (p, packet) -> processBlockUpdatePacket(p, (ClientboundBlockUpdatePacket) packet)); // --- Chunk packets --- @@ -415,49 +417,22 @@ public abstract class TechHider { // --- Sound packets natural part of the game -> should not be hidden --- // 7.1.115 Entity Sound Effect: entity id and sound. - processors.put(ClientboundSoundEntityPacket.class, this.buildEntityProcessor(ClientboundSoundEntityPacket::getId)); + processors.put(ClientboundSoundEntityPacket.class, tossPacket); // 7.1.116 Sound Effect: sound type and position. processors.put(ClientboundSoundPacket.class, tossPacket); // 7.1.118 Stop Sound: sound state side channel. processors.put(ClientboundStopSoundPacket.class, tossPacket); this.packetProcessors = processors; + } - this.interceptor = new TinyProtocol(plugin) { - @Override - public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) { - try { - if (bypassingPackets.contains(packet.getClass())) { - return packet; - } - - BiFunction, Packet> processor = packetProcessors - .get(packet.getClass()); - return processor == null ? null - : processor.apply(receiver, (Packet) packet); - } catch (Error e) { - // Errors during packet processing leeding to packet being tossed - e.printStackTrace(); - return null; - } - } - }; - - } - - private Packet processEntityPacketWithPosition(Player player, int entityId, double entityX, double entityY, double entityZ, Packet packet) { - if(isPlayerPrivilegedToAccessEntity(player, entityId) && isPlayerPrivilegedToAccessPosition(player, (int) entityX, (int) entityY, (int) entityZ)) { + private Packet processAddEntityPacket(Player player, ClientboundAddEntityPacket packet) { + if(isPlayerPrivilegedToAccessEntity(player, packet.getId()) && isPlayerPrivilegedToAccessPosition(player, (int) packet.getX(), (int) packet.getY(), (int) packet.getZ())) { return packet; } else { return null; } } - private > BiFunction, Packet> buildEntityWithPositionProcessor(ToIntFunction entityIdExtractor, ToDoubleFunction xExtractor, ToDoubleFunction yExtractor, ToDoubleFunction zExtractor) { - return (p, rawPacket) -> { - TTargetPacket packet = (TTargetPacket) rawPacket; - return processEntityPacketWithPosition(p, entityIdExtractor.applyAsInt(packet), xExtractor.applyAsDouble(packet), yExtractor.applyAsDouble(packet), zExtractor.applyAsDouble(packet), packet); - }; - } private Packet processEntityPacket(Player player, int entityId, Packet packet) { if(isPlayerPrivilegedToAccessEntity(player, entityId)) { @@ -466,51 +441,110 @@ public abstract class TechHider { return null; } } - private > BiFunction, Packet> buildEntityProcessor(ToIntFunction entityIdExtractor) { + private > BiFunction, Packet> buildEntityPacketProcessor(ToIntFunction entityIdExtractor) { return (p, rawPacket) -> { TTargetPacket packet = (TTargetPacket) rawPacket; return processEntityPacket(p, entityIdExtractor.applyAsInt(packet), packet); }; } - private ClientboundBlockEventPacket processBlockEventPacket(Player player, ClientboundBlockEventPacket packet) { - BlockPos blockPos = packet.getPos(); + private final Reflection.Field moveEntityPacketEntityIdField = Reflection.getField(ClientboundMoveEntityPacket.class, Integer.class, 0); + private Packet processMoveEntityPacket(Player player, ClientboundMoveEntityPacket packet) { + int entityId = moveEntityPacketEntityIdField.get(packet); - if (isPlayerPrivilegedToAccessPosition(player, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { + if(isPlayerPrivilegedToAccessEntity(player, entityId)) { + return packet; + } + else { + return null; + } + } + + private final Reflection.Field rotateHeadPacketEntityIdField = Reflection.getField(ClientboundRotateHeadPacket.class, Integer.class, 0); + private Packet processRotateHeadPacket(Player player, ClientboundRotateHeadPacket packet) { + int entityId = rotateHeadPacketEntityIdField.get(packet); + + if(isPlayerPrivilegedToAccessEntity(player, entityId)) { + return packet; + } + else { + return null; + } + } + + private Packet processRemoveEntitiesPacket(Player player, ClientboundRemoveEntitiesPacket packet) { + // TODO + return null; + } + + public Packet processSetEntityLinkPacket(Player player, ClientboundSetEntityLinkPacket packet) { + int fromEntityId = packet.getSourceId(); + int toEntityId = packet.getDestId(); + + if(isPlayerPrivilegedToAccessEntity(player, fromEntityId) && isPlayerPrivilegedToAccessEntity(player, toEntityId)) { + return packet; + } + else { + return null; + } + } + + private Packet processBlockEventPacket(Player player, ClientboundBlockEventPacket packet) { + BlockPos blockPos = packet.getPos(); + Block block = packet.getBlock(); + int blockX = blockPos.getX(); + int blockY = blockPos.getY(); + int blockZ = blockPos.getZ(); + + if (isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && isPlayerPrivilegedToAccessBlock(player, blockX, blockY, blockZ, block)) { return packet; } else { return null; } } - private ClientboundBlockUpdatePacket processBlockUpdatePacket(Player player, ClientboundBlockUpdatePacket packet) { + private Packet processBlockUpdatePacket(Player player, ClientboundBlockUpdatePacket packet) { BlockPos blockPos = packet.getPos(); - int id = BlockIds.impl.getCombinedId(packet.getBlockState()); + Block block = packet.getBlockState().getBlock(); + int blockX = blockPos.getX(); + int blockY = blockPos.getY(); + int blockZ = blockPos.getZ(); - if (isPlayerPrivilegedToAccessBlockPos(player, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { + if (isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && isPlayerPrivilegedToAccessBlock(player, blockX, blockY, blockZ, block)) { return packet; - } else if (blockIdsToObfuscate.contains(id)) { - ClientboundBlockUpdatePacket modifiedPacket = new ClientboundBlockUpdatePacket(packet.getPos(), - blockToObfuscateTo); - return modifiedPacket; + } else if(isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) { + return new ClientboundBlockUpdatePacket(blockPos, blockStateUsedForObfuscation); } else { - return packet; + return null; } } - private ClientboundBlockEntityDataPacket processBlockEntityDataPacket(Player p, - ClientboundBlockEntityDataPacket packet) { + private Packet processBlockEntityDataPacket(Player player, ClientboundBlockEntityDataPacket packet) { BlockPos blockPos = packet.getPos(); + BlockEntityType blockEntityType = packet.getType(); + int blockX = blockPos.getX(); + int blockY = blockPos.getY(); + int blockZ = blockPos.getZ(); - if (isPlayerPrivilegedToAccessBlockPos(p, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { - return packet; - } else if (ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet)) { + if (isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && isPlayerPrivilegedToAccessBlocEntity(player, blockX, blockY, blockZ, blockEntityType)) { return packet; } else { return null; } } + private Packet proccessBlockDestructionPacket(Player player, ClientboundBlockDestructionPacket packet) { + BlockPos blockPos = packet.getPos(); + int entityBreakingBlockId = packet.getId(); + + if(isPlayerPrivilegedToAccessEntity(player, entityBreakingBlockId) && isPlayerPrivilegedToAccessPosition(player, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { + return packet; + } + else { + return null; + } + } + private final Reflection.Field sectionPosField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, SectionPos.class, 0); private final Reflection.Field oldPosField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, short[].class, 0); private final Reflection.Field oldStatesField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, BlockState[].class, 0); @@ -577,8 +611,8 @@ public abstract class TechHider { } public abstract boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ); - public abstract boolean isPlayerPrivilegedToAccessBlock(Player p, int blockMaterialId, int blockX, int blockY,int blockZ); - public abstract boolean isPlayerPrivilegedToAccessBlockEntity(Player p, String blockEntityType, int blockX,int blockY, int blockZ); + public abstract boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block); public abstract boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId); + public abstract boolean isPlayerPrivilegedToAccessBlocEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type); public abstract boolean isPlayerPrivilegedToAccessContainer(Player p, int containerId); }