mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2024-11-24 03:25:19 +01:00
Extensions
This commit is contained in:
parent
b286faa1c5
commit
7a31edea09
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.luckperms.api.event.extension;
|
||||
|
||||
import net.luckperms.api.event.LuckPermsEvent;
|
||||
import net.luckperms.api.event.Param;
|
||||
import net.luckperms.api.extension.Extension;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
/**
|
||||
* Called when an {@link Extension} is loaded.
|
||||
*/
|
||||
public interface ExtensionLoadEvent extends LuckPermsEvent {
|
||||
|
||||
/**
|
||||
* Gets the extension that was loaded.
|
||||
*
|
||||
* @return the extension
|
||||
*/
|
||||
@Param(0)
|
||||
@NonNull Extension getExtension();
|
||||
|
||||
}
|
50
api/src/main/java/net/luckperms/api/extension/Extension.java
Normal file
50
api/src/main/java/net/luckperms/api/extension/Extension.java
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.luckperms.api.extension;
|
||||
|
||||
import net.luckperms.api.LuckPerms;
|
||||
|
||||
/**
|
||||
* Represents a simple extension "plugin" for LuckPerms.
|
||||
*
|
||||
* <p>Yes, that's right. A plugin for a plugin.</p>
|
||||
*
|
||||
* <p>Extensions should either declare a no-arg constructor, or a constructor
|
||||
* that accepts a single {@link LuckPerms} parameter as it's only argument.</p>
|
||||
*/
|
||||
public interface Extension {
|
||||
|
||||
/**
|
||||
* Loads the extension.
|
||||
*/
|
||||
void load();
|
||||
|
||||
/**
|
||||
* Unloads the extension.
|
||||
*/
|
||||
void unload();
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is part of luckperms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.luckperms.api.extension;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Manages extensions.
|
||||
*/
|
||||
public interface ExtensionManager {
|
||||
|
||||
/**
|
||||
* Loads the given extension.
|
||||
*
|
||||
* @param extension the extension to load
|
||||
*/
|
||||
void loadExtension(Extension extension);
|
||||
|
||||
/**
|
||||
* Loads the extension at the given path.
|
||||
*
|
||||
* @param path the path to the extension
|
||||
* @throws IOException if the extension could not be loaded
|
||||
*/
|
||||
@NonNull Extension loadExtension(Path path) throws IOException;
|
||||
|
||||
/**
|
||||
* Gets a collection of all loaded extensions.
|
||||
*
|
||||
* @return the loaded extensions
|
||||
*/
|
||||
@NonNull Collection<Extension> getLoadedExtensions();
|
||||
|
||||
}
|
@ -48,6 +48,7 @@ import net.luckperms.api.event.Cancellable;
|
||||
import net.luckperms.api.event.LuckPermsEvent;
|
||||
import net.luckperms.api.event.cause.CreationCause;
|
||||
import net.luckperms.api.event.cause.DeletionCause;
|
||||
import net.luckperms.api.event.extension.ExtensionLoadEvent;
|
||||
import net.luckperms.api.event.group.GroupCacheLoadEvent;
|
||||
import net.luckperms.api.event.group.GroupCreateEvent;
|
||||
import net.luckperms.api.event.group.GroupDataRecalculateEvent;
|
||||
@ -82,6 +83,7 @@ import net.luckperms.api.event.user.UserFirstLoginEvent;
|
||||
import net.luckperms.api.event.user.UserLoadEvent;
|
||||
import net.luckperms.api.event.user.track.UserDemoteEvent;
|
||||
import net.luckperms.api.event.user.track.UserPromoteEvent;
|
||||
import net.luckperms.api.extension.Extension;
|
||||
import net.luckperms.api.model.DataType;
|
||||
import net.luckperms.api.model.PlayerSaveResult;
|
||||
import net.luckperms.api.node.Node;
|
||||
@ -133,6 +135,10 @@ public final class EventFactory {
|
||||
return (T) GeneratedEventSpec.lookup(eventClass).newInstance(this.eventBus.getApiProvider(), params);
|
||||
}
|
||||
|
||||
public void handleExtensionLoad(Extension extension) {
|
||||
post(ExtensionLoadEvent.class, () -> generate(ExtensionLoadEvent.class, extension));
|
||||
}
|
||||
|
||||
public void handleGroupCacheLoad(Group group, GroupCachedDataManager data) {
|
||||
post(GroupCacheLoadEvent.class, () -> generate(GroupCacheLoadEvent.class, group.getApiDelegate(), data));
|
||||
}
|
||||
|
@ -0,0 +1,183 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.extension;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.util.gson.GsonProvider;
|
||||
|
||||
import net.luckperms.api.LuckPerms;
|
||||
import net.luckperms.api.extension.Extension;
|
||||
import net.luckperms.api.extension.ExtensionManager;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class SimpleExtensionManager implements ExtensionManager, AutoCloseable {
|
||||
private final LuckPermsPlugin plugin;
|
||||
private final Set<LoadedExtension> extensions = new HashSet<>();
|
||||
|
||||
public SimpleExtensionManager(LuckPermsPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
for (LoadedExtension extension : this.extensions) {
|
||||
try {
|
||||
extension.instance.unload();
|
||||
if (extension.classLoader != null) {
|
||||
extension.classLoader.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadExtension(Extension extension) {
|
||||
if (this.extensions.stream().anyMatch(e -> e.instance.equals(extension))) {
|
||||
return;
|
||||
}
|
||||
this.extensions.add(new LoadedExtension(extension, null, null));
|
||||
extension.load();
|
||||
this.plugin.getEventFactory().handleExtensionLoad(extension);
|
||||
}
|
||||
|
||||
public void loadExtensions(Path directory) {
|
||||
if (!Files.exists(directory) || !Files.isDirectory(directory)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (Stream<Path> stream = Files.list(directory)) {
|
||||
stream.forEach(path -> {
|
||||
try {
|
||||
loadExtension(path);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Extension loadExtension(Path path) throws IOException {
|
||||
if (this.extensions.stream().anyMatch(e -> path.equals(e.path))) {
|
||||
throw new IllegalStateException("Extension at path " + path.toString() + " already loaded.");
|
||||
}
|
||||
|
||||
if (!Files.exists(path)) {
|
||||
throw new NoSuchFileException("No file at " + path);
|
||||
}
|
||||
|
||||
URLClassLoader classLoader = new URLClassLoader(new URL[]{path.toUri().toURL()}, getClass().getClassLoader());
|
||||
String className;
|
||||
|
||||
try (InputStream in = classLoader.getResourceAsStream("extension.json")) {
|
||||
if (in == null) {
|
||||
throw new RuntimeException("extension.json not present in " + path.toString());
|
||||
}
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
|
||||
JsonElement parsed = GsonProvider.parser().parse(reader);
|
||||
className = parsed.getAsJsonObject().get("class").getAsString();
|
||||
}
|
||||
}
|
||||
|
||||
if (className == null) {
|
||||
throw new IllegalArgumentException("class is null");
|
||||
}
|
||||
|
||||
Class<? extends Extension> extensionClass;
|
||||
try {
|
||||
extensionClass = classLoader.loadClass(className).asSubclass(Extension.class);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
Extension extension = null;
|
||||
|
||||
try {
|
||||
Constructor<? extends Extension> constructor = extensionClass.getConstructor(LuckPerms.class);
|
||||
extension = constructor.newInstance(this.plugin.getApiProvider());
|
||||
} catch (NoSuchMethodException e) {
|
||||
// ignore
|
||||
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if (extension == null) {
|
||||
try {
|
||||
Constructor<? extends Extension> constructor = extensionClass.getConstructor();
|
||||
extension = constructor.newInstance();
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
this.extensions.add(new LoadedExtension(extension, classLoader, path));
|
||||
extension.load();
|
||||
this.plugin.getEventFactory().handleExtensionLoad(extension);
|
||||
return extension;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Collection<Extension> getLoadedExtensions() {
|
||||
return this.extensions.stream().map(e -> e.instance).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private static final class LoadedExtension {
|
||||
private final Extension instance;
|
||||
private final URLClassLoader classLoader;
|
||||
private final Path path;
|
||||
|
||||
private LoadedExtension(Extension instance, URLClassLoader classLoader, Path path) {
|
||||
this.instance = instance;
|
||||
this.classLoader = classLoader;
|
||||
this.path = path;
|
||||
}
|
||||
}
|
||||
}
|
@ -38,6 +38,7 @@ import me.lucko.luckperms.common.dependencies.Dependency;
|
||||
import me.lucko.luckperms.common.dependencies.DependencyManager;
|
||||
import me.lucko.luckperms.common.event.AbstractEventBus;
|
||||
import me.lucko.luckperms.common.event.EventFactory;
|
||||
import me.lucko.luckperms.common.extension.SimpleExtensionManager;
|
||||
import me.lucko.luckperms.common.inheritance.InheritanceHandler;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.locale.message.Message;
|
||||
@ -82,6 +83,7 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
|
||||
private CalculatorFactory calculatorFactory;
|
||||
private LuckPermsApiProvider apiProvider;
|
||||
private EventFactory eventFactory;
|
||||
private SimpleExtensionManager extensionManager;
|
||||
|
||||
/**
|
||||
* Performs the initial actions to load the plugin
|
||||
@ -166,6 +168,10 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
|
||||
ApiRegistrationUtil.registerProvider(this.apiProvider);
|
||||
registerApiOnPlatform(this.apiProvider);
|
||||
|
||||
// setup extension manager
|
||||
this.extensionManager = new SimpleExtensionManager(this);
|
||||
this.extensionManager.loadExtensions(getBootstrap().getConfigDirectory().resolve("extensions"));
|
||||
|
||||
// schedule update tasks
|
||||
int mins = getConfiguration().get(ConfigKeys.SYNC_TIME);
|
||||
if (mins > 0) {
|
||||
@ -194,6 +200,9 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
|
||||
this.permissionRegistry.stop();
|
||||
this.verboseHandler.stop();
|
||||
|
||||
// unload extensions
|
||||
this.extensionManager.close();
|
||||
|
||||
// remove any hooks into the platform
|
||||
removePlatformHooks();
|
||||
|
||||
@ -332,6 +341,11 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
|
||||
return this.apiProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleExtensionManager getExtensionManager() {
|
||||
return this.extensionManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventFactory getEventFactory() {
|
||||
return this.eventFactory;
|
||||
|
@ -34,6 +34,7 @@ import me.lucko.luckperms.common.config.LuckPermsConfiguration;
|
||||
import me.lucko.luckperms.common.context.ContextManager;
|
||||
import me.lucko.luckperms.common.dependencies.DependencyManager;
|
||||
import me.lucko.luckperms.common.event.EventFactory;
|
||||
import me.lucko.luckperms.common.extension.SimpleExtensionManager;
|
||||
import me.lucko.luckperms.common.inheritance.InheritanceHandler;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.messaging.InternalMessagingService;
|
||||
@ -146,6 +147,13 @@ public interface LuckPermsPlugin {
|
||||
*/
|
||||
LuckPermsApiProvider getApiProvider();
|
||||
|
||||
/**
|
||||
* Gets the extension manager.
|
||||
*
|
||||
* @return the extension manager
|
||||
*/
|
||||
SimpleExtensionManager getExtensionManager();
|
||||
|
||||
/**
|
||||
* Gets the command manager
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user