Utils/BukkitUtils/src/main/java/eu/mikroskeem/utils/bukkit/ServerUtils.java

145 lines
6.4 KiB
Java

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 <C> double[] getTPS(){
Class<C> _t = (Class<C>)double[].class;
try {
return Reflect.wrapClass(Bukkit.class).invokeMethod("getTPS", double[].class);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
try {
Optional<ClassWrapper<?>> minecraftServerClassOpt = Reflect.getClass(String.format(
"net.minecraft.server.%s.MinecraftServer", getNmsVersion()
));
if (minecraftServerClassOpt.isPresent()) {
ClassWrapper<?> minecraftServerClass = minecraftServerClassOpt.get();
minecraftServerClass.invokeMethod("getServer", minecraftServerClass.getWrappedClass());
Optional<FieldWrapper<C>> 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<FieldWrapper<Object>> loadersFieldOpt = Reflect
.wrapClass(pl.getClass())
.setClassInstance(pl)
.getField("loaders", Object.class);
if(loadersFieldOpt.isPresent()) {
FieldWrapper<Object> loadersField = loadersFieldOpt.get();
switch (ServerUtils.getNmsVersion()){
case "v1_8_R3":
case "v1_9_R1":
case "v1_9_R2":
Map<String, ClassLoader> loaderMap = (Map<String, ClassLoader>) loadersField.read();
return loaderMap.get(plugin.getName());
case "v1_10_R1":
case "v1_11_R1":
Optional<ClassWrapper<?>> pluginClassLoaderOpt = Reflect
.getClass("org.bukkit.plugin.java.PluginClassLoader");
if(pluginClassLoaderOpt.isPresent()){
List<ClassLoader> loaders = (List<ClassLoader>) 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<String, Class<?>> 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<String, Class<?>>) 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 "";
}
}
}