Remove Reflection

This commit is contained in:
2026-06-11 23:18:22 +02:00
parent 932732737d
commit 1590f8f0ee
12 changed files with 50 additions and 104 deletions
@@ -43,6 +43,8 @@ public class ClassPatcher {
/** Pre-computed set of targeted internal names for fast filtering. */
private final Set<String> targets;
private final Set<String> targetsPublicConstructor;
public ClassPatcher(List<AccessWidenerEntry> entries) {
this.entries = entries;
this.targets = entries.stream()
@@ -53,6 +55,10 @@ public class ClassPatcher {
return Stream.of(s, s.substring(0, index));
})
.collect(Collectors.toSet());
this.targetsPublicConstructor = entries.stream()
.filter(entry -> entry.directive().equals("transitive-extendable"))
.map(AccessWidenerEntry::target)
.collect(Collectors.toSet());
}
/**
@@ -66,7 +72,7 @@ public class ClassPatcher {
try {
ClassReader cr = new ClassReader(classBytes);
ClassWriter cw = new ClassWriter(cr, 0);
cr.accept(new ClassTransformer(cw, className, entries), ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
cr.accept(new ClassTransformer(cw, className, entries, targetsPublicConstructor.contains(className)), ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return cw.toByteArray();
} catch (Exception e) {
LOG.warning("[AccessWidener] Failed to transform " + className + ": " + e.getMessage());
@@ -30,11 +30,13 @@ public class ClassTransformer extends ClassVisitor {
private final String internalName;
private final List<AccessWidenerEntry> entries;
private final boolean appendPublicConstructor;
public ClassTransformer(ClassVisitor cv, String internalName, List<AccessWidenerEntry> entries) {
public ClassTransformer(ClassVisitor cv, String internalName, List<AccessWidenerEntry> entries, boolean appendPublicConstructor) {
super(Opcodes.ASM9, cv);
this.internalName = internalName;
this.entries = entries;
this.appendPublicConstructor = appendPublicConstructor;
}
@Override
@@ -44,6 +46,13 @@ public class ClassTransformer extends ClassVisitor {
if (!e.targets(internalName) || !"class".equals(e.memberType())) continue;
newAccess = applyDirective(e.directive(), newAccess, false);
}
if (appendPublicConstructor) {
MethodVisitor methodVisitor = visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
methodVisitor.visitMaxs(0, 0);
methodVisitor.visitCode();
methodVisitor.visitInsn(Opcodes.RETURN);
methodVisitor.visitEnd();
}
super.visit(version, newAccess, name, signature, superName, interfaces);
}
@@ -11,6 +11,3 @@ mutable field org/bukkit/craftbukkit/block/CraftBlockState world Lorg/bukkit/cra
# For TickManager
accessible field net/minecraft/server/ServerTickRateManager remainingSprintTicks J
# Test
accessible field net/minecraft/core/registries/BuiltInRegistries BLOCK_ENTITY_TYPE Lnet/minecraft/core/Registry;
@@ -38,6 +38,7 @@ import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.command.CommandMap;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -47,7 +48,6 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -73,19 +73,7 @@ public class BindCommand extends SWCommand implements Listener {
}
}
private static final CommandMap commandMap;
static {
Field knownCommandsField;
try {
knownCommandsField = Bukkit.getServer().getClass().getDeclaredField("commandMap");
knownCommandsField.setAccessible(true);
commandMap = (CommandMap) knownCommandsField.get(Bukkit.getServer());
} catch (IllegalAccessException | NoSuchFieldException var2) {
Bukkit.shutdown();
throw new SecurityException("Oh shit. Commands cannot be registered.", var2);
}
}
private static final CommandMap commandMap = ((CraftServer) Bukkit.getServer()).getCommandMap();
private static final NamespacedKey KEY = SWUtils.getNamespaceKey("command");
@@ -1,36 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar;
import jdk.internal.misc.Unsafe;
import lombok.experimental.UtilityClass;
@UtilityClass
public final class Reflection {
public static Object newInstance(Class<?> clazz) {
try {
return Unsafe.getUnsafe().allocateInstance(clazz);
} catch (InstantiationException e) {
throw new SecurityException("Could not create object", e);
}
}
}
@@ -22,35 +22,20 @@ package de.steamwar.command;
import lombok.experimental.UtilityClass;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandMap;
import org.bukkit.command.SimpleCommandMap;
import org.bukkit.craftbukkit.CraftServer;
import java.lang.reflect.Field;
import java.util.Map;
@UtilityClass
class CommandRegistering {
private static final CommandMap commandMap;
private static final SimpleCommandMap commandMap;
private static final Map<String, Command> knownCommandMap;
static {
try {
final Field commandMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap");
commandMapField.setAccessible(true);
commandMap = (CommandMap) commandMapField.get(Bukkit.getServer());
} catch (NoSuchFieldException | IllegalAccessException exception) {
Bukkit.shutdown();
throw new SecurityException("Oh shit. Commands cannot be registered.", exception);
}
try {
final Field knownCommandsField = SimpleCommandMap.class.getDeclaredField("knownCommands");
knownCommandsField.setAccessible(true);
knownCommandMap = (Map<String, Command>) knownCommandsField.get(commandMap);
} catch (NoSuchFieldException | IllegalAccessException exception) {
Bukkit.shutdown();
throw new SecurityException("Oh shit. Commands cannot be registered.", exception);
}
commandMap = ((CraftServer) Bukkit.getServer()).getCommandMap();
knownCommandMap = commandMap.getKnownCommands();
}
static void unregister(Command command) {
@@ -20,7 +20,6 @@
package de.steamwar.entity;
import com.mojang.datafixers.util.Pair;
import de.steamwar.Reflection;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import lombok.Getter;
import net.minecraft.core.registries.BuiltInRegistries;
@@ -174,7 +173,7 @@ public class REntity {
}
public void showAnimation(byte animation) {
ClientboundAnimatePacket packet = (ClientboundAnimatePacket) Reflection.newInstance(ClientboundAnimatePacket.class);
ClientboundAnimatePacket packet = new ClientboundAnimatePacket();
packet.id = entityId;
packet.action = animation;
server.updateEntity(this, packet);
@@ -185,7 +184,7 @@ public class REntity {
}
public void showDamage() {
ClientboundEntityEventPacket packet = (ClientboundEntityEventPacket) Reflection.newInstance(ClientboundEntityEventPacket.class);
ClientboundEntityEventPacket packet = new ClientboundEntityEventPacket();
packet.entityId = entityId;
packet.eventId = (byte) 2;
server.updateEntity(this, packet);
@@ -363,7 +362,7 @@ public class REntity {
}
private Object getHeadRotationPacket() {
ClientboundRotateHeadPacket packet = (ClientboundRotateHeadPacket) Reflection.newInstance(ClientboundRotateHeadPacket.class);
ClientboundRotateHeadPacket packet = new ClientboundRotateHeadPacket();
packet.entityId = entityId;
packet.yHeadRot = headYaw;
return packet;
@@ -33,8 +33,8 @@ import java.util.List;
import java.util.function.UnaryOperator;
public class ChunkHider {
private static final UnaryOperator<ClientboundLevelChunkWithLightPacket> chunkPacketShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class);
private static final UnaryOperator<ClientboundLevelChunkPacketData> chunkDataShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class);
private static final UnaryOperator<ClientboundLevelChunkWithLightPacket> chunkPacketShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkWithLightPacket::new);
private static final UnaryOperator<ClientboundLevelChunkPacketData> chunkDataShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class, ClientboundLevelChunkPacketData::new);
private final int SECTION_SPAN_SIZE = 16;
private final byte BIT_PER_BLOCK_INDIRECTION_LIMIT = 8;
@@ -20,41 +20,25 @@
package de.steamwar.techhider;
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.Supplier;
import java.util.function.UnaryOperator;
public class ProtocolUtils {
private ProtocolUtils() {
}
@Deprecated
public static BiFunction<Object, UnaryOperator<Object>, 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 <T> UnaryOperator<T> shallowCloneGenerator(Class<T> clazz) {
public static <T> UnaryOperator<T> shallowCloneGenerator(Class<T> clazz, Supplier<T> supplier) {
BiConsumer<T, T> filler = shallowFill(clazz);
return source -> {
T clone = (T) Reflection.newInstance(clazz);
T clone = supplier.get();
filler.accept(source, clone);
return clone;
};
@@ -40,8 +40,8 @@ import java.util.stream.Collectors;
public class ChunkHider {
public static final ChunkHider impl = new ChunkHider();
private static final UnaryOperator<ClientboundLevelChunkWithLightPacket> chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class);
private static final UnaryOperator<ClientboundLevelChunkPacketData> chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class);
private static final UnaryOperator<ClientboundLevelChunkWithLightPacket> chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkWithLightPacket::new);
private static final UnaryOperator<ClientboundLevelChunkPacketData> chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class, ClientboundLevelChunkPacketData::new);
public BiFunction<Player, ClientboundLevelChunkWithLightPacket, ClientboundLevelChunkWithLightPacket> chunkHiderGenerator(TechHider techHider) {
return (p, packet) -> {
@@ -92,9 +92,9 @@ public class TechHider {
techhiders.forEach(TinyProtocol.instance::removeFilter);
}
public static final UnaryOperator<ClientboundSectionBlocksUpdatePacket> multiBlockChangeCloner = ProtocolUtils.shallowCloneGenerator(ClientboundSectionBlocksUpdatePacket.class);
public static final UnaryOperator<ClientboundSectionBlocksUpdatePacket> multiBlockChangeCloner = ProtocolUtils.shallowCloneGenerator(ClientboundSectionBlocksUpdatePacket.class, ClientboundSectionBlocksUpdatePacket::new);
private static final UnaryOperator<ClientboundBlockUpdatePacket> blockChangeCloner = ProtocolUtils.shallowCloneGenerator(ClientboundBlockUpdatePacket.class);
private static final UnaryOperator<ClientboundBlockUpdatePacket> blockChangeCloner = ProtocolUtils.shallowCloneGenerator(ClientboundBlockUpdatePacket.class, ClientboundBlockUpdatePacket::new);
private ClientboundBlockUpdatePacket blockChangeHider(Player p, ClientboundBlockUpdatePacket packet) {
switch (locationEvaluator.checkBlockPos(p, packet.getPos())) {
@@ -15,16 +15,22 @@ accessible field net/minecraft/server/MinecraftServer services Lnet/minecraft/se
mutable field net/minecraft/server/MinecraftServer services Lnet/minecraft/server/Services;
# REntity
## transitive-extendable means that a public no args constructor is added without any super initialization
transitive-extendable class net/minecraft/network/protocol/game/ClientboundAnimatePacket
accessible field net/minecraft/network/protocol/game/ClientboundAnimatePacket id I
mutable field net/minecraft/network/protocol/game/ClientboundAnimatePacket id I
accessible field net/minecraft/network/protocol/game/ClientboundAnimatePacket action I
mutable field net/minecraft/network/protocol/game/ClientboundAnimatePacket action I
## transitive-extendable means that a public no args constructor is added without any super initialization
transitive-extendable class net/minecraft/network/protocol/game/ClientboundEntityEventPacket
accessible field net/minecraft/network/protocol/game/ClientboundEntityEventPacket entityId I
mutable field net/minecraft/network/protocol/game/ClientboundEntityEventPacket entityId I
accessible field net/minecraft/network/protocol/game/ClientboundEntityEventPacket eventId B
mutable field net/minecraft/network/protocol/game/ClientboundEntityEventPacket eventId B
## transitive-extendable means that a public no args constructor is added without any super initialization
transitive-extendable class net/minecraft/network/protocol/game/ClientboundRotateHeadPacket
accessible field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket yHeadRot B
mutable field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket yHeadRot B
## + TechHider
@@ -32,9 +38,13 @@ accessible field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket
mutable field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket entityId I
# For ChunkHider
## transitive-extendable means that a public no args constructor is added without any super initialization
transitive-extendable class net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket
accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket chunkData Lnet/minecraft/network/protocol/game/ClientboundLevelChunkPacketData;
mutable field net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket chunkData Lnet/minecraft/network/protocol/game/ClientboundLevelChunkPacketData;
## transitive-extendable means that a public no args constructor is added without any super initialization
transitive-extendable class net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData
accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData buffer [B
mutable field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData buffer [B
accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData blockEntitiesData Ljava/util/List;
@@ -47,6 +57,8 @@ accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacket
# For TechHider
accessible field net/minecraft/network/protocol/game/ClientboundMoveEntityPacket entityId I
## transitive-extendable means that a public no args constructor is added without any super initialization
transitive-extendable class net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket
accessible field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket sectionPos Lnet/minecraft/core/SectionPos;
accessible field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket positions [S
mutable field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket positions [S
@@ -54,4 +66,6 @@ accessible field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpd
mutable field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket states [Lnet/minecraft/world/level/block/state/BlockState;
# For legacy/TechHider
mutable field net/minecraft/network/protocol/game/ClientboundBlockUpdatePacket blockState Lnet/minecraft/world/level/block/state/BlockState;
## transitive-extendable means that a public no args constructor is added without any super initialization
transitive-extendable class net/minecraft/network/protocol/game/ClientboundBlockUpdatePacket
mutable field net/minecraft/network/protocol/game/ClientboundBlockUpdatePacket blockState Lnet/minecraft/world/level/block/state/BlockState;