package eu.mikroskeem.utils.bukkit; import eu.mikroskeem.shuriken.instrumentation.validate.ClassDescriptor; import eu.mikroskeem.shuriken.instrumentation.validate.FieldDescriptor; import eu.mikroskeem.shuriken.instrumentation.validate.Validate; import eu.mikroskeem.shuriken.reflect.Reflect; import eu.mikroskeem.shuriken.reflect.wrappers.ClassWrapper; import eu.mikroskeem.shuriken.reflect.wrappers.FieldWrapper; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPluginLoader; import org.jetbrains.annotations.Nullable; import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.Map; import java.util.Optional; import static com.google.common.base.Preconditions.checkNotNull; public class ServerUtils { /** * Get server's TPS * * Will use PaperSpigot's API if available, reflection otherwise * @return double array of TPS (not rounded!), values are -1 if reflection failed */ @SuppressWarnings("unchecked") public static double[] getTPS(){ Class _t = (Class)double[].class; try { return Reflect.wrapClass(Bukkit.class).invokeMethod("getTPS", double[].class); } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { try { Optional> minecraftServerClassOpt = Reflect.getClass(String.format( "net.minecraft.server.%s.MinecraftServer", getNmsVersion() )); if (minecraftServerClassOpt.isPresent()) { ClassWrapper minecraftServerClass = minecraftServerClassOpt.get(); minecraftServerClass.invokeMethod("getServer", minecraftServerClass.getWrappedClass()); Optional> recentTpsOpt = minecraftServerClass.getField("recentTps", _t); if(recentTpsOpt.isPresent()) return (double[]) recentTpsOpt.get().read(); } } catch (NoSuchMethodException|NoSuchFieldException|InvocationTargetException|IllegalAccessException ignored){} } return new double[]{-1, -1, -1}; } /** * Get plugin class loader. Useful when you want to add new classes on runtime * * @param plugin Plugin which will be used to get plugin ClassLoader * @return Plugin ClassLoader */ @SuppressWarnings("unchecked") @Nullable public static ClassLoader getPluginClassLoader(JavaPlugin plugin){ /* * Note: CB/Spigot has PluginClassLoader package-private, PaperSpigot has public * So *DO NOT* try to cast this ClassLoader to PluginClassLoader, if you aim to be * compatible with other Bukkit-based servers * I repeat, *DO NOT USE* PluginClassLoader! */ JavaPluginLoader pl = (JavaPluginLoader) plugin.getPluginLoader(); try { Optional> loadersFieldOpt = Reflect .wrapClass(pl.getClass()) .setClassInstance(pl) .getField("loaders", Object.class); if(loadersFieldOpt.isPresent()) { FieldWrapper loadersField = loadersFieldOpt.get(); switch (ServerUtils.getNmsVersion()){ case "v1_8_R3": case "v1_9_R1": case "v1_9_R2": Map loaderMap = (Map) loadersField.read(); return loaderMap.get(plugin.getName()); case "v1_10_R1": case "v1_11_R1": Optional> pluginClassLoaderOpt = Reflect .getClass("org.bukkit.plugin.java.PluginClassLoader"); if(pluginClassLoaderOpt.isPresent()){ List loaders = (List) loadersField.read(); return loaders.stream().filter(loader -> { try { String pluginName = Reflect.wrapInstance(loader) .getField("plugin", JavaPlugin.class) .get() .read() .getName(); return pluginName.equals(plugin.getName()); } catch (NoSuchFieldException|IllegalAccessException|NullPointerException ignored){} return false; }).findFirst().orElse(null); } break; } } } catch (NoSuchFieldException|IllegalAccessException ignored){} return null; } /** * Get PluginClassLoader classes map * @param classLoader PluginClassLoader instance * @return Classes map */ @SuppressWarnings("unchecked") @Nullable public static Map> getClassesMap(ClassLoader classLoader){ Class clClass = classLoader.getClass(); try { ClassWrapper pluginClassLoader = checkNotNull(Reflect .getClass("org.bukkit.plugin.java.PluginClassLoader").orElse(null), "Couldn't find org.bukkit.plugin.java.PluginClassLoader class") .setClassInstance(classLoader); Validate.checkFields(pluginClassLoader, FieldDescriptor.of("classes", Map.class)); Validate.checkClass(ClassDescriptor.ofWrapped(clClass, pluginClassLoader)); return (Map>) checkNotNull(pluginClassLoader.getField("classes", Map.class) .orElse(null), "Failed to get classes field").read(); } catch(NoSuchFieldException|IllegalAccessException|NullPointerException e){ e.printStackTrace(); } return null; } /** * Get NMS version of current running server * @return NMS version or 0 length string */ public static String getNmsVersion(){ try { String bukkitPackageName = Bukkit.getServer().getClass().getPackage().getName(); return bukkitPackageName.substring(bukkitPackageName.lastIndexOf(".") + 1); } catch (NullPointerException e){ return ""; } } }