package eu.mikroskeem.utils.bukkit; import eu.mikroskeem.utils.reflect.Reflect; 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.Field; import java.lang.reflect.Method; 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 */ public static double[] getTPS(){ if(Reflect.getMethod(Bukkit.class, "getTps") != null) { return Bukkit.getTPS(); } else { try { Class MinecraftServerClass = checkNotNull(Reflect.getClass( String.format("net.minecraft.server.%s.MinecraftServer", getNmsVersion()))); Method getServer = checkNotNull(Reflect.getMethod(MinecraftServerClass, "getServer")); Field recentTPS = checkNotNull(Reflect.getField(MinecraftServerClass, "recentTps")); Object server = checkNotNull(Reflect.invokeMethod(getServer, null)); return (double[]) checkNotNull(Reflect.readField(recentTPS, server)); } catch (NullPointerException 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 { Field loadersField = checkNotNull(Reflect.getField(pl.getClass(), "loaders")); switch (getNmsVersion()){ case "v1_8_R3": case "v1_9_R1": case "v1_9_R2": try { Map loaderMap = (Map) checkNotNull(Reflect.readField(loadersField, pl)); return loaderMap.get(plugin.getName()); } catch (NullPointerException ignored){} break; case "v1_10_R1": case "v1_11_R1": Class pluginClassLoader = checkNotNull(Reflect.getClass( "org.bukkit.plugin.java.PluginClassLoader")); List loaders = (List) checkNotNull(Reflect.readField(loadersField, pl)); Optional loader = loaders.stream() .filter(l->{ Field f = checkNotNull(Reflect.getField(pluginClassLoader, "plugin")); return checkNotNull(((JavaPlugin)Reflect.readField(f, l))) .getName().equals(plugin.getName()); }).findFirst(); return loader.isPresent()?loader.get():null; } } catch (NullPointerException ignored){} return null; } /** * Get PluginClassLoader classes map * @param classLoader PluginClassLoader instance * @return Classes map * @throws RuntimeException if ClassLoader doesn't extend PluginClassLoader */ @SuppressWarnings("unchecked") @Nullable public static Map> getClassesMap(ClassLoader classLoader){ Class clClass = classLoader.getClass(); try { Class pluginClassLoader = checkNotNull(Reflect.getClass( "org.bukkit.plugin.java.PluginClassLoader"), "Couldn't find org.bukkit.plugin.java.PluginClassLoader class"); if(pluginClassLoader.isAssignableFrom(clClass)){ Field classesField = checkNotNull(Reflect.getField(clClass, "classes"), "Failed to get classes field"); return (Map>) checkNotNull(Reflect.readField(classesField, classLoader)); } else { throw new RuntimeException( clClass.getName() + " does not extend " + pluginClassLoader.getName()); } } catch(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 ""; } } }