forked from SteamWar/SteamWar
Move TechHider into new HiderCore module
HiderCore holds the TechHider, ChunkHider and AccessPrivilegeProvider so that FightSystem and BauSystem can both shade it. BlockIds and ProtocolUtils stay in SpigotCore (used by AntiNocom and RFallingBlockEntity) and move to de.steamwar.core to avoid a split package. The access widener entries the TechHider needs are duplicated into hidercore.accesswidener; the legacy TechHider still needs its own copies in SpigotCore.
This commit is contained in:
+1
-1
@@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.techhider;
|
||||
package de.steamwar.core;
|
||||
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.techhider;
|
||||
package de.steamwar.core;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
@@ -21,9 +21,9 @@ package de.steamwar.core.events;
|
||||
|
||||
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||
import de.steamwar.core.Core;
|
||||
import de.steamwar.core.ProtocolUtils;
|
||||
import de.steamwar.linkage.Linked;
|
||||
import de.steamwar.sql.SWException;
|
||||
import de.steamwar.techhider.ProtocolUtils;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
|
||||
import net.minecraft.network.protocol.game.ServerboundUseItemOnPacket;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
package de.steamwar.entity;
|
||||
|
||||
import de.steamwar.techhider.BlockIds;
|
||||
import de.steamwar.core.BlockIds;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.Location;
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.techhider;
|
||||
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public interface AccessPrivilegeProvider {
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -1,290 +0,0 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2025 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||
import net.minecraft.util.SimpleBitStorage;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
public class ChunkHider {
|
||||
private static final UnaryOperator<ClientboundLevelChunkWithLightPacket> chunkPacketShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkWithLightPacket::new);
|
||||
private static final UnaryOperator<ClientboundLevelChunkPacketData> chunkDataShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class, ClientboundLevelChunkPacketData::new);
|
||||
|
||||
private final int SECTION_SPAN_SIZE = 16;
|
||||
private final byte BIT_PER_BLOCK_INDIRECTION_LIMIT = 8;
|
||||
private final byte BIT_PER_BIOME_INDIRECTION_LIMIT = 3;
|
||||
|
||||
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;
|
||||
|
||||
return dataLengthAsLongCount;
|
||||
}
|
||||
|
||||
private long[] readSectionDataFromBuffer(ByteBuf dataSource, short bitsPerEntry, int entryCount) {
|
||||
int dataLengthAsLongCount = getLongsRequiredToEncodeEntries(bitsPerEntry, entryCount);
|
||||
long[] dataArray = new long[dataLengthAsLongCount];
|
||||
|
||||
for (int i = 0; i < dataLengthAsLongCount; i++) {
|
||||
dataArray[i] = dataSource.readLong();
|
||||
}
|
||||
|
||||
return dataArray;
|
||||
}
|
||||
|
||||
public ClientboundLevelChunkWithLightPacket processLevelChunkWithLightPacket(ForPlayer access, ClientboundLevelChunkWithLightPacket packet) {
|
||||
Player player = access.getPlayer();
|
||||
int chunkX = packet.getX();
|
||||
int chunkZ = packet.getZ();
|
||||
ClientboundLevelChunkPacketData chunkData = packet.getChunkData();
|
||||
|
||||
ByteBuf in = Unpooled.wrappedBuffer(chunkData.getReadBuffer());
|
||||
ByteBuf out = Unpooled.buffer(in.readableBytes() + 64);
|
||||
|
||||
int worldMinHeight = player.getWorld().getMinHeight();
|
||||
int worldMaxHeight = player.getWorld().getMaxHeight();
|
||||
|
||||
for (int yOffset = worldMinHeight; yOffset < worldMaxHeight; yOffset += SECTION_SPAN_SIZE) {
|
||||
short blockCount = in.readShort();
|
||||
|
||||
byte bitsPerBlock = in.readByte();
|
||||
|
||||
if (bitsPerBlock == 0) {
|
||||
int sectionBlockId = ProtocolUtils.readVarInt(in);
|
||||
|
||||
out.writeShort(blockCount);
|
||||
out.writeByte(bitsPerBlock);
|
||||
ProtocolUtils.writeVarInt(out, sectionBlockId);
|
||||
} else if (bitsPerBlock <= BIT_PER_BLOCK_INDIRECTION_LIMIT) {
|
||||
int palletLength = ProtocolUtils.readVarInt(in);
|
||||
int[] pallet = ProtocolUtils.readVarIntArray(in, palletLength);
|
||||
|
||||
long[] rawData = readSectionDataFromBuffer(in, bitsPerBlock, BLOCKS_PER_SECTION);
|
||||
SimpleBitStorage data = new SimpleBitStorage(bitsPerBlock, BLOCKS_PER_SECTION, rawData);
|
||||
|
||||
int[] resolvedData = new int[BLOCKS_PER_SECTION];
|
||||
for (int i = 0; i < BLOCKS_PER_SECTION; i++) {
|
||||
int palletReference = data.get(i);
|
||||
resolvedData[i] = pallet[palletReference];
|
||||
}
|
||||
|
||||
int[] obfuscatedBlockData = obfuscateBlockDataArray(access, chunkX, chunkZ, yOffset, resolvedData);
|
||||
|
||||
long[] reEncodedData = encodeDirectBlockDataArray(obfuscatedBlockData);
|
||||
|
||||
|
||||
out.writeShort(blockCount);
|
||||
out.writeByte(15);
|
||||
for (long rawDataSegment : reEncodedData) {
|
||||
out.writeLong(rawDataSegment);
|
||||
}
|
||||
/*
|
||||
Int2IntMap blockIdToPalletIndex = new Int2IntOpenHashMap();
|
||||
IntArrayList newPallet = new IntArrayList();
|
||||
|
||||
for(int blockId : obfuscatedData) {
|
||||
if(!blockIdToPalletIndex.containsKey(blockId)) {
|
||||
newPallet.add(blockId);
|
||||
blockIdToPalletIndex.put(blockId, newPallet.size());
|
||||
}
|
||||
}
|
||||
|
||||
byte newBitsPerBlock = (byte) getUnsignedBitLength(newPallet.size());
|
||||
int[] newPalletRaw = newPallet.toArray(new int[newPallet.size()]);
|
||||
|
||||
SimpleBitStorage reEncodedData = new SimpleBitStorage(bitsPerBlock, BLOCKS_PER_SECTION, new long[rawData.length]);
|
||||
for(int i = 0; i < obfuscatedData.length; i++) {
|
||||
int blockId = obfuscatedData[i];
|
||||
int palletReference = blockIdToPalletIndex.get(blockId);
|
||||
|
||||
reEncodedData.set(i, palletReference);
|
||||
}
|
||||
|
||||
out.writeShort(blockCount);
|
||||
out.writeByte(newBitsPerBlock);
|
||||
ProtocolUtils.writeVarInt(out, palletLength);
|
||||
ProtocolUtils.writeVarIntArray(out, newPalletRaw);
|
||||
for(long rawDataSegment : reEncodedData.getRaw()) {
|
||||
out.writeLong(rawDataSegment);
|
||||
}*/
|
||||
|
||||
} else {
|
||||
long[] rawData = readSectionDataFromBuffer(in, bitsPerBlock, BLOCKS_PER_SECTION);
|
||||
|
||||
int[] blockData = resolveDirectBlockDataArray(rawData, bitsPerBlock);
|
||||
|
||||
int[] obfuscatedBlockData = obfuscateBlockDataArray(access, chunkX, chunkZ, yOffset, blockData);
|
||||
|
||||
long[] reEncodedData = encodeDirectBlockDataArray(obfuscatedBlockData);
|
||||
|
||||
|
||||
out.writeShort(blockCount);
|
||||
out.writeByte(15);
|
||||
for (long rawDataSegment : reEncodedData) {
|
||||
out.writeLong(rawDataSegment);
|
||||
}
|
||||
}
|
||||
|
||||
copyOverSectionBiomeData(in, out);
|
||||
}
|
||||
|
||||
if (in.readableBytes() != 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] data = new byte[out.readableBytes()];
|
||||
out.readBytes(data);
|
||||
|
||||
List<ClientboundLevelChunkPacketData.BlockEntityInfo> blockEntities = chunkData.blockEntitiesData;
|
||||
List<ClientboundLevelChunkPacketData.BlockEntityInfo> filteredBlockEntities = filterBlockEntities(access, blockEntities, chunkX, chunkZ);
|
||||
|
||||
return buildNewChunkPacket(packet, data, filteredBlockEntities);
|
||||
|
||||
}
|
||||
|
||||
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++) {
|
||||
for (int sectionX = 0; sectionX < SECTION_SPAN_SIZE; sectionX++) {
|
||||
int blockDataIndex = (((sectionY * SECTION_SPAN_SIZE) + sectionZ) * SECTION_SPAN_SIZE) + sectionX;
|
||||
|
||||
int worldX = sectionX + (SECTION_SPAN_SIZE * chunkX);
|
||||
int worldY = sectionY + yOffset;
|
||||
int worldZ = sectionZ + (SECTION_SPAN_SIZE * chunkZ);
|
||||
|
||||
int blockId = blockDataArray[blockDataIndex];
|
||||
BlockState blockState = Block.BLOCK_STATE_REGISTRY.byId(blockId);
|
||||
Block block = blockState.getBlock();
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return obfuscatedData;
|
||||
}
|
||||
|
||||
private int[] resolveDirectBlockDataArray(long[] rawDataArray, byte bitsPerBlock) {
|
||||
SimpleBitStorage data = new SimpleBitStorage(bitsPerBlock, BLOCKS_PER_SECTION, rawDataArray);
|
||||
|
||||
int[] resolvedData = new int[BLOCKS_PER_SECTION];
|
||||
for (int i = 0; i < BLOCKS_PER_SECTION; i++) {
|
||||
resolvedData[i] = data.get(i);
|
||||
}
|
||||
|
||||
return resolvedData;
|
||||
}
|
||||
|
||||
private long[] encodeDirectBlockDataArray(int[] blockDataArray) {
|
||||
int longsRequiredToEncodeData = getLongsRequiredToEncodeEntries(15, blockDataArray.length);
|
||||
|
||||
SimpleBitStorage reEncodedData = new SimpleBitStorage(15, BLOCKS_PER_SECTION, new long[longsRequiredToEncodeData]);
|
||||
for (int i = 0; i < blockDataArray.length; i++) {
|
||||
int blockId = blockDataArray[i];
|
||||
|
||||
reEncodedData.set(i, blockId);
|
||||
}
|
||||
|
||||
return reEncodedData.getRaw();
|
||||
}
|
||||
|
||||
private ClientboundLevelChunkWithLightPacket buildNewChunkPacket(ClientboundLevelChunkWithLightPacket originalPacket, byte[] newBlockDataBuffer, List<ClientboundLevelChunkPacketData.BlockEntityInfo> newBlockEntities) {
|
||||
ClientboundLevelChunkWithLightPacket clonedPacket = chunkPacketShallowCloner.apply(originalPacket);
|
||||
ClientboundLevelChunkPacketData clonedPacketChunkData = chunkDataShallowCloner.apply(originalPacket.getChunkData());
|
||||
|
||||
clonedPacketChunkData.buffer = newBlockDataBuffer;
|
||||
clonedPacketChunkData.blockEntitiesData = newBlockEntities;
|
||||
clonedPacket.chunkData = clonedPacketChunkData;
|
||||
|
||||
return clonedPacket;
|
||||
}
|
||||
|
||||
private List<ClientboundLevelChunkPacketData.BlockEntityInfo> filterBlockEntities(ForPlayer access, List<ClientboundLevelChunkPacketData.BlockEntityInfo> blockEntities, int chunkX, int chunkZ) {
|
||||
int fourBitBitmask = 0b0000_1111;
|
||||
|
||||
return blockEntities.stream()
|
||||
.filter((blockEntityInfo) -> {
|
||||
BlockEntityType<?> type = blockEntityInfo.type;
|
||||
int packedXZ = blockEntityInfo.packedXZ;
|
||||
|
||||
int localX = (packedXZ >> 4) & fourBitBitmask;
|
||||
int localZ = packedXZ & fourBitBitmask;
|
||||
|
||||
int worldX = (chunkX * SECTION_SPAN_SIZE) + localX;
|
||||
int worldZ = (chunkZ * SECTION_SPAN_SIZE) + localZ;
|
||||
|
||||
int worldY = blockEntityInfo.y;
|
||||
|
||||
return access.canAccessPosition(worldX, worldY, worldZ) && access.canAccessBlockEntity(worldX, worldY, worldZ, type);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
private void copyOverSectionBiomeData(ByteBuf oldData, ByteBuf newData) {
|
||||
short bitsPerBiome = oldData.readByte();
|
||||
newData.writeByte(bitsPerBiome);
|
||||
if (bitsPerBiome == 0) {
|
||||
int sectionBiomeId = ProtocolUtils.readVarInt(oldData);
|
||||
ProtocolUtils.writeVarInt(newData, sectionBiomeId);
|
||||
} else if (bitsPerBiome <= BIT_PER_BIOME_INDIRECTION_LIMIT) {
|
||||
int palletLength = ProtocolUtils.readVarInt(oldData);
|
||||
ProtocolUtils.writeVarInt(newData, palletLength);
|
||||
|
||||
for (int i = 0; i < palletLength; i++) {
|
||||
int palletEntry = ProtocolUtils.readVarInt(oldData);
|
||||
ProtocolUtils.writeVarInt(newData, palletEntry);
|
||||
}
|
||||
|
||||
long[] rawData = readSectionDataFromBuffer(oldData, bitsPerBiome, BIOMES_PER_SECTION);
|
||||
for (long rawDataSegment : rawData) {
|
||||
newData.writeLong(rawDataSegment);
|
||||
}
|
||||
} else {
|
||||
long[] rawData = readSectionDataFromBuffer(oldData, bitsPerBiome, BIOMES_PER_SECTION);
|
||||
for (long rawDataSegment : rawData) {
|
||||
newData.writeLong(rawDataSegment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,551 +0,0 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.techhider;
|
||||
|
||||
import de.steamwar.techhider.AccessPrivilegeProvider.ForPlayer;
|
||||
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||
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;
|
||||
import net.minecraft.network.protocol.configuration.ClientboundRegistryDataPacket;
|
||||
import net.minecraft.network.protocol.configuration.ClientboundSelectKnownPacks;
|
||||
import net.minecraft.network.protocol.configuration.ClientboundUpdateEnabledFeaturesPacket;
|
||||
import net.minecraft.network.protocol.cookie.ClientboundCookieRequestPacket;
|
||||
import net.minecraft.network.protocol.game.*;
|
||||
import net.minecraft.network.protocol.login.*;
|
||||
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.entity.Player;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.ToIntFunction;
|
||||
|
||||
/**
|
||||
* The TechHider follows the default-deny security principle,
|
||||
* any packet must be explicitly whitelisted or processed in a
|
||||
* way that is also compliant with the principle of default-deny.
|
||||
*/
|
||||
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 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);
|
||||
this.privilegeProvider = privilegeProvider;
|
||||
|
||||
this.bypassingPackets = new HashSet<>(List.of(
|
||||
// --- 5.1.x Login Protocol ---
|
||||
ClientboundLoginDisconnectPacket.class, // 5.1.1 Disconnect
|
||||
ClientboundHelloPacket.class, // 5.1.2 Encryption Request
|
||||
ClientboundLoginFinishedPacket.class, // 5.1.3 Login Success
|
||||
ClientboundLoginCompressionPacket.class, // 5.1.4 Set Compression
|
||||
ClientboundCustomQueryPacket.class, // 5.1.5 Login Plugin Request
|
||||
ClientboundCookieRequestPacket.class, // 5.1.6 Cookie Request
|
||||
|
||||
// --- 6.1.x Configuration Protocol ---
|
||||
ClientboundSelectKnownPacks.class, ClientboundCustomPayloadPacket.class, // 6.1.2 Clientbound Plugin Message
|
||||
ClientboundFinishConfigurationPacket.class, // 6.1.4 Finish Configuration
|
||||
ClientboundKeepAlivePacket.class, // 6.1.5 Clientbound Keep Alive
|
||||
ClientboundPingPacket.class, // 6.1.6 Ping
|
||||
ClientboundRegistryDataPacket.class, // 6.1.8 Registry Data
|
||||
ClientboundResourcePackPopPacket.class, // 6.1.9 Remove Resource Pack
|
||||
ClientboundResourcePackPushPacket.class, // 6.1.10 Add Resource Pack
|
||||
ClientboundStoreCookiePacket.class, // 6.1.11 Store Cookie
|
||||
ClientboundTransferPacket.class, // 6.1.12 Transfer
|
||||
ClientboundUpdateEnabledFeaturesPacket.class, // 6.1.13 Feature Flags
|
||||
ClientboundCustomReportDetailsPacket.class, // 6.1.16 Custom Report Details
|
||||
ClientboundServerLinksPacket.class, // 6.1.17 Server Links
|
||||
ClientboundSystemChatPacket.class, // 6.1.18/19 Dialogs are often handled via System Chat or Custom
|
||||
// Payloads
|
||||
ClientboundServerDataPacket.class, // 6.1.20 Code of Conduct is usually in Server Data
|
||||
|
||||
// --- 7.1.x Play Protocol ---
|
||||
|
||||
// --- Safe packets; Not involved with a critical subdomain ---
|
||||
ClientboundBundleDelimiterPacket.class, // 7.1.1 Bundle Delimiter
|
||||
ClientboundBossEventPacket.class, // 7.1.10 Boss Bar
|
||||
ClientboundChangeDifficultyPacket.class, // 7.1.11 Change Difficulty
|
||||
ClientboundClearTitlesPacket.class, // 7.1.15 Clear Titles
|
||||
ClientboundCommandSuggestionsPacket.class, // 7.1.16 Command Suggestions Response
|
||||
ClientboundCommandsPacket.class, // 7.1.17 Commands
|
||||
ClientboundCookieRequestPacket.class, // 7.1.22 Cookie Request (play)
|
||||
ClientboundCooldownPacket.class, // 7.1.23 Set Cooldown
|
||||
ClientboundCustomChatCompletionsPacket.class, // 7.1.24 Chat Suggestions
|
||||
ClientboundDeleteChatPacket.class, // 7.1.32 Delete Message
|
||||
ClientboundDisconnectPacket.class, // 7.1.33 Disconnect (play)
|
||||
ClientboundDisguisedChatPacket.class, // 7.1.34 Disguised Chat Message
|
||||
ClientboundGameEventPacket.class, // 7.1.39 Game Event (like ElderGuardian effect, rain, thunder, etc.)
|
||||
ClientboundHorseScreenOpenPacket.class, // 7.1.41 Open Horse Screen: entity id based container.
|
||||
ClientboundInitializeBorderPacket.class, // 7.1.43 Initialize World Border
|
||||
ClientboundKeepAlivePacket.class, // 7.1.44 Clientbound Keep Alive (play)
|
||||
ClientboundLoginPacket.class, // 7.1.49 Login (play)
|
||||
ClientboundMerchantOffersPacket.class, // 7.1.51 Merchant Offers
|
||||
ClientboundOpenBookPacket.class, // 7.1.57 Open Book
|
||||
ClientboundOpenScreenPacket.class, // 7.1.58 Open Screen
|
||||
ClientboundPingPacket.class, // 7.1.60 Ping (play)
|
||||
ClientboundPlaceGhostRecipePacket.class, // 7.1.62 Place Ghost Recipe
|
||||
ClientboundPlayerAbilitiesPacket.class, // 7.1.63 Player Abilities (clientbound)
|
||||
ClientboundPlayerChatPacket.class, // 7.1.64 Player Chat Message
|
||||
ClientboundPlayerCombatEndPacket.class, // 7.1.65 End Combat
|
||||
ClientboundPlayerCombatEnterPacket.class, // 7.1.66 Enter Combat
|
||||
ClientboundPlayerLookAtPacket.class, // 7.1.70 Look At (Player owning channel)
|
||||
ClientboundPlayerPositionPacket.class, // 7.1.71 Synchronize Player Position (Player owning the channel)
|
||||
ClientboundPlayerRotationPacket.class, // 7.1.72 Player Rotation (Player owning the channel)
|
||||
ClientboundRecipeBookAddPacket.class, // 7.1.73 Recipe Book Add
|
||||
ClientboundRecipeBookRemovePacket.class, // 7.1.74 Recipe Book Remove
|
||||
ClientboundRecipeBookSettingsPacket.class, // 7.1.75 Recipe Book Settings
|
||||
ClientboundResetScorePacket.class, // 7.1.78 Reset Score
|
||||
ClientboundResourcePackPopPacket.class, // 7.1.79 Remove Resource Pack (play)
|
||||
ClientboundResourcePackPushPacket.class, // 7.1.80 Add Resource Pack (play)
|
||||
ClientboundRespawnPacket.class, // 7.1.81 Respawn
|
||||
ClientboundSelectAdvancementsTabPacket.class, // 7.1.84 Select Advancements Tab
|
||||
ClientboundServerDataPacket.class, // 7.1.85 Server Data
|
||||
ClientboundSetActionBarTextPacket.class, // 7.1.86 Set Action Bar Text
|
||||
ClientboundSetBorderCenterPacket.class, // 7.1.87 Set Border Center
|
||||
ClientboundSetBorderLerpSizePacket.class, // 7.1.88 Set Border Lerp Size
|
||||
ClientboundSetBorderSizePacket.class, // 7.1.89 Set Border Size
|
||||
ClientboundSetBorderWarningDelayPacket.class, // 7.1.90 Set Border Warning Delay
|
||||
ClientboundSetBorderWarningDistancePacket.class, // 7.1.91 Set Border Warning Distance
|
||||
ClientboundSetCameraPacket.class, // 7.1.92 Set Camera
|
||||
ClientboundSetChunkCacheRadiusPacket.class, // 7.1.94 Set Render Distance
|
||||
ClientboundSetCursorItemPacket.class, // 7.1.95 Set Cursor Item
|
||||
ClientboundSetDefaultSpawnPositionPacket.class, // 7.1.96 Set Default Spawn Position
|
||||
ClientboundSetDisplayObjectivePacket.class, // 7.1.97 Display Objective
|
||||
ClientboundSetExperiencePacket.class, // 7.1.102 Set Experience
|
||||
ClientboundSetHealthPacket.class, // 7.1.103 Set Health, Saturation and food (Player owning the channel)
|
||||
ClientboundSetHeldSlotPacket.class, // 7.1.104 Set Held Item (Player owning the channel)
|
||||
ClientboundSetObjectivePacket.class, // 7.1.105 Update Objectives
|
||||
ClientboundSetPlayerInventoryPacket.class, // 7.1.107 Set Player Inventory Slot (Player owning the channel)
|
||||
ClientboundSetPlayerTeamPacket.class, // 7.1.108 Update Teams
|
||||
ClientboundSetScorePacket.class, // 7.1.109 Update Score
|
||||
ClientboundSetSimulationDistancePacket.class, // 7.1.110 Set Simulation Distance
|
||||
ClientboundSetSubtitleTextPacket.class, // 7.1.111 Set Subtitle Text
|
||||
ClientboundSetTimePacket.class, // 7.1.112 Update Time
|
||||
ClientboundSetTitleTextPacket.class, // 7.1.113 Set Title Text
|
||||
ClientboundSetTitlesAnimationPacket.class, // 7.1.114 Set Title Animation Times
|
||||
ClientboundStartConfigurationPacket.class, // 7.1.117 Start Configuration
|
||||
ClientboundStoreCookiePacket.class, // 7.1.119 Store Cookie (play)
|
||||
ClientboundSystemChatPacket.class, // 7.1.120 System Chat Message
|
||||
ClientboundTabListPacket.class, // 7.1.121 Set Tab List Header And Footer
|
||||
ClientboundTickingStatePacket.class, // 7.1.126 Set Ticking State
|
||||
ClientboundTickingStepPacket.class, // 7.1.127 Step Tick
|
||||
ClientboundTransferPacket.class, // 7.1.128 Transfer (play)
|
||||
ClientboundUpdateAdvancementsPacket.class, // 7.1.129 Update Advancements
|
||||
ClientboundUpdateRecipesPacket.class, // 7.1.132 Update Recipes
|
||||
ClientboundCustomReportDetailsPacket.class, // 7.1.135 Custom Report Details
|
||||
ClientboundServerLinksPacket.class, // 7.1.136 Server Links
|
||||
ClientboundClearDialogPacket.class, // 7.1.138 Clear Dialog (play)
|
||||
ClientboundShowDialogPacket.class, // 7.1.139 Show Dialog (play)
|
||||
|
||||
// --- Mostly safe packets; Are involved with critical subdomain, but in not critical way ---
|
||||
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)
|
||||
ClientboundChunksBiomesPacket.class, // 7.1.14 Chunk Biomes
|
||||
ClientboundContainerClosePacket.class, // 7.1.18 Close Container
|
||||
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)
|
||||
ClientboundStopSoundPacket.class, // 7.1.118 Stop Sound: sound state side channel
|
||||
|
||||
ClientboundLightUpdatePacket.class, // 7.1.48 Update Light
|
||||
|
||||
ClientboundContainerSetContentPacket.class, // 7.1.19 Set Container Content
|
||||
ClientboundContainerSetDataPacket.class, // 7.1.20 Set Container Property
|
||||
ClientboundContainerSetSlotPacket.class // 7.1.21 Set Container Slot
|
||||
));
|
||||
|
||||
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, (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.<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.
|
||||
processors.put(ClientboundHurtAnimationPacket.class, this.buildEntityPacketProcessor(ClientboundHurtAnimationPacket::id));
|
||||
// 7.1.100 Set Entity Velocity: entity id and movement.
|
||||
processors.put(ClientboundSetEntityMotionPacket.class, this.buildEntityPacketProcessor(ClientboundSetEntityMotionPacket::getId));
|
||||
// 7.1.101 Set Equipment: entity equipment can reveal wargear.
|
||||
processors.put(ClientboundSetEquipmentPacket.class, this.buildEntityPacketProcessor(ClientboundSetEquipmentPacket::getEntity));
|
||||
// 7.1.106 Set Passengers: entity relationship signal.
|
||||
processors.put(ClientboundSetPassengersPacket.class, this.buildEntityPacketProcessor(ClientboundSetPassengersPacket::getVehicle));
|
||||
// 7.1.130 Update Attributes: entity id and attribute state.
|
||||
processors.put(ClientboundUpdateAttributesPacket.class, this.buildEntityPacketProcessor(ClientboundUpdateAttributesPacket::getEntityId));
|
||||
// 7.1.131 Entity Effect: entity id and effect state.
|
||||
processors.put(ClientboundUpdateMobEffectPacket.class, this.buildEntityPacketProcessor(ClientboundUpdateMobEffectPacket::getEntityId));
|
||||
// 7.1.77 Remove Entity Effect: entity id and effect state.
|
||||
processors.put(ClientboundRemoveMobEffectPacket.class, this.buildEntityPacketProcessor(ClientboundRemoveMobEffectPacket::entityId));
|
||||
// 7.1.98 Set Entity Metadata: entity state can reveal blocks/items/displays.
|
||||
processors.put(ClientboundSetEntityDataPacket.class, this.buildEntityPacketProcessor(ClientboundSetEntityDataPacket::id));
|
||||
// 7.1.26 Damage Event: entity ids and damage source location can leak hide activity.
|
||||
processors.put(ClientboundDamageEventPacket.class, this.buildEntityPacketProcessor(ClientboundDamageEventPacket::entityId));
|
||||
// 7.1.54 Move Minecart Along Track: entity path and position signal.
|
||||
processors.put(ClientboundMoveMinecartPacket.class, this.buildEntityPacketProcessor(ClientboundMoveMinecartPacket::entityId));
|
||||
// 7.1.124 Synchronize Vehicle Position: entity/vehicle position.
|
||||
processors.put(ClientboundEntityPositionSyncPacket.class, this.buildEntityPacketProcessor(ClientboundEntityPositionSyncPacket::id));
|
||||
// 7.1.134 Projectile Power: projectile/entity signal.
|
||||
processors.put(ClientboundProjectilePowerPacket.class, this.buildEntityPacketProcessor(ClientboundProjectilePowerPacket::getId));
|
||||
// 7.1.123 Pickup Item: item/entity ids and pickup activity.
|
||||
processors.put(ClientboundTakeItemEntityPacket.class, this.buildEntityPacketProcessor(ClientboundTakeItemEntityPacket::getItemId));
|
||||
// 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, (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, (access, packet) -> this.processRotateHeadPacket(access, (ClientboundRotateHeadPacket) packet));
|
||||
// 7.1.99 Link Entities: entity relationship signal.
|
||||
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, (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, (access, packet) -> proccessBlockDestructionPacket(access, (ClientboundBlockDestructionPacket) packet));
|
||||
// 7.1.8 Block Action: currently filtered by block position.
|
||||
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, (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, (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, (access, packet) -> processSectionUpdate(access, (ClientboundSectionBlocksUpdatePacket) packet));
|
||||
|
||||
// --- Particle packets ---
|
||||
// 7.1.47 Particle: particle type and position can reveal hidden machinery.
|
||||
processors.put(ClientboundLevelParticlesPacket.class, (access, packet) -> processLevelParticlesPacket(access, (ClientboundLevelParticlesPacket) packet));
|
||||
|
||||
// --- Sound packets ---
|
||||
// 7.1.115 Entity Sound Effect: entity id and sound.
|
||||
processors.put(ClientboundSoundEntityPacket.class, this.buildEntityPacketProcessor(ClientboundSoundEntityPacket::getId));
|
||||
// 7.1.116 Sound Effect: sound type and position.
|
||||
processors.put(ClientboundSoundPacket.class, this.<ClientboundSoundPacket>buildPositionBasedPacketProcessor(packet -> {
|
||||
return new BlockPos.MutableBlockPos(packet.getX(), packet.getY(), packet.getZ()).immutable();
|
||||
}));
|
||||
|
||||
// --- Others ---
|
||||
// 7.1.137 Waypoint: world/entity tracking signal.
|
||||
processors.put(ClientboundTrackedWaypointPacket.class, tossPacket);
|
||||
// 7.1.50 Map Data: map pixels/icons can reveal player/world state.
|
||||
processors.put(ClientboundMapItemDataPacket.class, tossPacket);
|
||||
// 7.1.46 World Event: block position/event id can leak activity.
|
||||
processors.put(ClientboundLevelEventPacket.class, buildPositionBasedPacketProcessor(ClientboundLevelEventPacket::getPos));
|
||||
// 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, (access, rawPacket) -> {
|
||||
ClientboundExplodePacket packet = (ClientboundExplodePacket) rawPacket;
|
||||
Vec3 pos = packet.center();
|
||||
int blockX = Mth.floor(pos.x);
|
||||
int blockY = Mth.floor(pos.y);
|
||||
int blockZ = Mth.floor(pos.z);
|
||||
|
||||
|
||||
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(access, packet);
|
||||
|
||||
if (processedPacket != null) {
|
||||
processedPackets.add(processedPacket);
|
||||
}
|
||||
}
|
||||
|
||||
return new ClientboundBundlePacket(processedPackets);
|
||||
} else {
|
||||
return processPacket(access, rawPacket);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
TinyProtocol.instance.addFilter(ServerboundUseItemOnPacket.class, (p, packet) -> privilegeProvider.isPlayerPrivilegedToPerformAction(p) ? packet : null);
|
||||
TinyProtocol.instance.addFilter(ServerboundInteractPacket.class, (p, packet) -> privilegeProvider.isPlayerPrivilegedToPerformAction(p) ? packet : null);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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(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(ForPlayer access, int entityId, Packet<? extends PacketListener> packet) {
|
||||
if (access.canAccessEntity(entityId)) {
|
||||
return packet;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
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(access, entityIdExtractor.applyAsInt(packet), packet);
|
||||
};
|
||||
}
|
||||
|
||||
private Packet<?> processMoveEntityPacket(ForPlayer access, ClientboundMoveEntityPacket packet) {
|
||||
if (access.canAccessEntity(packet.entityId)) {
|
||||
return packet;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Packet<?> processRotateHeadPacket(ForPlayer access, ClientboundRotateHeadPacket packet) {
|
||||
if (access.canAccessEntity(packet.entityId)) {
|
||||
return packet;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Packet<?> processSetEntityLinkPacket(ForPlayer access, ClientboundSetEntityLinkPacket packet) {
|
||||
int fromEntityId = packet.getSourceId();
|
||||
int toEntityId = packet.getDestId();
|
||||
|
||||
if (access.canAccessEntity(fromEntityId) && access.canAccessEntity(toEntityId)) {
|
||||
return packet;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
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 (access.canAccessPosition(blockX, blockY, blockZ) && access.canAccessBlock(blockX, blockY, blockZ, block)) {
|
||||
return packet;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Packet<?> processBlockUpdatePacket(ForPlayer access, ClientboundBlockUpdatePacket packet) {
|
||||
BlockPos blockPos = packet.getPos();
|
||||
BlockState blockState = packet.getBlockState();
|
||||
Block block = blockState.getBlock();
|
||||
int blockX = blockPos.getX();
|
||||
int blockY = blockPos.getY();
|
||||
int blockZ = blockPos.getZ();
|
||||
|
||||
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);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
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 (access.canAccessPosition(blockX, blockY, blockZ) && access.canAccessBlockEntity(blockX, blockY, blockZ, blockEntityType)) {
|
||||
return packet;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Packet<?> proccessBlockDestructionPacket(ForPlayer access, ClientboundBlockDestructionPacket packet) {
|
||||
BlockPos blockPos = packet.getPos();
|
||||
int entityBreakingBlockId = packet.getId();
|
||||
|
||||
if (access.canAccessEntity(entityBreakingBlockId) && access.canAccessPosition(blockPos.getX(), blockPos.getY(), blockPos.getZ())) {
|
||||
return packet;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ClientboundSectionBlocksUpdatePacket processSectionUpdate(ForPlayer access, ClientboundSectionBlocksUpdatePacket packet) {
|
||||
SectionPos sectionPos = packet.sectionPos;
|
||||
short[] oldPos = packet.positions;
|
||||
BlockState[] oldStates = packet.states;
|
||||
|
||||
List<Short> filteredPos = new ArrayList<>(oldPos.length);
|
||||
List<BlockState> filteredStates = new ArrayList<>(oldStates.length);
|
||||
|
||||
for (int i = 0; i < oldPos.length; i++) {
|
||||
short posShort = oldPos[i];
|
||||
BlockState state = oldStates[i];
|
||||
Block block = state.getBlock();
|
||||
|
||||
int worldX = sectionPos.relativeToBlockX(posShort);
|
||||
int worldY = sectionPos.relativeToBlockY(posShort);
|
||||
int worldZ = sectionPos.relativeToBlockZ(posShort);
|
||||
|
||||
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 (access.canAccessPosition(worldX, worldY, worldZ)) {
|
||||
filteredPos.add(posShort);
|
||||
filteredStates.add(blockStateUsedForObfuscation);
|
||||
}
|
||||
}
|
||||
|
||||
if (filteredStates.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
short[] newPos = new short[filteredPos.size()];
|
||||
for (int i = 0; i < newPos.length; i++) {
|
||||
newPos[i] = filteredPos.get(i);
|
||||
}
|
||||
|
||||
BlockState[] newStates = filteredStates.toArray(new BlockState[0]);
|
||||
|
||||
return new ClientboundSectionBlocksUpdatePacket(sectionPos, ShortSets.unmodifiable(new ShortArraySet(newPos)), newStates);
|
||||
}
|
||||
|
||||
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 (access.canAccessPosition(blockX, blockY, blockZ)) {
|
||||
return packet;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ClientboundLevelChunkWithLightPacket processChunkWithLight(ForPlayer access, ClientboundLevelChunkWithLightPacket packet) {
|
||||
if (access.canAccessAllDataWithinChunk(packet.getX(), packet.getZ())) {
|
||||
return packet;
|
||||
} else {
|
||||
return chunkHider.processLevelChunkWithLightPacket(access, packet);
|
||||
}
|
||||
}
|
||||
|
||||
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<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(access, blockX, blockY, blockZ, packet);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
package de.steamwar.techhider.legacy;
|
||||
|
||||
import de.steamwar.techhider.ProtocolUtils;
|
||||
import de.steamwar.core.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.Getter;
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
package de.steamwar.techhider.legacy;
|
||||
|
||||
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||
import de.steamwar.techhider.BlockIds;
|
||||
import de.steamwar.techhider.ProtocolUtils;
|
||||
import de.steamwar.core.BlockIds;
|
||||
import de.steamwar.core.ProtocolUtils;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Vec3i;
|
||||
|
||||
@@ -33,11 +33,10 @@ mutable field net/minecraft/network/protocol/game/ClientboundEntityEventPacket e
|
||||
transitive-extendable class net/minecraft/network/protocol/game/ClientboundRotateHeadPacket
|
||||
accessible field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket yHeadRot B
|
||||
mutable field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket yHeadRot B
|
||||
## + TechHider
|
||||
accessible field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket entityId I
|
||||
mutable field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket entityId I
|
||||
|
||||
# For ChunkHider
|
||||
# For legacy/ChunkHider
|
||||
## transitive-extendable means that a public no args constructor is added without any super initialization
|
||||
transitive-extendable class net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket
|
||||
accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket chunkData Lnet/minecraft/network/protocol/game/ClientboundLevelChunkPacketData;
|
||||
@@ -55,8 +54,7 @@ accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacket
|
||||
accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData$BlockEntityInfo packedXZ I
|
||||
accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData$BlockEntityInfo y I
|
||||
|
||||
# For TechHider
|
||||
accessible field net/minecraft/network/protocol/game/ClientboundMoveEntityPacket entityId I
|
||||
# For legacy/TechHider
|
||||
## transitive-extendable means that a public no args constructor is added without any super initialization
|
||||
transitive-extendable class net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket
|
||||
accessible field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket sectionPos Lnet/minecraft/core/SectionPos;
|
||||
@@ -65,7 +63,6 @@ mutable field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdate
|
||||
accessible field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket states [Lnet/minecraft/world/level/block/state/BlockState;
|
||||
mutable field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket states [Lnet/minecraft/world/level/block/state/BlockState;
|
||||
|
||||
# For legacy/TechHider
|
||||
## transitive-extendable means that a public no args constructor is added without any super initialization
|
||||
transitive-extendable class net/minecraft/network/protocol/game/ClientboundBlockUpdatePacket
|
||||
mutable field net/minecraft/network/protocol/game/ClientboundBlockUpdatePacket blockState Lnet/minecraft/world/level/block/state/BlockState;
|
||||
Reference in New Issue
Block a user