Add initial multi plugin management

This commit is contained in:
Frank van der Heijden 2021-07-29 15:08:42 +02:00
parent df55162d73
commit 94e4693b5e
No known key found for this signature in database
GPG key ID: B808721C2DD5B5B8
54 changed files with 1988 additions and 937 deletions

View file

@ -21,16 +21,15 @@ public class VelocityCommandServerUtils
extends CommandServerUtils<VelocityPlugin, PluginContainer, VelocityCommandSender> {
public VelocityCommandServerUtils(VelocityPlugin plugin) {
super(plugin);
super(plugin, PluginContainer[]::new);
}
@Override
protected FormatBuilder createPluginInfo(
FormatBuilder builder,
Function<Consumer<ListBuilder<String>>, String> listBuilderFunction,
String pluginName
PluginContainer container
) {
PluginContainer container = plugin.getPluginManager().getPlugin(pluginName);
PluginDescription desc = container.getDescription();
return builder

View file

@ -1,20 +0,0 @@
package net.frankheijden.serverutils.velocity.entities;
import com.velocitypowered.api.plugin.PluginContainer;
import net.frankheijden.serverutils.common.entities.LoadResult;
import net.frankheijden.serverutils.common.entities.Result;
public class VelocityLoadResult extends LoadResult<PluginContainer> {
public VelocityLoadResult(PluginContainer obj, Result result) {
super(obj, result);
}
public VelocityLoadResult(PluginContainer obj) {
super(obj);
}
public VelocityLoadResult(Result result) {
super(result);
}
}

View file

@ -15,12 +15,7 @@ import net.frankheijden.serverutils.velocity.listeners.VelocityPlayerListener;
import net.frankheijden.serverutils.velocity.managers.VelocityPluginManager;
import net.frankheijden.serverutils.velocity.managers.VelocityTaskManager;
public class VelocityPlugin extends ServerUtilsPlugin<
PluginContainer,
ScheduledTask,
VelocityCommandSender,
CommandSource
> {
public class VelocityPlugin extends ServerUtilsPlugin<PluginContainer, ScheduledTask, VelocityCommandSender, CommandSource, VelocityPluginDescription> {
private final ServerUtils plugin;
private final VelocityPluginManager pluginManager;
@ -46,13 +41,15 @@ public class VelocityPlugin extends ServerUtilsPlugin<
@Override
protected VelocityCommandManager<VelocityCommandSender> newCommandManager() {
return new VelocityCommandManager<>(
VelocityCommandManager<VelocityCommandSender> commandManager = new VelocityCommandManager<>(
plugin.getPluginContainer(),
plugin.getProxy(),
AsynchronousCommandExecutionCoordinator.<VelocityCommandSender>newBuilder().build(),
chatProvider::get,
VelocityCommandSender::getSource
);
handleBrigadier(commandManager.brigadierManager());
return commandManager;
}
@Override

View file

@ -0,0 +1,69 @@
package net.frankheijden.serverutils.velocity.entities;
import com.velocitypowered.api.plugin.PluginDescription;
import com.velocitypowered.api.plugin.meta.PluginDependency;
import java.io.File;
import java.nio.file.Path;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import net.frankheijden.serverutils.common.entities.ServerUtilsPluginDescription;
import net.frankheijden.serverutils.common.entities.exceptions.InvalidPluginDescriptionException;
public class VelocityPluginDescription implements ServerUtilsPluginDescription {
private final PluginDescription description;
private final File file;
private final String author;
private final Set<String> dependencies;
/**
* Constructs a new BungeePluginDescription.
*/
public VelocityPluginDescription(PluginDescription description) {
this.description = description;
Optional<Path> sourceOptional = description.getSource();
if (!sourceOptional.isPresent()) throw new InvalidPluginDescriptionException("Source path is null");
this.file = sourceOptional.get().toFile();
this.author = String.join(", ", description.getAuthors());
this.dependencies = description.getDependencies().stream()
.map(PluginDependency::getId)
.collect(Collectors.toSet());
}
@Override
public String getId() {
return this.description.getId();
}
@Override
public String getName() {
return this.description.getName().orElse("<UNKNOWN>");
}
@Override
public String getVersion() {
return this.description.getVersion().orElse("<UNKNOWN>");
}
@Override
public String getAuthor() {
return this.author;
}
@Override
public File getFile() {
return this.file;
}
@Override
public Set<String> getDependencies() {
return this.dependencies;
}
public PluginDescription getDescription() {
return description;
}
}

View file

@ -26,11 +26,13 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import net.frankheijden.serverutils.common.entities.CloseableResult;
import net.frankheijden.serverutils.common.entities.Result;
import net.frankheijden.serverutils.common.entities.exceptions.InvalidPluginDescriptionException;
import net.frankheijden.serverutils.common.entities.results.CloseablePluginResults;
import net.frankheijden.serverutils.common.entities.results.PluginResults;
import net.frankheijden.serverutils.common.entities.results.Result;
import net.frankheijden.serverutils.common.events.PluginEvent;
import net.frankheijden.serverutils.common.managers.AbstractPluginManager;
import net.frankheijden.serverutils.velocity.entities.VelocityLoadResult;
import net.frankheijden.serverutils.velocity.entities.VelocityPluginDescription;
import net.frankheijden.serverutils.velocity.events.VelocityPluginDisableEvent;
import net.frankheijden.serverutils.velocity.events.VelocityPluginEnableEvent;
import net.frankheijden.serverutils.velocity.events.VelocityPluginLoadEvent;
@ -44,7 +46,7 @@ import net.frankheijden.serverutils.velocity.reflection.RVelocityPluginManager;
import net.frankheijden.serverutils.velocity.reflection.RVelocityScheduler;
import org.slf4j.Logger;
public class VelocityPluginManager implements AbstractPluginManager<PluginContainer> {
public class VelocityPluginManager extends AbstractPluginManager<PluginContainer, VelocityPluginDescription> {
private static VelocityPluginManager instance;
private final ProxyServer proxy;
@ -66,195 +68,223 @@ public class VelocityPluginManager implements AbstractPluginManager<PluginContai
}
@Override
public VelocityLoadResult loadPlugin(String pluginFile) {
return loadPlugin(new File(getPluginsFolder(), pluginFile));
}
public PluginResults<PluginContainer> loadPluginDescriptions(List<VelocityPluginDescription> descriptions) {
PluginResults<PluginContainer> loadResults = new PluginResults<>();
@Override
public VelocityLoadResult loadPlugin(File file) {
if (!file.exists() || file.isDirectory()) return new VelocityLoadResult(Result.NOT_EXISTS);
for (VelocityPluginDescription description : descriptions) {
Path source = description.getFile().toPath();
Path baseDirectory = source.getParent();
Object javaPluginLoader = RJavaPluginLoader.newInstance(proxy, file.toPath().getParent());
PluginDescription candidate = RJavaPluginLoader.loadPluginDescription(javaPluginLoader, file.toPath());
if (proxy.getPluginManager().isLoaded(candidate.getId())) return new VelocityLoadResult(Result.ALREADY_LOADED);
Object javaPluginLoader = RJavaPluginLoader.newInstance(proxy, baseDirectory);
PluginDescription candidate = RJavaPluginLoader.loadPluginDescription(javaPluginLoader, source);
for (PluginDependency dependency : candidate.getDependencies()) {
if (!dependency.isOptional() && !proxy.getPluginManager().isLoaded(dependency.getId())) {
logger.error(
"Can't load plugin {} due to missing dependency {}",
candidate.getId(),
dependency.getId()
);
return new VelocityLoadResult(Result.UNKNOWN_DEPENDENCY.arg(dependency.getId()));
}
}
PluginDescription realPlugin = RJavaPluginLoader.loadPlugin(javaPluginLoader, candidate);
PluginContainer container = RVelocityPluginContainer.newInstance(realPlugin);
proxy.getEventManager().fire(new VelocityPluginLoadEvent(container, PluginEvent.Stage.PRE));
proxy.getEventManager().fire(new VelocityPluginLoadEvent(container, PluginEvent.Stage.POST));
return new VelocityLoadResult(container);
}
@Override
public Result enablePlugin(PluginContainer container) {
proxy.getEventManager().fire(new VelocityPluginEnableEvent(container, PluginEvent.Stage.PRE));
if (proxy.getPluginManager().isLoaded(container.getDescription().getId())) return Result.ALREADY_ENABLED;
Object javaPluginLoader = RJavaPluginLoader.newInstance(
proxy,
container.getDescription().getSource().map(Path::getParent).orElse(null)
);
PluginDescription realPlugin = container.getDescription();
Module module = RJavaPluginLoader.createModule(javaPluginLoader, container);
AbstractModule commonModule = new AbstractModule() {
@Override
protected void configure() {
bind(ProxyServer.class).toInstance(proxy);
bind(PluginManager.class).toInstance(proxy.getPluginManager());
bind(EventManager.class).toInstance(proxy.getEventManager());
bind(CommandManager.class).toInstance(proxy.getCommandManager());
for (PluginContainer container : proxy.getPluginManager().getPlugins()) {
bind(PluginContainer.class)
.annotatedWith(Names.named(container.getDescription().getId()))
.toInstance(container);
dependencyCheck:
for (PluginDependency dependency : candidate.getDependencies()) {
String pluginId = dependency.getId();
for (VelocityPluginDescription desc : descriptions) {
if (desc.getId().equals(pluginId)) continue dependencyCheck;
}
bind(PluginContainer.class)
.annotatedWith(Names.named(realPlugin.getId()))
.toInstance(container);
}
};
try {
RJavaPluginLoader.createPlugin(javaPluginLoader, container, module, commonModule);
} catch (Exception ex) {
logger.error(
String.format("Can't create plugin %s", container.getDescription().getId()),
ex
);
return Result.ERROR;
if (!dependency.isOptional() && !proxy.getPluginManager().isLoaded(dependency.getId())) {
logger.error(
"Can't load plugin {} due to missing dependency {}",
candidate.getId(),
dependency.getId()
);
return loadResults.addResult(
description.getId(),
Result.UNKNOWN_DEPENDENCY.arg(dependency.getId())
);
}
}
PluginDescription realPlugin = RJavaPluginLoader.loadPlugin(javaPluginLoader, candidate);
PluginContainer container = RVelocityPluginContainer.newInstance(realPlugin);
proxy.getEventManager().fire(new VelocityPluginLoadEvent(container, PluginEvent.Stage.PRE));
proxy.getEventManager().fire(new VelocityPluginLoadEvent(container, PluginEvent.Stage.POST));
loadResults.addResult(description.getId(), container);
}
logger.info(
"Loaded plugin {} {} by {}",
realPlugin.getId(),
realPlugin.getVersion().orElse("<UNKNOWN>"),
Joiner.on(", ").join(realPlugin.getAuthors())
);
return loadResults;
}
RVelocityPluginManager.registerPlugin(proxy.getPluginManager(), container);
container.getInstance().ifPresent(pluginInstance -> {
RVelocityEventManager.registerInternally(proxy.getEventManager(), container, pluginInstance);
RVelocityEventManager.fireForPlugin(
proxy.getEventManager(),
new ProxyInitializeEvent(),
pluginInstance
).join();
@Override
public PluginResults<PluginContainer> enableOrderedPlugins(List<PluginContainer> containers) {
PluginResults<PluginContainer> enableResults = new PluginResults<>();
ConsoleCommandSource console = proxy.getConsoleCommandSource();
PermissionsSetupEvent event = new PermissionsSetupEvent(
console,
s -> PermissionFunction.ALWAYS_TRUE
List<Object> pluginInstances = new ArrayList<>(containers.size());
for (PluginContainer container : containers) {
String pluginId = container.getDescription().getId();
proxy.getEventManager().fire(new VelocityPluginEnableEvent(container, PluginEvent.Stage.PRE));
if (isPluginEnabled(pluginId)) {
return enableResults.addResult(pluginId, Result.ALREADY_ENABLED);
}
Object javaPluginLoader = RJavaPluginLoader.newInstance(
proxy,
container.getDescription().getSource().map(Path::getParent).orElse(null)
);
PermissionFunction permissionFunction = RVelocityEventManager.fireForPlugin(
proxy.getEventManager(),
event,
pluginInstance
).join().createFunction(console);
PluginDescription realPlugin = container.getDescription();
Module module = RJavaPluginLoader.createModule(javaPluginLoader, container);
if (permissionFunction == null) {
AbstractModule commonModule = new AbstractModule() {
@Override
protected void configure() {
bind(ProxyServer.class).toInstance(proxy);
bind(PluginManager.class).toInstance(proxy.getPluginManager());
bind(EventManager.class).toInstance(proxy.getEventManager());
bind(CommandManager.class).toInstance(proxy.getCommandManager());
for (PluginContainer container : proxy.getPluginManager().getPlugins()) {
bind(PluginContainer.class)
.annotatedWith(Names.named(container.getDescription().getId()))
.toInstance(container);
}
for (PluginContainer container : containers) {
bind(PluginContainer.class)
.annotatedWith(Names.named(container.getDescription().getId()))
.toInstance(container);
}
}
};
try {
RJavaPluginLoader.createPlugin(javaPluginLoader, container, module, commonModule);
} catch (Exception ex) {
logger.error(
"A plugin permission provider {} provided an invalid permission function for the console."
+ " This is a bug in the plugin, not in Velocity."
+ " Falling back to the default permission function.",
event.getProvider().getClass().getName()
String.format("Can't create plugin %s", container.getDescription().getId()),
ex
);
permissionFunction = PermissionFunction.ALWAYS_TRUE;
return enableResults.addResult(pluginId, Result.ERROR);
}
RVelocityConsole.setPermissionFunction(console, permissionFunction);
});
logger.info(
"Loaded plugin {} {} by {}",
realPlugin.getId(),
realPlugin.getVersion().orElse("<UNKNOWN>"),
Joiner.on(", ").join(realPlugin.getAuthors())
);
proxy.getEventManager().fire(new VelocityPluginEnableEvent(container, PluginEvent.Stage.POST));
return Result.SUCCESS;
RVelocityPluginManager.registerPlugin(proxy.getPluginManager(), container);
Optional<?> instanceOptional = container.getInstance();
if (instanceOptional.isPresent()) {
Object pluginInstance = instanceOptional.get();
RVelocityEventManager.registerInternally(proxy.getEventManager(), container, pluginInstance);
pluginInstances.add(pluginInstance);
}
}
RVelocityEventManager.fireForPlugins(
proxy.getEventManager(),
new ProxyInitializeEvent(),
pluginInstances
).join();
ConsoleCommandSource console = proxy.getConsoleCommandSource();
PermissionsSetupEvent event = new PermissionsSetupEvent(
console,
s -> PermissionFunction.ALWAYS_TRUE
);
PermissionFunction permissionFunction = RVelocityEventManager.fireForPlugins(
proxy.getEventManager(),
event,
pluginInstances
).join().createFunction(console);
if (permissionFunction == null) {
logger.error(
"A plugin permission provider {} provided an invalid permission function for the console."
+ " This is a bug in the plugin, not in Velocity."
+ " Falling back to the default permission function.",
event.getProvider().getClass().getName()
);
permissionFunction = PermissionFunction.ALWAYS_TRUE;
}
RVelocityConsole.setPermissionFunction(console, permissionFunction);
for (PluginContainer container : containers) {
proxy.getEventManager().fire(new VelocityPluginEnableEvent(container, PluginEvent.Stage.POST));
enableResults.addResult(container.getDescription().getId(), container);
}
return enableResults;
}
@Override
public Result disablePlugin(PluginContainer container) {
proxy.getEventManager().fire(new VelocityPluginDisableEvent(container, PluginEvent.Stage.PRE));
Object pluginInstance = container.getInstance().orElse(null);
if (pluginInstance == null) return Result.NOT_EXISTS;
public boolean isPluginEnabled(String pluginId) {
return proxy.getPluginManager().isLoaded(pluginId);
}
RVelocityEventManager.fireForPlugin(
@Override
public PluginResults<PluginContainer> disableOrderedPlugins(List<PluginContainer> containers) {
PluginResults<PluginContainer> disableResults = new PluginResults<>();
List<Object> pluginInstances = new ArrayList<>(containers.size());
for (PluginContainer container : containers) {
proxy.getEventManager().fire(new VelocityPluginDisableEvent(container, PluginEvent.Stage.PRE));
String pluginId = getPluginId(container);
Object pluginInstance = container.getInstance().orElse(null);
if (pluginInstance == null) {
return disableResults.addResult(pluginId, Result.ALREADY_DISABLED);
}
pluginInstances.add(pluginInstance);
}
RVelocityEventManager.fireForPlugins(
proxy.getEventManager(),
pluginInstance,
new ProxyShutdownEvent()
new ProxyShutdownEvent(),
pluginInstances
);
proxy.getEventManager().fire(new VelocityPluginDisableEvent(container, PluginEvent.Stage.POST));
return Result.SUCCESS;
}
@Override
public Result reloadPlugin(String pluginName) {
Optional<PluginContainer> pluginOptional = proxy.getPluginManager().getPlugin(pluginName);
if (!pluginOptional.isPresent()) return Result.NOT_EXISTS;
return reloadPlugin(pluginOptional.get());
}
@Override
public Result reloadPlugin(PluginContainer plugin) {
CloseableResult result = unloadPlugin(plugin);
if (result.getResult() != Result.SUCCESS) return result.getResult();
result.tryClose();
File file = getPluginFile(plugin.getDescription().getId());
if (file == null) return Result.FILE_DELETED;
VelocityLoadResult loadResult = loadPlugin(file);
if (!loadResult.isSuccess()) return loadResult.getResult();
return enablePlugin(loadResult.get());
}
@Override
public CloseableResult unloadPlugin(String pluginName) {
Optional<PluginContainer> pluginOptional = proxy.getPluginManager().getPlugin(pluginName);
if (!pluginOptional.isPresent()) return new CloseableResult(Result.NOT_EXISTS);
return unloadPlugin(pluginOptional.get());
}
@Override
public CloseableResult unloadPlugin(PluginContainer container) {
proxy.getEventManager().fire(new VelocityPluginUnloadEvent(container, PluginEvent.Stage.PRE));
Optional<?> pluginInstanceOptional = container.getInstance();
if (!pluginInstanceOptional.isPresent()) return new CloseableResult(Result.INVALID_PLUGIN);
Object pluginInstance = pluginInstanceOptional.get();
proxy.getEventManager().unregisterListeners(pluginInstance);
for (ScheduledTask task : RVelocityScheduler.getTasksByPlugin(proxy.getScheduler()).removeAll(pluginInstance)) {
task.cancel();
for (PluginContainer container : containers) {
proxy.getEventManager().fire(new VelocityPluginDisableEvent(container, PluginEvent.Stage.POST));
disableResults.addResult(getPluginId(container), container);
}
String pluginId = container.getDescription().getId();
for (String alias : pluginCommandManager.getPluginCommands().removeAll(pluginId)) {
proxy.getCommandManager().unregister(alias);
return disableResults;
}
@Override
public CloseablePluginResults<PluginContainer> unloadOrderedPlugins(List<PluginContainer> containers) {
CloseablePluginResults<PluginContainer> unloadResults = new CloseablePluginResults<>();
for (PluginContainer container : containers) {
proxy.getEventManager().fire(new VelocityPluginUnloadEvent(container, PluginEvent.Stage.PRE));
String pluginId = getPluginId(container);
Optional<?> pluginInstanceOptional = container.getInstance();
if (!pluginInstanceOptional.isPresent()) {
return unloadResults.addResult(pluginId, Result.INVALID_PLUGIN);
}
Object pluginInstance = pluginInstanceOptional.get();
proxy.getEventManager().unregisterListeners(pluginInstance);
for (ScheduledTask task : RVelocityScheduler.getTasksByPlugin(proxy.getScheduler())
.removeAll(pluginInstance)) {
task.cancel();
}
for (String alias : pluginCommandManager.getPluginCommands().removeAll(pluginId)) {
proxy.getCommandManager().unregister(alias);
}
RVelocityPluginManager.getPlugins(proxy.getPluginManager()).remove(pluginId);
RVelocityPluginManager.getPluginInstances(proxy.getPluginManager()).remove(pluginInstance);
List<Closeable> closeables = new ArrayList<>();
ClassLoader loader = pluginInstance.getClass().getClassLoader();
if (loader instanceof Closeable) {
closeables.add((Closeable) loader);
}
proxy.getEventManager().fire(new VelocityPluginUnloadEvent(container, PluginEvent.Stage.POST));
unloadResults.addResult(pluginId, container, closeables);
}
RVelocityPluginManager.getPlugins(proxy.getPluginManager()).remove(pluginId);
RVelocityPluginManager.getPluginInstances(proxy.getPluginManager()).remove(pluginInstance);
List<Closeable> closeables = new ArrayList<>();
ClassLoader loader = pluginInstance.getClass().getClassLoader();
if (loader instanceof Closeable) {
closeables.add((Closeable) loader);
}
proxy.getEventManager().fire(new VelocityPluginUnloadEvent(container, PluginEvent.Stage.POST));
return new CloseableResult(closeables);
return unloadResults;
}
@Override
@ -263,7 +293,7 @@ public class VelocityPluginManager implements AbstractPluginManager<PluginContai
}
@Override
public String getPluginName(PluginContainer plugin) {
public String getPluginId(PluginContainer plugin) {
return plugin.getDescription().getId();
}
@ -275,22 +305,43 @@ public class VelocityPluginManager implements AbstractPluginManager<PluginContai
}
@Override
public File getPluginFile(String pluginName) {
public Optional<File> getPluginFile(String pluginName) {
Object javaPluginLoader = RJavaPluginLoader.newInstance(instance.proxy, getPluginsFolder().toPath());
for (File file : getPluginJars()) {
PluginDescription desc = RJavaPluginLoader.loadPluginDescription(javaPluginLoader, file.toPath());
if (desc.getId().equals(pluginName)) {
return file;
return Optional.of(file);
}
}
return null;
return Optional.empty();
}
@Override
public PluginContainer getPlugin(String pluginName) {
return proxy.getPluginManager().getPlugin(pluginName).orElse(null);
public Optional<PluginContainer> getPlugin(String pluginName) {
return proxy.getPluginManager().getPlugin(pluginName);
}
@Override
public VelocityPluginDescription getLoadedPluginDescription(PluginContainer plugin) {
return new VelocityPluginDescription(plugin.getDescription());
}
@Override
public Optional<VelocityPluginDescription> getPluginDescription(
File file
) throws InvalidPluginDescriptionException {
Path source = file.toPath();
Path baseDirectory = source.getParent();
try {
Object javaPluginLoader = RJavaPluginLoader.newInstance(proxy, baseDirectory);
PluginDescription candidate = RJavaPluginLoader.loadPluginDescription(javaPluginLoader, source);
return Optional.of(new VelocityPluginDescription(candidate));
} catch (Exception ex) {
throw new InvalidPluginDescriptionException(ex);
}
}
@Override

View file

@ -9,6 +9,7 @@ import dev.frankheijden.minecraftreflection.MinecraftReflection;
import java.lang.reflect.Array;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
public class RVelocityEventManager {
@ -27,9 +28,14 @@ public class RVelocityEventManager {
* Retrieves the registrations from a plugin for a specific event.
*/
@SuppressWarnings("unchecked")
public static List<Object> getRegistrationsByPlugin(EventManager manager, Object plugin, Class<?> eventClass) {
public static List<Object> getRegistrationsByPlugins(
EventManager manager,
List<Object> plugins,
Class<?> eventClass
) {
return (List<Object>) getHandlersByType(manager).get(eventClass).stream()
.filter(r -> RHandlerRegistration.getPlugin(r).getInstance().orElse(null) == plugin)
.filter(r -> plugins.contains(RHandlerRegistration.getPlugin(r).getInstance().orElse(null)))
.sorted(reflection.get(manager, "handlerComparator"))
.collect(Collectors.toList());
}
@ -48,26 +54,27 @@ public class RVelocityEventManager {
/**
* Fires an event specifically for one plugin.
*/
public static <E> CompletableFuture<E> fireForPlugin(
public static <E> CompletableFuture<E> fireForPlugins(
EventManager manager,
E event,
Object plugin
List<Object> pluginInstances
) {
List<Object> registrations = getRegistrationsByPlugin(manager, plugin, event.getClass());
List<Object> registrations = getRegistrationsByPlugins(manager, pluginInstances, event.getClass());
CompletableFuture<E> future = new CompletableFuture<>();
Object registrationsEmptyArray = Array.newInstance(RHandlerRegistration.reflection.getClazz(), 0);
Class<?> registrationsArrayClass = registrationsEmptyArray.getClass();
reflection.invoke(
ExecutorService executor = reflection.invoke(manager, "getAsyncExecutor");
executor.execute(() -> reflection.invoke(
manager,
"fire",
ClassObject.of(CompletableFuture.class, future),
ClassObject.of(Object.class, event),
ClassObject.of(int.class, 0),
ClassObject.of(boolean.class, false),
ClassObject.of(boolean.class, true),
ClassObject.of(registrationsArrayClass, registrations.toArray((Object[]) registrationsEmptyArray))
);
));
return future;
}