forked from SteamWar/SteamWar
Migrate BauSystem to the new TechHider and remove the legacy TechHider
The new TechHider registers a permanent global filter, so BauSystem now uses a single BauTechHider that combines the hiding rules of the spectator techhider, /techhider and /xray per player. To support the different obfuscation blocks of these rules, ForPlayer now provides the obfuscation block per position. The ChunkHider now also checks single valued chunk sections, which were sent unfiltered before, and recalculates their block count if they change. Unhandled packet types are logged once; statistics, debug samples and ping responses are passed through. The legacy TechHider and its access widener entries are removed.
This commit is contained in:
@@ -69,6 +69,10 @@ public final class HullRegion {
|
||||
return contains(location.getBlockX(), location.getBlockY(), location.getBlockZ());
|
||||
}
|
||||
|
||||
public boolean chunkOutside(int chunkX, int chunkZ) {
|
||||
return chunkX < (minX >> 4) || chunkX > ((maxX - 1) >> 4) || chunkZ < (minZ >> 4) || chunkZ > ((maxZ - 1) >> 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the index of the block within a bitset of {@link #volume()} bits, only valid for contained blocks
|
||||
*/
|
||||
|
||||
@@ -53,5 +53,10 @@ public interface AccessPrivilegeProvider {
|
||||
boolean canAccessEntity(int entityId);
|
||||
|
||||
boolean canAccessBlockEntity(int blockX, int blockY, int blockZ, BlockEntityType<?> type);
|
||||
|
||||
/**
|
||||
* @return the block state sent instead of a block this player may not access at this position
|
||||
*/
|
||||
BlockState getObfuscation(int blockX, int blockY, int blockZ);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
@@ -45,12 +46,6 @@ public class ChunkHider {
|
||||
private final int BLOCKS_PER_SECTION = 4096;
|
||||
private final int BIOMES_PER_SECTION = 64;
|
||||
|
||||
private final int blockIdUsedForHiding;
|
||||
|
||||
public ChunkHider(Block blockUsedForObfuscation) {
|
||||
blockIdUsedForHiding = Block.BLOCK_STATE_REGISTRY.getId(blockUsedForObfuscation.defaultBlockState());
|
||||
}
|
||||
|
||||
private int getLongsRequiredToEncodeEntries(int bitsPerEntry, int entryCount) {
|
||||
int entriesPerLong = Long.SIZE / bitsPerEntry;
|
||||
int dataLengthAsLongCount = (entryCount + entriesPerLong - 1) / entriesPerLong;
|
||||
@@ -89,9 +84,22 @@ public class ChunkHider {
|
||||
if (bitsPerBlock == 0) {
|
||||
int sectionBlockId = ProtocolUtils.readVarInt(in);
|
||||
|
||||
out.writeShort(blockCount);
|
||||
out.writeByte(bitsPerBlock);
|
||||
ProtocolUtils.writeVarInt(out, sectionBlockId);
|
||||
int[] blockData = new int[BLOCKS_PER_SECTION];
|
||||
Arrays.fill(blockData, sectionBlockId);
|
||||
int[] obfuscatedBlockData = obfuscateBlockDataArray(access, chunkX, chunkZ, yOffset, blockData);
|
||||
|
||||
if (Arrays.equals(blockData, obfuscatedBlockData)) {
|
||||
out.writeShort(blockCount);
|
||||
out.writeByte(bitsPerBlock);
|
||||
ProtocolUtils.writeVarInt(out, sectionBlockId);
|
||||
} else {
|
||||
// The client skips sections without non-air blocks, so the count has to include the obfuscation blocks
|
||||
out.writeShort(countNonAirBlocks(obfuscatedBlockData));
|
||||
out.writeByte(15);
|
||||
for (long rawDataSegment : encodeDirectBlockDataArray(obfuscatedBlockData)) {
|
||||
out.writeLong(rawDataSegment);
|
||||
}
|
||||
}
|
||||
} else if (bitsPerBlock <= BIT_PER_BLOCK_INDIRECTION_LIMIT) {
|
||||
int palletLength = ProtocolUtils.readVarInt(in);
|
||||
int[] pallet = ProtocolUtils.readVarIntArray(in, palletLength);
|
||||
@@ -198,7 +206,7 @@ public class ChunkHider {
|
||||
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;
|
||||
obfuscatedData[blockDataIndex] = Block.getId(access.getObfuscation(worldX, worldY, worldZ));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,6 +215,16 @@ public class ChunkHider {
|
||||
return obfuscatedData;
|
||||
}
|
||||
|
||||
private int countNonAirBlocks(int[] blockDataArray) {
|
||||
int count = 0;
|
||||
for (int blockId : blockDataArray) {
|
||||
if (!Block.BLOCK_STATE_REGISTRY.byId(blockId).isAir()) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private int[] resolveDirectBlockDataArray(long[] rawDataArray, byte bitsPerBlock) {
|
||||
SimpleBitStorage data = new SimpleBitStorage(bitsPerBlock, BLOCKS_PER_SECTION, rawDataArray);
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ public final class ObfuscationConfig {
|
||||
}
|
||||
|
||||
private final Block obfuscateWith;
|
||||
private final BlockState obfuscationState;
|
||||
private final Set<Block> blocks;
|
||||
private final Set<BlockState> blockStates;
|
||||
private final Set<BlockEntityType<?>> blockEntityTypes;
|
||||
@@ -59,6 +60,7 @@ public final class ObfuscationConfig {
|
||||
*/
|
||||
public ObfuscationConfig(Material obfuscateWith, Set<Material> hiddenBlocks, Set<String> hiddenBlockEntities) {
|
||||
this.obfuscateWith = CraftMagicNumbers.getBlock(obfuscateWith);
|
||||
this.obfuscationState = this.obfuscateWith.defaultBlockState();
|
||||
|
||||
this.blocks = hiddenBlocks.stream()
|
||||
.map(CraftMagicNumbers::getBlock)
|
||||
|
||||
@@ -36,16 +36,20 @@ import net.minecraft.network.protocol.configuration.ClientboundUpdateEnabledFeat
|
||||
import net.minecraft.network.protocol.cookie.ClientboundCookieRequestPacket;
|
||||
import net.minecraft.network.protocol.game.*;
|
||||
import net.minecraft.network.protocol.login.*;
|
||||
import net.minecraft.network.protocol.ping.ClientboundPongResponsePacket;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.ToIntFunction;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* The TechHider follows the default-deny security principle,
|
||||
@@ -56,16 +60,14 @@ public class TechHider {
|
||||
private final Set<Class<?>> bypassingPackets;
|
||||
private final Map<Class<? extends Packet<? extends PacketListener>>, BiFunction<ForPlayer, Packet<? extends PacketListener>, Packet<? extends PacketListener>>> packetProcessors;
|
||||
|
||||
private final BlockState blockStateUsedForObfuscation;
|
||||
private final Set<Class<?>> loggedUnhandledPackets = ConcurrentHashMap.newKeySet();
|
||||
private final ChunkHider chunkHider;
|
||||
|
||||
private final AccessPrivilegeProvider privilegeProvider;
|
||||
|
||||
// TODO handle packet bundle
|
||||
public TechHider(Block blockUsedForObfuscation, AccessPrivilegeProvider privilegeProvider) {
|
||||
this.blockStateUsedForObfuscation = blockUsedForObfuscation.defaultBlockState();
|
||||
|
||||
this.chunkHider = new ChunkHider(blockUsedForObfuscation);
|
||||
public TechHider(AccessPrivilegeProvider privilegeProvider) {
|
||||
this.chunkHider = new ChunkHider();
|
||||
this.privilegeProvider = privilegeProvider;
|
||||
|
||||
this.bypassingPackets = new HashSet<>(List.of(
|
||||
@@ -176,6 +178,9 @@ public class TechHider {
|
||||
ClientboundBlockChangedAckPacket.class, // 7.1.5 Acknowledge Block Change
|
||||
ClientboundChunkBatchFinishedPacket.class, // 7.1.12 Chunk Batch Finished (Delimiter)
|
||||
ClientboundChunkBatchStartPacket.class, // 7.1.13 Chunk Batch Start (Delimiter)
|
||||
ClientboundAwardStatsPacket.class, // 7.1.4 Award Statistics: the player's own statistics
|
||||
ClientboundDebugSamplePacket.class, // 7.1.28 Debug Sample: server tick timings
|
||||
ClientboundPongResponsePacket.class, // Ping Response: answer to the client's own ping
|
||||
ClientboundChunksBiomesPacket.class, // 7.1.14 Chunk Biomes
|
||||
ClientboundContainerClosePacket.class, // 7.1.18 Close Container
|
||||
ClientboundSetChunkCacheCenterPacket.class, // 7.1.93 Set Center Chunk
|
||||
@@ -359,6 +364,9 @@ public class TechHider {
|
||||
if (processor != null) {
|
||||
return processor.apply(access.get(), packet);
|
||||
} else {
|
||||
if (loggedUnhandledPackets.add(packet.getClass())) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "[TechHider] Dropping unhandled packet type " + packet.getClass().getName());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -438,7 +446,7 @@ public class TechHider {
|
||||
if (access.canAccessPosition(blockX, blockY, blockZ) && access.canAccessBlock(blockX, blockY, blockZ, block) && access.canAccessBlockState(blockX, blockY, blockZ, blockState)) {
|
||||
return packet;
|
||||
} else if (access.canAccessPosition(blockX, blockY, blockZ)) {
|
||||
return new ClientboundBlockUpdatePacket(blockPos, blockStateUsedForObfuscation);
|
||||
return new ClientboundBlockUpdatePacket(blockPos, access.getObfuscation(blockX, blockY, blockZ));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@@ -491,7 +499,7 @@ public class TechHider {
|
||||
filteredStates.add(state);
|
||||
} else if (access.canAccessPosition(worldX, worldY, worldZ)) {
|
||||
filteredPos.add(posShort);
|
||||
filteredStates.add(blockStateUsedForObfuscation);
|
||||
filteredStates.add(access.getObfuscation(worldX, worldY, worldZ));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user