169 Zeilen
8.7 KiB
Java
169 Zeilen
8.7 KiB
Java
/*
|
|
This file is a part of the SteamWar software.
|
|
|
|
Copyright (C) 2021 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 com.comphenix.tinyprotocol.Reflection;
|
|
import com.comphenix.tinyprotocol.TinyProtocol;
|
|
import de.steamwar.core.Core;
|
|
import org.bukkit.GameMode;
|
|
import org.bukkit.Material;
|
|
import org.bukkit.entity.Player;
|
|
|
|
import java.util.*;
|
|
import java.util.function.BiFunction;
|
|
import java.util.function.Function;
|
|
import java.util.function.UnaryOperator;
|
|
import java.util.stream.Collectors;
|
|
|
|
public class TechHider {
|
|
|
|
public static final Class<?> blockPosition = Reflection.getClass("{nms.core}.BlockPosition");
|
|
private static final Class<?> baseBlockPosition = Reflection.getClass("{nms.core}.BaseBlockPosition");
|
|
public static final Reflection.FieldAccessor<Integer> blockPositionX = Reflection.getField(baseBlockPosition, int.class, 0);
|
|
public static final Reflection.FieldAccessor<Integer> blockPositionY = Reflection.getField(baseBlockPosition, int.class, 1);
|
|
public static final Reflection.FieldAccessor<Integer> blockPositionZ = Reflection.getField(baseBlockPosition, int.class, 2);
|
|
|
|
public static final Class<?> iBlockData = Reflection.getClass("{nms.world.level.block.state}.IBlockData");
|
|
public static final Class<?> block = Reflection.getClass("{nms.world.level.block}.Block");
|
|
private static final Reflection.MethodInvoker getBlockDataByBlock = Reflection.getTypedMethod(block, null, iBlockData);
|
|
|
|
public static final Class<?> craftMagicNumbers = Reflection.getClass("{obc}.util.CraftMagicNumbers");
|
|
private static final Reflection.MethodInvoker getBlockByMaterial = Reflection.getTypedMethod(craftMagicNumbers, "getBlock", block, Material.class);
|
|
|
|
private static final Reflection.MethodInvoker getBlockByBlockData = Reflection.getTypedMethod(iBlockData, null, block);
|
|
private static final Reflection.MethodInvoker getMaterialByBlock = Reflection.getTypedMethod(craftMagicNumbers, "getMaterial", Material.class, block);
|
|
public static boolean iBlockDataHidden(Set<Material> obfuscate, Object iBlockData) {
|
|
return obfuscate.contains((Material) getMaterialByBlock.invoke(null, getBlockByBlockData.invoke(iBlockData)));
|
|
}
|
|
|
|
private final Map<Class<?>, BiFunction<Player, Object, Object>> techhiders = new HashMap<>();
|
|
private final LocationEvaluator locationEvaluator;
|
|
private final Object obfuscationTarget;
|
|
private final Set<Material> obfuscate;
|
|
|
|
@Deprecated
|
|
public TechHider(BypassEvaluator bypass, Material obfuscationTarget, Set<Material> obfuscate, Set<String> hiddenBlockEntities) {
|
|
this((LocationEvaluator) (player, x, z) -> bypass.bypass(player, ProtocolUtils.posToChunk(x), ProtocolUtils.posToChunk(z)), obfuscationTarget, obfuscate, hiddenBlockEntities);
|
|
}
|
|
|
|
public TechHider(LocationEvaluator locationEvaluator, Material obfuscationTarget, Set<Material> obfuscate, Set<String> hiddenBlockEntities) {
|
|
this.locationEvaluator = locationEvaluator;
|
|
this.obfuscate = obfuscate;
|
|
this.obfuscationTarget = getBlockDataByBlock.invoke(getBlockByMaterial.invoke(null, obfuscationTarget));
|
|
|
|
techhiders.put(blockActionPacket, this::blockActionHider);
|
|
techhiders.put(blockChangePacket, this::blockChangeHider);
|
|
techhiders.put(tileEntityDataPacket, this::tileEntityDataHider);
|
|
techhiders.put(multiBlockChangePacket, ProtocolWrapper.impl.multiBlockChangeGenerator(this.obfuscationTarget, obfuscate, locationEvaluator));
|
|
techhiders.put(ChunkHider.impl.mapChunkPacket(), ChunkHider.impl.chunkHiderGenerator(locationEvaluator, BlockIds.impl.materialToId(obfuscationTarget), obfuscate.stream().flatMap(m -> BlockIds.impl.materialToAllIds(m).stream()).collect(Collectors.toSet()), hiddenBlockEntities));
|
|
|
|
if(Core.getVersion() > 12 && Core.getVersion() < 19) {
|
|
Class<?> blockBreakClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutBlockBreak");
|
|
techhiders.put(blockBreakClass, ProtocolWrapper.impl.blockBreakHiderGenerator(blockBreakClass, this.obfuscationTarget, obfuscate, locationEvaluator));
|
|
}
|
|
|
|
if(Core.getVersion() > 8){
|
|
techhiders.put(Reflection.getClass("{nms.network.protocol.game}.PacketPlayInUseItem"), (p, packet) -> p.getGameMode() == GameMode.SPECTATOR ? null : packet);
|
|
}
|
|
techhiders.put(Reflection.getClass("{nms.network.protocol.game}.PacketPlayInUseEntity"), (p, packet) -> p.getGameMode() == GameMode.SPECTATOR ? null : packet);
|
|
|
|
}
|
|
|
|
public void enable() {
|
|
techhiders.forEach(TinyProtocol.instance::addFilter);
|
|
}
|
|
|
|
public void disable() {
|
|
techhiders.forEach(TinyProtocol.instance::removeFilter);
|
|
}
|
|
|
|
public static final Class<?> multiBlockChangePacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutMultiBlockChange");
|
|
public static final UnaryOperator<Object> multiBlockChangeCloner = ProtocolUtils.shallowCloneGenerator(TechHider.multiBlockChangePacket);
|
|
|
|
private static final Class<?> blockChangePacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutBlockChange");
|
|
private static final Function<Object, Object> blockChangeCloner = ProtocolUtils.shallowCloneGenerator(blockChangePacket);
|
|
private static final Reflection.FieldAccessor<?> blockChangePosition = Reflection.getField(blockChangePacket, blockPosition, 0);
|
|
private static final Reflection.FieldAccessor<?> blockChangeBlockData = Reflection.getField(blockChangePacket, iBlockData, 0);
|
|
private Object blockChangeHider(Player p, Object packet) {
|
|
switch (locationEvaluator.checkBlockPos(p, blockChangePosition.get(packet))) {
|
|
case SKIP:
|
|
return packet;
|
|
case CHECK:
|
|
if(!iBlockDataHidden(obfuscate, blockChangeBlockData.get(packet)))
|
|
return packet;
|
|
default:
|
|
packet = blockChangeCloner.apply(packet);
|
|
blockChangeBlockData.set(packet, obfuscationTarget);
|
|
return packet;
|
|
}
|
|
}
|
|
|
|
private static final Class<?> blockActionPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutBlockAction");
|
|
private static final Reflection.FieldAccessor<?> blockActionPosition = Reflection.getField(blockActionPacket, blockPosition, 0);
|
|
private Object blockActionHider(Player p, Object packet) {
|
|
if (locationEvaluator.checkBlockPos(p, blockActionPosition.get(packet)) == State.SKIP)
|
|
return packet;
|
|
return null;
|
|
}
|
|
|
|
public static final Class<?> tileEntityDataPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutTileEntityData");
|
|
private static final Reflection.FieldAccessor<?> tileEntityDataPosition = Reflection.getField(tileEntityDataPacket, blockPosition, 0);
|
|
private Object tileEntityDataHider(Player p, Object packet) {
|
|
switch (locationEvaluator.checkBlockPos(p, tileEntityDataPosition.get(packet))) {
|
|
case SKIP:
|
|
return packet;
|
|
case CHECK:
|
|
if(ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet))
|
|
return packet;
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Deprecated
|
|
public interface BypassEvaluator {
|
|
boolean bypass(Player p, int chunkX, int chunkZ);
|
|
}
|
|
|
|
public enum State {
|
|
SKIP,
|
|
CHECK,
|
|
HIDE
|
|
}
|
|
|
|
public interface LocationEvaluator {
|
|
boolean skipChunk(Player player, int x, int z);
|
|
default boolean skipChunkSection(Player player, int x, int y, int z) {
|
|
return skipChunk(player, x, z);
|
|
}
|
|
default State check(Player player, int x, int y, int z) {
|
|
return skipChunkSection(player, ProtocolUtils.posToChunk(x), ProtocolUtils.posToChunk(y), ProtocolUtils.posToChunk(z)) ? State.SKIP : State.CHECK;
|
|
}
|
|
|
|
default State checkBlockPos(Player player, Object pos) {
|
|
return check(player, blockPositionX.get(pos), blockPositionY.get(pos), blockPositionZ.get(pos));
|
|
}
|
|
|
|
default boolean blockPrecise(Player player, int x, int y, int z) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|