@@ -0,0 +1,82 @@
|
||||
package io.papermc.paper.plugin.manager;
|
||||
|
||||
import io.papermc.paper.plugin.configuration.PluginMeta;
|
||||
import io.papermc.paper.plugin.provider.type.PluginFileType;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.plugin.InvalidDescriptionException;
|
||||
import org.bukkit.plugin.InvalidPluginException;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginDescriptionFile;
|
||||
import org.bukkit.plugin.PluginLoader;
|
||||
import org.bukkit.plugin.RegisteredListener;
|
||||
import org.bukkit.plugin.UnknownDependencyException;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A purely internal type that implements the now deprecated {@link PluginLoader} after the implementation
|
||||
* of papers new plugin system.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public class DummyBukkitPluginLoader implements PluginLoader {
|
||||
|
||||
private static final Pattern[] PATTERNS = new Pattern[0];
|
||||
|
||||
@Override
|
||||
public @NotNull Plugin loadPlugin(@NotNull File file) throws InvalidPluginException, UnknownDependencyException {
|
||||
try {
|
||||
return PaperPluginManagerImpl.getInstance().loadPlugin(file);
|
||||
} catch (InvalidDescriptionException e) {
|
||||
throw new InvalidPluginException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PluginDescriptionFile getPluginDescription(@NotNull File file) throws InvalidDescriptionException {
|
||||
try (JarFile jar = new JarFile(file)) {
|
||||
PluginFileType<?, ?> type = PluginFileType.guessType(jar);
|
||||
if (type == null) {
|
||||
throw new InvalidDescriptionException(new FileNotFoundException("Jar does not contain plugin.yml"));
|
||||
}
|
||||
|
||||
PluginMeta meta = type.getConfig(jar);
|
||||
if (meta instanceof PluginDescriptionFile pluginDescriptionFile) {
|
||||
return pluginDescriptionFile;
|
||||
} else {
|
||||
throw new InvalidDescriptionException("Plugin type does not use plugin.yml. Cannot read file description.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new InvalidDescriptionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Pattern[] getPluginFileFilters() {
|
||||
return PATTERNS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<Class<? extends Event>, Set<RegisteredListener>> createRegisteredListeners(@NotNull Listener listener, @NotNull Plugin plugin) {
|
||||
return PaperPluginManagerImpl.getInstance().paperEventManager.createRegisteredListeners(listener, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enablePlugin(@NotNull Plugin plugin) {
|
||||
Bukkit.getPluginManager().enablePlugin(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disablePlugin(@NotNull Plugin plugin) {
|
||||
Bukkit.getPluginManager().disablePlugin(plugin);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package io.papermc.paper.plugin.manager;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import io.papermc.paper.plugin.entrypoint.Entrypoint;
|
||||
import io.papermc.paper.plugin.entrypoint.LaunchEntryPointHandler;
|
||||
import io.papermc.paper.plugin.entrypoint.dependency.GraphDependencyContext;
|
||||
import io.papermc.paper.plugin.entrypoint.dependency.MetaDependencyTree;
|
||||
import io.papermc.paper.plugin.provider.PluginProvider;
|
||||
import io.papermc.paper.plugin.provider.type.paper.PaperPluginParent;
|
||||
import io.papermc.paper.plugin.storage.ServerPluginProviderStorage;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MultiRuntimePluginProviderStorage extends ServerPluginProviderStorage {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getClassLogger();
|
||||
private final List<JavaPlugin> provided = new ArrayList<>();
|
||||
|
||||
private final MetaDependencyTree dependencyTree;
|
||||
|
||||
MultiRuntimePluginProviderStorage(MetaDependencyTree dependencyTree) {
|
||||
this.dependencyTree = dependencyTree;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(PluginProvider<JavaPlugin> provider) {
|
||||
if (provider instanceof PaperPluginParent.PaperServerPluginProvider) {
|
||||
LOGGER.warn("Skipping loading of paper plugin requested from SimplePluginManager.");
|
||||
return;
|
||||
}
|
||||
super.register(provider);
|
||||
/*
|
||||
Register the provider into the server entrypoint, this allows it to show in /plugins correctly. Generally it might be better in the future to make a separate storage,
|
||||
as putting it into the entrypoint handlers doesn't make much sense.
|
||||
*/
|
||||
LaunchEntryPointHandler.INSTANCE.register(Entrypoint.PLUGIN, provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processProvided(PluginProvider<JavaPlugin> provider, JavaPlugin provided) {
|
||||
super.processProvided(provider, provided);
|
||||
this.provided.add(provided);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean throwOnCycle() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<JavaPlugin> getLoaded() {
|
||||
return this.provided;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaDependencyTree createDependencyTree() {
|
||||
return this.dependencyTree;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package io.papermc.paper.plugin.manager;
|
||||
|
||||
import org.bukkit.permissions.Permissible;
|
||||
import org.bukkit.permissions.Permission;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
class NormalPaperPermissionManager extends PaperPermissionManager {
|
||||
|
||||
private final Map<String, Permission> permissions = new HashMap<>();
|
||||
private final Map<Boolean, Set<Permission>> defaultPerms = new LinkedHashMap<>();
|
||||
private final Map<String, Map<Permissible, Boolean>> permSubs = new HashMap<>();
|
||||
private final Map<Boolean, Map<Permissible, Boolean>> defSubs = new HashMap<>();
|
||||
|
||||
public NormalPaperPermissionManager() {
|
||||
this.defaultPerms().put(true, new LinkedHashSet<>());
|
||||
this.defaultPerms().put(false, new LinkedHashSet<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Permission> permissions() {
|
||||
return this.permissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Boolean, Set<Permission>> defaultPerms() {
|
||||
return this.defaultPerms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Map<Permissible, Boolean>> permSubs() {
|
||||
return this.permSubs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Boolean, Map<Permissible, Boolean>> defSubs() {
|
||||
return this.defSubs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
package io.papermc.paper.plugin.manager;
|
||||
|
||||
import co.aikar.timings.TimedEventExecutor;
|
||||
import com.destroystokyo.paper.event.server.ServerExceptionEvent;
|
||||
import com.destroystokyo.paper.exception.ServerEventException;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.Warning;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.plugin.AuthorNagException;
|
||||
import org.bukkit.plugin.EventExecutor;
|
||||
import org.bukkit.plugin.IllegalPluginAccessException;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
|
||||
class PaperEventManager {
|
||||
|
||||
private final Server server;
|
||||
|
||||
public PaperEventManager(Server server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
// SimplePluginManager
|
||||
public void callEvent(@NotNull Event event) {
|
||||
if (event.isAsynchronous() && this.server.isPrimaryThread()) {
|
||||
throw new IllegalStateException(event.getEventName() + " may only be triggered asynchronously.");
|
||||
} else if (!event.isAsynchronous() && !this.server.isPrimaryThread() && !this.server.isStopping()) {
|
||||
throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously.");
|
||||
}
|
||||
|
||||
HandlerList handlers = event.getHandlers();
|
||||
RegisteredListener[] listeners = handlers.getRegisteredListeners();
|
||||
|
||||
for (RegisteredListener registration : listeners) {
|
||||
if (!registration.getPlugin().isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
registration.callEvent(event);
|
||||
} catch (AuthorNagException ex) {
|
||||
Plugin plugin = registration.getPlugin();
|
||||
|
||||
if (plugin.isNaggable()) {
|
||||
plugin.setNaggable(false);
|
||||
|
||||
this.server.getLogger().log(Level.SEVERE, String.format(
|
||||
"Nag author(s): '%s' of '%s' about the following: %s",
|
||||
plugin.getPluginMeta().getAuthors(),
|
||||
plugin.getPluginMeta().getDisplayName(),
|
||||
ex.getMessage()
|
||||
));
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
String msg = "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getPluginMeta().getDisplayName();
|
||||
this.server.getLogger().log(Level.SEVERE, msg, ex);
|
||||
if (!(event instanceof ServerExceptionEvent)) { // We don't want to cause an endless event loop
|
||||
this.callEvent(new ServerExceptionEvent(new ServerEventException(msg, ex, registration.getPlugin(), registration.getListener(), event)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void registerEvents(@NotNull Listener listener, @NotNull Plugin plugin) {
|
||||
if (!plugin.isEnabled()) {
|
||||
throw new IllegalPluginAccessException("Plugin attempted to register " + listener + " while not enabled");
|
||||
}
|
||||
|
||||
for (Map.Entry<Class<? extends Event>, Set<RegisteredListener>> entry : this.createRegisteredListeners(listener, plugin).entrySet()) {
|
||||
this.getEventListeners(this.getRegistrationClass(entry.getKey())).registerAll(entry.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void registerEvent(@NotNull Class<? extends Event> event, @NotNull Listener listener, @NotNull EventPriority priority, @NotNull EventExecutor executor, @NotNull Plugin plugin) {
|
||||
this.registerEvent(event, listener, priority, executor, plugin, false);
|
||||
}
|
||||
|
||||
public void registerEvent(@NotNull Class<? extends Event> event, @NotNull Listener listener, @NotNull EventPriority priority, @NotNull EventExecutor executor, @NotNull Plugin plugin, boolean ignoreCancelled) {
|
||||
if (!plugin.isEnabled()) {
|
||||
throw new IllegalPluginAccessException("Plugin attempted to register " + event + " while not enabled");
|
||||
}
|
||||
|
||||
executor = new TimedEventExecutor(executor, plugin, null, event);
|
||||
this.getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private HandlerList getEventListeners(@NotNull Class<? extends Event> type) {
|
||||
try {
|
||||
Method method = this.getRegistrationClass(type).getDeclaredMethod("getHandlerList");
|
||||
method.setAccessible(true);
|
||||
return (HandlerList) method.invoke(null);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalPluginAccessException(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Class<? extends Event> getRegistrationClass(@NotNull Class<? extends Event> clazz) {
|
||||
try {
|
||||
clazz.getDeclaredMethod("getHandlerList");
|
||||
return clazz;
|
||||
} catch (NoSuchMethodException e) {
|
||||
if (clazz.getSuperclass() != null
|
||||
&& !clazz.getSuperclass().equals(Event.class)
|
||||
&& Event.class.isAssignableFrom(clazz.getSuperclass())) {
|
||||
return this.getRegistrationClass(clazz.getSuperclass().asSubclass(Event.class));
|
||||
} else {
|
||||
throw new IllegalPluginAccessException("Unable to find handler list for event " + clazz.getName() + ". Static getHandlerList method required!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// JavaPluginLoader
|
||||
@NotNull
|
||||
public Map<Class<? extends Event>, Set<RegisteredListener>> createRegisteredListeners(@NotNull Listener listener, @NotNull final Plugin plugin) {
|
||||
Map<Class<? extends Event>, Set<RegisteredListener>> ret = new HashMap<>();
|
||||
|
||||
Set<Method> methods;
|
||||
try {
|
||||
Class<?> listenerClazz = listener.getClass();
|
||||
methods = Sets.union(
|
||||
Set.of(listenerClazz.getMethods()),
|
||||
Set.of(listenerClazz.getDeclaredMethods())
|
||||
);
|
||||
} catch (NoClassDefFoundError e) {
|
||||
plugin.getLogger().severe("Failed to register events for " + listener.getClass() + " because " + e.getMessage() + " does not exist.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (final Method method : methods) {
|
||||
final EventHandler eh = method.getAnnotation(EventHandler.class);
|
||||
if (eh == null) continue;
|
||||
// Do not register bridge or synthetic methods to avoid event duplication
|
||||
// Fixes SPIGOT-893
|
||||
if (method.isBridge() || method.isSynthetic()) {
|
||||
continue;
|
||||
}
|
||||
final Class<?> checkClass;
|
||||
if (method.getParameterTypes().length != 1 || !Event.class.isAssignableFrom(checkClass = method.getParameterTypes()[0])) {
|
||||
plugin.getLogger().severe(plugin.getPluginMeta().getDisplayName() + " attempted to register an invalid EventHandler method signature \"" + method.toGenericString() + "\" in " + listener.getClass());
|
||||
continue;
|
||||
}
|
||||
final Class<? extends Event> eventClass = checkClass.asSubclass(Event.class);
|
||||
method.setAccessible(true);
|
||||
Set<RegisteredListener> eventSet = ret.computeIfAbsent(eventClass, k -> new HashSet<>());
|
||||
|
||||
for (Class<?> clazz = eventClass; Event.class.isAssignableFrom(clazz); clazz = clazz.getSuperclass()) {
|
||||
// This loop checks for extending deprecated events
|
||||
if (clazz.getAnnotation(Deprecated.class) != null) {
|
||||
Warning warning = clazz.getAnnotation(Warning.class);
|
||||
Warning.WarningState warningState = this.server.getWarningState();
|
||||
if (!warningState.printFor(warning)) {
|
||||
break;
|
||||
}
|
||||
plugin.getLogger().log(
|
||||
Level.WARNING,
|
||||
String.format(
|
||||
"\"%s\" has registered a listener for %s on method \"%s\", but the event is Deprecated. \"%s\"; please notify the authors %s.",
|
||||
plugin.getPluginMeta().getDisplayName(),
|
||||
clazz.getName(),
|
||||
method.toGenericString(),
|
||||
(warning != null && warning.reason().length() != 0) ? warning.reason() : "Server performance will be affected",
|
||||
Arrays.toString(plugin.getPluginMeta().getAuthors().toArray())),
|
||||
warningState == Warning.WarningState.ON ? new AuthorNagException(null) : null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EventExecutor executor = new TimedEventExecutor(EventExecutor.create(method, eventClass), plugin, method, eventClass);
|
||||
eventSet.add(new RegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled()));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void clearEvents() {
|
||||
HandlerList.unregisterAll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package io.papermc.paper.plugin.manager;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import io.papermc.paper.plugin.PermissionManager;
|
||||
import org.bukkit.permissions.Permissible;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
/**
|
||||
* See
|
||||
* {@link StupidSPMPermissionManagerWrapper}
|
||||
*/
|
||||
abstract class PaperPermissionManager implements PermissionManager {
|
||||
|
||||
public abstract Map<String, Permission> permissions();
|
||||
|
||||
public abstract Map<Boolean, Set<Permission>> defaultPerms();
|
||||
|
||||
public abstract Map<String, Map<Permissible, Boolean>> permSubs();
|
||||
|
||||
public abstract Map<Boolean, Map<Permissible, Boolean>> defSubs();
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Permission getPermission(@NotNull String name) {
|
||||
return this.permissions().get(name.toLowerCase(java.util.Locale.ENGLISH));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPermission(@NotNull Permission perm) {
|
||||
this.addPermission(perm, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPermissions(@NotNull List<Permission> permissions) {
|
||||
for (Permission permission : permissions) {
|
||||
this.addPermission(permission, false);
|
||||
}
|
||||
this.dirtyPermissibles();
|
||||
}
|
||||
|
||||
// Allow suppressing permission default calculations
|
||||
private void addPermission(@NotNull Permission perm, boolean dirty) {
|
||||
String name = perm.getName().toLowerCase(java.util.Locale.ENGLISH);
|
||||
|
||||
if (this.permissions().containsKey(name)) {
|
||||
throw new IllegalArgumentException("The permission " + name + " is already defined!");
|
||||
}
|
||||
|
||||
this.permissions().put(name, perm);
|
||||
this.calculatePermissionDefault(perm, dirty);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Set<Permission> getDefaultPermissions(boolean op) {
|
||||
return ImmutableSet.copyOf(this.defaultPerms().get(op));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void removePermission(@NotNull Permission perm) {
|
||||
this.removePermission(perm.getName());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void removePermission(@NotNull String name) {
|
||||
this.permissions().remove(name.toLowerCase(java.util.Locale.ENGLISH));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recalculatePermissionDefaults(@NotNull Permission perm) {
|
||||
// we need a null check here because some plugins for some unknown reason pass null into this?
|
||||
if (perm != null && this.permissions().containsKey(perm.getName().toLowerCase(Locale.ROOT))) {
|
||||
this.defaultPerms().get(true).remove(perm);
|
||||
this.defaultPerms().get(false).remove(perm);
|
||||
|
||||
this.calculatePermissionDefault(perm, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void calculatePermissionDefault(@NotNull Permission perm, boolean dirty) {
|
||||
if ((perm.getDefault() == PermissionDefault.OP) || (perm.getDefault() == PermissionDefault.TRUE)) {
|
||||
this.defaultPerms().get(true).add(perm);
|
||||
if (dirty) {
|
||||
this.dirtyPermissibles(true);
|
||||
}
|
||||
}
|
||||
if ((perm.getDefault() == PermissionDefault.NOT_OP) || (perm.getDefault() == PermissionDefault.TRUE)) {
|
||||
this.defaultPerms().get(false).add(perm);
|
||||
if (dirty) {
|
||||
this.dirtyPermissibles(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void subscribeToPermission(@NotNull String permission, @NotNull Permissible permissible) {
|
||||
String name = permission.toLowerCase(java.util.Locale.ENGLISH);
|
||||
Map<Permissible, Boolean> map = this.permSubs().computeIfAbsent(name, k -> new WeakHashMap<>());
|
||||
|
||||
map.put(permissible, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribeFromPermission(@NotNull String permission, @NotNull Permissible permissible) {
|
||||
String name = permission.toLowerCase(java.util.Locale.ENGLISH);
|
||||
Map<Permissible, Boolean> map = this.permSubs().get(name);
|
||||
|
||||
if (map != null) {
|
||||
map.remove(permissible);
|
||||
|
||||
if (map.isEmpty()) {
|
||||
this.permSubs().remove(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Set<Permissible> getPermissionSubscriptions(@NotNull String permission) {
|
||||
String name = permission.toLowerCase(java.util.Locale.ENGLISH);
|
||||
Map<Permissible, Boolean> map = this.permSubs().get(name);
|
||||
|
||||
if (map == null) {
|
||||
return ImmutableSet.of();
|
||||
} else {
|
||||
return ImmutableSet.copyOf(map.keySet());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribeToDefaultPerms(boolean op, @NotNull Permissible permissible) {
|
||||
Map<Permissible, Boolean> map = this.defSubs().computeIfAbsent(op, k -> new WeakHashMap<>());
|
||||
|
||||
map.put(permissible, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribeFromDefaultPerms(boolean op, @NotNull Permissible permissible) {
|
||||
Map<Permissible, Boolean> map = this.defSubs().get(op);
|
||||
|
||||
if (map != null) {
|
||||
map.remove(permissible);
|
||||
|
||||
if (map.isEmpty()) {
|
||||
this.defSubs().remove(op);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Set<Permissible> getDefaultPermSubscriptions(boolean op) {
|
||||
Map<Permissible, Boolean> map = this.defSubs().get(op);
|
||||
|
||||
if (map == null) {
|
||||
return ImmutableSet.of();
|
||||
} else {
|
||||
return ImmutableSet.copyOf(map.keySet());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Set<Permission> getPermissions() {
|
||||
return new HashSet<>(this.permissions().values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearPermissions() {
|
||||
this.permissions().clear();
|
||||
this.defaultPerms().get(true).clear();
|
||||
this.defaultPerms().get(false).clear();
|
||||
}
|
||||
|
||||
|
||||
void dirtyPermissibles(boolean op) {
|
||||
Set<Permissible> permissibles = this.getDefaultPermSubscriptions(op);
|
||||
|
||||
for (Permissible p : permissibles) {
|
||||
p.recalculatePermissions();
|
||||
}
|
||||
}
|
||||
|
||||
void dirtyPermissibles() {
|
||||
this.dirtyPermissibles(true);
|
||||
this.dirtyPermissibles(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
package io.papermc.paper.plugin.manager;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.graph.GraphBuilder;
|
||||
import com.google.common.graph.MutableGraph;
|
||||
import io.papermc.paper.plugin.configuration.PluginMeta;
|
||||
import io.papermc.paper.plugin.entrypoint.Entrypoint;
|
||||
import io.papermc.paper.plugin.entrypoint.dependency.MetaDependencyTree;
|
||||
import io.papermc.paper.plugin.entrypoint.dependency.SimpleMetaDependencyTree;
|
||||
import io.papermc.paper.plugin.entrypoint.strategy.PluginGraphCycleException;
|
||||
import io.papermc.paper.plugin.provider.classloader.ConfiguredPluginClassLoader;
|
||||
import io.papermc.paper.plugin.provider.classloader.PaperClassLoaderStorage;
|
||||
import io.papermc.paper.plugin.provider.source.DirectoryProviderSource;
|
||||
import io.papermc.paper.plugin.provider.source.FileArrayProviderSource;
|
||||
import io.papermc.paper.plugin.provider.source.FileProviderSource;
|
||||
import java.io.File;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandMap;
|
||||
import org.bukkit.command.PluginCommandYamlParser;
|
||||
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.server.PluginDisableEvent;
|
||||
import org.bukkit.event.server.PluginEnableEvent;
|
||||
import org.bukkit.plugin.InvalidPluginException;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginDescriptionFile;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.UnknownDependencyException;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
class PaperPluginInstanceManager {
|
||||
|
||||
private static final FileProviderSource FILE_PROVIDER_SOURCE = new FileProviderSource("File '%s'"::formatted);
|
||||
|
||||
private final List<Plugin> plugins = new ArrayList<>();
|
||||
private final Map<String, Plugin> lookupNames = new HashMap<>();
|
||||
|
||||
private final PluginManager pluginManager;
|
||||
private final CommandMap commandMap;
|
||||
private final Server server;
|
||||
|
||||
private final MetaDependencyTree dependencyTree = new SimpleMetaDependencyTree(GraphBuilder.directed().build());
|
||||
|
||||
public PaperPluginInstanceManager(PluginManager pluginManager, CommandMap commandMap, Server server) {
|
||||
this.commandMap = commandMap;
|
||||
this.server = server;
|
||||
this.pluginManager = pluginManager;
|
||||
}
|
||||
|
||||
public @Nullable Plugin getPlugin(@NotNull String name) {
|
||||
return this.lookupNames.get(name.replace(' ', '_').toLowerCase(java.util.Locale.ENGLISH)); // Paper
|
||||
}
|
||||
|
||||
public @NotNull Plugin[] getPlugins() {
|
||||
return this.plugins.toArray(new Plugin[0]);
|
||||
}
|
||||
|
||||
public boolean isPluginEnabled(@NotNull String name) {
|
||||
Plugin plugin = this.getPlugin(name);
|
||||
|
||||
return this.isPluginEnabled(plugin);
|
||||
}
|
||||
|
||||
public synchronized boolean isPluginEnabled(@Nullable Plugin plugin) {
|
||||
if ((plugin != null) && (this.plugins.contains(plugin))) {
|
||||
return plugin.isEnabled();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void loadPlugin(Plugin provided) {
|
||||
PluginMeta configuration = provided.getPluginMeta();
|
||||
|
||||
this.plugins.add(provided);
|
||||
this.lookupNames.put(configuration.getName().toLowerCase(java.util.Locale.ENGLISH), provided);
|
||||
for (String providedPlugin : configuration.getProvidedPlugins()) {
|
||||
this.lookupNames.putIfAbsent(providedPlugin.toLowerCase(java.util.Locale.ENGLISH), provided);
|
||||
}
|
||||
|
||||
this.dependencyTree.add(configuration);
|
||||
}
|
||||
|
||||
// InvalidDescriptionException is never used, because the old JavaPluginLoader would wrap the exception.
|
||||
public @Nullable Plugin loadPlugin(@NotNull Path path) throws InvalidPluginException, UnknownDependencyException {
|
||||
RuntimePluginEntrypointHandler<SingularRuntimePluginProviderStorage> runtimePluginEntrypointHandler = new RuntimePluginEntrypointHandler<>(new SingularRuntimePluginProviderStorage(this.dependencyTree));
|
||||
|
||||
try {
|
||||
path = FILE_PROVIDER_SOURCE.prepareContext(path);
|
||||
FILE_PROVIDER_SOURCE.registerProviders(runtimePluginEntrypointHandler, path);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
return null; // Return null when the plugin file is not valid / plugin type is unknown
|
||||
} catch (PluginGraphCycleException exception) {
|
||||
throw new InvalidPluginException("Cannot import plugin that causes cyclic dependencies!");
|
||||
} catch (Exception e) {
|
||||
throw new InvalidPluginException(e);
|
||||
}
|
||||
|
||||
try {
|
||||
runtimePluginEntrypointHandler.enter(Entrypoint.PLUGIN);
|
||||
} catch (Throwable e) {
|
||||
throw new InvalidPluginException(e);
|
||||
}
|
||||
|
||||
return runtimePluginEntrypointHandler.getPluginProviderStorage().getSingleLoaded()
|
||||
.orElseThrow(() -> new InvalidPluginException("Plugin didn't load any plugin providers?"));
|
||||
}
|
||||
|
||||
public @NotNull Plugin[] loadPlugins(@NotNull File[] files) {
|
||||
RuntimePluginEntrypointHandler<MultiRuntimePluginProviderStorage> runtimePluginEntrypointHandler = new RuntimePluginEntrypointHandler<>(new MultiRuntimePluginProviderStorage(this.dependencyTree));
|
||||
try {
|
||||
List<Path> paths = FileArrayProviderSource.INSTANCE.prepareContext(files);
|
||||
DirectoryProviderSource.INSTANCE.registerProviders(runtimePluginEntrypointHandler, paths);
|
||||
runtimePluginEntrypointHandler.enter(Entrypoint.PLUGIN);
|
||||
} catch (Exception e) {
|
||||
// This should never happen, any errors that occur in this provider should instead be logged.
|
||||
this.server.getLogger().log(Level.SEVERE, "Unknown error occurred while loading plugins through PluginManager.", e);
|
||||
}
|
||||
|
||||
return runtimePluginEntrypointHandler.getPluginProviderStorage().getLoaded().toArray(new JavaPlugin[0]);
|
||||
}
|
||||
|
||||
// The behavior of this is that all errors are logged instead of being thrown
|
||||
public @NotNull Plugin[] loadPlugins(@NotNull Path directory) {
|
||||
Preconditions.checkArgument(Files.isDirectory(directory), "Directory must be a directory"); // Avoid creating a directory if it doesn't exist
|
||||
|
||||
RuntimePluginEntrypointHandler<MultiRuntimePluginProviderStorage> runtimePluginEntrypointHandler = new RuntimePluginEntrypointHandler<>(new MultiRuntimePluginProviderStorage(this.dependencyTree));
|
||||
try {
|
||||
List<Path> files = DirectoryProviderSource.INSTANCE.prepareContext(directory);
|
||||
DirectoryProviderSource.INSTANCE.registerProviders(runtimePluginEntrypointHandler, files);
|
||||
runtimePluginEntrypointHandler.enter(Entrypoint.PLUGIN);
|
||||
} catch (Exception e) {
|
||||
// This should never happen, any errors that occur in this provider should instead be logged.
|
||||
this.server.getLogger().log(Level.SEVERE, "Unknown error occurred while loading plugins through PluginManager.", e);
|
||||
}
|
||||
|
||||
return runtimePluginEntrypointHandler.getPluginProviderStorage().getLoaded().toArray(new JavaPlugin[0]);
|
||||
}
|
||||
|
||||
// Plugins are disabled in order like this inorder to "rougly" prevent
|
||||
// their dependencies unloading first. But, eh.
|
||||
public void disablePlugins() {
|
||||
Plugin[] plugins = this.getPlugins();
|
||||
for (int i = plugins.length - 1; i >= 0; i--) {
|
||||
this.disablePlugin(plugins[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearPlugins() {
|
||||
synchronized (this) {
|
||||
this.disablePlugins();
|
||||
this.plugins.clear();
|
||||
this.lookupNames.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void enablePlugin(@NotNull Plugin plugin) {
|
||||
if (plugin.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (plugin.getPluginMeta() instanceof PluginDescriptionFile) {
|
||||
List<Command> bukkitCommands = PluginCommandYamlParser.parse(plugin);
|
||||
|
||||
if (!bukkitCommands.isEmpty()) {
|
||||
this.commandMap.registerAll(plugin.getPluginMeta().getName(), bukkitCommands);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
String enableMsg = "Enabling " + plugin.getPluginMeta().getDisplayName();
|
||||
if (plugin.getPluginMeta() instanceof PluginDescriptionFile descriptionFile && CraftMagicNumbers.isLegacy(descriptionFile)) {
|
||||
enableMsg += "*";
|
||||
}
|
||||
plugin.getLogger().info(enableMsg);
|
||||
|
||||
JavaPlugin jPlugin = (JavaPlugin) plugin;
|
||||
|
||||
if (jPlugin.getClass().getClassLoader() instanceof ConfiguredPluginClassLoader classLoader) { // Paper
|
||||
if (PaperClassLoaderStorage.instance().registerUnsafePlugin(classLoader)) {
|
||||
this.server.getLogger().log(Level.WARNING, "Enabled plugin with unregistered ConfiguredPluginClassLoader " + plugin.getPluginMeta().getDisplayName());
|
||||
}
|
||||
} // Paper
|
||||
|
||||
try {
|
||||
jPlugin.setEnabled(true);
|
||||
} catch (Throwable ex) {
|
||||
this.server.getLogger().log(Level.SEVERE, "Error occurred while enabling " + plugin.getPluginMeta().getDisplayName() + " (Is it up to date?)", ex);
|
||||
// Paper start - Disable plugins that fail to load
|
||||
this.server.getPluginManager().disablePlugin(jPlugin);
|
||||
return;
|
||||
// Paper end
|
||||
}
|
||||
|
||||
// Perhaps abort here, rather than continue going, but as it stands,
|
||||
// an abort is not possible the way it's currently written
|
||||
this.server.getPluginManager().callEvent(new PluginEnableEvent(plugin));
|
||||
} catch (Throwable ex) {
|
||||
this.handlePluginException("Error occurred (in the plugin loader) while enabling "
|
||||
+ plugin.getPluginMeta().getDisplayName() + " (Is it up to date?)", ex, plugin);
|
||||
}
|
||||
|
||||
HandlerList.bakeAll();
|
||||
}
|
||||
|
||||
public synchronized void disablePlugin(@NotNull Plugin plugin) {
|
||||
if (!(plugin instanceof JavaPlugin javaPlugin)) {
|
||||
throw new IllegalArgumentException("Only expects java plugins.");
|
||||
}
|
||||
if (!plugin.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String pluginName = plugin.getPluginMeta().getDisplayName();
|
||||
|
||||
try {
|
||||
plugin.getLogger().info("Disabling %s".formatted(pluginName));
|
||||
|
||||
this.server.getPluginManager().callEvent(new PluginDisableEvent(plugin));
|
||||
try {
|
||||
javaPlugin.setEnabled(false);
|
||||
} catch (Throwable ex) {
|
||||
this.server.getLogger().log(Level.SEVERE, "Error occurred while disabling " + pluginName, ex);
|
||||
}
|
||||
|
||||
ClassLoader classLoader = plugin.getClass().getClassLoader();
|
||||
if (classLoader instanceof ConfiguredPluginClassLoader configuredPluginClassLoader) {
|
||||
try {
|
||||
configuredPluginClassLoader.close();
|
||||
} catch (IOException ex) {
|
||||
this.server.getLogger().log(Level.WARNING, "Error closing the classloader for '" + pluginName + "'", ex); // Paper - log exception
|
||||
}
|
||||
// Remove from the classloader pool inorder to prevent plugins from trying
|
||||
// to access classes
|
||||
PaperClassLoaderStorage.instance().unregisterClassloader(configuredPluginClassLoader);
|
||||
}
|
||||
|
||||
} catch (Throwable ex) {
|
||||
this.handlePluginException("Error occurred (in the plugin loader) while disabling "
|
||||
+ pluginName + " (Is it up to date?)", ex, plugin); // Paper
|
||||
}
|
||||
|
||||
try {
|
||||
this.server.getScheduler().cancelTasks(plugin);
|
||||
} catch (Throwable ex) {
|
||||
this.handlePluginException("Error occurred (in the plugin loader) while cancelling tasks for "
|
||||
+ pluginName + " (Is it up to date?)", ex, plugin); // Paper
|
||||
}
|
||||
|
||||
try {
|
||||
this.server.getServicesManager().unregisterAll(plugin);
|
||||
} catch (Throwable ex) {
|
||||
this.handlePluginException("Error occurred (in the plugin loader) while unregistering services for "
|
||||
+ pluginName + " (Is it up to date?)", ex, plugin); // Paper
|
||||
}
|
||||
|
||||
try {
|
||||
HandlerList.unregisterAll(plugin);
|
||||
} catch (Throwable ex) {
|
||||
this.handlePluginException("Error occurred (in the plugin loader) while unregistering events for "
|
||||
+ pluginName + " (Is it up to date?)", ex, plugin); // Paper
|
||||
}
|
||||
|
||||
try {
|
||||
this.server.getMessenger().unregisterIncomingPluginChannel(plugin);
|
||||
this.server.getMessenger().unregisterOutgoingPluginChannel(plugin);
|
||||
} catch (Throwable ex) {
|
||||
this.handlePluginException("Error occurred (in the plugin loader) while unregistering plugin channels for "
|
||||
+ pluginName + " (Is it up to date?)", ex, plugin); // Paper
|
||||
}
|
||||
|
||||
try {
|
||||
for (World world : this.server.getWorlds()) {
|
||||
world.removePluginChunkTickets(plugin);
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
this.handlePluginException("Error occurred (in the plugin loader) while removing chunk tickets for " + pluginName + " (Is it up to date?)", ex, plugin); // Paper
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: Implement event part in future patch (paper patch move up, this patch is lower)
|
||||
private void handlePluginException(String msg, Throwable ex, Plugin plugin) {
|
||||
Bukkit.getServer().getLogger().log(Level.SEVERE, msg, ex);
|
||||
this.pluginManager.callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerPluginEnableDisableException(msg, ex, plugin)));
|
||||
}
|
||||
|
||||
public boolean isTransitiveDepend(@NotNull PluginMeta plugin, @NotNull PluginMeta depend) {
|
||||
return this.dependencyTree.isTransitiveDependency(plugin, depend);
|
||||
}
|
||||
|
||||
public boolean hasDependency(String pluginIdentifier) {
|
||||
return this.getPlugin(pluginIdentifier) != null;
|
||||
}
|
||||
|
||||
// Debug only
|
||||
@ApiStatus.Internal
|
||||
public MutableGraph<String> getDependencyGraph() {
|
||||
return this.dependencyTree.getGraph();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
package io.papermc.paper.plugin.manager;
|
||||
|
||||
import com.google.common.graph.MutableGraph;
|
||||
import io.papermc.paper.plugin.PermissionManager;
|
||||
import io.papermc.paper.plugin.configuration.PluginMeta;
|
||||
import io.papermc.paper.plugin.provider.entrypoint.DependencyContext;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.CommandMap;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.permissions.Permissible;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.plugin.EventExecutor;
|
||||
import org.bukkit.plugin.InvalidDescriptionException;
|
||||
import org.bukkit.plugin.InvalidPluginException;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginLoader;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.SimplePluginManager;
|
||||
import org.bukkit.plugin.UnknownDependencyException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class PaperPluginManagerImpl implements PluginManager, DependencyContext {
|
||||
|
||||
final PaperPluginInstanceManager instanceManager;
|
||||
final PaperEventManager paperEventManager;
|
||||
PermissionManager permissionManager;
|
||||
|
||||
public PaperPluginManagerImpl(Server server, CommandMap commandMap, @Nullable SimplePluginManager permissionManager) {
|
||||
this.instanceManager = new PaperPluginInstanceManager(this, commandMap, server);
|
||||
this.paperEventManager = new PaperEventManager(server);
|
||||
|
||||
if (permissionManager == null) {
|
||||
this.permissionManager = new NormalPaperPermissionManager();
|
||||
} else {
|
||||
this.permissionManager = new StupidSPMPermissionManagerWrapper(permissionManager); // TODO: See comment when SimplePermissionManager is removed
|
||||
}
|
||||
}
|
||||
|
||||
// REMOVE THIS WHEN SimplePluginManager is removed.
|
||||
// Just cast and use Bukkit.getServer().getPluginManager()
|
||||
public static PaperPluginManagerImpl getInstance() {
|
||||
return ((CraftServer) (Bukkit.getServer())).paperPluginManager;
|
||||
}
|
||||
|
||||
// Plugin Manipulation
|
||||
|
||||
@Override
|
||||
public @Nullable Plugin getPlugin(@NotNull String name) {
|
||||
return this.instanceManager.getPlugin(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Plugin[] getPlugins() {
|
||||
return this.instanceManager.getPlugins();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPluginEnabled(@NotNull String name) {
|
||||
return this.instanceManager.isPluginEnabled(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPluginEnabled(@Nullable Plugin plugin) {
|
||||
return this.instanceManager.isPluginEnabled(plugin);
|
||||
}
|
||||
|
||||
public void loadPlugin(Plugin plugin) {
|
||||
this.instanceManager.loadPlugin(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Plugin loadPlugin(@NotNull File file) throws InvalidPluginException, InvalidDescriptionException, UnknownDependencyException {
|
||||
return this.instanceManager.loadPlugin(file.toPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Plugin[] loadPlugins(@NotNull File directory) {
|
||||
return this.instanceManager.loadPlugins(directory.toPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Plugin[] loadPlugins(final @NotNull File[] files) {
|
||||
return this.instanceManager.loadPlugins(files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disablePlugins() {
|
||||
this.instanceManager.disablePlugins();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void clearPlugins() {
|
||||
this.instanceManager.clearPlugins();
|
||||
this.permissionManager.clearPermissions();
|
||||
this.paperEventManager.clearEvents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enablePlugin(@NotNull Plugin plugin) {
|
||||
this.instanceManager.enablePlugin(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disablePlugin(@NotNull Plugin plugin) {
|
||||
this.instanceManager.disablePlugin(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTransitiveDependency(PluginMeta pluginMeta, PluginMeta dependencyConfig) {
|
||||
return this.instanceManager.isTransitiveDepend(pluginMeta, dependencyConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDependency(String pluginIdentifier) {
|
||||
return this.instanceManager.hasDependency(pluginIdentifier);
|
||||
}
|
||||
|
||||
// Event manipulation
|
||||
|
||||
@Override
|
||||
public void callEvent(@NotNull Event event) throws IllegalStateException {
|
||||
this.paperEventManager.callEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerEvents(@NotNull Listener listener, @NotNull Plugin plugin) {
|
||||
this.paperEventManager.registerEvents(listener, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerEvent(@NotNull Class<? extends Event> event, @NotNull Listener listener, @NotNull EventPriority priority, @NotNull EventExecutor executor, @NotNull Plugin plugin) {
|
||||
this.paperEventManager.registerEvent(event, listener, priority, executor, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerEvent(@NotNull Class<? extends Event> event, @NotNull Listener listener, @NotNull EventPriority priority, @NotNull EventExecutor executor, @NotNull Plugin plugin, boolean ignoreCancelled) {
|
||||
this.paperEventManager.registerEvent(event, listener, priority, executor, plugin, ignoreCancelled);
|
||||
}
|
||||
|
||||
// Permission manipulation
|
||||
|
||||
@Override
|
||||
public @Nullable Permission getPermission(@NotNull String name) {
|
||||
return this.permissionManager.getPermission(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPermission(@NotNull Permission perm) {
|
||||
this.permissionManager.addPermission(perm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePermission(@NotNull Permission perm) {
|
||||
this.permissionManager.removePermission(perm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePermission(@NotNull String name) {
|
||||
this.permissionManager.removePermission(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<Permission> getDefaultPermissions(boolean op) {
|
||||
return this.permissionManager.getDefaultPermissions(op);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recalculatePermissionDefaults(@NotNull Permission perm) {
|
||||
this.permissionManager.recalculatePermissionDefaults(perm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribeToPermission(@NotNull String permission, @NotNull Permissible permissible) {
|
||||
this.permissionManager.subscribeToPermission(permission, permissible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribeFromPermission(@NotNull String permission, @NotNull Permissible permissible) {
|
||||
this.permissionManager.unsubscribeFromPermission(permission, permissible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<Permissible> getPermissionSubscriptions(@NotNull String permission) {
|
||||
return this.permissionManager.getPermissionSubscriptions(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribeToDefaultPerms(boolean op, @NotNull Permissible permissible) {
|
||||
this.permissionManager.subscribeToDefaultPerms(op, permissible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribeFromDefaultPerms(boolean op, @NotNull Permissible permissible) {
|
||||
this.permissionManager.unsubscribeFromDefaultPerms(op, permissible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<Permissible> getDefaultPermSubscriptions(boolean op) {
|
||||
return this.permissionManager.getDefaultPermSubscriptions(op);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<Permission> getPermissions() {
|
||||
return this.permissionManager.getPermissions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPermissions(@NotNull List<Permission> perm) {
|
||||
this.permissionManager.addPermissions(perm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearPermissions() {
|
||||
this.permissionManager.clearPermissions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void overridePermissionManager(@NotNull Plugin plugin, @Nullable PermissionManager permissionManager) {
|
||||
this.permissionManager = permissionManager;
|
||||
}
|
||||
|
||||
// Etc
|
||||
|
||||
@Override
|
||||
public boolean useTimings() {
|
||||
return co.aikar.timings.Timings.isTimingsEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerInterface(@NotNull Class<? extends PluginLoader> loader) throws IllegalArgumentException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public MutableGraph<String> getInstanceManagerGraph() {
|
||||
return instanceManager.getDependencyGraph();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package io.papermc.paper.plugin.manager;
|
||||
|
||||
import com.destroystokyo.paper.util.SneakyThrow;
|
||||
import io.papermc.paper.plugin.entrypoint.Entrypoint;
|
||||
import io.papermc.paper.plugin.entrypoint.EntrypointHandler;
|
||||
import io.papermc.paper.plugin.provider.PluginProvider;
|
||||
import io.papermc.paper.plugin.storage.ProviderStorage;
|
||||
import org.bukkit.plugin.InvalidPluginException;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Used for loading plugins during runtime, only supporting providers that are plugins.
|
||||
* This is only used for the plugin manager, as it only allows plugins to be
|
||||
* registered to a provider storage.
|
||||
*/
|
||||
class RuntimePluginEntrypointHandler<T extends ProviderStorage<JavaPlugin>> implements EntrypointHandler {
|
||||
|
||||
private final T providerStorage;
|
||||
|
||||
RuntimePluginEntrypointHandler(T providerStorage) {
|
||||
this.providerStorage = providerStorage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void register(Entrypoint<T> entrypoint, PluginProvider<T> provider) {
|
||||
if (!entrypoint.equals(Entrypoint.PLUGIN)) {
|
||||
SneakyThrow.sneaky(new InvalidPluginException("Plugin cannot register entrypoints other than PLUGIN during runtime. Tried registering %s!".formatted(entrypoint)));
|
||||
// We have to throw an invalid plugin exception for legacy reasons
|
||||
}
|
||||
|
||||
this.providerStorage.register((PluginProvider<JavaPlugin>) provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter(Entrypoint<?> entrypoint) {
|
||||
if (entrypoint != Entrypoint.PLUGIN) {
|
||||
throw new IllegalArgumentException("Only plugin entrypoint supported");
|
||||
}
|
||||
this.providerStorage.enter();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public T getPluginProviderStorage() {
|
||||
return this.providerStorage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package io.papermc.paper.plugin.manager;
|
||||
|
||||
import com.destroystokyo.paper.util.SneakyThrow;
|
||||
import io.papermc.paper.plugin.entrypoint.Entrypoint;
|
||||
import io.papermc.paper.plugin.entrypoint.LaunchEntryPointHandler;
|
||||
import io.papermc.paper.plugin.entrypoint.dependency.GraphDependencyContext;
|
||||
import io.papermc.paper.plugin.entrypoint.dependency.MetaDependencyTree;
|
||||
import io.papermc.paper.plugin.provider.PluginProvider;
|
||||
import io.papermc.paper.plugin.provider.type.paper.PaperPluginParent;
|
||||
import io.papermc.paper.plugin.storage.ServerPluginProviderStorage;
|
||||
import org.bukkit.plugin.InvalidPluginException;
|
||||
import org.bukkit.plugin.PluginDescriptionFile;
|
||||
import org.bukkit.plugin.UnknownDependencyException;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Used for registering a single plugin provider.
|
||||
* This has special behavior in that some errors are thrown instead of logged.
|
||||
*/
|
||||
class SingularRuntimePluginProviderStorage extends ServerPluginProviderStorage {
|
||||
|
||||
private final MetaDependencyTree dependencyTree;
|
||||
private PluginProvider<JavaPlugin> lastProvider;
|
||||
private JavaPlugin singleLoaded;
|
||||
|
||||
SingularRuntimePluginProviderStorage(MetaDependencyTree dependencyTree) {
|
||||
this.dependencyTree = dependencyTree;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(PluginProvider<JavaPlugin> provider) {
|
||||
super.register(provider);
|
||||
if (this.lastProvider != null) {
|
||||
SneakyThrow.sneaky(new InvalidPluginException("Plugin registered two JavaPlugins"));
|
||||
}
|
||||
if (provider instanceof PaperPluginParent.PaperServerPluginProvider) {
|
||||
throw new IllegalStateException("Cannot register paper plugins during runtime!");
|
||||
}
|
||||
this.lastProvider = provider;
|
||||
// Register the provider into the server entrypoint, this allows it to show in /plugins correctly.
|
||||
// Generally it might be better in the future to make a separate storage, as putting it into the entrypoint handlers doesn't make much sense.
|
||||
LaunchEntryPointHandler.INSTANCE.register(Entrypoint.PLUGIN, provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter() {
|
||||
PluginProvider<JavaPlugin> provider = this.lastProvider;
|
||||
if (provider == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Go through normal plugin loading logic
|
||||
super.enter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processProvided(PluginProvider<JavaPlugin> provider, JavaPlugin provided) {
|
||||
super.processProvided(provider, provided);
|
||||
this.singleLoaded = provided;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean throwOnCycle() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Optional<JavaPlugin> getSingleLoaded() {
|
||||
return Optional.ofNullable(this.singleLoaded);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaDependencyTree createDependencyTree() {
|
||||
return this.dependencyTree;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package io.papermc.paper.plugin.manager;
|
||||
|
||||
import org.bukkit.permissions.Permissible;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.plugin.SimplePluginManager;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/*
|
||||
This is actually so cursed I hate it.
|
||||
We need to wrap these in fields as people override the fields, so we need to access them lazily at all times.
|
||||
// TODO: When SimplePluginManager is GONE remove this and cleanup the PaperPermissionManager to use actual fields.
|
||||
*/
|
||||
class StupidSPMPermissionManagerWrapper extends PaperPermissionManager {
|
||||
|
||||
private final SimplePluginManager simplePluginManager;
|
||||
|
||||
public StupidSPMPermissionManagerWrapper(SimplePluginManager simplePluginManager) {
|
||||
this.simplePluginManager = simplePluginManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Permission> permissions() {
|
||||
return this.simplePluginManager.permissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Boolean, Set<Permission>> defaultPerms() {
|
||||
return this.simplePluginManager.defaultPerms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Map<Permissible, Boolean>> permSubs() {
|
||||
return this.simplePluginManager.permSubs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Boolean, Map<Permissible, Boolean>> defSubs() {
|
||||
return this.simplePluginManager.defSubs;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user