Nukkit support (#764)

This commit is contained in:
Luck 2018-02-18 15:10:35 +00:00 committed by GitHub
parent 794455d728
commit 31e436868d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 4697 additions and 23 deletions

View File

@ -36,7 +36,8 @@ public enum PlatformType {
BUKKIT("Bukkit"),
BUNGEE("Bungee"),
SPONGE("Sponge");
SPONGE("Sponge"),
NUKKIT("Nukkit");
private final String friendlyName;

View File

@ -244,4 +244,12 @@
</exclusions>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
</repositories>
</project>

View File

@ -55,7 +55,7 @@ import javax.annotation.Nonnull;
*
* Injected by {@link InjectorDefaultsMap}.
*/
public class LPDefaultsMap implements Map<Boolean, Set<Permission>> {
public final class LPDefaultsMap implements Map<Boolean, Set<Permission>> {
// keyset for all instances
private static final Set<Boolean> KEY_SET = ImmutableSet.of(Boolean.TRUE, Boolean.FALSE);

View File

@ -56,7 +56,7 @@ import javax.annotation.Nonnull;
*
* Injected by {@link InjectorPermissionMap}.
*/
public class LPPermissionMap extends ForwardingMap<String, Permission> {
public final class LPPermissionMap extends ForwardingMap<String, Permission> {
// Uses perm.getName().toLowerCase(java.util.Locale.ENGLISH); to determine the key
private final Map<String, Permission> delegate = new ConcurrentHashMap<>();

View File

@ -33,7 +33,6 @@ import me.lucko.luckperms.common.utils.ImmutableCollectors;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.Permission;
import org.bukkit.plugin.PluginManager;
import java.util.Collection;
@ -56,7 +55,7 @@ import javax.annotation.Nonnull;
*
* Bukkit for some reason sometimes uses subscription status to determine whether
* a permissible has a given node, instead of checking directly with
* {@link Permissible#hasPermission(Permission)}.
* {@link Permissible#hasPermission(String)}.
*
* {@link org.bukkit.Server#broadcast(String, String)} is a good example of this.
*
@ -68,7 +67,7 @@ import javax.annotation.Nonnull;
*
* Injected by {@link InjectorSubscriptionMap}.
*/
public class LPSubscriptionMap extends HashMap<String, Map<Permissible, Boolean>> {
public final class LPSubscriptionMap extends HashMap<String, Map<Permissible, Boolean>> {
// the plugin instance
final LPBukkitPlugin plugin;
@ -151,7 +150,7 @@ public class LPSubscriptionMap extends HashMap<String, Map<Permissible, Boolean>
/**
* Value map extension which includes LP objects in Permissible related queries.
*/
public class LPSubscriptionValueMap implements Map<Permissible, Boolean> {
public final class LPSubscriptionValueMap implements Map<Permissible, Boolean> {
// the permission being mapped to this value map
private final String permission;
@ -159,7 +158,7 @@ public class LPSubscriptionMap extends HashMap<String, Map<Permissible, Boolean>
// the backing map
private final Map<Permissible, Boolean> backing;
public LPSubscriptionValueMap(String permission, Map<Permissible, Boolean> backing) {
private LPSubscriptionValueMap(String permission, Map<Permissible, Boolean> backing) {
this.permission = permission;
this.backing = backing;
}
@ -235,6 +234,11 @@ public class LPSubscriptionMap extends HashMap<String, Map<Permissible, Boolean>
return false;
}
@Override
public int size() {
return Math.max(1, this.backing.size());
}
// just delegate to the backing map
@Override
@ -247,13 +251,6 @@ public class LPSubscriptionMap extends HashMap<String, Map<Permissible, Boolean>
return this.backing.remove(key);
}
// the following methods are not used in the current impls of PluginManager, but just delegate them for now
@Override
public int size() {
return this.backing.size();
}
@Override
public boolean containsValue(Object value) {
return this.backing.containsValue(value);

View File

@ -208,6 +208,21 @@ public class ConfigKeys {
*/
public static final ConfigKey<Boolean> APPLY_BUKKIT_ATTACHMENT_PERMISSIONS = EnduringKey.wrap(BooleanKey.of("apply-bukkit-attachment-permissions", true));
/**
* If Nukkit child permissions are being applied. This setting is ignored on other platforms.
*/
public static final ConfigKey<Boolean> APPLY_NUKKIT_CHILD_PERMISSIONS = EnduringKey.wrap(BooleanKey.of("apply-nukkit-child-permissions", true));
/**
* If Nukkit default permissions are being applied. This setting is ignored on other platforms.
*/
public static final ConfigKey<Boolean> APPLY_NUKKIT_DEFAULT_PERMISSIONS = EnduringKey.wrap(BooleanKey.of("apply-nukkit-default-permissions", true));
/**
* If Nukkit attachment permissions are being applied. This setting is ignored on other platforms.
*/
public static final ConfigKey<Boolean> APPLY_NUKKIT_ATTACHMENT_PERMISSIONS = EnduringKey.wrap(BooleanKey.of("apply-nukkit-attachment-permissions", true));
/**
* If BungeeCord configured permissions are being applied. This setting is ignored on other platforms.
*/

156
nukkit/pom.xml Normal file
View File

@ -0,0 +1,156 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>luckperms-nukkit</artifactId>
<packaging>jar</packaging>
<build>
<defaultGoal>clean package</defaultGoal>
<finalName>LuckPerms-Nukkit-${full.version}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler.version}</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>${shade.version}</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>false</minimizeJar>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<!-- shaded dependencies -->
<relocation>
<pattern>net.kyori.text</pattern>
<shadedPattern>me.lucko.luckperms.lib.text</shadedPattern>
</relocation>
<!-- relocated dependencies -->
<relocation>
<pattern>com.github.benmanes.caffeine</pattern>
<shadedPattern>me.lucko.luckperms.lib.caffeine</shadedPattern>
</relocation>
<relocation>
<pattern>okio</pattern>
<shadedPattern>me.lucko.luckperms.lib.okio</shadedPattern>
</relocation>
<relocation>
<pattern>okhttp3</pattern>
<shadedPattern>me.lucko.luckperms.lib.okhttp3</shadedPattern>
</relocation>
<relocation>
<pattern>org.mariadb.jdbc</pattern>
<shadedPattern>me.lucko.luckperms.lib.mariadb</shadedPattern>
</relocation>
<relocation>
<pattern>com.mysql</pattern>
<shadedPattern>me.lucko.luckperms.lib.mysql</shadedPattern>
</relocation>
<relocation>
<pattern>org.postgresql</pattern>
<shadedPattern>me.lucko.luckperms.lib.postgresql</shadedPattern>
</relocation>
<relocation>
<pattern>com.zaxxer.hikari</pattern>
<shadedPattern>me.lucko.luckperms.lib.hikari</shadedPattern>
</relocation>
<relocation>
<pattern>com.mongodb</pattern>
<shadedPattern>me.lucko.luckperms.lib.mongodb</shadedPattern>
</relocation>
<relocation>
<pattern>org.bson</pattern>
<shadedPattern>me.lucko.luckperms.lib.bson</shadedPattern>
</relocation>
<relocation>
<pattern>redis.clients.jedis</pattern>
<shadedPattern>me.lucko.luckperms.lib.jedis</shadedPattern>
</relocation>
<relocation>
<pattern>org.apache.commons.pool2</pattern>
<shadedPattern>me.lucko.luckperms.lib.commonspool2</shadedPattern>
</relocation>
<relocation>
<pattern>ninja.leaping.configurate</pattern>
<shadedPattern>me.lucko.luckperms.lib.configurate</shadedPattern>
</relocation>
<relocation>
<pattern>com.typesafe.config</pattern>
<shadedPattern>me.lucko.luckperms.lib.hocon</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<!-- common -->
<dependency>
<groupId>me.lucko.luckperms</groupId>
<artifactId>luckperms-common</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<!-- jsr305 -->
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
<scope>provided</scope>
</dependency>
<!-- caffeine -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.6.1</version>
<scope>provided</scope>
</dependency>
<!-- Nukkit -->
<dependency>
<groupId>cn.nukkit</groupId>
<artifactId>nukkit</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>nukkit-repo</id>
<url>https://repo.potestas.xyz/main/</url>
</repository>
</repositories>
</project>

View File

@ -0,0 +1,634 @@
/*
* 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.nukkit;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.LuckPermsApi;
import me.lucko.luckperms.api.platform.PlatformType;
import me.lucko.luckperms.common.actionlog.LogDispatcher;
import me.lucko.luckperms.common.api.ApiRegistrationUtil;
import me.lucko.luckperms.common.api.LuckPermsApiProvider;
import me.lucko.luckperms.common.buffers.BufferedRequest;
import me.lucko.luckperms.common.buffers.UpdateTaskBuffer;
import me.lucko.luckperms.common.caching.handlers.CachedStateManager;
import me.lucko.luckperms.common.calculators.CalculatorFactory;
import me.lucko.luckperms.common.commands.CommandPermission;
import me.lucko.luckperms.common.commands.sender.Sender;
import me.lucko.luckperms.common.config.AbstractConfiguration;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.config.LuckPermsConfiguration;
import me.lucko.luckperms.common.contexts.ContextManager;
import me.lucko.luckperms.common.contexts.LuckPermsCalculator;
import me.lucko.luckperms.common.dependencies.DependencyManager;
import me.lucko.luckperms.common.dependencies.DependencyRegistry;
import me.lucko.luckperms.common.dependencies.classloader.PluginClassLoader;
import me.lucko.luckperms.common.dependencies.classloader.ReflectionClassLoader;
import me.lucko.luckperms.common.event.EventFactory;
import me.lucko.luckperms.common.inheritance.InheritanceHandler;
import me.lucko.luckperms.common.locale.LocaleManager;
import me.lucko.luckperms.common.locale.NoopLocaleManager;
import me.lucko.luckperms.common.locale.SimpleLocaleManager;
import me.lucko.luckperms.common.logging.Logger;
import me.lucko.luckperms.common.logging.SenderLogger;
import me.lucko.luckperms.common.managers.group.StandardGroupManager;
import me.lucko.luckperms.common.managers.track.StandardTrackManager;
import me.lucko.luckperms.common.managers.user.StandardUserManager;
import me.lucko.luckperms.common.messaging.InternalMessagingService;
import me.lucko.luckperms.common.messaging.MessagingFactory;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.storage.Storage;
import me.lucko.luckperms.common.storage.StorageFactory;
import me.lucko.luckperms.common.storage.StorageType;
import me.lucko.luckperms.common.storage.dao.file.FileWatcher;
import me.lucko.luckperms.common.tasks.CacheHousekeepingTask;
import me.lucko.luckperms.common.tasks.ExpireTemporaryTask;
import me.lucko.luckperms.common.tasks.UpdateTask;
import me.lucko.luckperms.common.treeview.PermissionVault;
import me.lucko.luckperms.common.verbose.VerboseHandler;
import me.lucko.luckperms.nukkit.calculators.NukkitCalculatorFactory;
import me.lucko.luckperms.nukkit.contexts.NukkitContextManager;
import me.lucko.luckperms.nukkit.contexts.WorldCalculator;
import me.lucko.luckperms.nukkit.listeners.NukkitConnectionListener;
import me.lucko.luckperms.nukkit.listeners.NukkitPlatformListener;
import me.lucko.luckperms.nukkit.model.PermissionDefault;
import me.lucko.luckperms.nukkit.model.permissible.LPPermissible;
import me.lucko.luckperms.nukkit.model.permissible.PermissibleInjector;
import me.lucko.luckperms.nukkit.model.permissible.PermissibleMonitoringInjector;
import me.lucko.luckperms.nukkit.model.server.InjectorDefaultsMap;
import me.lucko.luckperms.nukkit.model.server.InjectorPermissionMap;
import me.lucko.luckperms.nukkit.model.server.InjectorSubscriptionMap;
import me.lucko.luckperms.nukkit.model.server.LPDefaultsMap;
import me.lucko.luckperms.nukkit.model.server.LPPermissionMap;
import me.lucko.luckperms.nukkit.model.server.LPSubscriptionMap;
import cn.nukkit.Player;
import cn.nukkit.command.PluginCommand;
import cn.nukkit.event.HandlerList;
import cn.nukkit.permission.Permission;
import cn.nukkit.plugin.PluginBase;
import cn.nukkit.plugin.PluginManager;
import cn.nukkit.plugin.service.ServicePriority;
import cn.nukkit.utils.Config;
import java.io.File;
import java.io.InputStream;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
import javax.annotation.Nullable;
/**
* LuckPerms implementation for the Nukkit API.
*/
public class LPNukkitPlugin extends PluginBase implements LuckPermsPlugin {
private long startTime;
private NukkitSchedulerAdapter scheduler;
private NukkitCommandExecutor commandManager;
private LuckPermsConfiguration configuration;
private StandardUserManager userManager;
private StandardGroupManager groupManager;
private StandardTrackManager trackManager;
private Storage storage;
private FileWatcher fileWatcher = null;
private InternalMessagingService messagingService = null;
private LuckPermsApiProvider apiProvider;
private EventFactory eventFactory;
private Logger log;
private LPSubscriptionMap subscriptionMap;
private LPPermissionMap permissionMap;
private LPDefaultsMap defaultPermissionMap;
private LocaleManager localeManager;
private PluginClassLoader pluginClassLoader;
private DependencyManager dependencyManager;
private InheritanceHandler inheritanceHandler;
private CachedStateManager cachedStateManager;
private ContextManager<Player> contextManager;
private CalculatorFactory calculatorFactory;
private BufferedRequest<Void> updateTaskBuffer;
private VerboseHandler verboseHandler;
private NukkitSenderFactory senderFactory;
private PermissionVault permissionVault;
private LogDispatcher logDispatcher;
private Set<UUID> uniqueConnections = ConcurrentHashMap.newKeySet();
@Override
public void onLoad() {
// setup minimal functionality in order to load initial dependencies
this.scheduler = new NukkitSchedulerAdapter(this);
this.localeManager = new NoopLocaleManager();
this.senderFactory = new NukkitSenderFactory(this);
this.log = new SenderLogger(this, getConsoleSender());
this.pluginClassLoader = new ReflectionClassLoader(this);
this.dependencyManager = new DependencyManager(this);
this.dependencyManager.loadDependencies(DependencyRegistry.GLOBAL_DEPENDENCIES);
}
@Override
public void onEnable() {
this.startTime = System.currentTimeMillis();
sendStartupBanner(getConsoleSender());
this.verboseHandler = new VerboseHandler(this.scheduler.asyncNukkit(), getVersion());
this.permissionVault = new PermissionVault(this.scheduler.asyncNukkit());
this.logDispatcher = new LogDispatcher(this);
getLog().info("Loading configuration...");
this.configuration = new AbstractConfiguration(this, new NukkitConfigAdapter(this, resolveConfig("config.yml")));
this.configuration.loadAll();
StorageFactory storageFactory = new StorageFactory(this);
Set<StorageType> storageTypes = storageFactory.getRequiredTypes(StorageType.H2);
this.dependencyManager.loadStorageDependencies(storageTypes);
// register events
NukkitConnectionListener connectionListener = new NukkitConnectionListener(this);
getServer().getPluginManager().registerEvents(connectionListener, this);
getServer().getPluginManager().registerEvents(new NukkitPlatformListener(this), this);
if (getConfiguration().get(ConfigKeys.WATCH_FILES)) {
this.fileWatcher = new FileWatcher(this);
getScheduler().asyncRepeating(this.fileWatcher, 30L);
}
// initialise datastore
this.storage = storageFactory.getInstance(StorageType.H2);
// initialise messaging
this.messagingService = new MessagingFactory<>(this).getInstance();
// setup the update task buffer
this.updateTaskBuffer = new UpdateTaskBuffer(this);
// load locale
this.localeManager = new SimpleLocaleManager();
this.localeManager.tryLoad(this, new File(getDataFolder(), "lang.yml"));
// register commands
this.commandManager = new NukkitCommandExecutor(this);
PluginCommand main = (PluginCommand) getServer().getPluginCommand("luckperms");
main.setExecutor(this.commandManager);
// load internal managers
getLog().info("Loading internal permission managers...");
this.inheritanceHandler = new InheritanceHandler(this);
this.userManager = new StandardUserManager(this);
this.groupManager = new StandardGroupManager(this);
this.trackManager = new StandardTrackManager(this);
this.calculatorFactory = new NukkitCalculatorFactory(this);
this.cachedStateManager = new CachedStateManager();
// setup context manager
this.contextManager = new NukkitContextManager(this);
this.contextManager.registerCalculator(new WorldCalculator(this));
this.contextManager.registerStaticCalculator(new LuckPermsCalculator(getConfiguration()));
// inject our own custom permission maps
Runnable[] injectors = new Runnable[]{
new InjectorSubscriptionMap(this),
new InjectorPermissionMap(this),
new InjectorDefaultsMap(this)
};
for (Runnable injector : injectors) {
injector.run();
// schedule another injection after all plugins have loaded
// the entire pluginmanager instance is replaced by some plugins :(
this.scheduler.asyncLater(injector, 1L);
}
// inject verbose handlers into internal nukkit objects
new PermissibleMonitoringInjector(this).run();
// register with the LP API
this.apiProvider = new LuckPermsApiProvider(this);
// setup event factory
this.eventFactory = new EventFactory(this, this.apiProvider);
ApiRegistrationUtil.registerProvider(this.apiProvider);
getServer().getServiceManager().register(LuckPermsApi.class, this.apiProvider, this, ServicePriority.NORMAL);
// schedule update tasks
int mins = getConfiguration().get(ConfigKeys.SYNC_TIME);
if (mins > 0) {
long ticks = mins * 60 * 20;
this.scheduler.asyncRepeating(() -> this.updateTaskBuffer.request(), ticks);
}
this.scheduler.asyncLater(() -> this.updateTaskBuffer.request(), 40L);
// run an update instantly.
getLog().info("Performing initial data load...");
try {
new UpdateTask(this, true).run();
} catch (Exception e) {
e.printStackTrace();
}
// register tasks
this.scheduler.asyncRepeating(new ExpireTemporaryTask(this), 60L);
this.scheduler.asyncRepeating(new CacheHousekeepingTask(this), 2400L);
// register permissions
try {
PluginManager pm = getServer().getPluginManager();
PermissionDefault permDefault = getConfiguration().get(ConfigKeys.COMMANDS_ALLOW_OP) ? PermissionDefault.OP : PermissionDefault.FALSE;
for (CommandPermission p : CommandPermission.values()) {
pm.addPermission(new Permission(p.getPermission(), null, permDefault.toString()));
}
} catch (Exception e) {
// this throws an exception if the plugin is /reloaded, grr
}
if (!getConfiguration().get(ConfigKeys.OPS_ENABLED)) {
Config ops = getServer().getOps();
ops.getKeys(false).forEach(ops::remove);
}
// replace the temporary executor when the Nukkit one starts
getServer().getScheduler().scheduleTask(this, () -> this.scheduler.setUseFallback(false), true);
// Load any online users (in the case of a reload)
for (Player player : getServer().getOnlinePlayers().values()) {
this.scheduler.doAsync(() -> {
try {
User user = connectionListener.loadUser(player.getUniqueId(), player.getName());
if (user != null) {
this.scheduler.doSync(() -> {
try {
LPPermissible lpPermissible = new LPPermissible(player, user, this);
PermissibleInjector.inject(player, lpPermissible);
} catch (Throwable t) {
t.printStackTrace();
}
});
}
} catch (Exception e) {
e.printStackTrace();
}
});
}
getLog().info("Successfully enabled. (took " + (System.currentTimeMillis() - this.startTime) + "ms)");
}
@Override
public void onDisable() {
// Switch back to the fallback executor, the nukkit one won't allow new tasks
this.scheduler.setUseFallback(true);
this.permissionVault.shutdown();
this.verboseHandler.shutdown();
// uninject from players
for (Player player : getServer().getOnlinePlayers().values()) {
try {
PermissibleInjector.unInject(player, false);
} catch (Exception e) {
e.printStackTrace();
}
if (getConfiguration().get(ConfigKeys.AUTO_OP)) {
player.setOp(false);
}
final User user = getUserManager().getIfLoaded(player.getUniqueId());
if (user != null) {
user.getCachedData().invalidateCaches();
getUserManager().unload(user);
}
}
// uninject custom maps
InjectorSubscriptionMap.uninject();
InjectorPermissionMap.uninject();
InjectorDefaultsMap.uninject();
getLog().info("Closing storage...");
this.storage.shutdown();
if (this.fileWatcher != null) {
this.fileWatcher.close();
}
if (this.messagingService != null) {
getLog().info("Closing messaging service...");
this.messagingService.close();
}
ApiRegistrationUtil.unregisterProvider();
getServer().getServiceManager().cancel(this);
getLog().info("Shutting down internal scheduler...");
this.scheduler.shutdown();
// Nukkit will do this again when #onDisable completes, but we do it early to prevent NPEs elsewhere.
getServer().getScheduler().cancelTask(this);
HandlerList.unregisterAll(this);
getLog().info("Goodbye!");
}
public void refreshAutoOp(User user, Player player) {
if (user == null) {
return;
}
if (getConfiguration().get(ConfigKeys.AUTO_OP)) {
Map<String, Boolean> backing = user.getCachedData().getPermissionData(this.contextManager.getApplicableContexts(player)).getImmutableBacking();
boolean op = Optional.ofNullable(backing.get("luckperms.autoop")).orElse(false);
player.setOp(op);
}
}
private File resolveConfig(String file) {
File configFile = new File(getDataFolder(), file);
if (!configFile.exists()) {
getDataFolder().mkdirs();
saveResource("config.yml", false);
}
return configFile;
}
public LPSubscriptionMap getSubscriptionMap() {
return this.subscriptionMap;
}
public void setSubscriptionMap(LPSubscriptionMap subscriptionMap) {
this.subscriptionMap = subscriptionMap;
}
public LPPermissionMap getPermissionMap() {
return this.permissionMap;
}
public void setPermissionMap(LPPermissionMap permissionMap) {
this.permissionMap = permissionMap;
}
public LPDefaultsMap getDefaultPermissionMap() {
return this.defaultPermissionMap;
}
public void setDefaultPermissionMap(LPDefaultsMap defaultPermissionMap) {
this.defaultPermissionMap = defaultPermissionMap;
}
@Override
public Optional<InternalMessagingService> getMessagingService() {
return Optional.ofNullable(this.messagingService);
}
@Override
public void setMessagingService(InternalMessagingService messagingService) {
if (this.messagingService == null) {
this.messagingService = messagingService;
}
}
@Override
public Optional<FileWatcher> getFileWatcher() {
return Optional.ofNullable(this.fileWatcher);
}
@Override
public String getVersion() {
return getDescription().getVersion();
}
@Override
public PlatformType getServerType() {
return PlatformType.NUKKIT;
}
@Override
public String getServerBrand() {
return getServer().getName();
}
@Override
public String getServerVersion() {
return getServer().getVersion() + " - " + getServer().getNukkitVersion();
}
@Override
public String getServerName() {
return getServer().getServerUniqueId().toString();
}
@Override
public File getDataDirectory() {
return super.getDataFolder();
}
@Override
public InputStream getResourceStream(String path) {
return getResource(path);
}
@Override
public Player getPlayer(User user) {
return getServer().getOnlinePlayers().get(user.getUuid());
}
@Override
public Optional<UUID> lookupUuid(String username) {
return Optional.empty();
}
@Nullable
@Override
public Contexts getContextForUser(User user) {
Player player = getPlayer(user);
if (player == null) {
return null;
}
return this.contextManager.getApplicableContexts(player);
}
@Override
public int getPlayerCount() {
return getServer().getOnlinePlayers().size();
}
@Override
public Stream<String> getPlayerList() {
return getServer().getOnlinePlayers().values().stream().map(Player::getName);
}
@Override
public Stream<UUID> getOnlinePlayers() {
return getServer().getOnlinePlayers().values().stream().map(Player::getUniqueId);
}
@Override
public boolean isPlayerOnline(UUID external) {
Player player = getServer().getOnlinePlayers().get(external);
return player != null && player.isOnline();
}
@Override
public Stream<Sender> getOnlineSenders() {
return Stream.concat(
Stream.of(getConsoleSender()),
getServer().getOnlinePlayers().values().stream().map(p -> getSenderFactory().wrap(p))
);
}
@Override
public Sender getConsoleSender() {
return getSenderFactory().wrap(getServer().getConsoleSender());
}
@Override
public long getStartTime() {
return this.startTime;
}
@Override
public NukkitSchedulerAdapter getScheduler() {
return this.scheduler;
}
@Override
public NukkitCommandExecutor getCommandManager() {
return this.commandManager;
}
@Override
public LuckPermsConfiguration getConfiguration() {
return this.configuration;
}
@Override
public StandardUserManager getUserManager() {
return this.userManager;
}
@Override
public StandardGroupManager getGroupManager() {
return this.groupManager;
}
@Override
public StandardTrackManager getTrackManager() {
return this.trackManager;
}
@Override
public Storage getStorage() {
return this.storage;
}
@Override
public LuckPermsApiProvider getApiProvider() {
return this.apiProvider;
}
@Override
public EventFactory getEventFactory() {
return this.eventFactory;
}
@Override
public Logger getLog() {
return this.log;
}
@Override
public LocaleManager getLocaleManager() {
return this.localeManager;
}
@Override
public PluginClassLoader getPluginClassLoader() {
return this.pluginClassLoader;
}
@Override
public DependencyManager getDependencyManager() {
return this.dependencyManager;
}
@Override
public CachedStateManager getCachedStateManager() {
return this.cachedStateManager;
}
@Override
public ContextManager<Player> getContextManager() {
return this.contextManager;
}
@Override
public InheritanceHandler getInheritanceHandler() {
return this.inheritanceHandler;
}
@Override
public CalculatorFactory getCalculatorFactory() {
return this.calculatorFactory;
}
@Override
public BufferedRequest<Void> getUpdateTaskBuffer() {
return this.updateTaskBuffer;
}
@Override
public VerboseHandler getVerboseHandler() {
return this.verboseHandler;
}
public NukkitSenderFactory getSenderFactory() {
return this.senderFactory;
}
@Override
public PermissionVault getPermissionVault() {
return this.permissionVault;
}
@Override
public LogDispatcher getLogDispatcher() {
return this.logDispatcher;
}
@Override
public Set<UUID> getUniqueConnections() {
return this.uniqueConnections;
}
}

View File

@ -0,0 +1,59 @@
/*
* 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.nukkit;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import me.lucko.luckperms.common.commands.CommandManager;
import me.lucko.luckperms.common.commands.sender.Sender;
import cn.nukkit.command.Command;
import cn.nukkit.command.CommandExecutor;
import cn.nukkit.command.CommandSender;
import java.util.List;
public class NukkitCommandExecutor extends CommandManager implements CommandExecutor {
private static final Splitter ARGUMENT_SPLITTER = Splitter.on(COMMAND_SEPARATOR_PATTERN).omitEmptyStrings();
private static final Joiner ARGUMENT_JOINER = Joiner.on(' ');
private final LPNukkitPlugin plugin;
NukkitCommandExecutor(LPNukkitPlugin plugin) {
super(plugin);
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
Sender lpSender = this.plugin.getSenderFactory().wrap(sender);
List<String> arguments = stripQuotes(ARGUMENT_SPLITTER.splitToList(ARGUMENT_JOINER.join(args)));
onCommand(lpSender, label, arguments);
return true;
}
}

View File

@ -0,0 +1,109 @@
/*
* 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.nukkit;
import me.lucko.luckperms.common.config.adapter.AbstractConfigurationAdapter;
import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import cn.nukkit.utils.Config;
import cn.nukkit.utils.ConfigSection;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class NukkitConfigAdapter extends AbstractConfigurationAdapter implements ConfigurationAdapter {
private final File file;
private Config configuration;
public NukkitConfigAdapter(LuckPermsPlugin plugin, File file) {
super(plugin);
this.file = file;
reload();
}
@Override
public void reload() {
this.configuration = new Config(this.file, Config.YAML);
}
@Override
public boolean contains(String path) {
return this.configuration.exists(path);
}
@Override
public String getString(String path, String def) {
return this.configuration.getString(path, def);
}
@Override
public int getInt(String path, int def) {
return this.configuration.getInt(path, def);
}
@Override
public boolean getBoolean(String path, boolean def) {
return this.configuration.getBoolean(path, def);
}
@Override
public List<String> getList(String path, List<String> def) {
List<String> ret = this.configuration.getStringList(path);
return ret == null ? def : ret;
}
@Override
public List<String> getObjectList(String path, List<String> def) {
ConfigSection section = this.configuration.getSection(path);
if (section == null) {
return def;
}
Set<String> keys = section.getKeys(false);
return keys == null ? def : new ArrayList<>(keys);
}
@Override
public Map<String, String> getMap(String path, Map<String, String> def) {
Map<String, String> map = new HashMap<>();
ConfigSection section = this.configuration.getSection(path);
if (section == null) {
return def;
}
for (String key : section.getKeys(false)) {
map.put(key, section.getString(key));
}
return map;
}
}

View File

@ -0,0 +1,186 @@
/*
* 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.nukkit;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import me.lucko.luckperms.common.plugin.SchedulerAdapter;
import me.lucko.luckperms.common.plugin.SchedulerTask;
import me.lucko.luckperms.common.utils.SafeIteration;
import cn.nukkit.scheduler.ServerScheduler;
import cn.nukkit.scheduler.TaskHandler;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
public class NukkitSchedulerAdapter implements SchedulerAdapter {
private final LPNukkitPlugin plugin;
private final ExecutorService asyncFallback;
private final Executor asyncNukkit;
private final Executor sync;
private final Executor async;
private boolean useFallback = true;
private final Set<SchedulerTask> tasks = ConcurrentHashMap.newKeySet();
public NukkitSchedulerAdapter(LPNukkitPlugin plugin) {
this.plugin = plugin;
this.sync = new SyncExecutor();
this.asyncFallback = new FallbackAsyncExecutor();
this.asyncNukkit = new NukkitAsyncExecutor();
this.async = new AsyncExecutor();
}
private ServerScheduler scheduler() {
return this.plugin.getServer().getScheduler();
}
@Override
public void doAsync(Runnable runnable) {
async().execute(runnable);
}
@Override
public void doSync(Runnable runnable) {
sync().execute(runnable);
}
@Override
public SchedulerTask asyncRepeating(Runnable runnable, long intervalTicks) {
SchedulerTask task = new NukkitSchedulerTask(scheduler().scheduleDelayedRepeatingTask(this.plugin, runnable, (int) intervalTicks, (int) intervalTicks, true));
this.tasks.add(task);
return task;
}
@Override
public SchedulerTask syncRepeating(Runnable runnable, long intervalTicks) {
SchedulerTask task = new NukkitSchedulerTask(scheduler().scheduleDelayedRepeatingTask(this.plugin, runnable, (int) intervalTicks, (int) intervalTicks, false));
this.tasks.add(task);
return task;
}
@Override
public SchedulerTask asyncLater(Runnable runnable, long delayTicks) {
return new NukkitSchedulerTask(scheduler().scheduleDelayedTask(this.plugin, runnable, (int) delayTicks, true));
}
@Override
public SchedulerTask syncLater(Runnable runnable, long delayTicks) {
return new NukkitSchedulerTask(scheduler().scheduleDelayedTask(this.plugin, runnable, (int) delayTicks, false));
}
@Override
public void shutdown() {
SafeIteration.iterate(this.tasks, SchedulerTask::cancel);
// wait for executor
this.asyncFallback.shutdown();
try {
this.asyncFallback.awaitTermination(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public ExecutorService asyncFallback() {
return this.asyncFallback;
}
public Executor asyncNukkit() {
return this.asyncNukkit;
}
@Override
public Executor sync() {
return this.sync;
}
@Override
public Executor async() {
return this.async;
}
public void setUseFallback(boolean useFallback) {
this.useFallback = useFallback;
}
private final class SyncExecutor implements Executor {
@Override
public void execute(@Nonnull Runnable runnable) {
NukkitSchedulerAdapter.this.plugin.getServer().getScheduler().scheduleTask(NukkitSchedulerAdapter.this.plugin, runnable, false);
}
}
private final class AsyncExecutor implements Executor {
@Override
public void execute(@Nonnull Runnable runnable) {
if (NukkitSchedulerAdapter.this.useFallback || !NukkitSchedulerAdapter.this.plugin.isEnabled()) {
NukkitSchedulerAdapter.this.asyncFallback.execute(runnable);
} else {
NukkitSchedulerAdapter.this.asyncNukkit.execute(runnable);
}
}
}
private final class NukkitAsyncExecutor implements Executor {
@Override
public void execute(@Nonnull Runnable runnable) {
NukkitSchedulerAdapter.this.plugin.getServer().getScheduler().scheduleTask(NukkitSchedulerAdapter.this.plugin, runnable, true);
}
}
private static final class FallbackAsyncExecutor extends ThreadPoolExecutor {
private FallbackAsyncExecutor() {
super(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), new ThreadFactoryBuilder().setNameFormat("luckperms-fallback-%d").build());
}
}
private static final class NukkitSchedulerTask implements SchedulerTask {
private final TaskHandler task;
private NukkitSchedulerTask(TaskHandler task) {
this.task = task;
}
@Override
public void cancel() {
this.task.cancel();
}
}
}

View File

@ -0,0 +1,109 @@
/*
* 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.nukkit;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.common.commands.CommandManager;
import me.lucko.luckperms.common.commands.sender.SenderFactory;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.TextUtils;
import net.kyori.text.Component;
import cn.nukkit.Player;
import cn.nukkit.command.CommandSender;
import cn.nukkit.command.ConsoleCommandSender;
import java.util.UUID;
public class NukkitSenderFactory extends SenderFactory<CommandSender> {
public NukkitSenderFactory(LuckPermsPlugin plugin) {
super(plugin);
}
@Override
protected String getName(CommandSender sender) {
if (sender instanceof Player) {
return sender.getName();
}
return CommandManager.CONSOLE_NAME;
}
@Override
protected UUID getUuid(CommandSender sender) {
if (sender instanceof Player) {
return ((Player) sender).getUniqueId();
}
return CommandManager.CONSOLE_UUID;
}
@Override
protected void sendMessage(CommandSender sender, String s) {
// we can safely send async for players and the console
if (sender instanceof Player || sender instanceof ConsoleCommandSender) {
sender.sendMessage(s);
return;
}
// otherwise, send the message sync
getPlugin().getScheduler().doSync(new SyncMessengerAgent(sender, s));
}
@Override
protected void sendMessage(CommandSender sender, Component message) {
// Fallback to legacy format
sendMessage(sender, TextUtils.toLegacy(message));
}
@Override
protected Tristate getPermissionValue(CommandSender sender, String node) {
boolean isSet = sender.isPermissionSet(node);
boolean val = sender.hasPermission(node);
return !isSet ? val ? Tristate.TRUE : Tristate.UNDEFINED : Tristate.fromBoolean(val);
}
@Override
protected boolean hasPermission(CommandSender sender, String node) {
return sender.hasPermission(node);
}
private static final class SyncMessengerAgent implements Runnable {
private final CommandSender sender;
private final String message;
private SyncMessengerAgent(CommandSender sender, String message) {
this.sender = sender;
this.message = message;
}
@Override
public void run() {
this.sender.sendMessage(this.message);
}
}
}

View File

@ -0,0 +1,75 @@
/*
* 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.nukkit.calculators;
import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory;
import me.lucko.luckperms.common.calculators.PermissionCalculator;
import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.processors.MapProcessor;
import me.lucko.luckperms.common.processors.PermissionProcessor;
import me.lucko.luckperms.common.processors.RegexProcessor;
import me.lucko.luckperms.common.processors.WildcardProcessor;
import me.lucko.luckperms.common.references.HolderType;
import me.lucko.luckperms.nukkit.LPNukkitPlugin;
import me.lucko.luckperms.nukkit.processors.ChildProcessor;
import me.lucko.luckperms.nukkit.processors.DefaultsProcessor;
public class NukkitCalculatorFactory extends AbstractCalculatorFactory {
private final LPNukkitPlugin plugin;
public NukkitCalculatorFactory(LPNukkitPlugin plugin) {
this.plugin = plugin;
}
@Override
public PermissionCalculator build(Contexts contexts, PermissionCalculatorMetadata metadata) {
ImmutableList.Builder<PermissionProcessor> processors = ImmutableList.builder();
processors.add(new MapProcessor());
if (this.plugin.getConfiguration().get(ConfigKeys.APPLY_NUKKIT_CHILD_PERMISSIONS)) {
processors.add(new ChildProcessor(this.plugin));
}
if (this.plugin.getConfiguration().get(ConfigKeys.APPLYING_REGEX)) {
processors.add(new RegexProcessor());
}
if (this.plugin.getConfiguration().get(ConfigKeys.APPLYING_WILDCARDS)) {
processors.add(new WildcardProcessor());
}
if (this.plugin.getConfiguration().get(ConfigKeys.APPLY_NUKKIT_DEFAULT_PERMISSIONS) && metadata.getHolderType() == HolderType.USER) {
processors.add(new DefaultsProcessor(this.plugin, contexts.isOp()));
}
return registerCalculator(new PermissionCalculator(this.plugin, metadata, processors.build()));
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.nukkit.contexts;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.contexts.AbstractContextManager;
import me.lucko.luckperms.nukkit.LPNukkitPlugin;
import cn.nukkit.Player;
public class NukkitContextManager extends AbstractContextManager<Player> {
public NukkitContextManager(LPNukkitPlugin plugin) {
super(plugin, Player.class);
}
@Override
public Contexts formContexts(Player subject, ImmutableContextSet contextSet) {
return new Contexts(
contextSet,
this.plugin.getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
this.plugin.getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS),
true,
this.plugin.getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
this.plugin.getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS),
subject.isOp()
);
}
}

View File

@ -0,0 +1,56 @@
/*
* 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.nukkit.contexts;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.context.ContextCalculator;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import cn.nukkit.Player;
import javax.annotation.Nonnull;
public class WorldCalculator implements ContextCalculator<Player> {
private final LuckPermsPlugin plugin;
public WorldCalculator(LuckPermsPlugin plugin) {
this.plugin = plugin;
}
@Nonnull
@Override
public MutableContextSet giveApplicableContext(@Nonnull Player subject, @Nonnull MutableContextSet accumulator) {
String world = subject.getLevel().getName().toLowerCase();
while (!accumulator.has(Contexts.WORLD_KEY, world)) {
accumulator.add(Contexts.WORLD_KEY, world);
world = this.plugin.getConfiguration().get(ConfigKeys.WORLD_REWRITES).getOrDefault(world, world).toLowerCase();
}
return accumulator;
}
}

View File

@ -0,0 +1,205 @@
/*
* 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.nukkit.listeners;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.utils.AbstractLoginListener;
import me.lucko.luckperms.nukkit.LPNukkitPlugin;
import me.lucko.luckperms.nukkit.model.permissible.LPPermissible;
import me.lucko.luckperms.nukkit.model.permissible.PermissibleInjector;
import cn.nukkit.Player;
import cn.nukkit.event.EventHandler;
import cn.nukkit.event.EventPriority;
import cn.nukkit.event.Listener;
import cn.nukkit.event.player.PlayerAsyncPreLoginEvent;
import cn.nukkit.event.player.PlayerLoginEvent;
import cn.nukkit.event.player.PlayerQuitEvent;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class NukkitConnectionListener extends AbstractLoginListener implements Listener {
private final LPNukkitPlugin plugin;
private final Set<UUID> deniedAsyncLogin = Collections.synchronizedSet(new HashSet<>());
private final Set<UUID> deniedLogin = Collections.synchronizedSet(new HashSet<>());
public NukkitConnectionListener(LPNukkitPlugin plugin) {
super(plugin);
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.LOW)
public void onPlayerPreLogin(PlayerAsyncPreLoginEvent e) {
/* Called when the player first attempts a connection with the server.
Listening on LOW priority to allow plugins to modify username / UUID data here. (auth plugins) */
if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
this.plugin.getLog().info("Processing pre-login for " + e.getUuid() + " - " + e.getName());
}
this.plugin.getUniqueConnections().add(e.getUuid());
/* Actually process the login for the connection.
We do this here to delay the login until the data is ready.
If the login gets cancelled later on, then this will be cleaned up.
This includes:
- loading uuid data
- loading permissions
- creating a user instance in the UserManager for this connection.
- setting up cached data. */
try {
User user = loadUser(e.getUuid(), e.getName());
this.plugin.getEventFactory().handleUserLoginProcess(e.getUuid(), e.getName(), user);
} catch (Exception ex) {
this.plugin.getLog().severe("Exception occurred whilst loading data for " + e.getUuid() + " - " + e.getName());
ex.printStackTrace();
// deny the connection
this.deniedAsyncLogin.add(e.getUuid());
e.disAllow(Message.LOADING_ERROR.asString(this.plugin.getLocaleManager()));
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerPreLoginMonitor(PlayerAsyncPreLoginEvent e) {
/* Listen to see if the event was cancelled after we initially handled the connection
If the connection was cancelled here, we need to do something to clean up the data that was loaded. */
// Check to see if this connection was denied at LOW.
if (this.deniedAsyncLogin.remove(e.getUuid())) {
// their data was never loaded at LOW priority, now check to see if they have been magically allowed since then.
// This is a problem, as they were denied at low priority, but are now being allowed.
if (e.getLoginResult() == PlayerAsyncPreLoginEvent.LoginResult.SUCCESS) {
this.plugin.getLog().severe("Player connection was re-allowed for " + e.getUuid());
e.disAllow("");
}
}
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerLogin(PlayerLoginEvent e) {
/* Called when the player starts logging into the server.
At this point, the users data should be present and loaded. */
final Player player = e.getPlayer();
if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
this.plugin.getLog().info("Processing login for " + player.getUniqueId() + " - " + player.getName());
}
final User user = this.plugin.getUserManager().getIfLoaded(player.getUniqueId());
/* User instance is null for whatever reason. Could be that it was unloaded between asyncpre and now. */
if (user == null) {
this.deniedLogin.add(e.getPlayer().getUniqueId());
this.plugin.getLog().warn("User " + player.getUniqueId() + " - " + player.getName() + " doesn't have data pre-loaded. - denying login.");
e.setCancelled();
e.setKickMessage(Message.LOADING_ERROR.asString(this.plugin.getLocaleManager()));
return;
}
// User instance is there, now we can inject our custom Permissible into the player.
// Care should be taken at this stage to ensure that async tasks which manipulate nukkit data check that the player is still online.
try {
// Make a new permissible for the user
LPPermissible lpPermissible = new LPPermissible(player, user, this.plugin);
// Inject into the player
PermissibleInjector.inject(player, lpPermissible);
} catch (Throwable t) {
t.printStackTrace();
}
this.plugin.refreshAutoOp(user, player);
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerLoginMonitor(PlayerLoginEvent e) {
/* Listen to see if the event was cancelled after we initially handled the login
If the connection was cancelled here, we need to do something to clean up the data that was loaded. */
// Check to see if this connection was denied at LOW. Even if it was denied at LOW, their data will still be present.
boolean denied = false;
if (this.deniedLogin.remove(e.getPlayer().getUniqueId())) {
denied = true;
// This is a problem, as they were denied at low priority, but are now being allowed.
if (!e.isCancelled()) {
this.plugin.getLog().severe("Player connection was re-allowed for " + e.getPlayer().getUniqueId());
e.setCancelled();
}
}
// Login event was cancelled by another plugin since we first loaded their data
if (denied || e.isCancelled()) {
return;
}
// everything is going well. login was processed ok, this is just to refresh auto-op status.
this.plugin.refreshAutoOp(this.plugin.getUserManager().getIfLoaded(e.getPlayer().getUniqueId()), e.getPlayer());
}
// Wait until the last priority to unload, so plugins can still perform permission checks on this event
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerQuit(PlayerQuitEvent e) {
final Player player = e.getPlayer();
// Remove the custom permissible
try {
PermissibleInjector.unInject(player, true);
} catch (Exception ex) {
ex.printStackTrace();
}
// Handle auto op
if (this.plugin.getConfiguration().get(ConfigKeys.AUTO_OP)) {
player.setOp(false);
}
// Register with the housekeeper, so the User's instance will stick
// around for a bit after they disconnect
this.plugin.getUserManager().getHouseKeeper().registerUsage(player.getUniqueId());
// force a clear of transient nodes
this.plugin.getScheduler().doAsync(() -> {
User user = this.plugin.getUserManager().getIfLoaded(player.getUniqueId());
if (user != null) {
user.clearTransientNodes();
}
});
}
}

View File

@ -0,0 +1,97 @@
/*
* 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.nukkit.listeners;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.nukkit.LPNukkitPlugin;
import cn.nukkit.Player;
import cn.nukkit.command.CommandSender;
import cn.nukkit.event.Cancellable;
import cn.nukkit.event.EventHandler;
import cn.nukkit.event.EventPriority;
import cn.nukkit.event.Listener;
import cn.nukkit.event.entity.EntityLevelChangeEvent;
import cn.nukkit.event.player.PlayerCommandPreprocessEvent;
import cn.nukkit.event.server.RemoteServerCommandEvent;
import cn.nukkit.event.server.ServerCommandEvent;
public class NukkitPlatformListener implements Listener {
private final LPNukkitPlugin plugin;
public NukkitPlatformListener(LPNukkitPlugin plugin) {
this.plugin = plugin;
}
@EventHandler
public void onPlayerCommand(PlayerCommandPreprocessEvent e) {
handleCommand(e.getPlayer(), e.getMessage().toLowerCase(), e);
}
@EventHandler
public void onServerCommand(ServerCommandEvent e) {
handleCommand(e.getSender(), e.getCommand().toLowerCase(), e);
}
@EventHandler
public void onRemoteServerCommand(RemoteServerCommandEvent e) {
handleCommand(e.getSender(), e.getCommand().toLowerCase(), e);
}
private void handleCommand(CommandSender sender, String s, Cancellable event) {
if (s.isEmpty()) {
return;
}
if (this.plugin.getConfiguration().get(ConfigKeys.OPS_ENABLED)) {
return;
}
if (s.charAt(0) == '/') {
s = s.substring(1);
}
if (s.startsWith("minecraft:")) {
s = s.substring("minecraft:".length());
}
if (s.equals("op") || s.startsWith("op ") || s.equals("deop") || s.startsWith("deop ")) {
event.setCancelled(true);
sender.sendMessage(Message.OP_DISABLED.asString(this.plugin.getLocaleManager()));
}
}
@EventHandler(priority = EventPriority.LOWEST)
public void onWorldChange(EntityLevelChangeEvent e) {
if (e.getEntity() instanceof Player) {
Player player = (Player) e.getEntity();
this.plugin.getContextManager().invalidateCache(player);
this.plugin.refreshAutoOp(this.plugin.getUserManager().getIfLoaded(player.getUniqueId()), player);
}
}
}

View File

@ -0,0 +1,111 @@
/*
* 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.nukkit.model;
import cn.nukkit.permission.Permission;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
/**
* Represents the possible default values for permissions
*/
public enum PermissionDefault {
TRUE("true") {
@Override
public boolean getValue(boolean op) {
return true;
}
},
FALSE("false") {
@Override
public boolean getValue(boolean op) {
return false;
}
},
OP("op", "isop", "operator", "isoperator", "admin", "isadmin") {
@Override
public boolean getValue(boolean op) {
return op;
}
},
NOT_OP("!op", "notop", "!operator", "notoperator", "!admin", "notadmin") {
@Override
public boolean getValue(boolean op) {
return !op;
}
};
private final String[] names;
private static final Map<String, PermissionDefault> LOOKUP = new HashMap<>();
PermissionDefault(String... names) {
this.names = names;
}
/**
* Calculates the value of this PermissionDefault for the given operator
* value
*
* @param op If the target is op
* @return True if the default should be true, or false
*/
public abstract boolean getValue(boolean op);
/**
* Looks up a PermissionDefault by name
*
* @param name Name of the default
* @return Specified value, or null if not found
*/
@Nullable
public static PermissionDefault getByName(String name) {
return LOOKUP.get(name.toLowerCase().replaceAll("[^a-z!]", ""));
}
@Nullable
public static PermissionDefault fromPermission(@Nullable Permission permission) {
if (permission == null) {
return null;
}
return getByName(permission.getDefault());
}
@Override
public String toString() {
return this.names[0];
}
static {
for (PermissionDefault value : values()) {
for (String name : value.names) {
LOOKUP.put(name, value);
}
}
}
}

View File

@ -0,0 +1,88 @@
/*
* 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.nukkit.model.dummy;
import cn.nukkit.permission.PermissibleBase;
import cn.nukkit.permission.Permission;
import cn.nukkit.permission.PermissionAttachment;
import cn.nukkit.permission.PermissionAttachmentInfo;
import cn.nukkit.plugin.Plugin;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.Map;
public class DummyPermissibleBase extends PermissibleBase {
private static final Field ATTACHMENTS_FIELD;
private static final Field PERMISSIONS_FIELD;
static {
try {
ATTACHMENTS_FIELD = PermissibleBase.class.getDeclaredField("attachments");
ATTACHMENTS_FIELD.setAccessible(true);
PERMISSIONS_FIELD = PermissibleBase.class.getDeclaredField("permissions");
PERMISSIONS_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new ExceptionInInitializerError(e);
}
}
public static void nullFields(PermissibleBase permissibleBase) {
try {
ATTACHMENTS_FIELD.set(permissibleBase, null);
} catch (Exception e) {
// ignore
}
try {
PERMISSIONS_FIELD.set(permissibleBase, null);
} catch (Exception e) {
// ignore
}
}
public static final DummyPermissibleBase INSTANCE = new DummyPermissibleBase();
private DummyPermissibleBase() {
super(null);
nullFields(this);
}
@Override public boolean isOp() { return false; }
@Override public void setOp(boolean value) {}
@Override public boolean isPermissionSet(String name) { return false; }
@Override public boolean isPermissionSet(Permission perm) { return false; }
@Override public boolean hasPermission(String inName) { return false; }
@Override public boolean hasPermission(Permission perm) { return false; }
@Override public PermissionAttachment addAttachment(Plugin plugin) { return null; }
@Override public PermissionAttachment addAttachment(Plugin plugin, String name) { return null; }
@Override public PermissionAttachment addAttachment(Plugin plugin, String name, Boolean value) { return null; }
@Override public void removeAttachment(PermissionAttachment attachment) {}
@Override public void recalculatePermissions() {}
@Override public void clearPermissions() {}
@Override public Map<String, PermissionAttachmentInfo> getEffectivePermissions() { return Collections.emptyMap(); }
}

View File

@ -0,0 +1,78 @@
/*
* 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.nukkit.model.dummy;
import cn.nukkit.Server;
import cn.nukkit.command.Command;
import cn.nukkit.command.CommandSender;
import cn.nukkit.plugin.Plugin;
import cn.nukkit.plugin.PluginDescription;
import cn.nukkit.plugin.PluginLoader;
import cn.nukkit.plugin.PluginLogger;
import cn.nukkit.utils.Config;
import java.io.File;
import java.io.InputStream;
/**
* Dummy plugin instance
*/
public class DummyPlugin implements Plugin {
public static final DummyPlugin INSTANCE = new DummyPlugin();
private DummyPlugin() {
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public boolean isDisabled() {
return false;
}
@Override public File getDataFolder() { return null; }
@Override public PluginDescription getDescription() { return null; }
@Override public Config getConfig() { return null; }
@Override public InputStream getResource(String s) { return null; }
@Override public boolean saveResource(String s) { return false; }
@Override public void saveConfig() {}
@Override public void saveDefaultConfig() {}
@Override public boolean saveResource(String s, boolean b) { return false; }
@Override public boolean saveResource(String s, String s1, boolean b) { return false; }
@Override public void reloadConfig() {}
@Override public PluginLoader getPluginLoader() { return null; }
@Override public Server getServer() { return null; }
@Override public void onDisable() {}
@Override public void onLoad() {}
@Override public void onEnable() {}
@Override public PluginLogger getLogger() { return null; }
@Override public String getName() { return null; }
@Override public boolean onCommand(CommandSender commandSender, Command command, String s, String[] strings) { return false; }
}

View File

@ -0,0 +1,279 @@
/*
* 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.nukkit.model.permissible;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.verbose.CheckOrigin;
import me.lucko.luckperms.nukkit.LPNukkitPlugin;
import me.lucko.luckperms.nukkit.model.PermissionDefault;
import cn.nukkit.Player;
import cn.nukkit.permission.PermissibleBase;
import cn.nukkit.permission.Permission;
import cn.nukkit.permission.PermissionAttachment;
import cn.nukkit.permission.PermissionAttachmentInfo;
import cn.nukkit.plugin.Plugin;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* PermissibleBase for LuckPerms.
*
* This class overrides all methods defined in PermissibleBase, and provides custom handling
* from LuckPerms.
*
* This means that all permission checks made for a player are handled directly by the plugin.
* Method behaviour is retained, but alternate implementation is used.
*
* "Hot" method calls, (namely #hasPermission) are significantly faster than the base implementation.
*
* This class is **thread safe**. This means that when LuckPerms is installed on the server,
* is is safe to call Player#hasPermission asynchronously.
*/
public class LPPermissible extends PermissibleBase {
// the LuckPerms user this permissible references.
private final User user;
// the player this permissible is injected into.
private final Player player;
// the luckperms plugin instance
private final LPNukkitPlugin plugin;
// the players previous permissible. (the one they had before this one was injected)
private PermissibleBase oldPermissible = null;
// if the permissible is currently active.
private final AtomicBoolean active = new AtomicBoolean(false);
// the attachments hooked onto the permissible.
// this collection is only modified by the attachments themselves
final Set<LPPermissionAttachment> attachments = ConcurrentHashMap.newKeySet();
public LPPermissible(Player player, User user, LPNukkitPlugin plugin) {
super(player);
this.user = Objects.requireNonNull(user, "user");
this.player = Objects.requireNonNull(player, "player");
this.plugin = Objects.requireNonNull(plugin, "plugin");
}
@Override
public boolean isPermissionSet(String permission) {
if (permission == null) {
throw new NullPointerException("permission");
}
Tristate ts = this.user.getCachedData().getPermissionData(calculateContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK);
return ts != Tristate.UNDEFINED || PermissionDefault.OP.getValue(isOp());
}
@Override
public boolean isPermissionSet(Permission permission) {
if (permission == null) {
throw new NullPointerException("permission");
}
Tristate ts = this.user.getCachedData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_LOOKUP_CHECK);
if (ts != Tristate.UNDEFINED) {
return true;
}
if (!this.plugin.getConfiguration().get(ConfigKeys.APPLY_NUKKIT_DEFAULT_PERMISSIONS)) {
return PermissionDefault.OP.getValue(isOp());
} else {
PermissionDefault def = PermissionDefault.fromPermission(permission);
return def != null && def.getValue(isOp());
}
}
@Override
public boolean hasPermission(String permission) {
if (permission == null) {
throw new NullPointerException("permission");
}
Tristate ts = this.user.getCachedData().getPermissionData(calculateContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_PERMISSION_CHECK);
return ts != Tristate.UNDEFINED ? ts.asBoolean() : PermissionDefault.OP.getValue(isOp());
}
@Override
public boolean hasPermission(Permission permission) {
if (permission == null) {
throw new NullPointerException("permission");
}
Tristate ts = this.user.getCachedData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_PERMISSION_CHECK);
if (ts != Tristate.UNDEFINED) {
return ts.asBoolean();
}
if (!this.plugin.getConfiguration().get(ConfigKeys.APPLY_NUKKIT_DEFAULT_PERMISSIONS)) {
return PermissionDefault.OP.getValue(isOp());
} else {
PermissionDefault def = PermissionDefault.fromPermission(permission);
return def != null && def.getValue(isOp());
}
}
/**
* Adds attachments to this permissible.
*
* @param attachments the attachments to add
*/
public void convertAndAddAttachments(Collection<PermissionAttachment> attachments) {
for (PermissionAttachment attachment : attachments) {
new LPPermissionAttachment(this, attachment).hook();
}
}
/**
* Obtains a {@link Contexts} instance for the player.
* Values are determined using the plugins ContextManager.
*
* @return the calculated contexts for the player.
*/
private Contexts calculateContexts() {
return this.plugin.getContextManager().getApplicableContexts(this.player);
}
@Override
public void setOp(boolean value) {
this.player.setOp(value);
}
@Override
public Map<String, PermissionAttachmentInfo> getEffectivePermissions() {
Set<Map.Entry<String, Boolean>> permissions = this.user.getCachedData().getPermissionData(calculateContexts()).getImmutableBacking().entrySet();
Map<String, PermissionAttachmentInfo> ret = new HashMap<>(permissions.size());
for (Map.Entry<String, Boolean> entry : permissions) {
ret.put(entry.getKey(), new PermissionAttachmentInfo(this.player, entry.getKey(), null, entry.getValue()));
}
return ret;
}
@Override
public LPPermissionAttachment addAttachment(Plugin plugin) {
if (plugin == null) {
throw new NullPointerException("plugin");
}
LPPermissionAttachment ret = new LPPermissionAttachment(this, plugin);
ret.hook();
return ret;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String permission) {
if (plugin == null) {
throw new NullPointerException("plugin");
}
if (permission == null) {
throw new NullPointerException("permission");
}
PermissionAttachment ret = addAttachment(plugin);
ret.setPermission(permission, true);
return ret;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String permission, Boolean value) {
if (plugin == null) {
throw new NullPointerException("plugin");
}
if (permission == null) {
throw new NullPointerException("permission");
}
PermissionAttachment ret = addAttachment(plugin);
ret.setPermission(permission, value);
return ret;
}
@Override
public void removeAttachment(PermissionAttachment attachment) {
if (attachment == null) {
throw new NullPointerException("attachment");
}
if (!(attachment instanceof LPPermissionAttachment)) {
throw new IllegalArgumentException("Given attachment is not a LPPermissionAttachment.");
}
LPPermissionAttachment a = ((LPPermissionAttachment) attachment);
if (a.getPermissible() != this) {
throw new IllegalArgumentException("Attachment does not belong to this permissible.");
}
a.remove();
}
@Override
public void recalculatePermissions() {
// do nothing
}
@Override
public void clearPermissions() {
this.attachments.forEach(LPPermissionAttachment::remove);
}
public User getUser() {
return this.user;
}
public Player getPlayer() {
return this.player;
}
public LPNukkitPlugin getPlugin() {
return this.plugin;
}
public PermissibleBase getOldPermissible() {
return this.oldPermissible;
}
public AtomicBoolean getActive() {
return this.active;
}
public void setOldPermissible(PermissibleBase oldPermissible) {
this.oldPermissible = oldPermissible;
}
}

View File

@ -0,0 +1,432 @@
/*
* 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.nukkit.model.permissible;
import com.google.common.base.Preconditions;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.node.ImmutableTransientNode;
import me.lucko.luckperms.common.node.NodeFactory;
import me.lucko.luckperms.nukkit.model.dummy.DummyPlugin;
import cn.nukkit.permission.Permission;
import cn.nukkit.permission.PermissionAttachment;
import cn.nukkit.permission.PermissionRemovedExecutor;
import cn.nukkit.plugin.Plugin;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* PermissionAttachment for LuckPerms.
*
* Applies all permissions directly to the backing user instance via transient nodes.
*/
public class LPPermissionAttachment extends PermissionAttachment {
/**
* The field in PermissionAttachment where the attachments applied permissions
* are *usually* held.
*/
private static final Field PERMISSION_ATTACHMENT_PERMISSIONS_FIELD;
static {
try {
PERMISSION_ATTACHMENT_PERMISSIONS_FIELD = PermissionAttachment.class.getDeclaredField("permissions");
PERMISSION_ATTACHMENT_PERMISSIONS_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new ExceptionInInitializerError(e);
}
}
/**
* The parent LPPermissible
*/
private final LPPermissible permissible;
/**
* The plugin which "owns" this attachment, may be null
*/
private final Plugin owner;
/**
* The permissions being applied by this attachment
*/
private final Map<String, Boolean> perms = Collections.synchronizedMap(new HashMap<>());
/**
* If the attachment has been applied to the user
*/
private boolean hooked = false;
/**
* Callback to run when the attachment is removed
*/
private PermissionRemovedExecutor removalCallback = null;
public LPPermissionAttachment(LPPermissible permissible, Plugin owner) {
super(DummyPlugin.INSTANCE, null);
this.permissible = permissible;
this.owner = owner;
injectFakeMap();
}
public LPPermissionAttachment(LPPermissible permissible, PermissionAttachment nukkit) {
super(DummyPlugin.INSTANCE, null);
this.permissible = permissible;
this.owner = null;
// copy
this.perms.putAll(nukkit.getPermissions());
injectFakeMap();
}
/**
* Injects a fake 'permissions' map into the superclass, for (clever/dumb??) plugins
* which attempt to modify attachment permissions using reflection to get around the slow nukkit
* behaviour in the base PermissionAttachment implementation.
*
* The fake map proxies calls back to the methods on this attachment
*/
private void injectFakeMap() {
// inner class - this proxies calls back to us
FakeBackingMap fakeMap = new FakeBackingMap();
try {
// the field we need to modify is in the superclass - it has private
// and final modifiers so we have to use reflection to modify it.
PERMISSION_ATTACHMENT_PERMISSIONS_FIELD.set(this, fakeMap);
} catch (Exception e) {
e.printStackTrace();
}
}
public LPPermissible getPermissible() {
return this.permissible;
}
@Override
public PermissionRemovedExecutor getRemovalCallback() {
return this.removalCallback;
}
@Override
public void setRemovalCallback(PermissionRemovedExecutor removalCallback) {
this.removalCallback = removalCallback;
}
/**
* Hooks this attachment with the parent {@link User} instance.
*/
public void hook() {
this.hooked = true;
this.permissible.attachments.add(this);
for (Map.Entry<String, Boolean> entry : this.perms.entrySet()) {
if (entry.getKey() == null || entry.getKey().isEmpty()) {
continue;
}
setPermissionInternal(entry.getKey(), entry.getValue());
}
}
private void setPermissionInternal(String name, boolean value) {
if (!this.permissible.getPlugin().getConfiguration().get(ConfigKeys.APPLY_NUKKIT_ATTACHMENT_PERMISSIONS)) {
return;
}
// construct a node for the permission being set
// we use the servers static context to *try* to ensure that the node will apply
Node node = NodeFactory.builder(name)
.setValue(value)
.withExtraContext(this.permissible.getPlugin().getContextManager().getStaticContext())
.build();
// convert the constructed node to a transient node instance to refer back to this attachment
ImmutableTransientNode<LPPermissionAttachment> transientNode = ImmutableTransientNode.of(node, this);
// set the transient node
User user = this.permissible.getUser();
if (user.setTransientPermission(transientNode).asBoolean()) {
user.reloadCachedData();
}
}
private void unsetPermissionInternal(String name) {
if (!this.permissible.getPlugin().getConfiguration().get(ConfigKeys.APPLY_NUKKIT_ATTACHMENT_PERMISSIONS)) {
return;
}
// remove transient permissions from the holder which were added by this attachment & equal the permission
User user = this.permissible.getUser();
if (user.removeIfTransient(n -> n instanceof ImmutableTransientNode && ((ImmutableTransientNode) n).getOwner() == this && n.getPermission().equals(name))) {
user.reloadCachedData();
}
}
private void clearInternal() {
// remove all transient permissions added by this attachment
User user = this.permissible.getUser();
if (user.removeIfTransient(n -> n instanceof ImmutableTransientNode && ((ImmutableTransientNode) n).getOwner() == this)) {
user.reloadCachedData();
}
}
@Override
public void remove() {
if (!this.hooked) {
return;
}
// clear the internal permissions
clearInternal();
// run the callback
if (this.removalCallback != null) {
this.removalCallback.attachmentRemoved(this);
}
// unhook from the permissible
this.hooked = false;
this.permissible.attachments.remove(this);
}
@Override
public void setPermission(String name, boolean value) {
Objects.requireNonNull(name, "name is null");
Preconditions.checkArgument(!name.isEmpty(), "name is empty");
String permission = name.toLowerCase();
Boolean previous = this.perms.put(permission, value);
if (previous != null && previous == value) {
return;
}
// if we're not hooked, then don't actually apply the change
// it will get applied on hook - if that ever happens
if (!this.hooked) {
return;
}
if (previous != null) {
unsetPermissionInternal(permission);
}
setPermissionInternal(permission, value);
}
@Override
public void setPermission(Permission permission, boolean value) {
setPermission(permission.getName(), value);
}
@Override
public void setPermissions(Map<String, Boolean> permissions) {
for (Map.Entry<String, Boolean> entry : permissions.entrySet()) {
setPermission(entry.getKey(), entry.getValue());
}
}
@Override
public void unsetPermission(String name, boolean value) {
Objects.requireNonNull(name, "name is null");
Preconditions.checkArgument(!name.isEmpty(), "name is empty");
String permission = name.toLowerCase();
Boolean previous = this.perms.remove(permission);
if (previous == null) {
return;
}
// if we're not hooked, then don't actually apply the change
// it will get applied on hook - if that ever happens
if (!this.hooked) {
return;
}
unsetPermissionInternal(permission);
}
@Override
public void unsetPermission(Permission permission, boolean value) {
unsetPermission(permission.getName(), value);
}
@Override
public void unsetPermissions(List<String> permissions) {
for (String perm : permissions) {
unsetPermission(perm, true);
}
}
@Override
public void clearPermissions() {
this.perms.clear();
clearInternal();
}
@Override
public Map<String, Boolean> getPermissions() {
return this.perms;
}
@Override
public Plugin getPlugin() {
return this.owner != null ? this.owner : this.permissible.getPlugin();
}
@Override
public boolean equals(Object obj) {
return this == obj;
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
/**
* A fake map to be injected into the superclass. This implementation simply
* proxies calls back to this attachment instance.
*
* Some (clever/dumb??) plugins attempt to modify attachment permissions using reflection
* to get around the slow nukkit behaviour in the base PermissionAttachment implementation.
*
* An instance of this map is injected into the super instance so these plugins continue
* to work with LuckPerms.
*/
private final class FakeBackingMap implements Map<String, Boolean> {
@Override
public Boolean put(String key, Boolean value) {
// grab the previous result, so we can still satisfy the method signature of Map
Boolean previous = LPPermissionAttachment.this.perms.get(key);
// proxy the call back through the PermissionAttachment instance
setPermission(key, value);
// return the previous value
return previous;
}
@Override
public Boolean remove(Object key) {
// we only accept string keys
if (!(key instanceof String)) {
return null;
}
String permission = ((String) key);
// grab the previous result, so we can still satisfy the method signature of Map
Boolean previous = LPPermissionAttachment.this.perms.get(permission);
// proxy the call back through the PermissionAttachment instance
unsetPermission(permission, true);
// return the previous value
return previous;
}
@Override
public void putAll(Map<? extends String, ? extends Boolean> m) {
for (Map.Entry<? extends String, ? extends Boolean> entry : m.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
@Override
public void clear() {
// remove the permissions which have already been applied
if (LPPermissionAttachment.this.hooked) {
clearInternal();
}
// clear the backing map
LPPermissionAttachment.this.perms.clear();
}
@Override
public int size() {
// return the size of the permissions map - probably the most accurate value we have
return LPPermissionAttachment.this.perms.size();
}
@Override
public boolean isEmpty() {
// return if the permissions map is empty - again probably the most accurate thing
// we can return
return LPPermissionAttachment.this.perms.isEmpty();
}
@Override
public boolean containsKey(Object key) {
// just proxy
return LPPermissionAttachment.this.perms.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
// just proxy
return LPPermissionAttachment.this.perms.containsValue(value);
}
@Override
public Boolean get(Object key) {
// just proxy
return LPPermissionAttachment.this.perms.get(key);
}
@Override
public Set<String> keySet() {
// just proxy
return LPPermissionAttachment.this.perms.keySet();
}
@Override
public Collection<Boolean> values() {
// just proxy
return LPPermissionAttachment.this.perms.values();
}
@Override
public Set<Entry<String, Boolean>> entrySet() {
// just proxy
return LPPermissionAttachment.this.perms.entrySet();
}
}
}

View File

@ -0,0 +1,145 @@
/*
* 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.nukkit.model.permissible;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.verbose.CheckOrigin;
import me.lucko.luckperms.common.verbose.VerboseHandler;
import me.lucko.luckperms.nukkit.model.dummy.DummyPermissibleBase;
import cn.nukkit.permission.PermissibleBase;
import cn.nukkit.permission.Permission;
import cn.nukkit.permission.PermissionAttachment;
import cn.nukkit.permission.PermissionAttachmentInfo;
import cn.nukkit.plugin.Plugin;
import java.util.Map;
/**
* A PermissibleBase extension which logs permission checks to the
* plugin's {@link VerboseHandler} facility.
*
* Method calls are forwarded to the delegate permissible.
*/
public class MonitoredPermissibleBase extends PermissibleBase {
private final LuckPermsPlugin plugin;
private final PermissibleBase delegate;
private final String name;
// remains false until the object has been constructed
// necessary to catch the superclass call to #recalculatePermissions on init
@SuppressWarnings("UnusedAssignment")
private boolean initialised = false;
public MonitoredPermissibleBase(LuckPermsPlugin plugin, PermissibleBase delegate, String name) {
super(null);
DummyPermissibleBase.nullFields(this);
this.plugin = plugin;
this.delegate = delegate;
this.name = name;
this.initialised = true;
// since we effectively cancel the execution of this call in the super
// constructor we need to call it again.
recalculatePermissions();
}
private void logCheck(CheckOrigin origin, String permission, boolean result) {
this.plugin.getVerboseHandler().offerCheckData(origin, this.name, ContextSet.empty(), permission, Tristate.fromBoolean(result));
this.plugin.getPermissionVault().offer(permission);
}
PermissibleBase getDelegate() {
return this.delegate;
}
@Override
public boolean isPermissionSet(String permission) {
if (permission == null) {
throw new NullPointerException("permission");
}
final boolean result = this.delegate.isPermissionSet(permission);
logCheck(CheckOrigin.PLATFORM_LOOKUP_CHECK, permission, result);
return result;
}
@Override
public boolean isPermissionSet(Permission permission) {
if (permission == null) {
throw new NullPointerException("permission");
}
final boolean result = this.delegate.isPermissionSet(permission);
logCheck(CheckOrigin.PLATFORM_LOOKUP_CHECK, permission.getName(), result);
return result;
}
@Override
public boolean hasPermission(String permission) {
if (permission == null) {
throw new NullPointerException("permission");
}
final boolean result = this.delegate.hasPermission(permission);
logCheck(CheckOrigin.PLATFORM_PERMISSION_CHECK, permission, result);
return result;
}
@Override
public boolean hasPermission(Permission permission) {
if (permission == null) {
throw new NullPointerException("permission");
}
final boolean result = this.delegate.hasPermission(permission);
logCheck(CheckOrigin.PLATFORM_PERMISSION_CHECK, permission.getName(), result);
return result;
}
@Override
public void recalculatePermissions() {
if (!this.initialised) {
return;
}
this.delegate.recalculatePermissions();
}
// just forward calls to the delegate permissible
@Override public boolean isOp() { return this.delegate.isOp(); }
@Override public void setOp(boolean value) { this.delegate.setOp(value); }
@Override public PermissionAttachment addAttachment(Plugin plugin, String name, Boolean value) { return this.delegate.addAttachment(plugin, name, value); }
@Override public PermissionAttachment addAttachment(Plugin plugin, String name) { return this.delegate.addAttachment(plugin, name); }
@Override public PermissionAttachment addAttachment(Plugin plugin) { return this.delegate.addAttachment(plugin); }
@Override public void removeAttachment(PermissionAttachment attachment) { this.delegate.removeAttachment(attachment); }
@Override public void clearPermissions() { this.delegate.clearPermissions(); }
@Override public Map<String, PermissionAttachmentInfo> getEffectivePermissions() { return this.delegate.getEffectivePermissions(); }
}

View File

@ -0,0 +1,146 @@
/*
* 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.nukkit.model.permissible;
import me.lucko.luckperms.nukkit.model.dummy.DummyPermissibleBase;
import cn.nukkit.Player;
import cn.nukkit.permission.PermissibleBase;
import cn.nukkit.permission.PermissionAttachment;
import java.lang.reflect.Field;
import java.util.Set;
/**
* Injects a {@link LPPermissible} into a {@link Player}.
*
* This allows LuckPerms to directly intercept permission checks and take over all handling of
* checks made by plugins.
*/
public final class PermissibleInjector {
/**
* All permission checks made on standard Nukkit objects are effectively proxied to a
* {@link PermissibleBase} object, held as a variable on the object.
*
* This field is where the permissible is stored on a Player.
*/
private static final Field PLAYER_PERMISSIBLE_FIELD;
/**
* The field where attachments are stored on a permissible base.
*/
private static final Field PERMISSIBLE_BASE_ATTACHMENTS_FIELD;
static {
try {
// Try to load the permissible field.
PLAYER_PERMISSIBLE_FIELD = Player.class.getDeclaredField("perm");
PLAYER_PERMISSIBLE_FIELD.setAccessible(true);
// Try to load the attachments field.
PERMISSIBLE_BASE_ATTACHMENTS_FIELD = PermissibleBase.class.getDeclaredField("attachments");
PERMISSIBLE_BASE_ATTACHMENTS_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new ExceptionInInitializerError(e);
}
}
/**
* Injects a {@link LPPermissible} into a {@link Player}.
*
* @param player the player to inject into
* @param newPermissible the permissible to inject
* @throws Exception propagates any exceptions which were thrown during injection
*/
public static void inject(Player player, LPPermissible newPermissible) throws Exception {
// get the existing PermissibleBase held by the player
PermissibleBase oldPermissible = (PermissibleBase) PLAYER_PERMISSIBLE_FIELD.get(player);
// seems we have already injected into this player.
if (oldPermissible instanceof LPPermissible) {
throw new IllegalStateException("LPPermissible already injected into player " + player.toString());
}
// Move attachments over from the old permissible
//noinspection unchecked
Set<PermissionAttachment> attachments = (Set<PermissionAttachment>) PERMISSIBLE_BASE_ATTACHMENTS_FIELD.get(oldPermissible);
newPermissible.convertAndAddAttachments(attachments);
attachments.clear();
oldPermissible.clearPermissions();
// Setup the new permissible
newPermissible.getActive().set(true);
newPermissible.setOldPermissible(oldPermissible);
// inject the new instance
PLAYER_PERMISSIBLE_FIELD.set(player, newPermissible);
}
/**
* Uninjects a {@link LPPermissible} from a {@link Player}.
*
* @param player the player to uninject from
* @param dummy if the replacement permissible should be a dummy.
* @throws Exception propagates any exceptions which were thrown during uninjection
*/
public static void unInject(Player player, boolean dummy) throws Exception {
// gets the players current permissible.
PermissibleBase permissible = (PermissibleBase) PLAYER_PERMISSIBLE_FIELD.get(player);
// only uninject if the permissible was a luckperms one.
if (permissible instanceof LPPermissible) {
LPPermissible lpPermissible = ((LPPermissible) permissible);
// clear all permissions
lpPermissible.clearPermissions();
// set to inactive
lpPermissible.getActive().set(false);
// handle the replacement permissible.
if (dummy) {
// just inject a dummy class. this is used when we know the player is about to quit the server.
PLAYER_PERMISSIBLE_FIELD.set(player, DummyPermissibleBase.INSTANCE);
} else {
PermissibleBase newPb = lpPermissible.getOldPermissible();
if (newPb == null) {
newPb = new PermissibleBase(player);
}
PLAYER_PERMISSIBLE_FIELD.set(player, newPb);
}
}
}
private PermissibleInjector() {}
}

View File

@ -0,0 +1,84 @@
/*
* 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.nukkit.model.permissible;
import me.lucko.luckperms.nukkit.LPNukkitPlugin;
import cn.nukkit.command.ConsoleCommandSender;
import cn.nukkit.permission.PermissibleBase;
import java.lang.reflect.Field;
import java.util.Objects;
/**
* Injects {@link MonitoredPermissibleBase}s into non-player permissibles on
* the server so their checks can be monitored by the verbose facility.
*/
public class PermissibleMonitoringInjector implements Runnable {
private final LPNukkitPlugin plugin;
public PermissibleMonitoringInjector(LPNukkitPlugin plugin) {
this.plugin = plugin;
}
@Override
public void run() {
try {
injectConsole();
} catch (Exception e) {
// ignore
}
}
private MonitoredPermissibleBase wrap(PermissibleBase permBase, String name) {
Objects.requireNonNull(permBase, "permBase");
// unwrap any previous injection
if (permBase instanceof MonitoredPermissibleBase) {
permBase = ((MonitoredPermissibleBase) permBase).getDelegate();
}
// create a monitored instance which delegates to the previous PermissibleBase
return new MonitoredPermissibleBase(this.plugin, permBase, name);
}
private void injectConsole() throws Exception {
ConsoleCommandSender consoleSender = this.plugin.getServer().getConsoleSender();
// get the perm field
Field permField = ConsoleCommandSender.class.getDeclaredField("perm");
permField.setAccessible(true);
// get the PermissibleBase instance
PermissibleBase permBase = (PermissibleBase) permField.get(consoleSender);
// create a monitored instance which delegates to the previous PermissibleBase
MonitoredPermissibleBase newPermBase = wrap(permBase, "internal/console");
// inject the monitored instance
permField.set(consoleSender, newPermBase);
}
}

View File

@ -0,0 +1,138 @@
/*
* 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.nukkit.model.server;
import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.nukkit.LPNukkitPlugin;
import cn.nukkit.Server;
import cn.nukkit.permission.Permission;
import cn.nukkit.plugin.PluginManager;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* Injects a {@link LPDefaultsMap} info the {@link PluginManager}.
*/
public class InjectorDefaultsMap implements Runnable {
private static final Field OP_DEFAULT_PERMISSIONS_FIELD;
private static final Field NON_OP_DEFAULT_PERMISSIONS_FIELD;
static {
Field opPermissionsField = null;
try {
opPermissionsField = PluginManager.class.getDeclaredField("defaultPermsOp");
opPermissionsField.setAccessible(true);
} catch (Exception e) {
// ignore
}
OP_DEFAULT_PERMISSIONS_FIELD = opPermissionsField;
Field nonOpPermissionsField = null;
try {
nonOpPermissionsField = PluginManager.class.getDeclaredField("defaultPerms");
nonOpPermissionsField.setAccessible(true);
} catch (Exception e) {
// ignore
}
NON_OP_DEFAULT_PERMISSIONS_FIELD = nonOpPermissionsField;
}
private final LPNukkitPlugin plugin;
public InjectorDefaultsMap(LPNukkitPlugin plugin) {
this.plugin = plugin;
}
@Override
public void run() {
try {
LPDefaultsMap ret = inject();
if (ret != null) {
this.plugin.setDefaultPermissionMap(ret);
}
} catch (Exception e) {
this.plugin.getLog().severe("Exception occurred whilst injecting LuckPerms Default Permission map.");
e.printStackTrace();
}
}
private LPDefaultsMap inject() throws Exception {
Objects.requireNonNull(OP_DEFAULT_PERMISSIONS_FIELD, "OP_DEFAULT_PERMISSIONS_FIELD");
Objects.requireNonNull(NON_OP_DEFAULT_PERMISSIONS_FIELD, "NON_OP_DEFAULT_PERMISSIONS_FIELD");
PluginManager pluginManager = this.plugin.getServer().getPluginManager();
Object opMap = OP_DEFAULT_PERMISSIONS_FIELD.get(pluginManager);
Object nonOpMap = NON_OP_DEFAULT_PERMISSIONS_FIELD.get(pluginManager);
if (opMap instanceof LPDefaultsMap.DefaultPermissionSet && ((LPDefaultsMap.DefaultPermissionSet) opMap).parent.plugin == this.plugin) {
if (nonOpMap instanceof LPDefaultsMap.DefaultPermissionSet && ((LPDefaultsMap.DefaultPermissionSet) nonOpMap).parent.plugin == this.plugin) {
return null;
}
}
//noinspection unchecked
Map<String, Permission> castedOpMap = (Map<String, Permission>) opMap;
//noinspection unchecked
Map<String, Permission> castedNonOpMap = (Map<String, Permission>) nonOpMap;
// make a new map & inject it
LPDefaultsMap newMap = new LPDefaultsMap(this.plugin, ImmutableMap.of(true, castedOpMap, false, castedNonOpMap));
OP_DEFAULT_PERMISSIONS_FIELD.set(pluginManager, newMap.getOpPermissions());
NON_OP_DEFAULT_PERMISSIONS_FIELD.set(pluginManager, newMap.getNonOpPermissions());
return newMap;
}
public static void uninject() {
try {
Objects.requireNonNull(OP_DEFAULT_PERMISSIONS_FIELD, "OP_DEFAULT_PERMISSIONS_FIELD");
Objects.requireNonNull(NON_OP_DEFAULT_PERMISSIONS_FIELD, "NON_OP_DEFAULT_PERMISSIONS_FIELD");
PluginManager pluginManager = Server.getInstance().getPluginManager();
{
Object map = OP_DEFAULT_PERMISSIONS_FIELD.get(pluginManager);
if (map instanceof LPDefaultsMap.DefaultPermissionSet) {
LPDefaultsMap.DefaultPermissionSet lpMap = (LPDefaultsMap.DefaultPermissionSet) map;
OP_DEFAULT_PERMISSIONS_FIELD.set(pluginManager, new HashMap<>(lpMap));
}
}
{
Object map = NON_OP_DEFAULT_PERMISSIONS_FIELD.get(pluginManager);
if (map instanceof LPDefaultsMap.DefaultPermissionSet) {
LPDefaultsMap.DefaultPermissionSet lpMap = (LPDefaultsMap.DefaultPermissionSet) map;
NON_OP_DEFAULT_PERMISSIONS_FIELD.set(pluginManager, new HashMap<>(lpMap));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,108 @@
/*
* 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.nukkit.model.server;
import me.lucko.luckperms.nukkit.LPNukkitPlugin;
import cn.nukkit.Server;
import cn.nukkit.permission.Permission;
import cn.nukkit.plugin.PluginManager;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* Injects a {@link LPPermissionMap} into the {@link PluginManager}.
*/
public class InjectorPermissionMap implements Runnable {
private static final Field PERMISSIONS_FIELD;
static {
Field permissionsField = null;
try {
permissionsField = PluginManager.class.getDeclaredField("permissions");
permissionsField.setAccessible(true);
} catch (Exception e) {
// ignore
}
PERMISSIONS_FIELD = permissionsField;
}
private final LPNukkitPlugin plugin;
public InjectorPermissionMap(LPNukkitPlugin plugin) {
this.plugin = plugin;
}
@Override
public void run() {
try {
LPPermissionMap ret = inject();
if (ret != null) {
this.plugin.setPermissionMap(ret);
}
} catch (Exception e) {
this.plugin.getLog().severe("Exception occurred whilst injecting LuckPerms Permission map.");
e.printStackTrace();
}
}
private LPPermissionMap inject() throws Exception {
Objects.requireNonNull(PERMISSIONS_FIELD, "PERMISSIONS_FIELD");
PluginManager pluginManager = this.plugin.getServer().getPluginManager();
Object map = PERMISSIONS_FIELD.get(pluginManager);
if (map instanceof LPPermissionMap && ((LPPermissionMap) map).plugin == this.plugin) {
return null;
}
//noinspection unchecked
Map<String, Permission> castedMap = (Map<String, Permission>) map;
// make a new map & inject it
LPPermissionMap newMap = new LPPermissionMap(this.plugin, castedMap);
PERMISSIONS_FIELD.set(pluginManager, newMap);
return newMap;
}
public static void uninject() {
try {
Objects.requireNonNull(PERMISSIONS_FIELD, "PERMISSIONS_FIELD");
PluginManager pluginManager = Server.getInstance().getPluginManager();
Object map = PERMISSIONS_FIELD.get(pluginManager);
if (map instanceof LPPermissionMap) {
LPPermissionMap lpMap = (LPPermissionMap) map;
PERMISSIONS_FIELD.set(pluginManager, new HashMap<>(lpMap));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,112 @@
/*
* 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.nukkit.model.server;
import me.lucko.luckperms.nukkit.LPNukkitPlugin;
import cn.nukkit.Server;
import cn.nukkit.permission.Permissible;
import cn.nukkit.plugin.PluginManager;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* Injects a {@link LPSubscriptionMap} into the {@link PluginManager}.
*/
public class InjectorSubscriptionMap implements Runnable {
private static final Field PERM_SUBS_FIELD;
static {
Field permSubsField = null;
try {
permSubsField = PluginManager.class.getDeclaredField("permSubs");
permSubsField.setAccessible(true);
} catch (Exception e) {
// ignore
}
PERM_SUBS_FIELD = permSubsField;
}
private final LPNukkitPlugin plugin;
public InjectorSubscriptionMap(LPNukkitPlugin plugin) {
this.plugin = plugin;
}
@Override
public void run() {
try {
LPSubscriptionMap ret = inject();
if (ret != null) {
this.plugin.setSubscriptionMap(ret);
}
} catch (Exception e) {
this.plugin.getLog().severe("Exception occurred whilst injecting LuckPerms Permission Subscription map.");
e.printStackTrace();
}
}
private LPSubscriptionMap inject() throws Exception {
Objects.requireNonNull(PERM_SUBS_FIELD, "PERM_SUBS_FIELD");
PluginManager pluginManager = this.plugin.getServer().getPluginManager();
Object map = PERM_SUBS_FIELD.get(pluginManager);
if (map instanceof LPSubscriptionMap) {
if (((LPSubscriptionMap) map).plugin == this.plugin) {
return null;
}
map = ((LPSubscriptionMap) map).detach();
}
//noinspection unchecked
Map<String, Set<Permissible>> castedMap = (Map<String, Set<Permissible>>) map;
// make a new subscription map & inject it
LPSubscriptionMap newMap = new LPSubscriptionMap(this.plugin, castedMap);
PERM_SUBS_FIELD.set(pluginManager, newMap);
return newMap;
}
public static void uninject() {
try {
Objects.requireNonNull(PERM_SUBS_FIELD, "PERM_SUBS_FIELD");
PluginManager pluginManager = Server.getInstance().getPluginManager();
Object map = PERM_SUBS_FIELD.get(pluginManager);
if (map instanceof LPSubscriptionMap) {
LPSubscriptionMap lpMap = (LPSubscriptionMap) map;
PERM_SUBS_FIELD.set(pluginManager, lpMap.detach());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,166 @@
/*
* 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.nukkit.model.server;
import com.google.common.collect.ForwardingMap;
import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.nukkit.LPNukkitPlugin;
import cn.nukkit.permission.Permission;
import cn.nukkit.plugin.PluginManager;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* A replacement map for the 'defaultPerms' instance in Nukkit's SimplePluginManager.
*
* This instance allows LuckPerms to intercept calls to
* {@link PluginManager#addPermission(Permission)}, specifically regarding
* the default nature of the permission.
*
* Injected by {@link InjectorDefaultsMap}.
*/
public final class LPDefaultsMap {
// the plugin
final LPNukkitPlugin plugin;
// the two values in the map
private final Map<String, Permission> opSet = new DefaultPermissionSet(true);
private final Map<String, Permission> nonOpSet = new DefaultPermissionSet(false);
// fully resolved defaults (accounts for child permissions too)
private Map<String, Boolean> resolvedOpDefaults = ImmutableMap.of();
private Map<String, Boolean> resolvedNonOpDefaults = ImmutableMap.of();
public LPDefaultsMap(LPNukkitPlugin plugin, Map<Boolean, Map<String, Permission>> existingData) {
this.plugin = plugin;
this.opSet.putAll(existingData.getOrDefault(Boolean.TRUE, Collections.emptyMap()));
this.nonOpSet.putAll(existingData.getOrDefault(Boolean.FALSE, Collections.emptyMap()));
refreshOp();
refreshNonOp();
}
public Map<String, Permission> getOpPermissions() {
return this.opSet;
}
public Map<String, Permission> getNonOpPermissions() {
return this.nonOpSet;
}
/**
* Queries whether a given permission should be granted by default.
*
* @param permission the permission to query
* @param isOp if the player is op
* @return a tristate result
*/
public Tristate lookupDefaultPermission(String permission, boolean isOp) {
Map<String, Boolean> map = isOp ? this.resolvedOpDefaults : this.resolvedNonOpDefaults;
return Tristate.fromNullableBoolean(map.get(permission));
}
private void refresh(boolean op) {
if (op) {
refreshOp();
} else {
refreshNonOp();
}
}
/**
* Refreshes the op data in this provider.
*/
private void refreshOp() {
Map<String, Boolean> builder = new HashMap<>();
for (Permission perm : getOpPermissions().values()) {
String name = perm.getName().toLowerCase();
builder.put(name, true);
for (Map.Entry<String, Boolean> child : this.plugin.getPermissionMap().getChildPermissions(name, true).entrySet()) {
builder.putIfAbsent(child.getKey(), child.getValue());
}
}
this.resolvedOpDefaults = ImmutableMap.copyOf(builder);
}
/**
* Refreshes the non op data in this provider.
*/
private void refreshNonOp() {
Map<String, Boolean> builder = new HashMap<>();
for (Permission perm : getNonOpPermissions().values()) {
String name = perm.getName().toLowerCase();
builder.put(name, true);
for (Map.Entry<String, Boolean> child : this.plugin.getPermissionMap().getChildPermissions(name, true).entrySet()) {
builder.putIfAbsent(child.getKey(), child.getValue());
}
}
this.resolvedNonOpDefaults = ImmutableMap.copyOf(builder);
}
final class DefaultPermissionSet extends ForwardingMap<String, Permission> {
final LPDefaultsMap parent = LPDefaultsMap.this;
private final Map<String, Permission> delegate = new ConcurrentHashMap<>();
private final boolean op;
private DefaultPermissionSet(boolean op) {
this.op = op;
}
@Override
protected Map<String, Permission> delegate() {
return this.delegate;
}
@Override
public Permission put(String key, Permission value) {
Permission ret = super.put(key, value);
refresh(this.op);
return ret;
}
@Override
public Permission putIfAbsent(String key, Permission value) {
Permission ret = super.putIfAbsent(key, value);
refresh(this.op);
return ret;
}
@Override
public void putAll(Map<? extends String, ? extends Permission> map) {
super.putAll(map);
refresh(this.op);
}
}
}

View File

@ -0,0 +1,155 @@
/*
* 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.nukkit.model.server;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.collect.ForwardingMap;
import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.treeview.PermissionVault;
import cn.nukkit.permission.Permission;
import cn.nukkit.plugin.PluginManager;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/**
* A replacement map for the 'permissions' instance in Nukkit's SimplePluginManager.
*
* This instance allows LuckPerms to intercept calls to
* {@link PluginManager#addPermission(Permission)} and record permissions in the
* {@link PermissionVault}.
*
* It also allows us to pre-determine child permission relationships.
*
* Injected by {@link InjectorPermissionMap}.
*/
public final class LPPermissionMap extends ForwardingMap<String, Permission> {
// Uses perm.getName().toLowerCase(java.util.Locale.ENGLISH); to determine the key
private final Map<String, Permission> delegate = new ConcurrentHashMap<>();
// cache from permission --> children
private final LoadingCache<String, Map<String, Boolean>> trueChildPermissions = Caffeine.newBuilder()
.build(new ChildPermissionResolver(true));
private final LoadingCache<String, Map<String, Boolean>> falseChildPermissions = Caffeine.newBuilder()
.build(new ChildPermissionResolver(false));
/**
* The plugin instance
*/
final LuckPermsPlugin plugin;
public LPPermissionMap(LuckPermsPlugin plugin, Map<String, Permission> existingData) {
this.plugin = plugin;
putAll(existingData);
}
public Map<String, Boolean> getChildPermissions(String permission, boolean value) {
return value ? this.trueChildPermissions.get(permission) : this.falseChildPermissions.get(permission);
}
private void update() {
this.trueChildPermissions.invalidateAll();
this.falseChildPermissions.invalidateAll();
}
@Override
protected Map<String, Permission> delegate() {
return this.delegate;
}
@Override
public Permission put(@Nonnull String key, @Nonnull Permission value) {
this.plugin.getPermissionVault().offer(key);
Permission ret = super.put(key, value);
update();
return ret;
}
@Override
public void putAll(@Nonnull Map<? extends String, ? extends Permission> m) {
this.plugin.getPermissionVault().offerAll(m.keySet());
super.putAll(m);
update();
}
@Override
public Permission putIfAbsent(String key, Permission value) {
this.plugin.getPermissionVault().offer(key);
Permission ret = super.putIfAbsent(key, value);
update();
return ret;
}
private final class ChildPermissionResolver implements CacheLoader<String, Map<String, Boolean>> {
private final boolean value;
private ChildPermissionResolver(boolean value) {
this.value = value;
}
@CheckForNull
@Override
public Map<String, Boolean> load(@Nonnull String key) {
Map<String, Boolean> children = new HashMap<>();
resolveChildren(children, Collections.singletonMap(key, this.value), false);
children.remove(key, this.value);
return ImmutableMap.copyOf(children);
}
}
private void resolveChildren(Map<String, Boolean> accumulator, Map<String, Boolean> children, boolean invert) {
// iterate through the current known children.
// the first time this method is called for a given permission, the children map will contain only the permission itself.
for (Map.Entry<String, Boolean> e : children.entrySet()) {
if (accumulator.containsKey(e.getKey())) {
continue; // Prevent infinite loops
}
// xor the value using the parent (nukkit logic, not mine)
boolean value = e.getValue() ^ invert;
accumulator.put(e.getKey().toLowerCase(), value);
// lookup any deeper children & resolve if present
Permission perm = this.delegate.get(e.getKey());
if (perm != null) {
resolveChildren(accumulator, perm.getChildren(), !value);
}
}
}
}

View File

@ -0,0 +1,259 @@
/*
* 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.nukkit.model.server;
import com.google.common.collect.Sets;
import me.lucko.luckperms.nukkit.LPNukkitPlugin;
import cn.nukkit.permission.Permissible;
import cn.nukkit.plugin.PluginManager;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.stream.Collectors;
/**
* A replacement map for the 'permSubs' instance in Nukkit's SimplePluginManager.
*
* This instance allows LuckPerms to intercept calls to
* {@link PluginManager#subscribeToPermission(String, Permissible)},
* {@link PluginManager#unsubscribeFromPermission(String, Permissible)} and
* {@link PluginManager#getPermissionSubscriptions(String)}.
*
* Nukkit for some reason sometimes uses subscription status to determine whether
* a permissible has a given node, instead of checking directly with
* {@link Permissible#hasPermission(String)}.
*
* In order to implement predicable Nukkit behaviour, LP has two options:
* 1) register subscriptions for all players as normal, or
* 2) inject it's own map instance to proxy calls to {@link PluginManager#getPermissionSubscriptions(String)} back to LuckPerms.
*
* This class implements option 2 above. It is preferred because it is faster & uses less memory
*
* Injected by {@link InjectorSubscriptionMap}.
*/
public final class LPSubscriptionMap extends HashMap<String, Set<Permissible>> {
// the plugin instance
final LPNukkitPlugin plugin;
public LPSubscriptionMap(LPNukkitPlugin plugin, Map<String, Set<Permissible>> existingData) {
super(existingData);
this.plugin = plugin;
}
/*
* The get method is the only one which is actually used by SimplePluginManager
* we override it to always return a value - which means the null check in
* subscribeToDefaultPerms always fails - soo, we don't have to worry too much
* about implementing #put.
*
* we also ensure all returns are LPSubscriptionValueMaps. this extension
* will also delegate checks to online players - meaning we don't ever
* have to register their subscriptions with the plugin manager.
*/
@Override
public Set<Permissible> get(Object key) {
if (key == null || !(key instanceof String)) {
return null;
}
String permission = ((String) key);
Set<Permissible> result = super.get(key);
if (result == null) {
// calculate a new map - always!
result = new LPSubscriptionValueSet(permission);
super.put(permission, result);
} else if (!(result instanceof LPSubscriptionValueSet)) {
// ensure return type is a LPSubscriptionMap
result = new LPSubscriptionValueSet(permission, result);
super.put(permission, result);
}
return result;
}
@Override
public Set<Permissible> put(String key, Set<Permissible> value) {
if (value == null) {
throw new NullPointerException("Map value cannot be null");
}
// ensure values are LP subscription maps
if (!(value instanceof LPSubscriptionValueSet)) {
value = new LPSubscriptionValueSet(key, value);
}
return super.put(key, value);
}
// if the key isn't null and is a string, #get will always return a value for it
@Override
public boolean containsKey(Object key) {
return key != null && key instanceof String;
}
/**
* Converts this map back to a standard HashMap
*
* @return a standard representation of this map
*/
public Map<String, Set<Permissible>> detach() {
Map<String, Set<Permissible>> ret = new HashMap<>();
for (Map.Entry<String, Set<Permissible>> ent : entrySet()) {
if (ent.getValue() instanceof LPSubscriptionValueSet) {
Set<Permissible> backing = ((LPSubscriptionValueSet) ent.getValue()).backing;
Set<Permissible> copy; (copy = Collections.newSetFromMap(new WeakHashMap<>(backing.size()))).addAll(backing);
ret.put(ent.getKey(), copy);
} else {
ret.put(ent.getKey(), ent.getValue());
}
}
return ret;
}
/**
* Value map extension which includes LP objects in Permissible related queries.
*/
public final class LPSubscriptionValueSet implements Set<Permissible> {
// the permission being mapped to this value map
private final String permission;
// the backing map
private final Set<Permissible> backing;
private LPSubscriptionValueSet(String permission, Set<Permissible> content) {
this.permission = permission;
this.backing = Collections.newSetFromMap(new WeakHashMap<>());
if (content != null) {
this.backing.addAll(content);
}
}
private LPSubscriptionValueSet(String permission) {
this(permission, null);
}
private Sets.SetView<Permissible> getContentView() {
// gather players (LPPermissibles)
Set<Permissible> players = LPSubscriptionMap.this.plugin.getServer().getOnlinePlayers().values().stream()
.filter(player -> player.isPermissionSet(this.permission))
.collect(Collectors.toSet());
return Sets.union(players, this.backing);
}
@Override
public boolean contains(Object key) {
// try the backing map
if (this.backing.contains(key)) {
return true;
}
// then try the permissible
if (key instanceof Permissible) {
Permissible p = (Permissible) key;
return p.isPermissionSet(this.permission);
}
// no result
return false;
}
@Override
public boolean isEmpty() {
// we never want to remove this map from the parent - since it just gets recreated
// on subsequent calls
return false;
}
@Override
public int size() {
return Math.max(1, this.backing.size());
}
@Override
public Iterator<Permissible> iterator() {
return getContentView().iterator();
}
@Override
public Object[] toArray() {
return getContentView().toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return getContentView().toArray(a);
}
@Override
public boolean add(Permissible permissible) {
return this.backing.add(permissible);
}
@Override
public boolean addAll(Collection<? extends Permissible> c) {
return this.backing.addAll(c);
}
@Override
public boolean remove(Object o) {
return this.backing.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return getContentView().containsAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
return this.backing.retainAll(c);
}
@Override
public boolean removeAll(Collection<?> c) {
return this.backing.removeAll(c);
}
@Override
public void clear() {
this.backing.clear();
}
}
}

View File

@ -0,0 +1,64 @@
/*
* 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.nukkit.processors;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.common.processors.AbstractPermissionProcessor;
import me.lucko.luckperms.common.processors.PermissionProcessor;
import me.lucko.luckperms.nukkit.LPNukkitPlugin;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Permission Processor for Nukkits "child" permission system.
*/
public class ChildProcessor extends AbstractPermissionProcessor implements PermissionProcessor {
private final LPNukkitPlugin plugin;
private Map<String, Boolean> childPermissions = Collections.emptyMap();
public ChildProcessor(LPNukkitPlugin plugin) {
this.plugin = plugin;
}
@Override
public Tristate hasPermission(String permission) {
return Tristate.fromNullableBoolean(this.childPermissions.get(permission));
}
@Override
public void refresh() {
Map<String, Boolean> builder = new ConcurrentHashMap<>();
for (Map.Entry<String, Boolean> e : this.sourceMap.entrySet()) {
Map<String, Boolean> children = this.plugin.getPermissionMap().getChildPermissions(e.getKey(), e.getValue());
if (children != null) {
builder.putAll(children);
}
}
this.childPermissions = builder;
}
}

View File

@ -0,0 +1,55 @@
/*
* 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.nukkit.processors;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.common.processors.PermissionProcessor;
import me.lucko.luckperms.nukkit.LPNukkitPlugin;
import me.lucko.luckperms.nukkit.model.PermissionDefault;
/**
* Permission Processor for Nukkits "default" permission system.
*/
public class DefaultsProcessor implements PermissionProcessor {
private final LPNukkitPlugin plugin;
private final boolean isOp;
public DefaultsProcessor(LPNukkitPlugin plugin, boolean isOp) {
this.plugin = plugin;
this.isOp = isOp;
}
@Override
public Tristate hasPermission(String permission) {
Tristate t = this.plugin.getDefaultPermissionMap().lookupDefaultPermission(permission, this.isOp);
if (t != Tristate.UNDEFINED) {
return t;
}
PermissionDefault def = PermissionDefault.fromPermission(this.plugin.getPermissionMap().get(permission));
return def == null ? Tristate.UNDEFINED : Tristate.fromBoolean(def.getValue(this.isOp));
}
}

View File

@ -0,0 +1,459 @@
####################################################################################################
# +----------------------------------------------------------------------------------------------+ #
# | __ __ ___ __ __ | #
# | | | | / ` |__/ |__) |__ |__) |\/| /__` | #
# | |___ \__/ \__, | \ | |___ | \ | | .__/ | #
# | | #
# | | #
# | SOURCE CODE: https://github.com/lucko/LuckPerms | #
# | WIKI: https://github.com/lucko/LuckPerms/wiki | #
# | BUG REPORTS: https://github.com/lucko/LuckPerms/issues | #
# | | #
# | Each option in this file is documented and explained here: | #
# | ==> https://github.com/lucko/LuckPerms/wiki/Configuration | #
# | | #
# | New options are not added to this file automatically. Default values are used if an | #
# | option cannot be found. The latest config versions can be obtained at the link above. | #
# +----------------------------------------------------------------------------------------------+ #
####################################################################################################
# +----------------------------------------------------------------------------------------------+ #
# | General | #
# +----------------------------------------------------------------------------------------------+ #
# The name of the server, used for server specific permissions. Set to 'global' to disable.
server: global
# If users on this server should have their global permissions applied.
# If set to false, only server specific permissions will apply for users on this server
include-global: true
# If users on this server should have their global world permissions applied.
# If set to false, only world specific permissions will apply for users on this server
include-global-world: true
# If users on this server should have global (non-server specific) groups applied
apply-global-groups: true
# If users on this server should have global (non-world specific) groups applied
apply-global-world-groups: true
# If the servers own UUID cache/lookup facility should be used when there is no record for a player
# in the LuckPerms cache.
use-server-uuid-cache: false
# If LuckPerms should use the "server-name" property from the "server.properties"
# file as the "server" option within LuckPerms.
use-server-properties-name: false
# If set to true, LuckPerms will allow usernames with non alphanumeric characters.
#
# Note that due to the design of the storage implementation, usernames must still be
# 16 characters or less.
allow-invalid-usernames: false
# If LuckPerms should produce extra logging output when it handles logins.
# Useful if you're having issues with UUID forwarding or data not being loaded.
debug-logins: false
# If the plugin should send log notifications to users whenever permissions are modified.
log-notify: true
# Mirrors world names. Whenever LuckPerms checks what world a user is in, if the world name is in
# this list, the value assigned will be sent forward for permission calculation instead.
world-rewrite:
# world_nether: world
# world_the_end: world
# Controls how temporary permissions/parents/meta should be accumulated
#
# The default behaviour is "deny"
# If "accumulate": durations will be added to the existing expiry time
# If "replace": durations will be replaced if the new duration is later than the current expiration
# If "deny": the command will just fail if you try to add another node with the same expiry
temporary-add-behaviour: deny
# How should LuckPerms determine a users "primary" group.
#
# Available Options:
# -> stored use the value stored against the users record in the file/database
# -> parents-by-weight just use the users most highly weighted parent
# -> all-parents-by-weight same as above, but calculates based upon all parents inherited from both
# directly and indirectly
primary-group-calculation: parents-by-weight
# If set to false, the plugin will allow a Users primary group to be removed with the
# 'parent remove' command, and will set their primary group back to default.
prevent-primary-group-removal: false
# If the plugin should check for "extra" permissions with users run LP commands.
#
# These extra permissions allow finer control over what users can do with each command, and
# who they have access to edit.
#
# The permissions are *not* static, unlike the 'base' permisssions, and will depend upon the
# arguments given within the command.
argument-based-command-permissions: false
# +----------------------------------------------------------------------------------------------+ #
# | Permission Calculation | #
# +----------------------------------------------------------------------------------------------+ #
# If the plugin should apply wildcard permissions.
# If set to true, LuckPerms will detect wildcard permissions, and resolve & apply all registered
# permissions matching the wildcard.
apply-wildcards: true
# If the plugin should parse regex permissions.
# If set to true, LuckPerms will detect regex permissions, marked with "r=" at the start of the
# node, and resolve & apply all registered permissions matching the regex.
apply-regex: true
# If the plugin should complete and apply shorthand permissions.
# If set to true, LuckPerms will detect and expand shorthand node patterns.
apply-shorthand: true
# If the plugin should apply Nukkit child permissions.
# Plugin authors can define custom permissions structures for their plugin, which will be resolved
# and used by LuckPerms if this setting is enabled.
apply-nukkit-child-permissions: true
# If the plugin should apply Nukkit default permissions.
# Plugin authors can define permissions which should be given to all users by default, or setup
# permissions which should/shouldn't be given to opped players.
# If this option is set to false, LuckPerms will ignore these defaults.
apply-nukkit-default-permissions: true
# If the plugin should apply attachment permissions.
# Other plugins on the server are able to add their own "permission attachments" to players. This
# allows them to grant players additional permissions which last until the end of the session, or
# until they're removed. If this option is set to false, LuckPerms will not include these attachment
# permissions when considering if a player should have access to a certain permission.
apply-nukkit-attachment-permissions: true
# The algorithm LuckPerms should use when traversing the "inheritance tree".
#
# The valid options are:
# - breadth-first
# - depth-first-pre-order
# - depth-first-post-order
#
# See here for information about the differences between each algorithm.
# - https://en.wikipedia.org/wiki/Breadth-first_search
# - https://en.wikipedia.org/wiki/Depth-first_search
inheritance-traversal-algorithm: depth-first-pre-order
# Define special group weights for this server.
# Default is just 0.
group-weight:
# admin: 10
# +----------------------------------------------------------------------------------------------+ #
# | Meta Formatting & Stacking | #
# +----------------------------------------------------------------------------------------------+ #
# Allows you to setup prefix/suffix stacking options.
#
# Available formats:
# - highest
# - lowest
# - highest_own
# - lowest_own
# - highest_inherited
# - lowest_inherited
# - highest_on_track_<track>
# - lowest_on_track_<track>
# - highest_not_on_track_<track>
# - lowest_not_on_track_<track>
#
# Each element is added in the order listed.
meta-formatting:
prefix:
format:
- "highest"
start-spacer: ""
middle-spacer: " "
end-spacer: ""
suffix:
format:
- "highest"
start-spacer: ""
middle-spacer: " "
end-spacer: ""
# +----------------------------------------------------------------------------------------------+ #
# | OP (Server Operator) Settings | #
# +----------------------------------------------------------------------------------------------+ #
# If the vanilla OP system is enabled. If set to false, all users will be de-opped, and the op/deop
# commands will be disabled.
enable-ops: true
# If set to true, any user with the permission "luckperms.autoop" will automatically be granted
# server operator status. This permission can be inherited, or set on specific servers/worlds,
# temporarily, etc.
#
# Additionally, setting this to true will force the "enable-ops" option above to false. All users
# will be de-opped unless they have the permission node, and the op/deop commands will be disabled.
#
# It is important to note that this setting is only checked when a player first joins the server,
# and when they switch worlds. Therefore, simply removing this permission from a user will not
# automatically de-op them. A player needs to relog to have the change take effect.
#
# It is recommended that you use this option instead of assigning a single '*' permission.
auto-op: false
# If opped players should be allowed to use LuckPerms commands. Set to false to only allow users who
# have the permissions access to the commands
commands-allow-op: true
# +----------------------------------------------------------------------------------------------+ #
# | Vault | #
# +----------------------------------------------------------------------------------------------+ #
# If the vault-server option below should be used.
# When this option is set to false, the server value defined above under "server" is used.
use-vault-server: false
# The name of the server used within Vault operations. If you don't want Vault operations to be
# server specific, set this to "global".
#
# Will only take effect if use-vault-server is set to true above.
vault-server: global
# If global permissions should be considered when retrieving meta or player groups
vault-include-global: true
# If Vault operations should ignore any world arguments if supplied.
vault-ignore-world: false
# If LuckPerms should print debugging info to console when a plugin uses a Vault function
vault-debug: false
# +----------------------------------------------------------------------------------------------+ #
# | Storage | #
# +----------------------------------------------------------------------------------------------+ #
# Which storage method the plugin should use.
#
# See: https://github.com/lucko/LuckPerms/wiki/Choosing-a-Storage-type
# Currently supported: mysql, mariadb, postgresql, sqlite, h2, json, yaml, hocon, mongodb
#
# Fill out connection info below if you're using MySQL, MariaDB, PostgreSQL or MongoDB
# If your MySQL server supports it, the "mariadb" option is preferred over "mysql".
storage-method: h2
# When using a file-based storage type, LuckPerms can monitor the data files for changes, and then
# schedule automatic updates when changes are detected.
#
# If you don't want this to happen, set this option to false.
watch-files: true
# This block enables support for split datastores.
split-storage:
enabled: false
methods:
user: h2
group: h2
track: h2
uuid: h2
log: h2
data:
# Uses standard DB engine port by default
# MySQL: 3306, PostgreSQL: 5432, MongoDB: 27017
# Specify as "host:port" if differs
address: localhost
database: minecraft
username: root
password: ''
# These settings apply to the MySQL connection pool.
# The default values will be suitable for the majority of users.
# Do not change these settings unless you know what you're doing!
pool-settings:
# Sets the maximum size of the MySQL connection pool.
# Basically this value will determine the maximum number of actual
# connections to the database backend.
#
# More information about determining the size of connection pools can be found here:
# https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing
maximum-pool-size: 10
# Sets the minimum number of idle connections that the pool will try to maintain.
#
# For maximum performance and responsiveness to spike demands, it is recommended to not set
# this value and instead allow the pool to act as a fixed size connection pool.
# (set this value to the same as 'maximum-pool-size')
minimum-idle: 10
# This setting controls the maximum lifetime of a connection in the pool in milliseconds.
# The value should be at least 30 seconds less than any database or infrastructure imposed
# connection time limit.
maximum-lifetime: 1800000 # 30 minutes
# This setting controls the maximum number of milliseconds that the plugin will wait for a
# connection from the pool, before timing out.
connection-timeout: 5000 # 5 seconds
# This setting allows you to define extra properties for connections.
properties:
useUnicode: true
characterEncoding: utf8
# The prefix for all LuckPerms tables. Change this is you want to use different tables for
# different servers.
#
# This should *not* be set to "lp_" if you have previously ran LuckPerms v2.16.81 or earlier with
# this database.
table_prefix: 'luckperms_'
# The prefix to use for all LuckPerms collections. Change this if you want to use different
# collections for different servers. The default is no prefix.
mongodb_collection_prefix: ''
# This option controls how frequently LuckPerms will perform a sync task.
# A sync task will refresh all data from the storage, and ensure that the most up-to-date data is
# being used by the plugin.
#
# This is disabled by default, as most users will not need it. However, if you're using a remote
# storage type without a messaging service setup, you may wish to set this value to something like
# 3.
#
# Set to -1 to disable the task completely.
sync-minutes: -1
# Settings for the messaging service
#
# If enabled and configured, LuckPerms will use the messaging system to inform other
# connected servers of changes. Use the command "/luckperms networksync" to push changes.
# Data is NOT stored using this service. It is only used as a messaging platform.
#
# If you decide to enable this feature, you should set "sync-minutes" to -1, as there is no need for
# LuckPerms to poll the database for changes.
#
# Available options:
# -> bungee uses the plugin messaging channels. Must be enabled on all connected servers to work.
# -> lilypad uses lilypad pub sub to push changes. You need to have the LilyPad-Connect plugin
# installed.
# -> redis uses redis pub sub to push changes. Your redis server must be configured below.
# -> none nothing
messaging-service: none
# If LuckPerms should automatically push updates after a change has been made with a command.
auto-push-updates: true
# If LuckPerms should push logging entries to connected servers via the messaging service.
push-log-entries: true
# If LuckPerms should broadcast received logging entries to players on this platform.
#
# If you have LuckPerms installed on your backend servers as well as a BungeeCord proxy, you should
# set this option to false on either your backends or your proxies, to avoid players being messaged
# twice about log entries.
broadcast-received-log-entries: true
# Settings for Redis.
# Port 6379 is used by default; set address to "host:port" if differs
redis:
enabled: false
address: localhost
password: ''
# +----------------------------------------------------------------------------------------------+ #
# | Default Assignments | #
# +----------------------------------------------------------------------------------------------+ #
# This section allows you to define defaults to give users whenever they connect to the server.
# The default assignments are highly configurable and conditional.
#
# There is one default assignment built into LuckPerms, which will add all users to the "default"
# group if they are not a member of any other group. This setting cannot be disabled. However, you
# can use this section to add more of your own.
#
# IMPORTANT:
# In order to save storage space, LuckPerms does not store users who have no permissions defined,
# and are only a member of the default group. Adding default assignments to this section will negate
# this effect. It is HIGHLY RECCOMENDED that instead of assigning defaults here, you add permissions
# to the "default" group, or set the "default" group to inherit other groups, and then use the
# group-name-rewrite rule above.
#
# It is also important to note that these rules are considered every time a player logs into the
# server, and are applied directly to the user's data. Simply removing a rule here will not reverse
# the effect of that rule on any users who have already had it applied to them.
#
# The "has" and "lacks" conditions below support standard boolean logic, using the 'and' & 'or'
# characters used in Java.
# e.g. "(some.other.permission | some.permission.other) & some.thing.else" == a user has
# 'some.other.permission', or 'some.permission.other', and they also have 'some.thing.else'
#
# Groups are represented by the permission node: group.<group name>
# Per server and per world nodes are represented by "server-world/permission" or "server/permission"
#
# Within conditions, permission nodes MUST be escaped using "<" and ">". See the example below.
#
# Explanation of the examples below: (they're just to demonstrate the features & use cases)
#
# rule1:
# If a user is either in the vip or vip+ group, and they have the "titles.titlecollector" permission
# set to true, and the "some.random.permission" set to false... if they're not in the group
# "prison_titlepack" on the "prison" server, then give add them to the "prison_titlepack" group on
# the "prison" server, and remove "some.random.permission".
#
# rule2:
# If the user isn't in any of the following groups on the skyblock server: sb_level1, sb_level2,
# sb_level3, then add them to sb_level1 on the skyblock server.
#
# rule3:
# If the user is a member of the default group, remove them from default, add them to member, and
# set their primary group to member.
#
# WARNING: Unlike internal commands, this system does not ensure that a group exists before adding
# a user to it. It also does not unsure that a user is a member of a group before making that group
# their primary group.
#
# Before you use "give: group.<name>" or "set-primary-group", make sure that the group exists, and
# that the user is a member of the group.
default-assignments:
# rule1:
# if:
# has-true: (<group.vip> | <group.vip+>) & <titles.tilecollector>
# has-false: <some.random.permission>
# lacks: <prison/group.prison_titlepack>
# give:
# - prison/group.prison_titlepack
# take:
# - some.random.permission
# rule2:
# if:
# lacks: <skyblock/group.sb_level1> & <skyblock/group.sb_level2> & <skyblock/group.sb_level3>
# give:
# - skyblock/group.sb_level1
# rule3:
# if:
# has-true: <group.default>
# take:
# - group.default
# give:
# - group.member
# set-primary-group: member

View File

@ -0,0 +1,14 @@
name: LuckPerms
version: ${full.version}
api: ["1.0.5"]
description: A permissions plugin
author: Luck
website: https://github.com/lucko/LuckPerms
main: me.lucko.luckperms.nukkit.LPNukkitPlugin
load: STARTUP
commands:
luckperms:
description: Manage permissions
aliases: [lp, perm, perms, permission, permissions]

View File

@ -17,6 +17,7 @@
<module>sponge/sponge-service-api6</module>
<module>sponge/sponge-service-api7</module>
<module>sponge</module>
<module>nukkit</module>
</modules>
<name>LuckPerms</name>
@ -152,14 +153,6 @@
<id>luck-repo</id>
<url>https://repo.lucko.me/</url>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>sponge-repo</id>
<url>https://repo.spongepowered.org/maven</url>
</repository>
</repositories>
</project>

View File

@ -157,4 +157,11 @@
</dependency>
</dependencies>
<repositories>
<repository>
<id>sponge-repo</id>
<url>https://repo.spongepowered.org/maven</url>
</repository>
</repositories>
</project>

View File

@ -54,4 +54,11 @@
</dependency>
</dependencies>
<repositories>
<repository>
<id>sponge-repo</id>
<url>https://repo.spongepowered.org/maven</url>
</repository>
</repositories>
</project>

View File

@ -54,4 +54,11 @@
</dependency>
</dependencies>
<repositories>
<repository>
<id>sponge-repo</id>
<url>https://repo.spongepowered.org/maven</url>
</repository>
</repositories>
</project>

View File

@ -55,4 +55,11 @@
</dependency>
</dependencies>
<repositories>
<repository>
<id>sponge-repo</id>
<url>https://repo.spongepowered.org/maven</url>
</repository>
</repositories>
</project>