mirror of https://github.com/YatopiaMC/Yatopia.git
610 lines
24 KiB
Diff
610 lines
24 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Paul Sauve <paul@technove.co>
|
|
Date: Sun, 20 Dec 2020 14:08:24 -0600
|
|
Subject: [PATCH] Airplane Profiler
|
|
|
|
Airplane
|
|
Copyright (C) 2020 Technove LLC
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
diff --git a/pom.xml b/pom.xml
|
|
index 9cac82f2655c55b7374c7b23b1c65cb9958627e2..cb633e69891a57571b7e112eb8556ffaed34dcce 100644
|
|
--- a/pom.xml
|
|
+++ b/pom.xml
|
|
@@ -200,6 +200,13 @@
|
|
<version>fe3dbb4420</version>
|
|
<scope>compile</scope>
|
|
</dependency>
|
|
+ <!-- Airplane - Flare -->
|
|
+ <dependency>
|
|
+ <groupId>com.github.technove</groupId>
|
|
+ <artifactId>Flare</artifactId>
|
|
+ <version>master-SNAPSHOT</version>
|
|
+ <scope>compile</scope>
|
|
+ </dependency>
|
|
</dependencies>
|
|
|
|
<!-- This builds a completely 'ready to start' jar with all dependencies inside -->
|
|
diff --git a/src/main/java/gg/airplane/AirplaneConfig.java b/src/main/java/gg/airplane/AirplaneConfig.java
|
|
index 5077e70e4f408814b1072ceb45c52a322a7662d2..94c18e824695af69e39288195cc2fa83a13029d6 100644
|
|
--- a/src/main/java/gg/airplane/AirplaneConfig.java
|
|
+++ b/src/main/java/gg/airplane/AirplaneConfig.java
|
|
@@ -80,4 +80,27 @@ public class AirplaneConfig {
|
|
dynamicHoglinBehavior = config.getBoolean("behavior-activation.hoglin", true);
|
|
}
|
|
|
|
+
|
|
+ public static String profileWebUrl;
|
|
+
|
|
+ private static void profilerOptions() {
|
|
+ config.setComment("flare", "Configures Flare, the built-in profiler");
|
|
+
|
|
+ profileWebUrl = config.getString("flare.url", "https://flare.airplane.gg", "Sets the server to use for profiles.");
|
|
+ }
|
|
+
|
|
+
|
|
+ public static String accessToken;
|
|
+
|
|
+ private static void airplaneWebServices() {
|
|
+ config.setComment("web-services", "Options for connecting to Airplane's online utilities");
|
|
+
|
|
+ accessToken = config.getString("web-services.token", "");
|
|
+ // todo lookup token (off-thread) and let users know if their token is valid
|
|
+ if (accessToken.length() > 0) {
|
|
+ gg.airplane.flare.FlareSetup.init(); // Airplane
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
}
|
|
diff --git a/src/main/java/gg/airplane/AirplaneLogger.java b/src/main/java/gg/airplane/AirplaneLogger.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..1a9d71739019d12772bec6076b195552ff6299f9
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/AirplaneLogger.java
|
|
@@ -0,0 +1,17 @@
|
|
+package gg.airplane;
|
|
+
|
|
+import org.bukkit.Bukkit;
|
|
+
|
|
+import java.util.logging.Level;
|
|
+import java.util.logging.Logger;
|
|
+
|
|
+public class AirplaneLogger extends Logger {
|
|
+ public static final AirplaneLogger LOGGER = new AirplaneLogger();
|
|
+
|
|
+ private AirplaneLogger() {
|
|
+ super("Airplane", null);
|
|
+
|
|
+ setParent(Bukkit.getLogger());
|
|
+ setLevel(Level.ALL);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/commands/AirplaneCommands.java b/src/main/java/gg/airplane/commands/AirplaneCommands.java
|
|
index 807cf274619b8f7be839e249cb62b9817876ca04..66b20250a26d005427601b1cdee43bdd9eba70cc 100644
|
|
--- a/src/main/java/gg/airplane/commands/AirplaneCommands.java
|
|
+++ b/src/main/java/gg/airplane/commands/AirplaneCommands.java
|
|
@@ -1,10 +1,12 @@
|
|
package gg.airplane.commands;
|
|
|
|
import gg.airplane.AirplaneCommand;
|
|
+import gg.airplane.flare.FlareCommand;
|
|
import net.minecraft.server.MinecraftServer;
|
|
|
|
public class AirplaneCommands {
|
|
public static void init() {
|
|
MinecraftServer.getServer().server.getCommandMap().register("airplane", "Airplane", new AirplaneCommand());
|
|
+ MinecraftServer.getServer().server.getCommandMap().register("flare", "Airplane", new FlareCommand());
|
|
}
|
|
}
|
|
diff --git a/src/main/java/gg/airplane/compat/ServerConfigurations.java b/src/main/java/gg/airplane/compat/ServerConfigurations.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..f4976428bc721319d2926e97cbe0f64c6e9e503c
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/compat/ServerConfigurations.java
|
|
@@ -0,0 +1,77 @@
|
|
+package gg.airplane.compat;
|
|
+
|
|
+import co.aikar.timings.TimingsManager;
|
|
+import com.google.common.io.Files;
|
|
+import org.bukkit.configuration.InvalidConfigurationException;
|
|
+import org.bukkit.configuration.file.YamlConfiguration;
|
|
+
|
|
+import java.io.ByteArrayOutputStream;
|
|
+import java.io.File;
|
|
+import java.io.FileInputStream;
|
|
+import java.io.IOException;
|
|
+import java.nio.charset.StandardCharsets;
|
|
+import java.util.Arrays;
|
|
+import java.util.List;
|
|
+import java.util.Properties;
|
|
+import java.util.stream.Collectors;
|
|
+
|
|
+public class ServerConfigurations {
|
|
+
|
|
+ public static final String[] configurationFiles = new String[]{
|
|
+ "server.properties",
|
|
+ "bukkit.yml",
|
|
+ "spigot.yml",
|
|
+ "paper.yml",
|
|
+ "tuinity.yml",
|
|
+ "airplane.air"
|
|
+ };
|
|
+
|
|
+ public static String getCleanCopy(String configName) throws IOException {
|
|
+ File file = new File(configName);
|
|
+ List<String> hiddenConfigs = TimingsManager.hiddenConfigs;
|
|
+
|
|
+ if (configName.equals("airplane.air")) {
|
|
+ return Files.readLines(file, StandardCharsets.UTF_8)
|
|
+ .stream()
|
|
+ .filter(line -> !line.trim().startsWith("#"))
|
|
+ .map(line -> line.contains("token") ? " token = **" : line)
|
|
+ .collect(Collectors.joining("\n"));
|
|
+ }
|
|
+
|
|
+ switch (Files.getFileExtension(configName)) {
|
|
+ case "properties": {
|
|
+ Properties properties = new Properties();
|
|
+ try (FileInputStream inputStream = new FileInputStream(file)) {
|
|
+ properties.load(inputStream);
|
|
+ }
|
|
+ for (String hiddenConfig : hiddenConfigs) {
|
|
+ properties.remove(hiddenConfig);
|
|
+ }
|
|
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
|
+ properties.store(outputStream, "");
|
|
+ return Arrays.stream(outputStream.toString()
|
|
+ .split("\n"))
|
|
+ .filter(line -> !line.startsWith("#"))
|
|
+ .collect(Collectors.joining("\n"));
|
|
+ }
|
|
+ case "yml": {
|
|
+ YamlConfiguration configuration = new YamlConfiguration();
|
|
+ try {
|
|
+ configuration.load(file);
|
|
+ } catch (InvalidConfigurationException e) {
|
|
+ throw new IOException(e);
|
|
+ }
|
|
+ configuration.options().header(null);
|
|
+ for (String key : configuration.getKeys(true)) {
|
|
+ if (hiddenConfigs.contains(key)) {
|
|
+ configuration.set(key, null);
|
|
+ }
|
|
+ }
|
|
+ return configuration.saveToString();
|
|
+ }
|
|
+ default:
|
|
+ throw new IllegalArgumentException("Bad file type " + configName);
|
|
+ }
|
|
+ }
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/flare/FlareCommand.java b/src/main/java/gg/airplane/flare/FlareCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..ddc90f1589e683f452c5a74d9d2408803edea029
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/flare/FlareCommand.java
|
|
@@ -0,0 +1,149 @@
|
|
+package gg.airplane.flare;
|
|
+
|
|
+import com.google.common.collect.ImmutableList;
|
|
+import gg.airplane.AirplaneConfig;
|
|
+import gg.airplane.flare.exceptions.UserReportableException;
|
|
+import gg.airplane.flare.profiling.AsyncProfilerIntegration;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.kyori.adventure.text.event.ClickEvent;
|
|
+import net.kyori.adventure.text.format.NamedTextColor;
|
|
+import net.kyori.adventure.text.format.TextColor;
|
|
+import net.kyori.adventure.text.format.TextDecoration;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.command.Command;
|
|
+import org.bukkit.command.CommandSender;
|
|
+import org.bukkit.command.ConsoleCommandSender;
|
|
+import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin;
|
|
+import org.bukkit.util.StringUtil;
|
|
+
|
|
+import java.util.ArrayList;
|
|
+import java.util.Collections;
|
|
+import java.util.List;
|
|
+
|
|
+public class FlareCommand extends Command {
|
|
+
|
|
+ private static final String BASE_URL = "https://blog.airplane.gg/flare-tutorial/#setting-the-access-token";
|
|
+ private static final TextColor HEX = TextColor.fromHexString("#e3eaea");
|
|
+ private static final Component PREFIX = Component.text()
|
|
+ .append(Component.text("Flare ✈")
|
|
+ .color(TextColor.fromHexString("#6a7eda"))
|
|
+ .decoration(TextDecoration.BOLD, true)
|
|
+ .append(Component.text(" ", HEX)
|
|
+ .decoration(TextDecoration.BOLD, false)))
|
|
+ .asComponent();
|
|
+
|
|
+ public FlareCommand() {
|
|
+ super("flare", "Profile your server with Flare", "/flare", Collections.singletonList("profile"));
|
|
+ this.setPermission("airplane.flare");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean execute(CommandSender sender, String commandLabel, String[] args) {
|
|
+ if (!testPermission(sender)) return true;
|
|
+ if (AirplaneConfig.accessToken.length() == 0) {
|
|
+ Component clickable = Component.text(BASE_URL, HEX, TextDecoration.UNDERLINED).clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, BASE_URL));
|
|
+
|
|
+ sender.sendMessage(PREFIX.append(Component.text("Flare currently requires an access token to use. To learn more, visit ").color(HEX).append(clickable)));
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ if (AsyncProfilerIntegration.doesNotSupportProfiling()) {
|
|
+ sender.sendMessage(PREFIX.append(
|
|
+ Component.text("Profiling is not supported in this environment, reason: " + AsyncProfilerIntegration.getDisabledReason(), NamedTextColor.RED)));
|
|
+ return true;
|
|
+ }
|
|
+ if (ProfilingManager.isProfiling()) {
|
|
+ if (args.length == 1 && args[0].equalsIgnoreCase("status")) {
|
|
+ sender.sendMessage(PREFIX.append(Component.text("Status: " + AsyncProfilerIntegration.status(), HEX)));
|
|
+ return true;
|
|
+ }
|
|
+ if (ProfilingManager.stop()) {
|
|
+ if (!(sender instanceof ConsoleCommandSender)) {
|
|
+ sender.sendMessage(PREFIX.append(Component.text("Profiling has been stopped.", HEX)));
|
|
+ }
|
|
+ } else {
|
|
+ sender.sendMessage(PREFIX.append(Component.text("Profiling has already been stopped.", HEX)));
|
|
+ }
|
|
+ } else {
|
|
+ ProfileType profileType = null;
|
|
+ if (args.length > 0) {
|
|
+ try {
|
|
+ profileType = ProfileType.valueOf(args[0].toUpperCase());
|
|
+ } catch (Exception e) {
|
|
+ sender.sendMessage(PREFIX.append(Component
|
|
+ .text("Invalid profile type ", HEX)
|
|
+ .append(Component.text(args[0], HEX, TextDecoration.BOLD)
|
|
+ .append(Component.text("!", HEX)))
|
|
+ ));
|
|
+ }
|
|
+ }
|
|
+ int interval = 5;
|
|
+ if (args.length > 1) {
|
|
+ try {
|
|
+ interval = Integer.parseInt(args[1]);
|
|
+ } catch (Exception e) {
|
|
+ sender.sendMessage(PREFIX.append(Component
|
|
+ .text("Invalid time in milliseconds ", HEX)
|
|
+ .append(Component.text(args[1], HEX, TextDecoration.BOLD)
|
|
+ .append(Component.text("!", HEX)))
|
|
+ ));
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ int finalInterval = interval;
|
|
+ ProfileType finalProfileType = profileType;
|
|
+ Bukkit.getScheduler().runTaskAsynchronously(new MinecraftInternalPlugin(), () -> {
|
|
+ try {
|
|
+ if (ProfilingManager.start(finalProfileType, finalInterval)) {
|
|
+ if (!(sender instanceof ConsoleCommandSender)) {
|
|
+ sender.sendMessage(PREFIX.append(Component
|
|
+ .text("Flare has been started: " + ProfilingManager.getProfilingUrl().get(), HEX)
|
|
+ .clickEvent(ClickEvent.openUrl(ProfilingManager.getProfilingUrl().get()))
|
|
+ ));
|
|
+ sender.sendMessage(PREFIX.append(Component.text(" Run /" + commandLabel + " to stop the Flare.", HEX)));
|
|
+ }
|
|
+ } else {
|
|
+ sender.sendMessage(PREFIX.append(Component
|
|
+ .text("Flare has already been started: " + ProfilingManager.getProfilingUrl().get(), HEX)
|
|
+ .clickEvent(ClickEvent.openUrl(ProfilingManager.getProfilingUrl().get()))
|
|
+ ));
|
|
+ }
|
|
+ } catch (UserReportableException e) {
|
|
+ sender.sendMessage(Component.text("Flare failed to start: " + e.getUserError(), NamedTextColor.RED));
|
|
+ e.printStackTrace();
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
|
|
+ if (AsyncProfilerIntegration.doesNotSupportProfiling()) {
|
|
+ return ImmutableList.of();
|
|
+ }
|
|
+
|
|
+ List<String> list = new ArrayList<>();
|
|
+ if (AsyncProfilerIntegration.isProfiling()) {
|
|
+ if (args.length == 1) {
|
|
+ String lastWord = args[0];
|
|
+ if (StringUtil.startsWithIgnoreCase("status", lastWord)) {
|
|
+ list.add("status");
|
|
+ }
|
|
+ if (StringUtil.startsWithIgnoreCase("stop", lastWord)) {
|
|
+ list.add("stop");
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ if (args.length <= 1) {
|
|
+ String lastWord = args.length == 0 ? "" : args[0];
|
|
+ for (ProfileType value : ProfileType.values()) {
|
|
+ if (StringUtil.startsWithIgnoreCase(value.getInternalName(), lastWord)) {
|
|
+ list.add(value.name().toLowerCase());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return list;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/flare/FlareSetup.java b/src/main/java/gg/airplane/flare/FlareSetup.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..27ac32779e700494aeca8b425edb2871d3ec29cc
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/flare/FlareSetup.java
|
|
@@ -0,0 +1,146 @@
|
|
+package gg.airplane.flare;
|
|
+
|
|
+import com.google.common.cache.Cache;
|
|
+import com.google.common.cache.CacheBuilder;
|
|
+import gg.airplane.AirplaneConfig;
|
|
+import gg.airplane.AirplaneLogger;
|
|
+import gg.airplane.compat.ServerConfigurations;
|
|
+import gg.airplane.flare.profiling.AsyncProfilerIntegration;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin;
|
|
+import org.bukkit.plugin.Plugin;
|
|
+import org.bukkit.plugin.java.PluginClassLoader;
|
|
+import org.bukkit.scheduler.BukkitTask;
|
|
+
|
|
+import java.io.IOException;
|
|
+import java.util.HashMap;
|
|
+import java.util.LinkedHashMap;
|
|
+import java.util.Map;
|
|
+import java.util.concurrent.TimeUnit;
|
|
+import java.util.logging.Level;
|
|
+
|
|
+public class FlareSetup {
|
|
+
|
|
+ public static void init() {
|
|
+ ServerConnector.connector = new ServerConnector() {
|
|
+
|
|
+ private final Cache<String, String> pluginNameCache = CacheBuilder.newBuilder()
|
|
+ .expireAfterAccess(1, TimeUnit.MINUTES)
|
|
+ .maximumSize(1024)
|
|
+ .build();
|
|
+
|
|
+ @Override
|
|
+ public String getPluginForClass(String name) {
|
|
+ if (name.contains(".") && name.charAt(0) != '/') {
|
|
+ if (name.startsWith("net.minecraft") || name.startsWith("java.") || name.startsWith("com.mojang") || name.startsWith("com.google") || name.startsWith("it.unimi") || name.startsWith("sun")) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ String className = name.substring(0, name.lastIndexOf("."));
|
|
+ String existing = pluginNameCache.getIfPresent(name);
|
|
+ if (existing != null) {
|
|
+ return existing.isEmpty() ? null : existing;
|
|
+ }
|
|
+
|
|
+ String newValue = "";
|
|
+
|
|
+ for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) {
|
|
+ ClassLoader classLoader = plugin.getClass().getClassLoader();
|
|
+ if (classLoader instanceof PluginClassLoader) {
|
|
+ try {
|
|
+ Class<?> aClass = ((PluginClassLoader) classLoader)._airplane_findClass(className);
|
|
+ if (aClass != null) {
|
|
+ newValue = plugin.getName();
|
|
+ }
|
|
+ } catch (ClassNotFoundException | IllegalAccessError e) {
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pluginNameCache.put(name, newValue);
|
|
+ }
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Thread getMainThread() {
|
|
+ return MinecraftServer.getServer().serverThread;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Map<String, String> getConfigurations() {
|
|
+ Map<String, String> map = new LinkedHashMap<>();
|
|
+ for (String configurationFile : ServerConfigurations.configurationFiles) {
|
|
+ try {
|
|
+ map.put(configurationFile, ServerConfigurations.getCleanCopy(configurationFile));
|
|
+ } catch (IOException e) {
|
|
+ this.log(Level.WARNING, "Failed to load config file " + configurationFile, e);
|
|
+ }
|
|
+ }
|
|
+ return map;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void log(Level level, String s) {
|
|
+ AirplaneLogger.LOGGER.log(level, s);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void log(Level level, String s, Throwable throwable) {
|
|
+ AirplaneLogger.LOGGER.log(level, s, throwable);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public String getPrimaryVersion() {
|
|
+ return Bukkit.getVersion();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public String getApiVersion() {
|
|
+ return "bukkit:" + Bukkit.getBukkitVersion();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public String getMcVersion() {
|
|
+ return Bukkit.getMinecraftVersion();
|
|
+ }
|
|
+
|
|
+ private final Map<Runnable, BukkitTask> scheduledRunnables = new HashMap<>();
|
|
+
|
|
+ @Override
|
|
+ public void schedule(Runnable runnable, long l, long l1) {
|
|
+ BukkitTask task = Bukkit.getScheduler().runTaskTimer(new MinecraftInternalPlugin(), runnable, l, l1);
|
|
+ this.scheduledRunnables.put(runnable, task);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void scheduleAsync(Runnable runnable, long l, long l1) {
|
|
+ BukkitTask task = Bukkit.getScheduler().runTaskTimerAsynchronously(new MinecraftInternalPlugin(), runnable, l, l1);
|
|
+ this.scheduledRunnables.put(runnable, task);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void cancel(Runnable runnable) {
|
|
+ this.scheduledRunnables.get(runnable).cancel();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void runAsync(Runnable runnable) {
|
|
+ Bukkit.getScheduler().runTaskAsynchronously(new MinecraftInternalPlugin(), runnable);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public String getWebUrl() {
|
|
+ return AirplaneConfig.profileWebUrl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public String getToken() {
|
|
+ return AirplaneConfig.accessToken;
|
|
+ }
|
|
+ };
|
|
+ AsyncProfilerIntegration.init();
|
|
+ }
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/flare/ProfilingManager.java b/src/main/java/gg/airplane/flare/ProfilingManager.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..86d6650d174a7794a7ebe793cad033b42215c321
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/flare/ProfilingManager.java
|
|
@@ -0,0 +1,65 @@
|
|
+package gg.airplane.flare;
|
|
+
|
|
+import gg.airplane.AirplaneConfig;
|
|
+import gg.airplane.AirplaneLogger;
|
|
+import gg.airplane.flare.exceptions.UserReportableException;
|
|
+import gg.airplane.flare.profiling.ProfileController;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin;
|
|
+import org.bukkit.scheduler.BukkitTask;
|
|
+
|
|
+import javax.annotation.Nullable;
|
|
+import java.util.Optional;
|
|
+import java.util.logging.Level;
|
|
+
|
|
+public class ProfilingManager {
|
|
+
|
|
+ private static ProfileController currentController;
|
|
+ private static BukkitTask currentTask = null;
|
|
+
|
|
+ public static synchronized boolean isProfiling() {
|
|
+ return currentController != null;
|
|
+ }
|
|
+
|
|
+ public static synchronized Optional<String> getProfilingUrl() {
|
|
+ if (!isProfiling()) {
|
|
+ return Optional.empty();
|
|
+ }
|
|
+ return Optional.of(AirplaneConfig.profileWebUrl + "/" + currentController.getId());
|
|
+ }
|
|
+
|
|
+ public static synchronized boolean start(@Nullable ProfileType type, int interval) throws UserReportableException {
|
|
+ if (isProfiling()) {
|
|
+ return false;
|
|
+ }
|
|
+ if (Bukkit.isPrimaryThread()) {
|
|
+ throw new UserReportableException("Profiles should be started off-thread");
|
|
+ }
|
|
+ currentController = new ProfileController(type, Math.max(interval, 1)); // don't allow lower than 20ms: https://bugzilla.redhat.com/show_bug.cgi?id=645528
|
|
+ currentTask = Bukkit.getScheduler().runTaskLater(new MinecraftInternalPlugin(), ProfilingManager::stop, 20 * 60 * 15);
|
|
+ AirplaneLogger.LOGGER.log(Level.INFO, "Flare has been started: " + getProfilingUrl().orElse("An error occurred retrieving the Flare URL."));
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ public static synchronized boolean stop() {
|
|
+ if (!isProfiling()) {
|
|
+ return false;
|
|
+ }
|
|
+ AirplaneLogger.LOGGER.log(Level.INFO, "Flare has been stopped: " + getProfilingUrl().orElse("An error occurred retrieving the Flare URL."));
|
|
+ try {
|
|
+ currentController.cancel();
|
|
+ } catch (Throwable t) {
|
|
+ AirplaneLogger.LOGGER.log(Level.WARNING, "Error occurred stopping Flare", t);
|
|
+ }
|
|
+ currentController = null;
|
|
+
|
|
+ try {
|
|
+ currentTask.cancel();
|
|
+ } catch (Throwable t) {
|
|
+ AirplaneLogger.LOGGER.log(Level.WARNING, "Error occurred stopping Flare", t);
|
|
+ }
|
|
+ currentTask = null;
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java b/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java
|
|
index 49dc0c441b9dd7e7745cf15ced67f383ebee1f99..b343d8ee7435312929558efdaf127334d8e2fff6 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java
|
|
@@ -19,7 +19,8 @@ public class MinecraftInternalPlugin extends PluginBase {
|
|
private boolean enabled = true;
|
|
|
|
private final String pluginName;
|
|
- private PluginDescriptionFile pdf;
|
|
+ private org.bukkit.plugin.PluginLogger logger;
|
|
+ private PluginDescriptionFile pdf; // Airplane
|
|
|
|
public MinecraftInternalPlugin() {
|
|
this.pluginName = "Minecraft";
|
|
@@ -72,7 +73,12 @@ public class MinecraftInternalPlugin extends PluginBase {
|
|
|
|
@Override
|
|
public PluginLogger getLogger() {
|
|
- throw new UnsupportedOperationException("Not supported.");
|
|
+ // Airplane start
|
|
+ if (this.logger == null) {
|
|
+ this.logger = new org.bukkit.plugin.PluginLogger(this); // Airplane
|
|
+ }
|
|
+ return this.logger;
|
|
+ // Airplane end
|
|
}
|
|
|
|
@Override
|
|
@@ -82,7 +88,7 @@ public class MinecraftInternalPlugin extends PluginBase {
|
|
|
|
@Override
|
|
public Server getServer() {
|
|
- throw new UnsupportedOperationException("Not supported.");
|
|
+ return org.bukkit.Bukkit.getServer(); // Airplane - impl
|
|
}
|
|
|
|
@Override
|