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/commands/TechhiderbugCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/TechhiderbugCommand.java index 5527176b..f4ae7c73 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/TechhiderbugCommand.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/TechhiderbugCommand.java @@ -60,7 +60,7 @@ public class TechhiderbugCommand extends FightSWCommand { writer.append('\n'); writer.append("Hidden regions: "); - FightSystem.getTechHider().getHiddenRegion().forEach((p, r) -> writer.append(p.getName()).append(':').append(r.toString()).append(' ')); + FightSystem.getTechHider().getHiddenRegion().forEach((p, r) -> writer.append(p.getName()).append(':').append(Arrays.toString(r)).append(' ')); writer.append('\n'); writer.append("TinyProtocol: "); diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightPlayer.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightPlayer.java index 0473f751..e3f2cd20 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightPlayer.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightPlayer.java @@ -69,8 +69,8 @@ public class FightPlayer { public void setOut() { isOut = true; + stopEnternCountdown(); // before the event, so listeners already see canEntern() == false Bukkit.getPluginManager().callEvent(new TeamDeathEvent(this)); - stopEnternCountdown(); ifAI(AI::stop); } diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Hull.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Hull.java index 3ac2528d..16af9f4c 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Hull.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Hull.java @@ -34,6 +34,7 @@ import net.minecraft.core.SectionPos; import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket; import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; @@ -43,6 +44,7 @@ import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; import java.util.logging.Level; import java.util.stream.IntStream; @@ -65,7 +67,8 @@ public class Hull { private final HashMap visibilityDirections = new HashMap<>(); // Contains the visible directions of each occluding visible block private final HashSet uncoveredSurface = new HashSet<>(); - private final HashSet players = new HashSet<>(); + // Players that may see the hull contents (viewers). Everybody else is hidden from it (default deny). Concurrent as it is read on netty threads. + private final Set viewers = ConcurrentHashMap.newKeySet(); private final HashSet entities = new HashSet<>(); private final HashSet rentities = new HashSet<>(); @@ -75,10 +78,8 @@ public class Hull { this.occluding = new BitSet(region.volume()); this.visibility = new BitSet(region.volume()); - IntVector primaryDirection = new IntVector(0, 0, team.isBlue() == (Config.BlueToRedZ > 0) ? -1 : 1); directions = IntStream.range(0, 27) .mapToObj(v -> new IntVector(v % 3 - 1, (v / 3) % 3 - 1, v / 9 - 1)) - .filter(v -> v.sqLength() == 1 || v.x * primaryDirection.x + v.y * primaryDirection.y + v.z * primaryDirection.z == 1) // Not pointing away from primary direction .toArray(IntVector[]::new); for (IntVector direction : directions) { @@ -86,28 +87,29 @@ public class Hull { } } - public boolean blockPrecise(Player player, int chunkX, int chunkY, int chunkZ) { - return players.contains(player) && !region.chunkSectionOutside(chunkX, chunkY, chunkZ); - } - public boolean isBlockHidden(Player player, int x, int y, int z) { - return region.inRegion(x, y, z) && players.contains(player) && !visibility.get(((y - region.getMinY()) * region.getSizeZ() + (z - region.getMinZ())) * region.getSizeX() + (x - region.getMinX())); + return region.inRegion(x, y, z) && !isViewer(player) && !visibility.get(((y - region.getMinY()) * region.getSizeZ() + (z - region.getMinZ())) * region.getSizeX() + (x - region.getMinX())); } - public boolean isLocationHidden(Player player, Location location) { - return players.contains(player) && region.inRegion(location) && !visibility.get(new IntVector(location).toId(region)); + public boolean isViewer(Player player) { + return player != null && viewers.contains(player.getUniqueId()); } - public void addPlayer(Player player) { - if (players.add(player)) { - for (Entity entity : entities) { - untrackEntity(player, entity); - } + /** + * Hides the hull from this player (removes the viewer), also hiding all entities currently inside it. + */ + public void removeViewer(Player player) { + viewers.remove(player.getUniqueId()); + for (Entity entity : entities) { + if (!player.equals(entity)) untrackEntity(player, entity); } } - public void removePlayer(Player player, boolean activeRemoval) { - if (players.remove(player) && activeRemoval) { + /** + * Allows this player to see the hull (adds a viewer). + */ + public void addViewer(Player player) { + if (viewers.add(player.getUniqueId())) { for (Entity entity : entities) { trackEntity(player, entity); } @@ -115,18 +117,22 @@ public class Hull { } } + public void forgetViewer(Player player) { + viewers.remove(player.getUniqueId()); + } + public void checkEntity(Entity entity) { Location location = entity.getLocation(); if (region.inRegion(location) && !visibility.get(new IntVector(location).toId(region))) { if (entities.add(entity)) { - for (Player player : players) { - untrackEntity(player, entity); + for (Player player : Bukkit.getOnlinePlayers()) { + if (!isViewer(player) && !player.equals(entity)) untrackEntity(player, entity); } } } else { if (entities.remove(entity)) { - for (Player player : players) { - trackEntity(player, entity); + for (Player player : Bukkit.getOnlinePlayers()) { + if (!isViewer(player) && !player.equals(entity)) trackEntity(player, entity); } } } @@ -252,7 +258,7 @@ public class Hull { result = new ClientboundBlockUpdatePacket(new BlockPos(pos.getX(), pos.getY(), pos.getZ()), (BlockState) blockdata[0]); } Object packet = result; - players.forEach(player -> TinyProtocol.instance.sendPacket(player, packet)); + Bukkit.getOnlinePlayers().stream().filter(player -> !isViewer(player)).forEach(player -> TinyProtocol.instance.sendPacket(player, packet)); } } diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java index 58bfce48..fe41631e 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java @@ -21,6 +21,8 @@ package de.steamwar.fightsystem.utils; import de.steamwar.entity.REntity; import de.steamwar.fightsystem.Config; +import de.steamwar.fightsystem.FightSystem; +import de.steamwar.fightsystem.events.TeamDeathEvent; import de.steamwar.fightsystem.fight.Fight; import de.steamwar.fightsystem.fight.FightPlayer; import de.steamwar.fightsystem.fight.FightTeam; @@ -29,6 +31,7 @@ import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.StateDependentListener; import de.steamwar.fightsystem.states.StateDependentTask; import lombok.Getter; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.entity.Player; @@ -61,13 +64,22 @@ public class HullHider implements Listener { hulls = hullMap.values().toArray(new Hull[0]); + new StateDependentListener(TechHiderWrapper.ENABLED, FightState.All, new MembershipListener()); new StateDependentListener(TechHiderWrapper.ENABLED, FightState.Schem, this); new StateDependentTask(TechHiderWrapper.ENABLED, FightState.Schem, this::onTick, 0, 1); } public void initialize(FightTeam team) { if (!TechHiderWrapper.ENABLED) return; - hullMap.get(team).initialize(); + Hull hull = hullMap.get(team); + hull.initialize(); + + // Chunks encoded while the hull was filled as hidden contain obfuscated blocks that are visible now, but no block update is generated for them + for (Player player : Bukkit.getOnlinePlayers()) { + if (!hull.isViewer(player)) { + FightSystem.getTechHider().reloadChunks(player, team.getExtendRegion(), Region.EMPTY); + } + } } public void fill(FightTeam team, boolean visible) { @@ -75,14 +87,26 @@ public class HullHider implements Listener { hullMap.get(team).fill(visible); } - @EventHandler(priority = EventPriority.HIGH) - public void onJoin(PlayerJoinEvent e) { - updatePlayer(e.getPlayer()); - } + // Registered in every state: players must be hidden from the hulls by default, no matter when they join. + private class MembershipListener implements Listener { + @EventHandler(priority = EventPriority.HIGH) + public void onJoin(PlayerJoinEvent e) { + updatePlayer(e.getPlayer()); + } - @EventHandler - public void onLeave(PlayerQuitEvent e) { - removePlayer(e.getPlayer(), false); + @EventHandler + public void onLeave(PlayerQuitEvent e) { + forgetViewer(e.getPlayer()); + } + + // A dead player loses the right to see the enemy hull again + @EventHandler + public void onTeamDeath(TeamDeathEvent e) { + e.getFightPlayer().ifPlayer(player -> { + updatePlayer(player); + FightSystem.getTechHider().reloadChunks(player, Fight.getOpposite(e.getFightPlayer().getTeam()).getExtendRegion(), Region.EMPTY); + }); + } } public void updatePlayer(Player player) { @@ -92,16 +116,16 @@ public class HullHider implements Listener { FightPlayer fp = Fight.getFightPlayer(player); for (Map.Entry hull : hullMap.entrySet()) { if (Config.isReferee(player) || hull.getKey() == team || (fp != null && fp.canEntern())) { - hull.getValue().removePlayer(player, true); + hull.getValue().addViewer(player); } else { - hull.getValue().addPlayer(player); + hull.getValue().removeViewer(player); } } } - private void removePlayer(Player player, boolean activeRemoval) { + private void forgetViewer(Player player) { for (Hull hull : hulls) { - hull.removePlayer(player, activeRemoval); + hull.forgetViewer(player); } } 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 7c4c9d77..abc5dd52 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -25,11 +25,13 @@ import de.steamwar.fightsystem.events.BoardingEvent; import de.steamwar.fightsystem.events.TeamLeaveEvent; import de.steamwar.fightsystem.events.TeamSpawnEvent; import de.steamwar.fightsystem.fight.Fight; +import de.steamwar.fightsystem.fight.FightPlayer; import de.steamwar.fightsystem.fight.FightTeam; 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; @@ -49,6 +51,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerQuitEvent; +import java.util.Arrays; import java.util.Collection; import java.util.Objects; import java.util.Set; @@ -60,8 +63,10 @@ public class TechHiderWrapper extends StateDependent implements Listener { public static final boolean ENABLED = !Config.OnlyPublicSchematics && !Config.test() && Config.GameModeConfig.Techhider.Active; + private static final Region[] NO_REGIONS = new Region[0]; + @Getter - private final ConcurrentHashMap hiddenRegion = new ConcurrentHashMap<>(); + private final ConcurrentHashMap hiddenRegion = new ConcurrentHashMap<>(); private final HullHider hullHider; public TechHiderWrapper(HullHider hullHider) { @@ -90,49 +95,90 @@ public class TechHiderWrapper extends StateDependent implements Listener { .map(Holder.Reference::value) .collect(Collectors.toUnmodifiableSet()); - new TechHider(CraftMagicNumbers.getBlock(Config.GameModeConfig.Techhider.ObfuscateWith), new AccessPrivilegeProvider() { - @Override - public boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ) { - return Fight.teams().stream().map(FightTeam::getExtendRegion).allMatch(region -> region.chunkOutside(chunkX, chunkZ)); - } + 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 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, getHiddenRegions(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[] hiddenRegions; + private final Set blockStatesToObfuscate; + private final Set blocksToObfuscate; + private final Set> blockEntityTypeToObfuscate; + + private FightPlayerAccess(Player player, Region[] hiddenRegions, Set blockStatesToObfuscate, Set blocksToObfuscate, Set> blockEntityTypeToObfuscate) { + this.player = player; + this.hiddenRegions = hiddenRegions; + this.blockStatesToObfuscate = blockStatesToObfuscate; + this.blocksToObfuscate = blocksToObfuscate; + this.blockEntityTypeToObfuscate = blockEntityTypeToObfuscate; + } + + private boolean isHidden(int blockX, int blockY, int blockZ) { + for (Region region : hiddenRegions) { + if (region.inRegion(blockX, blockY, blockZ)) return true; + } + return false; + } + + @Override + public Player getPlayer() { + return player; + } + + @Override + public boolean canAccessAllDataWithinChunk(int chunkX, int chunkZ) { + // Hulls the player may not see lie within the hidden regions, everything outside of them is not critical + return Arrays.stream(hiddenRegions).allMatch(region -> region.chunkOutside(chunkX, chunkZ)); + } + + @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 !isHidden(blockX, blockY, blockZ) || !blocksToObfuscate.contains(block); + } + + @Override + public boolean canAccessBlockState(int blockX, int blockY, int blockZ, BlockState blockState) { + return !isHidden(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 !isHidden(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type); + } } @Override @@ -142,19 +188,19 @@ public class TechHiderWrapper extends StateDependent implements Listener { @EventHandler public void teamJoin(TeamSpawnEvent e) { - e.getFightPlayer().ifPlayer(player -> hiddenRegion.put(player, getHiddenRegion(player))); + e.getFightPlayer().ifPlayer(player -> hiddenRegion.put(player, getHiddenRegions(player))); } @EventHandler public void boarding(BoardingEvent e) { - e.getFightPlayer().ifPlayer(player -> hiddenRegion.put(player, getHiddenRegion(player))); + e.getFightPlayer().ifPlayer(player -> hiddenRegion.put(player, getHiddenRegions(player))); } @EventHandler public void teamLeave(TeamLeaveEvent e) { e.getFightPlayer().ifPlayer(player -> { if (player.isOnline()) { - hiddenRegion.put(player, getHiddenRegion(player)); + hiddenRegion.put(player, getHiddenRegions(player)); } }); } @@ -175,15 +221,19 @@ public class TechHiderWrapper extends StateDependent implements Listener { }); } - private Region getHiddenRegion(Player player) { - if (Config.isReferee(player)) return Region.EMPTY; + private Region[] getHiddenRegions(Player player) { + if (Config.isReferee(player)) return NO_REGIONS; - FightTeam team = Fight.getPlayerTeam(player); - if (team == null) return Config.ArenaRegion; + // Single lookup: runs on netty threads, the player can leave the team at any time + FightPlayer fightPlayer = Fight.getFightPlayer(player); + if (fightPlayer == null) { + // Spectators: both teams, the rest of the arena contains nothing critical + return Fight.teams().stream().map(FightTeam::getExtendRegion).toArray(Region[]::new); + } - if (team.canPlayerEntern(player)) return Region.EMPTY; + if (fightPlayer.canEntern()) return NO_REGIONS; - return Fight.getOpposite(team).getExtendRegion(); + return new Region[]{Fight.getOpposite(fightPlayer.getTeam()).getExtendRegion()}; } private Stream getWaterloggedBlockStates() { diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java index b04748c6..38b9b463 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java @@ -25,17 +25,33 @@ import net.minecraft.world.level.block.state.BlockState; 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(); + + /** + * @return true if nothing within this chunk is restricted for this player, so the chunk may be sent unmodified + */ + boolean canAccessAllDataWithinChunk(int chunkX, int chunkZ); + + 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 73db4439..eee198e6 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -19,14 +19,14 @@ 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; import it.unimi.dsi.fastutil.shorts.ShortArraySet; import it.unimi.dsi.fastutil.shorts.ShortSets; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.network.PacketListener; +import net.minecraft.util.Mth; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.common.*; import net.minecraft.network.protocol.configuration.ClientboundFinishConfigurationPacket; @@ -54,7 +54,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 +65,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( @@ -181,6 +181,9 @@ public class TechHider { ClientboundSetChunkCacheCenterPacket.class, // 7.1.93 Set Center Chunk ClientboundForgetLevelChunkPacket.class, // 7.1.38 Unload Chunk ClientboundUpdateTagsPacket.class, // 7.1.133 Update Tags (play) + // 7.1.76 Remove Entities: must not be filtered, otherwise Player#hideEntity leaves ghosts on the client. + // Entities that are already gone from the world pass the entity checks anyway, so no additional information is leaked. + ClientboundRemoveEntitiesPacket.class, ClientboundPlayerInfoRemovePacket.class, // 7.1.68 Player Info Remove ClientboundPlayerInfoUpdatePacket.class, // 7.1.69 Player Info Update ClientboundMoveVehiclePacket.class, // 7.1.56 Move Vehicle (vehicle the player is in) @@ -193,16 +196,16 @@ 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. - processors.put(ClientboundEntityEventPacket.class, this.buildEntityPacketProcessor(ClientboundEntityEventPacket::getEventId)); + processors.put(ClientboundEntityEventPacket.class, this.buildEntityPacketProcessor(packet -> packet.entityId)); // 7.1.36 Teleport Entity: entity id and absolute position. processors.put(ClientboundTeleportEntityPacket.class, this.buildEntityPacketProcessor(ClientboundTeleportEntityPacket::id)); // 7.1.42 Hurt Animation: entity id based signal. @@ -234,43 +237,41 @@ 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)); - // 7.1.76 Remove Entities: entity id visibility side channel. - processors.put(ClientboundRemoveEntitiesPacket.class, (p, packet) -> this.processRemoveEntitiesPacket(p, (ClientboundRemoveEntitiesPacket) packet)); + processors.put(ClientboundRotateHeadPacket.class, (access, packet) -> this.processRotateHeadPacket(access, (ClientboundRotateHeadPacket) 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,26 +291,29 @@ 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; - int blockY = (int) pos.y; - int blockZ = (int) pos.z; + int blockX = Mth.floor(pos.x); + int blockY = Mth.floor(pos.y); + int blockZ = Mth.floor(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,100 @@ 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(Mth.floor(packet.getX()), Mth.floor(packet.getY()), Mth.floor(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) { - IntList entityIdsToRemove = packet.getEntityIds(); - - IntList filteredEntitiesToRemove = entityIdsToRemove.intStream().filter((id) -> privilegeProvider.isPlayerPrivilegedToAccessEntity(player, 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 +435,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 +486,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 +509,43 @@ public class TechHider { return new ClientboundSectionBlocksUpdatePacket(sectionPos, ShortSets.unmodifiable(new ShortArraySet(newPos)), newStates); } - private Packet processLevelParticlesPacket(Player player, ClientboundLevelParticlesPacket packet) { - int blockX = (int) packet.getX(); - int blockY = (int) packet.getY(); - int blockZ = (int) packet.getZ(); + private Packet processLevelParticlesPacket(ForPlayer access, ClientboundLevelParticlesPacket packet) { + int blockX = Mth.floor(packet.getX()); + int blockY = Mth.floor(packet.getY()); + int blockZ = Mth.floor(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) { - if (privilegeProvider.isEveryonePrivilegedToAccessAllDataWithinChunk(packet.getX(), packet.getZ())) { + private ClientboundLevelChunkWithLightPacket processChunkWithLight(ForPlayer access, ClientboundLevelChunkWithLightPacket packet) { + if (access.canAccessAllDataWithinChunk(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); }; } }