From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Riley Park Date: Tue, 16 Jul 2024 14:55:23 -0700 Subject: [PATCH] Bundle spark diff --git a/build.gradle.kts b/build.gradle.kts index 1a734293c9416f13324bb0edf8f950c9029f8bc4..568d0b65bb5010e0ab365cb47b624720807a1f63 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -61,6 +61,10 @@ dependencies { implementation("io.papermc:reflection-rewriter-runtime:$reflectionRewriterVersion") implementation("io.papermc:reflection-rewriter-proxy-generator:$reflectionRewriterVersion") // Paper end - Remap reflection + // Paper start - spark + implementation("me.lucko:spark-api:0.1-SNAPSHOT") + implementation("me.lucko:spark-paper:1.10.83-SNAPSHOT") + // Paper end - spark } paperweight { diff --git a/src/main/java/io/papermc/paper/SparksFly.java b/src/main/java/io/papermc/paper/SparksFly.java new file mode 100644 index 0000000000000000000000000000000000000000..d589ae2aa89f386d96e4ff51dbc9f2aae1703598 --- /dev/null +++ b/src/main/java/io/papermc/paper/SparksFly.java @@ -0,0 +1,200 @@ +package io.papermc.paper; + +import io.papermc.paper.configuration.GlobalConfiguration; +import io.papermc.paper.plugin.ClassLoaderStorage; +import io.papermc.paper.plugin.entrypoint.classloader.PaperSimplePluginClassLoader; +import io.papermc.paper.util.MCUtil; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import me.lucko.spark.paper.api.Compatibility; +import me.lucko.spark.paper.api.PaperClassLookup; +import me.lucko.spark.paper.api.PaperScheduler; +import me.lucko.spark.paper.api.PaperSparkModule; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.TextColor; +import org.bukkit.Server; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.plugin.java.PluginClassLoader; + +// It's like electricity. +public final class SparksFly { + public static final String ID = "spark"; + public static final String COMMAND_NAME = "spark"; + + private static final String PREFER_SPARK_PLUGIN_PROPERTY = "paper.preferSparkPlugin"; + + private static final int SPARK_YELLOW = 0xffc93a; + + private final Logger logger; + private final PaperSparkModule spark; + + private boolean enabled; + private boolean disabledInConfigurationWarningLogged; + + public SparksFly(final Server server) { + this.logger = Logger.getLogger(ID); + this.logger.log(Level.INFO, "This server bundles the spark profiler. For more information please visit https://docs.papermc.io/paper/profiling"); + this.spark = PaperSparkModule.create(Compatibility.VERSION_1_0, server, this.logger, new PaperScheduler() { + @Override + public void executeAsync(final Runnable runnable) { + MCUtil.scheduleAsyncTask(this.catching(runnable, "asynchronous")); + } + + @Override + public void executeSync(final Runnable runnable) { + MCUtil.ensureMain(this.catching(runnable, "synchronous")); + } + + private Runnable catching(final Runnable runnable, final String type) { + return () -> { + try { + runnable.run(); + } catch (final Throwable t) { + SparksFly.this.logger.log(Level.SEVERE, "An exception was encountered while executing a " + type + " spark task", t); + } + }; + } + }, new PaperClassLookup() { + @Override + public Class lookup(final String className) throws Exception { + try { + return Class.forName(className); + } catch (final Exception e) { + for (final PaperSimplePluginClassLoader loader : ClassLoaderStorage.MODERN_LOADERS) { + final Class loadedClass = loader.loadClass(className); + if (loadedClass != null) { + return loadedClass; + } + } + for (final PluginClassLoader loader : ClassLoaderStorage.LEGACY_LOADERS) { + final Class loadedClass = loader.loadClass(className, true, false, true); + if (loadedClass != null) { + return loadedClass; + } + } + final ClassNotFoundException exception = new ClassNotFoundException("Could not find class " + className); + exception.addSuppressed(e); + throw exception; + } + } + }); + } + + public void enableEarlyIfRequested() { + if (!isPluginPreferred() && shouldEnableImmediately()) { + this.enable(); + } + } + + public void enableBeforePlugins() { + if (!isPluginPreferred()) { + this.enable(); + } + } + + public void enableAfterPlugins(final Server server) { + final boolean isPluginPreferred = isPluginPreferred(); + final boolean isPluginEnabled = isPluginEnabled(server); + if (!isPluginPreferred || !isPluginEnabled) { + if (isPluginPreferred && !this.enabled) { + this.logger.log(Level.INFO, "The spark plugin has been preferred but was not loaded. The bundled spark profiler will enabled instead."); + } + this.enable(); + } + } + + private void enable() { + if (!this.enabled) { + if (GlobalConfiguration.get().spark.enabled) { + this.enabled = true; + this.spark.enable(); + } else { + if (!this.disabledInConfigurationWarningLogged) { + this.logger.log(Level.INFO, "The spark profiler will not be enabled because it is currently disabled in the configuration."); + this.disabledInConfigurationWarningLogged = true; + } + } + } + } + + public void disable() { + if (this.enabled) { + this.spark.disable(); + this.enabled = false; + } + } + + public void registerCommandBeforePlugins(final Server server) { + if (!isPluginPreferred()) { + this.registerCommand(server); + } + } + + public void registerCommandAfterPlugins(final Server server) { + if ((!isPluginPreferred() || !isPluginEnabled(server)) && server.getCommandMap().getCommand(COMMAND_NAME) == null) { + this.registerCommand(server); + } + } + + private void registerCommand(final Server server) { + server.getCommandMap().register(COMMAND_NAME, "paper", new CommandImpl(COMMAND_NAME)); + } + + public void tickStart() { + this.spark.onServerTickStart(); + } + + public void tickEnd(final double duration) { + this.spark.onServerTickEnd(duration); + } + + void executeCommand(final CommandSender sender, final String[] args) { + this.spark.executeCommand(sender, args); + } + + List tabComplete(final CommandSender sender, final String[] args) { + return this.spark.tabComplete(sender, args); + } + + public static boolean isPluginPreferred() { + return Boolean.getBoolean(PREFER_SPARK_PLUGIN_PROPERTY); + } + + private static boolean isPluginEnabled(final Server server) { + return server.getPluginManager().isPluginEnabled(ID); + } + + private static boolean shouldEnableImmediately() { + return GlobalConfiguration.get().spark.enableImmediately; + } + + public static final class CommandImpl extends Command { + CommandImpl(final String name) { + super(name); + this.setPermission("spark"); + } + + @Override + public boolean execute(final CommandSender sender, final String commandLabel, final String[] args) { + final SparksFly spark = ((CraftServer) sender.getServer()).spark; + if (spark.enabled) { + spark.executeCommand(sender, args); + } else { + sender.sendMessage(Component.text("The spark profiler is currently disabled.", TextColor.color(SPARK_YELLOW))); + } + return true; + } + + @Override + public List tabComplete(final CommandSender sender, final String alias, final String[] args) throws IllegalArgumentException { + final SparksFly spark = ((CraftServer) sender.getServer()).spark; + if (spark.enabled) { + return spark.tabComplete(sender, args); + } + return List.of(); + } + } +} diff --git a/src/main/java/io/papermc/paper/plugin/ClassLoaderStorage.java b/src/main/java/io/papermc/paper/plugin/ClassLoaderStorage.java new file mode 100644 index 0000000000000000000000000000000000000000..cb080d89b3be899de5007c799e6ef40809f2ef61 --- /dev/null +++ b/src/main/java/io/papermc/paper/plugin/ClassLoaderStorage.java @@ -0,0 +1,11 @@ +package io.papermc.paper.plugin; + +import io.papermc.paper.plugin.entrypoint.classloader.PaperSimplePluginClassLoader; +import java.util.HashSet; +import java.util.Set; +import org.bukkit.plugin.java.PluginClassLoader; + +public final class ClassLoaderStorage { + public static final Set MODERN_LOADERS = new HashSet<>(); + public static final Set LEGACY_LOADERS = new HashSet<>(); +} diff --git a/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/PaperSimplePluginClassLoader.java b/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/PaperSimplePluginClassLoader.java index 967465e542483e93a736129b5f5c6622cefd33fa..f8094525064b46fe78f5b5f32ea53b234b4d525d 100644 --- a/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/PaperSimplePluginClassLoader.java +++ b/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/PaperSimplePluginClassLoader.java @@ -1,5 +1,6 @@ package io.papermc.paper.plugin.entrypoint.classloader; +import io.papermc.paper.plugin.ClassLoaderStorage; import io.papermc.paper.plugin.configuration.PluginMeta; import io.papermc.paper.plugin.util.NamespaceChecker; import org.jetbrains.annotations.ApiStatus; @@ -40,6 +41,8 @@ public class PaperSimplePluginClassLoader extends URLClassLoader { this.jarUrl = source.toUri().toURL(); this.configuration = configuration; this.jar = file; + + ClassLoaderStorage.MODERN_LOADERS.add(this); // Paper - spark } @Override diff --git a/src/main/java/io/papermc/paper/plugin/provider/source/FileProviderSource.java b/src/main/java/io/papermc/paper/plugin/provider/source/FileProviderSource.java index 6b8ed8a0baaf4a57d20e57cec3400af5561ddd79..48604e7f96adc9e226e034054c5e2bad0b024eb5 100644 --- a/src/main/java/io/papermc/paper/plugin/provider/source/FileProviderSource.java +++ b/src/main/java/io/papermc/paper/plugin/provider/source/FileProviderSource.java @@ -1,6 +1,9 @@ package io.papermc.paper.plugin.provider.source; +import com.mojang.logging.LogUtils; +import io.papermc.paper.SparksFly; import io.papermc.paper.plugin.PluginInitializerManager; +import io.papermc.paper.plugin.configuration.PluginMeta; import io.papermc.paper.plugin.entrypoint.EntrypointHandler; import io.papermc.paper.plugin.provider.type.PluginFileType; import org.bukkit.plugin.InvalidPluginException; @@ -17,12 +20,14 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.Set; import java.util.function.Function; import java.util.jar.JarFile; +import org.slf4j.Logger; /** * Loads a plugin provider at the given plugin jar file path. */ public class FileProviderSource implements ProviderSource { + private static final Logger LOGGER = LogUtils.getClassLogger(); private final Function contextChecker; private final boolean applyRemap; @@ -82,6 +87,12 @@ public class FileProviderSource implements ProviderSource { ); } + final PluginMeta config = type.getConfig(file); + if ((config.getName().equals("spark") && config.getMainClass().equals("me.lucko.spark.bukkit.BukkitSparkPlugin")) && !SparksFly.isPluginPreferred()) { + LOGGER.info("The spark plugin will not be loaded as this server bundles the spark profiler."); + return; + } + type.register(entrypointHandler, file, context); } diff --git a/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProvider.java b/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProvider.java index 0fd1040ed376f19c6d5326767baaf3048ce1bfb4..a9fd3f34aca19b1cfada5b771b3d46a2cfca4c51 100644 --- a/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProvider.java +++ b/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProvider.java @@ -2,6 +2,7 @@ package io.papermc.paper.plugin.provider.type.spigot; import com.destroystokyo.paper.util.SneakyThrow; import com.destroystokyo.paper.utils.PaperPluginLogger; +import io.papermc.paper.plugin.ClassLoaderStorage; import io.papermc.paper.plugin.manager.PaperPluginManagerImpl; import io.papermc.paper.plugin.provider.configuration.LoadOrderConfiguration; import io.papermc.paper.plugin.provider.entrypoint.DependencyContext; @@ -133,6 +134,7 @@ public class SpigotPluginProvider implements PluginProvider, Provide // We must provide a temporary context in order to properly handle dependencies on the plugin classloader constructor. loader.dependencyContext = PaperPluginManagerImpl.getInstance(); + ClassLoaderStorage.LEGACY_LOADERS.add(loader); // Paper - spark this.status = ProviderStatus.INITIALIZED; return loader.getPlugin(); diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index 8160c35368fc2c52d6f4a42df27adb2ef6eb87f3..9325d6f95165a7cee00d7de736af723681cc16b4 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -751,6 +751,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop