diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/Config.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/Config.java index c8a5883c..13f7e18a 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/Config.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/Config.java @@ -34,9 +34,11 @@ import org.bukkit.util.Vector; import java.io.File; import java.util.Collections; +import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.logging.Level; +import java.util.stream.Collectors; public class Config { @@ -80,7 +82,7 @@ public class Config { //event parameter public static final EventFight EventKampf; - private static final Set Referees; + private static final Set Referees; public static final int EventTeamBlueID; public static final int EventTeamRedID; public static final boolean BothTeamsPublic; @@ -237,7 +239,11 @@ public class Config { BothTeamsPublic = EventTeamRedID == 0 && EventTeamBlueID == 0; SpectatePort = EventKampf.getSpectatePort() == null ? 0 : EventKampf.getSpectatePort(); LiveReplay = SpectatePort != 0; - Referees = Referee.get(Config.EventKampf.getEventID()); + Referees = Referee.get(Config.EventKampf.getEventID()).stream() + .map(SteamwarUser::byId) + .filter(Objects::nonNull) + .map(SteamwarUser::getUUID) + .collect(Collectors.toUnmodifiableSet()); Event event = Event.byId(EventKampf.getEventID()); if (BothTeamsPublic) { @@ -305,6 +311,6 @@ public class Config { } public static boolean isReferee(Player player) { - return Referees.contains(SteamwarUser.get(player.getUniqueId()).getId()); + return Referees.contains(player.getUniqueId()); } } diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java index e614cda7..c12fd38b 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -31,6 +31,7 @@ import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.StateDependent; import de.steamwar.fightsystem.states.StateDependentListener; import de.steamwar.techhider.AccessPrivilegeProvider; +import de.steamwar.techhider.AccessPrivilegeProvider.ForPlayer; import de.steamwar.techhider.TechHider; import lombok.Getter; import net.minecraft.core.Holder; @@ -91,49 +92,82 @@ public class TechHiderWrapper extends StateDependent implements Listener { .map(Holder.Reference::value) .collect(Collectors.toUnmodifiableSet()); - new TechHider(CraftMagicNumbers.getBlock(Config.GameModeConfig.Techhider.ObfuscateWith), new AccessPrivilegeProvider() { + new TechHider(CraftMagicNumbers.getBlock(Config.GameModeConfig.Techhider.ObfuscateWith), newProvider(blockStatesToObfuscate, blocksToObfuscate, blockEntityTypeToObfuscate)); + } + + private AccessPrivilegeProvider newProvider(Set blockStatesToObfuscate, Set blocksToObfuscate, Set> blockEntityTypeToObfuscate) { + return new AccessPrivilegeProvider() { @Override public boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ) { return Fight.teams().stream().map(FightTeam::getExtendRegion).allMatch(region -> region.chunkOutside(chunkX, chunkZ)); } @Override - public boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ) { - return !hullHider.isBlockHidden(p, blockX, blockY, blockZ); - } - - @Override - public boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block) { - return !getHiddenRegion(p).inRegion(blockX, blockY, blockZ) || !blocksToObfuscate.contains(block); - } - - @Override - public boolean isPlayerPrivilegedToAccessBlockState(Player p, int blockX, int blockY, int blockZ, BlockState blockState) { - return !getHiddenRegion(p).inRegion(blockX, blockY, blockZ) || !blockStatesToObfuscate.contains(blockState); - } - - // TODO will require entity tracking on the netty thread to prevent future race conditions - @Override - public boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId) { - net.minecraft.world.entity.Entity nmsEntity = ((CraftWorld) p.getWorld()).getHandle().moonrise$getEntityLookup().get(entityId); - - if (nmsEntity != null) { - return !hullHider.isBlockHidden(p, nmsEntity.getBlockX(), nmsEntity.getBlockY(), nmsEntity.getBlockZ()); - } else { - return true; - } - } - - @Override - public boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type) { - return !getHiddenRegion(p).inRegion(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type); + public ForPlayer forPlayer(Player p) { + return new FightPlayerAccess(p, getHiddenRegion(p), blockStatesToObfuscate, blocksToObfuscate, blockEntityTypeToObfuscate); } @Override public boolean isPlayerPrivilegedToPerformAction(Player p) { return p.getGameMode() != GameMode.SPECTATOR; } - }); + }; + } + + /** + * Snapshot of what one player may access, taken once per outgoing packet on the netty thread. + */ + private class FightPlayerAccess implements ForPlayer { + private final Player player; + private final Region hiddenRegion; + private final Set blockStatesToObfuscate; + private final Set blocksToObfuscate; + private final Set> blockEntityTypeToObfuscate; + + private FightPlayerAccess(Player player, Region hiddenRegion, Set blockStatesToObfuscate, Set blocksToObfuscate, Set> blockEntityTypeToObfuscate) { + this.player = player; + this.hiddenRegion = hiddenRegion; + this.blockStatesToObfuscate = blockStatesToObfuscate; + this.blocksToObfuscate = blocksToObfuscate; + this.blockEntityTypeToObfuscate = blockEntityTypeToObfuscate; + } + + @Override + public Player getPlayer() { + return player; + } + + @Override + public boolean canAccessPosition(int blockX, int blockY, int blockZ) { + return !hullHider.isBlockHidden(player, blockX, blockY, blockZ); + } + + @Override + public boolean canAccessBlock(int blockX, int blockY, int blockZ, Block block) { + return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blocksToObfuscate.contains(block); + } + + @Override + public boolean canAccessBlockState(int blockX, int blockY, int blockZ, BlockState blockState) { + return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blockStatesToObfuscate.contains(blockState); + } + + // TODO will require entity tracking on the netty thread to prevent future race conditions + @Override + public boolean canAccessEntity(int entityId) { + net.minecraft.world.entity.Entity nmsEntity = ((CraftWorld) player.getWorld()).getHandle().moonrise$getEntityLookup().get(entityId); + + if (nmsEntity != null) { + return !hullHider.isBlockHidden(player, nmsEntity.getBlockX(), nmsEntity.getBlockY(), nmsEntity.getBlockZ()); + } else { + return true; + } + } + + @Override + public boolean canAccessBlockEntity(int blockX, int blockY, int blockZ, BlockEntityType type) { + return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type); + } } @Override diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java index b04748c6..740528a8 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java @@ -27,15 +27,28 @@ import org.bukkit.entity.Player; public interface AccessPrivilegeProvider { boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ); - boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ); - - boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block); - - boolean isPlayerPrivilegedToAccessBlockState(Player p, int blockX, int blockY, int blockZ, BlockState blockState); - - boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId); - - boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type); + /** + * Resolves everything that can change concurrently about this player once. + * The result is used for all decisions of one outgoing packet (including all packets of a bundle). + */ + ForPlayer forPlayer(Player p); boolean isPlayerPrivilegedToPerformAction(Player p); + + /** + * What a single player may access, valid for the processing of one outgoing packet. + */ + interface ForPlayer { + Player getPlayer(); + + boolean canAccessPosition(int blockX, int blockY, int blockZ); + + boolean canAccessBlock(int blockX, int blockY, int blockZ, Block block); + + boolean canAccessBlockState(int blockX, int blockY, int blockZ, BlockState blockState); + + boolean canAccessEntity(int entityId); + + boolean canAccessBlockEntity(int blockX, int blockY, int blockZ, BlockEntityType type); + } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java index bd21f357..caa9c96b 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java @@ -19,6 +19,7 @@ package de.steamwar.techhider; +import de.steamwar.techhider.AccessPrivilegeProvider.ForPlayer; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; @@ -44,11 +45,9 @@ public class ChunkHider { private final int BIOMES_PER_SECTION = 64; private final int blockIdUsedForHiding; - private final AccessPrivilegeProvider accessPrivilegeProvider; - public ChunkHider(Block blockUsedForObfuscation, AccessPrivilegeProvider accessPrivilegeProvider) { + public ChunkHider(Block blockUsedForObfuscation) { blockIdUsedForHiding = Block.BLOCK_STATE_REGISTRY.getId(blockUsedForObfuscation.defaultBlockState()); - this.accessPrivilegeProvider = accessPrivilegeProvider; } private int getLongsRequiredToEncodeEntries(int bitsPerEntry, int entryCount) { @@ -69,7 +68,8 @@ public class ChunkHider { return dataArray; } - public ClientboundLevelChunkWithLightPacket processLevelChunkWithLightPacket(Player player, ClientboundLevelChunkWithLightPacket packet) { + public ClientboundLevelChunkWithLightPacket processLevelChunkWithLightPacket(ForPlayer access, ClientboundLevelChunkWithLightPacket packet) { + Player player = access.getPlayer(); int chunkX = packet.getX(); int chunkZ = packet.getZ(); ClientboundLevelChunkPacketData chunkData = packet.getChunkData(); @@ -104,7 +104,7 @@ public class ChunkHider { resolvedData[i] = pallet[palletReference]; } - int[] obfuscatedBlockData = obfuscateBlockDataArray(player, chunkX, chunkZ, yOffset, resolvedData); + int[] obfuscatedBlockData = obfuscateBlockDataArray(access, chunkX, chunkZ, yOffset, resolvedData); long[] reEncodedData = encodeDirectBlockDataArray(obfuscatedBlockData); @@ -149,7 +149,7 @@ public class ChunkHider { int[] blockData = resolveDirectBlockDataArray(rawData, bitsPerBlock); - int[] obfuscatedBlockData = obfuscateBlockDataArray(player, chunkX, chunkZ, yOffset, blockData); + int[] obfuscatedBlockData = obfuscateBlockDataArray(access, chunkX, chunkZ, yOffset, blockData); long[] reEncodedData = encodeDirectBlockDataArray(obfuscatedBlockData); @@ -172,13 +172,13 @@ public class ChunkHider { out.readBytes(data); List blockEntities = chunkData.blockEntitiesData; - List filteredBlockEntities = filterBlockEntities(player, blockEntities, chunkX, chunkZ); + List filteredBlockEntities = filterBlockEntities(access, blockEntities, chunkX, chunkZ); return buildNewChunkPacket(packet, data, filteredBlockEntities); } - private int[] obfuscateBlockDataArray(Player player, int chunkX, int chunkZ, int yOffset, int[] blockDataArray) { + private int[] obfuscateBlockDataArray(ForPlayer access, int chunkX, int chunkZ, int yOffset, int[] blockDataArray) { int[] obfuscatedData = new int[BLOCKS_PER_SECTION]; for (int sectionY = 0; sectionY < SECTION_SPAN_SIZE; sectionY++) { for (int sectionZ = 0; sectionZ < SECTION_SPAN_SIZE; sectionZ++) { @@ -194,7 +194,7 @@ public class ChunkHider { Block block = blockState.getBlock(); - if (accessPrivilegeProvider.isPlayerPrivilegedToAccessPosition(player, worldX, worldY, worldZ) && accessPrivilegeProvider.isPlayerPrivilegedToAccessBlock(player, worldX, worldY, worldZ, block) && accessPrivilegeProvider.isPlayerPrivilegedToAccessBlockState(player, worldX, worldY, worldZ, blockState)) { + if (access.canAccessPosition(worldX, worldY, worldZ) && access.canAccessBlock(worldX, worldY, worldZ, block) && access.canAccessBlockState(worldX, worldY, worldZ, blockState)) { obfuscatedData[blockDataIndex] = blockId; } else { obfuscatedData[blockDataIndex] = blockIdUsedForHiding; @@ -241,7 +241,7 @@ public class ChunkHider { return clonedPacket; } - private List filterBlockEntities(Player player, List blockEntities, int chunkX, int chunkZ) { + private List filterBlockEntities(ForPlayer access, List blockEntities, int chunkX, int chunkZ) { int fourBitBitmask = 0b0000_1111; return blockEntities.stream() @@ -257,7 +257,7 @@ public class ChunkHider { int worldY = blockEntityInfo.y; - return accessPrivilegeProvider.isPlayerPrivilegedToAccessPosition(player, worldX, worldY, worldZ) && accessPrivilegeProvider.isPlayerPrivilegedToAccessBlockEntity(player, worldX, worldY, worldZ, type); + return access.canAccessPosition(worldX, worldY, worldZ) && access.canAccessBlockEntity(worldX, worldY, worldZ, type); }).toList(); } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 7f432b96..c24fad71 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -19,6 +19,7 @@ package de.steamwar.techhider; +import de.steamwar.techhider.AccessPrivilegeProvider.ForPlayer; import com.comphenix.tinyprotocol.TinyProtocol; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; @@ -54,7 +55,7 @@ import java.util.function.ToIntFunction; */ public class TechHider { private final Set> bypassingPackets; - private final Map>, BiFunction, Packet>> packetProcessors; + private final Map>, BiFunction, Packet>> packetProcessors; private final BlockState blockStateUsedForObfuscation; private final ChunkHider chunkHider; @@ -65,7 +66,7 @@ public class TechHider { public TechHider(Block blockUsedForObfuscation, AccessPrivilegeProvider privilegeProvider) { this.blockStateUsedForObfuscation = blockUsedForObfuscation.defaultBlockState(); - this.chunkHider = new ChunkHider(blockUsedForObfuscation, privilegeProvider); + this.chunkHider = new ChunkHider(blockUsedForObfuscation); this.privilegeProvider = privilegeProvider; this.bypassingPackets = new HashSet<>(List.of( @@ -193,12 +194,12 @@ public class TechHider { ClientboundContainerSetSlotPacket.class // 7.1.21 Set Container Slot )); - BiFunction, Packet> tossPacket = (p, packet) -> null; - Map>, BiFunction, Packet>> processors = new HashMap<>(); + BiFunction, Packet> tossPacket = (access, packet) -> null; + Map>, BiFunction, Packet>> processors = new HashMap<>(); // --- Entity packets - // 7.1.2 Spawn Entity: entity type and position can reveal hidden contraptions. - processors.put(ClientboundAddEntityPacket.class, (p, packet) -> this.processAddEntityPacket(p, (ClientboundAddEntityPacket) packet)); + processors.put(ClientboundAddEntityPacket.class, (access, packet) -> this.processAddEntityPacket(access, (ClientboundAddEntityPacket) packet)); // 7.1.3 Entity Animation: entity id based signal, keep blocked until entity visibility is modeled. processors.put(ClientboundAnimatePacket.class, this.buildEntityPacketProcessor(ClientboundAnimatePacket::getId)); // 7.1.35 Entity Event: entity id based signal. @@ -234,43 +235,43 @@ public class TechHider { // 7.1.67 Combat Death: entity/player ids and death message context. processors.put(ClientboundPlayerCombatKillPacket.class, this.buildEntityPacketProcessor(ClientboundPlayerCombatKillPacket::playerId)); // 7.1.52/53/55 Update Entity Position/Rotation: entity id and movement signal. - processors.put(ClientboundMoveEntityPacket.class, (p, packet) -> this.processMoveEntityPacket(p, (ClientboundMoveEntityPacket) packet)); - processors.put(ClientboundMoveEntityPacket.Pos.class, (p, packet) -> this.processMoveEntityPacket(p, (ClientboundMoveEntityPacket.Pos) packet)); - processors.put(ClientboundMoveEntityPacket.Rot.class, (p, packet) -> this.processMoveEntityPacket(p, (ClientboundMoveEntityPacket.Rot) packet)); - processors.put(ClientboundMoveEntityPacket.PosRot.class, (p, packet) -> this.processMoveEntityPacket(p, (ClientboundMoveEntityPacket.PosRot) packet)); + processors.put(ClientboundMoveEntityPacket.class, (access, packet) -> this.processMoveEntityPacket(access, (ClientboundMoveEntityPacket) packet)); + processors.put(ClientboundMoveEntityPacket.Pos.class, (access, packet) -> this.processMoveEntityPacket(access, (ClientboundMoveEntityPacket.Pos) packet)); + processors.put(ClientboundMoveEntityPacket.Rot.class, (access, packet) -> this.processMoveEntityPacket(access, (ClientboundMoveEntityPacket.Rot) packet)); + processors.put(ClientboundMoveEntityPacket.PosRot.class, (access, packet) -> this.processMoveEntityPacket(access, (ClientboundMoveEntityPacket.PosRot) packet)); // 7.1.82 Set Head Rotation: entity id and rotation. - processors.put(ClientboundRotateHeadPacket.class, (p, packet) -> this.processRotateHeadPacket(p, (ClientboundRotateHeadPacket) packet)); + processors.put(ClientboundRotateHeadPacket.class, (access, packet) -> this.processRotateHeadPacket(access, (ClientboundRotateHeadPacket) packet)); // 7.1.76 Remove Entities: entity id visibility side channel. - processors.put(ClientboundRemoveEntitiesPacket.class, (p, packet) -> this.processRemoveEntitiesPacket(p, (ClientboundRemoveEntitiesPacket) packet)); + processors.put(ClientboundRemoveEntitiesPacket.class, (access, packet) -> this.processRemoveEntitiesPacket(access, (ClientboundRemoveEntitiesPacket) packet)); // 7.1.99 Link Entities: entity relationship signal. - processors.put(ClientboundSetEntityLinkPacket.class, (p, packet) -> this.processSetEntityLinkPacket(p, (ClientboundSetEntityLinkPacket) packet)); + processors.put(ClientboundSetEntityLinkPacket.class, (access, packet) -> this.processSetEntityLinkPacket(access, (ClientboundSetEntityLinkPacket) packet)); // --- Block entity packets --- // 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)); + processors.put(ClientboundBlockEntityDataPacket.class, (access, packet) -> processBlockEntityDataPacket(access, (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, (p, packet) -> proccessBlockDestructionPacket(p, (ClientboundBlockDestructionPacket) packet)); + processors.put(ClientboundBlockDestructionPacket.class, (access, packet) -> proccessBlockDestructionPacket(access, (ClientboundBlockDestructionPacket) packet)); // 7.1.8 Block Action: currently filtered by block position. - processors.put(ClientboundBlockEventPacket.class, (p, packet) -> processBlockEventPacket(p, (ClientboundBlockEventPacket) packet)); + processors.put(ClientboundBlockEventPacket.class, (access, packet) -> processBlockEventPacket(access, (ClientboundBlockEventPacket) packet)); // 7.1.9 Block Update: currently filtered/obfuscated by block position and block id. - processors.put(ClientboundBlockUpdatePacket.class, (p, packet) -> processBlockUpdatePacket(p, (ClientboundBlockUpdatePacket) packet)); + processors.put(ClientboundBlockUpdatePacket.class, (access, packet) -> processBlockUpdatePacket(access, (ClientboundBlockUpdatePacket) packet)); // --- Chunk packets --- // 7.1.45 Chunk Data and Update Light: currently filtered/obfuscated by chunk data processor. - processors.put(ClientboundLevelChunkWithLightPacket.class, (p, packet) -> processChunkWithLight(p, (ClientboundLevelChunkWithLightPacket) packet)); + processors.put(ClientboundLevelChunkWithLightPacket.class, (access, packet) -> processChunkWithLight(access, (ClientboundLevelChunkWithLightPacket) packet)); // --- Section packets --- // 7.1.83 Update Section Blocks: currently filtered/obfuscated by section block processor. - processors.put(ClientboundSectionBlocksUpdatePacket.class, (p, packet) -> processSectionUpdate(p, (ClientboundSectionBlocksUpdatePacket) packet)); + processors.put(ClientboundSectionBlocksUpdatePacket.class, (access, packet) -> processSectionUpdate(access, (ClientboundSectionBlocksUpdatePacket) packet)); // --- Particle packets --- // 7.1.47 Particle: particle type and position can reveal hidden machinery. - processors.put(ClientboundLevelParticlesPacket.class, (p, packet) -> processLevelParticlesPacket(p, (ClientboundLevelParticlesPacket) packet)); + processors.put(ClientboundLevelParticlesPacket.class, (access, packet) -> processLevelParticlesPacket(access, (ClientboundLevelParticlesPacket) packet)); // --- Sound packets --- // 7.1.115 Entity Sound Effect: entity id and sound. @@ -290,7 +291,7 @@ public class TechHider { // 7.1.59 Open Sign Editor: block position. processors.put(ClientboundOpenSignEditorPacket.class, buildPositionBasedPacketProcessor(ClientboundOpenSignEditorPacket::getPos)); // 7.1.37 Explosion: position, affected blocks, and knockback can reveal TNT/tech. - processors.put(ClientboundExplodePacket.class, (p, rawPacket) -> { + processors.put(ClientboundExplodePacket.class, (access, rawPacket) -> { ClientboundExplodePacket packet = (ClientboundExplodePacket) rawPacket; Vec3 pos = packet.center(); int blockX = (int) pos.x; @@ -298,18 +299,21 @@ public class TechHider { int blockZ = (int) pos.z; - return proccessPositionBasedPacket(p, blockX, blockY, blockZ, packet); + return proccessPositionBasedPacket(access, blockX, blockY, blockZ, packet); }); this.packetProcessors = processors; TinyProtocol.instance.addGlobalClientboundFilter((player, rawPacket) -> { try { + // Resolved lazily (login packets arrive before the player is known) and once per outer packet + AccessHolder access = new AccessHolder(player); + if (rawPacket instanceof ClientboundBundlePacket bundlePacket) { List> processedPackets = new ArrayList<>(); for (Packet packet : bundlePacket.subPackets()) { - Packet processedPacket = (Packet) processPacket(player, packet); + Packet processedPacket = (Packet) processPacket(access, packet); if (processedPacket != null) { processedPackets.add(processedPacket); @@ -318,7 +322,7 @@ public class TechHider { return new ClientboundBundlePacket(processedPackets); } else { - return processPacket(player, rawPacket); + return processPacket(access, rawPacket); } } catch (Throwable t) { t.printStackTrace(); @@ -330,89 +334,108 @@ public class TechHider { TinyProtocol.instance.addFilter(ServerboundInteractPacket.class, (p, packet) -> privilegeProvider.isPlayerPrivilegedToPerformAction(p) ? packet : null); } - private Packet processPacket(Player player, Packet packet) { + private final class AccessHolder { + private final Player player; + private ForPlayer access; + + private AccessHolder(Player player) { + this.player = player; + } + + private ForPlayer get() { + if (access == null) { + access = privilegeProvider.forPlayer(player); + } + return access; + } + } + + private Packet processPacket(AccessHolder access, Packet packet) { if (bypassingPackets.contains(packet.getClass())) { return packet; - } else if (packetProcessors.containsKey(packet.getClass())) { - return packetProcessors.get(packet.getClass()).apply(player, packet); + } + + BiFunction, Packet> processor = packetProcessors.get(packet.getClass()); + if (processor != null) { + return processor.apply(access.get(), packet); } else { return null; } } - private Packet processAddEntityPacket(Player player, ClientboundAddEntityPacket packet) { - if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, packet.getId()) && privilegeProvider.isPlayerPrivilegedToAccessPosition(player, (int) packet.getX(), (int) packet.getY(), (int) packet.getZ())) { + private Packet processAddEntityPacket(ForPlayer access, ClientboundAddEntityPacket packet) { + if (access.canAccessEntity(packet.getId()) && access.canAccessPosition((int) packet.getX(), (int) packet.getY(), (int) packet.getZ())) { return packet; } else { return null; } } - private Packet processEntityPacket(Player player, int entityId, Packet packet) { - if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, entityId)) { + private Packet processEntityPacket(ForPlayer access, int entityId, Packet packet) { + if (access.canAccessEntity(entityId)) { return packet; } else { return null; } } - private > BiFunction, Packet> buildEntityPacketProcessor(ToIntFunction entityIdExtractor) { - return (p, rawPacket) -> { + private > BiFunction, Packet> buildEntityPacketProcessor(ToIntFunction entityIdExtractor) { + return (access, rawPacket) -> { TTargetPacket packet = (TTargetPacket) rawPacket; - return processEntityPacket(p, entityIdExtractor.applyAsInt(packet), packet); + return processEntityPacket(access, entityIdExtractor.applyAsInt(packet), packet); }; } - private Packet processMoveEntityPacket(Player player, ClientboundMoveEntityPacket packet) { - if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, packet.entityId)) { + private Packet processMoveEntityPacket(ForPlayer access, ClientboundMoveEntityPacket packet) { + if (access.canAccessEntity(packet.entityId)) { return packet; } else { return null; } } - private Packet processRotateHeadPacket(Player player, ClientboundRotateHeadPacket packet) { - if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, packet.entityId)) { + private Packet processRotateHeadPacket(ForPlayer access, ClientboundRotateHeadPacket packet) { + if (access.canAccessEntity(packet.entityId)) { return packet; } else { return null; } } - private Packet processRemoveEntitiesPacket(Player player, ClientboundRemoveEntitiesPacket packet) { + private Packet processRemoveEntitiesPacket(ForPlayer access, ClientboundRemoveEntitiesPacket packet) { IntList entityIdsToRemove = packet.getEntityIds(); - IntList filteredEntitiesToRemove = entityIdsToRemove.intStream().filter((id) -> privilegeProvider.isPlayerPrivilegedToAccessEntity(player, id)).collect(IntArrayList::new, IntList::add, IntList::addAll); + IntList filteredEntitiesToRemove = entityIdsToRemove.intStream().filter((id) -> access.canAccessEntity(id)).collect(IntArrayList::new, IntList::add, IntList::addAll); return new ClientboundRemoveEntitiesPacket(filteredEntitiesToRemove); } - public Packet processSetEntityLinkPacket(Player player, ClientboundSetEntityLinkPacket packet) { + public Packet processSetEntityLinkPacket(ForPlayer access, ClientboundSetEntityLinkPacket packet) { int fromEntityId = packet.getSourceId(); int toEntityId = packet.getDestId(); - if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, fromEntityId) && privilegeProvider.isPlayerPrivilegedToAccessEntity(player, toEntityId)) { + if (access.canAccessEntity(fromEntityId) && access.canAccessEntity(toEntityId)) { return packet; } else { return null; } } - private Packet processBlockEventPacket(Player player, ClientboundBlockEventPacket packet) { + private Packet processBlockEventPacket(ForPlayer access, ClientboundBlockEventPacket packet) { BlockPos blockPos = packet.getPos(); Block block = packet.getBlock(); int blockX = blockPos.getX(); int blockY = blockPos.getY(); int blockZ = blockPos.getZ(); - if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && privilegeProvider.isPlayerPrivilegedToAccessBlock(player, blockX, blockY, blockZ, block)) { + if (access.canAccessPosition(blockX, blockY, blockZ) && access.canAccessBlock(blockX, blockY, blockZ, block)) { return packet; } else { return null; } } - private Packet processBlockUpdatePacket(Player player, ClientboundBlockUpdatePacket packet) { + private Packet processBlockUpdatePacket(ForPlayer access, ClientboundBlockUpdatePacket packet) { BlockPos blockPos = packet.getPos(); BlockState blockState = packet.getBlockState(); Block block = blockState.getBlock(); @@ -420,41 +443,41 @@ public class TechHider { int blockY = blockPos.getY(); int blockZ = blockPos.getZ(); - if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && privilegeProvider.isPlayerPrivilegedToAccessBlock(player, blockX, blockY, blockZ, block) && privilegeProvider.isPlayerPrivilegedToAccessBlockState(player, blockX, blockY, blockZ, blockState)) { + if (access.canAccessPosition(blockX, blockY, blockZ) && access.canAccessBlock(blockX, blockY, blockZ, block) && access.canAccessBlockState(blockX, blockY, blockZ, blockState)) { return packet; - } else if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) { + } else if (access.canAccessPosition(blockX, blockY, blockZ)) { return new ClientboundBlockUpdatePacket(blockPos, blockStateUsedForObfuscation); } else { return null; } } - private Packet processBlockEntityDataPacket(Player player, ClientboundBlockEntityDataPacket packet) { + private Packet processBlockEntityDataPacket(ForPlayer access, ClientboundBlockEntityDataPacket packet) { BlockPos blockPos = packet.getPos(); BlockEntityType blockEntityType = packet.getType(); int blockX = blockPos.getX(); int blockY = blockPos.getY(); int blockZ = blockPos.getZ(); - if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && privilegeProvider.isPlayerPrivilegedToAccessBlockEntity(player, blockX, blockY, blockZ, blockEntityType)) { + if (access.canAccessPosition(blockX, blockY, blockZ) && access.canAccessBlockEntity(blockX, blockY, blockZ, blockEntityType)) { return packet; } else { return null; } } - private Packet proccessBlockDestructionPacket(Player player, ClientboundBlockDestructionPacket packet) { + private Packet proccessBlockDestructionPacket(ForPlayer access, ClientboundBlockDestructionPacket packet) { BlockPos blockPos = packet.getPos(); int entityBreakingBlockId = packet.getId(); - if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, entityBreakingBlockId) && privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { + if (access.canAccessEntity(entityBreakingBlockId) && access.canAccessPosition(blockPos.getX(), blockPos.getY(), blockPos.getZ())) { return packet; } else { return null; } } - private ClientboundSectionBlocksUpdatePacket processSectionUpdate(Player p, ClientboundSectionBlocksUpdatePacket packet) { + private ClientboundSectionBlocksUpdatePacket processSectionUpdate(ForPlayer access, ClientboundSectionBlocksUpdatePacket packet) { SectionPos sectionPos = packet.sectionPos; short[] oldPos = packet.positions; BlockState[] oldStates = packet.states; @@ -471,10 +494,10 @@ public class TechHider { int worldY = sectionPos.relativeToBlockY(posShort); int worldZ = sectionPos.relativeToBlockZ(posShort); - if (privilegeProvider.isPlayerPrivilegedToAccessPosition(p, worldX, worldY, worldZ) && privilegeProvider.isPlayerPrivilegedToAccessBlock(p, worldX, worldY, worldZ, block) && privilegeProvider.isPlayerPrivilegedToAccessBlockState(p, worldX, worldY, worldZ, state)) { + if (access.canAccessPosition(worldX, worldY, worldZ) && access.canAccessBlock(worldX, worldY, worldZ, block) && access.canAccessBlockState(worldX, worldY, worldZ, state)) { filteredPos.add(posShort); filteredStates.add(state); - } else if (privilegeProvider.isPlayerPrivilegedToAccessPosition(p, worldX, worldY, worldZ)) { + } else if (access.canAccessPosition(worldX, worldY, worldZ)) { filteredPos.add(posShort); filteredStates.add(blockStateUsedForObfuscation); } @@ -494,43 +517,43 @@ public class TechHider { return new ClientboundSectionBlocksUpdatePacket(sectionPos, ShortSets.unmodifiable(new ShortArraySet(newPos)), newStates); } - private Packet processLevelParticlesPacket(Player player, ClientboundLevelParticlesPacket packet) { + private Packet processLevelParticlesPacket(ForPlayer access, ClientboundLevelParticlesPacket packet) { int blockX = (int) packet.getX(); int blockY = (int) packet.getY(); int blockZ = (int) packet.getZ(); - if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) { + if (access.canAccessPosition(blockX, blockY, blockZ)) { return packet; } else { return null; } } - private ClientboundLevelChunkWithLightPacket processChunkWithLight(Player p, ClientboundLevelChunkWithLightPacket packet) { + private ClientboundLevelChunkWithLightPacket processChunkWithLight(ForPlayer access, ClientboundLevelChunkWithLightPacket packet) { if (privilegeProvider.isEveryonePrivilegedToAccessAllDataWithinChunk(packet.getX(), packet.getZ())) { return packet; } else { - return chunkHider.processLevelChunkWithLightPacket(p, packet); + return chunkHider.processLevelChunkWithLightPacket(access, packet); } } - private Packet proccessPositionBasedPacket(Player player, int blockX, int blockY, int blockZ, Packet packet) { - if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) { + private Packet proccessPositionBasedPacket(ForPlayer access, int blockX, int blockY, int blockZ, Packet packet) { + if (access.canAccessPosition(blockX, blockY, blockZ)) { return packet; } else { return null; } } - private > BiFunction, Packet> buildPositionBasedPacketProcessor(Function positionExtractor) { - return (p, rawPacket) -> { + private > BiFunction, Packet> buildPositionBasedPacketProcessor(Function positionExtractor) { + return (access, rawPacket) -> { TTargetPacket packet = (TTargetPacket) rawPacket; BlockPos pos = positionExtractor.apply(packet); int blockX = pos.getX(); int blockY = pos.getY(); int blockZ = pos.getZ(); - return proccessPositionBasedPacket(p, blockX, blockY, blockZ, packet); + return proccessPositionBasedPacket(access, blockX, blockY, blockZ, packet); }; } }