Make Techhider more robust

This commit is contained in:
2026-11-04 21:11:45 +01:00
parent a5bfc2fa44
commit 1eef62add3
7 changed files with 60 additions and 55 deletions
@@ -108,7 +108,7 @@ public class SpectatorListener implements Listener {
} }
public void addHidingRules(Player player, List<HidingRule> rules) { public void addHidingRules(Player player, List<HidingRule> rules) {
if (!(Permission.BUILD.hasPermission(player) || NO_TECHHIDER.contains(player))) { if (!NO_TECHHIDER.contains(player)) {
rules.add(SPECTATOR_RULE); rules.add(SPECTATOR_RULE);
} }
} }
@@ -138,6 +138,9 @@ public class SpectatorListener implements Listener {
@EventHandler @EventHandler
public void onPlayerJoin(PlayerJoinEvent event) { public void onPlayerJoin(PlayerJoinEvent event) {
if (Permission.BUILD.hasPermission(event.getPlayer())) {
NO_TECHHIDER.add(event.getPlayer());
}
if (BauSystem.DEV_SERVER) return; if (BauSystem.DEV_SERVER) return;
if (event.getPlayer().getUniqueId().equals(BauServer.getInstance().getOwner())) { if (event.getPlayer().getUniqueId().equals(BauServer.getInstance().getOwner())) {
return; return;
@@ -159,17 +162,20 @@ public class SpectatorListener implements Listener {
@EventHandler @EventHandler
public void onBauMemberUpdate(BauMemberUpdateEvent event) { public void onBauMemberUpdate(BauMemberUpdateEvent event) {
event.getChanged().forEach(player -> {
if (Permission.BUILD.hasPermission(player)) {
NO_TECHHIDER.add(player);
} else {
NO_TECHHIDER.remove(player);
}
resendChunks(player);
});
if (!anySupervisorOnline(null) && !BauSystem.DEV_SERVER) { if (!anySupervisorOnline(null) && !BauSystem.DEV_SERVER) {
Bukkit.getOnlinePlayers().forEach(player -> { Bukkit.getOnlinePlayers().forEach(player -> {
player.kickPlayer(""); player.kickPlayer("");
}); });
return; return;
} }
event.getChanged().forEach(player -> {
NO_TECHHIDER.remove(player);
resendChunks(player);
});
} }
@EventHandler @EventHandler
@@ -19,7 +19,6 @@
package de.steamwar.bausystem.features.xray; package de.steamwar.bausystem.features.xray;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.features.hullhider.HullHiderCommand; import de.steamwar.bausystem.features.hullhider.HullHiderCommand;
import de.steamwar.bausystem.features.techhider.HidingRule; import de.steamwar.bausystem.features.techhider.HidingRule;
@@ -30,12 +29,10 @@ import de.steamwar.core.CraftbukkitWrapper;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import de.steamwar.linkage.LinkedInstance; import de.steamwar.linkage.LinkedInstance;
import de.steamwar.techhider.ObfuscationConfig; import de.steamwar.techhider.ObfuscationConfig;
import io.papermc.paper.event.player.PlayerFailMoveEvent;
import io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason;
import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.ChatMessageType;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import net.minecraft.server.level.ServerPlayer;
import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
@@ -45,7 +42,6 @@ import org.bukkit.event.player.PlayerQuitEvent;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;
@Linked @Linked
public class XrayCommand extends SWCommand implements Listener { public class XrayCommand extends SWCommand implements Listener {
@@ -103,27 +99,12 @@ public class XrayCommand extends SWCommand implements Listener {
}); });
} }
{ @EventHandler
BiFunction<Player, ServerboundMovePlayerPacket, Object> positionSetter = (player, packet) -> { public void onFailMove(PlayerFailMoveEvent event) {
Region region = Region.getRegion(player.getLocation()); if (event.getFailReason() != FailReason.CLIPPED_INTO_BLOCK) return;
if (hidden.containsKey(region) && hidden.get(region).contains(player)) { Region region = Region.getRegion(event.getTo());
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { if (!hidden.containsKey(region) || !hidden.get(region).contains(event.getPlayer())) return;
ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); event.setAllowed(true);
if (packet.hasPos) {
serverPlayer.setPosRaw(packet.x, packet.y, packet.z);
}
if (packet.hasRot) {
serverPlayer.setXRot(packet.xRot);
serverPlayer.setYRot(packet.yRot);
}
}, 0);
return null;
}
return packet;
};
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Pos.class, positionSetter);
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.PosRot.class, positionSetter);
} }
@EventHandler @EventHandler
@@ -132,6 +132,10 @@ public class FightHullHider {
return hullHider.isBlockHidden(player, x, y, z); return hullHider.isBlockHidden(player, x, y, z);
} }
public boolean isEntityHidden(Player player, int entityId) {
return hullHider.isEntityHidden(player, entityId);
}
private void onTick() { private void onTick() {
Recording.iterateOverEntities(Objects::nonNull, hullHider::checkEntity); Recording.iterateOverEntities(Objects::nonNull, hullHider::checkEntity);
hullHider.sendUncoveredBlocks(); hullHider.sendUncoveredBlocks();
@@ -39,7 +39,6 @@ import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
@@ -133,16 +132,9 @@ public class TechHiderWrapper extends StateDependent implements Listener {
return !isHidden(blockX, blockY, blockZ) || !obfuscation.hidesBlockState(blockState); return !isHidden(blockX, blockY, blockZ) || !obfuscation.hidesBlockState(blockState);
} }
// TODO will require entity tracking on the netty thread to prevent future race conditions
@Override @Override
public boolean canAccessEntity(int entityId) { public boolean canAccessEntity(int entityId) {
net.minecraft.world.entity.Entity nmsEntity = ((CraftWorld) player.getWorld()).getHandle().moonrise$getEntityLookup().get(entityId); return !hullHider.isEntityHidden(player, entityId);
if (nmsEntity != null) {
return !hullHider.isBlockHidden(player, nmsEntity.getBlockX(), nmsEntity.getBlockY(), nmsEntity.getBlockZ());
} else {
return true;
}
} }
@Override @Override
+14 -8
View File
@@ -53,6 +53,7 @@ import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.logging.Level; import java.util.logging.Level;
@@ -81,8 +82,9 @@ public class Hull {
private final HashSet<IntVector> uncoveredSurface = new HashSet<>(); private final HashSet<IntVector> uncoveredSurface = new HashSet<>();
private final Viewers viewers; private final Viewers viewers;
private final HashSet<Entity> entities = new HashSet<>(); // Concurrent as it is read on netty threads, keyed by entity id
private final HashSet<REntity> rentities = new HashSet<>(); private final Map<Integer, Entity> entities = new ConcurrentHashMap<>();
private final Set<REntity> rentities = ConcurrentHashMap.newKeySet();
/** /**
* @param groundVisible whether the bottom layer of the region is uncovered (if the region does not reach the ground) * @param groundVisible whether the bottom layer of the region is uncovered (if the region does not reach the ground)
@@ -162,6 +164,10 @@ public class Hull {
return region.contains(x, y, z) && !isViewer(player) && !visibility.get(region.toIndex(x, y, z)); return region.contains(x, y, z) && !isViewer(player) && !visibility.get(region.toIndex(x, y, z));
} }
public boolean isEntityHidden(Player player, int entityId) {
return player.getEntityId() != entityId && entities.containsKey(entityId) && !isViewer(player);
}
public boolean isViewer(Player player) { public boolean isViewer(Player player) {
return player != null && viewers.isViewer(player); return player != null && viewers.isViewer(player);
} }
@@ -187,13 +193,13 @@ public class Hull {
private void updateEntities(Player player, boolean wasViewer) { private void updateEntities(Player player, boolean wasViewer) {
if (isViewer(player)) { if (isViewer(player)) {
if (!wasViewer) { if (!wasViewer) {
for (Entity entity : entities) { for (Entity entity : entities.values()) {
trackEntity(player, entity); trackEntity(player, entity);
} }
} }
} else { } else {
// Also hides the entities from players that joined after they were hidden // Also hides the entities from players that joined after they were hidden
for (Entity entity : entities) { for (Entity entity : entities.values()) {
if (!player.equals(entity)) untrackEntity(player, entity); if (!player.equals(entity)) untrackEntity(player, entity);
} }
} }
@@ -210,13 +216,13 @@ public class Hull {
public void checkEntity(Entity entity) { public void checkEntity(Entity entity) {
Location location = entity.getLocation(); Location location = entity.getLocation();
if (region.contains(location) && !visibility.get(new IntVector(location).toId(region))) { if (region.contains(location) && !visibility.get(new IntVector(location).toId(region))) {
if (entities.add(entity)) { if (entities.put(entity.getEntityId(), entity) == null) {
for (Player player : Bukkit.getOnlinePlayers()) { for (Player player : Bukkit.getOnlinePlayers()) {
if (!isViewer(player) && !player.equals(entity)) untrackEntity(player, entity); if (!isViewer(player) && !player.equals(entity)) untrackEntity(player, entity);
} }
} }
} else { } else {
if (entities.remove(entity)) { if (entities.remove(entity.getEntityId()) != null) {
for (Player player : Bukkit.getOnlinePlayers()) { for (Player player : Bukkit.getOnlinePlayers()) {
if (!isViewer(player) && !player.equals(entity)) trackEntity(player, entity); if (!isViewer(player) && !player.equals(entity)) trackEntity(player, entity);
} }
@@ -241,7 +247,7 @@ public class Hull {
} }
public void removeEntity(Entity entity) { public void removeEntity(Entity entity) {
entities.remove(entity); entities.remove(entity.getEntityId());
} }
public void checkREntity(REntity entity) { public void checkREntity(REntity entity) {
@@ -264,7 +270,7 @@ public class Hull {
for (Player player : Bukkit.getOnlinePlayers()) { for (Player player : Bukkit.getOnlinePlayers()) {
if (isViewer(player)) continue; if (isViewer(player)) continue;
for (Entity entity : entities) { for (Entity entity : entities.values()) {
if (!player.equals(entity)) trackEntity(player, entity); if (!player.equals(entity)) trackEntity(player, entity);
} }
} }
@@ -146,6 +146,16 @@ public class HullHider<K> implements Listener {
return false; return false;
} }
public boolean isEntityHidden(Player player, int entityId) {
for (Hull hull : hulls) {
if (hull.isEntityHidden(player, entityId)) {
return true;
}
}
return false;
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onBlockPhysic(BlockPhysicsEvent e) { public void onBlockPhysic(BlockPhysicsEvent e) {
if (e.getBlock() == e.getSourceBlock() || e.getChangedType() == Material.AIR) { if (e.getBlock() == e.getSourceBlock() || e.getChangedType() == Material.AIR) {
@@ -30,7 +30,8 @@ import net.minecraft.util.SimpleBitStorage;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.entity.Player; import org.bukkit.Bukkit;
import org.bukkit.World;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -48,6 +49,15 @@ public class ChunkHider {
private final int BLOCKS_PER_SECTION = 4096; private final int BLOCKS_PER_SECTION = 4096;
private final int BIOMES_PER_SECTION = 64; private final int BIOMES_PER_SECTION = 64;
private final int worldMinHeight;
private final int worldMaxHeight;
public ChunkHider() {
World world = Bukkit.getWorlds().getFirst();
worldMinHeight = world.getMinHeight();
worldMaxHeight = world.getMaxHeight();
}
private int getLongsRequiredToEncodeEntries(int bitsPerEntry, int entryCount) { private int getLongsRequiredToEncodeEntries(int bitsPerEntry, int entryCount) {
int entriesPerLong = Long.SIZE / bitsPerEntry; int entriesPerLong = Long.SIZE / bitsPerEntry;
int dataLengthAsLongCount = (entryCount + entriesPerLong - 1) / entriesPerLong; int dataLengthAsLongCount = (entryCount + entriesPerLong - 1) / entriesPerLong;
@@ -67,7 +77,6 @@ public class ChunkHider {
} }
public ClientboundLevelChunkWithLightPacket processLevelChunkWithLightPacket(ForPlayer access, ClientboundLevelChunkWithLightPacket packet) { public ClientboundLevelChunkWithLightPacket processLevelChunkWithLightPacket(ForPlayer access, ClientboundLevelChunkWithLightPacket packet) {
Player player = access.getPlayer();
int chunkX = packet.getX(); int chunkX = packet.getX();
int chunkZ = packet.getZ(); int chunkZ = packet.getZ();
ClientboundLevelChunkPacketData chunkData = packet.getChunkData(); ClientboundLevelChunkPacketData chunkData = packet.getChunkData();
@@ -75,9 +84,6 @@ public class ChunkHider {
ByteBuf in = Unpooled.wrappedBuffer(chunkData.getReadBuffer()); ByteBuf in = Unpooled.wrappedBuffer(chunkData.getReadBuffer());
ByteBuf out = Unpooled.buffer(in.readableBytes() + 64); 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) { for (int yOffset = worldMinHeight; yOffset < worldMaxHeight; yOffset += SECTION_SPAN_SIZE) {
short blockCount = in.readShort(); short blockCount = in.readShort();