From 23eaed2e4924ae6fcc990a1e00f9830eaacc5b7e Mon Sep 17 00:00:00 2001 From: Irmo van den Berge Date: Fri, 16 Oct 2020 22:47:00 +0200 Subject: [PATCH] Initial support update for Bukkit4fabric mod (Fabric) --- .../common/internal/CommonBootstrap.java | 16 +- .../common/server/Bukkit4FabricServer.java | 142 ++++++++++++++++++ .../common/server/CraftBukkitServer.java | 28 +++- 3 files changed, 177 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/bergerkiller/bukkit/common/server/Bukkit4FabricServer.java diff --git a/src/main/java/com/bergerkiller/bukkit/common/internal/CommonBootstrap.java b/src/main/java/com/bergerkiller/bukkit/common/internal/CommonBootstrap.java index 4adc001e9..44fc496aa 100644 --- a/src/main/java/com/bergerkiller/bukkit/common/internal/CommonBootstrap.java +++ b/src/main/java/com/bergerkiller/bukkit/common/internal/CommonBootstrap.java @@ -84,6 +84,7 @@ public class CommonBootstrap { // Always Spigot server CommonServer server = new SpigotServer(); server.init(); + initServerResolvers(server); server.postInit(); _commonServer = server; } else { @@ -93,6 +94,7 @@ public class CommonBootstrap { servers.add(new MagmaServer()); servers.add(new ArclightServer()); servers.add(new CatServerServer()); + servers.add(new Bukkit4FabricServer()); servers.add(new PurpurServer()); servers.add(new SpigotServer()); servers.add(new SportBukkitServer()); @@ -103,6 +105,7 @@ public class CommonBootstrap { for (CommonServer server : servers) { try { if (server.init()) { + initServerResolvers(server); server.postInit(); _commonServer = server; break; @@ -212,12 +215,11 @@ public class CommonBootstrap { } /** - * Initializes the type, field and method resolvers for a server, so that such information - * can be obtained at runtime. + * Registers any resolvers used by the current server handler * * @param server */ - private static void initResolvers(CommonServer server) { + private static void initServerResolvers(CommonServer server) { // Register server to handle field, method and class resolving if (server instanceof ClassPathResolver) { Resolver.registerClassResolver((ClassPathResolver) server); @@ -234,7 +236,15 @@ public class CommonBootstrap { if (server instanceof CompiledMethodNameResolver) { Resolver.registerCompiledMethodResolver((CompiledMethodNameResolver) server); } + } + /** + * Initializes the type, field and method resolvers for a server, so that such information + * can be obtained at runtime. + * + * @param server + */ + private static void initResolvers(CommonServer server) { // Enum Gamemode not available in package space on <= MC 1.9; we must proxy it if (Resolver.loadClass("net.minecraft.server.EnumGamemode", false) == null) { final String eg_path = Resolver.resolveClassPath("net.minecraft.server.EnumGamemode"); diff --git a/src/main/java/com/bergerkiller/bukkit/common/server/Bukkit4FabricServer.java b/src/main/java/com/bergerkiller/bukkit/common/server/Bukkit4FabricServer.java new file mode 100644 index 000000000..251148b3e --- /dev/null +++ b/src/main/java/com/bergerkiller/bukkit/common/server/Bukkit4FabricServer.java @@ -0,0 +1,142 @@ +package com.bergerkiller.bukkit.common.server; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + +import com.bergerkiller.mountiplex.reflection.declarations.Template; +import com.bergerkiller.mountiplex.reflection.resolver.FieldNameResolver; +import com.bergerkiller.mountiplex.reflection.resolver.MethodNameResolver; + +/** + * Fabric server support, that uses the Bukkit4Fabric mod for paper/spigot/bukkit support.
+ *
+ * https://github.com/BukkitFabric/bukkit4fabric/ + */ +public class Bukkit4FabricServer extends SpigotServer implements FieldNameResolver, MethodNameResolver { + private ReflectionRemapperClass reflectionRemapper = null; + + @Override + public boolean init() { + if (!super.init()) { + return false; + } + + // Check Bukkit4fabric mod exists, we expect this Class to exist + try { + Class.forName("com.javazilla.bukkitfabric.BukkitFabricMod"); + } catch (Throwable t) { + return false; + } + + // Make sure RemappingUtils exists, this initializes the RemappingUtilsHandle using the above initialized declaration + reflectionRemapper = Template.Class.create(ReflectionRemapperClass.class); + if (!reflectionRemapper.isAvailable()) { + return false; + } + + // Force initialization to avoid late catastrophic failing + reflectionRemapper.forceInitialization(); + + return true; + } + + @Override + public String getServerName() { + return "Fabric (Bukkit4fabric)"; + } + + @Override + public Collection getLoadableWorlds() { + return ForgeSupport.getLoadableWorlds(); + } + + @Override + public boolean isLoadableWorld(String worldName) { + return ForgeSupport.isLoadableWorld(worldName); + } + + @Override + public File getWorldRegionFolder(String worldName) { + return ForgeSupport.getWorldRegionFolder(worldName); + } + + @Override + public File getWorldFolder(String worldName) { + return ForgeSupport.getWorldFolder(worldName); + } + + @Override + public String resolveClassPath(String path) { + // Replaces path with proper net.minecraft.server.v1_1_1 path + path = super.resolveClassPath(path); + + // Ask Magma what the actual class name is on Forge + path = reflectionRemapper.mapClassName(path); + + return path; + } + + @Override + public boolean canLoadClassPath(String classPath) { + // The .class data at this path contains obfuscated type information + // These obfuscated names are deobufscated at runtime + // This difference causes compiler errors at runtime, so instead of + // loading the .class files, inspect the signatures using reflection. + if (classPath.startsWith("org.bukkit.craftbukkit.")) { + return false; + } + + return true; + } + + @Override + public String resolveMethodName(Class type, String methodName, Class[] params) { + try { + //TODO: Params??? + Method method = reflectionRemapper.getDeclaredMethodByName(type, methodName); + if (method != null) { + return method.getName(); + } + } catch (Throwable t) {} + + return methodName; + } + + @Override + public String resolveFieldName(Class type, String fieldName) { + try { + //TODO: Params??? + Field field = reflectionRemapper.getDeclaredFieldByName(type, fieldName); + if (field != null) { + return field.getName(); + } + } catch (Throwable t) {} + + return fieldName; + } + + @Override + public void addVariables(Map variables) { + super.addVariables(variables); + variables.put("fabric", "bukkit4fabric"); + variables.put("forge_nms_obfuscated", "true"); + } + + @Template.Optional + @Template.InstanceType("com.javazilla.bukkitfabric.nms.ReflectionRemapper") + public static abstract class ReflectionRemapperClass extends Template.Class { + + @Template.Generated("public static String mapClassName(String className)") + public abstract String mapClassName(String className); + + @Template.Generated("public static java.lang.reflect.Method getDeclaredMethodByName(Class calling, String f)") + public abstract Method getDeclaredMethodByName(Class type, String name); + + @Template.Generated("public static java.lang.reflect.Field getDeclaredFieldByName(Class calling, String f)") + public abstract Field getDeclaredFieldByName(Class type, String fieldName); + } + +} diff --git a/src/main/java/com/bergerkiller/bukkit/common/server/CraftBukkitServer.java b/src/main/java/com/bergerkiller/bukkit/common/server/CraftBukkitServer.java index f7c61e2f7..9bd8a15a1 100644 --- a/src/main/java/com/bergerkiller/bukkit/common/server/CraftBukkitServer.java +++ b/src/main/java/com/bergerkiller/bukkit/common/server/CraftBukkitServer.java @@ -4,8 +4,11 @@ import com.bergerkiller.bukkit.common.Logging; import com.bergerkiller.bukkit.common.utils.StringUtil; import com.bergerkiller.mountiplex.reflection.ClassTemplate; import com.bergerkiller.mountiplex.reflection.resolver.ClassPathResolver; +import com.bergerkiller.mountiplex.reflection.resolver.Resolver; import com.bergerkiller.mountiplex.reflection.util.asm.ASMUtil; +import com.bergerkiller.mountiplex.reflection.util.asm.MPLType; +import java.lang.reflect.Method; import java.util.Collections; import java.util.Map; @@ -73,18 +76,18 @@ public class CraftBukkitServer extends CommonServerBase implements ClassPathReso public void postInit() { try { // Find getVersion() method using reflection - Class minecraftServerType = Class.forName(NMS_ROOT_VERSIONED + ".MinecraftServer", false, this.getClass().getClassLoader()); - java.lang.reflect.Method getVersionMethod = minecraftServerType.getMethod("getVersion"); + Class minecraftServerType = loadClass(NMS_ROOT_VERSIONED + ".MinecraftServer"); + java.lang.reflect.Method getVersionMethod = getDeclaredMethod(minecraftServerType, "getVersion"); // This is easy when the server is already initialized if (Bukkit.getServer() != null) { // Obtain MinecraftServer instance from server - Class server = Class.forName(CB_ROOT_VERSIONED + ".CraftServer"); + Class server = loadClass(CB_ROOT_VERSIONED + ".CraftServer"); // Standard CraftServer::getServer() Object minecraftServerInstance; try { - java.lang.reflect.Method getServerMethod = server.getDeclaredMethod("getServer"); + java.lang.reflect.Method getServerMethod = getDeclaredMethod(server, "getServer"); minecraftServerInstance = getServerMethod.invoke(Bukkit.getServer()); MC_VERSION = (String) getVersionMethod.invoke(minecraftServerInstance); return; @@ -99,10 +102,10 @@ public class CraftBukkitServer extends CommonServerBase implements ClassPathReso try { Object gameVersion; try { - Class minecraftVersionClass = Class.forName(NMS_ROOT_VERSIONED + ".MinecraftVersion"); + Class minecraftVersionClass = loadClass(NMS_ROOT_VERSIONED + ".MinecraftVersion"); gameVersion = minecraftVersionClass.getDeclaredMethod("a").invoke(null); } catch (ClassNotFoundException | NoSuchMethodException ex) { - Class sharedConstantsClass = Class.forName(NMS_ROOT_VERSIONED + ".SharedConstants"); + Class sharedConstantsClass = loadClass(NMS_ROOT_VERSIONED + ".SharedConstants"); gameVersion = sharedConstantsClass.getDeclaredMethod("a").invoke(null); Logging.LOGGER.warning("Failed to find Minecraft Version using MinecraftVersion.class, used SharedConstants instead"); } @@ -143,6 +146,19 @@ public class CraftBukkitServer extends CommonServerBase implements ClassPathReso return path; } + private Method getDeclaredMethod(Class declaringClass, String methodName, Class... parameterTypes) throws NoSuchMethodException { + methodName = Resolver.resolveMethodName(declaringClass, methodName, parameterTypes); + return MPLType.getDeclaredMethod(declaringClass, methodName, parameterTypes); + } + + private Class loadClass(String name) throws ClassNotFoundException { + Class type = Resolver.loadClass(name, false); + if (type == null) { + throw new ClassNotFoundException("Failed to load class " + name); + } + return type; + } + @Override public String getMinecraftVersion() { return MC_VERSION;