PluginBridge

Class lookup across the classloaders of a hybrid server (Silkard, Youer, ...), reached from MaskingClassLoader.SnippetLoader once nothing in the host has the name.

Reflective throughout: common links only the JDK and Minecraft, while Bukkit either exists on the game loader at runtime or does not exist at all. The reflective handles are resolved once; the plugin list never is — it is read live per lookup, so enabling, disabling or reloading a plugin needs no invalidation.

There are more loaders than plugins. Two shapes hang one off the side of a plugin loader, both CHILDREN of it, so walking parents misses both: libraryLoader (the plugin's Maven libraries:, a field on the loader) and jar-in-jar (a nested implementation jar extracted at startup, then fed downloaded dependencies at runtime — only the plugin object holds it). collectFieldLoaders finds both by reading each field VALUE and asking which loader defined it. Not the field TYPE: LuckPerms declares its bootstrap field as LoaderBootstrap, so a type-based search finds nothing.

Only loaders the host cannot already reach belong here — this runs last, after masking, the game loader and its parents have all missed. The scan subtracts that chain explicitly, because a plugin loader's JavaPluginLoader field is defined by the server loader: left in, it drags the whole server classpath along behind it.

Constructors

Link copied to clipboard
private constructor()

Types

Link copied to clipboard
private interface Plugins
Live view of the loaders behind the currently loaded plugins.
Link copied to clipboard
private class Scan : Record
The last expand, keyed by the plugin loaders it was derived from.

Properties

Link copied to clipboard
private open var plugins: PluginBridge.Plugins
Link copied to clipboard
private open var probed: Boolean
Link copied to clipboard
private open var scan: PluginBridge.Scan

Functions

Link copied to clipboard
private open fun collectFieldLoaders(o: Any, stopAt: Class<out Any>, out: Set<ClassLoader>)
Every loader one field deep from o: per field, the loader that defined its value — or the value itself, when it already is a loader.
Link copied to clipboard
private open fun detect(): PluginBridge.Plugins
Bukkit's own PluginManager covers both plugin families: on Paper the legacy SimplePluginManager forwards getPlugins() to the Paper implementation, whose list holds legacy and Paper plugins alike.
Link copied to clipboard
private open fun expand(found: Array<Any>, owners: List<ClassLoader>, host: Set<ClassLoader>): List<ClassLoader>
The plugin loaders plus whatever hangs off their side, cached against the plugin loaders themselves.
Link copied to clipboard
open fun load(name: String): Class<out Any>
The class as one of those loaders DEFINES it, or null when none does — including the ordinary mod runtime, where there are no plugins at all.
Link copied to clipboard
open fun loaders(): List<ClassLoader>
The same loaders load searches, for PluginJarCollector's compile classpath.
Link copied to clipboard
private open fun plugins(): PluginBridge.Plugins