forked from SteamWar/SteamWar
Make TechHider hidden regions consistent for spectators
Spectators hid HiddenBlocks in the whole arena for block updates, but full chunk packets outside both extend regions were sent unfiltered, so the same blocks were hidden or visible depending on the packet type. Hidden regions are now a list: spectators hide both team extend regions, team members the opposite one. Block checks and the per-chunk bypass use the same regions, so everything outside the extend regions is visible and unfiltered.
This commit is contained in:
+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: ");
|
||||
|
||||
+35
-21
@@ -51,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;
|
||||
@@ -62,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) {
|
||||
@@ -97,14 +100,9 @@ public class TechHiderWrapper extends StateDependent implements Listener {
|
||||
|
||||
private AccessPrivilegeProvider newProvider(Set<BlockState> blockStatesToObfuscate, Set<Block> blocksToObfuscate, Set<BlockEntityType<?>> blockEntityTypeToObfuscate) {
|
||||
return new AccessPrivilegeProvider() {
|
||||
@Override
|
||||
public boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ) {
|
||||
return Fight.teams().stream().map(FightTeam::getExtendRegion).allMatch(region -> region.chunkOutside(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForPlayer forPlayer(Player p) {
|
||||
return new FightPlayerAccess(p, getHiddenRegion(p), blockStatesToObfuscate, blocksToObfuscate, blockEntityTypeToObfuscate);
|
||||
return new FightPlayerAccess(p, getHiddenRegions(p), blockStatesToObfuscate, blocksToObfuscate, blockEntityTypeToObfuscate);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,24 +117,37 @@ public class TechHiderWrapper extends StateDependent implements Listener {
|
||||
*/
|
||||
private class FightPlayerAccess implements ForPlayer {
|
||||
private final Player player;
|
||||
private final Region hiddenRegion;
|
||||
private final Region[] hiddenRegions;
|
||||
private final Set<BlockState> blockStatesToObfuscate;
|
||||
private final Set<Block> blocksToObfuscate;
|
||||
private final Set<BlockEntityType<?>> blockEntityTypeToObfuscate;
|
||||
|
||||
private FightPlayerAccess(Player player, Region hiddenRegion, Set<BlockState> blockStatesToObfuscate, Set<Block> blocksToObfuscate, Set<BlockEntityType<?>> blockEntityTypeToObfuscate) {
|
||||
private FightPlayerAccess(Player player, Region[] hiddenRegions, Set<BlockState> blockStatesToObfuscate, Set<Block> blocksToObfuscate, Set<BlockEntityType<?>> blockEntityTypeToObfuscate) {
|
||||
this.player = player;
|
||||
this.hiddenRegion = hiddenRegion;
|
||||
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);
|
||||
@@ -144,12 +155,12 @@ public class TechHiderWrapper extends StateDependent implements Listener {
|
||||
|
||||
@Override
|
||||
public boolean canAccessBlock(int blockX, int blockY, int blockZ, Block block) {
|
||||
return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blocksToObfuscate.contains(block);
|
||||
return !isHidden(blockX, blockY, blockZ) || !blocksToObfuscate.contains(block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAccessBlockState(int blockX, int blockY, int blockZ, BlockState blockState) {
|
||||
return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blockStatesToObfuscate.contains(blockState);
|
||||
return !isHidden(blockX, blockY, blockZ) || !blockStatesToObfuscate.contains(blockState);
|
||||
}
|
||||
|
||||
// TODO will require entity tracking on the netty thread to prevent future race conditions
|
||||
@@ -166,7 +177,7 @@ public class TechHiderWrapper extends StateDependent implements Listener {
|
||||
|
||||
@Override
|
||||
public boolean canAccessBlockEntity(int blockX, int blockY, int blockZ, BlockEntityType<?> type) {
|
||||
return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type);
|
||||
return !isHidden(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,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));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -210,16 +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;
|
||||
|
||||
// Single lookup: runs on netty threads, the player can leave the team at any time
|
||||
FightPlayer fightPlayer = Fight.getFightPlayer(player);
|
||||
if (fightPlayer == null) return Config.ArenaRegion;
|
||||
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 (fightPlayer.canEntern()) return Region.EMPTY;
|
||||
if (fightPlayer.canEntern()) return NO_REGIONS;
|
||||
|
||||
return Fight.getOpposite(fightPlayer.getTeam()).getExtendRegion();
|
||||
return new Region[]{Fight.getOpposite(fightPlayer.getTeam()).getExtendRegion()};
|
||||
}
|
||||
|
||||
private Stream<BlockState> getWaterloggedBlockStates() {
|
||||
|
||||
@@ -25,8 +25,6 @@ import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public interface AccessPrivilegeProvider {
|
||||
boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ);
|
||||
|
||||
/**
|
||||
* 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).
|
||||
@@ -41,6 +39,11 @@ public interface AccessPrivilegeProvider {
|
||||
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);
|
||||
|
||||
@@ -530,7 +530,7 @@ public class TechHider {
|
||||
}
|
||||
|
||||
private ClientboundLevelChunkWithLightPacket processChunkWithLight(ForPlayer access, ClientboundLevelChunkWithLightPacket packet) {
|
||||
if (privilegeProvider.isEveryonePrivilegedToAccessAllDataWithinChunk(packet.getX(), packet.getZ())) {
|
||||
if (access.canAccessAllDataWithinChunk(packet.getX(), packet.getZ())) {
|
||||
return packet;
|
||||
} else {
|
||||
return chunkHider.processLevelChunkWithLightPacket(access, packet);
|
||||
|
||||
Reference in New Issue
Block a user