diff --git a/BauSystem/BauSystem_Main/build.gradle.kts b/BauSystem/BauSystem_Main/build.gradle.kts
index c387bc36..c320e674 100644
--- a/BauSystem/BauSystem_Main/build.gradle.kts
+++ b/BauSystem/BauSystem_Main/build.gradle.kts
@@ -36,6 +36,7 @@ dependencies {
annotationProcessor(libs.classindex)
compileOnly(project(":SpigotCore", "default"))
compileOnly(project(":KotlinCore", "default"))
+ implementation(project(":HiderCore"))
compileOnly(libs.axiom)
compileOnly(libs.authlib)
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/BauTechHider.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/BauTechHider.java
new file mode 100644
index 00000000..a4fd51bf
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/BauTechHider.java
@@ -0,0 +1,137 @@
+/*
+ * 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 .
+ */
+
+package de.steamwar.bausystem.features.techhider;
+
+import de.steamwar.bausystem.features.world.SpectatorListener;
+import de.steamwar.bausystem.features.xray.XrayCommand;
+import de.steamwar.linkage.Linked;
+import de.steamwar.linkage.LinkedInstance;
+import de.steamwar.techhider.AccessPrivilegeProvider;
+import de.steamwar.techhider.TechHider;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.entity.BlockEntityType;
+import net.minecraft.world.level.block.state.BlockState;
+import org.bukkit.entity.Player;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The single TechHider of the server. It combines the hiding rules of the spectator techhider, /techhider and /xray per player.
+ */
+@Linked
+public class BauTechHider implements AccessPrivilegeProvider {
+
+ private static final HidingRule[] NO_RULES = new HidingRule[0];
+
+ // Injected after construction, until then nothing is hidden
+ @LinkedInstance
+ public XrayCommand xrayCommand;
+ @LinkedInstance
+ public TechHiderCommand techHiderCommand;
+ @LinkedInstance
+ public SpectatorListener spectatorListener;
+
+ public BauTechHider() {
+ new TechHider(this);
+ }
+
+ @Override
+ public ForPlayer forPlayer(Player p) {
+ if (xrayCommand == null || techHiderCommand == null || spectatorListener == null) {
+ return new BauPlayerAccess(p, NO_RULES);
+ }
+
+ // Region rules first: where rules overlap, the first one decides the obfuscation block
+ List rules = new ArrayList<>();
+ xrayCommand.addHidingRules(p, rules);
+ techHiderCommand.addHidingRules(p, rules);
+ spectatorListener.addHidingRules(p, rules);
+ return new BauPlayerAccess(p, rules.toArray(NO_RULES));
+ }
+
+ @Override
+ public boolean isPlayerPrivilegedToPerformAction(Player p) {
+ return true;
+ }
+
+ /**
+ * Snapshot of the hiding rules of one player, taken once per outgoing packet on the netty thread.
+ */
+ private record BauPlayerAccess(Player player, HidingRule[] rules) implements ForPlayer {
+
+ @Override
+ public Player getPlayer() {
+ return player;
+ }
+
+ @Override
+ public boolean canAccessAllDataWithinChunk(int chunkX, int chunkZ) {
+ for (HidingRule rule : rules) {
+ if (rule.coversChunk(chunkX, chunkZ)) return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean canAccessPosition(int blockX, int blockY, int blockZ) {
+ return true;
+ }
+
+ @Override
+ public boolean canAccessBlock(int blockX, int blockY, int blockZ, Block block) {
+ for (HidingRule rule : rules) {
+ if (rule.covers(blockX, blockY, blockZ) && rule.obfuscation().hidesBlock(block)) return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean canAccessBlockState(int blockX, int blockY, int blockZ, BlockState blockState) {
+ for (HidingRule rule : rules) {
+ if (rule.covers(blockX, blockY, blockZ) && rule.obfuscation().hidesBlockState(blockState)) return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean canAccessEntity(int entityId) {
+ return true;
+ }
+
+ @Override
+ public boolean canAccessBlockEntity(int blockX, int blockY, int blockZ, BlockEntityType> type) {
+ for (HidingRule rule : rules) {
+ if (rule.covers(blockX, blockY, blockZ) && rule.obfuscation().hidesBlockEntity(type)) return false;
+ }
+ return true;
+ }
+
+ @Override
+ public BlockState getObfuscation(int blockX, int blockY, int blockZ) {
+ for (HidingRule rule : rules) {
+ if (rule.covers(blockX, blockY, blockZ)) return rule.obfuscation().getObfuscationState();
+ }
+ // Only asked for positions a rule hides
+ return Blocks.END_STONE.defaultBlockState();
+ }
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/HidingRule.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/HidingRule.java
new file mode 100644
index 00000000..a7010e5a
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/HidingRule.java
@@ -0,0 +1,52 @@
+/*
+ * 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 .
+ */
+
+package de.steamwar.bausystem.features.techhider;
+
+import de.steamwar.bausystem.region.Point;
+import de.steamwar.bausystem.region.Region;
+import de.steamwar.hullhider.HullRegion;
+import de.steamwar.techhider.ObfuscationConfig;
+
+/**
+ * Hides the blocks of an obfuscation config within an area, or everywhere if the area is null.
+ */
+public record HidingRule(HullRegion area, ObfuscationConfig obfuscation) {
+
+ public static HidingRule everywhere(ObfuscationConfig obfuscation) {
+ return new HidingRule(null, obfuscation);
+ }
+
+ /**
+ * Covers the area including its extension.
+ */
+ public static HidingRule of(Region.Area area, ObfuscationConfig obfuscation) {
+ Point min = area.getMinPoint(true);
+ Point max = area.getMaxPoint(true);
+ return new HidingRule(HullRegion.fromInclusive(min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ()), obfuscation);
+ }
+
+ public boolean covers(int x, int y, int z) {
+ return area == null || area.contains(x, y, z);
+ }
+
+ public boolean coversChunk(int chunkX, int chunkZ) {
+ return area == null || !area.chunkOutside(chunkX, chunkZ);
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java
index 2f401a59..b6fd7d8a 100644
--- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java
@@ -31,7 +31,7 @@ import de.steamwar.command.TypeMapper;
import de.steamwar.core.CraftbukkitWrapper;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.LinkedInstance;
-import de.steamwar.techhider.legacy.TechHider;
+import de.steamwar.techhider.ObfuscationConfig;
import net.md_5.bungee.api.ChatMessageType;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
@@ -41,6 +41,7 @@ import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@Linked
@@ -50,8 +51,9 @@ public class TechHiderCommand extends SWCommand implements Listener, ScoreboardE
super("techhider", "hider", "obfuscate", "hide", "hide-tech");
}
- private Map> techHiders = new HashMap<>();
- private Map> hidden = new HashMap<>();
+ // Read on netty threads by the BauTechHider
+ private Map> hidingRules = new ConcurrentHashMap<>();
+ private Map> hidden = new ConcurrentHashMap<>();
@LinkedInstance
public XrayCommand xrayCommand;
@@ -64,22 +66,15 @@ public class TechHiderCommand extends SWCommand implements Listener, ScoreboardE
return;
}
- Optional techHider = techHiders.computeIfAbsent(region, rg -> {
+ Optional hidingRule = hidingRules.computeIfAbsent(region, rg -> {
if (!region.getGameModeConfig().Techhider.Active) {
return Optional.empty();
}
- hidden.put(rg, new HashSet<>());
-
- TechHider current = new TechHider((p, cX, cY) -> {
- if (rg.getBuildArea().isChunkOutside(cX, cY)) return true;
- return !hidden.get(rg).contains(p);
- }, region.getGameModeConfig().Techhider.ObfuscateWith, region.getGameModeConfig().Techhider.HiddenBlocks, region.getGameModeConfig().Techhider.HiddenBlockEntities);
- current.enable();
-
- return Optional.of(current);
+ hidden.put(rg, ConcurrentHashMap.newKeySet());
+ return Optional.of(HidingRule.of(rg.getBuildArea(), ObfuscationConfig.of(rg.getGameModeConfig().Techhider)));
});
- if (!techHider.isPresent()) {
+ if (!hidingRule.isPresent()) {
return;
}
@@ -128,6 +123,14 @@ public class TechHiderCommand extends SWCommand implements Listener, ScoreboardE
hidden.values().forEach(set -> set.remove(event.getPlayer()));
}
+ public void addHidingRules(Player player, List rules) {
+ hidden.forEach((region, players) -> {
+ if (players.contains(player)) {
+ hidingRules.getOrDefault(region, Optional.empty()).ifPresent(rules::add);
+ }
+ });
+ }
+
public void disable(Region region, Player player) {
hidden.computeIfPresent(region, (rg, set) -> {
set.remove(player);
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java
index 645600cd..ae32e0c1 100644
--- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java
@@ -22,10 +22,11 @@ package de.steamwar.bausystem.features.world;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.config.BauServer;
+import de.steamwar.bausystem.features.techhider.HidingRule;
import de.steamwar.bausystem.utils.BauMemberUpdateEvent;
import de.steamwar.linkage.Linked;
import de.steamwar.sql.BauweltMember;
-import de.steamwar.techhider.legacy.TechHider;
+import de.steamwar.techhider.ObfuscationConfig;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
@@ -41,14 +42,18 @@ import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.event.player.*;
import org.bukkit.util.Vector;
+import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
@Linked
public class SpectatorListener implements Listener {
- private static final TechHider techHider;
- private static final Set NO_TECHHIDER = new HashSet<>();
+ private static final HidingRule SPECTATOR_RULE;
+ // Read on netty threads by the BauTechHider
+ private static final Set NO_TECHHIDER = ConcurrentHashMap.newKeySet();
static {
Set materials = new HashSet<>();
@@ -99,19 +104,12 @@ public class SpectatorListener implements Listener {
materials.add(Material.WATER);
materials.remove(Material.BARRIER);
materials.remove(Material.STONE);
- techHider = new TechHider((TechHider.LocationEvaluator) (player, i, i1) -> {
- return Permission.BUILD.hasPermission(player) || NO_TECHHIDER.contains(player);
- }, Material.END_STONE, materials, new HashSet<>());
+ SPECTATOR_RULE = HidingRule.everywhere(new ObfuscationConfig(Material.END_STONE, materials, Collections.emptySet()));
}
- private static void enableOrDisableTechhider() {
- boolean enable = Bukkit.getOnlinePlayers()
- .stream()
- .anyMatch(player -> !(Permission.BUILD.hasPermission(player) || NO_TECHHIDER.contains(player)));
- if (enable) {
- techHider.enable();
- } else {
- techHider.disable();
+ public void addHidingRules(Player player, List rules) {
+ if (!(Permission.BUILD.hasPermission(player) || NO_TECHHIDER.contains(player))) {
+ rules.add(SPECTATOR_RULE);
}
}
@@ -122,7 +120,6 @@ public class SpectatorListener implements Listener {
NO_TECHHIDER.add(player);
}
resendChunks(player);
- enableOrDisableTechhider();
}
private static void resendChunks(Player player) {
@@ -141,7 +138,6 @@ public class SpectatorListener implements Listener {
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
- enableOrDisableTechhider();
if (BauSystem.DEV_SERVER) return;
if (event.getPlayer().getUniqueId().equals(BauServer.getInstance().getOwner())) {
return;
@@ -163,7 +159,6 @@ public class SpectatorListener implements Listener {
@EventHandler
public void onBauMemberUpdate(BauMemberUpdateEvent event) {
- enableOrDisableTechhider();
if (!anySupervisorOnline(null) && !BauSystem.DEV_SERVER) {
Bukkit.getOnlinePlayers().forEach(player -> {
player.kickPlayer("");
@@ -180,7 +175,6 @@ public class SpectatorListener implements Listener {
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
NO_TECHHIDER.remove(event.getPlayer());
- Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), SpectatorListener::enableOrDisableTechhider, 1);
if (BauSystem.DEV_SERVER) return;
if (!anySupervisorOnline(event.getPlayer())) {
Bukkit.getOnlinePlayers().forEach(player -> {
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java
index aa90081c..0b334429 100644
--- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java
@@ -21,6 +21,7 @@ package de.steamwar.bausystem.features.xray;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.features.techhider.HidingRule;
import de.steamwar.bausystem.features.techhider.TechHiderCommand;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.utils.ScoreboardElement;
@@ -28,7 +29,7 @@ import de.steamwar.command.SWCommand;
import de.steamwar.core.CraftbukkitWrapper;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.LinkedInstance;
-import de.steamwar.techhider.legacy.TechHider;
+import de.steamwar.techhider.ObfuscationConfig;
import net.md_5.bungee.api.ChatMessageType;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import net.minecraft.server.level.ServerPlayer;
@@ -43,6 +44,7 @@ import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;
@Linked
@@ -52,9 +54,10 @@ public class XrayCommand extends SWCommand implements Listener, ScoreboardElemen
super("xray");
}
- private Map> techHiders = new HashMap<>();
+ // Read on netty threads by the BauTechHider and the movement filter
+ private Map> hidingRules = new ConcurrentHashMap<>();
private Map> xrayedBlocks = new HashMap<>();
- private Map> hidden = new HashMap<>();
+ private Map> hidden = new ConcurrentHashMap<>();
@LinkedInstance
public TechHiderCommand techHiderCommand;
@@ -67,19 +70,19 @@ public class XrayCommand extends SWCommand implements Listener, ScoreboardElemen
return;
}
- Optional techHider = techHiders.computeIfAbsent(region, rg -> {
+ Optional hidingRule = hidingRules.computeIfAbsent(region, rg -> {
if (!region.getGameModeConfig().Techhider.Active) {
return Optional.empty();
}
- hidden.put(rg, new HashSet<>());
+ hidden.put(rg, ConcurrentHashMap.newKeySet());
Set blocks = new HashSet<>(Arrays.asList(region.getGameModeConfig().Techhider.ObfuscateWith));
if (blocks.contains(Material.END_STONE)) blocks.add(Material.END_STONE_BRICKS);
xrayedBlocks.put(region, blocks);
- return Optional.of(createXray(rg, blocks));
+ return Optional.of(HidingRule.of(rg.getBuildArea(), new ObfuscationConfig(Material.STRUCTURE_VOID, blocks, Collections.emptySet())));
});
- if (!techHider.isPresent()) {
+ if (!hidingRule.isPresent()) {
return;
}
@@ -139,13 +142,12 @@ public class XrayCommand extends SWCommand implements Listener, ScoreboardElemen
}
}
- private TechHider createXray(Region rg, Set obfuscate) {
- TechHider current = new TechHider((TechHider.LocationEvaluator) (p, cX, cY) -> {
- if (rg.getBuildArea().isChunkOutside(cX, cY)) return true;
- return !hidden.get(rg).contains(p);
- }, Material.STRUCTURE_VOID, obfuscate, new HashSet<>());
- current.enable();
- return current;
+ public void addHidingRules(Player player, List rules) {
+ hidden.forEach((region, players) -> {
+ if (players.contains(player)) {
+ hidingRules.getOrDefault(region, Optional.empty()).ifPresent(rules::add);
+ }
+ });
}
@EventHandler
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java
index 1ccaad8e..6f0b1290 100644
--- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java
@@ -70,7 +70,7 @@ public class TechHiderWrapper extends StateDependent implements Listener {
@Override
public void enable() {
ObfuscationConfig obfuscation = ObfuscationConfig.of(Config.GameModeConfig.Techhider);
- new TechHider(obfuscation.getObfuscateWith(), newProvider(obfuscation));
+ new TechHider(newProvider(obfuscation));
}
private AccessPrivilegeProvider newProvider(ObfuscationConfig obfuscation) {
@@ -150,6 +150,11 @@ public class TechHiderWrapper extends StateDependent implements Listener {
public boolean canAccessBlockEntity(int blockX, int blockY, int blockZ, BlockEntityType> type) {
return !isHidden(blockX, blockY, blockZ) || !obfuscation.hidesBlockEntity(type);
}
+
+ @Override
+ public BlockState getObfuscation(int blockX, int blockY, int blockZ) {
+ return obfuscation.getObfuscationState();
+ }
}
@Override
diff --git a/HiderCore/src/de/steamwar/hullhider/HullRegion.java b/HiderCore/src/de/steamwar/hullhider/HullRegion.java
index b8aacb0b..0006a2af 100644
--- a/HiderCore/src/de/steamwar/hullhider/HullRegion.java
+++ b/HiderCore/src/de/steamwar/hullhider/HullRegion.java
@@ -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
*/
diff --git a/HiderCore/src/de/steamwar/techhider/AccessPrivilegeProvider.java b/HiderCore/src/de/steamwar/techhider/AccessPrivilegeProvider.java
index 38b9b463..be0599cb 100644
--- a/HiderCore/src/de/steamwar/techhider/AccessPrivilegeProvider.java
+++ b/HiderCore/src/de/steamwar/techhider/AccessPrivilegeProvider.java
@@ -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);
}
}
diff --git a/HiderCore/src/de/steamwar/techhider/ChunkHider.java b/HiderCore/src/de/steamwar/techhider/ChunkHider.java
index 9ad25a4e..6cabee7b 100644
--- a/HiderCore/src/de/steamwar/techhider/ChunkHider.java
+++ b/HiderCore/src/de/steamwar/techhider/ChunkHider.java
@@ -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);
diff --git a/HiderCore/src/de/steamwar/techhider/ObfuscationConfig.java b/HiderCore/src/de/steamwar/techhider/ObfuscationConfig.java
index cc8310a5..554abaa3 100644
--- a/HiderCore/src/de/steamwar/techhider/ObfuscationConfig.java
+++ b/HiderCore/src/de/steamwar/techhider/ObfuscationConfig.java
@@ -49,6 +49,7 @@ public final class ObfuscationConfig {
}
private final Block obfuscateWith;
+ private final BlockState obfuscationState;
private final Set blocks;
private final Set blockStates;
private final Set> blockEntityTypes;
@@ -59,6 +60,7 @@ public final class ObfuscationConfig {
*/
public ObfuscationConfig(Material obfuscateWith, Set hiddenBlocks, Set hiddenBlockEntities) {
this.obfuscateWith = CraftMagicNumbers.getBlock(obfuscateWith);
+ this.obfuscationState = this.obfuscateWith.defaultBlockState();
this.blocks = hiddenBlocks.stream()
.map(CraftMagicNumbers::getBlock)
diff --git a/HiderCore/src/de/steamwar/techhider/TechHider.java b/HiderCore/src/de/steamwar/techhider/TechHider.java
index eee198e6..af640d18 100644
--- a/HiderCore/src/de/steamwar/techhider/TechHider.java
+++ b/HiderCore/src/de/steamwar/techhider/TechHider.java
@@ -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> bypassingPackets;
private final Map>, BiFunction, Packet extends PacketListener>>> packetProcessors;
- private final BlockState blockStateUsedForObfuscation;
+ private final Set> 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));
}
}
diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java
deleted file mode 100644
index bca91a11..00000000
--- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java
+++ /dev/null
@@ -1,301 +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 .
- */
-
-package de.steamwar.techhider.legacy;
-
-import de.steamwar.core.ProtocolUtils;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-import lombok.Getter;
-import net.minecraft.core.registries.BuiltInRegistries;
-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.entity.BlockEntityType;
-import org.bukkit.entity.Player;
-
-import java.util.Collections;
-import java.util.Set;
-import java.util.function.BiFunction;
-import java.util.function.UnaryOperator;
-import java.util.stream.Collectors;
-
-@Deprecated
-public class ChunkHider {
- public static final ChunkHider impl = new ChunkHider();
-
- private static final UnaryOperator chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkWithLightPacket::new);
- private static final UnaryOperator chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class, ClientboundLevelChunkPacketData::new);
-
- public BiFunction chunkHiderGenerator(TechHider techHider) {
- return (p, packet) -> {
- int chunkX = packet.getX();
- int chunkZ = packet.getZ();
- if (techHider.getLocationEvaluator().skipChunk(p, chunkX, chunkZ)) {
- return packet;
- }
-
- packet = chunkPacketCloner.apply(packet);
- ClientboundLevelChunkPacketData dataWrapper = chunkDataCloner.apply(packet.getChunkData());
-
- Set hiddenBlockEntities = techHider.getHiddenBlockEntities();
- dataWrapper.blockEntitiesData = dataWrapper.blockEntitiesData.stream()
- .filter(te -> tileEntityVisible(hiddenBlockEntities, te))
- .collect(Collectors.toList());
-
- ByteBuf in = Unpooled.wrappedBuffer(dataWrapper.buffer);
- ByteBuf out = Unpooled.buffer(in.readableBytes() + 64);
- for (int yOffset = p.getWorld().getMinHeight(); yOffset < p.getWorld().getMaxHeight(); yOffset += 16) {
- SectionHider section = new SectionHider(p, techHider, in, out, chunkX, yOffset / 16, chunkZ);
- section.copyBlockCount();
-
- blocks(section);
- biomes(section);
- }
-
- if (in.readableBytes() != 0) {
- throw new IllegalStateException("ChunkHider21: Incomplete chunk data, " + in.readableBytes() + " bytes left");
- }
-
- byte[] data = new byte[out.readableBytes()];
- out.readBytes(data);
- dataWrapper.buffer = data;
-
- packet.chunkData = dataWrapper;
- return packet;
- };
- }
-
- protected boolean tileEntityVisible(Set hiddenBlockEntities, ClientboundLevelChunkPacketData.BlockEntityInfo tile) {
- BlockEntityType> type = tile.type;
- String path = BuiltInRegistries.BLOCK_ENTITY_TYPE.getKey(type).getPath();
- return !hiddenBlockEntities.contains(path);
- }
-
- private void blocks(SectionHider section) {
- section.copyBitsPerBlock();
-
- boolean singleValued = section.getBitsPerBlock() == 0;
- if (singleValued) {
- int value = ProtocolUtils.readVarInt(section.getIn());
- ProtocolUtils.writeVarInt(section.getOut(), !section.isSkipSection() && section.getObfuscate().contains(value) ? section.getTarget() : value);
- return;
- } else if (section.getBitsPerBlock() < 9) {
- // Indirect (paletted) storage – only present when bitsPerBlock < 9 in 1.21+
- section.processPalette();
- }
-
- if (section.isSkipSection() || (!section.blockPrecise() && section.isPaletted())) {
- section.skipNewDataArray(4096);
- return;
- }
-
- SimpleBitStorage values = new SimpleBitStorage(section.getBitsPerBlock(), 4096, section.readNewDataArray(4096));
-
- for (int y = 0; y < 16; y++) {
- for (int z = 0; z < 16; z++) {
- for (int x = 0; x < 16; x++) {
- int pos = (((y * 16) + z) * 16) + x;
-
- TechHider.State test = section.test(x, y, z);
-
- switch (test) {
- case SKIP:
- break;
- case CHECK:
- if (!section.getObfuscate().contains(values.get(pos))) {
- break;
- }
- case HIDE:
- values.set(pos, section.getTarget());
- break;
- case HIDE_AIR:
- default:
- values.set(pos, section.getAir());
- }
- }
- }
- }
-
- section.writeDataArray(values.getRaw());
- }
-
- private void biomes(SectionHider section) {
- section.copyBitsPerBlock();
- if (section.getBitsPerBlock() == 0) {
- section.copyVarInt();
- } else if (section.getBitsPerBlock() < 6) {
- section.skipPalette();
- section.skipNewDataArray(64);
- } else {
- // Direct (global) biome IDs – no palette present
- section.skipNewDataArray(64);
- }
- }
-
- @Getter
- class SectionHider {
- private final Player player;
- private final TechHider techHider;
- private final ByteBuf in;
- private final ByteBuf out;
-
- private final int chunkX;
- private final int chunkY;
- private final int chunkZ;
- private final int offsetX;
- private final int offsetY;
- private final int offsetZ;
-
- private final boolean skipSection;
-
- private boolean paletted;
- private int bitsPerBlock;
- private int blockCount;
- private int air;
- private int target;
- private Set obfuscate;
-
- public SectionHider(Player player, TechHider techHider, ByteBuf in, ByteBuf out, int chunkX, int chunkY, int chunkZ) {
- this.player = player;
- this.techHider = techHider;
- this.in = in;
- this.out = out;
- this.chunkX = chunkX;
- this.chunkY = chunkY;
- this.chunkZ = chunkZ;
- this.offsetX = 16 * chunkX;
- this.offsetY = 16 * chunkY;
- this.offsetZ = 16 * chunkZ;
- this.skipSection = techHider.getLocationEvaluator().skipChunkSection(player, chunkX, chunkY, chunkZ);
-
- this.paletted = false;
- this.bitsPerBlock = 0;
- this.air = TechHider.AIR_ID;
- this.target = techHider.getObfuscationTargetId();
- this.obfuscate = techHider.getObfuscateIds();
- }
-
- public boolean blockPrecise() {
- return techHider.getLocationEvaluator().blockPrecise(player, chunkX, chunkY, chunkZ);
- }
-
- public TechHider.State test(int x, int y, int z) {
- return techHider.getLocationEvaluator().check(player, offsetX + x, offsetY + y, offsetZ + z);
- }
-
- public void copyBlockCount() {
- this.blockCount = in.readShort();
- out.writeShort(blockCount);
- }
-
- public void copyBitsPerBlock() {
- bitsPerBlock = in.readByte();
- out.writeByte(bitsPerBlock);
- }
-
- public int copyVarInt() {
- int value = ProtocolUtils.readVarInt(in);
- ProtocolUtils.writeVarInt(out, value);
- return value;
- }
-
- public void skipPalette() {
- int paletteLength = copyVarInt();
- for (int i = 0; i < paletteLength; i++) {
- copyVarInt();
- }
- }
-
- public void processPalette() {
- if (skipSection) {
- skipPalette();
- return;
- }
-
- int paletteLength = copyVarInt();
- if (paletteLength == 0) return;
-
- paletted = true;
- air = 0;
- target = 0;
-
- for (int i = 0; i < paletteLength; i++) {
- int entry = ProtocolUtils.readVarInt(in);
- if (obfuscate.contains(entry)) {
- entry = techHider.getObfuscationTargetId();
- }
-
- if (entry == TechHider.AIR_ID) {
- air = i;
- } else if (entry == techHider.getObfuscationTargetId()) {
- target = i;
- }
-
- ProtocolUtils.writeVarInt(out, entry);
- }
- obfuscate = Collections.emptySet();
- }
-
- public void skipDataArray() {
- int dataArrayLength = copyVarInt();
- out.writeBytes(in, dataArrayLength * 8);
- }
-
- public void skipNewDataArray(int entries) {
- if (bitsPerBlock == 0) {
- return;
- }
-
- char valuesPerLong = (char) (64 / bitsPerBlock);
- int i1 = (entries + valuesPerLong - 1) / valuesPerLong;
- out.writeBytes(in, i1 * Long.BYTES);
- }
-
- public long[] readDataArray() {
- long[] array = new long[copyVarInt()];
- for (int i = 0; i < array.length; i++) {
- array[i] = in.readLong();
- }
-
- return array;
- }
-
- public long[] readNewDataArray(int entries) {
- if (bitsPerBlock == 0) {
- return new long[entries];
- }
-
- char valuesPerLong = (char) (64 / bitsPerBlock);
- int i1 = (entries + valuesPerLong - 1) / valuesPerLong;
- long[] array = new long[i1];
- for (int i = 0; i < i1; i++) {
- array[i] = in.readLong();
- }
-
- return array;
- }
-
- public void writeDataArray(long[] array) {
- for (long l : array) {
- out.writeLong(l);
- }
- }
- }
-}
diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolWrapper.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolWrapper.java
deleted file mode 100644
index cb7b8d05..00000000
--- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolWrapper.java
+++ /dev/null
@@ -1,79 +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 .
- */
-
-package de.steamwar.techhider.legacy;
-
-import net.minecraft.core.SectionPos;
-import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket;
-import net.minecraft.world.level.block.state.BlockState;
-import org.bukkit.entity.Player;
-
-import java.util.ArrayList;
-import java.util.function.BiFunction;
-
-@Deprecated
-public class ProtocolWrapper {
- public static final ProtocolWrapper impl = new ProtocolWrapper();
-
- public BiFunction multiBlockChangeGenerator(TechHider techHider) {
- return (p, packet) -> {
- TechHider.LocationEvaluator locationEvaluator = techHider.getLocationEvaluator();
- SectionPos chunkCoords = packet.sectionPos;
- int chunkX = chunkCoords.getX();
- int chunkY = chunkCoords.getY();
- int chunkZ = chunkCoords.getZ();
- if (locationEvaluator.skipChunkSection(p, chunkX, chunkY, chunkZ)) {
- return packet;
- }
-
- packet = TechHider.multiBlockChangeCloner.apply(packet);
- final short[] oldPos = packet.positions;
- final BlockState[] oldBlocks = packet.states;
- ArrayList poss = new ArrayList<>(oldPos.length);
- ArrayList blocks = new ArrayList<>(oldPos.length);
- for (int i = 0; i < oldPos.length; i++) {
- short pos = oldPos[i];
- BlockState block = oldBlocks[i];
- switch (locationEvaluator.check(p, 16 * chunkX + (pos >> 8 & 0xF), 16 * chunkY + (pos & 0xF), 16 * chunkZ + (pos >> 4 & 0xF))) {
- case SKIP:
- poss.add(pos);
- blocks.add(block);
- break;
- case CHECK:
- poss.add(pos);
- blocks.add(techHider.iBlockDataHidden(block) ? (BlockState) techHider.getObfuscationTarget() : block);
- break;
- default:
- break;
- }
- }
-
- if (blocks.isEmpty()) return null;
-
- short[] newPos = new short[poss.size()];
- for (int i = 0; i < newPos.length; i++) {
- newPos[i] = poss.get(i);
- }
-
- packet.positions = newPos;
- packet.states = blocks.toArray(BlockState[]::new);
- return packet;
- };
- }
-}
diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java
deleted file mode 100644
index 5c9cd169..00000000
--- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java
+++ /dev/null
@@ -1,169 +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 .
- */
-
-package de.steamwar.techhider.legacy;
-
-import com.comphenix.tinyprotocol.TinyProtocol;
-import de.steamwar.core.BlockIds;
-import de.steamwar.core.ProtocolUtils;
-import lombok.Getter;
-import net.minecraft.core.BlockPos;
-import net.minecraft.core.Vec3i;
-import net.minecraft.network.protocol.game.*;
-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.Material;
-import org.bukkit.craftbukkit.util.CraftMagicNumbers;
-import org.bukkit.entity.Player;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.BiFunction;
-import java.util.function.UnaryOperator;
-import java.util.stream.Collectors;
-
-@Deprecated
-public class TechHider {
-
- public static final Class> blockPosition = BlockPos.class;
-
- public static final Class> iBlockData = BlockState.class;
- public static final Class> block = Block.class;
-
- public boolean iBlockDataHidden(BlockState iBlockData) {
- return obfuscateIds.contains(BlockIds.impl.getCombinedId(iBlockData));
- }
-
- public static final Object AIR = CraftMagicNumbers.getBlock(Material.AIR).defaultBlockState();
- public static final int AIR_ID = BlockIds.impl.materialToId(Material.AIR);
-
- private final Map, BiFunction> techhiders = new HashMap<>();
- @Getter
- private final LocationEvaluator locationEvaluator;
- @Getter
- private final Object obfuscationTarget;
- @Getter
- private final int obfuscationTargetId;
- @Getter
- private final Set obfuscateIds;
- @Getter
- private final Set hiddenBlockEntities;
-
- public TechHider(LocationEvaluator locationEvaluator, Material obfuscationTarget, Set obfuscate, Set hiddenBlockEntities) {
- this.locationEvaluator = locationEvaluator;
- this.obfuscateIds = obfuscate.stream().flatMap(m -> BlockIds.impl.materialToAllIds(m).stream()).collect(Collectors.toSet());
- this.hiddenBlockEntities = hiddenBlockEntities;
- this.obfuscationTarget = CraftMagicNumbers.getBlock(obfuscationTarget).defaultBlockState();
- this.obfuscationTargetId = BlockIds.impl.materialToId(obfuscationTarget);
-
- techhiders.put(ClientboundBlockEventPacket.class, (player, o) -> this.blockActionHider(player, (ClientboundBlockEventPacket) o));
- techhiders.put(ClientboundBlockUpdatePacket.class, (player, o) -> this.blockChangeHider(player, (ClientboundBlockUpdatePacket) o));
- techhiders.put(ClientboundBlockEntityDataPacket.class, (player, o) -> this.tileEntityDataHider(player, (ClientboundBlockEntityDataPacket) o));
- techhiders.put(ClientboundSectionBlocksUpdatePacket.class, (BiFunction) ProtocolWrapper.impl.multiBlockChangeGenerator(this));
- techhiders.put(ClientboundLevelChunkWithLightPacket.class, (BiFunction) ChunkHider.impl.chunkHiderGenerator(this));
- techhiders.put(ServerboundUseItemOnPacket.class, (p, packet) -> locationEvaluator.suppressInteractions(p) ? null : packet);
- techhiders.put(ServerboundInteractPacket.class, (p, packet) -> locationEvaluator.suppressInteractions(p) ? null : packet);
-
- }
-
- public void enable() {
- techhiders.forEach((type, playerObjectBiFunction) -> TinyProtocol.instance.addFilter((Class) type, playerObjectBiFunction));
- }
-
- public void disable() {
- techhiders.forEach(TinyProtocol.instance::removeFilter);
- }
-
- public static final UnaryOperator multiBlockChangeCloner = ProtocolUtils.shallowCloneGenerator(ClientboundSectionBlocksUpdatePacket.class, ClientboundSectionBlocksUpdatePacket::new);
-
- private static final UnaryOperator blockChangeCloner = ProtocolUtils.shallowCloneGenerator(ClientboundBlockUpdatePacket.class, ClientboundBlockUpdatePacket::new);
-
- private ClientboundBlockUpdatePacket blockChangeHider(Player p, ClientboundBlockUpdatePacket packet) {
- switch (locationEvaluator.checkBlockPos(p, packet.getPos())) {
- case SKIP:
- return packet;
- case CHECK:
- if (!iBlockDataHidden(packet.blockState)) {
- return packet;
- }
- case HIDE:
- packet = blockChangeCloner.apply(packet);
- packet.blockState = (BlockState) obfuscationTarget;
- return packet;
- case HIDE_AIR:
- default:
- packet = blockChangeCloner.apply(packet);
- packet.blockState = (BlockState) AIR;
- return packet;
- }
- }
-
- private Object blockActionHider(Player p, ClientboundBlockEventPacket packet) {
- if (locationEvaluator.checkBlockPos(p, packet.getPos()) == State.SKIP) {
- return packet;
- }
- return null;
- }
-
- private ClientboundBlockEntityDataPacket tileEntityDataHider(Player p, ClientboundBlockEntityDataPacket packet) {
- switch (locationEvaluator.checkBlockPos(p, packet.getPos())) {
- case SKIP:
- return packet;
- case CHECK:
- if (packet.getType() != BlockEntityType.SIGN) {
- return packet;
- }
- default:
- return null;
- }
- }
-
- public enum State {
- SKIP,
- CHECK,
- HIDE,
- HIDE_AIR
- }
-
- public interface LocationEvaluator {
- default boolean suppressInteractions(Player player) {
- return false;
- }
-
- 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, Vec3i pos) {
- return check(player, pos.getX(), pos.getY(), pos.getZ());
- }
-
- default boolean blockPrecise(Player player, int x, int y, int z) {
- return false;
- }
- }
-}
diff --git a/SpigotCore/SpigotCore_Main/src/spigotcore.accesswidener b/SpigotCore/SpigotCore_Main/src/spigotcore.accesswidener
index 99170241..210a5faf 100644
--- a/SpigotCore/SpigotCore_Main/src/spigotcore.accesswidener
+++ b/SpigotCore/SpigotCore_Main/src/spigotcore.accesswidener
@@ -35,34 +35,3 @@ accessible field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket
mutable field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket yHeadRot B
accessible field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket entityId I
mutable field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket entityId I
-
-# 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;
-mutable field net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket chunkData Lnet/minecraft/network/protocol/game/ClientboundLevelChunkPacketData;
-
-## transitive-extendable means that a public no args constructor is added without any super initialization
-transitive-extendable class net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData
-accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData buffer [B
-mutable field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData buffer [B
-accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData blockEntitiesData Ljava/util/List;
-mutable field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData blockEntitiesData Ljava/util/List;
-
-accessible class net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData$BlockEntityInfo
-accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData$BlockEntityInfo type Lnet/minecraft/world/level/block/entity/BlockEntityType;
-accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData$BlockEntityInfo packedXZ I
-accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData$BlockEntityInfo y 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;
-accessible field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket positions [S
-mutable field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket positions [S
-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;
-
-## 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;
\ No newline at end of file