forked from SteamWar/SteamWar
Merge pull request 'Hull hider fixes' (#517) from HullHiderFixes into main
Reviewed-on: SteamWar/SteamWar#517
This commit is contained in:
@@ -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<Integer> Referees;
|
||||
private static final Set<UUID> 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());
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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: ");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<IntVector, BitSet> visibilityDirections = new HashMap<>(); // Contains the visible directions of each occluding visible block
|
||||
private final HashSet<IntVector> uncoveredSurface = new HashSet<>();
|
||||
|
||||
private final HashSet<Player> 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<UUID> viewers = ConcurrentHashMap.newKeySet();
|
||||
private final HashSet<Entity> entities = new HashSet<>();
|
||||
private final HashSet<REntity> 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<FightTeam, Hull> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+95
-45
@@ -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<Player, Region> hiddenRegion = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<Player, Region[]> 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<BlockState> blockStatesToObfuscate, Set<Block> blocksToObfuscate, Set<BlockEntityType<?>> 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<BlockState> blockStatesToObfuscate;
|
||||
private final Set<Block> blocksToObfuscate;
|
||||
private final Set<BlockEntityType<?>> blockEntityTypeToObfuscate;
|
||||
|
||||
private FightPlayerAccess(Player player, Region[] hiddenRegions, Set<BlockState> blockStatesToObfuscate, Set<Block> blocksToObfuscate, Set<BlockEntityType<?>> 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<BlockState> getWaterloggedBlockStates() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ClientboundLevelChunkPacketData.BlockEntityInfo> blockEntities = chunkData.blockEntitiesData;
|
||||
List<ClientboundLevelChunkPacketData.BlockEntityInfo> filteredBlockEntities = filterBlockEntities(player, blockEntities, chunkX, chunkZ);
|
||||
List<ClientboundLevelChunkPacketData.BlockEntityInfo> 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<ClientboundLevelChunkPacketData.BlockEntityInfo> filterBlockEntities(Player player, List<ClientboundLevelChunkPacketData.BlockEntityInfo> blockEntities, int chunkX, int chunkZ) {
|
||||
private List<ClientboundLevelChunkPacketData.BlockEntityInfo> filterBlockEntities(ForPlayer access, List<ClientboundLevelChunkPacketData.BlockEntityInfo> 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Class<?>> bypassingPackets;
|
||||
private final Map<Class<? extends Packet<? extends PacketListener>>, BiFunction<Player, Packet<? extends PacketListener>, Packet<? extends PacketListener>>> packetProcessors;
|
||||
private final Map<Class<? extends Packet<? extends PacketListener>>, BiFunction<ForPlayer, Packet<? extends PacketListener>, Packet<? extends PacketListener>>> 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<Player, Packet<? extends PacketListener>, Packet<? extends PacketListener>> tossPacket = (p, packet) -> null;
|
||||
Map<Class<? extends Packet<? extends PacketListener>>, BiFunction<Player, Packet<? extends PacketListener>, Packet<? extends PacketListener>>> processors = new HashMap<>();
|
||||
BiFunction<ForPlayer, Packet<? extends PacketListener>, Packet<? extends PacketListener>> tossPacket = (access, packet) -> null;
|
||||
Map<Class<? extends Packet<? extends PacketListener>>, BiFunction<ForPlayer, Packet<? extends PacketListener>, Packet<? extends PacketListener>>> 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.<ClientboundEntityEventPacket>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<Packet<? super ClientGamePacketListener>> processedPackets = new ArrayList<>();
|
||||
|
||||
for (Packet<? super ClientGamePacketListener> packet : bundlePacket.subPackets()) {
|
||||
Packet<? super ClientGamePacketListener> processedPacket = (Packet<? super ClientGamePacketListener>) processPacket(player, packet);
|
||||
Packet<? super ClientGamePacketListener> processedPacket = (Packet<? super ClientGamePacketListener>) 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<ForPlayer, Packet<? extends PacketListener>, Packet<? extends PacketListener>> processor = packetProcessors.get(packet.getClass());
|
||||
if (processor != null) {
|
||||
return processor.apply(access.get(), packet);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Packet<? extends PacketListener> 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<? extends PacketListener> 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<? extends PacketListener> processEntityPacket(Player player, int entityId, Packet<? extends PacketListener> packet) {
|
||||
if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, entityId)) {
|
||||
private Packet<? extends PacketListener> processEntityPacket(ForPlayer access, int entityId, Packet<? extends PacketListener> packet) {
|
||||
if (access.canAccessEntity(entityId)) {
|
||||
return packet;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private <TTargetPacket extends Packet<?>> BiFunction<Player, Packet<? extends PacketListener>, Packet<? extends PacketListener>> buildEntityPacketProcessor(ToIntFunction<TTargetPacket> entityIdExtractor) {
|
||||
return (p, rawPacket) -> {
|
||||
private <TTargetPacket extends Packet<?>> BiFunction<ForPlayer, Packet<? extends PacketListener>, Packet<? extends PacketListener>> buildEntityPacketProcessor(ToIntFunction<TTargetPacket> 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<? extends PacketListener> proccessPositionBasedPacket(Player player, int blockX, int blockY, int blockZ, Packet<? extends PacketListener> packet) {
|
||||
if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) {
|
||||
private Packet<? extends PacketListener> proccessPositionBasedPacket(ForPlayer access, int blockX, int blockY, int blockZ, Packet<? extends PacketListener> packet) {
|
||||
if (access.canAccessPosition(blockX, blockY, blockZ)) {
|
||||
return packet;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private <TTargetPacket extends Packet<?>> BiFunction<Player, Packet<?>, Packet<?>> buildPositionBasedPacketProcessor(Function<TTargetPacket, BlockPos> positionExtractor) {
|
||||
return (p, rawPacket) -> {
|
||||
private <TTargetPacket extends Packet<?>> BiFunction<ForPlayer, Packet<?>, Packet<?>> buildPositionBasedPacketProcessor(Function<TTargetPacket, BlockPos> 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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user