|
| 1 | +package io.github.projectunified.faststats.bukkit; |
| 2 | + |
| 3 | +import io.github.projectunified.faststats.core.Config; |
| 4 | +import io.github.projectunified.faststats.core.DefaultConfig; |
| 5 | +import io.github.projectunified.faststats.core.Metric; |
| 6 | +import io.github.projectunified.faststats.core.Platform; |
| 7 | +import org.bukkit.Server; |
| 8 | +import org.bukkit.plugin.Plugin; |
| 9 | + |
| 10 | +import java.io.File; |
| 11 | +import java.lang.invoke.MethodHandle; |
| 12 | +import java.lang.invoke.MethodHandles; |
| 13 | +import java.nio.file.Path; |
| 14 | +import java.util.ArrayList; |
| 15 | +import java.util.Collection; |
| 16 | +import java.util.List; |
| 17 | +import java.util.logging.Level; |
| 18 | + |
| 19 | +/** |
| 20 | + * Bukkit implementation of {@link Platform}. |
| 21 | + */ |
| 22 | +public class BukkitPlatform implements Platform { |
| 23 | + private static final MethodHandle GET_PLUGINS_FOLDER; |
| 24 | + private static final MethodHandle SPIGOT; |
| 25 | + private static final MethodHandle GET_MINECRAFT_VERSION; |
| 26 | + private static final MethodHandle GET_SERVER_CONFIG; |
| 27 | + private static final MethodHandle GET_PLUGIN_META; |
| 28 | + private static final MethodHandle GET_PAPER_CONFIG; |
| 29 | + private static final MethodHandle GET_SPIGOT_CONFIG; |
| 30 | + private static final MethodHandle GET_CONFIGURATION_SECTION; |
| 31 | + private static final MethodHandle GET_BOOLEAN; |
| 32 | + |
| 33 | + static { |
| 34 | + MethodHandles.Lookup lookup = MethodHandles.publicLookup(); |
| 35 | + MethodHandle getPluginsFolder = null; |
| 36 | + MethodHandle spigot = null; |
| 37 | + MethodHandle getMinecraftVersion = null; |
| 38 | + MethodHandle getServerConfig = null; |
| 39 | + MethodHandle getPluginMeta = null; |
| 40 | + MethodHandle getPaperConfig = null; |
| 41 | + MethodHandle getSpigotConfig = null; |
| 42 | + MethodHandle getConfigurationSection = null; |
| 43 | + MethodHandle getBoolean = null; |
| 44 | + |
| 45 | + try { |
| 46 | + getPluginsFolder = lookup.unreflect(Server.class.getMethod("getPluginsFolder")); |
| 47 | + } catch (Throwable ignored) { |
| 48 | + } |
| 49 | + |
| 50 | + try { |
| 51 | + spigot = lookup.unreflect(Server.class.getMethod("spigot")); |
| 52 | + } catch (Throwable ignored) { |
| 53 | + } |
| 54 | + |
| 55 | + try { |
| 56 | + getMinecraftVersion = lookup.unreflect(Server.class.getMethod("getMinecraftVersion")); |
| 57 | + } catch (Throwable ignored) { |
| 58 | + } |
| 59 | + |
| 60 | + try { |
| 61 | + getServerConfig = lookup.unreflect(Server.class.getMethod("getServerConfig")); |
| 62 | + } catch (Throwable ignored) { |
| 63 | + } |
| 64 | + |
| 65 | + try { |
| 66 | + getPluginMeta = lookup.unreflect(Plugin.class.getMethod("getPluginMeta")); |
| 67 | + } catch (Throwable ignored) { |
| 68 | + } |
| 69 | + |
| 70 | + try { |
| 71 | + Class<?> configSectionClass = Class.forName("org.bukkit.configuration.ConfigurationSection"); |
| 72 | + getConfigurationSection = lookup.unreflect(configSectionClass.getMethod("getConfigurationSection", String.class)); |
| 73 | + getBoolean = lookup.unreflect(configSectionClass.getMethod("getBoolean", String.class)); |
| 74 | + } catch (Throwable ignored) { |
| 75 | + } |
| 76 | + |
| 77 | + try { |
| 78 | + Class<?> spigotClass = Class.forName("org.bukkit.Server$Spigot"); |
| 79 | + getPaperConfig = lookup.unreflect(spigotClass.getMethod("getPaperConfig")); |
| 80 | + getSpigotConfig = lookup.unreflect(spigotClass.getMethod("getSpigotConfig")); |
| 81 | + } catch (Throwable ignored) { |
| 82 | + } |
| 83 | + |
| 84 | + GET_PLUGINS_FOLDER = getPluginsFolder; |
| 85 | + SPIGOT = spigot; |
| 86 | + GET_MINECRAFT_VERSION = getMinecraftVersion; |
| 87 | + GET_SERVER_CONFIG = getServerConfig; |
| 88 | + GET_PLUGIN_META = getPluginMeta; |
| 89 | + GET_PAPER_CONFIG = getPaperConfig; |
| 90 | + GET_SPIGOT_CONFIG = getSpigotConfig; |
| 91 | + GET_CONFIGURATION_SECTION = getConfigurationSection; |
| 92 | + GET_BOOLEAN = getBoolean; |
| 93 | + } |
| 94 | + |
| 95 | + private final Plugin plugin; |
| 96 | + private final Config config; |
| 97 | + private final List<Metric<?>> defaultMetrics; |
| 98 | + |
| 99 | + /** |
| 100 | + * Constructs a new {@link BukkitPlatform} for the given plugin. |
| 101 | + * |
| 102 | + * @param plugin the plugin instance |
| 103 | + */ |
| 104 | + public BukkitPlatform(Plugin plugin) { |
| 105 | + this.plugin = plugin; |
| 106 | + |
| 107 | + File pluginsFolder = null; |
| 108 | + try { |
| 109 | + if (GET_PLUGINS_FOLDER != null) { |
| 110 | + pluginsFolder = (File) GET_PLUGINS_FOLDER.invoke(plugin.getServer()); |
| 111 | + } |
| 112 | + } catch (Throwable ignored) { |
| 113 | + } |
| 114 | + |
| 115 | + if (pluginsFolder == null) { |
| 116 | + pluginsFolder = plugin.getDataFolder().getParentFile(); |
| 117 | + } |
| 118 | + |
| 119 | + Path configPath = pluginsFolder.toPath().resolve("faststats").resolve("config.properties"); |
| 120 | + this.config = DefaultConfig.read(configPath); |
| 121 | + |
| 122 | + this.defaultMetrics = new ArrayList<>(); |
| 123 | + setupDefaultMetrics(); |
| 124 | + } |
| 125 | + |
| 126 | + private void setupDefaultMetrics() { |
| 127 | + final Server server = plugin.getServer(); |
| 128 | + |
| 129 | + // Minecraft Version |
| 130 | + defaultMetrics.add(Metric.string("minecraft_version", () -> { |
| 131 | + try { |
| 132 | + if (GET_MINECRAFT_VERSION != null) { |
| 133 | + return (String) GET_MINECRAFT_VERSION.invoke(server); |
| 134 | + } |
| 135 | + } catch (Throwable ignored) { |
| 136 | + } |
| 137 | + |
| 138 | + try { |
| 139 | + return server.getBukkitVersion().split("-", 2)[0]; |
| 140 | + } catch (Exception ex) { |
| 141 | + return server.getVersion().split("\\(MC: |\\)", 3)[1]; |
| 142 | + } |
| 143 | + })); |
| 144 | + |
| 145 | + // Online Mode |
| 146 | + defaultMetrics.add(Metric.bool("online_mode", () -> { |
| 147 | + try { |
| 148 | + if (GET_SERVER_CONFIG != null) { |
| 149 | + Object serverConfig = GET_SERVER_CONFIG.invoke(server); |
| 150 | + MethodHandle isProxyOnlineMode = MethodHandles.publicLookup() |
| 151 | + .unreflect(serverConfig.getClass().getMethod("isProxyOnlineMode")); |
| 152 | + return (Boolean) isProxyOnlineMode.invoke(serverConfig); |
| 153 | + } |
| 154 | + } catch (Throwable ignored) { |
| 155 | + } |
| 156 | + |
| 157 | + try { |
| 158 | + return isProxyOnlineMode(); |
| 159 | + } catch (Exception ex) { |
| 160 | + return server.getOnlineMode(); |
| 161 | + } |
| 162 | + })); |
| 163 | + |
| 164 | + // Player Count |
| 165 | + defaultMetrics.add(Metric.number("player_count", () -> { |
| 166 | + try { |
| 167 | + return server.getOnlinePlayers().size(); |
| 168 | + } catch (Throwable t) { |
| 169 | + return 0; |
| 170 | + } |
| 171 | + })); |
| 172 | + |
| 173 | + // Plugin Version |
| 174 | + defaultMetrics.add(Metric.string("plugin_version", () -> { |
| 175 | + try { |
| 176 | + if (GET_PLUGIN_META != null) { |
| 177 | + Object pluginMeta = GET_PLUGIN_META.invoke(plugin); |
| 178 | + MethodHandle getVersion = MethodHandles.publicLookup() |
| 179 | + .unreflect(pluginMeta.getClass().getMethod("getVersion")); |
| 180 | + return (String) getVersion.invoke(pluginMeta); |
| 181 | + } |
| 182 | + } catch (Throwable ignored) { |
| 183 | + } |
| 184 | + |
| 185 | + return plugin.getDescription().getVersion(); |
| 186 | + })); |
| 187 | + |
| 188 | + // Server Type |
| 189 | + defaultMetrics.add(Metric.string("server_type", server::getName)); |
| 190 | + } |
| 191 | + |
| 192 | + private boolean isProxyOnlineMode() { |
| 193 | + if (SPIGOT == null) { |
| 194 | + return false; |
| 195 | + } |
| 196 | + |
| 197 | + Server server = plugin.getServer(); |
| 198 | + Object spigot; |
| 199 | + try { |
| 200 | + spigot = SPIGOT.invoke(server); |
| 201 | + } catch (Throwable e) { |
| 202 | + return false; |
| 203 | + } |
| 204 | + |
| 205 | + Object proxies = null; |
| 206 | + try { |
| 207 | + if (GET_PAPER_CONFIG != null && GET_CONFIGURATION_SECTION != null && GET_BOOLEAN != null) { |
| 208 | + Object paperConfig = GET_PAPER_CONFIG.invoke(spigot); |
| 209 | + proxies = GET_CONFIGURATION_SECTION.invoke(paperConfig, "proxies"); |
| 210 | + if (proxies != null) { |
| 211 | + boolean velocityEnabled = (boolean) GET_BOOLEAN.invoke(proxies, "velocity.enabled"); |
| 212 | + boolean velocityOnline = (boolean) GET_BOOLEAN.invoke(proxies, "velocity.online-mode"); |
| 213 | + if (velocityEnabled && velocityOnline) { |
| 214 | + return true; |
| 215 | + } |
| 216 | + } |
| 217 | + } |
| 218 | + } catch (Throwable ignored) { |
| 219 | + } |
| 220 | + |
| 221 | + try { |
| 222 | + if (GET_SPIGOT_CONFIG != null && GET_CONFIGURATION_SECTION != null && GET_BOOLEAN != null) { |
| 223 | + Object spigotConfig = GET_SPIGOT_CONFIG.invoke(spigot); |
| 224 | + Object settings = GET_CONFIGURATION_SECTION.invoke(spigotConfig, "settings"); |
| 225 | + if (settings != null) { |
| 226 | + boolean bungee = (boolean) GET_BOOLEAN.invoke(settings, "bungeecord"); |
| 227 | + if (bungee && proxies != null) { |
| 228 | + return (boolean) GET_BOOLEAN.invoke(proxies, "bungee-cord.online-mode"); |
| 229 | + } |
| 230 | + } |
| 231 | + } |
| 232 | + } catch (Throwable ignored) { |
| 233 | + } |
| 234 | + |
| 235 | + return false; |
| 236 | + } |
| 237 | + |
| 238 | + @Override |
| 239 | + public Config getConfig() { |
| 240 | + return config; |
| 241 | + } |
| 242 | + |
| 243 | + @Override |
| 244 | + public Collection<Metric<?>> getMetrics() { |
| 245 | + return defaultMetrics; |
| 246 | + } |
| 247 | + |
| 248 | + @Override |
| 249 | + public void logInfo(String message) { |
| 250 | + plugin.getLogger().info(message); |
| 251 | + } |
| 252 | + |
| 253 | + @Override |
| 254 | + public void logWarning(String message) { |
| 255 | + plugin.getLogger().warning(message); |
| 256 | + } |
| 257 | + |
| 258 | + @Override |
| 259 | + public void logError(String message, Throwable throwable) { |
| 260 | + plugin.getLogger().log(Level.SEVERE, message, throwable); |
| 261 | + } |
| 262 | +} |
0 commit comments