From 164a7f5826075a0cf910a93cca58f95bb14ceca0 Mon Sep 17 00:00:00 2001 From: Manuel Frohn Date: Wed, 13 May 2026 14:04:38 +0200 Subject: [PATCH 01/52] Replace tinyProtocolLib with newest version --- .../comphenix/tinyprotocol/Reflection.java | 502 +++++++++++++ .../comphenix/tinyprotocol/TinyProtocol.java | 681 +++++++++++++----- 2 files changed, 990 insertions(+), 193 deletions(-) create mode 100644 SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/Reflection.java diff --git a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/Reflection.java b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/Reflection.java new file mode 100644 index 00000000..c9bd2db3 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/Reflection.java @@ -0,0 +1,502 @@ +package com.comphenix.tinyprotocol; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.bukkit.Bukkit; + +/** + * An utility class that simplifies reflection in Bukkit plugins. + * + * @author Kristian + */ +public final class Reflection { + /** + * An interface for invoking a specific constructor. + */ + public interface ConstructorInvoker { + /** + * Invoke a constructor for a specific class. + * + * @param arguments - the arguments to pass to the constructor. + * @return The constructed object. + */ + public Object invoke(Object... arguments); + } + + /** + * An interface for invoking a specific method. + */ + public interface MethodInvoker { + /** + * Invoke a method on a specific target object. + * + * @param target - the target object, or NULL for a static method. + * @param arguments - the arguments to pass to the method. + * @return The return value, or NULL if is void. + */ + public Object invoke(Object target, Object... arguments); + } + + /** + * An interface for retrieving the field content. + * + * @param - field type. + */ + public interface FieldAccessor { + /** + * Retrieve the content of a field. + * + * @param target - the target object, or NULL for a static field. + * @return The value of the field. + */ + public T get(Object target); + + /** + * Set the content of a field. + * + * @param target - the target object, or NULL for a static field. + * @param value - the new value of the field. + */ + public void set(Object target, Object value); + + /** + * Determine if the given object has this field. + * + * @param target - the object to test. + * @return TRUE if it does, FALSE otherwise. + */ + public boolean hasField(Object target); + } + + // Deduce the net.minecraft.server.v* package + private static String OBC_PREFIX = Bukkit.getServer().getClass().getPackage().getName(); + private static String NMS_PREFIX = OBC_PREFIX.replace("org.bukkit.craftbukkit", "net.minecraft.server"); + private static String VERSION = OBC_PREFIX.replace("org.bukkit.craftbukkit", "").replace(".", ""); + + // Variable replacement + private static Pattern MATCH_VARIABLE = Pattern.compile("\\{([^\\}]+)\\}"); + + private Reflection() { + // Seal class + } + + /** + * Retrieve a field accessor for a specific field type and name. + * + * @param target - the target type. + * @param name - the name of the field, or NULL to ignore. + * @param fieldType - a compatible field type. + * @return The field accessor. + */ + public static FieldAccessor getField(Class target, String name, Class fieldType) { + return getField(target, name, fieldType, 0); + } + + /** + * Retrieve a field accessor for a specific field type and name. + * + * @param className - lookup name of the class, see {@link #getClass(String)}. + * @param name - the name of the field, or NULL to ignore. + * @param fieldType - a compatible field type. + * @return The field accessor. + */ + public static FieldAccessor getField(String className, String name, Class fieldType) { + return getField(getClass(className), name, fieldType, 0); + } + + /** + * Retrieve a field accessor for a specific field type and name. + * + * @param target - the target type. + * @param fieldType - a compatible field type. + * @param index - the number of compatible fields to skip. + * @return The field accessor. + */ + public static FieldAccessor getField(Class target, Class fieldType, int index) { + return getField(target, null, fieldType, index); + } + + /** + * Retrieve a field accessor for a specific field type and name. + * + * @param className - lookup name of the class, see {@link #getClass(String)}. + * @param fieldType - a compatible field type. + * @param index - the number of compatible fields to skip. + * @return The field accessor. + */ + public static FieldAccessor getField(String className, Class fieldType, int index) { + return getField(getClass(className), fieldType, index); + } + + // Common method + private static FieldAccessor getField(Class target, String name, Class fieldType, int index) { + for (final Field field : target.getDeclaredFields()) { + if ((name == null || field.getName().equals(name)) && fieldType.isAssignableFrom(field.getType()) && index-- <= 0) { + field.setAccessible(true); + + // A function for retrieving a specific field value + return new FieldAccessor() { + + @Override + @SuppressWarnings("unchecked") + public T get(Object target) { + try { + return (T) field.get(target); + } catch (IllegalAccessException e) { + throw new RuntimeException("Cannot access reflection.", e); + } + } + + @Override + public void set(Object target, Object value) { + try { + field.set(target, value); + } catch (IllegalAccessException e) { + throw new RuntimeException("Cannot access reflection.", e); + } + } + + @Override + public boolean hasField(Object target) { + // target instanceof DeclaringClass + return field.getDeclaringClass().isAssignableFrom(target.getClass()); + } + }; + } + } + + // Search in parent classes + if (target.getSuperclass() != null) + return getField(target.getSuperclass(), name, fieldType, index); + + throw new IllegalArgumentException("Cannot find field with type " + fieldType); + } + + /** + * Retrieves a field with a given type and parameters. This is most useful + * when dealing with Collections. + * + * @param target the target class. + * @param fieldType Type of the field + * @param params Variable length array of type parameters + * @return The field + * + * @throws IllegalArgumentException If the field cannot be found + */ + public static Field getParameterizedField(Class target, Class fieldType, Class... params) { + for (Field field : target.getDeclaredFields()) { + if (field.getType().equals(fieldType)) { + Type type = field.getGenericType(); + if (type instanceof ParameterizedType) { + if (Arrays.equals(((ParameterizedType) type).getActualTypeArguments(), params)) + return field; + } + } + } + + throw new IllegalArgumentException("Unable to find a field with type " + fieldType + " and params " + Arrays.toString(params)); + } + + /** + * Search for the first publicly and privately defined method of the given name and parameter count. + * + * @param className - lookup name of the class, see {@link #getClass(String)}. + * @param methodName - the method name, or NULL to skip. + * @param params - the expected parameters. + * @return An object that invokes this specific method. + * @throws IllegalStateException If we cannot find this method. + */ + public static MethodInvoker getMethod(String className, String methodName, Class... params) { + return getTypedMethod(getClass(className), methodName, null, params); + } + + /** + * Search for the first publicly and privately defined method of the given name and parameter count. + * + * @param clazz - a class to start with. + * @param methodName - the method name, or NULL to skip. + * @param params - the expected parameters. + * @return An object that invokes this specific method. + * @throws IllegalStateException If we cannot find this method. + */ + public static MethodInvoker getMethod(Class clazz, String methodName, Class... params) { + return getTypedMethod(clazz, methodName, null, params); + } + + /** + * Search for the first publicly and privately defined method of the given name and parameter count. + * + * @param clazz - a class to start with. + * @param methodName - the method name, or NULL to skip. + * @param returnType - the expected return type, or NULL to ignore. + * @param params - the expected parameters. + * @return An object that invokes this specific method. + * @throws IllegalStateException If we cannot find this method. + */ + public static MethodInvoker getTypedMethod(Class clazz, String methodName, Class returnType, Class... params) { + for (final Method method : clazz.getDeclaredMethods()) { + if ((methodName == null || method.getName().equals(methodName)) + && (returnType == null || method.getReturnType().equals(returnType)) + && Arrays.equals(method.getParameterTypes(), params)) { + method.setAccessible(true); + + return new MethodInvoker() { + + @Override + public Object invoke(Object target, Object... arguments) { + try { + return method.invoke(target, arguments); + } catch (Exception e) { + throw new RuntimeException("Cannot invoke method " + method, e); + } + } + + }; + } + } + + // Search in every superclass + if (clazz.getSuperclass() != null) + return getMethod(clazz.getSuperclass(), methodName, params); + + throw new IllegalStateException(String.format("Unable to find method %s (%s).", methodName, Arrays.asList(params))); + } + + /** + * Search for the first publically and privately defined constructor of the given name and parameter count. + * + * @param className - lookup name of the class, see {@link #getClass(String)}. + * @param params - the expected parameters. + * @return An object that invokes this constructor. + * @throws IllegalStateException If we cannot find this method. + */ + public static ConstructorInvoker getConstructor(String className, Class... params) { + return getConstructor(getClass(className), params); + } + + /** + * Search for the first publically and privately defined constructor of the given name and parameter count. + * + * @param clazz - a class to start with. + * @param params - the expected parameters. + * @return An object that invokes this constructor. + * @throws IllegalStateException If we cannot find this method. + */ + public static ConstructorInvoker getConstructor(Class clazz, Class... params) { + for (final Constructor constructor : clazz.getDeclaredConstructors()) { + if (Arrays.equals(constructor.getParameterTypes(), params)) { + constructor.setAccessible(true); + + return new ConstructorInvoker() { + + @Override + public Object invoke(Object... arguments) { + try { + return constructor.newInstance(arguments); + } catch (Exception e) { + throw new RuntimeException("Cannot invoke constructor " + constructor, e); + } + } + + }; + } + } + + throw new IllegalStateException(String.format("Unable to find constructor for %s (%s).", clazz, Arrays.asList(params))); + } + + /** + * Retrieve a class from its full name, without knowing its type on compile time. + *

+ * This is useful when looking up fields by a NMS or OBC type. + *

+ * + * @see {@link #getClass()} for more information. + * @param lookupName - the class name with variables. + * @return The class. + */ + public static Class getUntypedClass(String lookupName) { + @SuppressWarnings({ "rawtypes", "unchecked" }) + Class clazz = (Class) getClass(lookupName); + return clazz; + } + + /** + * Retrieve a class from its full name with alternatives, without knowing its type on compile time. + *

+ * This is useful when looking up fields by a NMS or OBC type. + *

+ * + * @see {@link #getClass()} for more information. + * @param lookupName - the class name with variables. + * @param aliases - alternative names for this class. + * @return The class. + */ + public static Class getUntypedClass(String lookupName, String... aliases) { + @SuppressWarnings({ "rawtypes", "unchecked" }) + Class clazz = (Class) getClass(lookupName, aliases); + return clazz; + } + + /** + * Retrieve a class from its full name. + *

+ * Strings enclosed with curly brackets - such as {TEXT} - will be replaced according to the following table: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
VariableContent
{nms}Actual package name of net.minecraft.server.VERSION
{obc}Actual pacakge name of org.bukkit.craftbukkit.VERSION
{version}The current Minecraft package VERSION, if any.
+ * + * @param lookupName - the class name with variables. + * @return The looked up class. + * @throws IllegalArgumentException If a variable or class could not be found. + */ + public static Class getClass(String lookupName) { + return getCanonicalClass(expandVariables(lookupName)); + } + + /** + * Retrieve the first class that matches the full class name. + *

+ * Strings enclosed with curly brackets - such as {TEXT} - will be replaced according to the following table: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
VariableContent
{nms}Actual package name of net.minecraft.server.VERSION
{obc}Actual pacakge name of org.bukkit.craftbukkit.VERSION
{version}The current Minecraft package VERSION, if any.
+ * + * @param lookupName - the class name with variables. + * @param aliases - alternative names for this class. + * @return Class object. + * @throws RuntimeException If we are unable to find any of the given classes. + */ + public static Class getClass(String lookupName, String... aliases) { + try { + // Try the main class first + return getClass(lookupName); + } catch (RuntimeException e) { + Class success = null; + + // Try every alias too + for (String alias : aliases) { + try { + success = getClass(alias); + break; + } catch (RuntimeException e1) { + // e1.printStackTrace(); + } + } + + if (success != null) { + return success; + } else { + // Hack failed + throw new RuntimeException(String.format("Unable to find %s (%s)", lookupName, String.join(",", aliases))); + } + } + } + + /** + * Retrieve a class in the net.minecraft.server.VERSION.* package. + * + * @param name - the name of the class, excluding the package. + * @throws IllegalArgumentException If the class doesn't exist. + */ + public static Class getMinecraftClass(String name) { + return getCanonicalClass(NMS_PREFIX + "." + name); + } + + /** + * Retrieve a class in the org.bukkit.craftbukkit.VERSION.* package. + * + * @param name - the name of the class, excluding the package. + * @throws IllegalArgumentException If the class doesn't exist. + */ + public static Class getCraftBukkitClass(String name) { + return getCanonicalClass(OBC_PREFIX + "." + name); + } + + /** + * Retrieve a class by its canonical name. + * + * @param canonicalName - the canonical name. + * @return The class. + */ + private static Class getCanonicalClass(String canonicalName) { + try { + return Class.forName(canonicalName); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException("Cannot find " + canonicalName, e); + } + } + + /** + * Expand variables such as "{nms}" and "{obc}" to their corresponding packages. + * + * @param name - the full name of the class. + * @return The expanded string. + */ + private static String expandVariables(String name) { + StringBuffer output = new StringBuffer(); + Matcher matcher = MATCH_VARIABLE.matcher(name); + + while (matcher.find()) { + String variable = matcher.group(1); + String replacement = ""; + + // Expand all detected variables + if ("nms".equalsIgnoreCase(variable)) + replacement = NMS_PREFIX; + else if ("obc".equalsIgnoreCase(variable)) + replacement = OBC_PREFIX; + else if ("version".equalsIgnoreCase(variable)) + replacement = VERSION; + else + throw new IllegalArgumentException("Unknown variable: " + variable); + + // Assume the expanded variables are all packages, and append a dot + if (replacement.length() > 0 && matcher.end() < name.length() && name.charAt(matcher.end()) != '.') + replacement += "."; + matcher.appendReplacement(output, Matcher.quoteReplacement(replacement)); + } + + matcher.appendTail(output); + return output.toString(); + } +} \ No newline at end of file diff --git a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java index cb4aca06..ac626e47 100644 --- a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java +++ b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java @@ -1,29 +1,24 @@ -/* - * 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 com.comphenix.tinyprotocol; -import de.steamwar.Reflection; -import de.steamwar.Reflection.Field; -import de.steamwar.core.Core; -import io.netty.channel.*; -import lombok.Getter; +import io.netty.channel.Channel; +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.ChannelPromise; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; + import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -31,217 +26,517 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerLoginEvent; -import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.server.PluginDisableEvent; import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; -import java.util.*; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.function.BiFunction; -import java.util.logging.Level; +import com.comphenix.tinyprotocol.Reflection.FieldAccessor; +import com.comphenix.tinyprotocol.Reflection.MethodInvoker; +import com.google.common.collect.Lists; +import com.google.common.collect.MapMaker; +import com.mojang.authlib.GameProfile; -public class TinyProtocol implements Listener { +/** + * Represents a very tiny alternative to ProtocolLib. + *

+ * It now supports intercepting packets during login and status ping (such as OUT_SERVER_PING)! + * + * @author Kristian + */ +public abstract class TinyProtocol { + private static final AtomicInteger ID = new AtomicInteger(0); - private static final Class craftServer = Reflection.getClass("org.bukkit.craftbukkit.CraftServer"); - private static final Class dedicatedPlayerList = Reflection.getClass("net.minecraft.server.dedicated.DedicatedPlayerList"); - private static final Field getPlayerList = Reflection.getField(craftServer, dedicatedPlayerList, 0); - private static final Class playerList = Reflection.getClass("net.minecraft.server.players.PlayerList"); - private static final Class minecraftServer = Reflection.getClass("net.minecraft.server.MinecraftServer"); - private static final Field getMinecraftServer = Reflection.getField(playerList, minecraftServer, 0); - public static final Class serverConnection = Reflection.getClass("net.minecraft.server.network.ServerConnectionListener"); - private static final Field getServerConnection = Reflection.getField(minecraftServer, serverConnection, 0); - public static Object getServerConnection(Plugin plugin) { - return getServerConnection.get(getMinecraftServer.get(getPlayerList.get(plugin.getServer()))); - } - private static final Class networkManager = Reflection.getClass("net.minecraft.network.NetworkManager"); - public static final Field networkManagers = Reflection.getField(serverConnection, List.class, 0, networkManager); + // Required Minecraft classes + private static final Class entityPlayerClass = Reflection.getClass("{nms}.EntityPlayer", "net.minecraft.server.level.EntityPlayer"); + private static final Class playerConnectionClass = Reflection.getClass("{nms}.PlayerConnection", "net.minecraft.server.network.PlayerConnection"); + private static final Class networkManagerClass = Reflection.getClass("{nms}.NetworkManager", "net.minecraft.network.NetworkManager"); - private static final String HANDLER_NAME = "tiny-steamwar"; - public static final TinyProtocol instance = new TinyProtocol(Core.getInstance()); + // Used in order to lookup a channel + private static final MethodInvoker getPlayerHandle = Reflection.getMethod("{obc}.entity.CraftPlayer", "getHandle"); + private static final FieldAccessor getConnection = Reflection.getField(entityPlayerClass, null, playerConnectionClass); + private static final FieldAccessor getManager = Reflection.getField(playerConnectionClass, null, networkManagerClass); + private static final FieldAccessor getChannel = Reflection.getField(networkManagerClass, Channel.class, 0); - public static void init() { - //enforce init - } + // Looking up ServerConnection + private static final Class minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer", "net.minecraft.server.MinecraftServer"); + private static final Class serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection", "net.minecraft.server.network.ServerConnection"); + private static final FieldAccessor getMinecraftServer = Reflection.getField("{obc}.CraftServer", minecraftServerClass, 0); + private static final FieldAccessor getServerConnection = Reflection.getField(minecraftServerClass, serverConnectionClass, 0); - private final Plugin plugin; - private final List connections; - private boolean closed; + // Packets we have to intercept + private static final Class PACKET_LOGIN_IN_START = Reflection.getClass("{nms}.PacketLoginInStart", "net.minecraft.network.protocol.login.PacketLoginInStart"); + private static final FieldAccessor getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START, GameProfile.class, 0); - private final Map, List>> packetFilters = new HashMap<>(); - @Getter - private final Map playerInterceptors = new HashMap<>(); + // Speedup channel lookup + private Map channelLookup = new MapMaker().weakValues().makeMap(); + private Listener listener; - @Override - public String toString() { - return "TinyProtocol{" + - "plugin=" + plugin + - ", connections=" + connections + - ", closed=" + closed + - ", packetFilters=" + packetFilters + - ", playerInterceptors=" + playerInterceptors + - '}'; - } + // Channels that have already been removed + private Set uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap()); - private TinyProtocol(final Plugin plugin) { - this.plugin = plugin; - this.connections = networkManagers.get(getServerConnection(plugin)); + // List of network markers + private List networkManagers; - plugin.getServer().getPluginManager().registerEvents(this, plugin); + // Injected channel handlers + private List serverChannels = new ArrayList<>(); + private ChannelInboundHandlerAdapter serverChannelHandler; + private ChannelInitializer beginInitProtocol; + private ChannelInitializer endInitProtocol; - for (Player player : plugin.getServer().getOnlinePlayers()) { - new PacketInterceptor(player); - } - } + // Current handler name + private String handlerName; - @EventHandler(priority = EventPriority.LOWEST) - public void onPlayerLogin(PlayerLoginEvent e) { - plugin.getLogger().info("Creating PacketInterceptor for: " + e.getPlayer().getName() + " (" + closed + ")"); - if(closed) - return; - new PacketInterceptor(e.getPlayer()); - } + protected volatile boolean closed; + protected Plugin plugin; - @EventHandler(priority = EventPriority.MONITOR) - public void onPlayerDisconnect(PlayerQuitEvent e) { - getInterceptor(e.getPlayer()).ifPresent(PacketInterceptor::close); - } + /** + * Construct a new instance of TinyProtocol, and start intercepting packets for all connected clients and future clients. + *

+ * You can construct multiple instances per plugin. + * + * @param plugin - the plugin. + */ + public TinyProtocol(final Plugin plugin) { + this.plugin = plugin; - @EventHandler - public void onPluginDisable(PluginDisableEvent e) { - if (e.getPlugin().equals(plugin)) { - close(); - } - } + // Compute handler name + this.handlerName = getHandlerName(); - public void addFilter(Class packetType, BiFunction filter) { - packetFilters.computeIfAbsent(packetType, c -> new CopyOnWriteArrayList<>()).add(filter); - } + // Prepare existing players + registerBukkitEvents(); - public void removeFilter(Class packetType, BiFunction filter) { - packetFilters.getOrDefault(packetType, Collections.emptyList()).remove(filter); - } + try { + registerChannelHandler(); + registerPlayers(plugin); + } catch (IllegalArgumentException ex) { + // Damn you, late bind + plugin.getLogger().info("[TinyProtocol] Delaying server channel injection due to late bind."); - public void sendPacket(Player player, Object packet) { - getInterceptor(player).ifPresent(i -> i.sendPacket(packet)); - } + new BukkitRunnable() { + @Override + public void run() { + registerChannelHandler(); + registerPlayers(plugin); + plugin.getLogger().info("[TinyProtocol] Late bind injection successful."); + } + }.runTask(plugin); + } + } - public void receivePacket(Player player, Object packet) { - getInterceptor(player).ifPresent(i -> i.receivePacket(packet)); - } + private void createServerChannelHandler() { + // Handle connected channels + endInitProtocol = new ChannelInitializer() { - public final void close() { - plugin.getLogger().log(Level.INFO, "Closing PacketInterceptor", new Exception("Stacktrace")); + @Override + protected void initChannel(Channel channel) throws Exception { + try { + // This can take a while, so we need to stop the main thread from interfering + synchronized (networkManagers) { + // Stop injecting channels + if (!closed) { + channel.eventLoop().submit(() -> injectChannelInternal(channel)); + } + } + } catch (Exception e) { + plugin.getLogger().log(Level.SEVERE, "Cannot inject incomming channel " + channel, e); + } + } - if(closed) - return; - closed = true; + }; - HandlerList.unregisterAll(this); + // This is executed before Minecraft's channel handler + beginInitProtocol = new ChannelInitializer() { - for (Player player : plugin.getServer().getOnlinePlayers()) { - getInterceptor(player).ifPresent(PacketInterceptor::close); - } - } + @Override + protected void initChannel(Channel channel) throws Exception { + channel.pipeline().addLast(endInitProtocol); + } - private Optional getInterceptor(Player player) { - synchronized (playerInterceptors) { - return Optional.ofNullable(playerInterceptors.get(player)); - } - } + }; - private static final Field getChannel = Reflection.getField(networkManager, Channel.class, 0); - private static final Field getUUID = Reflection.getField(networkManager, UUID.class, 0); + serverChannelHandler = new ChannelInboundHandlerAdapter() { + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + Channel channel = (Channel) msg; - public final class PacketInterceptor extends ChannelDuplexHandler { - private final Player player; - @Getter - private final Channel channel; + // Prepare to initialize ths channel + channel.pipeline().addFirst(beginInitProtocol); + ctx.fireChannelRead(msg); + } - private PacketInterceptor(Player player) { - this.player = player; + }; + } - channel = connections.stream().filter(connection -> player.getUniqueId().equals(getUUID.get(connection))).map(getChannel::get).filter(Channel::isActive).findAny().orElseThrow(() -> { - Bukkit.getScheduler().runTask(plugin, () -> player.kickPlayer("Connection failure.")); - return new SecurityException("Could not find channel for player " + player.getName()); - }); + /** + * Register bukkit events. + */ + private void registerBukkitEvents() { + listener = new Listener() { - if(!channel.isActive()) - return; + @EventHandler(priority = EventPriority.LOWEST) + public final void onPlayerLogin(PlayerLoginEvent e) { + if (closed) + return; - synchronized (playerInterceptors) { - playerInterceptors.put(player, this); - } - plugin.getLogger().info("Adding Techhider for: " + player.getName()); + Channel channel = getChannel(e.getPlayer()); - try { - channel.pipeline().addBefore("packet_handler", HANDLER_NAME, this); - } catch (IllegalArgumentException | NoSuchElementException e) { - Bukkit.getScheduler().runTask(plugin, () -> player.kickPlayer("Connection failure.")); - throw new SecurityException(e); - } + // Don't inject players that have been explicitly uninjected + if (!uninjectedChannels.contains(channel)) { + injectPlayer(e.getPlayer()); + } + } + + @EventHandler + public final void onPluginDisable(PluginDisableEvent e) { + if (e.getPlugin().equals(plugin)) { + close(); + } + } + + }; + + plugin.getServer().getPluginManager().registerEvents(listener, plugin); + } + + @SuppressWarnings("unchecked") + private void registerChannelHandler() { + Object mcServer = getMinecraftServer.get(Bukkit.getServer()); + Object serverConnection = getServerConnection.get(mcServer); + boolean looking = true; + + try { + Field field = Reflection.getParameterizedField(serverConnectionClass, List.class, networkManagerClass); + field.setAccessible(true); + + networkManagers = (List) field.get(serverConnection); + } catch (Exception ex) { + plugin.getLogger().info("Encountered an exception checking list fields" + ex); + MethodInvoker method = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass); + + networkManagers = (List) method.invoke(null, serverConnection); } - private void sendPacket(Object packet) { - channel.pipeline().writeAndFlush(packet); - } + if (networkManagers == null) { + throw new IllegalArgumentException("Failed to obtain list of network managers"); + } + // We need to synchronize against this list + createServerChannelHandler(); - private void receivePacket(Object packet) { - channel.pipeline().context("encoder").fireChannelRead(packet); - } + // Find the correct list, or implicitly throw an exception + for (int i = 0; looking; i++) { + List list = Reflection.getField(serverConnection.getClass(), List.class, i).get(serverConnection); - private void close() { - if(channel.isActive()) { - channel.eventLoop().execute(() -> { - try { - channel.pipeline().remove(HANDLER_NAME); - } catch (NoSuchElementException e) { - // ignore - } - }); - } + for (Object item : list) { + if (!ChannelFuture.class.isInstance(item)) + break; - synchronized (playerInterceptors) { - playerInterceptors.remove(player, this); - } - } + // Channel future that contains the server connection + Channel serverChannel = ((ChannelFuture) item).channel(); - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - try { - msg = filterPacket(player, msg); - } catch (Exception e) { - plugin.getLogger().log(Level.SEVERE, "Error during incoming packet processing", e); - } + serverChannels.add(serverChannel); + serverChannel.pipeline().addFirst(serverChannelHandler); + looking = false; + } + } + } - if (msg != null) { - super.channelRead(ctx, msg); - } - } + private void unregisterChannelHandler() { + if (serverChannelHandler == null) + return; - @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - try { - msg = filterPacket(player, msg); - } catch (Exception e) { - plugin.getLogger().log(Level.SEVERE, "Error during outgoing packet processing", e); - } + for (Channel serverChannel : serverChannels) { + final ChannelPipeline pipeline = serverChannel.pipeline(); - if (msg != null) { - super.write(ctx, msg, promise); - } - } + // Remove channel handler + serverChannel.eventLoop().execute(new Runnable() { - private Object filterPacket(Player player, Object packet) { - List> filters = packetFilters.getOrDefault(packet.getClass(), Collections.emptyList()); + @Override + public void run() { + try { + pipeline.remove(serverChannelHandler); + } catch (NoSuchElementException e) { + // That's fine + } + } - for(BiFunction filter : filters) { - packet = filter.apply(player, packet); + }); + } + } - if(packet == null) - break; - } + private void registerPlayers(Plugin plugin) { + for (Player player : plugin.getServer().getOnlinePlayers()) { + injectPlayer(player); + } + } - return packet; - } - } -} + /** + * Invoked when the server is starting to send a packet to a player. + *

+ * Note that this is not executed on the main thread. + * + * @param receiver - the receiving player, NULL for early login/status packets. + * @param channel - the channel that received the packet. Never NULL. + * @param packet - the packet being sent. + * @return The packet to send instead, or NULL to cancel the transmission. + */ + public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) { + return packet; + } + + /** + * Invoked when the server has received a packet from a given player. + *

+ * Use {@link Channel#remoteAddress()} to get the remote address of the client. + * + * @param sender - the player that sent the packet, NULL for early login/status packets. + * @param channel - channel that received the packet. Never NULL. + * @param packet - the packet being received. + * @return The packet to recieve instead, or NULL to cancel. + */ + public Object onPacketInAsync(Player sender, Channel channel, Object packet) { + return packet; + } + + /** + * Send a packet to a particular player. + *

+ * Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet. + * + * @param player - the destination player. + * @param packet - the packet to send. + */ + public void sendPacket(Player player, Object packet) { + sendPacket(getChannel(player), packet); + } + + /** + * Send a packet to a particular client. + *

+ * Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet. + * + * @param channel - client identified by a channel. + * @param packet - the packet to send. + */ + public void sendPacket(Channel channel, Object packet) { + channel.pipeline().writeAndFlush(packet); + } + + /** + * Pretend that a given packet has been received from a player. + *

+ * Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet. + * + * @param player - the player that sent the packet. + * @param packet - the packet that will be received by the server. + */ + public void receivePacket(Player player, Object packet) { + receivePacket(getChannel(player), packet); + } + + /** + * Pretend that a given packet has been received from a given client. + *

+ * Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet. + * + * @param channel - client identified by a channel. + * @param packet - the packet that will be received by the server. + */ + public void receivePacket(Channel channel, Object packet) { + channel.pipeline().context("encoder").fireChannelRead(packet); + } + + /** + * Retrieve the name of the channel injector, default implementation is "tiny-" + plugin name + "-" + a unique ID. + *

+ * Note that this method will only be invoked once. It is no longer necessary to override this to support multiple instances. + * + * @return A unique channel handler name. + */ + protected String getHandlerName() { + return "tiny-" + plugin.getName() + "-" + ID.incrementAndGet(); + } + + /** + * Add a custom channel handler to the given player's channel pipeline, allowing us to intercept sent and received packets. + *

+ * This will automatically be called when a player has logged in. + * + * @param player - the player to inject. + */ + public void injectPlayer(Player player) { + injectChannelInternal(getChannel(player)).player = player; + } + + /** + * Add a custom channel handler to the given channel. + * + * @param channel - the channel to inject. + * @return The intercepted channel, or NULL if it has already been injected. + */ + public void injectChannel(Channel channel) { + injectChannelInternal(channel); + } + + /** + * Add a custom channel handler to the given channel. + * + * @param channel - the channel to inject. + * @return The packet interceptor. + */ + private PacketInterceptor injectChannelInternal(Channel channel) { + try { + PacketInterceptor interceptor = (PacketInterceptor) channel.pipeline().get(handlerName); + + // Inject our packet interceptor + if (interceptor == null) { + interceptor = new PacketInterceptor(); + channel.pipeline().addBefore("packet_handler", handlerName, interceptor); + uninjectedChannels.remove(channel); + } + + return interceptor; + } catch (IllegalArgumentException e) { + // Try again + return (PacketInterceptor) channel.pipeline().get(handlerName); + } + } + + /** + * Retrieve the Netty channel associated with a player. This is cached. + * + * @param player - the player. + * @return The Netty channel. + */ + public Channel getChannel(Player player) { + Channel channel = channelLookup.get(player.getName()); + + // Lookup channel again + if (channel == null) { + Object connection = getConnection.get(getPlayerHandle.invoke(player)); + Object manager = getManager.get(connection); + + channelLookup.put(player.getName(), channel = getChannel.get(manager)); + } + + return channel; + } + + /** + * Uninject a specific player. + * + * @param player - the injected player. + */ + public void uninjectPlayer(Player player) { + uninjectChannel(getChannel(player)); + } + + /** + * Uninject a specific channel. + *

+ * This will also disable the automatic channel injection that occurs when a player has properly logged in. + * + * @param channel - the injected channel. + */ + public void uninjectChannel(final Channel channel) { + // No need to guard against this if we're closing + if (!closed) { + uninjectedChannels.add(channel); + } + + // See ChannelInjector in ProtocolLib, line 590 + channel.eventLoop().execute(new Runnable() { + + @Override + public void run() { + channel.pipeline().remove(handlerName); + } + + }); + } + + /** + * Determine if the given player has been injected by TinyProtocol. + * + * @param player - the player. + * @return TRUE if it is, FALSE otherwise. + */ + public boolean hasInjected(Player player) { + return hasInjected(getChannel(player)); + } + + /** + * Determine if the given channel has been injected by TinyProtocol. + * + * @param channel - the channel. + * @return TRUE if it is, FALSE otherwise. + */ + public boolean hasInjected(Channel channel) { + return channel.pipeline().get(handlerName) != null; + } + + /** + * Cease listening for packets. This is called automatically when your plugin is disabled. + */ + public final void close() { + if (!closed) { + closed = true; + + // Remove our handlers + for (Player player : plugin.getServer().getOnlinePlayers()) { + uninjectPlayer(player); + } + + // Clean up Bukkit + HandlerList.unregisterAll(listener); + unregisterChannelHandler(); + } + } + + /** + * Channel handler that is inserted into the player's channel pipeline, allowing us to intercept sent and received packets. + * + * @author Kristian + */ + private final class PacketInterceptor extends ChannelDuplexHandler { + // Updated by the login event + public volatile Player player; + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + // Intercept channel + final Channel channel = ctx.channel(); + handleLoginStart(channel, msg); + + try { + msg = onPacketInAsync(player, channel, msg); + } catch (Exception e) { + plugin.getLogger().log(Level.SEVERE, "Error in onPacketInAsync().", e); + } + + if (msg != null) { + super.channelRead(ctx, msg); + } + } + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + try { + msg = onPacketOutAsync(player, ctx.channel(), msg); + } catch (Exception e) { + plugin.getLogger().log(Level.SEVERE, "Error in onPacketOutAsync().", e); + } + + if (msg != null) { + super.write(ctx, msg, promise); + } + } + + private void handleLoginStart(Channel channel, Object packet) { + if (PACKET_LOGIN_IN_START.isInstance(packet)) { + GameProfile profile = getGameProfile.get(packet); + channelLookup.put(profile.getName(), channel); + } + } + } +} \ No newline at end of file From 744dc67ca929a2c815f196a87d2a5882e1a8da51 Mon Sep 17 00:00:00 2001 From: Manuel Frohn Date: Wed, 13 May 2026 15:33:08 +0200 Subject: [PATCH 02/52] Move over identified packages from old impl --- SpigotCore/SpigotCore_Main/build.gradle.kts | 1 + .../steamwar/techhider/TechHiderUpdated.java | 127 ++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java diff --git a/SpigotCore/SpigotCore_Main/build.gradle.kts b/SpigotCore/SpigotCore_Main/build.gradle.kts index 85ff16cc..5972ebba 100644 --- a/SpigotCore/SpigotCore_Main/build.gradle.kts +++ b/SpigotCore/SpigotCore_Main/build.gradle.kts @@ -38,6 +38,7 @@ dependencies { compileOnly(libs.netty) compileOnly(libs.authlib) compileOnly(libs.fastutil) + compileOnly(libs.nms21) implementation(libs.anvilgui) } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java new file mode 100644 index 00000000..097de47d --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java @@ -0,0 +1,127 @@ +package de.steamwar.techhider; + +import java.util.Map; +import java.util.Set; +import java.util.function.BiFunction; + +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +import com.comphenix.tinyprotocol.TinyProtocol; + +import io.netty.channel.Channel; +import net.minecraft.network.PacketListener; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.common.ClientCommonPacketListener; +import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; +import net.minecraft.network.protocol.common.ClientboundKeepAlivePacket; +import net.minecraft.network.protocol.common.ClientboundPingPacket; +import net.minecraft.network.protocol.game.ClientboundBlockChangedAckPacket; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.network.protocol.game.ClientboundBlockEventPacket; +import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; +import net.minecraft.network.protocol.game.ClientboundBossEventPacket; +import net.minecraft.network.protocol.game.ClientboundClearTitlesPacket; +import net.minecraft.network.protocol.game.ClientboundCommandSuggestionsPacket; +import net.minecraft.network.protocol.game.ClientboundCommandsPacket; +import net.minecraft.network.protocol.game.ClientboundDisguisedChatPacket; +import net.minecraft.network.protocol.game.ClientboundGameEventPacket; +import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; +import net.minecraft.network.protocol.game.ClientboundPlayerAbilitiesPacket; +import net.minecraft.network.protocol.game.ClientboundPlayerChatPacket; +import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket; +import net.minecraft.network.protocol.game.ClientboundServerDataPacket; +import net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket; +import net.minecraft.network.protocol.game.ClientboundSetExperiencePacket; +import net.minecraft.network.protocol.game.ClientboundSetHealthPacket; +import net.minecraft.network.protocol.game.ClientboundSetSubtitleTextPacket; +import net.minecraft.network.protocol.game.ClientboundSetTimePacket; +import net.minecraft.network.protocol.game.ClientboundSetTitleTextPacket; +import net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket; +import net.minecraft.network.protocol.game.ClientboundSystemChatPacket; +import net.minecraft.network.protocol.game.ClientboundTabListPacket; +import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket; +import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; +import net.minecraft.network.protocol.login.ClientboundHelloPacket; +import net.minecraft.network.protocol.login.ClientboundLoginCompressionPacket; +import net.minecraft.network.protocol.login.ClientboundLoginFinishedPacket; + +/** + * 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 TechHiderUpdated { + private final Set>> bypassingPackets; + private final Map>, BiFunction, Packet>> packetProcessors; + private final TinyProtocol interceptor; + + + public TechHiderUpdated(Plugin plugin) { + this.bypassingPackets = Set.of( + // --- Handshake & Login Protocol --- + // These must be whitelisted to allow the player to actually reach the 'Play' + // state + ClientboundHelloPacket.class, // Encryption request + ClientboundLoginFinishedPacket.class, // Login Success + ClientboundLoginCompressionPacket.class, // Set Compression + ClientboundCustomQueryPacket.class, // Velocity/Bungee/Mod identity checks + + // --- Protocol & Connection --- + ClientboundKeepAlivePacket.class, + ClientboundPingPacket.class, + ClientboundCustomPayloadPacket.class, + ClientboundServerDataPacket.class, + + // --- UI & Communication --- + ClientboundSystemChatPacket.class, + ClientboundDisguisedChatPacket.class, + ClientboundBossEventPacket.class, + ClientboundTabListPacket.class, + ClientboundSetTitleTextPacket.class, + ClientboundSetSubtitleTextPacket.class, + ClientboundSetTitlesAnimationPacket.class, + ClientboundClearTitlesPacket.class, + ClientboundPlayerChatPacket.class, + + // --- Player Stats & Logic --- + ClientboundSetExperiencePacket.class, + ClientboundSetHealthPacket.class, + ClientboundPlayerAbilitiesPacket.class, + ClientboundUpdateAttributesPacket.class, + ClientboundCommandSuggestionsPacket.class, + + // --- World Metadata (Non-Structural) --- + ClientboundSetTimePacket.class, + ClientboundSetDefaultSpawnPositionPacket.class, + ClientboundGameEventPacket.class, // Weather, Gamemode changes, etc. + ClientboundCommandsPacket.class // Command tree for tab-complete + ); + + BiFunction, Packet> tossPacket = (p, packet) -> null; + + this.packetProcessors = Map.of( + ClientboundBlockEventPacket.class, tossPacket, + ClientboundBlockUpdatePacket.class, tossPacket, + ClientboundBlockEntityDataPacket.class, tossPacket, + ClientboundSectionBlocksUpdatePacket.class, tossPacket, + ClientboundLevelChunkWithLightPacket.class, tossPacket + ); + + this.interceptor = new TinyProtocol(plugin) { + @Override + public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) { + if (bypassingPackets.stream().anyMatch(clazz -> clazz.isInstance(packet))) { + return packet; + } + else if (packetProcessors.containsKey(packet.getClass())) { + return packetProcessors.get(packet.getClass()).apply(receiver, (Packet) packet); + } + else { + return null; + } + + } + }; + } +} From d3aad6a198018644743fed30a9e878585dc761a8 Mon Sep 17 00:00:00 2001 From: Manuel Frohn Date: Wed, 13 May 2026 16:24:45 +0200 Subject: [PATCH 03/52] Move over signle block event package processing --- .../src/de/steamwar/techhider/TechHider.java | 45 -------------- .../steamwar/techhider/TechHiderUpdated.java | 58 +++++++++++++++++-- 2 files changed, 52 insertions(+), 51 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 46e53bcf..0428c78c 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -104,51 +104,6 @@ public class TechHider { public static final Class multiBlockChangePacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket"); public static final UnaryOperator multiBlockChangeCloner = ProtocolUtils.shallowCloneGenerator(TechHider.multiBlockChangePacket); - private static final Class blockChangePacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket"); - private static final Function blockChangeCloner = ProtocolUtils.shallowCloneGenerator(blockChangePacket); - private static final Reflection.Field blockChangePosition = Reflection.getField(blockChangePacket, blockPosition, 0); - private static final Reflection.Field 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(blockChangeBlockData.get(packet))) - return packet; - case HIDE: - packet = blockChangeCloner.apply(packet); - blockChangeBlockData.set(packet, obfuscationTarget); - return packet; - case HIDE_AIR: - default: - packet = blockChangeCloner.apply(packet); - blockChangeBlockData.set(packet, AIR); - return packet; - } - } - - private static final Class blockActionPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundBlockEventPacket"); - private static final Reflection.Field 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("net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket"); - private static final Reflection.Field 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; - } - } - public enum State { SKIP, CHECK, diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java index 097de47d..b5def8e9 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java @@ -10,13 +10,12 @@ import org.bukkit.plugin.Plugin; import com.comphenix.tinyprotocol.TinyProtocol; import io.netty.channel.Channel; +import net.minecraft.core.BlockPos; import net.minecraft.network.PacketListener; import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.common.ClientCommonPacketListener; import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; import net.minecraft.network.protocol.common.ClientboundKeepAlivePacket; import net.minecraft.network.protocol.common.ClientboundPingPacket; -import net.minecraft.network.protocol.game.ClientboundBlockChangedAckPacket; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.network.protocol.game.ClientboundBlockEventPacket; import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; @@ -45,6 +44,8 @@ import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; import net.minecraft.network.protocol.login.ClientboundHelloPacket; import net.minecraft.network.protocol.login.ClientboundLoginCompressionPacket; import net.minecraft.network.protocol.login.ClientboundLoginFinishedPacket; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; /** * The TechHider follows the default-deny security principle, @@ -55,9 +56,13 @@ public class TechHiderUpdated { private final Set>> bypassingPackets; private final Map>, BiFunction, Packet>> packetProcessors; private final TinyProtocol interceptor; + private final Set blockIdsToObfuscate; + private final BlockState blockToObfuscateTo; - public TechHiderUpdated(Plugin plugin) { + public TechHiderUpdated(Plugin plugin, Set blockIdsToObfuscate, BlockState blockToObfuscateTo) { + this.blockIdsToObfuscate = blockIdsToObfuscate; + this.blockToObfuscateTo = blockToObfuscateTo; this.bypassingPackets = Set.of( // --- Handshake & Login Protocol --- // These must be whitelisted to allow the player to actually reach the 'Play' @@ -101,9 +106,9 @@ public class TechHiderUpdated { BiFunction, Packet> tossPacket = (p, packet) -> null; this.packetProcessors = Map.of( - ClientboundBlockEventPacket.class, tossPacket, - ClientboundBlockUpdatePacket.class, tossPacket, - ClientboundBlockEntityDataPacket.class, tossPacket, + ClientboundBlockEventPacket.class, (p, packet) -> processBlockEventPacket(p, (ClientboundBlockEventPacket) packet), + ClientboundBlockUpdatePacket.class, (p, packet) -> processBlockUpdatePacket(p, (ClientboundBlockUpdatePacket) packet), + ClientboundBlockEntityDataPacket.class, (p, packet) -> processBlockEntityDataPacket(p, (ClientboundBlockEntityDataPacket) packet), ClientboundSectionBlocksUpdatePacket.class, tossPacket, ClientboundLevelChunkWithLightPacket.class, tossPacket ); @@ -124,4 +129,45 @@ public class TechHiderUpdated { } }; } + + private ClientboundBlockEventPacket processBlockEventPacket(Player player, ClientboundBlockEventPacket packet) { + BlockPos blockPos = packet.getPos(); + + + if(isPlayerPrivilegedToAccessBlockPos(player, blockPos)) { + return packet; + } + else { + return null; + } + } + + private ClientboundBlockUpdatePacket processBlockUpdatePacket(Player p, ClientboundBlockUpdatePacket packet) { + int id = BlockIds.impl.getCombinedId(packet.getBlockState()); + + if(blockIdsToObfuscate.contains(id)) { + // Return a modified copy of the packet with the obfuscated block state + ClientboundBlockUpdatePacket modifiedPacket = new ClientboundBlockUpdatePacket(packet.getPos(), blockToObfuscateTo); + return modifiedPacket; + } + else { + return packet; + } + } + + private ClientboundBlockEntityDataPacket processBlockEntityDataPacket(Player p, ClientboundBlockEntityDataPacket packet) { + if(isPlayerPrivilegedToAccessBlockPos(p, packet.getPos()) ) { + return packet; + } + else if(ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet)){ + return packet; + } + else { + return null; + } + } + + public boolean isPlayerPrivilegedToAccessBlockPos(Player p, BlockPos block) { + return false; + } } From c30aad179b6394bc412350c671712fc3f693b6bd Mon Sep 17 00:00:00 2001 From: Manuel Frohn Date: Wed, 13 May 2026 17:20:32 +0200 Subject: [PATCH 04/52] Continue refactor --- .../src/de/steamwar/techhider/ChunkHider.java | 157 +++++++++++++++++- .../src/de/steamwar/techhider/TechHider.java | 7 - .../steamwar/techhider/TechHiderUpdated.java | 123 ++++++++++---- 3 files changed, 247 insertions(+), 40 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java index bdc8cdd3..69f0123a 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java @@ -29,11 +29,160 @@ import java.util.Collections; import java.util.Set; import java.util.function.BiFunction; -public interface ChunkHider { - ChunkHider impl = VersionDependent.getVersionImpl(Core.getInstance()); +/* + * 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 . + */ - Class mapChunkPacket(); - BiFunction chunkHiderGenerator(TechHider techHider); +package de.steamwar.techhider; + +import de.steamwar.Reflection; +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.entity.BlockEntityType; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; + +public class ChunkHider { + private static final UnaryOperator chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class); + private static final UnaryOperator chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class); + + private static final Reflection.Field chunkXField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 0); + private static final Reflection.Field chunkZField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 1); + private static final Reflection.Field chunkData = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkPacketData.class, 0); + + private static final Reflection.Field dataField = Reflection.getField(ClientboundLevelChunkPacketData.class, byte[].class, 0); + private static final Reflection.Field tileEntities = Reflection.getField(ClientboundLevelChunkPacketData.class, List.class, 0); + + public BiFunction processLevelChunkWithLightPacket(TechHider techHider) { + return (p, packet) -> { + int chunkX = chunkXField.get(packet); + int chunkZ = chunkZField.get(packet); + if (techHider.getLocationEvaluator().skipChunk(p, chunkX, chunkZ)) + return packet; + + packet = chunkPacketCloner.apply(packet); + Object dataWrapper = chunkDataCloner.apply(chunkData.get(packet)); + + Set hiddenBlockEntities = techHider.getHiddenBlockEntities(); + tileEntities.set(dataWrapper, ((List)tileEntities.get(dataWrapper)).stream().filter(te -> tileEntityVisible(hiddenBlockEntities, te)).collect(Collectors.toList())); + + ByteBuf in = Unpooled.wrappedBuffer(dataField.get(dataWrapper)); + 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); + dataField.set(dataWrapper, data); + + chunkData.set(packet, dataWrapper); + return packet; + }; + } + + public static final Class tileEntity = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$BlockEntityInfo"); + protected static final Reflection.Field entityType = Reflection.getField(tileEntity, BlockEntityType.class, 0); + private static final Class builtInRegestries = Reflection.getClass("net.minecraft.core.registries.BuiltInRegistries"); + private static final Class registry = Reflection.getClass("net.minecraft.core.Registry"); + private static final Reflection.Field nameField = Reflection.getField(builtInRegestries, "BLOCK_ENTITY_TYPE", registry); + private static final Class resourceLocation = Reflection.getClass("net.minecraft.resources.ResourceLocation"); + private static final Reflection.Method getKey = Reflection.getTypedMethod(registry, "getKey", resourceLocation, Object.class); + private static final Reflection.Method getName = Reflection.getTypedMethod(resourceLocation, "getPath", String.class); + protected boolean tileEntityVisible(Set hiddenBlockEntities, Object tile) { + return !hiddenBlockEntities.contains(getName.invoke(getKey.invoke(nameField.get(null), entityType.get(tile)))); + } + + 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 { diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 0428c78c..bd8e41d8 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -75,10 +75,6 @@ public class TechHider { this.obfuscationTarget = getBlockDataByBlock.invoke(getBlockByMaterial.invoke(null, obfuscationTarget)); this.obfuscationTargetId = BlockIds.impl.materialToId(obfuscationTarget); - techhiders.put(blockActionPacket, this::blockActionHider); - techhiders.put(blockChangePacket, this::blockChangeHider); - techhiders.put(tileEntityDataPacket, this::tileEntityDataHider); - techhiders.put(multiBlockChangePacket, ProtocolWrapper.impl.multiBlockChangeGenerator(this)); techhiders.put(ChunkHider.impl.mapChunkPacket(), ChunkHider.impl.chunkHiderGenerator(this)); if(Core.getVersion() > 12 && Core.getVersion() < 19) { @@ -101,9 +97,6 @@ public class TechHider { techhiders.forEach(TinyProtocol.instance::removeFilter); } - public static final Class multiBlockChangePacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket"); - public static final UnaryOperator multiBlockChangeCloner = ProtocolUtils.shallowCloneGenerator(TechHider.multiBlockChangePacket); - public enum State { SKIP, CHECK, diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java index b5def8e9..26138c02 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java @@ -1,16 +1,25 @@ package de.steamwar.techhider; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.BiFunction; +import java.util.stream.Collectors; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import com.comphenix.tinyprotocol.TinyProtocol; +import de.steamwar.Reflection; +import de.steamwar.techhider.ChunkHider.SectionHider; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import net.minecraft.core.BlockPos; +import net.minecraft.core.SectionPos; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.PacketListener; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; @@ -25,6 +34,7 @@ import net.minecraft.network.protocol.game.ClientboundCommandSuggestionsPacket; import net.minecraft.network.protocol.game.ClientboundCommandsPacket; import net.minecraft.network.protocol.game.ClientboundDisguisedChatPacket; import net.minecraft.network.protocol.game.ClientboundGameEventPacket; +import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; import net.minecraft.network.protocol.game.ClientboundPlayerAbilitiesPacket; import net.minecraft.network.protocol.game.ClientboundPlayerChatPacket; @@ -57,8 +67,7 @@ public class TechHiderUpdated { private final Map>, BiFunction, Packet>> packetProcessors; private final TinyProtocol interceptor; private final Set blockIdsToObfuscate; - private final BlockState blockToObfuscateTo; - + private final BlockState blockToObfuscateTo; public TechHiderUpdated(Plugin plugin, Set blockIdsToObfuscate, BlockState blockToObfuscateTo) { this.blockIdsToObfuscate = blockIdsToObfuscate; @@ -103,26 +112,28 @@ public class TechHiderUpdated { ClientboundCommandsPacket.class // Command tree for tab-complete ); - BiFunction, Packet> tossPacket = (p, packet) -> null; + BiFunction, Packet> tossPacket = (p, + packet) -> null; this.packetProcessors = Map.of( - ClientboundBlockEventPacket.class, (p, packet) -> processBlockEventPacket(p, (ClientboundBlockEventPacket) packet), - ClientboundBlockUpdatePacket.class, (p, packet) -> processBlockUpdatePacket(p, (ClientboundBlockUpdatePacket) packet), - ClientboundBlockEntityDataPacket.class, (p, packet) -> processBlockEntityDataPacket(p, (ClientboundBlockEntityDataPacket) packet), - ClientboundSectionBlocksUpdatePacket.class, tossPacket, - ClientboundLevelChunkWithLightPacket.class, tossPacket - ); + ClientboundBlockEventPacket.class, + (p, packet) -> processBlockEventPacket(p, (ClientboundBlockEventPacket) packet), + ClientboundBlockUpdatePacket.class, + (p, packet) -> processBlockUpdatePacket(p, (ClientboundBlockUpdatePacket) packet), + ClientboundBlockEntityDataPacket.class, + (p, packet) -> processBlockEntityDataPacket(p, (ClientboundBlockEntityDataPacket) packet), + ClientboundSectionBlocksUpdatePacket.class, tossPacket, + ClientboundLevelChunkWithLightPacket.class, tossPacket); this.interceptor = new TinyProtocol(plugin) { @Override public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) { if (bypassingPackets.stream().anyMatch(clazz -> clazz.isInstance(packet))) { return packet; - } - else if (packetProcessors.containsKey(packet.getClass())) { - return packetProcessors.get(packet.getClass()).apply(receiver, (Packet) packet); - } - else { + } else if (packetProcessors.containsKey(packet.getClass())) { + return packetProcessors.get(packet.getClass()).apply(receiver, + (Packet) packet); + } else { return null; } @@ -133,11 +144,9 @@ public class TechHiderUpdated { private ClientboundBlockEventPacket processBlockEventPacket(Player player, ClientboundBlockEventPacket packet) { BlockPos blockPos = packet.getPos(); - - if(isPlayerPrivilegedToAccessBlockPos(player, blockPos)) { + if (isPlayerPrivilegedToAccessBlockPos(player, blockPos)) { return packet; - } - else { + } else { return null; } } @@ -145,29 +154,85 @@ public class TechHiderUpdated { private ClientboundBlockUpdatePacket processBlockUpdatePacket(Player p, ClientboundBlockUpdatePacket packet) { int id = BlockIds.impl.getCombinedId(packet.getBlockState()); - if(blockIdsToObfuscate.contains(id)) { + if (blockIdsToObfuscate.contains(id)) { // Return a modified copy of the packet with the obfuscated block state - ClientboundBlockUpdatePacket modifiedPacket = new ClientboundBlockUpdatePacket(packet.getPos(), blockToObfuscateTo); + ClientboundBlockUpdatePacket modifiedPacket = new ClientboundBlockUpdatePacket(packet.getPos(), + blockToObfuscateTo); return modifiedPacket; - } - else { + } else { return packet; } } - private ClientboundBlockEntityDataPacket processBlockEntityDataPacket(Player p, ClientboundBlockEntityDataPacket packet) { - if(isPlayerPrivilegedToAccessBlockPos(p, packet.getPos()) ) { + private ClientboundBlockEntityDataPacket processBlockEntityDataPacket(Player p, + ClientboundBlockEntityDataPacket packet) { + if (isPlayerPrivilegedToAccessBlockPos(p, packet.getPos())) { return packet; - } - else if(ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet)){ + } else if (ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet)) { return packet; - } - else { + } else { return null; } } - public boolean isPlayerPrivilegedToAccessBlockPos(Player p, BlockPos block) { + private ClientboundSectionBlocksUpdatePacket processSectionUpdate(Player p, + ClientboundSectionBlocksUpdatePacket packet) { + SectionPos sectionPos = packet.sectionPos; + short[] oldPos = packet.positions; + BlockState[] oldStates = packet.states; + + boolean modified = false; + List filteredPos = new ArrayList<>(oldPos.length); + List filteredStates = new ArrayList<>(oldStates.length); + + for (int i = 0; i < oldPos.length; i++) { + short posShort = oldPos[i]; + BlockState state = oldStates[i]; + + int worldX = sectionPos.relativeToBlockX(posShort); + int worldY = sectionPos.relativeToBlockY(posShort); + int worldZ = sectionPos.relativeToBlockZ(posShort); + + BlockPos pos = new BlockPos(worldX, worldY, worldZ); + + if (isPlayerPrivilegedToAccessBlockPos(p, pos)) { + filteredPos.add(posShort); + filteredStates.add(state); + } else { + int id = Block.getId(state); + if (blockIdsToObfuscate.contains(id)) { + filteredPos.add(posShort); + filteredStates.add(blockToObfuscateTo); + modified = true; + } else { + filteredPos.add(posShort); + filteredStates.add(state); + } + } + } + + if (filteredStates.isEmpty()) + return null; + if (!modified) + return packet; + + 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, + it.unimi.dsi.fastutil.shorts.ShortSets + .unmodifiable(new it.unimi.dsi.fastutil.shorts.ShortArraySet(newPos)), + newStates); + } + + private ClientboundLevelChunkWithLightPacket processChunkWithLight(Player p, ClientboundLevelChunkWithLightPacket packet) { + } + + private boolean isPlayerPrivilegedToAccessBlockPos(Player p, BlockPos pos) { return false; } + } From 6766862a006981b21608930d45362ba20f739e29 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Wed, 13 May 2026 22:48:44 +0200 Subject: [PATCH 05/52] Refactored over all functionality --- .../features/techhider/TechHiderCommand.java | 1 - .../features/world/SpectatorListener.java | 1 - .../bausystem/features/xray/XrayCommand.java | 1 - .../steamwar/fightsystem/utils/HullHider.java | 2 - .../fightsystem/utils/TechHiderWrapper.java | 1 - .../de/steamwar/techhider/ChunkHider18.java | 4 +- .../de/steamwar/techhider/ChunkHider21.java | 4 +- .../de/steamwar/techhider/ChunkHider9.java | 2 +- .../de/steamwar/core/events/AntiNocom.java | 1 - .../src/de/steamwar/techhider/ChunkHider.java | 94 ++++--------- .../de/steamwar/techhider/QuadFunction.java | 24 ++++ .../src/de/steamwar/techhider/TechHider.java | 128 ------------------ .../steamwar/techhider/TechHiderUpdated.java | 62 +++++---- 13 files changed, 91 insertions(+), 234 deletions(-) create mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/QuadFunction.java delete mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java 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 74888bec..ba727fba 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,6 @@ import de.steamwar.command.TypeMapper; import de.steamwar.core.CraftbukkitWrapper; import de.steamwar.linkage.Linked; import de.steamwar.linkage.LinkedInstance; -import de.steamwar.techhider.TechHider; import net.md_5.bungee.api.ChatMessageType; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; 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 cfdc6fe3..28a77b54 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 @@ -27,7 +27,6 @@ import de.steamwar.inventory.SWItem; import de.steamwar.linkage.Linked; import de.steamwar.linkage.MinVersion; import de.steamwar.sql.BauweltMember; -import de.steamwar.techhider.TechHider; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; 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 b128e27a..2a5a09f0 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 @@ -30,7 +30,6 @@ import de.steamwar.command.SWCommand; import de.steamwar.core.CraftbukkitWrapper; import de.steamwar.linkage.Linked; import de.steamwar.linkage.LinkedInstance; -import de.steamwar.techhider.TechHider; import net.md_5.bungee.api.ChatMessageType; import org.bukkit.Bukkit; import org.bukkit.Material; diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java index 074a9e4d..c2af05ee 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java @@ -32,7 +32,6 @@ import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.StateDependent; import de.steamwar.fightsystem.states.StateDependentListener; import de.steamwar.fightsystem.states.StateDependentTask; -import de.steamwar.techhider.TechHider; import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -49,7 +48,6 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.BiFunction; 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 d72e6949..c581525c 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -31,7 +31,6 @@ import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.StateDependent; import de.steamwar.fightsystem.states.StateDependentListener; import de.steamwar.sql.SteamwarUser; -import de.steamwar.techhider.TechHider; import lombok.Getter; import org.bukkit.GameMode; import org.bukkit.entity.Player; diff --git a/SpigotCore/SpigotCore_18/src/de/steamwar/techhider/ChunkHider18.java b/SpigotCore/SpigotCore_18/src/de/steamwar/techhider/ChunkHider18.java index 3033feac..76ab1ad8 100644 --- a/SpigotCore/SpigotCore_18/src/de/steamwar/techhider/ChunkHider18.java +++ b/SpigotCore/SpigotCore_18/src/de/steamwar/techhider/ChunkHider18.java @@ -102,7 +102,7 @@ public class ChunkHider18 implements ChunkHider { boolean singletonPalette = section.getBitsPerBlock() == 0; if(singletonPalette) { int value = ProtocolUtils.readVarInt(section.getIn()); - ProtocolUtils.writeVarInt(section.getOut(), !section.isSkipSection() && section.getObfuscate().contains(value) ? section.getTarget() : value); + ProtocolUtils.writeVarInt(section.getOut(), !section.isSkipSection() && section.getBlockIdsToObfuscate().contains(value) ? section.getTarget() : value); }else if(section.getBitsPerBlock() < 15) { section.processPalette(); } @@ -123,7 +123,7 @@ public class ChunkHider18 implements ChunkHider { case SKIP: break; case CHECK: - if(!section.getObfuscate().contains(values.a(pos))) + if(!section.getBlockIdsToObfuscate().contains(values.a(pos))) break; case HIDE: values.b(pos, section.getTarget()); diff --git a/SpigotCore/SpigotCore_21/src/de/steamwar/techhider/ChunkHider21.java b/SpigotCore/SpigotCore_21/src/de/steamwar/techhider/ChunkHider21.java index 1cc6c29c..b26a4ceb 100644 --- a/SpigotCore/SpigotCore_21/src/de/steamwar/techhider/ChunkHider21.java +++ b/SpigotCore/SpigotCore_21/src/de/steamwar/techhider/ChunkHider21.java @@ -105,7 +105,7 @@ public class ChunkHider21 implements ChunkHider { 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); + ProtocolUtils.writeVarInt(section.getOut(), !section.isSkipSection() && section.getBlockIdsToObfuscate().contains(value) ? section.getTarget() : value); return; } else if (section.getBitsPerBlock() < 9) { // Indirect (paletted) storage – only present when bitsPerBlock < 9 in 1.21+ @@ -130,7 +130,7 @@ public class ChunkHider21 implements ChunkHider { case SKIP: break; case CHECK: - if (!section.getObfuscate().contains(values.get(pos))) + if (!section.getBlockIdsToObfuscate().contains(values.get(pos))) break; case HIDE: values.set(pos, section.getTarget()); diff --git a/SpigotCore/SpigotCore_9/src/de/steamwar/techhider/ChunkHider9.java b/SpigotCore/SpigotCore_9/src/de/steamwar/techhider/ChunkHider9.java index 71b47743..edea577b 100644 --- a/SpigotCore/SpigotCore_9/src/de/steamwar/techhider/ChunkHider9.java +++ b/SpigotCore/SpigotCore_9/src/de/steamwar/techhider/ChunkHider9.java @@ -100,7 +100,7 @@ public class ChunkHider9 extends ChunkHider8 { case SKIP: break; case CHECK: - if(!section.getObfuscate().contains(values.get(pos))) + if(!section.getBlockIdsToObfuscate().contains(values.get(pos))) break; case HIDE: values.set(pos, section.getTarget()); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java index 40cdb955..5ed98eac 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java @@ -25,7 +25,6 @@ import de.steamwar.core.Core; import de.steamwar.linkage.Linked; import de.steamwar.sql.SWException; import de.steamwar.techhider.ProtocolUtils; -import de.steamwar.techhider.TechHider; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java index 69f0123a..79559b56 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java @@ -19,15 +19,13 @@ package de.steamwar.techhider; -import de.steamwar.core.Core; -import de.steamwar.core.VersionDependent; import io.netty.buffer.ByteBuf; import lombok.Getter; +import net.minecraft.world.level.block.state.BlockState; import org.bukkit.entity.Player; import java.util.Collections; import java.util.Set; -import java.util.function.BiFunction; /* * This file is a part of the SteamWar software. @@ -51,17 +49,13 @@ import java.util.function.BiFunction; package de.steamwar.techhider; import de.steamwar.Reflection; -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.entity.BlockEntityType; -import org.bukkit.entity.Player; import java.util.List; -import java.util.Set; -import java.util.function.BiFunction; import java.util.function.UnaryOperator; import java.util.stream.Collectors; @@ -69,33 +63,27 @@ public class ChunkHider { private static final UnaryOperator chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class); private static final UnaryOperator chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class); - private static final Reflection.Field chunkXField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 0); - private static final Reflection.Field chunkZField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 1); private static final Reflection.Field chunkData = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkPacketData.class, 0); private static final Reflection.Field dataField = Reflection.getField(ClientboundLevelChunkPacketData.class, byte[].class, 0); private static final Reflection.Field tileEntities = Reflection.getField(ClientboundLevelChunkPacketData.class, List.class, 0); - public BiFunction processLevelChunkWithLightPacket(TechHider techHider) { - return (p, packet) -> { - int chunkX = chunkXField.get(packet); - int chunkZ = chunkZField.get(packet); - if (techHider.getLocationEvaluator().skipChunk(p, chunkX, chunkZ)) - return packet; + public ClientboundLevelChunkWithLightPacket processLevelChunkWithLightPacket(Player recivingPlayer, Set hiddenBlockEntities , QuadFunction isPlayerPrivilegedToAccessBlockPos, BlockState blockToObfuscateTo, Set blockIdsToObfuscate, ClientboundLevelChunkWithLightPacket packet) { + int chunkX = packet.getX(); + int chunkZ = packet.getZ(); - packet = chunkPacketCloner.apply(packet); - Object dataWrapper = chunkDataCloner.apply(chunkData.get(packet)); + Object clonedPacket = chunkPacketCloner.apply(packet); + Object dataWrapper = chunkDataCloner.apply(chunkData.get(clonedPacket)); - Set hiddenBlockEntities = techHider.getHiddenBlockEntities(); tileEntities.set(dataWrapper, ((List)tileEntities.get(dataWrapper)).stream().filter(te -> tileEntityVisible(hiddenBlockEntities, te)).collect(Collectors.toList())); ByteBuf in = Unpooled.wrappedBuffer(dataField.get(dataWrapper)); 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); + for(int yOffset = recivingPlayer.getWorld().getMinHeight(); yOffset < recivingPlayer.getWorld().getMaxHeight(); yOffset += 16) { + SectionHider section = new SectionHider(recivingPlayer, blockToObfuscateTo, blockIdsToObfuscate, in, out, chunkX, yOffset/16, chunkZ); section.copyBlockCount(); - blocks(section); + blocks(section, isPlayerPrivilegedToAccessBlockPos); biomes(section); } @@ -109,7 +97,6 @@ public class ChunkHider { chunkData.set(packet, dataWrapper); return packet; - }; } public static final Class tileEntity = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$BlockEntityInfo"); @@ -124,45 +111,29 @@ public class ChunkHider { return !hiddenBlockEntities.contains(getName.invoke(getKey.invoke(nameField.get(null), entityType.get(tile)))); } - private void blocks(SectionHider section) { + private void blocks(SectionHider section, QuadFunction isPlayerPrivilegedToAccessBlockPos) { 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); + ProtocolUtils.writeVarInt(section.getOut(), section.getBlockIdsToObfuscate().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; + int blockId = values.get(pos); - 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()); + if(!isPlayerPrivilegedToAccessBlockPos.apply(section.player, section.getOffsetX() * x, section.getOffsetY() * y, section.getOffsetZ() * z) && !section.getBlockIdsToObfuscate().contains(blockId)) { + values.set(pos, section.getTarget()); } } } @@ -187,7 +158,6 @@ public class ChunkHider { @Getter class SectionHider { private final Player player; - private final TechHider techHider; private final ByteBuf in; private final ByteBuf out; @@ -198,18 +168,17 @@ public class ChunkHider { 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; + private Set blockIdsToObfuscate; + private final int blockIdToObfuscateTo; - public SectionHider(Player player, TechHider techHider, ByteBuf in, ByteBuf out, int chunkX, int chunkY, int chunkZ) { + public SectionHider(Player player, BlockState blockToObfuscateTo, Set blockIdsToObfuscate, 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; @@ -218,21 +187,13 @@ public class ChunkHider { this.offsetX = 16*chunkX; this.offsetY = 16*chunkY; this.offsetZ = 16*chunkZ; - this.skipSection = techHider.getLocationEvaluator().skipChunkSection(player, chunkX, chunkY, chunkZ); + + this.blockIdsToObfuscate = blockIdsToObfuscate; + this.blockIdToObfuscateTo = BlockIds.impl.getCombinedId(blockToObfuscateTo); + 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() { @@ -258,11 +219,6 @@ public class ChunkHider { } public void processPalette() { - if(skipSection) { - skipPalette(); - return; - } - int paletteLength = copyVarInt(); if(paletteLength == 0) return; @@ -273,17 +229,17 @@ public class ChunkHider { for(int i = 0; i < paletteLength; i++) { int entry = ProtocolUtils.readVarInt(in); - if(obfuscate.contains(entry)) - entry = techHider.getObfuscationTargetId(); + if(blockIdsToObfuscate.contains(entry)) + entry = blockIdToObfuscateTo; if(entry == TechHider.AIR_ID) air = i; - else if(entry == techHider.getObfuscationTargetId()) + else if(entry == blockIdToObfuscateTo) target = i; ProtocolUtils.writeVarInt(out, entry); } - obfuscate = Collections.emptySet(); + blockIdsToObfuscate = Collections.emptySet(); } public void skipDataArray() { diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/QuadFunction.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/QuadFunction.java new file mode 100644 index 00000000..43df8d3b --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/QuadFunction.java @@ -0,0 +1,24 @@ +/* + * 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.techhider; + +public interface QuadFunction { + Return apply(A a, B b, C c, D d); +} \ No newline at end of file diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java deleted file mode 100644 index bd8e41d8..00000000 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ /dev/null @@ -1,128 +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; - -import com.comphenix.tinyprotocol.TinyProtocol; -import de.steamwar.Reflection; -import de.steamwar.core.Core; -import lombok.Getter; -import org.bukkit.Material; -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.Function; -import java.util.function.UnaryOperator; -import java.util.stream.Collectors; - -public class TechHider { - - public static final Class blockPosition = Reflection.getClass("net.minecraft.core.BlockPos"); - private static final Class baseBlockPosition = Reflection.getClass("net.minecraft.core.Vec3i"); - public static final Reflection.Field blockPositionX = Reflection.getField(baseBlockPosition, int.class, 0); - public static final Reflection.Field blockPositionY = Reflection.getField(baseBlockPosition, int.class, 1); - public static final Reflection.Field blockPositionZ = Reflection.getField(baseBlockPosition, int.class, 2); - - public static final Class iBlockData = Reflection.getClass("net.minecraft.world.level.block.state.BlockState"); - public static final Class block = Reflection.getClass("net.minecraft.world.level.block.Block"); - private static final Reflection.Method getBlockDataByBlock = Reflection.getTypedMethod(block, null, iBlockData); - - public static final Class craftMagicNumbers = Reflection.getClass("org.bukkit.craftbukkit.util.CraftMagicNumbers"); - private static final Reflection.Method getBlockByMaterial = Reflection.getTypedMethod(craftMagicNumbers, "getBlock", block, Material.class); - - public boolean iBlockDataHidden(Object iBlockData) { - return obfuscateIds.contains(BlockIds.impl.getCombinedId(iBlockData)); - } - - public static final Object AIR = getBlockDataByBlock.invoke(getBlockByMaterial.invoke(null, Material.AIR)); - 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 = getBlockDataByBlock.invoke(getBlockByMaterial.invoke(null, obfuscationTarget)); - this.obfuscationTargetId = BlockIds.impl.materialToId(obfuscationTarget); - - techhiders.put(ChunkHider.impl.mapChunkPacket(), ChunkHider.impl.chunkHiderGenerator(this)); - - if(Core.getVersion() > 12 && Core.getVersion() < 19) { - Class blockBreakClass = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket"); - techhiders.put(blockBreakClass, ProtocolWrapper.impl.blockBreakHiderGenerator(blockBreakClass, this)); - } - - if(Core.getVersion() > 8){ - techhiders.put(Reflection.getClass("net.minecraft.network.protocol.game.ServerboundUseItemOnPacket"), (p, packet) -> locationEvaluator.suppressInteractions(p) ? null : packet); - } - techhiders.put(Reflection.getClass("net.minecraft.network.protocol.game.ServerboundInteractPacket"), (p, packet) -> locationEvaluator.suppressInteractions(p) ? null : packet); - - } - - public void enable() { - techhiders.forEach(TinyProtocol.instance::addFilter); - } - - public void disable() { - techhiders.forEach(TinyProtocol.instance::removeFilter); - } - - 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, 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; - } - } -} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java index 26138c02..7ad1f958 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java @@ -67,11 +67,13 @@ public class TechHiderUpdated { private final Map>, BiFunction, Packet>> packetProcessors; private final TinyProtocol interceptor; private final Set blockIdsToObfuscate; + private final Set blockEntitiesToHide; private final BlockState blockToObfuscateTo; - public TechHiderUpdated(Plugin plugin, Set blockIdsToObfuscate, BlockState blockToObfuscateTo) { + public TechHiderUpdated(Plugin plugin, Set blockIdsToObfuscate, BlockState blockToObfuscateTo, Set blockEntitiesToHide) { this.blockIdsToObfuscate = blockIdsToObfuscate; this.blockToObfuscateTo = blockToObfuscateTo; + this.blockEntitiesToHide = blockEntitiesToHide; this.bypassingPackets = Set.of( // --- Handshake & Login Protocol --- // These must be whitelisted to allow the player to actually reach the 'Play' @@ -112,8 +114,7 @@ public class TechHiderUpdated { ClientboundCommandsPacket.class // Command tree for tab-complete ); - BiFunction, Packet> tossPacket = (p, - packet) -> null; + BiFunction, Packet> tossPacket = (p, packet) -> null; this.packetProcessors = Map.of( ClientboundBlockEventPacket.class, @@ -122,8 +123,11 @@ public class TechHiderUpdated { (p, packet) -> processBlockUpdatePacket(p, (ClientboundBlockUpdatePacket) packet), ClientboundBlockEntityDataPacket.class, (p, packet) -> processBlockEntityDataPacket(p, (ClientboundBlockEntityDataPacket) packet), - ClientboundSectionBlocksUpdatePacket.class, tossPacket, - ClientboundLevelChunkWithLightPacket.class, tossPacket); + ClientboundSectionBlocksUpdatePacket.class, + (p, packet) -> processSectionUpdate(p, (ClientboundSectionBlocksUpdatePacket) packet), + ClientboundLevelChunkWithLightPacket.class, + (p, packet) -> processChunkWithLight(p, (ClientboundLevelChunkWithLightPacket) packet) + ); this.interceptor = new TinyProtocol(plugin) { @Override @@ -131,8 +135,7 @@ public class TechHiderUpdated { if (bypassingPackets.stream().anyMatch(clazz -> clazz.isInstance(packet))) { return packet; } else if (packetProcessors.containsKey(packet.getClass())) { - return packetProcessors.get(packet.getClass()).apply(receiver, - (Packet) packet); + return packetProcessors.get(packet.getClass()).apply(receiver, (Packet) packet); } else { return null; } @@ -144,29 +147,32 @@ public class TechHiderUpdated { private ClientboundBlockEventPacket processBlockEventPacket(Player player, ClientboundBlockEventPacket packet) { BlockPos blockPos = packet.getPos(); - if (isPlayerPrivilegedToAccessBlockPos(player, blockPos)) { + if (isPlayerPrivilegedToAccessBlockPos(player, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { return packet; } else { return null; } } - private ClientboundBlockUpdatePacket processBlockUpdatePacket(Player p, ClientboundBlockUpdatePacket packet) { + private ClientboundBlockUpdatePacket processBlockUpdatePacket(Player player, ClientboundBlockUpdatePacket packet) { + BlockPos blockPos = packet.getPos(); int id = BlockIds.impl.getCombinedId(packet.getBlockState()); - if (blockIdsToObfuscate.contains(id)) { - // Return a modified copy of the packet with the obfuscated block state - ClientboundBlockUpdatePacket modifiedPacket = new ClientboundBlockUpdatePacket(packet.getPos(), - blockToObfuscateTo); + if(isPlayerPrivilegedToAccessBlockPos(player, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { + return packet; + } + else if (blockIdsToObfuscate.contains(id)) { + ClientboundBlockUpdatePacket modifiedPacket = new ClientboundBlockUpdatePacket(packet.getPos(), blockToObfuscateTo); return modifiedPacket; } else { return packet; } } - private ClientboundBlockEntityDataPacket processBlockEntityDataPacket(Player p, - ClientboundBlockEntityDataPacket packet) { - if (isPlayerPrivilegedToAccessBlockPos(p, packet.getPos())) { + private ClientboundBlockEntityDataPacket processBlockEntityDataPacket(Player p, ClientboundBlockEntityDataPacket packet) { + BlockPos blockPos = packet.getPos(); + + if (isPlayerPrivilegedToAccessBlockPos(p, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { return packet; } else if (ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet)) { return packet; @@ -175,8 +181,7 @@ public class TechHiderUpdated { } } - private ClientboundSectionBlocksUpdatePacket processSectionUpdate(Player p, - ClientboundSectionBlocksUpdatePacket packet) { + private ClientboundSectionBlocksUpdatePacket processSectionUpdate(Player p, ClientboundSectionBlocksUpdatePacket packet) { SectionPos sectionPos = packet.sectionPos; short[] oldPos = packet.positions; BlockState[] oldStates = packet.states; @@ -195,7 +200,7 @@ public class TechHiderUpdated { BlockPos pos = new BlockPos(worldX, worldY, worldZ); - if (isPlayerPrivilegedToAccessBlockPos(p, pos)) { + if (isPlayerPrivilegedToAccessBlockPos(p, pos.getX(), pos.getY(), pos.getZ())) { filteredPos.add(posShort); filteredStates.add(state); } else { @@ -211,14 +216,19 @@ public class TechHiderUpdated { } } - if (filteredStates.isEmpty()) + if (filteredStates.isEmpty()) { return null; - if (!modified) + } + if (!modified) { return packet; + } + short[] newPos = new short[filteredPos.size()]; - for (int i = 0; i < newPos.length; i++) + for (int i = 0; i < newPos.length; i++) { newPos[i] = filteredPos.get(i); + } + BlockState[] newStates = filteredStates.toArray(new BlockState[0]); return new ClientboundSectionBlocksUpdatePacket( @@ -228,10 +238,12 @@ public class TechHiderUpdated { newStates); } - private ClientboundLevelChunkWithLightPacket processChunkWithLight(Player p, ClientboundLevelChunkWithLightPacket packet) { - } + private final ChunkHider chunkHider = new ChunkHider(); + private ClientboundLevelChunkWithLightPacket processChunkWithLight(Player p, ClientboundLevelChunkWithLightPacket packet) { + return chunkHider.processLevelChunkWithLightPacket(p, blockEntitiesToHide, this::isPlayerPrivilegedToAccessBlockPos,blockToObfuscateTo, blockIdsToObfuscate, packet); + } - private boolean isPlayerPrivilegedToAccessBlockPos(Player p, BlockPos pos) { + private boolean isPlayerPrivilegedToAccessBlockPos(Player p, int blockX, int blockY, int blockZ) { return false; } From 51ff0e1a9fe2f5eb3d69929b2ae84b675b16a09e Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Thu, 14 May 2026 11:57:26 +0200 Subject: [PATCH 06/52] Fix remaining local issues --- .../src/de/steamwar/techhider/ChunkHider.java | 25 ++----------------- .../steamwar/techhider/TechHiderUpdated.java | 9 ++++--- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java index 79559b56..0182d9e7 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java @@ -22,32 +22,11 @@ package de.steamwar.techhider; import io.netty.buffer.ByteBuf; import lombok.Getter; import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.Material; import org.bukkit.entity.Player; import java.util.Collections; import java.util.Set; - -/* - * 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; - import de.steamwar.Reflection; import io.netty.buffer.Unpooled; import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; @@ -232,7 +211,7 @@ public class ChunkHider { if(blockIdsToObfuscate.contains(entry)) entry = blockIdToObfuscateTo; - if(entry == TechHider.AIR_ID) + if(entry == BlockIds.impl.materialToId(Material.AIR)) air = i; else if(entry == blockIdToObfuscateTo) target = i; diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java index 7ad1f958..64d12b8a 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java @@ -181,10 +181,13 @@ public class TechHiderUpdated { } } + private final Reflection.Field sectionPosField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, SectionPos.class, 0); + private final Reflection.Field oldPosField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, short[].class, 0); + private final Reflection.Field oldStatesField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, BlockState[].class, 0); private ClientboundSectionBlocksUpdatePacket processSectionUpdate(Player p, ClientboundSectionBlocksUpdatePacket packet) { - SectionPos sectionPos = packet.sectionPos; - short[] oldPos = packet.positions; - BlockState[] oldStates = packet.states; + SectionPos sectionPos = sectionPosField.get(packet); + short[] oldPos = oldPosField.get(packet); + BlockState[] oldStates = oldStatesField.get(packet); boolean modified = false; List filteredPos = new ArrayList<>(oldPos.length); From 13bddbe35955e8bcea97ebb49b4c9ee83f456ab6 Mon Sep 17 00:00:00 2001 From: Manuel Frohn Date: Thu, 14 May 2026 20:21:36 +0200 Subject: [PATCH 07/52] Update bypass list with full set of configuration and login packets --- .../{TechHiderUpdated.java => TechHider.java} | 75 +++++++++---------- 1 file changed, 36 insertions(+), 39 deletions(-) rename SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/{TechHiderUpdated.java => TechHider.java} (77%) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java similarity index 77% rename from SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java rename to SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 64d12b8a..55ecc674 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHiderUpdated.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -23,8 +23,19 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.PacketListener; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; +import net.minecraft.network.protocol.common.ClientboundCustomReportDetailsPacket; +import net.minecraft.network.protocol.common.ClientboundDisconnectPacket; import net.minecraft.network.protocol.common.ClientboundKeepAlivePacket; import net.minecraft.network.protocol.common.ClientboundPingPacket; +import net.minecraft.network.protocol.common.ClientboundResourcePackPopPacket; +import net.minecraft.network.protocol.common.ClientboundResourcePackPushPacket; +import net.minecraft.network.protocol.common.ClientboundServerLinksPacket; +import net.minecraft.network.protocol.common.ClientboundStoreCookiePacket; +import net.minecraft.network.protocol.common.ClientboundTransferPacket; +import net.minecraft.network.protocol.configuration.ClientboundFinishConfigurationPacket; +import net.minecraft.network.protocol.configuration.ClientboundRegistryDataPacket; +import net.minecraft.network.protocol.configuration.ClientboundUpdateEnabledFeaturesPacket; +import net.minecraft.network.protocol.cookie.ClientboundCookieRequestPacket; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.network.protocol.game.ClientboundBlockEventPacket; import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; @@ -34,7 +45,6 @@ import net.minecraft.network.protocol.game.ClientboundCommandSuggestionsPacket; import net.minecraft.network.protocol.game.ClientboundCommandsPacket; import net.minecraft.network.protocol.game.ClientboundDisguisedChatPacket; import net.minecraft.network.protocol.game.ClientboundGameEventPacket; -import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; import net.minecraft.network.protocol.game.ClientboundPlayerAbilitiesPacket; import net.minecraft.network.protocol.game.ClientboundPlayerChatPacket; @@ -53,6 +63,7 @@ import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket; import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; import net.minecraft.network.protocol.login.ClientboundHelloPacket; import net.minecraft.network.protocol.login.ClientboundLoginCompressionPacket; +import net.minecraft.network.protocol.login.ClientboundLoginDisconnectPacket; import net.minecraft.network.protocol.login.ClientboundLoginFinishedPacket; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; @@ -62,7 +73,7 @@ import net.minecraft.world.level.block.state.BlockState; * any packet must be explicitly whitelisted or processed in a * way that is also compliant with the principle of default-deny. */ -public class TechHiderUpdated { +public class TechHider { private final Set>> bypassingPackets; private final Map>, BiFunction, Packet>> packetProcessors; private final TinyProtocol interceptor; @@ -70,48 +81,34 @@ public class TechHiderUpdated { private final Set blockEntitiesToHide; private final BlockState blockToObfuscateTo; - public TechHiderUpdated(Plugin plugin, Set blockIdsToObfuscate, BlockState blockToObfuscateTo, Set blockEntitiesToHide) { + public TechHider(Plugin plugin, Set blockIdsToObfuscate, BlockState blockToObfuscateTo, Set blockEntitiesToHide) { this.blockIdsToObfuscate = blockIdsToObfuscate; this.blockToObfuscateTo = blockToObfuscateTo; this.blockEntitiesToHide = blockEntitiesToHide; this.bypassingPackets = Set.of( - // --- Handshake & Login Protocol --- - // These must be whitelisted to allow the player to actually reach the 'Play' - // state - ClientboundHelloPacket.class, // Encryption request - ClientboundLoginFinishedPacket.class, // Login Success - ClientboundLoginCompressionPacket.class, // Set Compression - ClientboundCustomQueryPacket.class, // Velocity/Bungee/Mod identity checks + // --- 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 - // --- Protocol & Connection --- - ClientboundKeepAlivePacket.class, - ClientboundPingPacket.class, - ClientboundCustomPayloadPacket.class, - ClientboundServerDataPacket.class, - - // --- UI & Communication --- - ClientboundSystemChatPacket.class, - ClientboundDisguisedChatPacket.class, - ClientboundBossEventPacket.class, - ClientboundTabListPacket.class, - ClientboundSetTitleTextPacket.class, - ClientboundSetSubtitleTextPacket.class, - ClientboundSetTitlesAnimationPacket.class, - ClientboundClearTitlesPacket.class, - ClientboundPlayerChatPacket.class, - - // --- Player Stats & Logic --- - ClientboundSetExperiencePacket.class, - ClientboundSetHealthPacket.class, - ClientboundPlayerAbilitiesPacket.class, - ClientboundUpdateAttributesPacket.class, - ClientboundCommandSuggestionsPacket.class, - - // --- World Metadata (Non-Structural) --- - ClientboundSetTimePacket.class, - ClientboundSetDefaultSpawnPositionPacket.class, - ClientboundGameEventPacket.class, // Weather, Gamemode changes, etc. - ClientboundCommandsPacket.class // Command tree for tab-complete + // --- 6.1.x Configuration Protocol --- + 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 ); BiFunction, Packet> tossPacket = (p, packet) -> null; From 99e1f01869143202da1d70ef6786fd3ecc3ef63c Mon Sep 17 00:00:00 2001 From: Manuel Frohn Date: Thu, 14 May 2026 21:38:37 +0200 Subject: [PATCH 08/52] Update bypass list with harmless play-client bound packets --- .../src/de/steamwar/techhider/TechHider.java | 139 +++++++++++++++++- 1 file changed, 135 insertions(+), 4 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 55ecc674..a4386a08 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -1,6 +1,7 @@ package de.steamwar.techhider; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -22,6 +23,7 @@ import net.minecraft.core.SectionPos; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.PacketListener; import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.common.ClientboundClearDialogPacket; import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; import net.minecraft.network.protocol.common.ClientboundCustomReportDetailsPacket; import net.minecraft.network.protocol.common.ClientboundDisconnectPacket; @@ -30,36 +32,84 @@ import net.minecraft.network.protocol.common.ClientboundPingPacket; import net.minecraft.network.protocol.common.ClientboundResourcePackPopPacket; import net.minecraft.network.protocol.common.ClientboundResourcePackPushPacket; import net.minecraft.network.protocol.common.ClientboundServerLinksPacket; +import net.minecraft.network.protocol.common.ClientboundShowDialogPacket; import net.minecraft.network.protocol.common.ClientboundStoreCookiePacket; import net.minecraft.network.protocol.common.ClientboundTransferPacket; import net.minecraft.network.protocol.configuration.ClientboundFinishConfigurationPacket; import net.minecraft.network.protocol.configuration.ClientboundRegistryDataPacket; import net.minecraft.network.protocol.configuration.ClientboundUpdateEnabledFeaturesPacket; import net.minecraft.network.protocol.cookie.ClientboundCookieRequestPacket; +import net.minecraft.network.protocol.game.ClientboundAwardStatsPacket; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.network.protocol.game.ClientboundBlockEventPacket; import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; import net.minecraft.network.protocol.game.ClientboundBossEventPacket; +import net.minecraft.network.protocol.game.ClientboundBundleDelimiterPacket; +import net.minecraft.network.protocol.game.ClientboundChangeDifficultyPacket; +import net.minecraft.network.protocol.game.ClientboundChunkBatchFinishedPacket; +import net.minecraft.network.protocol.game.ClientboundChunkBatchStartPacket; import net.minecraft.network.protocol.game.ClientboundClearTitlesPacket; import net.minecraft.network.protocol.game.ClientboundCommandSuggestionsPacket; import net.minecraft.network.protocol.game.ClientboundCommandsPacket; +import net.minecraft.network.protocol.game.ClientboundContainerClosePacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetDataPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundCooldownPacket; +import net.minecraft.network.protocol.game.ClientboundCustomChatCompletionsPacket; +import net.minecraft.network.protocol.game.ClientboundDeleteChatPacket; import net.minecraft.network.protocol.game.ClientboundDisguisedChatPacket; import net.minecraft.network.protocol.game.ClientboundGameEventPacket; +import net.minecraft.network.protocol.game.ClientboundInitializeBorderPacket; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; +import net.minecraft.network.protocol.game.ClientboundLoginPacket; +import net.minecraft.network.protocol.game.ClientboundMerchantOffersPacket; +import net.minecraft.network.protocol.game.ClientboundOpenBookPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.network.protocol.game.ClientboundPlaceGhostRecipePacket; import net.minecraft.network.protocol.game.ClientboundPlayerAbilitiesPacket; import net.minecraft.network.protocol.game.ClientboundPlayerChatPacket; +import net.minecraft.network.protocol.game.ClientboundPlayerCombatEndPacket; +import net.minecraft.network.protocol.game.ClientboundPlayerCombatEnterPacket; +import net.minecraft.network.protocol.game.ClientboundPlayerPositionPacket; +import net.minecraft.network.protocol.game.ClientboundPlayerRotationPacket; +import net.minecraft.network.protocol.game.ClientboundRecipeBookAddPacket; +import net.minecraft.network.protocol.game.ClientboundRecipeBookRemovePacket; +import net.minecraft.network.protocol.game.ClientboundRecipeBookSettingsPacket; +import net.minecraft.network.protocol.game.ClientboundResetScorePacket; +import net.minecraft.network.protocol.game.ClientboundRespawnPacket; import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket; +import net.minecraft.network.protocol.game.ClientboundSelectAdvancementsTabPacket; +import net.minecraft.network.protocol.game.ClientboundSetActionBarTextPacket; +import net.minecraft.network.protocol.game.ClientboundSetBorderCenterPacket; +import net.minecraft.network.protocol.game.ClientboundSetBorderLerpSizePacket; +import net.minecraft.network.protocol.game.ClientboundSetBorderSizePacket; +import net.minecraft.network.protocol.game.ClientboundSetBorderWarningDelayPacket; +import net.minecraft.network.protocol.game.ClientboundSetBorderWarningDistancePacket; +import net.minecraft.network.protocol.game.ClientboundSetChunkCacheCenterPacket; +import net.minecraft.network.protocol.game.ClientboundSetChunkCacheRadiusPacket; +import net.minecraft.network.protocol.game.ClientboundSetCursorItemPacket; import net.minecraft.network.protocol.game.ClientboundServerDataPacket; import net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket; +import net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket; import net.minecraft.network.protocol.game.ClientboundSetExperiencePacket; +import net.minecraft.network.protocol.game.ClientboundSetHeldSlotPacket; import net.minecraft.network.protocol.game.ClientboundSetHealthPacket; +import net.minecraft.network.protocol.game.ClientboundSetObjectivePacket; +import net.minecraft.network.protocol.game.ClientboundSetPlayerInventoryPacket; +import net.minecraft.network.protocol.game.ClientboundSetScorePacket; +import net.minecraft.network.protocol.game.ClientboundSetSimulationDistancePacket; import net.minecraft.network.protocol.game.ClientboundSetSubtitleTextPacket; import net.minecraft.network.protocol.game.ClientboundSetTimePacket; import net.minecraft.network.protocol.game.ClientboundSetTitleTextPacket; import net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket; +import net.minecraft.network.protocol.game.ClientboundStartConfigurationPacket; import net.minecraft.network.protocol.game.ClientboundSystemChatPacket; import net.minecraft.network.protocol.game.ClientboundTabListPacket; -import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket; +import net.minecraft.network.protocol.game.ClientboundTickingStatePacket; +import net.minecraft.network.protocol.game.ClientboundTickingStepPacket; +import net.minecraft.network.protocol.game.ClientboundUpdateAdvancementsPacket; +import net.minecraft.network.protocol.game.ClientboundUpdateRecipesPacket; import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; import net.minecraft.network.protocol.login.ClientboundHelloPacket; import net.minecraft.network.protocol.login.ClientboundLoginCompressionPacket; @@ -81,11 +131,12 @@ public class TechHider { private final Set blockEntitiesToHide; private final BlockState blockToObfuscateTo; + //TODO handle packet bundle public TechHider(Plugin plugin, Set blockIdsToObfuscate, BlockState blockToObfuscateTo, Set blockEntitiesToHide) { this.blockIdsToObfuscate = blockIdsToObfuscate; this.blockToObfuscateTo = blockToObfuscateTo; this.blockEntitiesToHide = blockEntitiesToHide; - this.bypassingPackets = Set.of( + this.bypassingPackets = new HashSet<>(List.of( // --- 5.1.x Login Protocol --- ClientboundLoginDisconnectPacket.class, // 5.1.1 Disconnect ClientboundHelloPacket.class, // 5.1.2 Encryption Request @@ -108,8 +159,88 @@ public class TechHider { 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 - ); + ClientboundServerDataPacket.class, // 6.1.20 Code of Conduct is usually in Server Data + + // --- 7.1.x Play Protocol: packets that do not expose hidden blocks, light, sounds, or entities --- + ClientboundBundleDelimiterPacket.class, // 7.1.1 Bundle Delimiter + ClientboundAwardStatsPacket.class, // 7.1.4 Award Statistics + ClientboundBossEventPacket.class, // 7.1.10 Boss Bar + ClientboundChangeDifficultyPacket.class, // 7.1.11 Change Difficulty + ClientboundChunkBatchFinishedPacket.class, // 7.1.12 Chunk Batch Finished + ClientboundChunkBatchStartPacket.class, // 7.1.13 Chunk Batch Start + ClientboundClearTitlesPacket.class, // 7.1.15 Clear Titles + ClientboundCommandSuggestionsPacket.class, // 7.1.16 Command Suggestions Response + ClientboundCommandsPacket.class, // 7.1.17 Commands + ClientboundContainerClosePacket.class, // 7.1.18 Close Container + ClientboundContainerSetContentPacket.class, // 7.1.19 Set Container Content + ClientboundContainerSetDataPacket.class, // 7.1.20 Set Container Property + ClientboundContainerSetSlotPacket.class, // 7.1.21 Set Container Slot + 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.) + 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 + 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 + ClientboundSetChunkCacheCenterPacket.class, // 7.1.93 Set Center Chunk + 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 + ClientboundSetHeldSlotPacket.class, // 7.1.104 Set Held Item (clientbound) + ClientboundSetObjectivePacket.class, // 7.1.105 Update Objectives + ClientboundSetPlayerInventoryPacket.class, // 7.1.107 Set Player Inventory Slot + 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) + )); BiFunction, Packet> tossPacket = (p, packet) -> null; From 42faba70d9017a4798027477952fe051fa349dde Mon Sep 17 00:00:00 2001 From: Manuel Frohn Date: Thu, 14 May 2026 22:08:17 +0200 Subject: [PATCH 09/52] Add remaining packets with toss handler for now --- .../src/de/steamwar/techhider/TechHider.java | 196 +++++++++++++++--- 1 file changed, 172 insertions(+), 24 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index a4386a08..a5b2c505 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -1,12 +1,12 @@ package de.steamwar.techhider; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.BiFunction; -import java.util.stream.Collectors; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; @@ -14,13 +14,9 @@ import org.bukkit.plugin.Plugin; import com.comphenix.tinyprotocol.TinyProtocol; import de.steamwar.Reflection; -import de.steamwar.techhider.ChunkHider.SectionHider; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; -import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.PacketListener; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.common.ClientboundClearDialogPacket; @@ -35,19 +31,26 @@ import net.minecraft.network.protocol.common.ClientboundServerLinksPacket; import net.minecraft.network.protocol.common.ClientboundShowDialogPacket; import net.minecraft.network.protocol.common.ClientboundStoreCookiePacket; import net.minecraft.network.protocol.common.ClientboundTransferPacket; +import net.minecraft.network.protocol.common.ClientboundUpdateTagsPacket; import net.minecraft.network.protocol.configuration.ClientboundFinishConfigurationPacket; import net.minecraft.network.protocol.configuration.ClientboundRegistryDataPacket; import net.minecraft.network.protocol.configuration.ClientboundUpdateEnabledFeaturesPacket; import net.minecraft.network.protocol.cookie.ClientboundCookieRequestPacket; +import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; +import net.minecraft.network.protocol.game.ClientboundAnimatePacket; import net.minecraft.network.protocol.game.ClientboundAwardStatsPacket; +import net.minecraft.network.protocol.game.ClientboundBlockChangedAckPacket; +import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.network.protocol.game.ClientboundBlockEventPacket; import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; import net.minecraft.network.protocol.game.ClientboundBossEventPacket; import net.minecraft.network.protocol.game.ClientboundBundleDelimiterPacket; +import net.minecraft.network.protocol.game.ClientboundBundlePacket; import net.minecraft.network.protocol.game.ClientboundChangeDifficultyPacket; import net.minecraft.network.protocol.game.ClientboundChunkBatchFinishedPacket; import net.minecraft.network.protocol.game.ClientboundChunkBatchStartPacket; +import net.minecraft.network.protocol.game.ClientboundChunksBiomesPacket; import net.minecraft.network.protocol.game.ClientboundClearTitlesPacket; import net.minecraft.network.protocol.game.ClientboundCommandSuggestionsPacket; import net.minecraft.network.protocol.game.ClientboundCommandsPacket; @@ -57,27 +60,54 @@ import net.minecraft.network.protocol.game.ClientboundContainerSetDataPacket; import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; import net.minecraft.network.protocol.game.ClientboundCooldownPacket; import net.minecraft.network.protocol.game.ClientboundCustomChatCompletionsPacket; +import net.minecraft.network.protocol.game.ClientboundDamageEventPacket; +import net.minecraft.network.protocol.game.ClientboundDebugSamplePacket; import net.minecraft.network.protocol.game.ClientboundDeleteChatPacket; import net.minecraft.network.protocol.game.ClientboundDisguisedChatPacket; +import net.minecraft.network.protocol.game.ClientboundEntityEventPacket; +import net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket; +import net.minecraft.network.protocol.game.ClientboundExplodePacket; +import net.minecraft.network.protocol.game.ClientboundForgetLevelChunkPacket; import net.minecraft.network.protocol.game.ClientboundGameEventPacket; +import net.minecraft.network.protocol.game.ClientboundHorseScreenOpenPacket; +import net.minecraft.network.protocol.game.ClientboundHurtAnimationPacket; import net.minecraft.network.protocol.game.ClientboundInitializeBorderPacket; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; +import net.minecraft.network.protocol.game.ClientboundLevelEventPacket; +import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket; +import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket; import net.minecraft.network.protocol.game.ClientboundLoginPacket; +import net.minecraft.network.protocol.game.ClientboundMapItemDataPacket; import net.minecraft.network.protocol.game.ClientboundMerchantOffersPacket; +import net.minecraft.network.protocol.game.ClientboundMoveEntityPacket; +import net.minecraft.network.protocol.game.ClientboundMoveEntityPacket.Pos; +import net.minecraft.network.protocol.game.ClientboundMoveEntityPacket.PosRot; +import net.minecraft.network.protocol.game.ClientboundMoveEntityPacket.Rot; +import net.minecraft.network.protocol.game.ClientboundMoveMinecartPacket; +import net.minecraft.network.protocol.game.ClientboundMoveVehiclePacket; import net.minecraft.network.protocol.game.ClientboundOpenBookPacket; import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.network.protocol.game.ClientboundOpenSignEditorPacket; import net.minecraft.network.protocol.game.ClientboundPlaceGhostRecipePacket; import net.minecraft.network.protocol.game.ClientboundPlayerAbilitiesPacket; import net.minecraft.network.protocol.game.ClientboundPlayerChatPacket; import net.minecraft.network.protocol.game.ClientboundPlayerCombatEndPacket; import net.minecraft.network.protocol.game.ClientboundPlayerCombatEnterPacket; +import net.minecraft.network.protocol.game.ClientboundPlayerCombatKillPacket; +import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket; +import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket; +import net.minecraft.network.protocol.game.ClientboundPlayerLookAtPacket; import net.minecraft.network.protocol.game.ClientboundPlayerPositionPacket; import net.minecraft.network.protocol.game.ClientboundPlayerRotationPacket; +import net.minecraft.network.protocol.game.ClientboundProjectilePowerPacket; import net.minecraft.network.protocol.game.ClientboundRecipeBookAddPacket; import net.minecraft.network.protocol.game.ClientboundRecipeBookRemovePacket; import net.minecraft.network.protocol.game.ClientboundRecipeBookSettingsPacket; +import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; +import net.minecraft.network.protocol.game.ClientboundRemoveMobEffectPacket; import net.minecraft.network.protocol.game.ClientboundResetScorePacket; import net.minecraft.network.protocol.game.ClientboundRespawnPacket; +import net.minecraft.network.protocol.game.ClientboundRotateHeadPacket; import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket; import net.minecraft.network.protocol.game.ClientboundSelectAdvancementsTabPacket; import net.minecraft.network.protocol.game.ClientboundSetActionBarTextPacket; @@ -86,29 +116,45 @@ import net.minecraft.network.protocol.game.ClientboundSetBorderLerpSizePacket; import net.minecraft.network.protocol.game.ClientboundSetBorderSizePacket; import net.minecraft.network.protocol.game.ClientboundSetBorderWarningDelayPacket; import net.minecraft.network.protocol.game.ClientboundSetBorderWarningDistancePacket; +import net.minecraft.network.protocol.game.ClientboundSetCameraPacket; import net.minecraft.network.protocol.game.ClientboundSetChunkCacheCenterPacket; import net.minecraft.network.protocol.game.ClientboundSetChunkCacheRadiusPacket; import net.minecraft.network.protocol.game.ClientboundSetCursorItemPacket; import net.minecraft.network.protocol.game.ClientboundServerDataPacket; import net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket; import net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket; +import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; +import net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket; +import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket; +import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket; import net.minecraft.network.protocol.game.ClientboundSetExperiencePacket; import net.minecraft.network.protocol.game.ClientboundSetHeldSlotPacket; import net.minecraft.network.protocol.game.ClientboundSetHealthPacket; import net.minecraft.network.protocol.game.ClientboundSetObjectivePacket; +import net.minecraft.network.protocol.game.ClientboundSetPassengersPacket; import net.minecraft.network.protocol.game.ClientboundSetPlayerInventoryPacket; +import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket; import net.minecraft.network.protocol.game.ClientboundSetScorePacket; import net.minecraft.network.protocol.game.ClientboundSetSimulationDistancePacket; import net.minecraft.network.protocol.game.ClientboundSetSubtitleTextPacket; import net.minecraft.network.protocol.game.ClientboundSetTimePacket; import net.minecraft.network.protocol.game.ClientboundSetTitleTextPacket; import net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket; +import net.minecraft.network.protocol.game.ClientboundSoundEntityPacket; +import net.minecraft.network.protocol.game.ClientboundSoundPacket; import net.minecraft.network.protocol.game.ClientboundStartConfigurationPacket; +import net.minecraft.network.protocol.game.ClientboundStopSoundPacket; import net.minecraft.network.protocol.game.ClientboundSystemChatPacket; import net.minecraft.network.protocol.game.ClientboundTabListPacket; +import net.minecraft.network.protocol.game.ClientboundTagQueryPacket; +import net.minecraft.network.protocol.game.ClientboundTakeItemEntityPacket; +import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket; import net.minecraft.network.protocol.game.ClientboundTickingStatePacket; import net.minecraft.network.protocol.game.ClientboundTickingStepPacket; +import net.minecraft.network.protocol.game.ClientboundTrackedWaypointPacket; import net.minecraft.network.protocol.game.ClientboundUpdateAdvancementsPacket; +import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket; +import net.minecraft.network.protocol.game.ClientboundUpdateMobEffectPacket; import net.minecraft.network.protocol.game.ClientboundUpdateRecipesPacket; import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; import net.minecraft.network.protocol.login.ClientboundHelloPacket; @@ -243,32 +289,134 @@ public class TechHider { )); BiFunction, Packet> tossPacket = (p, packet) -> null; + Map>, BiFunction, Packet>> processors = new HashMap<>(); - this.packetProcessors = Map.of( - ClientboundBlockEventPacket.class, - (p, packet) -> processBlockEventPacket(p, (ClientboundBlockEventPacket) packet), - ClientboundBlockUpdatePacket.class, - (p, packet) -> processBlockUpdatePacket(p, (ClientboundBlockUpdatePacket) packet), - ClientboundBlockEntityDataPacket.class, - (p, packet) -> processBlockEntityDataPacket(p, (ClientboundBlockEntityDataPacket) packet), - ClientboundSectionBlocksUpdatePacket.class, - (p, packet) -> processSectionUpdate(p, (ClientboundSectionBlocksUpdatePacket) packet), - ClientboundLevelChunkWithLightPacket.class, - (p, packet) -> processChunkWithLight(p, (ClientboundLevelChunkWithLightPacket) packet) - ); + // 7.1.x Bundle Packet: packet container; blocked until nested packets are safely unbundled/processed. + processors.put(ClientboundBundlePacket.class, tossPacket); + + // 7.1.2 Spawn Entity: entity type and position can reveal hidden contraptions. + processors.put(ClientboundAddEntityPacket.class, tossPacket); + // 7.1.3 Entity Animation: entity id based signal, keep blocked until entity visibility is modeled. + processors.put(ClientboundAnimatePacket.class, tossPacket); + // 7.1.5 Acknowledge Block Change: block interaction side channel; blocked pending review. + processors.put(ClientboundBlockChangedAckPacket.class, tossPacket); + // 7.1.6 Set Block Destroy Stage: block position and mining progress can reveal activity. + processors.put(ClientboundBlockDestructionPacket.class, tossPacket); + // 7.1.7 Block Entity Data: currently filtered by block position and allowed tile-entity actions. + processors.put(ClientboundBlockEntityDataPacket.class, (p, packet) -> processBlockEntityDataPacket(p, (ClientboundBlockEntityDataPacket) packet)); + // 7.1.8 Block Action: currently filtered by block position. + processors.put(ClientboundBlockEventPacket.class, (p, packet) -> processBlockEventPacket(p, (ClientboundBlockEventPacket) packet)); + // 7.1.9 Block Update: currently filtered/obfuscated by block position and block id. + processors.put(ClientboundBlockUpdatePacket.class, (p, packet) -> processBlockUpdatePacket(p, (ClientboundBlockUpdatePacket) packet)); + // 7.1.14 Chunk Biomes: chunk/world data side channel. + processors.put(ClientboundChunksBiomesPacket.class, tossPacket); + // 7.1.26 Damage Event: entity ids and damage source location can leak hidden activity. + processors.put(ClientboundDamageEventPacket.class, tossPacket); + // 7.1.31 Debug Sample: debug telemetry; blocked by default. + processors.put(ClientboundDebugSamplePacket.class, tossPacket); + // 7.1.35 Entity Event: entity id based signal. + processors.put(ClientboundEntityEventPacket.class, tossPacket); + // 7.1.36 Teleport Entity: entity id and absolute position. + processors.put(ClientboundTeleportEntityPacket.class, tossPacket); + // 7.1.37 Explosion: position, affected blocks, and knockback can reveal TNT/tech. + processors.put(ClientboundExplodePacket.class, tossPacket); + // 7.1.38 Unload Chunk: chunk visibility side channel. + processors.put(ClientboundForgetLevelChunkPacket.class, tossPacket); + // 7.1.41 Open Horse Screen: entity id based container. + processors.put(ClientboundHorseScreenOpenPacket.class, tossPacket); + // 7.1.42 Hurt Animation: entity id based signal. + processors.put(ClientboundHurtAnimationPacket.class, tossPacket); + // 7.1.45 Chunk Data and Update Light: currently filtered/obfuscated by chunk data processor. + processors.put(ClientboundLevelChunkWithLightPacket.class, (p, packet) -> processChunkWithLight(p, (ClientboundLevelChunkWithLightPacket) packet)); + // 7.1.46 World Event: block position/event id can leak activity. + processors.put(ClientboundLevelEventPacket.class, tossPacket); + // 7.1.47 Particle: particle type and position can reveal hidden machinery. + processors.put(ClientboundLevelParticlesPacket.class, tossPacket); + // 7.1.48 Update Light: lighting can reveal hidden blocks/operations. + processors.put(ClientboundLightUpdatePacket.class, tossPacket); + // 7.1.50 Map Data: map pixels/icons can reveal player/world state. + processors.put(ClientboundMapItemDataPacket.class, tossPacket); + // 7.1.52/53/55 Update Entity Position/Rotation: entity id and movement signal. + processors.put(ClientboundMoveEntityPacket.class, tossPacket); + processors.put(Pos.class, tossPacket); + processors.put(PosRot.class, tossPacket); + processors.put(Rot.class, tossPacket); + // 7.1.54 Move Minecart Along Track: entity path and position signal. + processors.put(ClientboundMoveMinecartPacket.class, tossPacket); + // 7.1.56 Move Vehicle (clientbound): vehicle position signal. + processors.put(ClientboundMoveVehiclePacket.class, tossPacket); + // 7.1.59 Open Sign Editor: block position. + processors.put(ClientboundOpenSignEditorPacket.class, tossPacket); + // 7.1.67 Combat Death: entity/player ids and death message context. + processors.put(ClientboundPlayerCombatKillPacket.class, tossPacket); + // 7.1.68 Player Info Remove: other-player identity/state. + processors.put(ClientboundPlayerInfoRemovePacket.class, tossPacket); + // 7.1.69 Player Info Update: other-player identity/state. + processors.put(ClientboundPlayerInfoUpdatePacket.class, tossPacket); + // 7.1.70 Look At: target position/entity. + processors.put(ClientboundPlayerLookAtPacket.class, tossPacket); + // 7.1.76 Remove Entities: entity id visibility side channel. + processors.put(ClientboundRemoveEntitiesPacket.class, tossPacket); + // 7.1.77 Remove Entity Effect: entity id and effect state. + processors.put(ClientboundRemoveMobEffectPacket.class, tossPacket); + // 7.1.82 Set Head Rotation: entity id and rotation. + processors.put(ClientboundRotateHeadPacket.class, tossPacket); + // 7.1.83 Update Section Blocks: currently filtered/obfuscated by section block processor. + processors.put(ClientboundSectionBlocksUpdatePacket.class, (p, packet) -> processSectionUpdate(p, (ClientboundSectionBlocksUpdatePacket) packet)); + // 7.1.92 Set Camera: entity id. + processors.put(ClientboundSetCameraPacket.class, tossPacket); + // 7.1.98 Set Entity Metadata: entity state can reveal blocks/items/displays. + processors.put(ClientboundSetEntityDataPacket.class, tossPacket); + // 7.1.99 Link Entities: entity relationship signal. + processors.put(ClientboundSetEntityLinkPacket.class, tossPacket); + // 7.1.100 Set Entity Velocity: entity id and movement. + processors.put(ClientboundSetEntityMotionPacket.class, tossPacket); + // 7.1.101 Set Equipment: entity equipment can reveal wargear. + processors.put(ClientboundSetEquipmentPacket.class, tossPacket); + // 7.1.106 Set Passengers: entity relationship signal. + processors.put(ClientboundSetPassengersPacket.class, tossPacket); + // 7.1.108 Update Teams: player/team membership can be used as intel. + processors.put(ClientboundSetPlayerTeamPacket.class, tossPacket); + // 7.1.115 Entity Sound Effect: entity id and sound. + processors.put(ClientboundSoundEntityPacket.class, tossPacket); + // 7.1.116 Sound Effect: sound type and position. + processors.put(ClientboundSoundPacket.class, tossPacket); + // 7.1.118 Stop Sound: sound state side channel. + processors.put(ClientboundStopSoundPacket.class, tossPacket); + // 7.1.122 Tag Query Response: block-entity query result. + processors.put(ClientboundTagQueryPacket.class, tossPacket); + // 7.1.123 Pickup Item: item/entity ids and pickup activity. + processors.put(ClientboundTakeItemEntityPacket.class, tossPacket); + // 7.1.124 Synchronize Vehicle Position: entity/vehicle position. + processors.put(ClientboundEntityPositionSyncPacket.class, tossPacket); + // 7.1.130 Update Attributes: entity id and attribute state. + processors.put(ClientboundUpdateAttributesPacket.class, tossPacket); + // 7.1.131 Entity Effect: entity id and effect state. + processors.put(ClientboundUpdateMobEffectPacket.class, tossPacket); + // 7.1.133 Update Tags (play): registry/tag data can be added back after review. + processors.put(ClientboundUpdateTagsPacket.class, tossPacket); + // 7.1.134 Projectile Power: projectile/entity signal. + processors.put(ClientboundProjectilePowerPacket.class, tossPacket); + // 7.1.137 Waypoint: world/entity tracking signal. + processors.put(ClientboundTrackedWaypointPacket.class, tossPacket); + + this.packetProcessors = processors; this.interceptor = new TinyProtocol(plugin) { @Override public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) { - if (bypassingPackets.stream().anyMatch(clazz -> clazz.isInstance(packet))) { - return packet; - } else if (packetProcessors.containsKey(packet.getClass())) { - return packetProcessors.get(packet.getClass()).apply(receiver, (Packet) packet); - } else { + try { + if (bypassingPackets.contains(packet.getClass())) { + return packet; + } + + BiFunction, Packet> processor = packetProcessors.get(packet.getClass()); + return processor == null ? null : processor.apply(receiver, (Packet) packet); + } catch(Error e) { + // Errors during packet processing leeding to packet being tossed + e.printStackTrace(); return null; } - - } }; } From 57c21122e6af6513a422e20d45c58ce8579c0873 Mon Sep 17 00:00:00 2001 From: Manuel Frohn Date: Fri, 15 May 2026 00:32:09 +0200 Subject: [PATCH 10/52] Classify packets by scope --- .../src/de/steamwar/techhider/TechHider.java | 141 +++++++++++------- 1 file changed, 84 insertions(+), 57 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index a5b2c505..07131759 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -228,6 +228,7 @@ public class TechHider { 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) @@ -285,7 +286,12 @@ public class TechHider { 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) + ClientboundShowDialogPacket.class, // 7.1.139 Show Dialog (play) + + // --- Play packets required manual review after update --- + // They include world data but non that can reveal critical information + ClientboundBlockChangedAckPacket.class, // 7.1.5 Acknowledge Block Change + ClientboundChunksBiomesPacket.class // 7.1.14 Chunk Biomes )); BiFunction, Packet> tossPacket = (p, packet) -> null; @@ -294,77 +300,30 @@ public class TechHider { // 7.1.x Bundle Packet: packet container; blocked until nested packets are safely unbundled/processed. processors.put(ClientboundBundlePacket.class, tossPacket); + // --- Entity packets --- // 7.1.2 Spawn Entity: entity type and position can reveal hidden contraptions. processors.put(ClientboundAddEntityPacket.class, tossPacket); // 7.1.3 Entity Animation: entity id based signal, keep blocked until entity visibility is modeled. processors.put(ClientboundAnimatePacket.class, tossPacket); - // 7.1.5 Acknowledge Block Change: block interaction side channel; blocked pending review. - processors.put(ClientboundBlockChangedAckPacket.class, tossPacket); - // 7.1.6 Set Block Destroy Stage: block position and mining progress can reveal activity. - processors.put(ClientboundBlockDestructionPacket.class, tossPacket); - // 7.1.7 Block Entity Data: currently filtered by block position and allowed tile-entity actions. - processors.put(ClientboundBlockEntityDataPacket.class, (p, packet) -> processBlockEntityDataPacket(p, (ClientboundBlockEntityDataPacket) packet)); - // 7.1.8 Block Action: currently filtered by block position. - processors.put(ClientboundBlockEventPacket.class, (p, packet) -> processBlockEventPacket(p, (ClientboundBlockEventPacket) packet)); - // 7.1.9 Block Update: currently filtered/obfuscated by block position and block id. - processors.put(ClientboundBlockUpdatePacket.class, (p, packet) -> processBlockUpdatePacket(p, (ClientboundBlockUpdatePacket) packet)); - // 7.1.14 Chunk Biomes: chunk/world data side channel. - processors.put(ClientboundChunksBiomesPacket.class, tossPacket); // 7.1.26 Damage Event: entity ids and damage source location can leak hidden activity. processors.put(ClientboundDamageEventPacket.class, tossPacket); - // 7.1.31 Debug Sample: debug telemetry; blocked by default. - processors.put(ClientboundDebugSamplePacket.class, tossPacket); // 7.1.35 Entity Event: entity id based signal. processors.put(ClientboundEntityEventPacket.class, tossPacket); // 7.1.36 Teleport Entity: entity id and absolute position. processors.put(ClientboundTeleportEntityPacket.class, tossPacket); - // 7.1.37 Explosion: position, affected blocks, and knockback can reveal TNT/tech. - processors.put(ClientboundExplodePacket.class, tossPacket); - // 7.1.38 Unload Chunk: chunk visibility side channel. - processors.put(ClientboundForgetLevelChunkPacket.class, tossPacket); - // 7.1.41 Open Horse Screen: entity id based container. - processors.put(ClientboundHorseScreenOpenPacket.class, tossPacket); // 7.1.42 Hurt Animation: entity id based signal. processors.put(ClientboundHurtAnimationPacket.class, tossPacket); - // 7.1.45 Chunk Data and Update Light: currently filtered/obfuscated by chunk data processor. - processors.put(ClientboundLevelChunkWithLightPacket.class, (p, packet) -> processChunkWithLight(p, (ClientboundLevelChunkWithLightPacket) packet)); - // 7.1.46 World Event: block position/event id can leak activity. - processors.put(ClientboundLevelEventPacket.class, tossPacket); - // 7.1.47 Particle: particle type and position can reveal hidden machinery. - processors.put(ClientboundLevelParticlesPacket.class, tossPacket); - // 7.1.48 Update Light: lighting can reveal hidden blocks/operations. - processors.put(ClientboundLightUpdatePacket.class, tossPacket); - // 7.1.50 Map Data: map pixels/icons can reveal player/world state. - processors.put(ClientboundMapItemDataPacket.class, tossPacket); // 7.1.52/53/55 Update Entity Position/Rotation: entity id and movement signal. processors.put(ClientboundMoveEntityPacket.class, tossPacket); processors.put(Pos.class, tossPacket); processors.put(PosRot.class, tossPacket); processors.put(Rot.class, tossPacket); - // 7.1.54 Move Minecart Along Track: entity path and position signal. - processors.put(ClientboundMoveMinecartPacket.class, tossPacket); - // 7.1.56 Move Vehicle (clientbound): vehicle position signal. - processors.put(ClientboundMoveVehiclePacket.class, tossPacket); - // 7.1.59 Open Sign Editor: block position. - processors.put(ClientboundOpenSignEditorPacket.class, tossPacket); - // 7.1.67 Combat Death: entity/player ids and death message context. - processors.put(ClientboundPlayerCombatKillPacket.class, tossPacket); - // 7.1.68 Player Info Remove: other-player identity/state. - processors.put(ClientboundPlayerInfoRemovePacket.class, tossPacket); - // 7.1.69 Player Info Update: other-player identity/state. - processors.put(ClientboundPlayerInfoUpdatePacket.class, tossPacket); - // 7.1.70 Look At: target position/entity. - processors.put(ClientboundPlayerLookAtPacket.class, tossPacket); // 7.1.76 Remove Entities: entity id visibility side channel. processors.put(ClientboundRemoveEntitiesPacket.class, tossPacket); // 7.1.77 Remove Entity Effect: entity id and effect state. processors.put(ClientboundRemoveMobEffectPacket.class, tossPacket); // 7.1.82 Set Head Rotation: entity id and rotation. processors.put(ClientboundRotateHeadPacket.class, tossPacket); - // 7.1.83 Update Section Blocks: currently filtered/obfuscated by section block processor. - processors.put(ClientboundSectionBlocksUpdatePacket.class, (p, packet) -> processSectionUpdate(p, (ClientboundSectionBlocksUpdatePacket) packet)); - // 7.1.92 Set Camera: entity id. - processors.put(ClientboundSetCameraPacket.class, tossPacket); // 7.1.98 Set Entity Metadata: entity state can reveal blocks/items/displays. processors.put(ClientboundSetEntityDataPacket.class, tossPacket); // 7.1.99 Link Entities: entity relationship signal. @@ -375,24 +334,90 @@ public class TechHider { processors.put(ClientboundSetEquipmentPacket.class, tossPacket); // 7.1.106 Set Passengers: entity relationship signal. processors.put(ClientboundSetPassengersPacket.class, tossPacket); - // 7.1.108 Update Teams: player/team membership can be used as intel. - processors.put(ClientboundSetPlayerTeamPacket.class, tossPacket); + // 7.1.130 Update Attributes: entity id and attribute state. + processors.put(ClientboundUpdateAttributesPacket.class, tossPacket); + // 7.1.131 Entity Effect: entity id and effect state. + processors.put(ClientboundUpdateMobEffectPacket.class, tossPacket); + + + // --- Block entity packets --- + // 7.1.7 Block Entity Data: currently filtered by block position and allowed tile-entity actions. + processors.put(ClientboundBlockEntityDataPacket.class, (p, packet) -> processBlockEntityDataPacket(p, (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, tossPacket); + // 7.1.8 Block Action: currently filtered by block position. + processors.put(ClientboundBlockEventPacket.class, (p, packet) -> processBlockEventPacket(p, (ClientboundBlockEventPacket) packet)); + // 7.1.9 Block Update: currently filtered/obfuscated by block position and block id. + processors.put(ClientboundBlockUpdatePacket.class, (p, packet) -> processBlockUpdatePacket(p, (ClientboundBlockUpdatePacket) packet)); + + // --- Chunk packets --- + // 7.1.38 Unload Chunk: chunk visibility side channel. + processors.put(ClientboundForgetLevelChunkPacket.class, tossPacket); + // 7.1.45 Chunk Data and Update Light: currently filtered/obfuscated by chunk data processor. + processors.put(ClientboundLevelChunkWithLightPacket.class, (p, packet) -> processChunkWithLight(p, (ClientboundLevelChunkWithLightPacket) packet)); + + // --- Section packets --- + // 7.1.83 Update Section Blocks: currently filtered/obfuscated by section block processor. + processors.put(ClientboundSectionBlocksUpdatePacket.class, (p, packet) -> processSectionUpdate(p, (ClientboundSectionBlocksUpdatePacket) packet)); + + + // --- Particle packets --- + // 7.1.47 Particle: particle type and position can reveal hidden machinery. + processors.put(ClientboundLevelParticlesPacket.class, tossPacket); + + // --- Sound packets --- // 7.1.115 Entity Sound Effect: entity id and sound. processors.put(ClientboundSoundEntityPacket.class, tossPacket); // 7.1.116 Sound Effect: sound type and position. processors.put(ClientboundSoundPacket.class, tossPacket); // 7.1.118 Stop Sound: sound state side channel. processors.put(ClientboundStopSoundPacket.class, tossPacket); - // 7.1.122 Tag Query Response: block-entity query result. - processors.put(ClientboundTagQueryPacket.class, tossPacket); + + + // --- Lighting packets --- + // 7.1.48 Update Light: lighting can reveal hidden blocks/operations. + processors.put(ClientboundLightUpdatePacket.class, tossPacket); + + // --- Player packets --- + // 7.1.67 Combat Death: entity/player ids and death message context. + processors.put(ClientboundPlayerCombatKillPacket.class, tossPacket); + // 7.1.68 Player Info Remove: other-player identity/state. + processors.put(ClientboundPlayerInfoRemovePacket.class, tossPacket); + // 7.1.69 Player Info Update: other-player identity/state. + processors.put(ClientboundPlayerInfoUpdatePacket.class, tossPacket); + // 7.1.70 Look At: target position/entity. + processors.put(ClientboundPlayerLookAtPacket.class, tossPacket); + + + // --- Others --- + + // 7.1.37 Explosion: position, affected blocks, and knockback can reveal TNT/tech. + processors.put(ClientboundExplodePacket.class, tossPacket); + // 7.1.46 World Event: block position/event id can leak activity. + processors.put(ClientboundLevelEventPacket.class, tossPacket); + // 7.1.50 Map Data: map pixels/icons can reveal player/world state. + processors.put(ClientboundMapItemDataPacket.class, tossPacket); + // 7.1.54 Move Minecart Along Track: entity path and position signal. + processors.put(ClientboundMoveMinecartPacket.class, tossPacket); + // 7.1.56 Move Vehicle (clientbound): vehicle position signal. + processors.put(ClientboundMoveVehiclePacket.class, tossPacket); + // 7.1.59 Open Sign Editor: block position. + processors.put(ClientboundOpenSignEditorPacket.class, tossPacket); + + // 7.1.92 Set Camera: entity id. + processors.put(ClientboundSetCameraPacket.class, tossPacket); + // 7.1.108 Update Teams: player/team membership can be used as intel. + processors.put(ClientboundSetPlayerTeamPacket.class, tossPacket); + // 7.1.123 Pickup Item: item/entity ids and pickup activity. processors.put(ClientboundTakeItemEntityPacket.class, tossPacket); // 7.1.124 Synchronize Vehicle Position: entity/vehicle position. processors.put(ClientboundEntityPositionSyncPacket.class, tossPacket); - // 7.1.130 Update Attributes: entity id and attribute state. - processors.put(ClientboundUpdateAttributesPacket.class, tossPacket); - // 7.1.131 Entity Effect: entity id and effect state. - processors.put(ClientboundUpdateMobEffectPacket.class, tossPacket); // 7.1.133 Update Tags (play): registry/tag data can be added back after review. processors.put(ClientboundUpdateTagsPacket.class, tossPacket); // 7.1.134 Projectile Power: projectile/entity signal. @@ -526,4 +551,6 @@ public class TechHider { return false; } + + } From 42808f40c060bf557573a5b162e5ed5d0868db8b Mon Sep 17 00:00:00 2001 From: Manuel Frohn Date: Fri, 15 May 2026 00:48:45 +0200 Subject: [PATCH 11/52] preclassify packets as safe --- .../src/de/steamwar/techhider/TechHider.java | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 07131759..74126417 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -382,17 +382,6 @@ public class TechHider { // --- Lighting packets --- // 7.1.48 Update Light: lighting can reveal hidden blocks/operations. processors.put(ClientboundLightUpdatePacket.class, tossPacket); - - // --- Player packets --- - // 7.1.67 Combat Death: entity/player ids and death message context. - processors.put(ClientboundPlayerCombatKillPacket.class, tossPacket); - // 7.1.68 Player Info Remove: other-player identity/state. - processors.put(ClientboundPlayerInfoRemovePacket.class, tossPacket); - // 7.1.69 Player Info Update: other-player identity/state. - processors.put(ClientboundPlayerInfoUpdatePacket.class, tossPacket); - // 7.1.70 Look At: target position/entity. - processors.put(ClientboundPlayerLookAtPacket.class, tossPacket); - // --- Others --- @@ -408,23 +397,34 @@ public class TechHider { processors.put(ClientboundMoveVehiclePacket.class, tossPacket); // 7.1.59 Open Sign Editor: block position. processors.put(ClientboundOpenSignEditorPacket.class, tossPacket); - - // 7.1.92 Set Camera: entity id. - processors.put(ClientboundSetCameraPacket.class, tossPacket); - // 7.1.108 Update Teams: player/team membership can be used as intel. - processors.put(ClientboundSetPlayerTeamPacket.class, tossPacket); - - // 7.1.123 Pickup Item: item/entity ids and pickup activity. - processors.put(ClientboundTakeItemEntityPacket.class, tossPacket); // 7.1.124 Synchronize Vehicle Position: entity/vehicle position. processors.put(ClientboundEntityPositionSyncPacket.class, tossPacket); - // 7.1.133 Update Tags (play): registry/tag data can be added back after review. - processors.put(ClientboundUpdateTagsPacket.class, tossPacket); // 7.1.134 Projectile Power: projectile/entity signal. processors.put(ClientboundProjectilePowerPacket.class, tossPacket); // 7.1.137 Waypoint: world/entity tracking signal. processors.put(ClientboundTrackedWaypointPacket.class, tossPacket); + // --- Safe; should be added to bypass --- + // 7.1.133 Update Tags (play): registry/tag data can be added back after review. + processors.put(ClientboundUpdateTagsPacket.class, tossPacket); + // 7.1.70 Look At: target position/entity. + processors.put(ClientboundPlayerLookAtPacket.class, tossPacket); + // 7.1.68 Player Info Remove: other-player identity/state. + processors.put(ClientboundPlayerInfoRemovePacket.class, tossPacket); + // 7.1.69 Player Info Update: other-player identity/state. + processors.put(ClientboundPlayerInfoUpdatePacket.class, tossPacket); + + + // --- Safe enough (not enough relevant data) to leak critical information; review and add to bypass --- + // 7.1.123 Pickup Item: item/entity ids and pickup activity. + processors.put(ClientboundTakeItemEntityPacket.class, tossPacket); + // 7.1.92 Set Camera: entity id. + processors.put(ClientboundSetCameraPacket.class, tossPacket); + // 7.1.108 Update Teams: player/team membership can be used as intel. + processors.put(ClientboundSetPlayerTeamPacket.class, tossPacket); + // 7.1.67 Combat Death: entity/player ids and death message context. + processors.put(ClientboundPlayerCombatKillPacket.class, tossPacket); + this.packetProcessors = processors; this.interceptor = new TinyProtocol(plugin) { From 9a75f38226169a3cf4361ea28cfcbbc61ffe88bb Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Fri, 15 May 2026 16:14:05 +0200 Subject: [PATCH 12/52] Continue --- .../src/de/steamwar/techhider/TechHider.java | 235 ++++++++++-------- 1 file changed, 131 insertions(+), 104 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 74126417..8859c582 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -7,6 +7,8 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.BiFunction; +import java.util.function.ToDoubleFunction; +import java.util.function.ToIntFunction; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; @@ -169,58 +171,50 @@ import net.minecraft.world.level.block.state.BlockState; * any packet must be explicitly whitelisted or processed in a * way that is also compliant with the principle of default-deny. */ -public class TechHider { +public abstract class TechHider { private final Set>> bypassingPackets; private final Map>, BiFunction, Packet>> packetProcessors; private final TinyProtocol interceptor; - private final Set blockIdsToObfuscate; - private final Set blockEntitiesToHide; - private final BlockState blockToObfuscateTo; + private final BlockState blockUsedForObfuscation; - //TODO handle packet bundle - public TechHider(Plugin plugin, Set blockIdsToObfuscate, BlockState blockToObfuscateTo, Set blockEntitiesToHide) { - this.blockIdsToObfuscate = blockIdsToObfuscate; - this.blockToObfuscateTo = blockToObfuscateTo; - this.blockEntitiesToHide = blockEntitiesToHide; + // TODO handle packet bundle + public TechHider(Plugin plugin, BlockState blockUsedForObfuscation) { + this.blockUsedForObfuscation = blockUsedForObfuscation; 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 + 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 + ClientboundCustomQueryPacket.class, // 5.1.5 Login Plugin Request + ClientboundCookieRequestPacket.class, // 5.1.6 Cookie Request - // --- 6.1.x Configuration Protocol --- + // --- 6.1.x Configuration Protocol --- 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 + 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 + 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 + 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: packets that do not expose hidden blocks, light, sounds, or entities --- + // --- 7.1.x Play Protocol --- + + // --- Safe packets; Not involved with a critical subdomain --- ClientboundBundleDelimiterPacket.class, // 7.1.1 Bundle Delimiter - ClientboundAwardStatsPacket.class, // 7.1.4 Award Statistics ClientboundBossEventPacket.class, // 7.1.10 Boss Bar ClientboundChangeDifficultyPacket.class, // 7.1.11 Change Difficulty - ClientboundChunkBatchFinishedPacket.class, // 7.1.12 Chunk Batch Finished - ClientboundChunkBatchStartPacket.class, // 7.1.13 Chunk Batch Start ClientboundClearTitlesPacket.class, // 7.1.15 Clear Titles ClientboundCommandSuggestionsPacket.class, // 7.1.16 Command Suggestions Response ClientboundCommandsPacket.class, // 7.1.17 Commands - ClientboundContainerClosePacket.class, // 7.1.18 Close Container - ClientboundContainerSetContentPacket.class, // 7.1.19 Set Container Content - ClientboundContainerSetDataPacket.class, // 7.1.20 Set Container Property - ClientboundContainerSetSlotPacket.class, // 7.1.21 Set Container Slot ClientboundCookieRequestPacket.class, // 7.1.22 Cookie Request (play) ClientboundCooldownPacket.class, // 7.1.23 Set Cooldown ClientboundCustomChatCompletionsPacket.class, // 7.1.24 Chat Suggestions @@ -241,8 +235,9 @@ public class TechHider { 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 + 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 @@ -258,16 +253,16 @@ public class TechHider { 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 - ClientboundSetChunkCacheCenterPacket.class, // 7.1.93 Set Center Chunk 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 - ClientboundSetHeldSlotPacket.class, // 7.1.104 Set Held Item (clientbound) + 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 + ClientboundSetPlayerInventoryPacket.class, // 7.1.107 Set Player Inventory Slot (Player owning the + // channel) ClientboundSetScorePacket.class, // 7.1.109 Update Score ClientboundSetSimulationDistancePacket.class, // 7.1.110 Set Simulation Distance ClientboundSetSubtitleTextPacket.class, // 7.1.111 Set Subtitle Text @@ -288,24 +283,34 @@ public class TechHider { ClientboundClearDialogPacket.class, // 7.1.138 Clear Dialog (play) ClientboundShowDialogPacket.class, // 7.1.139 Show Dialog (play) - // --- Play packets required manual review after update --- - // They include world data but non that can reveal critical information - ClientboundBlockChangedAckPacket.class, // 7.1.5 Acknowledge Block Change - ClientboundChunksBiomesPacket.class // 7.1.14 Chunk Biomes + // --- 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) + ClientboundPlayerInfoRemovePacket.class, // 7.1.68 Player Info Remove + ClientboundPlayerInfoUpdatePacket.class // 7.1.69 Player Info Update )); BiFunction, Packet> tossPacket = (p, packet) -> null; Map>, BiFunction, Packet>> processors = new HashMap<>(); - // 7.1.x Bundle Packet: packet container; blocked until nested packets are safely unbundled/processed. + // 7.1.x Bundle Packet: packet container; blocked until nested packets are + // safely unbundled/processed. processors.put(ClientboundBundlePacket.class, tossPacket); - // --- Entity packets --- + // --- Entity packets - // 7.1.2 Spawn Entity: entity type and position can reveal hidden contraptions. - processors.put(ClientboundAddEntityPacket.class, tossPacket); + processors.put(ClientboundAddEntityPacket.class, this.buildEntityWithPositionProcessor(ClientboundAddEntityPacket::getId, ClientboundAddEntityPacket::getX, ClientboundAddEntityPacket::getY, ClientboundAddEntityPacket::getZ)); // 7.1.3 Entity Animation: entity id based signal, keep blocked until entity visibility is modeled. processors.put(ClientboundAnimatePacket.class, tossPacket); - // 7.1.26 Damage Event: entity ids and damage source location can leak hidden activity. + // 7.1.26 Damage Event: entity ids and damage source location can leak hidden + // activity. processors.put(ClientboundDamageEventPacket.class, tossPacket); // 7.1.35 Entity Event: entity id based signal. processors.put(ClientboundEntityEventPacket.class, tossPacket); @@ -318,6 +323,16 @@ public class TechHider { processors.put(Pos.class, tossPacket); processors.put(PosRot.class, tossPacket); processors.put(Rot.class, tossPacket); + // 7.1.100 Set Entity Velocity: entity id and movement. + processors.put(ClientboundSetEntityMotionPacket.class, tossPacket); + // 7.1.101 Set Equipment: entity equipment can reveal wargear. + processors.put(ClientboundSetEquipmentPacket.class, tossPacket); + // 7.1.106 Set Passengers: entity relationship signal. + processors.put(ClientboundSetPassengersPacket.class, tossPacket); + // 7.1.130 Update Attributes: entity id and attribute state. + processors.put(ClientboundUpdateAttributesPacket.class, tossPacket); + // 7.1.131 Entity Effect: entity id and effect state. + processors.put(ClientboundUpdateMobEffectPacket.class, tossPacket); // 7.1.76 Remove Entities: entity id visibility side channel. processors.put(ClientboundRemoveEntitiesPacket.class, tossPacket); // 7.1.77 Remove Entity Effect: entity id and effect state. @@ -328,66 +343,53 @@ public class TechHider { processors.put(ClientboundSetEntityDataPacket.class, tossPacket); // 7.1.99 Link Entities: entity relationship signal. processors.put(ClientboundSetEntityLinkPacket.class, tossPacket); - // 7.1.100 Set Entity Velocity: entity id and movement. - processors.put(ClientboundSetEntityMotionPacket.class, tossPacket); - // 7.1.101 Set Equipment: entity equipment can reveal wargear. - processors.put(ClientboundSetEquipmentPacket.class, tossPacket); - // 7.1.106 Set Passengers: entity relationship signal. - processors.put(ClientboundSetPassengersPacket.class, tossPacket); - // 7.1.130 Update Attributes: entity id and attribute state. - processors.put(ClientboundUpdateAttributesPacket.class, tossPacket); - // 7.1.131 Entity Effect: entity id and effect state. - processors.put(ClientboundUpdateMobEffectPacket.class, tossPacket); - - + // --- Block entity packets --- - // 7.1.7 Block Entity Data: currently filtered by block position and allowed tile-entity actions. + // 7.1.7 Block Entity Data: currently filtered by block position and allowed + // tile-entity actions. processors.put(ClientboundBlockEntityDataPacket.class, (p, packet) -> processBlockEntityDataPacket(p, (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. + // 7.1.6 Set Block Destroy Stage: block position and mining progress can reveal + // activity. processors.put(ClientboundBlockDestructionPacket.class, tossPacket); // 7.1.8 Block Action: currently filtered by block position. processors.put(ClientboundBlockEventPacket.class, (p, packet) -> processBlockEventPacket(p, (ClientboundBlockEventPacket) packet)); - // 7.1.9 Block Update: currently filtered/obfuscated by block position and block id. + // 7.1.9 Block Update: currently filtered/obfuscated by block position and block + // id. processors.put(ClientboundBlockUpdatePacket.class, (p, packet) -> processBlockUpdatePacket(p, (ClientboundBlockUpdatePacket) packet)); // --- Chunk packets --- - // 7.1.38 Unload Chunk: chunk visibility side channel. - processors.put(ClientboundForgetLevelChunkPacket.class, tossPacket); // 7.1.45 Chunk Data and Update Light: currently filtered/obfuscated by chunk data processor. processors.put(ClientboundLevelChunkWithLightPacket.class, (p, packet) -> processChunkWithLight(p, (ClientboundLevelChunkWithLightPacket) packet)); - + // --- Section packets --- // 7.1.83 Update Section Blocks: currently filtered/obfuscated by section block processor. processors.put(ClientboundSectionBlocksUpdatePacket.class, (p, packet) -> processSectionUpdate(p, (ClientboundSectionBlocksUpdatePacket) packet)); - // --- Particle packets --- // 7.1.47 Particle: particle type and position can reveal hidden machinery. processors.put(ClientboundLevelParticlesPacket.class, tossPacket); - // --- Sound packets --- - // 7.1.115 Entity Sound Effect: entity id and sound. - processors.put(ClientboundSoundEntityPacket.class, tossPacket); - // 7.1.116 Sound Effect: sound type and position. - processors.put(ClientboundSoundPacket.class, tossPacket); - // 7.1.118 Stop Sound: sound state side channel. - processors.put(ClientboundStopSoundPacket.class, tossPacket); - - // --- Lighting packets --- // 7.1.48 Update Light: lighting can reveal hidden blocks/operations. processors.put(ClientboundLightUpdatePacket.class, tossPacket); + // --- Container packets --- + // 7.1.19 Set Container Content + processors.put(ClientboundContainerSetContentPacket.class, tossPacket); + // 7.1.20 Set Container Property + processors.put(ClientboundContainerSetDataPacket.class, tossPacket); + // 7.1.21 Set Container Slot + processors.put(ClientboundContainerSetSlotPacket.class, tossPacket); + // --- Others --- - - // 7.1.37 Explosion: position, affected blocks, and knockback can reveal TNT/tech. + // 7.1.37 Explosion: position, affected blocks, and knockback can reveal + // TNT/tech. processors.put(ClientboundExplodePacket.class, tossPacket); - // 7.1.46 World Event: block position/event id can leak activity. + // 7.1.46 World Event: block position/event id can leak activity. processors.put(ClientboundLevelEventPacket.class, tossPacket); // 7.1.50 Map Data: map pixels/icons can reveal player/world state. processors.put(ClientboundMapItemDataPacket.class, tossPacket); @@ -404,18 +406,8 @@ public class TechHider { // 7.1.137 Waypoint: world/entity tracking signal. processors.put(ClientboundTrackedWaypointPacket.class, tossPacket); - // --- Safe; should be added to bypass --- - // 7.1.133 Update Tags (play): registry/tag data can be added back after review. - processors.put(ClientboundUpdateTagsPacket.class, tossPacket); - // 7.1.70 Look At: target position/entity. - processors.put(ClientboundPlayerLookAtPacket.class, tossPacket); - // 7.1.68 Player Info Remove: other-player identity/state. - processors.put(ClientboundPlayerInfoRemovePacket.class, tossPacket); - // 7.1.69 Player Info Update: other-player identity/state. - processors.put(ClientboundPlayerInfoUpdatePacket.class, tossPacket); - - - // --- Safe enough (not enough relevant data) to leak critical information; review and add to bypass --- + // --- Safe enough (not enough relevant data) to leak critical information; + // review and add to bypass --- // 7.1.123 Pickup Item: item/entity ids and pickup activity. processors.put(ClientboundTakeItemEntityPacket.class, tossPacket); // 7.1.92 Set Camera: entity id. @@ -425,6 +417,14 @@ public class TechHider { // 7.1.67 Combat Death: entity/player ids and death message context. processors.put(ClientboundPlayerCombatKillPacket.class, tossPacket); + // --- Sound packets natural part of the game -> should not be hidden --- + // 7.1.115 Entity Sound Effect: entity id and sound. + processors.put(ClientboundSoundEntityPacket.class, tossPacket); + // 7.1.116 Sound Effect: sound type and position. + processors.put(ClientboundSoundPacket.class, tossPacket); + // 7.1.118 Stop Sound: sound state side channel. + processors.put(ClientboundStopSoundPacket.class, tossPacket); + this.packetProcessors = processors; this.interceptor = new TinyProtocol(plugin) { @@ -435,20 +435,47 @@ public class TechHider { return packet; } - BiFunction, Packet> processor = packetProcessors.get(packet.getClass()); - return processor == null ? null : processor.apply(receiver, (Packet) packet); - } catch(Error e) { + BiFunction, Packet> processor = packetProcessors + .get(packet.getClass()); + return processor == null ? null + : processor.apply(receiver, (Packet) packet); + } catch (Error e) { // Errors during packet processing leeding to packet being tossed e.printStackTrace(); return null; } + } }; + + } + + private Packet processEntityPacketWithPosition(Player player, int entityId, double entityX, double entityY, double entityZ, Packet packet) { + if(isPlayerPrivilegedToAccessEntity(player, entityId) && isPlayerPrivilegedToAccessPosition(player, (int) entityX, (int) entityY, (int) entityZ)) { + return packet; + } else { + return new ClientboundRemoveEntitiesPacket(entityId); + } + } + private > BiFunction, Packet> buildEntityWithPositionProcessor(ToIntFunction entityIdExtractor, ToDoubleFunction xExtractor, ToDoubleFunction yExtractor, ToDoubleFunction zExtractor) { + + return (p, rawPacket) -> { + TTargetPacket packet = (TTargetPacket) rawPacket; + processEntityPacketWithPosition(p, entityIdExtractor.applyAsInt(packet), xExtractor.applyAsDouble(packet), yExtractor.applyAsDouble(packet), zExtractor.applyAsDouble(packet), packet) + }; + } + + private Packet processEntityPacket(Player player, int entityId, Packet packet) { + if(isPlayerPrivilegedToAccessEntity(player, entityId)) { + return packet; + } else { + return new ClientboundRemoveEntitiesPacket(entityId); + } } private ClientboundBlockEventPacket processBlockEventPacket(Player player, ClientboundBlockEventPacket packet) { BlockPos blockPos = packet.getPos(); - if (isPlayerPrivilegedToAccessBlockPos(player, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { + if (isPlayerPrivilegedToAccessPosition(player, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { return packet; } else { return null; @@ -459,18 +486,19 @@ public class TechHider { BlockPos blockPos = packet.getPos(); int id = BlockIds.impl.getCombinedId(packet.getBlockState()); - if(isPlayerPrivilegedToAccessBlockPos(player, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { + if (isPlayerPrivilegedToAccessBlockPos(player, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { return packet; - } - else if (blockIdsToObfuscate.contains(id)) { - ClientboundBlockUpdatePacket modifiedPacket = new ClientboundBlockUpdatePacket(packet.getPos(), blockToObfuscateTo); + } else if (blockIdsToObfuscate.contains(id)) { + ClientboundBlockUpdatePacket modifiedPacket = new ClientboundBlockUpdatePacket(packet.getPos(), + blockToObfuscateTo); return modifiedPacket; } else { return packet; } } - private ClientboundBlockEntityDataPacket processBlockEntityDataPacket(Player p, ClientboundBlockEntityDataPacket packet) { + private ClientboundBlockEntityDataPacket processBlockEntityDataPacket(Player p, + ClientboundBlockEntityDataPacket packet) { BlockPos blockPos = packet.getPos(); if (isPlayerPrivilegedToAccessBlockPos(p, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { @@ -483,8 +511,8 @@ public class TechHider { } private final Reflection.Field sectionPosField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, SectionPos.class, 0); - private final Reflection.Field oldPosField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, short[].class, 0); - private final Reflection.Field oldStatesField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, BlockState[].class, 0); + private final Reflection.Field oldPosField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, short[].class, 0); + private final Reflection.Field oldStatesField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, BlockState[].class, 0); private ClientboundSectionBlocksUpdatePacket processSectionUpdate(Player p, ClientboundSectionBlocksUpdatePacket packet) { SectionPos sectionPos = sectionPosField.get(packet); short[] oldPos = oldPosField.get(packet); @@ -527,7 +555,6 @@ public class TechHider { return packet; } - short[] newPos = new short[filteredPos.size()]; for (int i = 0; i < newPos.length; i++) { newPos[i] = filteredPos.get(i); @@ -543,14 +570,14 @@ public class TechHider { } private final ChunkHider chunkHider = new ChunkHider(); - private ClientboundLevelChunkWithLightPacket processChunkWithLight(Player p, ClientboundLevelChunkWithLightPacket packet) { - return chunkHider.processLevelChunkWithLightPacket(p, blockEntitiesToHide, this::isPlayerPrivilegedToAccessBlockPos,blockToObfuscateTo, blockIdsToObfuscate, packet); + + private ClientboundLevelChunkWithLightPacket processChunkWithLight(Player p, ClientboundLevelChunkWithLightPacket packet) { + return chunkHider.processLevelChunkWithLightPacket(p, blockEntitiesToHide, this::isPlayerPrivilegedToAccessBlockPos, blockToObfuscateTo, blockIdsToObfuscate, packet); } - private boolean isPlayerPrivilegedToAccessBlockPos(Player p, int blockX, int blockY, int blockZ) { - return false; - } - - - -} + public abstract boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ); + public abstract boolean isPlayerPrivilegedToAccessBlock(Player p, int blockMaterialId, int blockX, int blockY,int blockZ); + public abstract boolean isPlayerPrivilegedToAccessBlockEntity(Player p, String blockEntityType, int blockX,int blockY, int blockZ); + public abstract boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId); + public abstract boolean isPlayerPrivilegedToAccessContainer(Player p, int containerId); +} \ No newline at end of file From 0452a9faa5602f4c8ccd5a5eeca50f85623e48d9 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Fri, 15 May 2026 16:23:23 +0200 Subject: [PATCH 13/52] Add processors for a sed of id only entity packets --- .../src/de/steamwar/techhider/TechHider.java | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 8859c582..65a6c97f 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -308,7 +308,7 @@ public abstract class TechHider { // 7.1.2 Spawn Entity: entity type and position can reveal hidden contraptions. processors.put(ClientboundAddEntityPacket.class, this.buildEntityWithPositionProcessor(ClientboundAddEntityPacket::getId, ClientboundAddEntityPacket::getX, ClientboundAddEntityPacket::getY, ClientboundAddEntityPacket::getZ)); // 7.1.3 Entity Animation: entity id based signal, keep blocked until entity visibility is modeled. - processors.put(ClientboundAnimatePacket.class, tossPacket); + processors.put(ClientboundAnimatePacket.class, this.buildEntityProcessor(ClientboundAnimatePacket::getId)); // 7.1.26 Damage Event: entity ids and damage source location can leak hidden // activity. processors.put(ClientboundDamageEventPacket.class, tossPacket); @@ -317,30 +317,30 @@ public abstract class TechHider { // 7.1.36 Teleport Entity: entity id and absolute position. processors.put(ClientboundTeleportEntityPacket.class, tossPacket); // 7.1.42 Hurt Animation: entity id based signal. - processors.put(ClientboundHurtAnimationPacket.class, tossPacket); + processors.put(ClientboundHurtAnimationPacket.class, this.buildEntityProcessor(ClientboundHurtAnimationPacket::id)); // 7.1.52/53/55 Update Entity Position/Rotation: entity id and movement signal. processors.put(ClientboundMoveEntityPacket.class, tossPacket); processors.put(Pos.class, tossPacket); processors.put(PosRot.class, tossPacket); processors.put(Rot.class, tossPacket); // 7.1.100 Set Entity Velocity: entity id and movement. - processors.put(ClientboundSetEntityMotionPacket.class, tossPacket); + processors.put(ClientboundSetEntityMotionPacket.class, this.buildEntityProcessor(ClientboundSetEntityMotionPacket::getId)); // 7.1.101 Set Equipment: entity equipment can reveal wargear. - processors.put(ClientboundSetEquipmentPacket.class, tossPacket); + processors.put(ClientboundSetEquipmentPacket.class, this.buildEntityProcessor(ClientboundSetEquipmentPacket::getEntity)); // 7.1.106 Set Passengers: entity relationship signal. processors.put(ClientboundSetPassengersPacket.class, tossPacket); // 7.1.130 Update Attributes: entity id and attribute state. - processors.put(ClientboundUpdateAttributesPacket.class, tossPacket); + processors.put(ClientboundUpdateAttributesPacket.class, this.buildEntityProcessor(ClientboundUpdateAttributesPacket::getEntityId)); // 7.1.131 Entity Effect: entity id and effect state. - processors.put(ClientboundUpdateMobEffectPacket.class, tossPacket); + processors.put(ClientboundUpdateMobEffectPacket.class, this.buildEntityProcessor(ClientboundUpdateMobEffectPacket::getEntityId)); // 7.1.76 Remove Entities: entity id visibility side channel. processors.put(ClientboundRemoveEntitiesPacket.class, tossPacket); // 7.1.77 Remove Entity Effect: entity id and effect state. - processors.put(ClientboundRemoveMobEffectPacket.class, tossPacket); + processors.put(ClientboundRemoveMobEffectPacket.class, this.buildEntityProcessor(ClientboundRemoveMobEffectPacket::entityId)); // 7.1.82 Set Head Rotation: entity id and rotation. processors.put(ClientboundRotateHeadPacket.class, tossPacket); // 7.1.98 Set Entity Metadata: entity state can reveal blocks/items/displays. - processors.put(ClientboundSetEntityDataPacket.class, tossPacket); + processors.put(ClientboundSetEntityDataPacket.class, this.buildEntityProcessor(ClientboundSetEntityDataPacket::id)); // 7.1.99 Link Entities: entity relationship signal. processors.put(ClientboundSetEntityLinkPacket.class, tossPacket); @@ -419,7 +419,7 @@ public abstract class TechHider { // --- Sound packets natural part of the game -> should not be hidden --- // 7.1.115 Entity Sound Effect: entity id and sound. - processors.put(ClientboundSoundEntityPacket.class, tossPacket); + processors.put(ClientboundSoundEntityPacket.class, this.buildEntityProcessor(ClientboundSoundEntityPacket::getId)); // 7.1.116 Sound Effect: sound type and position. processors.put(ClientboundSoundPacket.class, tossPacket); // 7.1.118 Stop Sound: sound state side channel. @@ -457,10 +457,9 @@ public abstract class TechHider { } } private > BiFunction, Packet> buildEntityWithPositionProcessor(ToIntFunction entityIdExtractor, ToDoubleFunction xExtractor, ToDoubleFunction yExtractor, ToDoubleFunction zExtractor) { - return (p, rawPacket) -> { TTargetPacket packet = (TTargetPacket) rawPacket; - processEntityPacketWithPosition(p, entityIdExtractor.applyAsInt(packet), xExtractor.applyAsDouble(packet), yExtractor.applyAsDouble(packet), zExtractor.applyAsDouble(packet), packet) + return processEntityPacketWithPosition(p, entityIdExtractor.applyAsInt(packet), xExtractor.applyAsDouble(packet), yExtractor.applyAsDouble(packet), zExtractor.applyAsDouble(packet), packet); }; } @@ -471,6 +470,12 @@ public abstract class TechHider { return new ClientboundRemoveEntitiesPacket(entityId); } } + private > BiFunction, Packet> buildEntityProcessor(ToIntFunction entityIdExtractor) { + return (p, rawPacket) -> { + TTargetPacket packet = (TTargetPacket) rawPacket; + return processEntityPacket(p, entityIdExtractor.applyAsInt(packet), packet); + }; + } private ClientboundBlockEventPacket processBlockEventPacket(Player player, ClientboundBlockEventPacket packet) { BlockPos blockPos = packet.getPos(); @@ -580,4 +585,4 @@ public abstract class TechHider { public abstract boolean isPlayerPrivilegedToAccessBlockEntity(Player p, String blockEntityType, int blockX,int blockY, int blockZ); public abstract boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId); public abstract boolean isPlayerPrivilegedToAccessContainer(Player p, int containerId); -} \ No newline at end of file +} From 4a43e09a8be6119ef62e28eca5d6df69a92437b7 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Sat, 16 May 2026 14:45:36 +0200 Subject: [PATCH 14/52] Add more handlers --- .../src/de/steamwar/techhider/TechHider.java | 54 +++++++++---------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 65a6c97f..e0370955 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -306,41 +306,37 @@ public abstract class TechHider { // --- Entity packets - // 7.1.2 Spawn Entity: entity type and position can reveal hidden contraptions. - processors.put(ClientboundAddEntityPacket.class, this.buildEntityWithPositionProcessor(ClientboundAddEntityPacket::getId, ClientboundAddEntityPacket::getX, ClientboundAddEntityPacket::getY, ClientboundAddEntityPacket::getZ)); + processors.put(ClientboundAddEntityPacket.class, this.buildEntityWithPositionProcessor(ClientboundAddEntityPacket::getId, ClientboundAddEntityPacket::getX, ClientboundAddEntityPacket::getY, ClientboundAddEntityPacket::getZ)); // 7.1.3 Entity Animation: entity id based signal, keep blocked until entity visibility is modeled. - processors.put(ClientboundAnimatePacket.class, this.buildEntityProcessor(ClientboundAnimatePacket::getId)); - // 7.1.26 Damage Event: entity ids and damage source location can leak hidden - // activity. - processors.put(ClientboundDamageEventPacket.class, tossPacket); + processors.put(ClientboundAnimatePacket.class, this.buildEntityProcessor(ClientboundAnimatePacket::getId)); // 7.1.35 Entity Event: entity id based signal. - processors.put(ClientboundEntityEventPacket.class, tossPacket); + processors.put(ClientboundEntityEventPacket.class, this.buildEntityProcessor(ClientboundEntityEventPacket::getEventId)); // 7.1.36 Teleport Entity: entity id and absolute position. - processors.put(ClientboundTeleportEntityPacket.class, tossPacket); + processors.put(ClientboundTeleportEntityPacket.class, this.buildEntityProcessor(ClientboundTeleportEntityPacket::id)); // 7.1.42 Hurt Animation: entity id based signal. - processors.put(ClientboundHurtAnimationPacket.class, this.buildEntityProcessor(ClientboundHurtAnimationPacket::id)); + processors.put(ClientboundHurtAnimationPacket.class, this.buildEntityProcessor(ClientboundHurtAnimationPacket::id)); + // 7.1.100 Set Entity Velocity: entity id and movement. + processors.put(ClientboundSetEntityMotionPacket.class, this.buildEntityProcessor(ClientboundSetEntityMotionPacket::getId)); + // 7.1.101 Set Equipment: entity equipment can reveal wargear. + processors.put(ClientboundSetEquipmentPacket.class, this.buildEntityProcessor(ClientboundSetEquipmentPacket::getEntity)); + // 7.1.106 Set Passengers: entity relationship signal. + processors.put(ClientboundSetPassengersPacket.class, this.buildEntityProcessor(ClientboundSetPassengersPacket::getVehicle)); + // 7.1.130 Update Attributes: entity id and attribute state. + processors.put(ClientboundUpdateAttributesPacket.class, this.buildEntityProcessor(ClientboundUpdateAttributesPacket::getEntityId)); + // 7.1.131 Entity Effect: entity id and effect state. + processors.put(ClientboundUpdateMobEffectPacket.class, this.buildEntityProcessor(ClientboundUpdateMobEffectPacket::getEntityId)); + // 7.1.77 Remove Entity Effect: entity id and effect state. + processors.put(ClientboundRemoveMobEffectPacket.class, this.buildEntityProcessor(ClientboundRemoveMobEffectPacket::entityId)); + // 7.1.98 Set Entity Metadata: entity state can reveal blocks/items/displays. + processors.put(ClientboundSetEntityDataPacket.class, this.buildEntityProcessor(ClientboundSetEntityDataPacket::id)); + // 7.1.26 Damage Event: entity ids and damage source location can leak hidde activity. + processors.put(ClientboundDamageEventPacket.class, tossPacket); // 7.1.52/53/55 Update Entity Position/Rotation: entity id and movement signal. processors.put(ClientboundMoveEntityPacket.class, tossPacket); - processors.put(Pos.class, tossPacket); - processors.put(PosRot.class, tossPacket); - processors.put(Rot.class, tossPacket); - // 7.1.100 Set Entity Velocity: entity id and movement. - processors.put(ClientboundSetEntityMotionPacket.class, this.buildEntityProcessor(ClientboundSetEntityMotionPacket::getId)); - // 7.1.101 Set Equipment: entity equipment can reveal wargear. - processors.put(ClientboundSetEquipmentPacket.class, this.buildEntityProcessor(ClientboundSetEquipmentPacket::getEntity)); - // 7.1.106 Set Passengers: entity relationship signal. - processors.put(ClientboundSetPassengersPacket.class, tossPacket); - // 7.1.130 Update Attributes: entity id and attribute state. - processors.put(ClientboundUpdateAttributesPacket.class, this.buildEntityProcessor(ClientboundUpdateAttributesPacket::getEntityId)); - // 7.1.131 Entity Effect: entity id and effect state. - processors.put(ClientboundUpdateMobEffectPacket.class, this.buildEntityProcessor(ClientboundUpdateMobEffectPacket::getEntityId)); - // 7.1.76 Remove Entities: entity id visibility side channel. - processors.put(ClientboundRemoveEntitiesPacket.class, tossPacket); - // 7.1.77 Remove Entity Effect: entity id and effect state. - processors.put(ClientboundRemoveMobEffectPacket.class, this.buildEntityProcessor(ClientboundRemoveMobEffectPacket::entityId)); // 7.1.82 Set Head Rotation: entity id and rotation. processors.put(ClientboundRotateHeadPacket.class, tossPacket); - // 7.1.98 Set Entity Metadata: entity state can reveal blocks/items/displays. - processors.put(ClientboundSetEntityDataPacket.class, this.buildEntityProcessor(ClientboundSetEntityDataPacket::id)); + // 7.1.76 Remove Entities: entity id visibility side channel. + processors.put(ClientboundRemoveEntitiesPacket.class, tossPacket); // 7.1.99 Link Entities: entity relationship signal. processors.put(ClientboundSetEntityLinkPacket.class, tossPacket); @@ -453,7 +449,7 @@ public abstract class TechHider { if(isPlayerPrivilegedToAccessEntity(player, entityId) && isPlayerPrivilegedToAccessPosition(player, (int) entityX, (int) entityY, (int) entityZ)) { return packet; } else { - return new ClientboundRemoveEntitiesPacket(entityId); + return null; } } private > BiFunction, Packet> buildEntityWithPositionProcessor(ToIntFunction entityIdExtractor, ToDoubleFunction xExtractor, ToDoubleFunction yExtractor, ToDoubleFunction zExtractor) { @@ -467,7 +463,7 @@ public abstract class TechHider { if(isPlayerPrivilegedToAccessEntity(player, entityId)) { return packet; } else { - return new ClientboundRemoveEntitiesPacket(entityId); + return null; } } private > BiFunction, Packet> buildEntityProcessor(ToIntFunction entityIdExtractor) { From 6f1a3fe70c9f4675bb628bd443dec990134c5733 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Sat, 16 May 2026 19:19:49 +0200 Subject: [PATCH 15/52] Update a lot of handlers and prepare for merge with main --- .../src/de/steamwar/techhider/TechHider.java | 186 +++++++++++------- 1 file changed, 110 insertions(+), 76 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index e0370955..70649d41 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -10,6 +10,8 @@ import java.util.function.BiFunction; import java.util.function.ToDoubleFunction; import java.util.function.ToIntFunction; +import it.unimi.dsi.fastutil.ints.IntList; +import net.minecraft.world.level.block.entity.BlockEntityType; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; @@ -174,12 +176,15 @@ import net.minecraft.world.level.block.state.BlockState; public abstract class TechHider { private final Set>> bypassingPackets; private final Map>, BiFunction, Packet>> packetProcessors; - private final TinyProtocol interceptor; - private final BlockState blockUsedForObfuscation; + + private final Block blockUsedForObfuscation; + private final BlockState blockStateUsedForObfuscation; // TODO handle packet bundle - public TechHider(Plugin plugin, BlockState blockUsedForObfuscation) { + public TechHider(Plugin plugin, Block blockUsedForObfuscation) { this.blockUsedForObfuscation = blockUsedForObfuscation; + this.blockStateUsedForObfuscation = blockUsedForObfuscation.defaultBlockState(); + this.bypassingPackets = new HashSet<>(List.of( // --- 5.1.x Login Protocol --- ClientboundLoginDisconnectPacket.class, // 5.1.1 Disconnect @@ -306,55 +311,52 @@ public abstract class TechHider { // --- Entity packets - // 7.1.2 Spawn Entity: entity type and position can reveal hidden contraptions. - processors.put(ClientboundAddEntityPacket.class, this.buildEntityWithPositionProcessor(ClientboundAddEntityPacket::getId, ClientboundAddEntityPacket::getX, ClientboundAddEntityPacket::getY, ClientboundAddEntityPacket::getZ)); + processors.put(ClientboundAddEntityPacket.class, (p, packet) -> this.processAddEntityPacket(p, (ClientboundAddEntityPacket) packet)); // 7.1.3 Entity Animation: entity id based signal, keep blocked until entity visibility is modeled. - processors.put(ClientboundAnimatePacket.class, this.buildEntityProcessor(ClientboundAnimatePacket::getId)); + processors.put(ClientboundAnimatePacket.class, this.buildEntityPacketProcessor(ClientboundAnimatePacket::getId)); // 7.1.35 Entity Event: entity id based signal. - processors.put(ClientboundEntityEventPacket.class, this.buildEntityProcessor(ClientboundEntityEventPacket::getEventId)); + processors.put(ClientboundEntityEventPacket.class, this.buildEntityPacketProcessor(ClientboundEntityEventPacket::getEventId)); // 7.1.36 Teleport Entity: entity id and absolute position. - processors.put(ClientboundTeleportEntityPacket.class, this.buildEntityProcessor(ClientboundTeleportEntityPacket::id)); + processors.put(ClientboundTeleportEntityPacket.class, this.buildEntityPacketProcessor(ClientboundTeleportEntityPacket::id)); // 7.1.42 Hurt Animation: entity id based signal. - processors.put(ClientboundHurtAnimationPacket.class, this.buildEntityProcessor(ClientboundHurtAnimationPacket::id)); + processors.put(ClientboundHurtAnimationPacket.class, this.buildEntityPacketProcessor(ClientboundHurtAnimationPacket::id)); // 7.1.100 Set Entity Velocity: entity id and movement. - processors.put(ClientboundSetEntityMotionPacket.class, this.buildEntityProcessor(ClientboundSetEntityMotionPacket::getId)); + processors.put(ClientboundSetEntityMotionPacket.class, this.buildEntityPacketProcessor(ClientboundSetEntityMotionPacket::getId)); // 7.1.101 Set Equipment: entity equipment can reveal wargear. - processors.put(ClientboundSetEquipmentPacket.class, this.buildEntityProcessor(ClientboundSetEquipmentPacket::getEntity)); + processors.put(ClientboundSetEquipmentPacket.class, this.buildEntityPacketProcessor(ClientboundSetEquipmentPacket::getEntity)); // 7.1.106 Set Passengers: entity relationship signal. - processors.put(ClientboundSetPassengersPacket.class, this.buildEntityProcessor(ClientboundSetPassengersPacket::getVehicle)); + processors.put(ClientboundSetPassengersPacket.class, this.buildEntityPacketProcessor(ClientboundSetPassengersPacket::getVehicle)); // 7.1.130 Update Attributes: entity id and attribute state. - processors.put(ClientboundUpdateAttributesPacket.class, this.buildEntityProcessor(ClientboundUpdateAttributesPacket::getEntityId)); + processors.put(ClientboundUpdateAttributesPacket.class, this.buildEntityPacketProcessor(ClientboundUpdateAttributesPacket::getEntityId)); // 7.1.131 Entity Effect: entity id and effect state. - processors.put(ClientboundUpdateMobEffectPacket.class, this.buildEntityProcessor(ClientboundUpdateMobEffectPacket::getEntityId)); + processors.put(ClientboundUpdateMobEffectPacket.class, this.buildEntityPacketProcessor(ClientboundUpdateMobEffectPacket::getEntityId)); // 7.1.77 Remove Entity Effect: entity id and effect state. - processors.put(ClientboundRemoveMobEffectPacket.class, this.buildEntityProcessor(ClientboundRemoveMobEffectPacket::entityId)); + 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.buildEntityProcessor(ClientboundSetEntityDataPacket::id)); + processors.put(ClientboundSetEntityDataPacket.class, this.buildEntityPacketProcessor(ClientboundSetEntityDataPacket::id)); // 7.1.26 Damage Event: entity ids and damage source location can leak hidde activity. - processors.put(ClientboundDamageEventPacket.class, tossPacket); + processors.put(ClientboundDamageEventPacket.class, this.buildEntityPacketProcessor(ClientboundDamageEventPacket::entityId)); // 7.1.52/53/55 Update Entity Position/Rotation: entity id and movement signal. - processors.put(ClientboundMoveEntityPacket.class, tossPacket); + processors.put(ClientboundMoveEntityPacket.class, (p, packet) -> this.processMoveEntityPacket(p, (ClientboundMoveEntityPacket) packet)); // 7.1.82 Set Head Rotation: entity id and rotation. - processors.put(ClientboundRotateHeadPacket.class, tossPacket); + processors.put(ClientboundRotateHeadPacket.class, (p, packet) -> this.processRotateHeadPacket(p, (ClientboundRotateHeadPacket) packet)); // 7.1.76 Remove Entities: entity id visibility side channel. processors.put(ClientboundRemoveEntitiesPacket.class, tossPacket); // 7.1.99 Link Entities: entity relationship signal. - processors.put(ClientboundSetEntityLinkPacket.class, tossPacket); + processors.put(ClientboundSetEntityLinkPacket.class, (p, packet) -> this.processSetEntityLinkPacket(p, (ClientboundSetEntityLinkPacket) packet)); // --- Block entity packets --- - // 7.1.7 Block Entity Data: currently filtered by block position and allowed - // tile-entity actions. + // 7.1.7 Block Entity Data: currently filtered by block position and allowed tile-entity actions. processors.put(ClientboundBlockEntityDataPacket.class, (p, packet) -> processBlockEntityDataPacket(p, (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, tossPacket); + // 7.1.6 Set Block Destroy Stage: block position and mining progress can reveal activity. + processors.put(ClientboundBlockDestructionPacket.class, (p, packet) -> proccessBlockDestructionPacket(p, (ClientboundBlockDestructionPacket) packet)); // 7.1.8 Block Action: currently filtered by block position. processors.put(ClientboundBlockEventPacket.class, (p, packet) -> processBlockEventPacket(p, (ClientboundBlockEventPacket) packet)); - // 7.1.9 Block Update: currently filtered/obfuscated by block position and block - // id. + // 7.1.9 Block Update: currently filtered/obfuscated by block position and block id. processors.put(ClientboundBlockUpdatePacket.class, (p, packet) -> processBlockUpdatePacket(p, (ClientboundBlockUpdatePacket) packet)); // --- Chunk packets --- @@ -415,49 +417,22 @@ public abstract class TechHider { // --- Sound packets natural part of the game -> should not be hidden --- // 7.1.115 Entity Sound Effect: entity id and sound. - processors.put(ClientboundSoundEntityPacket.class, this.buildEntityProcessor(ClientboundSoundEntityPacket::getId)); + processors.put(ClientboundSoundEntityPacket.class, tossPacket); // 7.1.116 Sound Effect: sound type and position. processors.put(ClientboundSoundPacket.class, tossPacket); // 7.1.118 Stop Sound: sound state side channel. processors.put(ClientboundStopSoundPacket.class, tossPacket); this.packetProcessors = processors; + } - this.interceptor = new TinyProtocol(plugin) { - @Override - public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) { - try { - if (bypassingPackets.contains(packet.getClass())) { - return packet; - } - - BiFunction, Packet> processor = packetProcessors - .get(packet.getClass()); - return processor == null ? null - : processor.apply(receiver, (Packet) packet); - } catch (Error e) { - // Errors during packet processing leeding to packet being tossed - e.printStackTrace(); - return null; - } - } - }; - - } - - private Packet processEntityPacketWithPosition(Player player, int entityId, double entityX, double entityY, double entityZ, Packet packet) { - if(isPlayerPrivilegedToAccessEntity(player, entityId) && isPlayerPrivilegedToAccessPosition(player, (int) entityX, (int) entityY, (int) entityZ)) { + private Packet processAddEntityPacket(Player player, ClientboundAddEntityPacket packet) { + if(isPlayerPrivilegedToAccessEntity(player, packet.getId()) && isPlayerPrivilegedToAccessPosition(player, (int) packet.getX(), (int) packet.getY(), (int) packet.getZ())) { return packet; } else { return null; } } - private > BiFunction, Packet> buildEntityWithPositionProcessor(ToIntFunction entityIdExtractor, ToDoubleFunction xExtractor, ToDoubleFunction yExtractor, ToDoubleFunction zExtractor) { - return (p, rawPacket) -> { - TTargetPacket packet = (TTargetPacket) rawPacket; - return processEntityPacketWithPosition(p, entityIdExtractor.applyAsInt(packet), xExtractor.applyAsDouble(packet), yExtractor.applyAsDouble(packet), zExtractor.applyAsDouble(packet), packet); - }; - } private Packet processEntityPacket(Player player, int entityId, Packet packet) { if(isPlayerPrivilegedToAccessEntity(player, entityId)) { @@ -466,51 +441,110 @@ public abstract class TechHider { return null; } } - private > BiFunction, Packet> buildEntityProcessor(ToIntFunction entityIdExtractor) { + private > BiFunction, Packet> buildEntityPacketProcessor(ToIntFunction entityIdExtractor) { return (p, rawPacket) -> { TTargetPacket packet = (TTargetPacket) rawPacket; return processEntityPacket(p, entityIdExtractor.applyAsInt(packet), packet); }; } - private ClientboundBlockEventPacket processBlockEventPacket(Player player, ClientboundBlockEventPacket packet) { - BlockPos blockPos = packet.getPos(); + private final Reflection.Field moveEntityPacketEntityIdField = Reflection.getField(ClientboundMoveEntityPacket.class, Integer.class, 0); + private Packet processMoveEntityPacket(Player player, ClientboundMoveEntityPacket packet) { + int entityId = moveEntityPacketEntityIdField.get(packet); - if (isPlayerPrivilegedToAccessPosition(player, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { + if(isPlayerPrivilegedToAccessEntity(player, entityId)) { + return packet; + } + else { + return null; + } + } + + private final Reflection.Field rotateHeadPacketEntityIdField = Reflection.getField(ClientboundRotateHeadPacket.class, Integer.class, 0); + private Packet processRotateHeadPacket(Player player, ClientboundRotateHeadPacket packet) { + int entityId = rotateHeadPacketEntityIdField.get(packet); + + if(isPlayerPrivilegedToAccessEntity(player, entityId)) { + return packet; + } + else { + return null; + } + } + + private Packet processRemoveEntitiesPacket(Player player, ClientboundRemoveEntitiesPacket packet) { + // TODO + return null; + } + + public Packet processSetEntityLinkPacket(Player player, ClientboundSetEntityLinkPacket packet) { + int fromEntityId = packet.getSourceId(); + int toEntityId = packet.getDestId(); + + if(isPlayerPrivilegedToAccessEntity(player, fromEntityId) && isPlayerPrivilegedToAccessEntity(player, toEntityId)) { + return packet; + } + else { + return null; + } + } + + private Packet processBlockEventPacket(Player player, ClientboundBlockEventPacket packet) { + BlockPos blockPos = packet.getPos(); + Block block = packet.getBlock(); + int blockX = blockPos.getX(); + int blockY = blockPos.getY(); + int blockZ = blockPos.getZ(); + + if (isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && isPlayerPrivilegedToAccessBlock(player, blockX, blockY, blockZ, block)) { return packet; } else { return null; } } - private ClientboundBlockUpdatePacket processBlockUpdatePacket(Player player, ClientboundBlockUpdatePacket packet) { + private Packet processBlockUpdatePacket(Player player, ClientboundBlockUpdatePacket packet) { BlockPos blockPos = packet.getPos(); - int id = BlockIds.impl.getCombinedId(packet.getBlockState()); + Block block = packet.getBlockState().getBlock(); + int blockX = blockPos.getX(); + int blockY = blockPos.getY(); + int blockZ = blockPos.getZ(); - if (isPlayerPrivilegedToAccessBlockPos(player, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { + if (isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && isPlayerPrivilegedToAccessBlock(player, blockX, blockY, blockZ, block)) { return packet; - } else if (blockIdsToObfuscate.contains(id)) { - ClientboundBlockUpdatePacket modifiedPacket = new ClientboundBlockUpdatePacket(packet.getPos(), - blockToObfuscateTo); - return modifiedPacket; + } else if(isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) { + return new ClientboundBlockUpdatePacket(blockPos, blockStateUsedForObfuscation); } else { - return packet; + return null; } } - private ClientboundBlockEntityDataPacket processBlockEntityDataPacket(Player p, - ClientboundBlockEntityDataPacket packet) { + private Packet processBlockEntityDataPacket(Player player, ClientboundBlockEntityDataPacket packet) { BlockPos blockPos = packet.getPos(); + BlockEntityType blockEntityType = packet.getType(); + int blockX = blockPos.getX(); + int blockY = blockPos.getY(); + int blockZ = blockPos.getZ(); - if (isPlayerPrivilegedToAccessBlockPos(p, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { - return packet; - } else if (ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet)) { + if (isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && isPlayerPrivilegedToAccessBlocEntity(player, blockX, blockY, blockZ, blockEntityType)) { return packet; } else { return null; } } + private Packet proccessBlockDestructionPacket(Player player, ClientboundBlockDestructionPacket packet) { + BlockPos blockPos = packet.getPos(); + int entityBreakingBlockId = packet.getId(); + + if(isPlayerPrivilegedToAccessEntity(player, entityBreakingBlockId) && isPlayerPrivilegedToAccessPosition(player, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { + return packet; + } + else { + return null; + } + } + private final Reflection.Field sectionPosField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, SectionPos.class, 0); private final Reflection.Field oldPosField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, short[].class, 0); private final Reflection.Field oldStatesField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, BlockState[].class, 0); @@ -577,8 +611,8 @@ public abstract class TechHider { } public abstract boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ); - public abstract boolean isPlayerPrivilegedToAccessBlock(Player p, int blockMaterialId, int blockX, int blockY,int blockZ); - public abstract boolean isPlayerPrivilegedToAccessBlockEntity(Player p, String blockEntityType, int blockX,int blockY, int blockZ); + public abstract boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block); public abstract boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId); + public abstract boolean isPlayerPrivilegedToAccessBlocEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type); public abstract boolean isPlayerPrivilegedToAccessContainer(Player p, int containerId); } From 88de28ed689aa22b9eff0cbb18960208117c55c8 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Sat, 16 May 2026 19:25:32 +0200 Subject: [PATCH 16/52] Add new handler --- .../src/de/steamwar/techhider/TechHider.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 70649d41..7583de12 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -369,7 +369,7 @@ public abstract class TechHider { // --- Particle packets --- // 7.1.47 Particle: particle type and position can reveal hidden machinery. - processors.put(ClientboundLevelParticlesPacket.class, tossPacket); + processors.put(ClientboundLevelParticlesPacket.class, (p, packet) -> processLevelParticlesPacket(p, (ClientboundLevelParticlesPacket) packet)); // --- Lighting packets --- // 7.1.48 Update Light: lighting can reveal hidden blocks/operations. @@ -604,6 +604,19 @@ public abstract class TechHider { newStates); } + private Packet processLevelParticlesPacket(Player player, ClientboundLevelParticlesPacket packet) { + int blockX = (int) packet.getX(); + int blockY = (int) packet.getY(); + int blockZ = (int) packet.getZ(); + + if(isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) { + return packet; + } + else { + return null; + } + } + private final ChunkHider chunkHider = new ChunkHider(); private ClientboundLevelChunkWithLightPacket processChunkWithLight(Player p, ClientboundLevelChunkWithLightPacket packet) { From 723a7dc0ca51fd148c4c23a0fc00e4209aa8e32f Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Sat, 16 May 2026 20:14:09 +0200 Subject: [PATCH 17/52] Add handling for container --- .../src/de/steamwar/techhider/TechHider.java | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 7583de12..48068779 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -11,6 +11,7 @@ import java.util.function.ToDoubleFunction; import java.util.function.ToIntFunction; import it.unimi.dsi.fastutil.ints.IntList; +import net.minecraft.server.packs.repository.Pack; import net.minecraft.world.level.block.entity.BlockEntityType; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; @@ -377,11 +378,11 @@ public abstract class TechHider { // --- Container packets --- // 7.1.19 Set Container Content - processors.put(ClientboundContainerSetContentPacket.class, tossPacket); + processors.put(ClientboundContainerSetContentPacket.class, buildContainerPacketProcessor(ClientboundContainerSetContentPacket::containerId)); // 7.1.20 Set Container Property - processors.put(ClientboundContainerSetDataPacket.class, tossPacket); + processors.put(ClientboundContainerSetDataPacket.class, buildContainerPacketProcessor(ClientboundContainerSetDataPacket::getContainerId)); // 7.1.21 Set Container Slot - processors.put(ClientboundContainerSetSlotPacket.class, tossPacket); + processors.put(ClientboundContainerSetSlotPacket.class, buildContainerPacketProcessor(ClientboundContainerSetSlotPacket::getContainerId)); // --- Others --- // 7.1.37 Explosion: position, affected blocks, and knockback can reveal @@ -618,11 +619,25 @@ public abstract class TechHider { } private final ChunkHider chunkHider = new ChunkHider(); - private ClientboundLevelChunkWithLightPacket processChunkWithLight(Player p, ClientboundLevelChunkWithLightPacket packet) { return chunkHider.processLevelChunkWithLightPacket(p, blockEntitiesToHide, this::isPlayerPrivilegedToAccessBlockPos, blockToObfuscateTo, blockIdsToObfuscate, packet); } + private Packet processContainerPacket(Player player, int containerId, Packet packet) { + if(isPlayerPrivilegedToAccessContainer(player, containerId)) { + return packet; + } else { + return null; + } + } + private > BiFunction, Packet> buildContainerPacketProcessor(ToIntFunction containerIdExtractor) { + return (p, rawPacket) -> { + TTargetPacket packet = (TTargetPacket) rawPacket; + + return processContainerPacket(p, containerIdExtractor.applyAsInt(packet), packet); + }; + } + public abstract boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ); public abstract boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block); public abstract boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId); From 3bd1cf71677fd65af6cbea1911bfb2a883ce7d80 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Sat, 16 May 2026 22:12:25 +0200 Subject: [PATCH 18/52] Even more handlers --- .../src/de/steamwar/techhider/TechHider.java | 117 +++++++++++------- 1 file changed, 73 insertions(+), 44 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 48068779..c3a467cc 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -7,12 +7,17 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.BiFunction; +import java.util.function.Function; import java.util.function.ToDoubleFunction; import java.util.function.ToIntFunction; +import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.shorts.ShortArraySet; +import it.unimi.dsi.fastutil.shorts.ShortSets; import net.minecraft.server.packs.repository.Pack; import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.phys.Vec3; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; @@ -259,6 +264,7 @@ public abstract class TechHider { 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 @@ -267,8 +273,8 @@ public abstract class TechHider { 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) + 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 @@ -289,8 +295,7 @@ public abstract class TechHider { 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 --- + // --- 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) @@ -300,7 +305,8 @@ public abstract class TechHider { ClientboundForgetLevelChunkPacket.class, // 7.1.38 Unload Chunk ClientboundUpdateTagsPacket.class, // 7.1.133 Update Tags (play) ClientboundPlayerInfoRemovePacket.class, // 7.1.68 Player Info Remove - ClientboundPlayerInfoUpdatePacket.class // 7.1.69 Player Info Update + ClientboundPlayerInfoUpdatePacket.class, // 7.1.69 Player Info Update + ClientboundMoveVehiclePacket.class // 7.1.56 Move Vehicle (vehicle the player is in) )); BiFunction, Packet> tossPacket = (p, packet) -> null; @@ -337,12 +343,22 @@ public abstract class TechHider { processors.put(ClientboundSetEntityDataPacket.class, this.buildEntityPacketProcessor(ClientboundSetEntityDataPacket::id)); // 7.1.26 Damage Event: entity ids and damage source location can leak hidde 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, (p, packet) -> this.processMoveEntityPacket(p, (ClientboundMoveEntityPacket) packet)); // 7.1.82 Set Head Rotation: entity id and rotation. processors.put(ClientboundRotateHeadPacket.class, (p, packet) -> this.processRotateHeadPacket(p, (ClientboundRotateHeadPacket) packet)); // 7.1.76 Remove Entities: entity id visibility side channel. - processors.put(ClientboundRemoveEntitiesPacket.class, tossPacket); + processors.put(ClientboundRemoveEntitiesPacket.class, (p, packet) -> this.processRemoveEntitiesPacket(p, (ClientboundRemoveEntitiesPacket) packet)); // 7.1.99 Link Entities: entity relationship signal. processors.put(ClientboundSetEntityLinkPacket.class, (p, packet) -> this.processSetEntityLinkPacket(p, (ClientboundSetEntityLinkPacket) packet)); @@ -384,39 +400,7 @@ public abstract class TechHider { // 7.1.21 Set Container Slot processors.put(ClientboundContainerSetSlotPacket.class, buildContainerPacketProcessor(ClientboundContainerSetSlotPacket::getContainerId)); - // --- Others --- - // 7.1.37 Explosion: position, affected blocks, and knockback can reveal - // TNT/tech. - processors.put(ClientboundExplodePacket.class, tossPacket); - // 7.1.46 World Event: block position/event id can leak activity. - processors.put(ClientboundLevelEventPacket.class, tossPacket); - // 7.1.50 Map Data: map pixels/icons can reveal player/world state. - processors.put(ClientboundMapItemDataPacket.class, tossPacket); - // 7.1.54 Move Minecart Along Track: entity path and position signal. - processors.put(ClientboundMoveMinecartPacket.class, tossPacket); - // 7.1.56 Move Vehicle (clientbound): vehicle position signal. - processors.put(ClientboundMoveVehiclePacket.class, tossPacket); - // 7.1.59 Open Sign Editor: block position. - processors.put(ClientboundOpenSignEditorPacket.class, tossPacket); - // 7.1.124 Synchronize Vehicle Position: entity/vehicle position. - processors.put(ClientboundEntityPositionSyncPacket.class, tossPacket); - // 7.1.134 Projectile Power: projectile/entity signal. - processors.put(ClientboundProjectilePowerPacket.class, tossPacket); - // 7.1.137 Waypoint: world/entity tracking signal. - processors.put(ClientboundTrackedWaypointPacket.class, tossPacket); - - // --- Safe enough (not enough relevant data) to leak critical information; - // review and add to bypass --- - // 7.1.123 Pickup Item: item/entity ids and pickup activity. - processors.put(ClientboundTakeItemEntityPacket.class, tossPacket); - // 7.1.92 Set Camera: entity id. - processors.put(ClientboundSetCameraPacket.class, tossPacket); - // 7.1.108 Update Teams: player/team membership can be used as intel. - processors.put(ClientboundSetPlayerTeamPacket.class, tossPacket); - // 7.1.67 Combat Death: entity/player ids and death message context. - processors.put(ClientboundPlayerCombatKillPacket.class, tossPacket); - - // --- Sound packets natural part of the game -> should not be hidden --- + // --- Sound packets --- // 7.1.115 Entity Sound Effect: entity id and sound. processors.put(ClientboundSoundEntityPacket.class, tossPacket); // 7.1.116 Sound Effect: sound type and position. @@ -424,6 +408,27 @@ public abstract class TechHider { // 7.1.118 Stop Sound: sound state side channel. processors.put(ClientboundStopSoundPacket.class, tossPacket); + // --- 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(ClientboundLevelEventPacket::getPos)); + // 7.1.37 Explosion: position, affected blocks, and knockback can reveal TNT/tech. + processors.put(ClientboundExplodePacket.class, (p, rawPacket) -> { + ClientboundExplodePacket packet = (ClientboundExplodePacket) rawPacket; + Vec3 pos = packet.center(); + int blockX = (int) pos.x; + int blockY = (int) pos.y; + int blockZ = (int) pos.z; + + + return proccessPositionBasedPacket(p, blockX, blockY, blockZ, packet); + }); + this.packetProcessors = processors; } @@ -474,8 +479,13 @@ public abstract class TechHider { } private Packet processRemoveEntitiesPacket(Player player, ClientboundRemoveEntitiesPacket packet) { - // TODO - return null; + IntList entityIdsToRemove = packet.getEntityIds(); + + IntList filteredEntitiesToRemove = entityIdsToRemove.intStream() + .filter((id) -> isPlayerPrivilegedToAccessEntity(player, id)) + .collect(IntArrayList::new, IntList::add, IntList::addAll); + + return new ClientboundRemoveEntitiesPacket(filteredEntitiesToRemove); } public Packet processSetEntityLinkPacket(Player player, ClientboundSetEntityLinkPacket packet) { @@ -600,9 +610,9 @@ public abstract class TechHider { return new ClientboundSectionBlocksUpdatePacket( sectionPos, - it.unimi.dsi.fastutil.shorts.ShortSets - .unmodifiable(new it.unimi.dsi.fastutil.shorts.ShortArraySet(newPos)), - newStates); + ShortSets.unmodifiable(new ShortArraySet(newPos)), + newStates + ); } private Packet processLevelParticlesPacket(Player player, ClientboundLevelParticlesPacket packet) { @@ -638,6 +648,25 @@ public abstract class TechHider { }; } + private Packet proccessPositionBasedPacket(Player player, int blockX, int blockY, int blockZ, Packet packet) { + if(isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) { + return packet; + } else { + return null; + } + } + private > BiFunction, Packet> buildPositionBasedPacketProcessor(Function positionExtractor) { + return (p, rawPacket) -> { + TTargetPacket packet = (TTargetPacket) rawPacket; + BlockPos pos = positionExtractor.apply(packet); + int blockX = pos.getX(); + int blockY = pos.getY(); + int blockZ = pos.getZ(); + + return proccessPositionBasedPacket(p, blockX, blockY, blockZ, packet); + }; + } + public abstract boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ); public abstract boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block); public abstract boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId); From 9f319128cc6ff2843270ff0d4a5ede5eb11bb71f Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Sun, 17 May 2026 13:49:46 +0200 Subject: [PATCH 19/52] Update multiblock update packet filter --- .../src/de/steamwar/techhider/TechHider.java | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index c3a467cc..61358c26 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -15,6 +15,7 @@ import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.shorts.ShortArraySet; import it.unimi.dsi.fastutil.shorts.ShortSets; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.repository.Pack; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.phys.Vec3; @@ -299,7 +300,7 @@ public abstract 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) - ClientboundChunksBiomesPacket.class, // 7.1.14 Chunk Biomes + // 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 @@ -571,26 +572,18 @@ public abstract class TechHider { 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); - BlockPos pos = new BlockPos(worldX, worldY, worldZ); - - if (isPlayerPrivilegedToAccessBlockPos(p, pos.getX(), pos.getY(), pos.getZ())) { + if (isPlayerPrivilegedToAccessPosition(p, worldX, worldY, worldZ) && isPlayerPrivilegedToAccessBlock(p, worldX, worldY, worldZ, block)) { filteredPos.add(posShort); filteredStates.add(state); - } else { - int id = Block.getId(state); - if (blockIdsToObfuscate.contains(id)) { - filteredPos.add(posShort); - filteredStates.add(blockToObfuscateTo); - modified = true; - } else { - filteredPos.add(posShort); - filteredStates.add(state); - } + } else if(isPlayerPrivilegedToAccessPosition(p, worldX, worldY, worldZ)){ + filteredPos.add(posShort); + filteredStates.add(blockStateUsedForObfuscation); } } @@ -672,4 +665,5 @@ public abstract class TechHider { public abstract boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId); public abstract boolean isPlayerPrivilegedToAccessBlocEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type); public abstract boolean isPlayerPrivilegedToAccessContainer(Player p, int containerId); + public abstract boolean isPlayerPrivilegedToAccessSound(Player p, ResourceLocation soundId); } From cf7a1ee086c159ebe603fdb44e3d4cc3c1911022 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Tue, 19 May 2026 22:17:41 +0200 Subject: [PATCH 20/52] Implement logic for hiding chunk data (not finished) --- SpigotCore/SpigotCore_Main/build.gradle.kts | 1 - .../src/de/steamwar/techhider/ChunkHider.java | 301 ++++++++++++------ .../de/steamwar/techhider/ProtocolUtils.java | 16 + 3 files changed, 217 insertions(+), 101 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/build.gradle.kts b/SpigotCore/SpigotCore_Main/build.gradle.kts index 7c10ec0e..a5e3227d 100644 --- a/SpigotCore/SpigotCore_Main/build.gradle.kts +++ b/SpigotCore/SpigotCore_Main/build.gradle.kts @@ -58,7 +58,6 @@ dependencies { compileOnly(libs.netty) compileOnly(libs.brigadier) compileOnly(libs.fastutil) - compileOnly(libs.nms21) implementation(libs.anvilgui) } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java index 238d7fdf..e45e5159 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java @@ -22,134 +22,240 @@ package de.steamwar.techhider; import de.steamwar.Reflection; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import it.unimi.dsi.fastutil.ints.Int2IntArrayMap; +import it.unimi.dsi.fastutil.ints.Int2IntMap; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import lombok.Getter; import net.minecraft.core.Registry; +import net.minecraft.core.SectionPos; import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.SimpleBitStorage; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; import org.bukkit.entity.Player; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.function.BiFunction; import java.util.function.UnaryOperator; import java.util.stream.Collectors; -public class ChunkHider { - public static final ChunkHider impl = new ChunkHider(); - public Class mapChunkPacket() { - return ClientboundLevelChunkWithLightPacket.class; - } +public abstract class ChunkHider { + private static final UnaryOperator chunkPacketShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class); + private static final UnaryOperator chunkDataShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class); - private static final UnaryOperator chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class); - private static final UnaryOperator chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class); + private static final Reflection.Field levelChunkPacketDataField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkPacketData.class, 0); - private static final Reflection.Field chunkXField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 0); - private static final Reflection.Field chunkZField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 1); - private static final Reflection.Field chunkData = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkPacketData.class, 0); + private static final Reflection.Field chunkBlockDataField = Reflection.getField(ClientboundLevelChunkPacketData.class, byte[].class, 0); + private static final Reflection.Field chunkBlockEntitiesDataField = Reflection.getField(ClientboundLevelChunkPacketData.class, List.class, 0); - private static final Reflection.Field dataField = Reflection.getField(ClientboundLevelChunkPacketData.class, byte[].class, 0); - private static final Reflection.Field tileEntities = Reflection.getField(ClientboundLevelChunkPacketData.class, List.class, 0); + private final int SECTION_SPAN_SIZE = 16; + private final byte BIT_PER_BLOCK_INDIRECTION_LIMIT = 8; + private final int BLOCKS_PER_SECTION = 4096; - public BiFunction chunkHiderGenerator(TechHider techHider) { - return (p, packet) -> { - int chunkX = chunkXField.get(packet); - int chunkZ = chunkZField.get(packet); - if (techHider.getLocationEvaluator().skipChunk(p, chunkX, chunkZ)) { - return packet; - } + private final byte BITS_PER_LONG = 64; - packet = chunkPacketCloner.apply(packet); - Object dataWrapper = chunkDataCloner.apply(chunkData.get(packet)); + private long[] readSectionDataFromBuffer(ByteBuf dataSource, byte bitsPerEntry, int entryCount) { + int entriesPerLong = BITS_PER_LONG / bitsPerEntry; + int dataLengthAsLongCount = (entryCount + entriesPerLong - 1) / entriesPerLong; - Set hiddenBlockEntities = techHider.getHiddenBlockEntities(); - tileEntities.set(dataWrapper, ((List) tileEntities.get(dataWrapper)).stream().filter(te -> tileEntityVisible(hiddenBlockEntities, te)).collect(Collectors.toList())); + long[] dataArray = new long[dataLengthAsLongCount]; - ByteBuf in = Unpooled.wrappedBuffer(dataField.get(dataWrapper)); - 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); - dataField.set(dataWrapper, data); - - chunkData.set(packet, dataWrapper); - return packet; - }; - } - - private static final Registry> registry = Reflection.getField(BuiltInRegistries.class, "BLOCK_ENTITY_TYPE", Registry.class).get(null); - private static final Reflection.Method getKey = Reflection.getTypedMethod(Reflection.getClass("net.minecraft.core.Registry"), "getKey", ResourceLocation.class, Object.class); - public static final Class tileEntity = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$BlockEntityInfo"); - protected static final Reflection.Field entityType = Reflection.getField(tileEntity, BlockEntityType.class, 0); - - protected boolean tileEntityVisible(Set hiddenBlockEntities, Object tile) { - BlockEntityType type = entityType.get(tile); - String path = ((ResourceLocation) getKey.invoke(registry, 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(); + for(int i = 0; i < dataLengthAsLongCount; i++){ + dataArray[i] = dataSource.readLong(); } - if (section.isSkipSection() || (!section.blockPrecise() && section.isPaletted())) { - section.skipNewDataArray(4096); - return; - } + return dataArray; + } - SimpleBitStorage values = new SimpleBitStorage(section.getBitsPerBlock(), 4096, section.readNewDataArray(4096)); + public ClientboundLevelChunkWithLightPacket chunkHiderGenerator(Player player, ClientboundLevelChunkWithLightPacket packet) { + int chunkX = packet.getX(); + int chunkZ = packet.getZ(); + ClientboundLevelChunkPacketData chunkData = packet.getChunkData(); - 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; + ByteBuf in = Unpooled.wrappedBuffer(chunkData.getReadBuffer()); + ByteBuf out = Unpooled.buffer(in.readableBytes() + 64); - TechHider.State test = section.test(x, y, z); + int worldMinHeight = player.getWorld().getMinHeight(); + int worldMaxHeight = player.getWorld().getMaxHeight(); - switch (test) { - case SKIP: - break; - case CHECK: - if (!section.getObfuscate().contains(values.get(pos))) { - break; + for (int yOffset = worldMinHeight; yOffset < worldMaxHeight; yOffset += SECTION_SPAN_SIZE) { + // TODO make configurable + int blockIdUsedForHiding = 121; + + short blockCount = in.readShort(); + + byte bitsPerBlock = in.readByte(); + + if(bitsPerBlock == 0) { + int sectionBlockId = ProtocolUtils.readVarInt(in); + } + 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[] obfuscatedData = new int[BLOCKS_PER_SECTION]; + for (int sectionY = 0; sectionY < 16; sectionY++) { + for (int sectionZ = 0; sectionZ < 16; sectionZ++) { + for (int sectionX = 0; sectionX < 16; sectionX++) { + int blockDataIndex = (((sectionY * 16) + sectionZ) * 16) + sectionX; + + int worldX = sectionX * chunkX; + int worldY = sectionY * yOffset; + int worldZ = sectionZ * chunkZ; + + int blockId = resolvedData[blockDataIndex]; + Block block = BuiltInRegistries.BLOCK.get(blockId).get().value(); + + if(isPlayerPrivilegedToAccessPosition(player, worldX, worldY, worldZ) && isPlayerPrivilegedToAccessBlock(player, worldX, worldY, worldZ, block)) { + obfuscatedData[blockDataIndex] = blockId; } - case HIDE: - values.set(pos, section.getTarget()); - break; - case HIDE_AIR: - default: - values.set(pos, section.getAir()); + else { + obfuscatedData[blockDataIndex] = blockIdUsedForHiding; + } + } } } + + Int2IntMap blockIdToPalletIndex = new Int2IntOpenHashMap(); + int[] newPallet = new int[palletLength]; + int runningIndex = 0; + + for(int blockId : obfuscatedData) { + if(!blockIdToPalletIndex.containsKey(blockId)) { + newPallet[runningIndex] = blockId; + blockIdToPalletIndex.put(blockId, runningIndex); + runningIndex++; + } + } + + 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.writeByte(blockCount); + out.writeByte(bitsPerBlock); + ProtocolUtils.writeVarInt(out, palletLength); + ProtocolUtils.writeVarIntArray(out, pallet); + for(long rawDataSegment : reEncodedData.getRaw()) { + out.writeLong(rawDataSegment); + } } + else { + 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++){ + resolvedData[i] = data.get(i); + } + + int[] obfuscatedData = new int[BLOCKS_PER_SECTION]; + for (int sectionY = 0; sectionY < 16; sectionY++) { + for (int sectionZ = 0; sectionZ < 16; sectionZ++) { + for (int sectionX = 0; sectionX < 16; sectionX++) { + int blockDataIndex = (((sectionY * 16) + sectionZ) * 16) + sectionX; + + int worldX = sectionX * chunkX; + int worldY = sectionY * yOffset; + int worldZ = sectionZ * chunkZ; + + int blockId = resolvedData[blockDataIndex]; + Block block = BuiltInRegistries.BLOCK.get(blockId).get().value(); + + if(isPlayerPrivilegedToAccessPosition(player, worldX, worldY, worldZ) && isPlayerPrivilegedToAccessBlock(player, worldX, worldY, worldZ, block)) { + obfuscatedData[blockDataIndex] = blockId; + } + else { + obfuscatedData[blockDataIndex] = blockIdUsedForHiding; + } + } + } + } + + SimpleBitStorage reEncodedData = new SimpleBitStorage(bitsPerBlock, BLOCKS_PER_SECTION, new long[rawData.length]); + for(int i = 0; i < obfuscatedData.length; i++) { + int blockId = obfuscatedData[i]; + + reEncodedData.set(i, blockId); + } + + out.writeByte(blockCount); + out.writeByte(bitsPerBlock); + for(long rawDataSegment : reEncodedData.getRaw()) { + out.writeLong(rawDataSegment); + } + } + } - section.writeDataArray(values.getRaw()); + if (in.readableBytes() != 0) { + return null; + } + + byte[] data = new byte[out.readableBytes()]; + out.readBytes(data); + + List blockEntities = chunkBlockEntitiesDataField.get(chunkData); + List filteredBlockEntities = filterBlockEntities(player, blockEntities); + + return buildNewChunkPacket(packet, data, filteredBlockEntities); + + } + + public abstract boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ); + public abstract boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block); + public abstract boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type); + + private ClientboundLevelChunkWithLightPacket buildNewChunkPacket(ClientboundLevelChunkWithLightPacket originalPacket, byte[] newBlockDataBuffer, List newBlockEntities) { + ClientboundLevelChunkWithLightPacket clonedPacket = (ClientboundLevelChunkWithLightPacket) chunkPacketShallowCloner.apply(originalPacket); + ClientboundLevelChunkPacketData clonedPacketChunkData = (ClientboundLevelChunkPacketData) chunkDataShallowCloner.apply(originalPacket.getChunkData()); + + chunkBlockDataField.set(clonedPacketChunkData, newBlockDataBuffer); + chunkBlockEntitiesDataField.set(clonedPacketChunkData, newBlockEntities); + + levelChunkPacketDataField.set(clonedPacket, clonedPacketChunkData); + + return clonedPacket; + } + + + private static final Class blockEntitiyInfoClass = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$BlockEntityInfo"); + + private static final Reflection.Field blockEntityInfoTypeField = Reflection.getField(blockEntitiyInfoClass, BlockEntityType.class, 0); + private static final Reflection.Field packedXZField = Reflection.getField(blockEntitiyInfoClass, Integer.class, 0); + private static final Reflection.Field yField = Reflection.getField(blockEntitiyInfoClass, Integer.class, 1); + private List filterBlockEntities(Player player, List blockEntities) { + return blockEntities.stream() + .filter((blockEntityInfo) -> { + BlockEntityType type = blockEntityInfoTypeField.get(blockEntityInfo); + + int packedXZ = packedXZField.get(blockEntityInfo); + + int y = yField.get(blockEntityInfo); + int x = SectionPos.sectionRelativeX((short) packedXZ); + int z = SectionPos.sectionRelativeZ((short) packedXZ); + + return isPlayerPrivilegedToAccessPosition(player, x, y, z) && isPlayerPrivilegedToAccessBlockEntity(player, x, y, z, type); + }).toList(); } private void biomes(SectionHider section) { @@ -187,7 +293,7 @@ public class ChunkHider { private Set blockIdsToObfuscate; private final int blockIdToObfuscateTo; - public SectionHider(Player player, BlockState blockToObfuscateTo, Set blockIdsToObfuscate, ByteBuf in, ByteBuf out, int chunkX, int chunkY, int chunkZ) { + public SectionHider(Player player, ByteBuf in, ByteBuf out, int chunkX, int chunkY, int chunkZ) { this.player = player; this.in = in; this.out = out; @@ -238,11 +344,6 @@ public class ChunkHider { } public void processPalette() { - if (skipSection) { - skipPalette(); - return; - } - int paletteLength = copyVarInt(); if (paletteLength == 0) return; diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java index 736027cc..100dac77 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java @@ -140,6 +140,16 @@ public class ProtocolUtils { return result; } + public static int[] readVarIntArray(ByteBuf buffer, int length) { + int[] array = new int[length]; + + for(int i = 0; i < length; i++) { + array[i] = readVarInt(buffer); + } + + return array; + } + public static void writeVarInt(ByteBuf buf, int value) { do { int temp = value & 0b01111111; @@ -152,6 +162,12 @@ public class ProtocolUtils { } while (value != 0); } + public static void writeVarIntArray(ByteBuf buf, int[] values){ + for(int varInt : values) { + writeVarInt(buf, varInt); + } + } + @Deprecated public static int readVarIntLength(byte[] array, int startPos) { int numRead = 0; From 32a2cbb4dd015100ad2219148569434453a27083 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Tue, 19 May 2026 22:47:22 +0200 Subject: [PATCH 21/52] Finish initial implementation of logic --- .../src/de/steamwar/techhider/ChunkHider.java | 189 ++++-------------- .../src/de/steamwar/techhider/TechHider.java | 21 +- 2 files changed, 56 insertions(+), 154 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java index e45e5159..3ba49d16 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java @@ -56,11 +56,19 @@ public abstract class ChunkHider { private final int SECTION_SPAN_SIZE = 16; private final byte BIT_PER_BLOCK_INDIRECTION_LIMIT = 8; + private final int BLOCKS_PER_SECTION = 4096; + private final int BIOMES_PER_SECTION = 64; private final byte BITS_PER_LONG = 64; - private long[] readSectionDataFromBuffer(ByteBuf dataSource, byte bitsPerEntry, int entryCount) { + private final int blockIdUsedForHiding; + + public ChunkHider(Block blockUsedForObfuscation) { + blockIdUsedForHiding = Block.BLOCK_STATE_REGISTRY.getId(blockUsedForObfuscation.defaultBlockState()); + } + + private long[] readSectionDataFromBuffer(ByteBuf dataSource, short bitsPerEntry, int entryCount) { int entriesPerLong = BITS_PER_LONG / bitsPerEntry; int dataLengthAsLongCount = (entryCount + entriesPerLong - 1) / entriesPerLong; @@ -73,7 +81,7 @@ public abstract class ChunkHider { return dataArray; } - public ClientboundLevelChunkWithLightPacket chunkHiderGenerator(Player player, ClientboundLevelChunkWithLightPacket packet) { + public ClientboundLevelChunkWithLightPacket processLevelChunkWithLightPacket(Player player, ClientboundLevelChunkWithLightPacket packet) { int chunkX = packet.getX(); int chunkZ = packet.getZ(); ClientboundLevelChunkPacketData chunkData = packet.getChunkData(); @@ -85,15 +93,16 @@ public abstract class ChunkHider { int worldMaxHeight = player.getWorld().getMaxHeight(); for (int yOffset = worldMinHeight; yOffset < worldMaxHeight; yOffset += SECTION_SPAN_SIZE) { - // TODO make configurable - int blockIdUsedForHiding = 121; - 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); @@ -205,6 +214,7 @@ public abstract class ChunkHider { } } + copyOverSectionBiomeData(in, out); } if (in.readableBytes() != 0) { @@ -258,159 +268,34 @@ public abstract class ChunkHider { }).toList(); } - 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); + private void copyOverSectionBiomeData(ByteBuf oldData, ByteBuf newData){ + short bitsPerBiome = oldData.readShort(); + newData.writeShort(bitsPerBiome); + + if(bitsPerBiome == 0) { + int sectionBiomeId = ProtocolUtils.readVarInt(oldData); + ProtocolUtils.writeVarInt(newData, sectionBiomeId); } - } + else if(bitsPerBiome <= BIT_PER_BLOCK_INDIRECTION_LIMIT) { + int palletLength = ProtocolUtils.readVarInt(oldData); + ProtocolUtils.writeVarInt(newData, palletLength); - @Getter - class SectionHider { - private final Player player; - private final ByteBuf in; - private final ByteBuf out; + for(int i = 0; i < palletLength; i++) { + int palletEntry = ProtocolUtils.readVarInt(oldData); + ProtocolUtils.writeVarInt(newData, palletEntry); + } - 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 boolean paletted; - private int bitsPerBlock; - private int blockCount; - private int air; - private int target; - private Set blockIdsToObfuscate; - private final int blockIdToObfuscateTo; - - public SectionHider(Player player, ByteBuf in, ByteBuf out, int chunkX, int chunkY, int chunkZ) { - this.player = player; - 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(); + long[] rawData = readSectionDataFromBuffer(oldData, bitsPerBiome, BIOMES_PER_SECTION); + for(long rawDataSegment : rawData ) { + newData.writeLong(rawDataSegment); + } } - - 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(); + else { + long[] rawData = readSectionDataFromBuffer(oldData, bitsPerBiome, BIOMES_PER_SECTION); + for(long rawDataSegment : rawData ) { + newData.writeLong(rawDataSegment); } } - public void processPalette() { - 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); - } - blockIdsToObfuscate = 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/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 61358c26..ead26362 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -186,12 +186,30 @@ public abstract class TechHider { private final Block blockUsedForObfuscation; private final BlockState blockStateUsedForObfuscation; + private ChunkHider chunkHider; // TODO handle packet bundle public TechHider(Plugin plugin, Block blockUsedForObfuscation) { this.blockUsedForObfuscation = blockUsedForObfuscation; this.blockStateUsedForObfuscation = blockUsedForObfuscation.defaultBlockState(); + this.chunkHider = new ChunkHider(blockUsedForObfuscation) { + @Override + public boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block) { + return TechHider.this.isPlayerPrivilegedToAccessBlock(p, blockX, blockY, blockZ, block); + } + + @Override + public boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type) { + return TechHider.this.isPlayerPrivilegedToAccessBlocEntity(p, blockX, blockY, blockZ, type); + } + + @Override + public boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ) { + return TechHider.this.isPlayerPrivilegedToAccessPosition(p, blockX, blockY, blockZ); + } + }; + this.bypassingPackets = new HashSet<>(List.of( // --- 5.1.x Login Protocol --- ClientboundLoginDisconnectPacket.class, // 5.1.1 Disconnect @@ -621,9 +639,8 @@ public abstract class TechHider { } } - private final ChunkHider chunkHider = new ChunkHider(); private ClientboundLevelChunkWithLightPacket processChunkWithLight(Player p, ClientboundLevelChunkWithLightPacket packet) { - return chunkHider.processLevelChunkWithLightPacket(p, blockEntitiesToHide, this::isPlayerPrivilegedToAccessBlockPos, blockToObfuscateTo, blockIdsToObfuscate, packet); + return chunkHider.processLevelChunkWithLightPacket(p, packet); } private Packet processContainerPacket(Player player, int containerId, Packet packet) { From ba29e5cf233aa6402f60dd506fa9748870e829e5 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Wed, 20 May 2026 18:28:39 +0200 Subject: [PATCH 22/52] Add tinyProtocol Handler for techhider --- .../src/com/comphenix/tinyprotocol/TinyProtocol.java | 12 ++++++++++++ .../src/de/steamwar/techhider/TechHider.java | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java index 9cd1bc93..24773cd6 100644 --- a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java +++ b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java @@ -26,6 +26,8 @@ import com.google.common.collect.Lists; import com.google.common.collect.MapMaker; import com.mojang.authlib.GameProfile; +import java.util.function.BiFunction; + /** * Represents a very tiny alternative to ProtocolLib. *

@@ -58,6 +60,7 @@ public class TinyProtocol { public static final TinyProtocol instance = new TinyProtocol(Core.getInstance()); private final Map, List>> packetFilters = new HashMap<>(); + private final Set> globalFilters = new HashSet<>(); public static void init() { // enforce init @@ -235,6 +238,10 @@ public class TinyProtocol { packetFilters.getOrDefault(packetType, Collections.emptyList()).remove(filter); } + public void addGlobalFilter(BiFunction filter) { + globalFilters.add(filter); + } + /** * Send a packet to a particular player. * @@ -469,6 +476,11 @@ public class TinyProtocol { if (packet == null) break; } + for (BiFunction filter : globalFilters) { + packet = filter.apply(player, packet); + if (packet == null) break; + } + return packet; } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index ead26362..490e19ff 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -449,6 +449,18 @@ public abstract class TechHider { }); this.packetProcessors = processors; + + TinyProtocol.instance.addGlobalFilter((player, packet) -> { + if(bypassingPackets.contains(packet.getClass())) { + return packet; + } + else if(packetProcessors.containsKey(packet.getClass())) { + return packetProcessors.get(packet.getClass()).apply(player, packet); + } + else { + return null; + } + }); } private Packet processAddEntityPacket(Player player, ClientboundAddEntityPacket packet) { From 5da3767378c924cab0e76dd6a82a87321e4df5a7 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Wed, 20 May 2026 21:30:44 +0200 Subject: [PATCH 23/52] Started embedding new techhider into fight system --- .../de/steamwar/fightsystem/FightSystem.java | 3 +- .../steamwar/fightsystem/utils/HullHider.java | 55 -------- .../fightsystem/utils/TechHiderWrapper.java | 130 +++++++----------- .../src/de/steamwar/techhider/TechHider.java | 2 +- 4 files changed, 55 insertions(+), 135 deletions(-) diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java index ab85a9b2..1c348b4b 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java @@ -100,8 +100,9 @@ public class FightSystem extends JavaPlugin { new OneShotStateDependent(ArenaMode.Test, FightState.All, WorldEditRendererCUIEditor::new); Config.world.setGameRule(GameRule.REDUCED_DEBUG_INFO, ArenaMode.AntiTest.contains(Config.mode)); - techHider = new TechHiderWrapper(); hullHider = new HullHider(); + techHider = new TechHiderWrapper(hullHider); + FileSource.startReplay(); diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java index d50730a8..4d94b335 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java @@ -62,9 +62,6 @@ public class HullHider implements Listener { @Getter private final Map hullMap = new HashMap<>(); private final Hull[] hulls; - private final Map, BiFunction> packetHiders = new HashMap<>(); - - private static final Class packetPlayOutExplosion = ClientboundExplodePacket.class; public HullHider() { if (!TechHiderWrapper.ENABLED) { @@ -75,25 +72,8 @@ public class HullHider implements Listener { Fight.teams().forEach(team -> hullMap.put(team, new Hull(team))); hulls = hullMap.values().toArray(new Hull[0]); - packetHiders.put(packetPlayOutWorldEvent, this::worldEventHider); - packetHiders.put(packetPlayOutExplosion, this::explosionHider); - posHiderGenerator(ClientboundLevelParticlesPacket.class, double.class, 1.0); - posHiderGenerator(ClientboundSoundPacket.class, int.class, 8.0); new StateDependentListener(TechHiderWrapper.ENABLED, FightState.Schem, this); - new StateDependent(TechHiderWrapper.ENABLED, FightState.Schem) { - @Override - public void enable() { - packetHiders.forEach(TinyProtocol.instance::addFilter); - Bukkit.getOnlinePlayers().forEach(HullHider.this::updatePlayer); - } - - @Override - public void disable() { - Bukkit.getOnlinePlayers().forEach(player -> removePlayer(player, true)); - packetHiders.forEach(TinyProtocol.instance::removeFilter); - } - }.register(); new StateDependentTask(TechHiderWrapper.ENABLED, FightState.Schem, this::onTick, 0, 1); } @@ -209,39 +189,4 @@ public class HullHider implements Listener { hull.removeREntity(e); } } - - - private static final Class packetPlayOutWorldEvent = ClientboundLevelEventPacket.class; - private static final Reflection.Field worldEventPosition = Reflection.getField(packetPlayOutWorldEvent, TechHider.blockPosition, 0); - public static final Reflection.Field blockPositionY = Reflection.getField(Vec3i.class, int.class, 1); - - private Object worldEventHider(Player player, Object packet) { - Object baseBlock = worldEventPosition.get(packet); - return packetHider(player, packet, new Location(Config.world, TechHider.blockPositionX.get(baseBlock), blockPositionY.get(baseBlock), TechHider.blockPositionZ.get(baseBlock))); - } - - private Object explosionHider(Player player, Object packet) { - return ReflectionWrapper.impl.explosionHider(player, packet, this::packetHider); - } - - private void posHiderGenerator(Class type, Class posType, double factor) { - Function location = posPacketToLocation(type, posType, factor); - packetHiders.put(type, (player, packet) -> packetHider(player, packet, location.apply(packet))); - } - - public static Function posPacketToLocation(Class type, Class posType, double factor) { - Reflection.Field x = Reflection.getField(type, posType, 0); - Reflection.Field y = Reflection.getField(type, posType, 1); - Reflection.Field z = Reflection.getField(type, posType, 2); - - return packet -> new Location(Config.world, x.get(packet).doubleValue() / factor, y.get(packet).doubleValue() / factor, z.get(packet).doubleValue() / factor); - } - - private Object packetHider(Player player, Object packet, Location location) { - for (Hull hull : hulls) { - if (hull.isLocationHidden(player, location)) return null; - } - - return packet; - } } 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 75143c58..2519de0c 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -31,8 +31,14 @@ import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.StateDependent; import de.steamwar.fightsystem.states.StateDependentListener; import de.steamwar.sql.SteamwarUser; +import de.steamwar.techhider.TechHider; import lombok.Getter; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntityType; import org.bukkit.GameMode; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -45,27 +51,62 @@ import java.io.IOException; import java.nio.file.Files; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; -public class TechHiderWrapper extends StateDependent implements TechHider.LocationEvaluator, Listener { +public class TechHiderWrapper extends StateDependent implements Listener { public static final boolean ENABLED = !Config.OnlyPublicSchematics && !Config.test() && Config.GameModeConfig.Techhider.Active; @Getter private final ConcurrentHashMap hiddenRegion = new ConcurrentHashMap<>(); - private final ConcurrentHashMap patterns = new ConcurrentHashMap<>(); private final TechHider techHider; - private final SecretKey key; - public TechHiderWrapper() { + public TechHiderWrapper(HullHider hullHider) { super(ENABLED, FightState.Schem); - techHider = new TechHider(this, Config.GameModeConfig.Techhider.ObfuscateWith, Config.GameModeConfig.Techhider.HiddenBlocks, Config.GameModeConfig.Techhider.HiddenBlockEntities); + Set blocksToObfuscate = Config.GameModeConfig.Techhider.HiddenBlocks.stream() + .map(CraftMagicNumbers::getBlock) + .collect(Collectors.toUnmodifiableSet()); + Set> blockEntityTypeToObfuscate = Config.GameModeConfig.Techhider.HiddenBlockEntities.stream() + .map(id -> { + ResourceLocation parsedId = ResourceLocation.parse(id); + return BuiltInRegistries.BLOCK_ENTITY_TYPE.get(parsedId).get().value(); + }) + .collect(Collectors.toUnmodifiableSet()); + techHider = new TechHider(CraftMagicNumbers.getBlock(Config.GameModeConfig.Techhider.ObfuscateWith)) { + @Override + public boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ) { + return !hullHider.isBlockHidden(p, blockX, blockY, blockZ); + } - try { - key = new SecretKeySpec(Files.readAllBytes(new File(System.getProperty("user.home"), "hullhider.key").toPath()), "AES"); - } catch (IOException e) { - throw new SecurityException(e); - } + @Override + public boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block) { + Region hiddenRegion = getHiddenRegion(p); + return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blocksToObfuscate.contains(block) ; + } + + @Override + public boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId) { + return false; + } + + @Override + public boolean isPlayerPrivilegedToAccessBlocEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type) { + Region hiddenRegion = getHiddenRegion(p); + return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type); + } + + @Override + public boolean isPlayerPrivilegedToAccessContainer(Player p, int containerId) { + return false; + } + + @Override + public boolean isPlayerPrivilegedToAccessSound(Player p, ResourceLocation soundId) { + return false; + } + }; new StateDependentListener(ENABLED, FightState.Schem, this); register(); @@ -73,12 +114,11 @@ public class TechHiderWrapper extends StateDependent implements TechHider.Locati @Override public void enable() { - techHider.enable(); + } @Override public void disable() { - techHider.disable(); hiddenRegion.clear(); } @@ -117,72 +157,6 @@ public class TechHiderWrapper extends StateDependent implements TechHider.Locati }); } - @Override - public boolean suppressInteractions(Player player) { - return player.getGameMode() == GameMode.SPECTATOR; - } - - @Override - public boolean skipChunk(Player player, int chunkX, int chunkZ) { - return getHiddenRegion(player).chunkOutside(chunkX, chunkZ); - } - - @Override - public boolean skipChunkSection(Player player, int chunkX, int chunkY, int chunkZ) { - return getHiddenRegion(player).chunkSectionOutside(chunkX, chunkY, chunkZ); - } - - @Override - public TechHider.State check(Player player, int x, int y, int z) { - if (hiddenRegion.computeIfAbsent(player, this::getHiddenRegion).inRegion(x, y, z)) { - if (FightSystem.getHullHider().isBlockHidden(player, x, y, z)) { - int id = ((y & 3) << 4) + ((z & 3) << 2) + (x & 3); - return (patterns.computeIfAbsent(player, this::getPattern) & (1L << id)) == 0 ? TechHider.State.HIDE_AIR : TechHider.State.HIDE; - } else { - return TechHider.State.CHECK; - } - } else { - return TechHider.State.SKIP; - } - } - - public long getPattern(Player player) { - long pattern = SteamwarUser.get(player.getUniqueId()).getId(); - - byte[] bytes = new byte[]{ - (byte) (pattern & 0xFF), - (byte) ((pattern >>> 8) & 0xFF), - (byte) ((pattern >>> 16) & 0xFF), - (byte) ((pattern >>> 24) & 0xFF) - }; - - try { - Cipher cipher = Cipher.getInstance("AES_256/CFB/NoPadding"); - cipher.init(Cipher.ENCRYPT_MODE, key); - bytes = cipher.doFinal(bytes); - } catch (IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException | - InvalidKeyException e) { - throw new SecurityException(e); - } - - pattern = bytes[0] & 0xFFL | - ((bytes[1] & 0xFFL) << 8) | - ((bytes[2] & 0xFFL) << 16) | - ((bytes[3] & 0xFFL) << 24); - - /* XXXO OOOX - * XXXO OOOX - * XXOX OOXO - * OOXX XXOO */ - pattern |= 0b1110_1110_1101_0011___0001_0001_0010_1100___0000_0000_0000_0000___0000_0000_0000_0000L; - return pattern; - } - - @Override - public boolean blockPrecise(Player player, int chunkX, int chunkY, int chunkZ) { - return FightSystem.getHullHider().blockPrecise(player, chunkX, chunkY, chunkZ); - } - private Region getHiddenRegion(Player player) { if (Config.isReferee(player)) return Region.EMPTY; diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 490e19ff..ffadb20b 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -189,7 +189,7 @@ public abstract class TechHider { private ChunkHider chunkHider; // TODO handle packet bundle - public TechHider(Plugin plugin, Block blockUsedForObfuscation) { + public TechHider(Block blockUsedForObfuscation) { this.blockUsedForObfuscation = blockUsedForObfuscation; this.blockStateUsedForObfuscation = blockUsedForObfuscation.defaultBlockState(); From a7a8c4d051e92d5cfa1ea1763d7e3a6e47a9bc01 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Wed, 20 May 2026 21:33:01 +0200 Subject: [PATCH 24/52] Fix imports in tinyProtocol --- .../src/com/comphenix/tinyprotocol/TinyProtocol.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java index 24773cd6..c0a869d0 100644 --- a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java +++ b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java @@ -20,13 +20,10 @@ import org.bukkit.event.server.PluginDisableEvent; import org.bukkit.plugin.Plugin; import org.bukkit.scheduler.BukkitRunnable; -import com.comphenix.tinyprotocol.Reflection.FieldAccessor; -import com.comphenix.tinyprotocol.Reflection.MethodInvoker; -import com.google.common.collect.Lists; -import com.google.common.collect.MapMaker; -import com.mojang.authlib.GameProfile; - +import java.util.*; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.BiFunction; +import java.util.logging.Level; /** * Represents a very tiny alternative to ProtocolLib. From 7ed36fe56e2ad075fa4b849f6a0fcd59f445f6bc Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Wed, 20 May 2026 21:44:28 +0200 Subject: [PATCH 25/52] removed reflections form anti nocom guard --- .../de/steamwar/core/events/AntiNocom.java | 26 ++++++------------- .../src/de/steamwar/techhider/TechHider.java | 12 +-------- 2 files changed, 9 insertions(+), 29 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java index 858a2af0..67230149 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java @@ -26,6 +26,7 @@ import de.steamwar.linkage.Linked; import de.steamwar.sql.SWException; import de.steamwar.techhider.ProtocolUtils; import de.steamwar.techhider.TechHider; +import net.minecraft.core.BlockPos; import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; import net.minecraft.network.protocol.game.ServerboundUseItemOnPacket; import net.minecraft.world.phys.BlockHitResult; @@ -45,7 +46,7 @@ public class AntiNocom implements Listener { private final Map flags = new ConcurrentHashMap<>(); public AntiNocom() { - TinyProtocol.instance.addFilter(blockDig, this::onDig); + TinyProtocol.instance.addTypedFilter(ServerboundPlayerActionPacket.class, this::onDig); registerUseItem(); } @@ -55,26 +56,15 @@ public class AntiNocom implements Listener { } private void registerUseItem() { - Class useItem = ServerboundUseItemOnPacket.class; - - Class movingObjectPositionBlock = BlockHitResult.class; - Reflection.Field useItemPosition = Reflection.getField(useItem, movingObjectPositionBlock, 0); - Reflection.Field movingBlockPosition = Reflection.getField(movingObjectPositionBlock, TechHider.blockPosition, 0); - - Function getPosition = (packet) -> movingBlockPosition.get(useItemPosition.get(packet)); - - TinyProtocol.instance.addFilter(useItem, (player, packet) -> { - Object pos = getPosition.apply(packet); - return isValid(player, "UseItem", TechHider.blockPositionX.get(pos), TechHider.blockPositionZ.get(pos)) ? packet : null; + TinyProtocol.instance.addTypedFilter(ServerboundUseItemOnPacket.class, (player, packet) -> { + BlockPos pos = packet.getHitResult().getBlockPos(); + return isValid(player, "UseItem", pos.getX(), pos.getZ()) ? packet : null; }); } - private static final Class blockDig = ServerboundPlayerActionPacket.class; - private static final Reflection.Field digPosition = Reflection.getField(blockDig, TechHider.blockPosition, 0); - - private Object onDig(Player player, Object packet) { - Object pos = digPosition.get(packet); - return isValid(player, "Dig", TechHider.blockPositionX.get(pos), TechHider.blockPositionZ.get(pos)) ? packet : null; + private ServerboundPlayerActionPacket onDig(Player player, ServerboundPlayerActionPacket packet) { + BlockPos pos = packet.getPos(); + return isValid(player, "Dig", pos.getX(), pos.getZ()) ? packet : null; } private boolean isValid(Player player, String type, int x, int z) { diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index ffadb20b..b889c3c1 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -8,7 +8,6 @@ import java.util.Map; import java.util.Set; import java.util.function.BiFunction; import java.util.function.Function; -import java.util.function.ToDoubleFunction; import java.util.function.ToIntFunction; import it.unimi.dsi.fastutil.ints.IntArrayList; @@ -16,16 +15,13 @@ import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.shorts.ShortArraySet; import it.unimi.dsi.fastutil.shorts.ShortSets; import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.repository.Pack; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.phys.Vec3; import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; import com.comphenix.tinyprotocol.TinyProtocol; import de.steamwar.Reflection; -import io.netty.channel.Channel; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.network.PacketListener; @@ -49,7 +45,6 @@ import net.minecraft.network.protocol.configuration.ClientboundUpdateEnabledFeat import net.minecraft.network.protocol.cookie.ClientboundCookieRequestPacket; import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; import net.minecraft.network.protocol.game.ClientboundAnimatePacket; -import net.minecraft.network.protocol.game.ClientboundAwardStatsPacket; import net.minecraft.network.protocol.game.ClientboundBlockChangedAckPacket; import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; @@ -61,7 +56,6 @@ import net.minecraft.network.protocol.game.ClientboundBundlePacket; import net.minecraft.network.protocol.game.ClientboundChangeDifficultyPacket; import net.minecraft.network.protocol.game.ClientboundChunkBatchFinishedPacket; import net.minecraft.network.protocol.game.ClientboundChunkBatchStartPacket; -import net.minecraft.network.protocol.game.ClientboundChunksBiomesPacket; import net.minecraft.network.protocol.game.ClientboundClearTitlesPacket; import net.minecraft.network.protocol.game.ClientboundCommandSuggestionsPacket; import net.minecraft.network.protocol.game.ClientboundCommandsPacket; @@ -72,7 +66,6 @@ import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; import net.minecraft.network.protocol.game.ClientboundCooldownPacket; import net.minecraft.network.protocol.game.ClientboundCustomChatCompletionsPacket; import net.minecraft.network.protocol.game.ClientboundDamageEventPacket; -import net.minecraft.network.protocol.game.ClientboundDebugSamplePacket; import net.minecraft.network.protocol.game.ClientboundDeleteChatPacket; import net.minecraft.network.protocol.game.ClientboundDisguisedChatPacket; import net.minecraft.network.protocol.game.ClientboundEntityEventPacket; @@ -91,9 +84,6 @@ import net.minecraft.network.protocol.game.ClientboundLoginPacket; import net.minecraft.network.protocol.game.ClientboundMapItemDataPacket; import net.minecraft.network.protocol.game.ClientboundMerchantOffersPacket; import net.minecraft.network.protocol.game.ClientboundMoveEntityPacket; -import net.minecraft.network.protocol.game.ClientboundMoveEntityPacket.Pos; -import net.minecraft.network.protocol.game.ClientboundMoveEntityPacket.PosRot; -import net.minecraft.network.protocol.game.ClientboundMoveEntityPacket.Rot; import net.minecraft.network.protocol.game.ClientboundMoveMinecartPacket; import net.minecraft.network.protocol.game.ClientboundMoveVehiclePacket; import net.minecraft.network.protocol.game.ClientboundOpenBookPacket; @@ -455,7 +445,7 @@ public abstract class TechHider { return packet; } else if(packetProcessors.containsKey(packet.getClass())) { - return packetProcessors.get(packet.getClass()).apply(player, packet); + return packetProcessors.get(packet.getClass()).apply(player, (Packet) packet); } else { return null; From 72df91931284e39ae45f4781e5d6255e6cb62bb9 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Wed, 20 May 2026 21:47:17 +0200 Subject: [PATCH 26/52] cut down old protocol wrapper --- .../steamwar/techhider/ProtocolWrapper.java | 61 ------------------- 1 file changed, 61 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolWrapper.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolWrapper.java index 72469241..7699c991 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolWrapper.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolWrapper.java @@ -31,65 +31,4 @@ import java.util.function.BiFunction; public class ProtocolWrapper { public static final ProtocolWrapper impl = new ProtocolWrapper(); - - private static final Reflection.Field multiBlockChangeChunk = Reflection.getField(TechHider.multiBlockChangePacket, SectionPos.class, 0); - private static final Reflection.Field multiBlockChangePos = Reflection.getField(TechHider.multiBlockChangePacket, short[].class, 0); - private static final Reflection.Field multiBlockChangeBlocks = Reflection.getField(TechHider.multiBlockChangePacket, BlockState[].class, 0); - - public BiFunction multiBlockChangeGenerator(TechHider techHider) { - return (p, packet) -> { - TechHider.LocationEvaluator locationEvaluator = techHider.getLocationEvaluator(); - Object chunkCoords = multiBlockChangeChunk.get(packet); - int chunkX = TechHider.blockPositionX.get(chunkCoords); - int chunkY = TechHider.blockPositionY.get(chunkCoords); - int chunkZ = TechHider.blockPositionZ.get(chunkCoords); - if (locationEvaluator.skipChunkSection(p, chunkX, chunkY, chunkZ)) { - return packet; - } - - packet = TechHider.multiBlockChangeCloner.apply(packet); - final short[] oldPos = multiBlockChangePos.get(packet); - final BlockState[] oldBlocks = multiBlockChangeBlocks.get(packet); - 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); - } - - multiBlockChangePos.set(packet, newPos); - multiBlockChangeBlocks.set(packet, blocks.toArray(new BlockState[0])); - return packet; - }; - } - - private static final Reflection.Field tileEntityType = Reflection.getField(TechHider.tileEntityDataPacket, BlockEntityType.class, 0); - private static final BlockEntityType signType = Reflection.getField(BlockEntityType.class, BlockEntityType.class, 0, SignBlockEntity.class).get(null); - - public boolean unfilteredTileEntityDataAction(Object packet) { - return tileEntityType.get(packet) != signType; - } - - public BiFunction blockBreakHiderGenerator(Class blockBreakPacket, TechHider techHider) { - return null; - } } From 8424f842c32338c3a9e2017fbdcaa256a96e1737 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Thu, 21 May 2026 00:18:12 +0200 Subject: [PATCH 27/52] resolve last compile issue in techhider --- FightSystem/FightSystem_Core/build.gradle.kts | 1 + .../fightsystem/utils/TechHiderWrapper.java | 17 ++++------------- .../src/de/steamwar/techhider/BlockIds.java | 14 ++++++++++---- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/FightSystem/FightSystem_Core/build.gradle.kts b/FightSystem/FightSystem_Core/build.gradle.kts index b346e97c..9b2f746b 100644 --- a/FightSystem/FightSystem_Core/build.gradle.kts +++ b/FightSystem/FightSystem_Core/build.gradle.kts @@ -37,4 +37,5 @@ dependencies { compileOnly(libs.authlib) compileOnly(libs.nms) compileOnly(libs.fawe) + compileOnly(libs.datafixer) } 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 2519de0c..871d679e 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -21,7 +21,6 @@ package de.steamwar.fightsystem.utils; import de.steamwar.core.CraftbukkitWrapper; import de.steamwar.fightsystem.Config; -import de.steamwar.fightsystem.FightSystem; import de.steamwar.fightsystem.events.BoardingEvent; import de.steamwar.fightsystem.events.TeamLeaveEvent; import de.steamwar.fightsystem.events.TeamSpawnEvent; @@ -30,27 +29,18 @@ import de.steamwar.fightsystem.fight.FightTeam; import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.StateDependent; import de.steamwar.fightsystem.states.StateDependentListener; -import de.steamwar.sql.SteamwarUser; import de.steamwar.techhider.TechHider; import lombok.Getter; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; -import org.bukkit.GameMode; import org.bukkit.craftbukkit.util.CraftMagicNumbers; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerQuitEvent; -import javax.crypto.*; -import javax.crypto.spec.SecretKeySpec; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -63,15 +53,16 @@ public class TechHiderWrapper extends StateDependent implements Listener { private final ConcurrentHashMap hiddenRegion = new ConcurrentHashMap<>(); private final TechHider techHider; + public TechHiderWrapper(HullHider hullHider) { super(ENABLED, FightState.Schem); Set blocksToObfuscate = Config.GameModeConfig.Techhider.HiddenBlocks.stream() .map(CraftMagicNumbers::getBlock) .collect(Collectors.toUnmodifiableSet()); Set> blockEntityTypeToObfuscate = Config.GameModeConfig.Techhider.HiddenBlockEntities.stream() - .map(id -> { - ResourceLocation parsedId = ResourceLocation.parse(id); - return BuiltInRegistries.BLOCK_ENTITY_TYPE.get(parsedId).get().value(); + .map((id) -> { + ResourceLocation loc = ResourceLocation.parse(id); + return BuiltInRegistries.BLOCK_ENTITY_TYPE.get(loc).get().value(); }) .collect(Collectors.toUnmodifiableSet()); techHider = new TechHider(CraftMagicNumbers.getBlock(Config.GameModeConfig.Techhider.ObfuscateWith)) { diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/BlockIds.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/BlockIds.java index ec4a5f95..b623c5cf 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/BlockIds.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/BlockIds.java @@ -21,11 +21,15 @@ package de.steamwar.techhider; import de.steamwar.Reflection; import net.minecraft.core.IdMapper; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.Fluids; import org.bukkit.Material; +import org.bukkit.Registry; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Waterlogged; import org.bukkit.craftbukkit.util.CraftMagicNumbers; import java.util.HashSet; @@ -39,18 +43,20 @@ public class BlockIds { } private static final FluidState water = Fluids.WATER.getSource(false); - private static final Iterable registryBlockId = (Iterable) Reflection.getField(TechHider.block, IdMapper.class, 0).get(null); public Set materialToAllIds(Material material) { Set ids = new HashSet<>(); + for (BlockState data : getBlock(material).getStateDefinition().getPossibleStates()) { ids.add(getCombinedId(data)); } if (material == Material.WATER) { - for (BlockState data : registryBlockId) { - if (data.getFluidState() == water) { - ids.add(getCombinedId(data)); + for (Block block : BuiltInRegistries.BLOCK) { + for (BlockState data : block.getStateDefinition().getPossibleStates()) { + if (data.getFluidState() == water) { + ids.add(getCombinedId(data)); + } } } } From a1d22b7d77ca891ca4d6f65c34554dea70efbf6c Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Thu, 21 May 2026 10:26:46 +0200 Subject: [PATCH 28/52] twast --- .../de/steamwar/fightsystem/utils/TechHiderWrapper.java | 9 +++++---- .../src/de/steamwar/techhider/ChunkHider.java | 8 ++++---- .../src/de/steamwar/techhider/TechHider.java | 4 ++-- 3 files changed, 11 insertions(+), 10 deletions(-) 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 871d679e..1160b650 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -59,12 +59,12 @@ public class TechHiderWrapper extends StateDependent implements Listener { Set blocksToObfuscate = Config.GameModeConfig.Techhider.HiddenBlocks.stream() .map(CraftMagicNumbers::getBlock) .collect(Collectors.toUnmodifiableSet()); - Set> blockEntityTypeToObfuscate = Config.GameModeConfig.Techhider.HiddenBlockEntities.stream() + /* Set> blockEntityTypeToObfuscate = Config.GameModeConfig.Techhider.HiddenBlockEntities.stream() .map((id) -> { ResourceLocation loc = ResourceLocation.parse(id); return BuiltInRegistries.BLOCK_ENTITY_TYPE.get(loc).get().value(); }) - .collect(Collectors.toUnmodifiableSet()); + .collect(Collectors.toUnmodifiableSet()); */ techHider = new TechHider(CraftMagicNumbers.getBlock(Config.GameModeConfig.Techhider.ObfuscateWith)) { @Override public boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ) { @@ -84,8 +84,9 @@ public class TechHiderWrapper extends StateDependent implements Listener { @Override public boolean isPlayerPrivilegedToAccessBlocEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type) { - Region hiddenRegion = getHiddenRegion(p); - return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type); + //Region hiddenRegion = getHiddenRegion(p); + //return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type); + return false; } @Override diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java index 3ba49d16..7262a6f4 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java @@ -62,10 +62,10 @@ public abstract class ChunkHider { private final byte BITS_PER_LONG = 64; - private final int blockIdUsedForHiding; + private final int blockIdUsedForHiding = 121; public ChunkHider(Block blockUsedForObfuscation) { - blockIdUsedForHiding = Block.BLOCK_STATE_REGISTRY.getId(blockUsedForObfuscation.defaultBlockState()); + // blockIdUsedForHiding = Block.BLOCK_STATE_REGISTRY.getId(blockUsedForObfuscation.defaultBlockState()); } private long[] readSectionDataFromBuffer(ByteBuf dataSource, short bitsPerEntry, int entryCount) { @@ -251,8 +251,8 @@ public abstract class ChunkHider { private static final Class blockEntitiyInfoClass = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$BlockEntityInfo"); private static final Reflection.Field blockEntityInfoTypeField = Reflection.getField(blockEntitiyInfoClass, BlockEntityType.class, 0); - private static final Reflection.Field packedXZField = Reflection.getField(blockEntitiyInfoClass, Integer.class, 0); - private static final Reflection.Field yField = Reflection.getField(blockEntitiyInfoClass, Integer.class, 1); + private static final Reflection.Field packedXZField = Reflection.getField(blockEntitiyInfoClass, int.class, 0); + private static final Reflection.Field yField = Reflection.getField(blockEntitiyInfoClass, int.class, 1); private List filterBlockEntities(Player player, List blockEntities) { return blockEntities.stream() .filter((blockEntityInfo) -> { diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index b889c3c1..013b02bf 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -475,7 +475,7 @@ public abstract class TechHider { }; } - private final Reflection.Field moveEntityPacketEntityIdField = Reflection.getField(ClientboundMoveEntityPacket.class, Integer.class, 0); + private final Reflection.Field moveEntityPacketEntityIdField = Reflection.getField(ClientboundMoveEntityPacket.class, int.class, 0); private Packet processMoveEntityPacket(Player player, ClientboundMoveEntityPacket packet) { int entityId = moveEntityPacketEntityIdField.get(packet); @@ -487,7 +487,7 @@ public abstract class TechHider { } } - private final Reflection.Field rotateHeadPacketEntityIdField = Reflection.getField(ClientboundRotateHeadPacket.class, Integer.class, 0); + private final Reflection.Field rotateHeadPacketEntityIdField = Reflection.getField(ClientboundRotateHeadPacket.class, int.class, 0); private Packet processRotateHeadPacket(Player player, ClientboundRotateHeadPacket packet) { int entityId = rotateHeadPacketEntityIdField.get(packet); From dc0365834a966a4462a29931535a6f4c49537f55 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Thu, 21 May 2026 12:13:41 +0200 Subject: [PATCH 29/52] fixes --- FightSystem/build.gradle.kts | 1 + .../comphenix/tinyprotocol/TinyProtocol.java | 18 ++++++++++-------- .../src/de/steamwar/techhider/ChunkHider.java | 8 ++++++-- .../src/de/steamwar/techhider/TechHider.java | 13 +++++++++++-- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/FightSystem/build.gradle.kts b/FightSystem/build.gradle.kts index 6f0c40a7..a9a84cbd 100644 --- a/FightSystem/build.gradle.kts +++ b/FightSystem/build.gradle.kts @@ -57,6 +57,7 @@ tasks.register("WarGear21") { description = "Run a WarGear 1.21 Fight Server" dependsOn(":SpigotCore:shadowJar") dependsOn(":FightSystem:shadowJar") + dependsOn(":KotlinCore:shadowJar") template = "WarGear21" worldName = "arenas/Pentraki" config = "WarGear20.yml" diff --git a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java index c0a869d0..ec599390 100644 --- a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java +++ b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java @@ -57,7 +57,7 @@ public class TinyProtocol { public static final TinyProtocol instance = new TinyProtocol(Core.getInstance()); private final Map, List>> packetFilters = new HashMap<>(); - private final Set> globalFilters = new HashSet<>(); + private final Set> globalClientboundFilters = new HashSet<>(); public static void init() { // enforce init @@ -235,8 +235,8 @@ public class TinyProtocol { packetFilters.getOrDefault(packetType, Collections.emptyList()).remove(filter); } - public void addGlobalFilter(BiFunction filter) { - globalFilters.add(filter); + public void addGlobalClientboundFilter(BiFunction filter) { + globalClientboundFilters.add(filter); } /** @@ -450,6 +450,13 @@ public class TinyProtocol { public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { try { msg = filterPacket(player, msg); + + if(msg instanceof Packet) { + for (BiFunction filter : globalClientboundFilters) { + msg = filter.apply(player, msg); + if (msg == null) break; + } + } } catch (Exception e) { plugin.getLogger().log(Level.SEVERE, "Error in onPacketOutAsync().", e); } @@ -473,11 +480,6 @@ public class TinyProtocol { if (packet == null) break; } - for (BiFunction filter : globalFilters) { - packet = filter.apply(player, packet); - if (packet == null) break; - } - return packet; } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java index 7262a6f4..d643dd44 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java @@ -56,6 +56,7 @@ public abstract class ChunkHider { 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; @@ -108,6 +109,8 @@ public abstract class ChunkHider { int palletLength = ProtocolUtils.readVarInt(in); int[] pallet = ProtocolUtils.readVarIntArray(in, palletLength); + System.out.println(Arrays.toString(pallet)); + long[] rawData = readSectionDataFromBuffer(in, bitsPerBlock, BLOCKS_PER_SECTION); SimpleBitStorage data = new SimpleBitStorage(bitsPerBlock, BLOCKS_PER_SECTION, rawData); @@ -128,6 +131,7 @@ public abstract class ChunkHider { int worldZ = sectionZ * chunkZ; int blockId = resolvedData[blockDataIndex]; + System.out.println("DEV - " + blockId); Block block = BuiltInRegistries.BLOCK.get(blockId).get().value(); if(isPlayerPrivilegedToAccessPosition(player, worldX, worldY, worldZ) && isPlayerPrivilegedToAccessBlock(player, worldX, worldY, worldZ, block)) { @@ -271,12 +275,12 @@ public abstract class ChunkHider { private void copyOverSectionBiomeData(ByteBuf oldData, ByteBuf newData){ short bitsPerBiome = oldData.readShort(); newData.writeShort(bitsPerBiome); - + System.out.println(bitsPerBiome); if(bitsPerBiome == 0) { int sectionBiomeId = ProtocolUtils.readVarInt(oldData); ProtocolUtils.writeVarInt(newData, sectionBiomeId); } - else if(bitsPerBiome <= BIT_PER_BLOCK_INDIRECTION_LIMIT) { + else if(bitsPerBiome <= BIT_PER_BIOME_INDIRECTION_LIMIT) { int palletLength = ProtocolUtils.readVarInt(oldData); ProtocolUtils.writeVarInt(newData, palletLength); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 013b02bf..43d27615 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -14,6 +14,7 @@ import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.shorts.ShortArraySet; import it.unimi.dsi.fastutil.shorts.ShortSets; +import net.minecraft.network.UnconfiguredPipelineHandler; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.phys.Vec3; @@ -26,6 +27,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.network.PacketListener; import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.handshake.ClientIntentionPacket; import net.minecraft.network.protocol.common.ClientboundClearDialogPacket; import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; import net.minecraft.network.protocol.common.ClientboundCustomReportDetailsPacket; @@ -162,6 +164,7 @@ import net.minecraft.network.protocol.login.ClientboundHelloPacket; import net.minecraft.network.protocol.login.ClientboundLoginCompressionPacket; import net.minecraft.network.protocol.login.ClientboundLoginDisconnectPacket; import net.minecraft.network.protocol.login.ClientboundLoginFinishedPacket; +import net.minecraft.network.protocol.configuration.ClientboundSelectKnownPacks; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; @@ -171,7 +174,7 @@ import net.minecraft.world.level.block.state.BlockState; * way that is also compliant with the principle of default-deny. */ public abstract class TechHider { - private final Set>> bypassingPackets; + private final Set> bypassingPackets; private final Map>, BiFunction, Packet>> packetProcessors; private final Block blockUsedForObfuscation; @@ -210,6 +213,7 @@ public abstract class TechHider { 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 @@ -440,14 +444,19 @@ public abstract class TechHider { this.packetProcessors = processors; - TinyProtocol.instance.addGlobalFilter((player, packet) -> { + TinyProtocol.instance.addGlobalClientboundFilter((player, packet) -> { + System.out.println(packet.getClass().toString()); + if(bypassingPackets.contains(packet.getClass())) { + System.out.println("bypassing"); return packet; } else if(packetProcessors.containsKey(packet.getClass())) { + System.out.println("processing"); return packetProcessors.get(packet.getClass()).apply(player, (Packet) packet); } else { + System.out.println("dropping"); return null; } }); From 49fafa5955a10bdf668ed52d73b23b2aec1803f8 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 21 May 2026 13:00:53 +0200 Subject: [PATCH 30/52] Fix build --- .../SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java index 2361be24..b5276c00 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java @@ -56,7 +56,7 @@ public class AntiNocom implements Listener { } private void registerUseItem() { - TinyProtocol.instance.addTypedFilter(ServerboundUseItemOnPacket.class, (player, packet) -> { + TinyProtocol.instance.addFilter(ServerboundUseItemOnPacket.class, (player, packet) -> { BlockPos pos = packet.getHitResult().getBlockPos(); return isValid(player, "UseItem", pos.getX(), pos.getZ()) ? packet : null; }); From 1f44b3169e6b5b160baf9cb8d38dd9aa65f84c19 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 21 May 2026 13:05:16 +0200 Subject: [PATCH 31/52] Remove unused stuff --- .../de/steamwar/techhider/QuadFunction.java | 24 ------------------- .../src/de/steamwar/techhider/TechHider.java | 2 +- 2 files changed, 1 insertion(+), 25 deletions(-) delete mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/QuadFunction.java diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/QuadFunction.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/QuadFunction.java deleted file mode 100644 index 43df8d3b..00000000 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/QuadFunction.java +++ /dev/null @@ -1,24 +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 . - */ - -package de.steamwar.techhider; - -public interface QuadFunction { - Return apply(A a, B b, C c, D d); -} \ No newline at end of file diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 43d27615..8ed5b0f7 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -354,7 +354,7 @@ public abstract class TechHider { 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 hidde activity. + // 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)); From a2caa79f99ef611356357c5fba8542307ffb1ed9 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Thu, 21 May 2026 15:06:33 +0200 Subject: [PATCH 32/52] Erste bugs ausgemerzt --- .../fightsystem/utils/TechHiderWrapper.java | 11 +- .../src/de/steamwar/techhider/ChunkHider.java | 169 +++++++++--------- .../src/de/steamwar/techhider/TechHider.java | 19 +- 3 files changed, 110 insertions(+), 89 deletions(-) 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 1160b650..9f1ab63b 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -74,29 +74,30 @@ public class TechHiderWrapper extends StateDependent implements Listener { @Override public boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block) { Region hiddenRegion = getHiddenRegion(p); - return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blocksToObfuscate.contains(block) ; + return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blocksToObfuscate.contains(block); } + // TODO @Override public boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId) { - return false; + return true; } @Override public boolean isPlayerPrivilegedToAccessBlocEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type) { //Region hiddenRegion = getHiddenRegion(p); //return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type); - return false; + return true; } @Override public boolean isPlayerPrivilegedToAccessContainer(Player p, int containerId) { - return false; + return true; } @Override public boolean isPlayerPrivilegedToAccessSound(Player p, ResourceLocation soundId) { - return false; + return true; } }; diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java index d643dd44..01a0fdfe 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java @@ -22,27 +22,20 @@ package de.steamwar.techhider; import de.steamwar.Reflection; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import it.unimi.dsi.fastutil.ints.Int2IntArrayMap; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; -import lombok.Getter; -import net.minecraft.core.Registry; +import it.unimi.dsi.fastutil.ints.IntArrayList; import net.minecraft.core.SectionPos; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; -import net.minecraft.resources.ResourceLocation; import net.minecraft.util.SimpleBitStorage; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; -import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import net.minecraft.world.level.block.state.BlockState; import org.bukkit.entity.Player; import java.util.*; -import java.util.function.BiFunction; import java.util.function.UnaryOperator; -import java.util.stream.Collectors; public abstract class ChunkHider { @@ -63,16 +56,21 @@ public abstract class ChunkHider { private final byte BITS_PER_LONG = 64; - private final int blockIdUsedForHiding = 121; + private final int blockIdUsedForHiding = 315; public ChunkHider(Block blockUsedForObfuscation) { // blockIdUsedForHiding = Block.BLOCK_STATE_REGISTRY.getId(blockUsedForObfuscation.defaultBlockState()); } - private long[] readSectionDataFromBuffer(ByteBuf dataSource, short bitsPerEntry, int entryCount) { + private int getLongsRequiredToEncodeEntries(int bitsPerEntry, int entryCount) { int entriesPerLong = BITS_PER_LONG / 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++){ @@ -109,8 +107,6 @@ public abstract class ChunkHider { int palletLength = ProtocolUtils.readVarInt(in); int[] pallet = ProtocolUtils.readVarIntArray(in, palletLength); - System.out.println(Arrays.toString(pallet)); - long[] rawData = readSectionDataFromBuffer(in, bitsPerBlock, BLOCKS_PER_SECTION); SimpleBitStorage data = new SimpleBitStorage(bitsPerBlock, BLOCKS_PER_SECTION, rawData); @@ -120,42 +116,30 @@ public abstract class ChunkHider { resolvedData[i] = pallet[palletReference]; } - int[] obfuscatedData = new int[BLOCKS_PER_SECTION]; - for (int sectionY = 0; sectionY < 16; sectionY++) { - for (int sectionZ = 0; sectionZ < 16; sectionZ++) { - for (int sectionX = 0; sectionX < 16; sectionX++) { - int blockDataIndex = (((sectionY * 16) + sectionZ) * 16) + sectionX; + int[] obfuscatedBlockData = obfuscateBlockDataArray(player, chunkX, chunkZ, yOffset, resolvedData); - int worldX = sectionX * chunkX; - int worldY = sectionY * yOffset; - int worldZ = sectionZ * chunkZ; + long[] reEncodedData = encodeDirectBlockDataArray(obfuscatedBlockData); - int blockId = resolvedData[blockDataIndex]; - System.out.println("DEV - " + blockId); - Block block = BuiltInRegistries.BLOCK.get(blockId).get().value(); - if(isPlayerPrivilegedToAccessPosition(player, worldX, worldY, worldZ) && isPlayerPrivilegedToAccessBlock(player, worldX, worldY, worldZ, block)) { - obfuscatedData[blockDataIndex] = blockId; - } - else { - obfuscatedData[blockDataIndex] = blockIdUsedForHiding; - } - } - } + out.writeShort(blockCount); + out.writeByte(15); + for(long rawDataSegment : reEncodedData) { + out.writeLong(rawDataSegment); } - + /* Int2IntMap blockIdToPalletIndex = new Int2IntOpenHashMap(); - int[] newPallet = new int[palletLength]; - int runningIndex = 0; + IntArrayList newPallet = new IntArrayList(); for(int blockId : obfuscatedData) { if(!blockIdToPalletIndex.containsKey(blockId)) { - newPallet[runningIndex] = blockId; - blockIdToPalletIndex.put(blockId, runningIndex); - runningIndex++; + 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]; @@ -164,56 +148,28 @@ public abstract class ChunkHider { reEncodedData.set(i, palletReference); } - out.writeByte(blockCount); - out.writeByte(bitsPerBlock); + out.writeShort(blockCount); + out.writeByte(newBitsPerBlock); ProtocolUtils.writeVarInt(out, palletLength); - ProtocolUtils.writeVarIntArray(out, pallet); + ProtocolUtils.writeVarIntArray(out, newPalletRaw); for(long rawDataSegment : reEncodedData.getRaw()) { out.writeLong(rawDataSegment); - } + }*/ + } else { 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++){ - resolvedData[i] = data.get(i); - } + int[] blockData = resolveDirectBlockDataArray(rawData, bitsPerBlock); - int[] obfuscatedData = new int[BLOCKS_PER_SECTION]; - for (int sectionY = 0; sectionY < 16; sectionY++) { - for (int sectionZ = 0; sectionZ < 16; sectionZ++) { - for (int sectionX = 0; sectionX < 16; sectionX++) { - int blockDataIndex = (((sectionY * 16) + sectionZ) * 16) + sectionX; + int[] obfuscatedBlockData = obfuscateBlockDataArray(player, chunkX, chunkZ, yOffset, blockData); - int worldX = sectionX * chunkX; - int worldY = sectionY * yOffset; - int worldZ = sectionZ * chunkZ; + long[] reEncodedData = encodeDirectBlockDataArray(obfuscatedBlockData); - int blockId = resolvedData[blockDataIndex]; - Block block = BuiltInRegistries.BLOCK.get(blockId).get().value(); - if(isPlayerPrivilegedToAccessPosition(player, worldX, worldY, worldZ) && isPlayerPrivilegedToAccessBlock(player, worldX, worldY, worldZ, block)) { - obfuscatedData[blockDataIndex] = blockId; - } - else { - obfuscatedData[blockDataIndex] = blockIdUsedForHiding; - } - } - } - } - - SimpleBitStorage reEncodedData = new SimpleBitStorage(bitsPerBlock, BLOCKS_PER_SECTION, new long[rawData.length]); - for(int i = 0; i < obfuscatedData.length; i++) { - int blockId = obfuscatedData[i]; - - reEncodedData.set(i, blockId); - } - - out.writeByte(blockCount); - out.writeByte(bitsPerBlock); - for(long rawDataSegment : reEncodedData.getRaw()) { + out.writeShort(blockCount); + out.writeByte(15); + for(long rawDataSegment : reEncodedData) { out.writeLong(rawDataSegment); } } @@ -235,6 +191,60 @@ public abstract class ChunkHider { } + private int[] obfuscateBlockDataArray(Player player, 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(isPlayerPrivilegedToAccessPosition(player, worldX, worldY, worldZ) && isPlayerPrivilegedToAccessBlock(player, worldX, worldY, worldZ, block)) { + 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(); + } + public abstract boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ); public abstract boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block); public abstract boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type); @@ -273,9 +283,8 @@ public abstract class ChunkHider { } private void copyOverSectionBiomeData(ByteBuf oldData, ByteBuf newData){ - short bitsPerBiome = oldData.readShort(); - newData.writeShort(bitsPerBiome); - System.out.println(bitsPerBiome); + short bitsPerBiome = oldData.readByte(); + newData.writeByte(bitsPerBiome); if(bitsPerBiome == 0) { int sectionBiomeId = ProtocolUtils.readVarInt(oldData); ProtocolUtils.writeVarInt(newData, sectionBiomeId); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 43d27615..f1ec20e0 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -319,7 +319,10 @@ public abstract class TechHider { ClientboundUpdateTagsPacket.class, // 7.1.133 Update Tags (play) 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) + ClientboundMoveVehiclePacket.class, // 7.1.56 Move Vehicle (vehicle the player is in) + + //TODO rem later + ClientboundBundlePacket.class )); BiFunction, Packet> tossPacket = (p, packet) -> null; @@ -368,6 +371,10 @@ public abstract class TechHider { 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, (p, packet) -> this.processMoveEntityPacket(p, (ClientboundMoveEntityPacket) packet)); + processors.put(ClientboundMoveEntityPacket.Pos.class, (p, packet) -> this.processMoveEntityPacket(p, (ClientboundMoveEntityPacket.Pos) packet)); + processors.put(ClientboundMoveEntityPacket.Rot.class, (p, packet) -> this.processMoveEntityPacket(p, (ClientboundMoveEntityPacket.Rot) packet)); + processors.put(ClientboundMoveEntityPacket.PosRot.class, (p, packet) -> this.processMoveEntityPacket(p, (ClientboundMoveEntityPacket.PosRot) packet)); + // 7.1.82 Set Head Rotation: entity id and rotation. processors.put(ClientboundRotateHeadPacket.class, (p, packet) -> this.processRotateHeadPacket(p, (ClientboundRotateHeadPacket) packet)); // 7.1.76 Remove Entities: entity id visibility side channel. @@ -445,18 +452,19 @@ public abstract class TechHider { this.packetProcessors = processors; TinyProtocol.instance.addGlobalClientboundFilter((player, packet) -> { - System.out.println(packet.getClass().toString()); + if(bypassingPackets.contains(packet.getClass())) { - System.out.println("bypassing"); return packet; } else if(packetProcessors.containsKey(packet.getClass())) { - System.out.println("processing"); + //System.out.println("processing"); + //System.out.println(packet.getClass()); return packetProcessors.get(packet.getClass()).apply(player, (Packet) packet); } else { System.out.println("dropping"); + System.out.println(packet.getClass()); return null; } }); @@ -608,9 +616,12 @@ public abstract class TechHider { int worldZ = sectionPos.relativeToBlockZ(posShort); if (isPlayerPrivilegedToAccessPosition(p, worldX, worldY, worldZ) && isPlayerPrivilegedToAccessBlock(p, worldX, worldY, worldZ, block)) { + // TODO statefull !!! + modified = true; filteredPos.add(posShort); filteredStates.add(state); } else if(isPlayerPrivilegedToAccessPosition(p, worldX, worldY, worldZ)){ + modified = true; filteredPos.add(posShort); filteredStates.add(blockStateUsedForObfuscation); } From c241aef9320005ff7359e37d1beb0389e0caf61c Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 21 May 2026 17:53:51 +0200 Subject: [PATCH 33/52] Add BlockEntityType handling --- .../fightsystem/utils/TechHiderWrapper.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) 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 9f1ab63b..8179834a 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -19,6 +19,7 @@ package de.steamwar.fightsystem.utils; +import de.steamwar.Reflection; import de.steamwar.core.CraftbukkitWrapper; import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.events.BoardingEvent; @@ -31,6 +32,7 @@ import de.steamwar.fightsystem.states.StateDependent; import de.steamwar.fightsystem.states.StateDependentListener; import de.steamwar.techhider.TechHider; import lombok.Getter; +import net.minecraft.core.Holder; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; @@ -41,6 +43,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerQuitEvent; +import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -59,12 +62,21 @@ public class TechHiderWrapper extends StateDependent implements Listener { Set blocksToObfuscate = Config.GameModeConfig.Techhider.HiddenBlocks.stream() .map(CraftMagicNumbers::getBlock) .collect(Collectors.toUnmodifiableSet()); - /* Set> blockEntityTypeToObfuscate = Config.GameModeConfig.Techhider.HiddenBlockEntities.stream() + + Object blockEntityType; + try { + blockEntityType = BuiltInRegistries.class.getDeclaredField("BLOCK_ENTITY_TYPE").get(null); + } catch (Exception e) { + throw new IllegalStateException(e); + } + Reflection.Method method = Reflection.getTypedMethod(Reflection.getClass("net.minecraft.core.Registry"), "get", Optional.class, ResourceLocation.class); + Set> blockEntityTypeToObfuscate = Config.GameModeConfig.Techhider.HiddenBlockEntities.stream() .map((id) -> { ResourceLocation loc = ResourceLocation.parse(id); - return BuiltInRegistries.BLOCK_ENTITY_TYPE.get(loc).get().value(); + return ((Optional>>) method.invoke(blockEntityType, loc)).get().value(); }) - .collect(Collectors.toUnmodifiableSet()); */ + .collect(Collectors.toUnmodifiableSet()); + techHider = new TechHider(CraftMagicNumbers.getBlock(Config.GameModeConfig.Techhider.ObfuscateWith)) { @Override public boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ) { @@ -85,9 +97,8 @@ public class TechHiderWrapper extends StateDependent implements Listener { @Override public boolean isPlayerPrivilegedToAccessBlocEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type) { - //Region hiddenRegion = getHiddenRegion(p); - //return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type); - return true; + Region hiddenRegion = getHiddenRegion(p); + return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type); } @Override From 32fd4b4630af1d7029092756a1391174f489ff97 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 21 May 2026 18:11:42 +0200 Subject: [PATCH 34/52] Add Sound handling --- .../src/de/steamwar/techhider/TechHider.java | 177 ++---------------- 1 file changed, 20 insertions(+), 157 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 5317caf7..4e90c5df 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -1,172 +1,34 @@ package de.steamwar.techhider; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.ToIntFunction; - +import com.comphenix.tinyprotocol.TinyProtocol; +import de.steamwar.Reflection; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.shorts.ShortArraySet; import it.unimi.dsi.fastutil.shorts.ShortSets; -import net.minecraft.network.UnconfiguredPipelineHandler; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.phys.Vec3; -import org.bukkit.entity.Player; - -import com.comphenix.tinyprotocol.TinyProtocol; - -import de.steamwar.Reflection; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.network.PacketListener; import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.handshake.ClientIntentionPacket; -import net.minecraft.network.protocol.common.ClientboundClearDialogPacket; -import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; -import net.minecraft.network.protocol.common.ClientboundCustomReportDetailsPacket; -import net.minecraft.network.protocol.common.ClientboundDisconnectPacket; -import net.minecraft.network.protocol.common.ClientboundKeepAlivePacket; -import net.minecraft.network.protocol.common.ClientboundPingPacket; -import net.minecraft.network.protocol.common.ClientboundResourcePackPopPacket; -import net.minecraft.network.protocol.common.ClientboundResourcePackPushPacket; -import net.minecraft.network.protocol.common.ClientboundServerLinksPacket; -import net.minecraft.network.protocol.common.ClientboundShowDialogPacket; -import net.minecraft.network.protocol.common.ClientboundStoreCookiePacket; -import net.minecraft.network.protocol.common.ClientboundTransferPacket; -import net.minecraft.network.protocol.common.ClientboundUpdateTagsPacket; +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.ClientboundAddEntityPacket; -import net.minecraft.network.protocol.game.ClientboundAnimatePacket; -import net.minecraft.network.protocol.game.ClientboundBlockChangedAckPacket; -import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket; -import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; -import net.minecraft.network.protocol.game.ClientboundBlockEventPacket; -import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; -import net.minecraft.network.protocol.game.ClientboundBossEventPacket; -import net.minecraft.network.protocol.game.ClientboundBundleDelimiterPacket; -import net.minecraft.network.protocol.game.ClientboundBundlePacket; -import net.minecraft.network.protocol.game.ClientboundChangeDifficultyPacket; -import net.minecraft.network.protocol.game.ClientboundChunkBatchFinishedPacket; -import net.minecraft.network.protocol.game.ClientboundChunkBatchStartPacket; -import net.minecraft.network.protocol.game.ClientboundClearTitlesPacket; -import net.minecraft.network.protocol.game.ClientboundCommandSuggestionsPacket; -import net.minecraft.network.protocol.game.ClientboundCommandsPacket; -import net.minecraft.network.protocol.game.ClientboundContainerClosePacket; -import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; -import net.minecraft.network.protocol.game.ClientboundContainerSetDataPacket; -import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; -import net.minecraft.network.protocol.game.ClientboundCooldownPacket; -import net.minecraft.network.protocol.game.ClientboundCustomChatCompletionsPacket; -import net.minecraft.network.protocol.game.ClientboundDamageEventPacket; -import net.minecraft.network.protocol.game.ClientboundDeleteChatPacket; -import net.minecraft.network.protocol.game.ClientboundDisguisedChatPacket; -import net.minecraft.network.protocol.game.ClientboundEntityEventPacket; -import net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket; -import net.minecraft.network.protocol.game.ClientboundExplodePacket; -import net.minecraft.network.protocol.game.ClientboundForgetLevelChunkPacket; -import net.minecraft.network.protocol.game.ClientboundGameEventPacket; -import net.minecraft.network.protocol.game.ClientboundHorseScreenOpenPacket; -import net.minecraft.network.protocol.game.ClientboundHurtAnimationPacket; -import net.minecraft.network.protocol.game.ClientboundInitializeBorderPacket; -import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; -import net.minecraft.network.protocol.game.ClientboundLevelEventPacket; -import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket; -import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket; -import net.minecraft.network.protocol.game.ClientboundLoginPacket; -import net.minecraft.network.protocol.game.ClientboundMapItemDataPacket; -import net.minecraft.network.protocol.game.ClientboundMerchantOffersPacket; -import net.minecraft.network.protocol.game.ClientboundMoveEntityPacket; -import net.minecraft.network.protocol.game.ClientboundMoveMinecartPacket; -import net.minecraft.network.protocol.game.ClientboundMoveVehiclePacket; -import net.minecraft.network.protocol.game.ClientboundOpenBookPacket; -import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; -import net.minecraft.network.protocol.game.ClientboundOpenSignEditorPacket; -import net.minecraft.network.protocol.game.ClientboundPlaceGhostRecipePacket; -import net.minecraft.network.protocol.game.ClientboundPlayerAbilitiesPacket; -import net.minecraft.network.protocol.game.ClientboundPlayerChatPacket; -import net.minecraft.network.protocol.game.ClientboundPlayerCombatEndPacket; -import net.minecraft.network.protocol.game.ClientboundPlayerCombatEnterPacket; -import net.minecraft.network.protocol.game.ClientboundPlayerCombatKillPacket; -import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket; -import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket; -import net.minecraft.network.protocol.game.ClientboundPlayerLookAtPacket; -import net.minecraft.network.protocol.game.ClientboundPlayerPositionPacket; -import net.minecraft.network.protocol.game.ClientboundPlayerRotationPacket; -import net.minecraft.network.protocol.game.ClientboundProjectilePowerPacket; -import net.minecraft.network.protocol.game.ClientboundRecipeBookAddPacket; -import net.minecraft.network.protocol.game.ClientboundRecipeBookRemovePacket; -import net.minecraft.network.protocol.game.ClientboundRecipeBookSettingsPacket; -import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; -import net.minecraft.network.protocol.game.ClientboundRemoveMobEffectPacket; -import net.minecraft.network.protocol.game.ClientboundResetScorePacket; -import net.minecraft.network.protocol.game.ClientboundRespawnPacket; -import net.minecraft.network.protocol.game.ClientboundRotateHeadPacket; -import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket; -import net.minecraft.network.protocol.game.ClientboundSelectAdvancementsTabPacket; -import net.minecraft.network.protocol.game.ClientboundSetActionBarTextPacket; -import net.minecraft.network.protocol.game.ClientboundSetBorderCenterPacket; -import net.minecraft.network.protocol.game.ClientboundSetBorderLerpSizePacket; -import net.minecraft.network.protocol.game.ClientboundSetBorderSizePacket; -import net.minecraft.network.protocol.game.ClientboundSetBorderWarningDelayPacket; -import net.minecraft.network.protocol.game.ClientboundSetBorderWarningDistancePacket; -import net.minecraft.network.protocol.game.ClientboundSetCameraPacket; -import net.minecraft.network.protocol.game.ClientboundSetChunkCacheCenterPacket; -import net.minecraft.network.protocol.game.ClientboundSetChunkCacheRadiusPacket; -import net.minecraft.network.protocol.game.ClientboundSetCursorItemPacket; -import net.minecraft.network.protocol.game.ClientboundServerDataPacket; -import net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket; -import net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket; -import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; -import net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket; -import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket; -import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket; -import net.minecraft.network.protocol.game.ClientboundSetExperiencePacket; -import net.minecraft.network.protocol.game.ClientboundSetHeldSlotPacket; -import net.minecraft.network.protocol.game.ClientboundSetHealthPacket; -import net.minecraft.network.protocol.game.ClientboundSetObjectivePacket; -import net.minecraft.network.protocol.game.ClientboundSetPassengersPacket; -import net.minecraft.network.protocol.game.ClientboundSetPlayerInventoryPacket; -import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket; -import net.minecraft.network.protocol.game.ClientboundSetScorePacket; -import net.minecraft.network.protocol.game.ClientboundSetSimulationDistancePacket; -import net.minecraft.network.protocol.game.ClientboundSetSubtitleTextPacket; -import net.minecraft.network.protocol.game.ClientboundSetTimePacket; -import net.minecraft.network.protocol.game.ClientboundSetTitleTextPacket; -import net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket; -import net.minecraft.network.protocol.game.ClientboundSoundEntityPacket; -import net.minecraft.network.protocol.game.ClientboundSoundPacket; -import net.minecraft.network.protocol.game.ClientboundStartConfigurationPacket; -import net.minecraft.network.protocol.game.ClientboundStopSoundPacket; -import net.minecraft.network.protocol.game.ClientboundSystemChatPacket; -import net.minecraft.network.protocol.game.ClientboundTabListPacket; -import net.minecraft.network.protocol.game.ClientboundTagQueryPacket; -import net.minecraft.network.protocol.game.ClientboundTakeItemEntityPacket; -import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket; -import net.minecraft.network.protocol.game.ClientboundTickingStatePacket; -import net.minecraft.network.protocol.game.ClientboundTickingStepPacket; -import net.minecraft.network.protocol.game.ClientboundTrackedWaypointPacket; -import net.minecraft.network.protocol.game.ClientboundUpdateAdvancementsPacket; -import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket; -import net.minecraft.network.protocol.game.ClientboundUpdateMobEffectPacket; -import net.minecraft.network.protocol.game.ClientboundUpdateRecipesPacket; -import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; -import net.minecraft.network.protocol.login.ClientboundHelloPacket; -import net.minecraft.network.protocol.login.ClientboundLoginCompressionPacket; -import net.minecraft.network.protocol.login.ClientboundLoginDisconnectPacket; -import net.minecraft.network.protocol.login.ClientboundLoginFinishedPacket; -import net.minecraft.network.protocol.configuration.ClientboundSelectKnownPacks; +import net.minecraft.network.protocol.game.*; +import net.minecraft.network.protocol.login.*; +import net.minecraft.resources.ResourceLocation; 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, @@ -320,6 +182,7 @@ public abstract class TechHider { 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 //TODO rem later ClientboundBundlePacket.class @@ -422,11 +285,11 @@ public abstract class TechHider { // --- Sound packets --- // 7.1.115 Entity Sound Effect: entity id and sound. - processors.put(ClientboundSoundEntityPacket.class, tossPacket); + processors.put(ClientboundSoundEntityPacket.class, this.buildEntityPacketProcessor(ClientboundSoundEntityPacket::getId)); // 7.1.116 Sound Effect: sound type and position. - processors.put(ClientboundSoundPacket.class, tossPacket); - // 7.1.118 Stop Sound: sound state side channel. - processors.put(ClientboundStopSoundPacket.class, tossPacket); + processors.put(ClientboundSoundPacket.class, this.buildPositionBasedPacketProcessor(packet -> { + return new BlockPos.MutableBlockPos(packet.getX(), packet.getY(), packet.getZ()).immutable(); + })); // --- Others --- // 7.1.137 Waypoint: world/entity tracking signal. From 29106d5bb50173b9d50d24338653aab6246f9038 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Thu, 21 May 2026 18:20:15 +0200 Subject: [PATCH 35/52] Add bundle packet handling --- .../steamwar/fightsystem/utils/HullHider.java | 12 ---- .../fightsystem/utils/TechHiderWrapper.java | 11 +++- .../src/de/steamwar/techhider/TechHider.java | 59 ++++++++++++------- 3 files changed, 47 insertions(+), 35 deletions(-) diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java index ea00c560..3b4555c6 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java @@ -140,18 +140,6 @@ public class HullHider implements Listener { return false; } - public boolean blockPrecise(Player player, int chunkX, int chunkY, int chunkZ) { - if (!TechHiderWrapper.ENABLED) return false; - - for (Hull hull : hulls) { - if (hull.blockPrecise(player, chunkX, chunkY, chunkZ)) { - return true; - } - } - - return false; - } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onSpawn(EntitySpawnEvent e) { for (Hull hull : hulls) { 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 8179834a..15853d7c 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -37,6 +37,7 @@ import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; +import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.util.CraftMagicNumbers; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -89,10 +90,16 @@ public class TechHiderWrapper extends StateDependent implements Listener { return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blocksToObfuscate.contains(block); } - // TODO @Override public boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId) { - return true; + net.minecraft.world.entity.Entity nmsEntity = ((CraftWorld) p.getWorld()).getHandle().moonrise$getEntityLookup().get(entityId); + + if(nmsEntity != null) { + return !hullHider.isBlockHidden(p, nmsEntity.getBlockX(), nmsEntity.getBlockY(), nmsEntity.getBlockZ()); + } + else { + return false; + } } @Override diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 4e90c5df..dab7bf8a 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -6,6 +6,11 @@ import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.shorts.ShortArraySet; import it.unimi.dsi.fastutil.shorts.ShortSets; +import net.minecraft.network.protocol.game.*; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.phys.Vec3; +import org.bukkit.entity.Player; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.network.PacketListener; @@ -18,12 +23,13 @@ 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.resources.ResourceLocation; +import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; +import net.minecraft.network.protocol.login.ClientboundHelloPacket; +import net.minecraft.network.protocol.login.ClientboundLoginCompressionPacket; +import net.minecraft.network.protocol.login.ClientboundLoginDisconnectPacket; +import net.minecraft.network.protocol.login.ClientboundLoginFinishedPacket; 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; @@ -182,19 +188,14 @@ public abstract class TechHider { 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 + ClientboundStopSoundPacket.class // 7.1.118 Stop Sound: sound state side channel + - //TODO rem later - ClientboundBundlePacket.class )); BiFunction, Packet> tossPacket = (p, packet) -> null; Map>, BiFunction, Packet>> processors = new HashMap<>(); - // 7.1.x Bundle Packet: packet container; blocked until nested packets are - // safely unbundled/processed. - processors.put(ClientboundBundlePacket.class, tossPacket); - // --- Entity packets - // 7.1.2 Spawn Entity: entity type and position can reveal hidden contraptions. processors.put(ClientboundAddEntityPacket.class, (p, packet) -> this.processAddEntityPacket(p, (ClientboundAddEntityPacket) packet)); @@ -299,7 +300,7 @@ public abstract class TechHider { // 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(ClientboundLevelEventPacket::getPos)); + processors.put(ClientboundOpenSignEditorPacket.class, buildPositionBasedPacketProcessor(ClientboundOpenSignEditorPacket::getPos)); // 7.1.37 Explosion: position, affected blocks, and knockback can reveal TNT/tech. processors.put(ClientboundExplodePacket.class, (p, rawPacket) -> { ClientboundExplodePacket packet = (ClientboundExplodePacket) rawPacket; @@ -314,25 +315,41 @@ public abstract class TechHider { this.packetProcessors = processors; - TinyProtocol.instance.addGlobalClientboundFilter((player, packet) -> { + TinyProtocol.instance.addGlobalClientboundFilter((player, rawPacket) -> { + if(rawPacket instanceof ClientboundBundlePacket bundlePacket) { + List> processedPackets = new ArrayList<>(); + for(Packet packet : bundlePacket.subPackets()) { + Packet processedPacket = (Packet) processPacket(player, packet); - if(bypassingPackets.contains(packet.getClass())) { - return packet; + if(processedPacket != null) { + processedPackets.add(processedPacket); + } + } + + return new ClientboundBundlePacket(processedPackets); } - else if(packetProcessors.containsKey(packet.getClass())) { - //System.out.println("processing"); - //System.out.println(packet.getClass()); - return packetProcessors.get(packet.getClass()).apply(player, (Packet) packet); + else if(rawPacket instanceof Packet) { + return processPacket(player, (Packet) rawPacket); } else { - System.out.println("dropping"); - System.out.println(packet.getClass()); return null; } }); } + private Packet processPacket(Player player, Packet packet) { + if(bypassingPackets.contains(packet.getClass())) { + return packet; + } + else if(packetProcessors.containsKey(packet.getClass())) { + return packetProcessors.get(packet.getClass()).apply(player, (Packet) packet); + } + else { + return null; + } + } + private Packet processAddEntityPacket(Player player, ClientboundAddEntityPacket packet) { if(isPlayerPrivilegedToAccessEntity(player, packet.getId()) && isPlayerPrivilegedToAccessPosition(player, (int) packet.getX(), (int) packet.getY(), (int) packet.getZ())) { return packet; From d07a86c13fd2188631dda0ef914cd541f718cf50 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Thu, 21 May 2026 18:41:55 +0200 Subject: [PATCH 36/52] remove container hiding as it has no lacalaity mapping posibility --- .../fightsystem/utils/TechHiderWrapper.java | 10 ------ .../src/de/steamwar/techhider/TechHider.java | 33 +++---------------- 2 files changed, 5 insertions(+), 38 deletions(-) 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 15853d7c..193f851b 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -107,16 +107,6 @@ public class TechHiderWrapper extends StateDependent implements Listener { Region hiddenRegion = getHiddenRegion(p); return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type); } - - @Override - public boolean isPlayerPrivilegedToAccessContainer(Player p, int containerId) { - return true; - } - - @Override - public boolean isPlayerPrivilegedToAccessSound(Player p, ResourceLocation soundId) { - return true; - } }; new StateDependentListener(ENABLED, FightState.Schem, this); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index dab7bf8a..4d848379 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -180,7 +180,7 @@ public abstract 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) - // ClientboundChunksBiomesPacket.class, // 7.1.14 Chunk Biomes + 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 @@ -188,9 +188,11 @@ public abstract class TechHider { 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 - + ClientboundStopSoundPacket.class, // 7.1.118 Stop Sound: sound state side channel + 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, Packet> tossPacket = (p, packet) -> null; @@ -276,14 +278,6 @@ public abstract class TechHider { // 7.1.48 Update Light: lighting can reveal hidden blocks/operations. processors.put(ClientboundLightUpdatePacket.class, tossPacket); - // --- Container packets --- - // 7.1.19 Set Container Content - processors.put(ClientboundContainerSetContentPacket.class, buildContainerPacketProcessor(ClientboundContainerSetContentPacket::containerId)); - // 7.1.20 Set Container Property - processors.put(ClientboundContainerSetDataPacket.class, buildContainerPacketProcessor(ClientboundContainerSetDataPacket::getContainerId)); - // 7.1.21 Set Container Slot - processors.put(ClientboundContainerSetSlotPacket.class, buildContainerPacketProcessor(ClientboundContainerSetSlotPacket::getContainerId)); - // --- Sound packets --- // 7.1.115 Entity Sound Effect: entity id and sound. processors.put(ClientboundSoundEntityPacket.class, this.buildEntityPacketProcessor(ClientboundSoundEntityPacket::getId)); @@ -545,21 +539,6 @@ public abstract class TechHider { return chunkHider.processLevelChunkWithLightPacket(p, packet); } - private Packet processContainerPacket(Player player, int containerId, Packet packet) { - if(isPlayerPrivilegedToAccessContainer(player, containerId)) { - return packet; - } else { - return null; - } - } - private > BiFunction, Packet> buildContainerPacketProcessor(ToIntFunction containerIdExtractor) { - return (p, rawPacket) -> { - TTargetPacket packet = (TTargetPacket) rawPacket; - - return processContainerPacket(p, containerIdExtractor.applyAsInt(packet), packet); - }; - } - private Packet proccessPositionBasedPacket(Player player, int blockX, int blockY, int blockZ, Packet packet) { if(isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) { return packet; @@ -583,6 +562,4 @@ public abstract class TechHider { public abstract boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block); public abstract boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId); public abstract boolean isPlayerPrivilegedToAccessBlocEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type); - public abstract boolean isPlayerPrivilegedToAccessContainer(Player p, int containerId); - public abstract boolean isPlayerPrivilegedToAccessSound(Player p, ResourceLocation soundId); } From 108aa2af9a65ee04199986fd7d584550cc17b26d Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Thu, 21 May 2026 18:46:00 +0200 Subject: [PATCH 37/52] remove unused reflection class --- .../comphenix/tinyprotocol/Reflection.java | 502 ------------------ 1 file changed, 502 deletions(-) delete mode 100644 SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/Reflection.java diff --git a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/Reflection.java b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/Reflection.java deleted file mode 100644 index c9bd2db3..00000000 --- a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/Reflection.java +++ /dev/null @@ -1,502 +0,0 @@ -package com.comphenix.tinyprotocol; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Arrays; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.bukkit.Bukkit; - -/** - * An utility class that simplifies reflection in Bukkit plugins. - * - * @author Kristian - */ -public final class Reflection { - /** - * An interface for invoking a specific constructor. - */ - public interface ConstructorInvoker { - /** - * Invoke a constructor for a specific class. - * - * @param arguments - the arguments to pass to the constructor. - * @return The constructed object. - */ - public Object invoke(Object... arguments); - } - - /** - * An interface for invoking a specific method. - */ - public interface MethodInvoker { - /** - * Invoke a method on a specific target object. - * - * @param target - the target object, or NULL for a static method. - * @param arguments - the arguments to pass to the method. - * @return The return value, or NULL if is void. - */ - public Object invoke(Object target, Object... arguments); - } - - /** - * An interface for retrieving the field content. - * - * @param - field type. - */ - public interface FieldAccessor { - /** - * Retrieve the content of a field. - * - * @param target - the target object, or NULL for a static field. - * @return The value of the field. - */ - public T get(Object target); - - /** - * Set the content of a field. - * - * @param target - the target object, or NULL for a static field. - * @param value - the new value of the field. - */ - public void set(Object target, Object value); - - /** - * Determine if the given object has this field. - * - * @param target - the object to test. - * @return TRUE if it does, FALSE otherwise. - */ - public boolean hasField(Object target); - } - - // Deduce the net.minecraft.server.v* package - private static String OBC_PREFIX = Bukkit.getServer().getClass().getPackage().getName(); - private static String NMS_PREFIX = OBC_PREFIX.replace("org.bukkit.craftbukkit", "net.minecraft.server"); - private static String VERSION = OBC_PREFIX.replace("org.bukkit.craftbukkit", "").replace(".", ""); - - // Variable replacement - private static Pattern MATCH_VARIABLE = Pattern.compile("\\{([^\\}]+)\\}"); - - private Reflection() { - // Seal class - } - - /** - * Retrieve a field accessor for a specific field type and name. - * - * @param target - the target type. - * @param name - the name of the field, or NULL to ignore. - * @param fieldType - a compatible field type. - * @return The field accessor. - */ - public static FieldAccessor getField(Class target, String name, Class fieldType) { - return getField(target, name, fieldType, 0); - } - - /** - * Retrieve a field accessor for a specific field type and name. - * - * @param className - lookup name of the class, see {@link #getClass(String)}. - * @param name - the name of the field, or NULL to ignore. - * @param fieldType - a compatible field type. - * @return The field accessor. - */ - public static FieldAccessor getField(String className, String name, Class fieldType) { - return getField(getClass(className), name, fieldType, 0); - } - - /** - * Retrieve a field accessor for a specific field type and name. - * - * @param target - the target type. - * @param fieldType - a compatible field type. - * @param index - the number of compatible fields to skip. - * @return The field accessor. - */ - public static FieldAccessor getField(Class target, Class fieldType, int index) { - return getField(target, null, fieldType, index); - } - - /** - * Retrieve a field accessor for a specific field type and name. - * - * @param className - lookup name of the class, see {@link #getClass(String)}. - * @param fieldType - a compatible field type. - * @param index - the number of compatible fields to skip. - * @return The field accessor. - */ - public static FieldAccessor getField(String className, Class fieldType, int index) { - return getField(getClass(className), fieldType, index); - } - - // Common method - private static FieldAccessor getField(Class target, String name, Class fieldType, int index) { - for (final Field field : target.getDeclaredFields()) { - if ((name == null || field.getName().equals(name)) && fieldType.isAssignableFrom(field.getType()) && index-- <= 0) { - field.setAccessible(true); - - // A function for retrieving a specific field value - return new FieldAccessor() { - - @Override - @SuppressWarnings("unchecked") - public T get(Object target) { - try { - return (T) field.get(target); - } catch (IllegalAccessException e) { - throw new RuntimeException("Cannot access reflection.", e); - } - } - - @Override - public void set(Object target, Object value) { - try { - field.set(target, value); - } catch (IllegalAccessException e) { - throw new RuntimeException("Cannot access reflection.", e); - } - } - - @Override - public boolean hasField(Object target) { - // target instanceof DeclaringClass - return field.getDeclaringClass().isAssignableFrom(target.getClass()); - } - }; - } - } - - // Search in parent classes - if (target.getSuperclass() != null) - return getField(target.getSuperclass(), name, fieldType, index); - - throw new IllegalArgumentException("Cannot find field with type " + fieldType); - } - - /** - * Retrieves a field with a given type and parameters. This is most useful - * when dealing with Collections. - * - * @param target the target class. - * @param fieldType Type of the field - * @param params Variable length array of type parameters - * @return The field - * - * @throws IllegalArgumentException If the field cannot be found - */ - public static Field getParameterizedField(Class target, Class fieldType, Class... params) { - for (Field field : target.getDeclaredFields()) { - if (field.getType().equals(fieldType)) { - Type type = field.getGenericType(); - if (type instanceof ParameterizedType) { - if (Arrays.equals(((ParameterizedType) type).getActualTypeArguments(), params)) - return field; - } - } - } - - throw new IllegalArgumentException("Unable to find a field with type " + fieldType + " and params " + Arrays.toString(params)); - } - - /** - * Search for the first publicly and privately defined method of the given name and parameter count. - * - * @param className - lookup name of the class, see {@link #getClass(String)}. - * @param methodName - the method name, or NULL to skip. - * @param params - the expected parameters. - * @return An object that invokes this specific method. - * @throws IllegalStateException If we cannot find this method. - */ - public static MethodInvoker getMethod(String className, String methodName, Class... params) { - return getTypedMethod(getClass(className), methodName, null, params); - } - - /** - * Search for the first publicly and privately defined method of the given name and parameter count. - * - * @param clazz - a class to start with. - * @param methodName - the method name, or NULL to skip. - * @param params - the expected parameters. - * @return An object that invokes this specific method. - * @throws IllegalStateException If we cannot find this method. - */ - public static MethodInvoker getMethod(Class clazz, String methodName, Class... params) { - return getTypedMethod(clazz, methodName, null, params); - } - - /** - * Search for the first publicly and privately defined method of the given name and parameter count. - * - * @param clazz - a class to start with. - * @param methodName - the method name, or NULL to skip. - * @param returnType - the expected return type, or NULL to ignore. - * @param params - the expected parameters. - * @return An object that invokes this specific method. - * @throws IllegalStateException If we cannot find this method. - */ - public static MethodInvoker getTypedMethod(Class clazz, String methodName, Class returnType, Class... params) { - for (final Method method : clazz.getDeclaredMethods()) { - if ((methodName == null || method.getName().equals(methodName)) - && (returnType == null || method.getReturnType().equals(returnType)) - && Arrays.equals(method.getParameterTypes(), params)) { - method.setAccessible(true); - - return new MethodInvoker() { - - @Override - public Object invoke(Object target, Object... arguments) { - try { - return method.invoke(target, arguments); - } catch (Exception e) { - throw new RuntimeException("Cannot invoke method " + method, e); - } - } - - }; - } - } - - // Search in every superclass - if (clazz.getSuperclass() != null) - return getMethod(clazz.getSuperclass(), methodName, params); - - throw new IllegalStateException(String.format("Unable to find method %s (%s).", methodName, Arrays.asList(params))); - } - - /** - * Search for the first publically and privately defined constructor of the given name and parameter count. - * - * @param className - lookup name of the class, see {@link #getClass(String)}. - * @param params - the expected parameters. - * @return An object that invokes this constructor. - * @throws IllegalStateException If we cannot find this method. - */ - public static ConstructorInvoker getConstructor(String className, Class... params) { - return getConstructor(getClass(className), params); - } - - /** - * Search for the first publically and privately defined constructor of the given name and parameter count. - * - * @param clazz - a class to start with. - * @param params - the expected parameters. - * @return An object that invokes this constructor. - * @throws IllegalStateException If we cannot find this method. - */ - public static ConstructorInvoker getConstructor(Class clazz, Class... params) { - for (final Constructor constructor : clazz.getDeclaredConstructors()) { - if (Arrays.equals(constructor.getParameterTypes(), params)) { - constructor.setAccessible(true); - - return new ConstructorInvoker() { - - @Override - public Object invoke(Object... arguments) { - try { - return constructor.newInstance(arguments); - } catch (Exception e) { - throw new RuntimeException("Cannot invoke constructor " + constructor, e); - } - } - - }; - } - } - - throw new IllegalStateException(String.format("Unable to find constructor for %s (%s).", clazz, Arrays.asList(params))); - } - - /** - * Retrieve a class from its full name, without knowing its type on compile time. - *

- * This is useful when looking up fields by a NMS or OBC type. - *

- * - * @see {@link #getClass()} for more information. - * @param lookupName - the class name with variables. - * @return The class. - */ - public static Class getUntypedClass(String lookupName) { - @SuppressWarnings({ "rawtypes", "unchecked" }) - Class clazz = (Class) getClass(lookupName); - return clazz; - } - - /** - * Retrieve a class from its full name with alternatives, without knowing its type on compile time. - *

- * This is useful when looking up fields by a NMS or OBC type. - *

- * - * @see {@link #getClass()} for more information. - * @param lookupName - the class name with variables. - * @param aliases - alternative names for this class. - * @return The class. - */ - public static Class getUntypedClass(String lookupName, String... aliases) { - @SuppressWarnings({ "rawtypes", "unchecked" }) - Class clazz = (Class) getClass(lookupName, aliases); - return clazz; - } - - /** - * Retrieve a class from its full name. - *

- * Strings enclosed with curly brackets - such as {TEXT} - will be replaced according to the following table: - *

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
VariableContent
{nms}Actual package name of net.minecraft.server.VERSION
{obc}Actual pacakge name of org.bukkit.craftbukkit.VERSION
{version}The current Minecraft package VERSION, if any.
- * - * @param lookupName - the class name with variables. - * @return The looked up class. - * @throws IllegalArgumentException If a variable or class could not be found. - */ - public static Class getClass(String lookupName) { - return getCanonicalClass(expandVariables(lookupName)); - } - - /** - * Retrieve the first class that matches the full class name. - *

- * Strings enclosed with curly brackets - such as {TEXT} - will be replaced according to the following table: - *

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
VariableContent
{nms}Actual package name of net.minecraft.server.VERSION
{obc}Actual pacakge name of org.bukkit.craftbukkit.VERSION
{version}The current Minecraft package VERSION, if any.
- * - * @param lookupName - the class name with variables. - * @param aliases - alternative names for this class. - * @return Class object. - * @throws RuntimeException If we are unable to find any of the given classes. - */ - public static Class getClass(String lookupName, String... aliases) { - try { - // Try the main class first - return getClass(lookupName); - } catch (RuntimeException e) { - Class success = null; - - // Try every alias too - for (String alias : aliases) { - try { - success = getClass(alias); - break; - } catch (RuntimeException e1) { - // e1.printStackTrace(); - } - } - - if (success != null) { - return success; - } else { - // Hack failed - throw new RuntimeException(String.format("Unable to find %s (%s)", lookupName, String.join(",", aliases))); - } - } - } - - /** - * Retrieve a class in the net.minecraft.server.VERSION.* package. - * - * @param name - the name of the class, excluding the package. - * @throws IllegalArgumentException If the class doesn't exist. - */ - public static Class getMinecraftClass(String name) { - return getCanonicalClass(NMS_PREFIX + "." + name); - } - - /** - * Retrieve a class in the org.bukkit.craftbukkit.VERSION.* package. - * - * @param name - the name of the class, excluding the package. - * @throws IllegalArgumentException If the class doesn't exist. - */ - public static Class getCraftBukkitClass(String name) { - return getCanonicalClass(OBC_PREFIX + "." + name); - } - - /** - * Retrieve a class by its canonical name. - * - * @param canonicalName - the canonical name. - * @return The class. - */ - private static Class getCanonicalClass(String canonicalName) { - try { - return Class.forName(canonicalName); - } catch (ClassNotFoundException e) { - throw new IllegalArgumentException("Cannot find " + canonicalName, e); - } - } - - /** - * Expand variables such as "{nms}" and "{obc}" to their corresponding packages. - * - * @param name - the full name of the class. - * @return The expanded string. - */ - private static String expandVariables(String name) { - StringBuffer output = new StringBuffer(); - Matcher matcher = MATCH_VARIABLE.matcher(name); - - while (matcher.find()) { - String variable = matcher.group(1); - String replacement = ""; - - // Expand all detected variables - if ("nms".equalsIgnoreCase(variable)) - replacement = NMS_PREFIX; - else if ("obc".equalsIgnoreCase(variable)) - replacement = OBC_PREFIX; - else if ("version".equalsIgnoreCase(variable)) - replacement = VERSION; - else - throw new IllegalArgumentException("Unknown variable: " + variable); - - // Assume the expanded variables are all packages, and append a dot - if (replacement.length() > 0 && matcher.end() < name.length() && name.charAt(matcher.end()) != '.') - replacement += "."; - matcher.appendReplacement(output, Matcher.quoteReplacement(replacement)); - } - - matcher.appendTail(output); - return output.toString(); - } -} \ No newline at end of file From bede8caa82dd8d92c45a40bbb87f4a804406fa56 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Thu, 21 May 2026 20:20:18 +0200 Subject: [PATCH 38/52] Add chunk skipping optimisation --- .../steamwar/fightsystem/utils/TechHiderWrapper.java | 10 ++++++++++ .../src/de/steamwar/techhider/TechHider.java | 8 +++++++- 2 files changed, 17 insertions(+), 1 deletion(-) 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 193f851b..c840d84f 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -22,6 +22,7 @@ package de.steamwar.fightsystem.utils; import de.steamwar.Reflection; import de.steamwar.core.CraftbukkitWrapper; import de.steamwar.fightsystem.Config; +import de.steamwar.fightsystem.FightSystem; import de.steamwar.fightsystem.events.BoardingEvent; import de.steamwar.fightsystem.events.TeamLeaveEvent; import de.steamwar.fightsystem.events.TeamSpawnEvent; @@ -107,6 +108,11 @@ public class TechHiderWrapper extends StateDependent implements Listener { Region hiddenRegion = getHiddenRegion(p); return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type); } + + @Override + public boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ) { + return getHiddenRegions().stream().allMatch(region -> region.chunkOutside(chunkX, chunkZ)); + } }; new StateDependentListener(ENABLED, FightState.Schem, this); @@ -158,6 +164,10 @@ public class TechHiderWrapper extends StateDependent implements Listener { }); } + private Set getHiddenRegions() { + return Fight.teams().stream().map(FightTeam::getExtendRegion).collect(Collectors.toSet()); + } + private Region getHiddenRegion(Player player) { if (Config.isReferee(player)) return Region.EMPTY; diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 4d848379..c68878db 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -536,7 +536,12 @@ public abstract class TechHider { } private ClientboundLevelChunkWithLightPacket processChunkWithLight(Player p, ClientboundLevelChunkWithLightPacket packet) { - return chunkHider.processLevelChunkWithLightPacket(p, packet); + if(isEveryonePrivilegedToAccessAllDataWithinChunk(packet.getX(), packet.getZ())) { + return packet; + } + else { + return chunkHider.processLevelChunkWithLightPacket(p, packet); + } } private Packet proccessPositionBasedPacket(Player player, int blockX, int blockY, int blockZ, Packet packet) { @@ -562,4 +567,5 @@ public abstract class TechHider { public abstract boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block); public abstract boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId); public abstract boolean isPlayerPrivilegedToAccessBlocEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type); + public abstract boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ); } From 9240bcfd5f43b497d0b08477321e5763d5efdc98 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 21 May 2026 20:57:59 +0200 Subject: [PATCH 39/52] Add blockUsedForObfuscation --- .../SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java index 01a0fdfe..2c9bc074 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java @@ -56,10 +56,10 @@ public abstract class ChunkHider { private final byte BITS_PER_LONG = 64; - private final int blockIdUsedForHiding = 315; + private final int blockIdUsedForHiding; public ChunkHider(Block blockUsedForObfuscation) { - // blockIdUsedForHiding = Block.BLOCK_STATE_REGISTRY.getId(blockUsedForObfuscation.defaultBlockState()); + blockIdUsedForHiding = Block.BLOCK_STATE_REGISTRY.getId(blockUsedForObfuscation.defaultBlockState()); } private int getLongsRequiredToEncodeEntries(int bitsPerEntry, int entryCount) { From 29d0c8978c1ad518cb3cc0be6bb2cff9017ae276 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Thu, 21 May 2026 22:00:15 +0200 Subject: [PATCH 40/52] Rmeove old player join quickfix --- .../listener/PlayerJoinListener.java | 74 ------------------- 1 file changed, 74 deletions(-) delete mode 100644 FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PlayerJoinListener.java diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PlayerJoinListener.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PlayerJoinListener.java deleted file mode 100644 index 71ecf2a7..00000000 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PlayerJoinListener.java +++ /dev/null @@ -1,74 +0,0 @@ -package de.steamwar.fightsystem.listener; - -import de.steamwar.fightsystem.states.FightState; -import de.steamwar.fightsystem.states.StateDependentListener; -import de.steamwar.linkage.Linked; -import net.minecraft.network.protocol.game.ClientboundForgetLevelChunkPacket; -import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.chunk.LevelChunk; -import org.bukkit.Bukkit; -import org.bukkit.Chunk; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.craftbukkit.CraftWorld; -import org.bukkit.craftbukkit.entity.CraftPlayer; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; - -@Linked -public class PlayerJoinListener implements Listener { - - public PlayerJoinListener() { - new StateDependentListener(true, FightState.All, this); - } - - @EventHandler() - public void onPlayerJoin(PlayerJoinEvent event) { - Player player = event.getPlayer(); - World world = player.getWorld(); - - Location loc = player.getLocation(); - int viewDistance = Bukkit.getViewDistance(); - - int chunkX = loc.getChunk().getX(); - int chunkZ = loc.getChunk().getZ(); - - // Iterate through the chunks around the player and force a resend - for (int x = -viewDistance; x <= viewDistance; x++) { - for (int z = -viewDistance; z <= viewDistance; z++) { - Chunk chunk = world.getChunkAt(chunkX + x, chunkZ + z); - this.forceRefreshChunkForPlayer(player, chunk); - } - } - } - - public void forceRefreshChunkForPlayer(Player player, Chunk bukkitChunk) { - ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); - CraftWorld craftWorld = (CraftWorld) bukkitChunk.getWorld(); - - int chunkX = bukkitChunk.getX(); - int chunkZ = bukkitChunk.getZ(); - - LevelChunk nmsChunk = craftWorld.getHandle().getChunkSource().getChunk(chunkX, chunkZ, false); - if (nmsChunk == null) { - // Chunk isn't loaded in memory on the server side; - return; - } - - ClientboundForgetLevelChunkPacket unloadPacket = new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ)); - serverPlayer.connection.send(unloadPacket); - - ClientboundLevelChunkWithLightPacket loadPacket = new ClientboundLevelChunkWithLightPacket( - nmsChunk, - craftWorld.getHandle().getLightEngine(), - null, - null, - true - ); - serverPlayer.connection.send(loadPacket); - } -} From 21f6394359b1f57be14310543f2b2e85e7967ca4 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Thu, 21 May 2026 22:20:40 +0200 Subject: [PATCH 41/52] Fix section packet being unnececarily reconstructed --- .../SpigotCore_Main/src/de/steamwar/techhider/TechHider.java | 1 - 1 file changed, 1 deletion(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index c68878db..24cc88b6 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -491,7 +491,6 @@ public abstract class TechHider { if (isPlayerPrivilegedToAccessPosition(p, worldX, worldY, worldZ) && isPlayerPrivilegedToAccessBlock(p, worldX, worldY, worldZ, block)) { // TODO statefull !!! - modified = true; filteredPos.add(posShort); filteredStates.add(state); } else if(isPlayerPrivilegedToAccessPosition(p, worldX, worldY, worldZ)){ From 72b23ad11643e3715cda529c0bb9ebb30861b062 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Fri, 22 May 2026 16:24:34 +0200 Subject: [PATCH 42/52] Add filling off Hull for reset and paste with either visible blocks or occluding blocks --- .../fightsystem/fight/FightSchematic.java | 2 ++ .../steamwar/fightsystem/fight/FightWorld.java | 13 +++++++++---- .../src/de/steamwar/fightsystem/utils/Hull.java | 9 +++++++++ .../steamwar/fightsystem/utils/HullHider.java | 17 ++++------------- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightSchematic.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightSchematic.java index 790a1cec..c8174349 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightSchematic.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightSchematic.java @@ -158,6 +158,8 @@ public class FightSchematic extends StateDependent { FreezeWorld freezer = new FreezeWorld(); team.teleportToSpawn(); + // TODO: Implement hull generation based on clipboard content! + FightSystem.getHullHider().fill(team, false); Vector dims = WorldeditWrapper.impl.getDimensions(clipboard); WorldeditWrapper.impl.pasteClipboard( clipboard, diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightWorld.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightWorld.java index 5663fe33..83c15170 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightWorld.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightWorld.java @@ -68,6 +68,13 @@ public class FightWorld extends StateDependent { } public static void resetWorld() { + World backup = new WorldCreator(Config.world.getName() + "/backup").createWorld(); + assert backup != null; + Config.ArenaRegion.forEachChunk((x, z) -> { + CraftbukkitWrapper.impl.resetChunk(Config.world, backup, x, z); + }); + Bukkit.unloadWorld(backup, false); + List entities = new ArrayList<>(); Recording.iterateOverEntities(Objects::nonNull, entity -> { if (entity.getType() != EntityType.PLAYER && (!Config.GameModeConfig.Arena.Leaveable || Config.ArenaRegion.inRegion(entity.getLocation()))) { @@ -77,14 +84,12 @@ public class FightWorld extends StateDependent { entities.forEach(Entity::remove); entities.clear(); - World backup = new WorldCreator(Config.world.getName() + "/backup").createWorld(); - assert backup != null; + FightSystem.getHullHider().getHullMap().values().forEach(hull -> hull.fill(true)); + Config.ArenaRegion.forEachChunk((x, z) -> { - CraftbukkitWrapper.impl.resetChunk(Config.world, backup, x, z); for (Player p : Bukkit.getOnlinePlayers()) { de.steamwar.core.CraftbukkitWrapper.sendChunk(p, x, z); } }); - Bukkit.unloadWorld(backup, false); } } \ No newline at end of file diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Hull.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Hull.java index 72af266e..3ac2528d 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Hull.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Hull.java @@ -165,6 +165,15 @@ public class Hull { rentities.remove(entity); } + public void fill(boolean visible) { + visibility.set(0, visibility.size(), visible); + occluding.set(0, occluding.size(), !visible); + uncoveredSurface.clear(); + for (BitSet directionalVisibility : visibilityDirections.values()) { + directionalVisibility.set(0, directionalVisibility.size(), visible); + } + } + public void initialize() { visibility.clear(); occluding.clear(); diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java index 3b4555c6..58bfce48 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java @@ -19,8 +19,6 @@ package de.steamwar.fightsystem.utils; -import com.comphenix.tinyprotocol.TinyProtocol; -import de.steamwar.Reflection; import de.steamwar.entity.REntity; import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.fight.Fight; @@ -28,17 +26,9 @@ import de.steamwar.fightsystem.fight.FightPlayer; import de.steamwar.fightsystem.fight.FightTeam; import de.steamwar.fightsystem.listener.Recording; import de.steamwar.fightsystem.states.FightState; -import de.steamwar.fightsystem.states.StateDependent; import de.steamwar.fightsystem.states.StateDependentListener; import de.steamwar.fightsystem.states.StateDependentTask; import lombok.Getter; -import net.minecraft.core.Vec3i; -import net.minecraft.network.protocol.game.ClientboundExplodePacket; -import net.minecraft.network.protocol.game.ClientboundLevelEventPacket; -import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket; -import net.minecraft.network.protocol.game.ClientboundSoundPacket; -import org.bukkit.Bukkit; -import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.entity.Player; @@ -54,8 +44,6 @@ import org.bukkit.event.player.PlayerQuitEvent; import java.util.HashMap; import java.util.Map; import java.util.Objects; -import java.util.function.BiFunction; -import java.util.function.Function; public class HullHider implements Listener { @@ -79,10 +67,13 @@ public class HullHider implements Listener { public void initialize(FightTeam team) { if (!TechHiderWrapper.ENABLED) return; - hullMap.get(team).initialize(); } + public void fill(FightTeam team, boolean visible) { + if (!TechHiderWrapper.ENABLED) return; + hullMap.get(team).fill(visible); + } @EventHandler(priority = EventPriority.HIGH) public void onJoin(PlayerJoinEvent e) { From 7e18207b87e42b3cf406f78bac7c0a6693d63b56 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Fri, 22 May 2026 16:32:49 +0200 Subject: [PATCH 43/52] fix entities not despawning --- .../src/de/steamwar/fightsystem/utils/TechHiderWrapper.java | 5 +++-- FightSystem/build.gradle.kts | 2 +- .../src/de/steamwar/techhider/TechHider.java | 6 ++---- 3 files changed, 6 insertions(+), 7 deletions(-) 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 193f851b..fb2dc750 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -59,7 +59,7 @@ public class TechHiderWrapper extends StateDependent implements Listener { public TechHiderWrapper(HullHider hullHider) { - super(ENABLED, FightState.Schem); + super(ENABLED, FightState.All); Set blocksToObfuscate = Config.GameModeConfig.Techhider.HiddenBlocks.stream() .map(CraftMagicNumbers::getBlock) .collect(Collectors.toUnmodifiableSet()); @@ -90,6 +90,7 @@ public class TechHiderWrapper extends StateDependent implements Listener { return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blocksToObfuscate.contains(block); } + // TODO will require entity tracking on the netty thread to prevent future race conditions @Override public boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId) { net.minecraft.world.entity.Entity nmsEntity = ((CraftWorld) p.getWorld()).getHandle().moonrise$getEntityLookup().get(entityId); @@ -98,7 +99,7 @@ public class TechHiderWrapper extends StateDependent implements Listener { return !hullHider.isBlockHidden(p, nmsEntity.getBlockX(), nmsEntity.getBlockY(), nmsEntity.getBlockZ()); } else { - return false; + return true; } } diff --git a/FightSystem/build.gradle.kts b/FightSystem/build.gradle.kts index a9a84cbd..c6b14b8b 100644 --- a/FightSystem/build.gradle.kts +++ b/FightSystem/build.gradle.kts @@ -60,7 +60,7 @@ tasks.register("WarGear21") { dependsOn(":KotlinCore:shadowJar") template = "WarGear21" worldName = "arenas/Pentraki" - config = "WarGear20.yml" + config = "WarGear21.yml" jar = "/jars/paper-1.21.6.jar" } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 4d848379..7d92284f 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -190,6 +190,8 @@ public abstract class TechHider { 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 @@ -274,10 +276,6 @@ public abstract class TechHider { // 7.1.47 Particle: particle type and position can reveal hidden machinery. processors.put(ClientboundLevelParticlesPacket.class, (p, packet) -> processLevelParticlesPacket(p, (ClientboundLevelParticlesPacket) packet)); - // --- Lighting packets --- - // 7.1.48 Update Light: lighting can reveal hidden blocks/operations. - processors.put(ClientboundLightUpdatePacket.class, tossPacket); - // --- Sound packets --- // 7.1.115 Entity Sound Effect: entity id and sound. processors.put(ClientboundSoundEntityPacket.class, this.buildEntityPacketProcessor(ClientboundSoundEntityPacket::getId)); From 7d74eb0c099c8316b4dd1ee20a3a43d9599fab84 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Fri, 22 May 2026 17:40:33 +0200 Subject: [PATCH 44/52] add old techhider to skip supporting bausystem for now --- .../features/techhider/TechHiderCommand.java | 1 + .../features/world/SpectatorListener.java | 1 + .../bausystem/features/xray/XrayCommand.java | 1 + .../steamwar/techhider/legacy/BlockIds.java | 68 ++++ .../steamwar/techhider/legacy/ChunkHider.java | 317 ++++++++++++++++++ .../techhider/legacy/ProtocolUtils.java | 203 +++++++++++ .../techhider/legacy/ProtocolWrapper.java | 95 ++++++ .../steamwar/techhider/legacy/TechHider.java | 181 ++++++++++ 8 files changed, 867 insertions(+) create mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/BlockIds.java create mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java create mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolUtils.java create mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolWrapper.java create mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java 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 4820b072..4744e41d 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 @@ -38,6 +38,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerQuitEvent; +import de.steamwar.techhider.legacy.TechHider; import java.util.*; import java.util.stream.Collectors; 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 3b02bffc..92a5c59e 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 @@ -39,6 +39,7 @@ import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.event.player.*; import org.bukkit.util.Vector; +import de.steamwar.techhider.legacy.TechHider; import java.util.HashSet; import java.util.Set; 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 1ddfe6f7..55bcbeeb 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 @@ -40,6 +40,7 @@ import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerQuitEvent; +import de.steamwar.techhider.legacy.TechHider; import java.util.*; import java.util.function.BiFunction; diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/BlockIds.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/BlockIds.java new file mode 100644 index 00000000..0d10088f --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/BlockIds.java @@ -0,0 +1,68 @@ +/* + * 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.Reflection; +import net.minecraft.core.IdMapper; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import org.bukkit.Material; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; + +import java.util.HashSet; +import java.util.Set; + +public class BlockIds { + public static final BlockIds impl = new BlockIds(); + + public int materialToId(Material material) { + return getCombinedId(getBlock(material).defaultBlockState()); + } + + private static final FluidState water = Fluids.WATER.getSource(false); + private static final Iterable registryBlockId = (Iterable) Reflection.getField(TechHider.block, IdMapper.class, 0).get(null); + + public Set materialToAllIds(Material material) { + Set ids = new HashSet<>(); + for (BlockState data : getBlock(material).getStateDefinition().getPossibleStates()) { + ids.add(getCombinedId(data)); + } + + if (material == Material.WATER) { + for (BlockState data : registryBlockId) { + if (data.getFluidState() == water) { + ids.add(getCombinedId(data)); + } + } + } + + return ids; + } + + private Block getBlock(Material material) { + return CraftMagicNumbers.getBlock(material); + } + + public int getCombinedId(BlockState blockData) { + return Block.getId(blockData); + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java new file mode 100644 index 00000000..9394d1b0 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java @@ -0,0 +1,317 @@ +/* + * 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.Reflection; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.Getter; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; +import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; +import net.minecraft.resources.ResourceLocation; +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.List; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; + +public class ChunkHider { + public static final ChunkHider impl = new ChunkHider(); + + public Class mapChunkPacket() { + return ClientboundLevelChunkWithLightPacket.class; + } + + private static final UnaryOperator chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class); + private static final UnaryOperator chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class); + + private static final Reflection.Field chunkXField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 0); + private static final Reflection.Field chunkZField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 1); + private static final Reflection.Field chunkData = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkPacketData.class, 0); + + private static final Reflection.Field dataField = Reflection.getField(ClientboundLevelChunkPacketData.class, byte[].class, 0); + private static final Reflection.Field tileEntities = Reflection.getField(ClientboundLevelChunkPacketData.class, List.class, 0); + + public BiFunction chunkHiderGenerator(TechHider techHider) { + return (p, packet) -> { + int chunkX = chunkXField.get(packet); + int chunkZ = chunkZField.get(packet); + if (techHider.getLocationEvaluator().skipChunk(p, chunkX, chunkZ)) { + return packet; + } + + packet = chunkPacketCloner.apply(packet); + Object dataWrapper = chunkDataCloner.apply(chunkData.get(packet)); + + Set hiddenBlockEntities = techHider.getHiddenBlockEntities(); + tileEntities.set(dataWrapper, ((List) tileEntities.get(dataWrapper)).stream().filter(te -> tileEntityVisible(hiddenBlockEntities, te)).collect(Collectors.toList())); + + ByteBuf in = Unpooled.wrappedBuffer(dataField.get(dataWrapper)); + 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); + dataField.set(dataWrapper, data); + + chunkData.set(packet, dataWrapper); + return packet; + }; + } + + private static final Registry> registry = Reflection.getField(BuiltInRegistries.class, "BLOCK_ENTITY_TYPE", Registry.class).get(null); + private static final Reflection.Method getKey = Reflection.getTypedMethod(Reflection.getClass("net.minecraft.core.Registry"), "getKey", ResourceLocation.class, Object.class); + public static final Class tileEntity = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$BlockEntityInfo"); + protected static final Reflection.Field entityType = Reflection.getField(tileEntity, BlockEntityType.class, 0); + + protected boolean tileEntityVisible(Set hiddenBlockEntities, Object tile) { + BlockEntityType type = entityType.get(tile); + String path = ((ResourceLocation) getKey.invoke(registry, 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/ProtocolUtils.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolUtils.java new file mode 100644 index 00000000..5e9a7c78 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolUtils.java @@ -0,0 +1,203 @@ +/* + * 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.google.common.primitives.Bytes; +import de.steamwar.Reflection; +import io.netty.buffer.ByteBuf; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.UnaryOperator; + +public class ProtocolUtils { + private ProtocolUtils() { + } + + @Deprecated + public static BiFunction, Object> arrayCloneGenerator(Class elementClass) { + return (array, worker) -> { + int length = Array.getLength(array); + Object result = Array.newInstance(elementClass, length); + + for (int i = 0; i < length; i++) { + Array.set(result, i, worker.apply(Array.get(array, i))); + } + + return result; + }; + } + + public static UnaryOperator shallowCloneGenerator(Class clazz) { + BiConsumer filler = shallowFill(clazz); + + return source -> { + Object clone = Reflection.newInstance(clazz); + filler.accept(source, clone); + return clone; + }; + } + + public static UnaryOperator shallowTypedCloneGenerator(Class clazz) { + BiConsumer filler = shallowFill(clazz); + + return source -> { + Object clone = Reflection.newInstance(clazz); + filler.accept(source, clone); + return (T) clone; + }; + } + + private static BiConsumer shallowFill(Class clazz) { + if (clazz == null) { + return (source, clone) -> { + }; + } + + BiConsumer superFiller = shallowFill(clazz.getSuperclass()); + + Field[] fds = clazz.getDeclaredFields(); + List fields = new ArrayList<>(); + for (Field field : fds) { + if (Modifier.isStatic(field.getModifiers())) continue; + + field.setAccessible(true); + fields.add(field); + } + + return (source, clone) -> { + superFiller.accept(source, clone); + try { + for (Field field : fields) { + field.set(clone, field.get(source)); + } + } catch (IllegalAccessException e) { + throw new IllegalStateException("Could not set field", e); + } + + }; + } + + public static int posToChunk(int c) { + int chunk = c / 16; + if (c < 0) chunk--; + return chunk; + } + + @Deprecated + public static int readVarInt(byte[] array, int startPos) { + int numRead = 0; + int result = 0; + byte read; + do { + read = array[startPos + numRead]; + int value = (read & 0b01111111); + result |= (value << (7 * numRead)); + + numRead++; + if (numRead > 5) { + break; + } + } while ((read & 0b10000000) != 0); + + return result; + } + + public static int readVarInt(ByteBuf buf) { + int numRead = 0; + int result = 0; + byte read; + do { + read = buf.readByte(); + int value = (read & 0b01111111); + result |= (value << (7 * numRead)); + + if (++numRead > 5) throw new SecurityException("VarInt too long"); + } while ((read & 0b10000000) != 0); + + return result; + } + + public static void writeVarInt(ByteBuf buf, int value) { + do { + int temp = value & 0b01111111; + // Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone + value >>>= 7; + if (value != 0) { + temp |= 0b10000000; + } + buf.writeByte(temp); + } while (value != 0); + } + + @Deprecated + public static int readVarIntLength(byte[] array, int startPos) { + int numRead = 0; + byte read; + do { + read = array[startPos + numRead]; + numRead++; + if (numRead > 5) { + break; + } + } while ((read & 0b10000000) != 0); + + return numRead; + } + + @Deprecated + public static byte[] writeVarInt(int value) { + List buffer = new ArrayList<>(5); + do { + byte temp = (byte) (value & 0b01111111); + // Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone + value >>>= 7; + if (value != 0) { + temp |= 0b10000000; + } + buffer.add(temp); + } while (value != 0); + return Bytes.toArray(buffer); + } + + @Deprecated + public static class ChunkPos { + final int x; + final int z; + + public ChunkPos(int x, int z) { + this.x = x; + this.z = z; + } + + public final int x() { + return x; + } + + public final int z() { + return z; + } + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolWrapper.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolWrapper.java new file mode 100644 index 00000000..9b6ac216 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolWrapper.java @@ -0,0 +1,95 @@ +/* + * 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.Reflection; +import net.minecraft.core.SectionPos; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.entity.SignBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.function.BiFunction; + +public class ProtocolWrapper { + public static final ProtocolWrapper impl = new ProtocolWrapper(); + + private static final Reflection.Field multiBlockChangeChunk = Reflection.getField(TechHider.multiBlockChangePacket, SectionPos.class, 0); + private static final Reflection.Field multiBlockChangePos = Reflection.getField(TechHider.multiBlockChangePacket, short[].class, 0); + private static final Reflection.Field multiBlockChangeBlocks = Reflection.getField(TechHider.multiBlockChangePacket, BlockState[].class, 0); + + public BiFunction multiBlockChangeGenerator(TechHider techHider) { + return (p, packet) -> { + TechHider.LocationEvaluator locationEvaluator = techHider.getLocationEvaluator(); + Object chunkCoords = multiBlockChangeChunk.get(packet); + int chunkX = TechHider.blockPositionX.get(chunkCoords); + int chunkY = TechHider.blockPositionY.get(chunkCoords); + int chunkZ = TechHider.blockPositionZ.get(chunkCoords); + if (locationEvaluator.skipChunkSection(p, chunkX, chunkY, chunkZ)) { + return packet; + } + + packet = TechHider.multiBlockChangeCloner.apply(packet); + final short[] oldPos = multiBlockChangePos.get(packet); + final BlockState[] oldBlocks = multiBlockChangeBlocks.get(packet); + 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); + } + + multiBlockChangePos.set(packet, newPos); + multiBlockChangeBlocks.set(packet, blocks.toArray(new BlockState[0])); + return packet; + }; + } + + private static final Reflection.Field tileEntityType = Reflection.getField(TechHider.tileEntityDataPacket, BlockEntityType.class, 0); + private static final BlockEntityType signType = Reflection.getField(BlockEntityType.class, BlockEntityType.class, 0, SignBlockEntity.class).get(null); + + public boolean unfilteredTileEntityDataAction(Object packet) { + return tileEntityType.get(packet) != signType; + } + + public BiFunction blockBreakHiderGenerator(Class blockBreakPacket, TechHider techHider) { + return null; + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java new file mode 100644 index 00000000..006b50d0 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java @@ -0,0 +1,181 @@ +/* + * 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.Reflection; +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.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.Function; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; + +public class TechHider { + + public static final Class blockPosition = BlockPos.class; + private static final Class baseBlockPosition = Vec3i.class; + public static final Reflection.Field blockPositionX = Reflection.getField(baseBlockPosition, int.class, 0); + public static final Reflection.Field blockPositionY = Reflection.getField(baseBlockPosition, int.class, 1); + public static final Reflection.Field blockPositionZ = Reflection.getField(baseBlockPosition, int.class, 2); + + 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(blockActionPacket, this::blockActionHider); + techhiders.put(blockChangePacket, this::blockChangeHider); + techhiders.put(tileEntityDataPacket, this::tileEntityDataHider); + techhiders.put(multiBlockChangePacket, ProtocolWrapper.impl.multiBlockChangeGenerator(this)); + techhiders.put(ChunkHider.impl.mapChunkPacket(), 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(TinyProtocol.instance::addFilter); + } + + public void disable() { + techhiders.forEach(TinyProtocol.instance::removeFilter); + } + + public static final Class multiBlockChangePacket = ClientboundSectionBlocksUpdatePacket.class; + public static final UnaryOperator multiBlockChangeCloner = ProtocolUtils.shallowCloneGenerator(TechHider.multiBlockChangePacket); + + private static final Class blockChangePacket = ClientboundBlockUpdatePacket.class; + private static final Function blockChangeCloner = ProtocolUtils.shallowCloneGenerator(blockChangePacket); + private static final Reflection.Field blockChangePosition = Reflection.getField(blockChangePacket, blockPosition, 0); + private static final Reflection.Field 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((BlockState) blockChangeBlockData.get(packet))) { + return packet; + } + case HIDE: + packet = blockChangeCloner.apply(packet); + blockChangeBlockData.set(packet, obfuscationTarget); + return packet; + case HIDE_AIR: + default: + packet = blockChangeCloner.apply(packet); + blockChangeBlockData.set(packet, AIR); + return packet; + } + } + + private static final Class blockActionPacket = ClientboundBlockEventPacket.class; + private static final Reflection.Field 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 = ClientboundBlockEntityDataPacket.class; + private static final Reflection.Field 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; + } + } + + 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, 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; + } + } +} From d682e3515902ec0fe96f9b50100ad5645f141002 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Fri, 22 May 2026 17:55:12 +0200 Subject: [PATCH 45/52] Resolve open comments --- .../fightsystem/utils/TechHiderWrapper.java | 2 +- .../techhider/AccessPrivilegeProvider.java | 31 +++++++++++++ .../src/de/steamwar/techhider/ChunkHider.java | 16 +++---- .../src/de/steamwar/techhider/TechHider.java | 45 ++++++++++--------- 4 files changed, 63 insertions(+), 31 deletions(-) create mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java 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 fb2dc750..2ba2decd 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -104,7 +104,7 @@ public class TechHiderWrapper extends StateDependent implements Listener { } @Override - public boolean isPlayerPrivilegedToAccessBlocEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type) { + public boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type) { Region hiddenRegion = getHiddenRegion(p); return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type); } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java new file mode 100644 index 00000000..b4525127 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java @@ -0,0 +1,31 @@ +/* + * 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.techhider; + +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntityType; +import org.bukkit.entity.Player; + +public interface AccessPrivilegeProvider { + boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ); + boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block); + boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId); + boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type); +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java index 2c9bc074..6d9cdee8 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java @@ -38,7 +38,7 @@ import java.util.*; import java.util.function.UnaryOperator; -public abstract class ChunkHider { +public class ChunkHider { private static final UnaryOperator chunkPacketShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class); private static final UnaryOperator chunkDataShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class); @@ -54,12 +54,14 @@ public abstract class ChunkHider { private final int BLOCKS_PER_SECTION = 4096; private final int BIOMES_PER_SECTION = 64; - private final byte BITS_PER_LONG = 64; + private final byte BITS_PER_LONG = Long.SIZE; private final int blockIdUsedForHiding; + private final AccessPrivilegeProvider accessPrivilegeProvider; - public ChunkHider(Block blockUsedForObfuscation) { + public ChunkHider(Block blockUsedForObfuscation, AccessPrivilegeProvider accessPrivilegeProvider) { blockIdUsedForHiding = Block.BLOCK_STATE_REGISTRY.getId(blockUsedForObfuscation.defaultBlockState()); + this.accessPrivilegeProvider = accessPrivilegeProvider; } private int getLongsRequiredToEncodeEntries(int bitsPerEntry, int entryCount) { @@ -208,7 +210,7 @@ public abstract class ChunkHider { - if(isPlayerPrivilegedToAccessPosition(player, worldX, worldY, worldZ) && isPlayerPrivilegedToAccessBlock(player, worldX, worldY, worldZ, block)) { + if(accessPrivilegeProvider.isPlayerPrivilegedToAccessPosition(player, worldX, worldY, worldZ) && accessPrivilegeProvider.isPlayerPrivilegedToAccessBlock(player, worldX, worldY, worldZ, block)) { obfuscatedData[blockDataIndex] = blockId; } else { @@ -245,10 +247,6 @@ public abstract class ChunkHider { return reEncodedData.getRaw(); } - public abstract boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ); - public abstract boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block); - public abstract boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type); - private ClientboundLevelChunkWithLightPacket buildNewChunkPacket(ClientboundLevelChunkWithLightPacket originalPacket, byte[] newBlockDataBuffer, List newBlockEntities) { ClientboundLevelChunkWithLightPacket clonedPacket = (ClientboundLevelChunkWithLightPacket) chunkPacketShallowCloner.apply(originalPacket); ClientboundLevelChunkPacketData clonedPacketChunkData = (ClientboundLevelChunkPacketData) chunkDataShallowCloner.apply(originalPacket.getChunkData()); @@ -278,7 +276,7 @@ public abstract class ChunkHider { int x = SectionPos.sectionRelativeX((short) packedXZ); int z = SectionPos.sectionRelativeZ((short) packedXZ); - return isPlayerPrivilegedToAccessPosition(player, x, y, z) && isPlayerPrivilegedToAccessBlockEntity(player, x, y, z, type); + return accessPrivilegeProvider.isPlayerPrivilegedToAccessPosition(player, x, y, z) && accessPrivilegeProvider.isPlayerPrivilegedToAccessBlockEntity(player, x, y, z, type); }).toList(); } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 7d92284f..13f1159c 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -1,3 +1,22 @@ +/* + * 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.techhider; import com.comphenix.tinyprotocol.TinyProtocol; @@ -7,7 +26,6 @@ import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.shorts.ShortArraySet; import it.unimi.dsi.fastutil.shorts.ShortSets; import net.minecraft.network.protocol.game.*; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.phys.Vec3; import org.bukkit.entity.Player; @@ -41,35 +59,20 @@ import java.util.function.ToIntFunction; * any packet must be explicitly whitelisted or processed in a * way that is also compliant with the principle of default-deny. */ -public abstract class TechHider { +public abstract class TechHider implements AccessPrivilegeProvider { private final Set> bypassingPackets; private final Map>, BiFunction, Packet>> packetProcessors; private final Block blockUsedForObfuscation; private final BlockState blockStateUsedForObfuscation; - private ChunkHider chunkHider; + private final ChunkHider chunkHider; // TODO handle packet bundle public TechHider(Block blockUsedForObfuscation) { this.blockUsedForObfuscation = blockUsedForObfuscation; this.blockStateUsedForObfuscation = blockUsedForObfuscation.defaultBlockState(); - this.chunkHider = new ChunkHider(blockUsedForObfuscation) { - @Override - public boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block) { - return TechHider.this.isPlayerPrivilegedToAccessBlock(p, blockX, blockY, blockZ, block); - } - - @Override - public boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type) { - return TechHider.this.isPlayerPrivilegedToAccessBlocEntity(p, blockX, blockY, blockZ, type); - } - - @Override - public boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ) { - return TechHider.this.isPlayerPrivilegedToAccessPosition(p, blockX, blockY, blockZ); - } - }; + this.chunkHider = new ChunkHider(blockUsedForObfuscation, this); this.bypassingPackets = new HashSet<>(List.of( // --- 5.1.x Login Protocol --- @@ -447,7 +450,7 @@ public abstract class TechHider { int blockY = blockPos.getY(); int blockZ = blockPos.getZ(); - if (isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && isPlayerPrivilegedToAccessBlocEntity(player, blockX, blockY, blockZ, blockEntityType)) { + if (isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && isPlayerPrivilegedToAccessBlockEntity(player, blockX, blockY, blockZ, blockEntityType)) { return packet; } else { return null; @@ -559,5 +562,5 @@ public abstract class TechHider { public abstract boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ); public abstract boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block); public abstract boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId); - public abstract boolean isPlayerPrivilegedToAccessBlocEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type); + public abstract boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type); } From ebc10c1ce45ed51a877b699a03d5ac36a8123aab Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Fri, 22 May 2026 17:57:15 +0200 Subject: [PATCH 46/52] Fix constructor visiblity --- .../SpigotCore_Main/src/de/steamwar/techhider/TechHider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 13f1159c..3511c62d 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -68,7 +68,7 @@ public abstract class TechHider implements AccessPrivilegeProvider { private final ChunkHider chunkHider; // TODO handle packet bundle - public TechHider(Block blockUsedForObfuscation) { + protected TechHider(Block blockUsedForObfuscation) { this.blockUsedForObfuscation = blockUsedForObfuscation; this.blockStateUsedForObfuscation = blockUsedForObfuscation.defaultBlockState(); From e11f3f7cbcf9fe725503aea1affbf4a47e6a80e3 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Fri, 22 May 2026 18:27:53 +0200 Subject: [PATCH 47/52] Fix preschem state and techider not being always active --- .../src/de/steamwar/fightsystem/FightSystem.java | 1 + .../src/de/steamwar/fightsystem/utils/TechHiderWrapper.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java index be822c7c..d3339975 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java @@ -110,6 +110,7 @@ public class FightSystem extends JavaPlugin { hullHider = new HullHider(); techHider = new TechHiderWrapper(hullHider); + FightSystem.getHullHider().getHullMap().values().forEach(hull -> hull.fill(true)); FileSource.startReplay(); 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 2ba2decd..0bb2236e 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -110,7 +110,7 @@ public class TechHiderWrapper extends StateDependent implements Listener { } }; - new StateDependentListener(ENABLED, FightState.Schem, this); + new StateDependentListener(ENABLED, FightState.All, this); register(); } From 54fa47bd998e50fa060b4381d6af8dca6975cf91 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Fri, 22 May 2026 19:39:57 +0200 Subject: [PATCH 48/52] Ensure parity with old techhider by suppressing select packets --- .../src/de/steamwar/fightsystem/utils/TechHiderWrapper.java | 6 ++++++ .../src/de/steamwar/techhider/AccessPrivilegeProvider.java | 1 + .../src/de/steamwar/techhider/TechHider.java | 4 ++++ 3 files changed, 11 insertions(+) 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 0bb2236e..4c7e96a1 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -37,6 +37,7 @@ import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; +import org.bukkit.GameMode; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.util.CraftMagicNumbers; import org.bukkit.entity.Player; @@ -108,6 +109,11 @@ public class TechHiderWrapper extends StateDependent implements Listener { Region hiddenRegion = getHiddenRegion(p); return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type); } + + @Override + public boolean isPlayerPrivalegedToPerformAction(Player p) { + return !(p.getGameMode() == GameMode.SPECTATOR); + } }; new StateDependentListener(ENABLED, FightState.All, this); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java index b4525127..1b34fe65 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java @@ -28,4 +28,5 @@ public interface AccessPrivilegeProvider { boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block); boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId); boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type); + boolean isPlayerPrivalegedToPerformAction(Player p); } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 3511c62d..fdd242cd 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -331,6 +331,9 @@ public abstract class TechHider implements AccessPrivilegeProvider { return null; } }); + + TinyProtocol.instance.addFilter(ServerboundUseItemOnPacket.class, (p, packet) -> isPlayerPrivalegedToPerformAction(p) ? packet : null); + TinyProtocol.instance.addFilter(ServerboundInteractPacket.class, (p, packet) -> isPlayerPrivalegedToPerformAction(p) ? packet : null); } private Packet processPacket(Player player, Packet packet) { @@ -563,4 +566,5 @@ public abstract class TechHider implements AccessPrivilegeProvider { public abstract boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block); public abstract boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId); public abstract boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type); + public abstract boolean isPlayerPrivalegedToPerformAction(Player p); } From e55ca911c4a8270051634b838f1c79f7732f00e1 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Fri, 22 May 2026 20:41:05 +0200 Subject: [PATCH 49/52] Inline get all regions --- .../de/steamwar/fightsystem/utils/TechHiderWrapper.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) 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 8e0f7f04..5f8926eb 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -113,7 +113,8 @@ public class TechHiderWrapper extends StateDependent implements Listener { @Override public boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ) { - return getHiddenRegions().stream().allMatch(region -> region.chunkOutside(chunkX, chunkZ)); + Set hiddenRegions = Fight.teams().stream().map(FightTeam::getExtendRegion).collect(Collectors.toSet()); + return hiddenRegions.stream().allMatch(region -> region.chunkOutside(chunkX, chunkZ)); } @Override @@ -171,10 +172,6 @@ public class TechHiderWrapper extends StateDependent implements Listener { }); } - private Set getHiddenRegions() { - return Fight.teams().stream().map(FightTeam::getExtendRegion).collect(Collectors.toSet()); - } - private Region getHiddenRegion(Player player) { if (Config.isReferee(player)) return Region.EMPTY; From 1810cb7546d949b1f52bb430ce97228f15e7f001 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Fri, 22 May 2026 20:51:24 +0200 Subject: [PATCH 50/52] Fix inlineing --- .../src/de/steamwar/fightsystem/utils/TechHiderWrapper.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 5f8926eb..bbacf038 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -113,11 +113,10 @@ public class TechHiderWrapper extends StateDependent implements Listener { @Override public boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ) { - Set hiddenRegions = Fight.teams().stream().map(FightTeam::getExtendRegion).collect(Collectors.toSet()); - return hiddenRegions.stream().allMatch(region -> region.chunkOutside(chunkX, chunkZ)); + return Fight.teams().stream().map(FightTeam::getExtendRegion).allMatch(region -> region.chunkOutside(chunkX, chunkZ)); } - @Override + @Override public boolean isPlayerPrivalegedToPerformAction(Player p) { return !(p.getGameMode() == GameMode.SPECTATOR); } From 4f9fe07951545d71714c59d7cc34df2f98137cf5 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Fri, 22 May 2026 21:29:58 +0200 Subject: [PATCH 51/52] Fix final formatting stuff --- .../fightsystem/listener/Recording.java | 3 +- .../fightsystem/utils/TechHiderWrapper.java | 38 ++-- .../comphenix/tinyprotocol/TinyProtocol.java | 8 +- .../techhider/AccessPrivilegeProvider.java | 8 +- .../src/de/steamwar/techhider/BlockIds.java | 5 - .../src/de/steamwar/techhider/ChunkHider.java | 72 +++---- .../de/steamwar/techhider/ProtocolUtils.java | 6 +- .../steamwar/techhider/ProtocolWrapper.java | 34 --- .../src/de/steamwar/techhider/TechHider.java | 172 +++++++-------- .../steamwar/techhider/legacy/BlockIds.java | 68 ------ .../steamwar/techhider/legacy/ChunkHider.java | 2 + .../techhider/legacy/ProtocolUtils.java | 203 ------------------ .../techhider/legacy/ProtocolWrapper.java | 5 +- .../steamwar/techhider/legacy/TechHider.java | 5 +- 14 files changed, 144 insertions(+), 485 deletions(-) delete mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolWrapper.java delete mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/BlockIds.java delete mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolUtils.java diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java index 05bed604..24741cda 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java @@ -37,6 +37,7 @@ import de.steamwar.fightsystem.states.StateDependentListener; import de.steamwar.fightsystem.states.StateDependentTask; import de.steamwar.fightsystem.utils.SWSound; import de.steamwar.linkage.Linked; +import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; import net.minecraft.network.protocol.game.ServerboundUseItemPacket; import net.minecraft.world.InteractionHand; @@ -142,7 +143,7 @@ public class Recording implements Listener { GlobalRecorder.getInstance().entitySpeed(entity); } - private static final Class blockDigPacket = ServerboundPlayerActionPacket.class; + private static final Class> blockDigPacket = ServerboundPlayerActionPacket.class; private static final Class playerDigType = blockDigPacket.getDeclaredClasses()[0]; private static final Reflection.Field blockDigType = Reflection.getField(blockDigPacket, playerDigType, 0); private static final Object releaseUseItem = playerDigType.getEnumConstants()[5]; 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 bbacf038..091d5717 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -22,7 +22,6 @@ package de.steamwar.fightsystem.utils; import de.steamwar.Reflection; import de.steamwar.core.CraftbukkitWrapper; import de.steamwar.fightsystem.Config; -import de.steamwar.fightsystem.FightSystem; import de.steamwar.fightsystem.events.BoardingEvent; import de.steamwar.fightsystem.events.TeamLeaveEvent; import de.steamwar.fightsystem.events.TeamSpawnEvent; @@ -31,6 +30,7 @@ import de.steamwar.fightsystem.fight.FightTeam; import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.StateDependent; import de.steamwar.fightsystem.states.StateDependentListener; +import de.steamwar.techhider.AccessPrivilegeProvider; import de.steamwar.techhider.TechHider; import lombok.Getter; import net.minecraft.core.Holder; @@ -57,8 +57,6 @@ public class TechHiderWrapper extends StateDependent implements Listener { @Getter private final ConcurrentHashMap hiddenRegion = new ConcurrentHashMap<>(); - private final TechHider techHider; - public TechHiderWrapper(HullHider hullHider) { super(ENABLED, FightState.All); @@ -73,14 +71,19 @@ public class TechHiderWrapper extends StateDependent implements Listener { throw new IllegalStateException(e); } Reflection.Method method = Reflection.getTypedMethod(Reflection.getClass("net.minecraft.core.Registry"), "get", Optional.class, ResourceLocation.class); - Set> blockEntityTypeToObfuscate = Config.GameModeConfig.Techhider.HiddenBlockEntities.stream() + Set> blockEntityTypeToObfuscate = Config.GameModeConfig.Techhider.HiddenBlockEntities.stream() .map((id) -> { ResourceLocation loc = ResourceLocation.parse(id); return ((Optional>>) method.invoke(blockEntityType, loc)).get().value(); }) .collect(Collectors.toUnmodifiableSet()); - techHider = new TechHider(CraftMagicNumbers.getBlock(Config.GameModeConfig.Techhider.ObfuscateWith)) { + new TechHider(CraftMagicNumbers.getBlock(Config.GameModeConfig.Techhider.ObfuscateWith), new AccessPrivilegeProvider() { + @Override + public boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ) { + return Fight.teams().stream().map(FightTeam::getExtendRegion).allMatch(region -> region.chunkOutside(chunkX, chunkZ)); + } + @Override public boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ) { return !hullHider.isBlockHidden(p, blockX, blockY, blockZ); @@ -88,39 +91,31 @@ public class TechHiderWrapper extends StateDependent implements Listener { @Override public boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block) { - Region hiddenRegion = getHiddenRegion(p); - return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blocksToObfuscate.contains(block); + return !getHiddenRegion(p).inRegion(blockX, blockY, blockZ) || !blocksToObfuscate.contains(block); } // TODO will require entity tracking on the netty thread to prevent future race conditions @Override public boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId) { - net.minecraft.world.entity.Entity nmsEntity = ((CraftWorld) p.getWorld()).getHandle().moonrise$getEntityLookup().get(entityId); + net.minecraft.world.entity.Entity nmsEntity = ((CraftWorld) p.getWorld()).getHandle().moonrise$getEntityLookup().get(entityId); - if(nmsEntity != null) { + if (nmsEntity != null) { return !hullHider.isBlockHidden(p, nmsEntity.getBlockX(), nmsEntity.getBlockY(), nmsEntity.getBlockZ()); - } - else { + } else { return true; } } @Override public boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type) { - Region hiddenRegion = getHiddenRegion(p); - return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type); + return !getHiddenRegion(p).inRegion(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type); } @Override - public boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ) { - return Fight.teams().stream().map(FightTeam::getExtendRegion).allMatch(region -> region.chunkOutside(chunkX, chunkZ)); + public boolean isPlayerPrivilegedToPerformAction(Player p) { + return p.getGameMode() != GameMode.SPECTATOR; } - - @Override - public boolean isPlayerPrivalegedToPerformAction(Player p) { - return !(p.getGameMode() == GameMode.SPECTATOR); - } - }; + }); new StateDependentListener(ENABLED, FightState.All, this); register(); @@ -128,7 +123,6 @@ public class TechHiderWrapper extends StateDependent implements Listener { @Override public void enable() { - } @Override diff --git a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java index abcfbe26..f7df16c1 100644 --- a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java +++ b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java @@ -227,7 +227,7 @@ public class TinyProtocol { } } - public void addFilter(Class packetType, BiFunction filter) { + public > void addFilter(Class packetType, BiFunction filter) { packetFilters.computeIfAbsent(packetType, c -> new CopyOnWriteArrayList<>()).add((BiFunction) filter); } @@ -235,8 +235,8 @@ public class TinyProtocol { packetFilters.getOrDefault(packetType, Collections.emptyList()).remove(filter); } - public void addGlobalClientboundFilter(BiFunction filter) { - globalClientboundFilters.add(filter); + public void addGlobalClientboundFilter(BiFunction, Object> filter) { + globalClientboundFilters.add((BiFunction) filter); } /** @@ -453,7 +453,7 @@ public class TinyProtocol { try { msg = filterPacket(player, msg); - if(msg instanceof Packet) { + if (msg instanceof Packet) { for (BiFunction filter : globalClientboundFilters) { msg = filter.apply(player, msg); if (msg == null) break; diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java index 1b34fe65..16df8ab0 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java @@ -24,9 +24,15 @@ import net.minecraft.world.level.block.entity.BlockEntityType; import org.bukkit.entity.Player; public interface AccessPrivilegeProvider { + boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ); + boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ); + boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block); + boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId); + boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type); - boolean isPlayerPrivalegedToPerformAction(Player p); + + boolean isPlayerPrivilegedToPerformAction(Player p); } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/BlockIds.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/BlockIds.java index b623c5cf..b13295bf 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/BlockIds.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/BlockIds.java @@ -19,17 +19,12 @@ package de.steamwar.techhider; -import de.steamwar.Reflection; -import net.minecraft.core.IdMapper; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.Fluids; import org.bukkit.Material; -import org.bukkit.Registry; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Waterlogged; import org.bukkit.craftbukkit.util.CraftMagicNumbers; import java.util.HashSet; diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java index 6d9cdee8..0a280bee 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java @@ -22,9 +22,6 @@ package de.steamwar.techhider; import de.steamwar.Reflection; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import it.unimi.dsi.fastutil.ints.Int2IntMap; -import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; -import it.unimi.dsi.fastutil.ints.IntArrayList; import net.minecraft.core.SectionPos; import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; @@ -34,10 +31,9 @@ import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import org.bukkit.entity.Player; -import java.util.*; +import java.util.List; import java.util.function.UnaryOperator; - public class ChunkHider { private static final UnaryOperator chunkPacketShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class); private static final UnaryOperator chunkDataShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class); @@ -54,18 +50,16 @@ public class ChunkHider { private final int BLOCKS_PER_SECTION = 4096; private final int BIOMES_PER_SECTION = 64; - private final byte BITS_PER_LONG = Long.SIZE; - private final int blockIdUsedForHiding; private final AccessPrivilegeProvider accessPrivilegeProvider; public ChunkHider(Block blockUsedForObfuscation, AccessPrivilegeProvider accessPrivilegeProvider) { - blockIdUsedForHiding = Block.BLOCK_STATE_REGISTRY.getId(blockUsedForObfuscation.defaultBlockState()); + blockIdUsedForHiding = Block.BLOCK_STATE_REGISTRY.getId(blockUsedForObfuscation.defaultBlockState()); this.accessPrivilegeProvider = accessPrivilegeProvider; } private int getLongsRequiredToEncodeEntries(int bitsPerEntry, int entryCount) { - int entriesPerLong = BITS_PER_LONG / bitsPerEntry; + int entriesPerLong = Long.SIZE / bitsPerEntry; int dataLengthAsLongCount = (entryCount + entriesPerLong - 1) / entriesPerLong; return dataLengthAsLongCount; @@ -75,7 +69,7 @@ public class ChunkHider { int dataLengthAsLongCount = getLongsRequiredToEncodeEntries(bitsPerEntry, entryCount); long[] dataArray = new long[dataLengthAsLongCount]; - for(int i = 0; i < dataLengthAsLongCount; i++){ + for (int i = 0; i < dataLengthAsLongCount; i++) { dataArray[i] = dataSource.readLong(); } @@ -98,22 +92,21 @@ public class ChunkHider { byte bitsPerBlock = in.readByte(); - if(bitsPerBlock == 0) { + 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) { + } 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); + 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++){ + for (int i = 0; i < BLOCKS_PER_SECTION; i++) { int palletReference = data.get(i); resolvedData[i] = pallet[palletReference]; } @@ -125,7 +118,7 @@ public class ChunkHider { out.writeShort(blockCount); out.writeByte(15); - for(long rawDataSegment : reEncodedData) { + for (long rawDataSegment : reEncodedData) { out.writeLong(rawDataSegment); } /* @@ -158,9 +151,8 @@ public class ChunkHider { out.writeLong(rawDataSegment); }*/ - } - else { - long[] rawData = readSectionDataFromBuffer(in, bitsPerBlock, BLOCKS_PER_SECTION); + } else { + long[] rawData = readSectionDataFromBuffer(in, bitsPerBlock, BLOCKS_PER_SECTION); int[] blockData = resolveDirectBlockDataArray(rawData, bitsPerBlock); @@ -171,7 +163,7 @@ public class ChunkHider { out.writeShort(blockCount); out.writeByte(15); - for(long rawDataSegment : reEncodedData) { + for (long rawDataSegment : reEncodedData) { out.writeLong(rawDataSegment); } } @@ -205,15 +197,13 @@ public class ChunkHider { int worldZ = sectionZ + (SECTION_SPAN_SIZE * chunkZ); int blockId = blockDataArray[blockDataIndex]; - BlockState blockState = Block.BLOCK_STATE_REGISTRY.byId(blockId); + BlockState blockState = Block.BLOCK_STATE_REGISTRY.byId(blockId); Block block = blockState.getBlock(); - - if(accessPrivilegeProvider.isPlayerPrivilegedToAccessPosition(player, worldX, worldY, worldZ) && accessPrivilegeProvider.isPlayerPrivilegedToAccessBlock(player, worldX, worldY, worldZ, block)) { + if (accessPrivilegeProvider.isPlayerPrivilegedToAccessPosition(player, worldX, worldY, worldZ) && accessPrivilegeProvider.isPlayerPrivilegedToAccessBlock(player, worldX, worldY, worldZ, block)) { obfuscatedData[blockDataIndex] = blockId; - } - else { + } else { obfuscatedData[blockDataIndex] = blockIdUsedForHiding; } } @@ -227,7 +217,7 @@ public class ChunkHider { 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++){ + for (int i = 0; i < BLOCKS_PER_SECTION; i++) { resolvedData[i] = data.get(i); } @@ -238,7 +228,7 @@ public class ChunkHider { 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++) { + for (int i = 0; i < blockDataArray.length; i++) { int blockId = blockDataArray[i]; reEncodedData.set(i, blockId); @@ -256,7 +246,7 @@ public class ChunkHider { levelChunkPacketDataField.set(clonedPacket, clonedPacketChunkData); - return clonedPacket; + return clonedPacket; } @@ -265,6 +255,7 @@ public class ChunkHider { private static final Reflection.Field blockEntityInfoTypeField = Reflection.getField(blockEntitiyInfoClass, BlockEntityType.class, 0); private static final Reflection.Field packedXZField = Reflection.getField(blockEntitiyInfoClass, int.class, 0); private static final Reflection.Field yField = Reflection.getField(blockEntitiyInfoClass, int.class, 1); + private List filterBlockEntities(Player player, List blockEntities) { return blockEntities.stream() .filter((blockEntityInfo) -> { @@ -280,33 +271,30 @@ public class ChunkHider { }).toList(); } - private void copyOverSectionBiomeData(ByteBuf oldData, ByteBuf newData){ + private void copyOverSectionBiomeData(ByteBuf oldData, ByteBuf newData) { short bitsPerBiome = oldData.readByte(); newData.writeByte(bitsPerBiome); - if(bitsPerBiome == 0) { + if (bitsPerBiome == 0) { int sectionBiomeId = ProtocolUtils.readVarInt(oldData); ProtocolUtils.writeVarInt(newData, sectionBiomeId); - } - else if(bitsPerBiome <= BIT_PER_BIOME_INDIRECTION_LIMIT) { + } else if (bitsPerBiome <= BIT_PER_BIOME_INDIRECTION_LIMIT) { int palletLength = ProtocolUtils.readVarInt(oldData); ProtocolUtils.writeVarInt(newData, palletLength); - for(int i = 0; i < palletLength; i++) { + 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 ) { + 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); } } - else { - long[] rawData = readSectionDataFromBuffer(oldData, bitsPerBiome, BIOMES_PER_SECTION); - for(long rawDataSegment : rawData ) { - newData.writeLong(rawDataSegment); - } - } - } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java index 100dac77..e71b522a 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java @@ -143,7 +143,7 @@ public class ProtocolUtils { public static int[] readVarIntArray(ByteBuf buffer, int length) { int[] array = new int[length]; - for(int i = 0; i < length; i++) { + for (int i = 0; i < length; i++) { array[i] = readVarInt(buffer); } @@ -162,8 +162,8 @@ public class ProtocolUtils { } while (value != 0); } - public static void writeVarIntArray(ByteBuf buf, int[] values){ - for(int varInt : values) { + public static void writeVarIntArray(ByteBuf buf, int[] values) { + for (int varInt : values) { writeVarInt(buf, varInt); } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolWrapper.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolWrapper.java deleted file mode 100644 index 7699c991..00000000 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolWrapper.java +++ /dev/null @@ -1,34 +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; - -import de.steamwar.Reflection; -import net.minecraft.core.SectionPos; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.entity.SignBlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import org.bukkit.entity.Player; - -import java.util.ArrayList; -import java.util.function.BiFunction; - -public class ProtocolWrapper { - public static final ProtocolWrapper impl = new ProtocolWrapper(); -} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 00c6cd33..ed18f556 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -25,10 +25,6 @@ import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.shorts.ShortArraySet; import it.unimi.dsi.fastutil.shorts.ShortSets; -import net.minecraft.network.protocol.game.*; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.phys.Vec3; -import org.bukkit.entity.Player; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.network.PacketListener; @@ -41,13 +37,11 @@ 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.login.ClientboundCustomQueryPacket; -import net.minecraft.network.protocol.login.ClientboundHelloPacket; -import net.minecraft.network.protocol.login.ClientboundLoginCompressionPacket; -import net.minecraft.network.protocol.login.ClientboundLoginDisconnectPacket; -import net.minecraft.network.protocol.login.ClientboundLoginFinishedPacket; 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; @@ -59,20 +53,21 @@ import java.util.function.ToIntFunction; * any packet must be explicitly whitelisted or processed in a * way that is also compliant with the principle of default-deny. */ -public abstract class TechHider implements AccessPrivilegeProvider { +public class TechHider { private final Set> bypassingPackets; private final Map>, BiFunction, Packet>> packetProcessors; - private final Block blockUsedForObfuscation; private final BlockState blockStateUsedForObfuscation; private final ChunkHider chunkHider; + private final AccessPrivilegeProvider privilegeProvider; + // TODO handle packet bundle - protected TechHider(Block blockUsedForObfuscation) { - this.blockUsedForObfuscation = blockUsedForObfuscation; + public TechHider(Block blockUsedForObfuscation, AccessPrivilegeProvider privilegeProvider) { this.blockStateUsedForObfuscation = blockUsedForObfuscation.defaultBlockState(); - this.chunkHider = new ChunkHider(blockUsedForObfuscation, this); + this.chunkHider = new ChunkHider(blockUsedForObfuscation, privilegeProvider); + this.privilegeProvider = privilegeProvider; this.bypassingPackets = new HashSet<>(List.of( // --- 5.1.x Login Protocol --- @@ -84,8 +79,7 @@ public abstract class TechHider implements AccessPrivilegeProvider { ClientboundCookieRequestPacket.class, // 5.1.6 Cookie Request // --- 6.1.x Configuration Protocol --- - ClientboundSelectKnownPacks.class, - ClientboundCustomPayloadPacket.class, // 6.1.2 Clientbound Plugin Message + 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 @@ -98,7 +92,7 @@ public abstract class TechHider implements AccessPrivilegeProvider { 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 + // Payloads ClientboundServerDataPacket.class, // 6.1.20 Code of Conduct is usually in Server Data // --- 7.1.x Play Protocol --- @@ -237,7 +231,7 @@ public abstract class TechHider implements AccessPrivilegeProvider { // 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)); + 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. @@ -305,51 +299,50 @@ public abstract class TechHider implements AccessPrivilegeProvider { int blockZ = (int) pos.z; - return proccessPositionBasedPacket(p, blockX, blockY, blockZ, packet); + return proccessPositionBasedPacket(p, blockX, blockY, blockZ, packet); }); this.packetProcessors = processors; TinyProtocol.instance.addGlobalClientboundFilter((player, rawPacket) -> { - if(rawPacket instanceof ClientboundBundlePacket bundlePacket) { - List> processedPackets = new ArrayList<>(); + try { + if (rawPacket instanceof ClientboundBundlePacket bundlePacket) { + List> processedPackets = new ArrayList<>(); - for(Packet packet : bundlePacket.subPackets()) { - Packet processedPacket = (Packet) processPacket(player, packet); + for (Packet packet : bundlePacket.subPackets()) { + Packet processedPacket = (Packet) processPacket(player, packet); - if(processedPacket != null) { - processedPackets.add(processedPacket); + if (processedPacket != null) { + processedPackets.add(processedPacket); + } } - } - return new ClientboundBundlePacket(processedPackets); - } - else if(rawPacket instanceof Packet) { - return processPacket(player, (Packet) rawPacket); - } - else { + return new ClientboundBundlePacket(processedPackets); + } else { + return processPacket(player, rawPacket); + } + } catch (Throwable t) { + t.printStackTrace(); return null; } }); - TinyProtocol.instance.addFilter(ServerboundUseItemOnPacket.class, (p, packet) -> isPlayerPrivalegedToPerformAction(p) ? packet : null); - TinyProtocol.instance.addFilter(ServerboundInteractPacket.class, (p, packet) -> isPlayerPrivalegedToPerformAction(p) ? packet : 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 Packet processPacket(Player player, Packet packet) { - if(bypassingPackets.contains(packet.getClass())) { + if (bypassingPackets.contains(packet.getClass())) { return packet; - } - else if(packetProcessors.containsKey(packet.getClass())) { - return packetProcessors.get(packet.getClass()).apply(player, (Packet) packet); - } - else { + } else if (packetProcessors.containsKey(packet.getClass())) { + return packetProcessors.get(packet.getClass()).apply(player, packet); + } else { return null; } } private Packet processAddEntityPacket(Player player, ClientboundAddEntityPacket packet) { - if(isPlayerPrivilegedToAccessEntity(player, packet.getId()) && isPlayerPrivilegedToAccessPosition(player, (int) packet.getX(), (int) packet.getY(), (int) packet.getZ())) { + if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, packet.getId()) && privilegeProvider.isPlayerPrivilegedToAccessPosition(player, (int) packet.getX(), (int) packet.getY(), (int) packet.getZ())) { return packet; } else { return null; @@ -357,13 +350,14 @@ public abstract class TechHider implements AccessPrivilegeProvider { } private Packet processEntityPacket(Player player, int entityId, Packet packet) { - if(isPlayerPrivilegedToAccessEntity(player, entityId)) { + if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, entityId)) { return packet; } else { return null; } } - private > BiFunction, Packet> buildEntityPacketProcessor(ToIntFunction entityIdExtractor) { + + private > BiFunction, Packet> buildEntityPacketProcessor(ToIntFunction entityIdExtractor) { return (p, rawPacket) -> { TTargetPacket packet = (TTargetPacket) rawPacket; return processEntityPacket(p, entityIdExtractor.applyAsInt(packet), packet); @@ -371,25 +365,25 @@ public abstract class TechHider implements AccessPrivilegeProvider { } private final Reflection.Field moveEntityPacketEntityIdField = Reflection.getField(ClientboundMoveEntityPacket.class, int.class, 0); + private Packet processMoveEntityPacket(Player player, ClientboundMoveEntityPacket packet) { int entityId = moveEntityPacketEntityIdField.get(packet); - if(isPlayerPrivilegedToAccessEntity(player, entityId)) { + if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, entityId)) { return packet; - } - else { + } else { return null; } } - private final Reflection.Field rotateHeadPacketEntityIdField = Reflection.getField(ClientboundRotateHeadPacket.class, int.class, 0); + private final Reflection.Field rotateHeadPacketEntityIdField = Reflection.getField(ClientboundRotateHeadPacket.class, int.class, 0); + private Packet processRotateHeadPacket(Player player, ClientboundRotateHeadPacket packet) { int entityId = rotateHeadPacketEntityIdField.get(packet); - if(isPlayerPrivilegedToAccessEntity(player, entityId)) { + if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, entityId)) { return packet; - } - else { + } else { return null; } } @@ -397,9 +391,7 @@ public abstract class TechHider implements AccessPrivilegeProvider { private Packet processRemoveEntitiesPacket(Player player, ClientboundRemoveEntitiesPacket packet) { IntList entityIdsToRemove = packet.getEntityIds(); - IntList filteredEntitiesToRemove = entityIdsToRemove.intStream() - .filter((id) -> isPlayerPrivilegedToAccessEntity(player, id)) - .collect(IntArrayList::new, IntList::add, IntList::addAll); + IntList filteredEntitiesToRemove = entityIdsToRemove.intStream().filter((id) -> privilegeProvider.isPlayerPrivilegedToAccessEntity(player, id)).collect(IntArrayList::new, IntList::add, IntList::addAll); return new ClientboundRemoveEntitiesPacket(filteredEntitiesToRemove); } @@ -408,10 +400,9 @@ public abstract class TechHider implements AccessPrivilegeProvider { int fromEntityId = packet.getSourceId(); int toEntityId = packet.getDestId(); - if(isPlayerPrivilegedToAccessEntity(player, fromEntityId) && isPlayerPrivilegedToAccessEntity(player, toEntityId)) { + if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, fromEntityId) && privilegeProvider.isPlayerPrivilegedToAccessEntity(player, toEntityId)) { return packet; - } - else { + } else { return null; } } @@ -423,7 +414,7 @@ public abstract class TechHider implements AccessPrivilegeProvider { int blockY = blockPos.getY(); int blockZ = blockPos.getZ(); - if (isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && isPlayerPrivilegedToAccessBlock(player, blockX, blockY, blockZ, block)) { + if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && privilegeProvider.isPlayerPrivilegedToAccessBlock(player, blockX, blockY, blockZ, block)) { return packet; } else { return null; @@ -437,9 +428,9 @@ public abstract class TechHider implements AccessPrivilegeProvider { int blockY = blockPos.getY(); int blockZ = blockPos.getZ(); - if (isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && isPlayerPrivilegedToAccessBlock(player, blockX, blockY, blockZ, block)) { + if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && privilegeProvider.isPlayerPrivilegedToAccessBlock(player, blockX, blockY, blockZ, block)) { return packet; - } else if(isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) { + } else if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) { return new ClientboundBlockUpdatePacket(blockPos, blockStateUsedForObfuscation); } else { return null; @@ -453,7 +444,7 @@ public abstract class TechHider implements AccessPrivilegeProvider { int blockY = blockPos.getY(); int blockZ = blockPos.getZ(); - if (isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && isPlayerPrivilegedToAccessBlockEntity(player, blockX, blockY, blockZ, blockEntityType)) { + if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && privilegeProvider.isPlayerPrivilegedToAccessBlockEntity(player, blockX, blockY, blockZ, blockEntityType)) { return packet; } else { return null; @@ -464,10 +455,9 @@ public abstract class TechHider implements AccessPrivilegeProvider { BlockPos blockPos = packet.getPos(); int entityBreakingBlockId = packet.getId(); - if(isPlayerPrivilegedToAccessEntity(player, entityBreakingBlockId) && isPlayerPrivilegedToAccessPosition(player, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { + if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, entityBreakingBlockId) && privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { return packet; - } - else { + } else { return null; } } @@ -475,6 +465,7 @@ public abstract class TechHider implements AccessPrivilegeProvider { private final Reflection.Field sectionPosField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, SectionPos.class, 0); private final Reflection.Field oldPosField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, short[].class, 0); private final Reflection.Field oldStatesField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, BlockState[].class, 0); + private ClientboundSectionBlocksUpdatePacket processSectionUpdate(Player p, ClientboundSectionBlocksUpdatePacket packet) { SectionPos sectionPos = sectionPosField.get(packet); short[] oldPos = oldPosField.get(packet); @@ -493,11 +484,11 @@ public abstract class TechHider implements AccessPrivilegeProvider { int worldY = sectionPos.relativeToBlockY(posShort); int worldZ = sectionPos.relativeToBlockZ(posShort); - if (isPlayerPrivilegedToAccessPosition(p, worldX, worldY, worldZ) && isPlayerPrivilegedToAccessBlock(p, worldX, worldY, worldZ, block)) { + if (privilegeProvider.isPlayerPrivilegedToAccessPosition(p, worldX, worldY, worldZ) && privilegeProvider.isPlayerPrivilegedToAccessBlock(p, worldX, worldY, worldZ, block)) { // TODO statefull !!! filteredPos.add(posShort); filteredStates.add(state); - } else if(isPlayerPrivilegedToAccessPosition(p, worldX, worldY, worldZ)){ + } else if (privilegeProvider.isPlayerPrivilegedToAccessPosition(p, worldX, worldY, worldZ)) { modified = true; filteredPos.add(posShort); filteredStates.add(blockStateUsedForObfuscation); @@ -518,11 +509,7 @@ public abstract class TechHider implements AccessPrivilegeProvider { BlockState[] newStates = filteredStates.toArray(new BlockState[0]); - return new ClientboundSectionBlocksUpdatePacket( - sectionPos, - ShortSets.unmodifiable(new ShortArraySet(newPos)), - newStates - ); + return new ClientboundSectionBlocksUpdatePacket(sectionPos, ShortSets.unmodifiable(new ShortArraySet(newPos)), newStates); } private Packet processLevelParticlesPacket(Player player, ClientboundLevelParticlesPacket packet) { @@ -530,30 +517,29 @@ public abstract class TechHider implements AccessPrivilegeProvider { int blockY = (int) packet.getY(); int blockZ = (int) packet.getZ(); - if(isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) { - return packet; - } - else { - return null; - } - } - - private ClientboundLevelChunkWithLightPacket processChunkWithLight(Player p, ClientboundLevelChunkWithLightPacket packet) { - if(isEveryonePrivilegedToAccessAllDataWithinChunk(packet.getX(), packet.getZ())) { - return packet; - } - else { - return chunkHider.processLevelChunkWithLightPacket(p, packet); - } - } - - private Packet proccessPositionBasedPacket(Player player, int blockX, int blockY, int blockZ, Packet packet) { - if(isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) { + if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) { return packet; } else { return null; } } + + private ClientboundLevelChunkWithLightPacket processChunkWithLight(Player p, ClientboundLevelChunkWithLightPacket packet) { + if (privilegeProvider.isEveryonePrivilegedToAccessAllDataWithinChunk(packet.getX(), packet.getZ())) { + return packet; + } else { + return chunkHider.processLevelChunkWithLightPacket(p, packet); + } + } + + private Packet proccessPositionBasedPacket(Player player, int blockX, int blockY, int blockZ, Packet packet) { + if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) { + return packet; + } else { + return null; + } + } + private > BiFunction, Packet> buildPositionBasedPacketProcessor(Function positionExtractor) { return (p, rawPacket) -> { TTargetPacket packet = (TTargetPacket) rawPacket; @@ -565,12 +551,4 @@ public abstract class TechHider implements AccessPrivilegeProvider { return proccessPositionBasedPacket(p, blockX, blockY, blockZ, packet); }; } - - public abstract boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ); - public abstract boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block); - public abstract boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId); - public abstract boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ); - public abstract boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type); - public abstract boolean isPlayerPrivalegedToPerformAction(Player p); - } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/BlockIds.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/BlockIds.java deleted file mode 100644 index 0d10088f..00000000 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/BlockIds.java +++ /dev/null @@ -1,68 +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.Reflection; -import net.minecraft.core.IdMapper; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.FluidState; -import net.minecraft.world.level.material.Fluids; -import org.bukkit.Material; -import org.bukkit.craftbukkit.util.CraftMagicNumbers; - -import java.util.HashSet; -import java.util.Set; - -public class BlockIds { - public static final BlockIds impl = new BlockIds(); - - public int materialToId(Material material) { - return getCombinedId(getBlock(material).defaultBlockState()); - } - - private static final FluidState water = Fluids.WATER.getSource(false); - private static final Iterable registryBlockId = (Iterable) Reflection.getField(TechHider.block, IdMapper.class, 0).get(null); - - public Set materialToAllIds(Material material) { - Set ids = new HashSet<>(); - for (BlockState data : getBlock(material).getStateDefinition().getPossibleStates()) { - ids.add(getCombinedId(data)); - } - - if (material == Material.WATER) { - for (BlockState data : registryBlockId) { - if (data.getFluidState() == water) { - ids.add(getCombinedId(data)); - } - } - } - - return ids; - } - - private Block getBlock(Material material) { - return CraftMagicNumbers.getBlock(material); - } - - public int getCombinedId(BlockState blockData) { - return Block.getId(blockData); - } -} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java index 9394d1b0..c3df8e6f 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java @@ -20,6 +20,7 @@ package de.steamwar.techhider.legacy; import de.steamwar.Reflection; +import de.steamwar.techhider.ProtocolUtils; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import lombok.Getter; @@ -39,6 +40,7 @@ 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(); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolUtils.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolUtils.java deleted file mode 100644 index 5e9a7c78..00000000 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolUtils.java +++ /dev/null @@ -1,203 +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.google.common.primitives.Bytes; -import de.steamwar.Reflection; -import io.netty.buffer.ByteBuf; - -import java.lang.reflect.Array; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; -import java.util.function.UnaryOperator; - -public class ProtocolUtils { - private ProtocolUtils() { - } - - @Deprecated - public static BiFunction, Object> arrayCloneGenerator(Class elementClass) { - return (array, worker) -> { - int length = Array.getLength(array); - Object result = Array.newInstance(elementClass, length); - - for (int i = 0; i < length; i++) { - Array.set(result, i, worker.apply(Array.get(array, i))); - } - - return result; - }; - } - - public static UnaryOperator shallowCloneGenerator(Class clazz) { - BiConsumer filler = shallowFill(clazz); - - return source -> { - Object clone = Reflection.newInstance(clazz); - filler.accept(source, clone); - return clone; - }; - } - - public static UnaryOperator shallowTypedCloneGenerator(Class clazz) { - BiConsumer filler = shallowFill(clazz); - - return source -> { - Object clone = Reflection.newInstance(clazz); - filler.accept(source, clone); - return (T) clone; - }; - } - - private static BiConsumer shallowFill(Class clazz) { - if (clazz == null) { - return (source, clone) -> { - }; - } - - BiConsumer superFiller = shallowFill(clazz.getSuperclass()); - - Field[] fds = clazz.getDeclaredFields(); - List fields = new ArrayList<>(); - for (Field field : fds) { - if (Modifier.isStatic(field.getModifiers())) continue; - - field.setAccessible(true); - fields.add(field); - } - - return (source, clone) -> { - superFiller.accept(source, clone); - try { - for (Field field : fields) { - field.set(clone, field.get(source)); - } - } catch (IllegalAccessException e) { - throw new IllegalStateException("Could not set field", e); - } - - }; - } - - public static int posToChunk(int c) { - int chunk = c / 16; - if (c < 0) chunk--; - return chunk; - } - - @Deprecated - public static int readVarInt(byte[] array, int startPos) { - int numRead = 0; - int result = 0; - byte read; - do { - read = array[startPos + numRead]; - int value = (read & 0b01111111); - result |= (value << (7 * numRead)); - - numRead++; - if (numRead > 5) { - break; - } - } while ((read & 0b10000000) != 0); - - return result; - } - - public static int readVarInt(ByteBuf buf) { - int numRead = 0; - int result = 0; - byte read; - do { - read = buf.readByte(); - int value = (read & 0b01111111); - result |= (value << (7 * numRead)); - - if (++numRead > 5) throw new SecurityException("VarInt too long"); - } while ((read & 0b10000000) != 0); - - return result; - } - - public static void writeVarInt(ByteBuf buf, int value) { - do { - int temp = value & 0b01111111; - // Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone - value >>>= 7; - if (value != 0) { - temp |= 0b10000000; - } - buf.writeByte(temp); - } while (value != 0); - } - - @Deprecated - public static int readVarIntLength(byte[] array, int startPos) { - int numRead = 0; - byte read; - do { - read = array[startPos + numRead]; - numRead++; - if (numRead > 5) { - break; - } - } while ((read & 0b10000000) != 0); - - return numRead; - } - - @Deprecated - public static byte[] writeVarInt(int value) { - List buffer = new ArrayList<>(5); - do { - byte temp = (byte) (value & 0b01111111); - // Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone - value >>>= 7; - if (value != 0) { - temp |= 0b10000000; - } - buffer.add(temp); - } while (value != 0); - return Bytes.toArray(buffer); - } - - @Deprecated - public static class ChunkPos { - final int x; - final int z; - - public ChunkPos(int x, int z) { - this.x = x; - this.z = z; - } - - public final int x() { - return x; - } - - public final int z() { - return z; - } - } -} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolWrapper.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolWrapper.java index 9b6ac216..00e0dc31 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolWrapper.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolWrapper.java @@ -29,6 +29,7 @@ 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(); @@ -88,8 +89,4 @@ public class ProtocolWrapper { public boolean unfilteredTileEntityDataAction(Object packet) { return tileEntityType.get(packet) != signType; } - - public BiFunction blockBreakHiderGenerator(Class blockBreakPacket, TechHider techHider) { - return null; - } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java index 006b50d0..25b3f215 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java @@ -21,6 +21,8 @@ package de.steamwar.techhider.legacy; import com.comphenix.tinyprotocol.TinyProtocol; import de.steamwar.Reflection; +import de.steamwar.techhider.BlockIds; +import de.steamwar.techhider.ProtocolUtils; import lombok.Getter; import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; @@ -39,6 +41,7 @@ import java.util.function.Function; import java.util.function.UnaryOperator; import java.util.stream.Collectors; +@Deprecated public class TechHider { public static final Class blockPosition = BlockPos.class; @@ -87,7 +90,7 @@ public class TechHider { } public void enable() { - techhiders.forEach(TinyProtocol.instance::addFilter); + techhiders.forEach((type, playerObjectBiFunction) -> TinyProtocol.instance.addFilter((Class) type, playerObjectBiFunction)); } public void disable() { From 23b5ab3e8299d1053a49338205384fc2fa2a58cf Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Fri, 22 May 2026 21:35:01 +0200 Subject: [PATCH 52/52] Fix build --- .../src/de/steamwar/fightsystem/listener/ClickAnalyzer.java | 3 ++- .../src/de/steamwar/fightsystem/listener/Recording.java | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ClickAnalyzer.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ClickAnalyzer.java index 3def965c..3e84d916 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ClickAnalyzer.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ClickAnalyzer.java @@ -24,6 +24,7 @@ import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.utils.CraftbukkitWrapper; import de.steamwar.linkage.Linked; import net.minecraft.network.protocol.game.ServerboundUseItemOnPacket; +import net.minecraft.network.protocol.game.ServerboundUseItemPacket; import org.bukkit.entity.Player; import java.io.*; @@ -42,7 +43,7 @@ public class ClickAnalyzer { } public ClickAnalyzer() { - TinyProtocol.instance.addFilter(Recording.blockPlacePacket, this::onBlockPlace); + TinyProtocol.instance.addFilter(ServerboundUseItemPacket.class, this::onBlockPlace); TinyProtocol.instance.addFilter(ServerboundUseItemOnPacket.class, this::onBlockPlace); } diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java index 24741cda..0187d089 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java @@ -125,7 +125,7 @@ public class Recording implements Listener { @Override public void disable() { - TinyProtocol.instance.removeFilter(blockPlacePacket, place); + TinyProtocol.instance.removeFilter(ServerboundUseItemPacket.class, place); TinyProtocol.instance.removeFilter(blockDigPacket, dig); } }.register(); @@ -155,8 +155,6 @@ public class Recording implements Listener { return packet; } - public static final Class blockPlacePacket = ServerboundUseItemPacket.class; - private Object blockPlace(Player p, ServerboundUseItemPacket packet) { boolean mainHand = packet.getHand() == InteractionHand.MAIN_HAND; if (!isNotSent(p) && (mainHand ? p.getInventory().getItemInMainHand() : p.getInventory().getItemInOffHand()).getType() == Material.BOW) {