Restore patch to vanilla command node (#464)
This commit is contained in:
parent
d07354e23d
commit
b547770edb
@ -3,6 +3,7 @@ package io.izzel.arclight.common.mixin.core.commands;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import io.izzel.arclight.common.bridge.core.command.CommandSourceBridge;
|
||||
import io.izzel.arclight.common.bridge.core.command.ICommandSourceBridge;
|
||||
import io.izzel.arclight.common.mod.compat.CommandNodeHooks;
|
||||
import net.minecraft.commands.CommandSource;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
@ -28,8 +29,10 @@ public abstract class CommandSourceStackMixin implements CommandSourceBridge {
|
||||
|
||||
public CommandNode currentCommand;
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Inject(method = "hasPermission", cancellable = true, at = @At("HEAD"))
|
||||
public void arclight$checkPermission(int level, CallbackInfoReturnable<Boolean> cir) {
|
||||
CommandNode currentCommand = bridge$getCurrentCommand();
|
||||
if (currentCommand != null) {
|
||||
cir.setReturnValue(hasPermission(level, VanillaCommandWrapper.getPermission(currentCommand)));
|
||||
}
|
||||
@ -47,8 +50,12 @@ public abstract class CommandSourceStackMixin implements CommandSourceBridge {
|
||||
|
||||
@Override
|
||||
public CommandNode<?> bridge$getCurrentCommand() {
|
||||
if (currentCommand == null) {
|
||||
return CommandNodeHooks.getCurrent();
|
||||
} else {
|
||||
return currentCommand;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridge$setCurrentCommand(CommandNode<?> node) {
|
||||
|
||||
@ -8,13 +8,16 @@ import java.util.Map;
|
||||
|
||||
public class CommandNodeHooks {
|
||||
|
||||
private static final long CHILDREN, LITERALS, ARGUMENTS;
|
||||
private static final long CHILDREN, LITERALS, ARGUMENTS, CURRENT;
|
||||
private static final Object CURRENT_BASE;
|
||||
|
||||
static {
|
||||
try {
|
||||
CHILDREN = Unsafe.objectFieldOffset(CommandNode.class.getDeclaredField("children"));
|
||||
LITERALS = Unsafe.objectFieldOffset(CommandNode.class.getDeclaredField("literals"));
|
||||
ARGUMENTS = Unsafe.objectFieldOffset(CommandNode.class.getDeclaredField("arguments"));
|
||||
CURRENT_BASE = Unsafe.staticFieldBase(CommandNode.class.getDeclaredField("CURRENT_COMMAND"));
|
||||
CURRENT = Unsafe.staticFieldOffset(CommandNode.class.getDeclaredField("CURRENT_COMMAND"));
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
@ -27,6 +30,10 @@ public class CommandNodeHooks {
|
||||
((Map<String, ?>) Unsafe.getObject(node, ARGUMENTS)).remove(command);
|
||||
}
|
||||
|
||||
public static CommandNode<?> getCurrent() {
|
||||
return (CommandNode<?>) Unsafe.getObjectVolatile(CURRENT_BASE, CURRENT);
|
||||
}
|
||||
|
||||
public static <S> boolean canUse(CommandNode<S> node, S source) {
|
||||
if (source instanceof CommandSourceBridge s) {
|
||||
try {
|
||||
|
||||
@ -5,13 +5,27 @@ import com.google.gson.reflect.TypeToken;
|
||||
import io.izzel.arclight.api.ArclightVersion;
|
||||
import io.izzel.arclight.api.Unsafe;
|
||||
import io.izzel.arclight.i18n.ArclightLocale;
|
||||
import net.minecraftforge.forgespi.locating.IModLocator;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.FieldInsnNode;
|
||||
import org.objectweb.asm.tree.FieldNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.InsnNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
import org.objectweb.asm.tree.VarInsnNode;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Map;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.stream.Collectors;
|
||||
@ -24,6 +38,54 @@ public class AbstractBootstrap {
|
||||
Object base = Unsafe.staticFieldBase(field);
|
||||
long offset = Unsafe.staticFieldOffset(field);
|
||||
Unsafe.putObjectVolatile(base, offset, new EnumTypeFactory());
|
||||
try (var in = getClass().getClassLoader().getResourceAsStream("com/mojang/brigadier/tree/CommandNode.class")) {
|
||||
var node = new ClassNode();
|
||||
new ClassReader(in).accept(node, 0);
|
||||
{
|
||||
FieldNode fieldNode = new FieldNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_VOLATILE, "CURRENT_COMMAND", "Lcom/mojang/brigadier/tree/CommandNode;", null, null);
|
||||
node.fields.add(fieldNode);
|
||||
for (var method : node.methods) {
|
||||
if (method.name.equals("canUse")) {
|
||||
for (var instruction : method.instructions) {
|
||||
if (instruction.getOpcode() == Opcodes.INVOKEINTERFACE || instruction.getOpcode() == Opcodes.INVOKEVIRTUAL) {
|
||||
var assign = new InsnList();
|
||||
assign.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||
assign.add(new FieldInsnNode(Opcodes.PUTSTATIC, "com/mojang/brigadier/tree/CommandNode", fieldNode.name, fieldNode.desc));
|
||||
method.instructions.insertBefore(instruction, assign);
|
||||
var reset = new InsnList();
|
||||
reset.add(new InsnNode(Opcodes.ACONST_NULL));
|
||||
reset.add(new FieldInsnNode(Opcodes.PUTSTATIC, "com/mojang/brigadier/tree/CommandNode", fieldNode.name, fieldNode.desc));
|
||||
method.instructions.insert(instruction, assign);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
var removeCommand = new MethodNode();
|
||||
removeCommand.access = Opcodes.ACC_PUBLIC;
|
||||
removeCommand.name = "removeCommand";
|
||||
removeCommand.desc = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class));
|
||||
removeCommand.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||
removeCommand.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
|
||||
removeCommand.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, "com/mojang/brigadier/tree/CommandNode", "children", Type.getDescriptor(Map.class)));
|
||||
removeCommand.instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, Type.getInternalName(Map.class), "remove", "(Ljava/lang/Object;)Ljava/lang/Object;", true));
|
||||
removeCommand.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||
removeCommand.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
|
||||
removeCommand.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, "com/mojang/brigadier/tree/CommandNode", "literals", Type.getDescriptor(Map.class)));
|
||||
removeCommand.instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, Type.getInternalName(Map.class), "remove", "(Ljava/lang/Object;)Ljava/lang/Object;", true));
|
||||
removeCommand.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||
removeCommand.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
|
||||
removeCommand.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, "com/mojang/brigadier/tree/CommandNode", "arguments", Type.getDescriptor(Map.class)));
|
||||
removeCommand.instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, Type.getInternalName(Map.class), "remove", "(Ljava/lang/Object;)Ljava/lang/Object;", true));
|
||||
node.methods.add(removeCommand);
|
||||
}
|
||||
var cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
node.accept(cw);
|
||||
byte[] bytes = cw.toByteArray();
|
||||
Unsafe.defineClass("com.mojang.brigadier.tree.CommandNode", bytes, 0, bytes.length, IModLocator.class.getClassLoader() /* MC-BOOTSTRAP */ , getClass().getProtectionDomain());
|
||||
}
|
||||
}
|
||||
|
||||
protected void setupMod() throws Exception {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user