Integrated patcher with WorldEdit support
This commit is contained in:
parent
7023a576e6
commit
0f76fd6de8
|
@ -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.ClassLoaderRemapper;
|
||||||
import io.izzel.arclight.common.mod.util.remapper.GlobalClassRepo;
|
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.PluginTransformer;
|
||||||
|
import io.izzel.arclight.common.mod.util.remapper.patcher.integrated.IntegratedPatcher;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
|
||||||
|
@ -14,7 +15,6 @@ import java.io.InputStreamReader;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -37,10 +37,10 @@ public class ArclightPluginPatcher implements PluginTransformer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<PluginPatcher> load(List<PluginTransformer> transformerList) {
|
public static List<PluginPatcher> load(List<PluginTransformer> transformerList) {
|
||||||
|
var list = new ArrayList<PluginPatcher>();
|
||||||
File pluginFolder = new File("plugins");
|
File pluginFolder = new File("plugins");
|
||||||
if (pluginFolder.exists()) {
|
if (pluginFolder.exists()) {
|
||||||
ArclightMod.LOGGER.info("patcher.loading");
|
ArclightMod.LOGGER.info("patcher.loading");
|
||||||
ArrayList<PluginPatcher> list = new ArrayList<>();
|
|
||||||
File[] files = pluginFolder.listFiles();
|
File[] files = pluginFolder.listFiles();
|
||||||
if (files != null) {
|
if (files != null) {
|
||||||
for (File file : files) {
|
for (File file : files) {
|
||||||
|
@ -49,14 +49,14 @@ public class ArclightPluginPatcher implements PluginTransformer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!list.isEmpty()) {
|
if (!list.isEmpty()) {
|
||||||
list.sort(Comparator.comparing(PluginPatcher::priority));
|
|
||||||
ArclightMod.LOGGER.info("patcher.loaded", list.size());
|
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<PluginPatcher> loadFromJar(File file) {
|
private static Optional<PluginPatcher> loadFromJar(File file) {
|
||||||
|
|
|
@ -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<String, BiConsumer<ClassNode, ClassRepo>> SPECIFIC = new HashMap<>() {};
|
||||||
|
private static final List<BiConsumer<ClassNode, ClassRepo>> 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<ClassNode, ClassRepo> consumer = SPECIFIC.get(node.name);
|
||||||
|
if (consumer != null) {
|
||||||
|
consumer.accept(node, classRepo);
|
||||||
|
} else {
|
||||||
|
for (BiConsumer<ClassNode, ClassRepo> general : GENERAL) {
|
||||||
|
general.accept(node, classRepo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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("<init>")) {
|
||||||
|
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", "<init>", "()V", false));
|
||||||
|
method.instructions.add(new InsnNode(Opcodes.ATHROW));
|
||||||
|
method.tryCatchBlocks.clear();
|
||||||
|
method.localVariables.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user