diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/patcher/ArclightPluginPatcher.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/patcher/ArclightPluginPatcher.java index 5876afbd..c814e7b6 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/patcher/ArclightPluginPatcher.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/patcher/ArclightPluginPatcher.java @@ -5,6 +5,7 @@ import io.izzel.arclight.common.mod.ArclightMod; import io.izzel.arclight.common.mod.util.remapper.ClassLoaderRemapper; import io.izzel.arclight.common.mod.util.remapper.GlobalClassRepo; import io.izzel.arclight.common.mod.util.remapper.PluginTransformer; +import io.izzel.arclight.common.mod.util.remapper.patcher.integrated.IntegratedPatcher; import org.bukkit.configuration.file.YamlConfiguration; import org.objectweb.asm.tree.ClassNode; @@ -14,7 +15,6 @@ import java.io.InputStreamReader; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Optional; @@ -37,10 +37,10 @@ public class ArclightPluginPatcher implements PluginTransformer { } public static List load(List transformerList) { + var list = new ArrayList(); File pluginFolder = new File("plugins"); if (pluginFolder.exists()) { ArclightMod.LOGGER.info("patcher.loading"); - ArrayList list = new ArrayList<>(); File[] files = pluginFolder.listFiles(); if (files != null) { for (File file : files) { @@ -49,14 +49,14 @@ public class ArclightPluginPatcher implements PluginTransformer { } } if (!list.isEmpty()) { - list.sort(Comparator.comparing(PluginPatcher::priority)); ArclightMod.LOGGER.info("patcher.loaded", list.size()); - transformerList.add(new ArclightPluginPatcher(list)); - return list; } } } - return Collections.emptyList(); + list.add(new IntegratedPatcher()); + list.sort(Comparator.comparing(PluginPatcher::priority)); + transformerList.add(new ArclightPluginPatcher(list)); + return list; } private static Optional loadFromJar(File file) { diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/patcher/integrated/IntegratedPatcher.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/patcher/integrated/IntegratedPatcher.java new file mode 100644 index 00000000..d017e80c --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/patcher/integrated/IntegratedPatcher.java @@ -0,0 +1,34 @@ +package io.izzel.arclight.common.mod.util.remapper.patcher.integrated; + +import io.izzel.arclight.api.PluginPatcher; +import org.objectweb.asm.tree.ClassNode; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; + +public class IntegratedPatcher implements PluginPatcher { + + private static final Map> SPECIFIC = new HashMap<>() {}; + private static final List> GENERAL = new ArrayList<>(); + + static { + SPECIFIC.put("com/sk89q/worldedit/bukkit/BukkitAdapter", WorldEdit::handleBukkitAdapter); + SPECIFIC.put("com/sk89q/worldedit/bukkit/adapter/Refraction", WorldEdit::handlePickName); + GENERAL.add(WorldEdit::handleWatchdog); + } + + @Override + public void handleClass(ClassNode node, ClassRepo classRepo) { + BiConsumer consumer = SPECIFIC.get(node.name); + if (consumer != null) { + consumer.accept(node, classRepo); + } else { + for (BiConsumer general : GENERAL) { + general.accept(node, classRepo); + } + } + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/patcher/integrated/WorldEdit.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/patcher/integrated/WorldEdit.java new file mode 100644 index 00000000..b0826dc6 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/patcher/integrated/WorldEdit.java @@ -0,0 +1,113 @@ +package io.izzel.arclight.common.mod.util.remapper.patcher.integrated; + +import io.izzel.arclight.api.PluginPatcher; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.GeneratorAdapter; +import org.objectweb.asm.commons.Method; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +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.TypeInsnNode; +import org.objectweb.asm.tree.VarInsnNode; + +import java.util.Locale; + +public class WorldEdit { + + public static void handleBukkitAdapter(ClassNode node, PluginPatcher.ClassRepo repo) { + MethodNode standardize = new MethodNode(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, "patcher$standardize", + Type.getMethodDescriptor(Type.getType(String.class), Type.getType(String.class)), null, null); + try { + GeneratorAdapter adapter = new GeneratorAdapter(standardize, standardize.access, standardize.name, standardize.desc); + adapter.loadArg(0); + adapter.push(':'); + adapter.push('_'); + adapter.invokeVirtual(Type.getType(String.class), Method.getMethod(String.class.getMethod("replace", char.class, char.class))); + adapter.push("\\s+"); + adapter.push("_"); + adapter.invokeVirtual(Type.getType(String.class), Method.getMethod(String.class.getMethod("replaceAll", String.class, String.class))); + adapter.push("\\W"); + adapter.push(""); + adapter.invokeVirtual(Type.getType(String.class), Method.getMethod(String.class.getMethod("replaceAll", String.class, String.class))); + adapter.getStatic(Type.getType(Locale.class), "ENGLISH", Type.getType(Locale.class)); + adapter.invokeVirtual(Type.getType(String.class), Method.getMethod(String.class.getMethod("toUpperCase", Locale.class))); + adapter.returnValue(); + adapter.endMethod(); + } catch (Throwable t) { + t.printStackTrace(); + } + node.methods.add(standardize); + for (MethodNode method : node.methods) { + if (method.name.equals("adapt")) { + handleAdapt(node, standardize, method); + } + } + } + + public static void handlePickName(ClassNode node, PluginPatcher.ClassRepo repo) { + for (MethodNode method : node.methods) { + if (method.name.equals("pickName")) { + method.instructions.clear(); + method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); + method.instructions.add(new InsnNode(Opcodes.ARETURN)); + return; + } + } + } + + private static void handleAdapt(ClassNode node, MethodNode standardize, MethodNode method) { + switch (method.desc) { + case "(Lcom/sk89q/worldedit/world/item/ItemType;)Lorg/bukkit/Material;": + case "(Lcom/sk89q/worldedit/world/block/BlockType;)Lorg/bukkit/Material;": + case "(Lcom/sk89q/worldedit/world/biome/BiomeType;)Lorg/bukkit/block/Biome;": + case "(Lcom/sk89q/worldedit/world/entity/EntityType;)Lorg/bukkit/entity/EntityType;": { + for (AbstractInsnNode instruction : method.instructions) { + if (instruction.getOpcode() == Opcodes.ATHROW) { + InsnList list = new InsnList(); + list.add(new VarInsnNode(Opcodes.ALOAD, 0)); + list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getMethodType(method.desc).getArgumentTypes()[0].getInternalName(), "getId", "()Ljava/lang/String;", false)); + list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, node.name, standardize.name, standardize.desc, false)); + switch (Type.getMethodType(method.desc).getReturnType().getInternalName()) { + case "org/bukkit/Material": + list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "org/bukkit/Material", "getMaterial", "(Ljava/lang/String;)Lorg/bukkit/Material;", false)); + break; + case "org/bukkit/block/Biome": + list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "org/bukkit/block/Biome", "valueOf", "(Ljava/lang/String;)Lorg/bukkit/block/Biome;", false)); + break; + case "org/bukkit/entity/EntityType": + list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "org/bukkit/entity/EntityType", "fromName", "(Ljava/lang/String;)Lorg/bukkit/entity/EntityType;", false)); + break; + } + list.add(new InsnNode(Opcodes.ARETURN)); + method.instructions.insert(instruction, list); + method.instructions.set(instruction, new InsnNode(Opcodes.POP)); + return; + } + } + break; + } + } + } + + public static void handleWatchdog(ClassNode node, PluginPatcher.ClassRepo repo) { + if (node.interfaces.size() == 1 && node.interfaces.get(0).equals("com/sk89q/worldedit/extension/platform/Watchdog") + && node.name.contains("SpigotWatchdog")) { + for (MethodNode method : node.methods) { + if (method.name.equals("")) { + method.instructions.clear(); + method.instructions.add(new TypeInsnNode(Opcodes.NEW, "java/lang/ClassNotFoundException")); + method.instructions.add(new InsnNode(Opcodes.DUP)); + method.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/ClassNotFoundException", "", "()V", false)); + method.instructions.add(new InsnNode(Opcodes.ATHROW)); + method.tryCatchBlocks.clear(); + method.localVariables.clear(); + return; + } + } + } + } +} \ No newline at end of file