Compare commits

...
45 Commits
Author SHA1 Message Date
Luck fa9324bda8 Implement basic standalone base, working towards #9 2016-09-20 21:50:28 +01:00
Luck 1e72f9d8fe Listen on lowest priority, may fix issues with plugins checking for permissions on login events 2016-09-20 21:04:53 +01:00
Luck 50fd954599 Use a BiMap in UuidCache 2016-09-19 21:13:45 +01:00
Luck b1e34b55f8 Re-add regex permissions & actually take note of the config options 2016-09-19 21:00:23 +01:00
Luck cff1b8a411 Separate PermissionProcessor into own class 2016-09-19 20:45:37 +01:00
Luck 6ffa6720be Make more specific wildcards take priority over less specific ones 2016-09-19 20:36:52 +01:00
Luck f52a106165 Order nodes alphabetically after other priority rules have been applied 2016-09-19 17:31:00 +01:00
Luck bf247b71e1 Fix possible race condition & maybe use better Hikari flags 2016-09-18 23:13:20 +01:00
Luck 66818e4c82 Fix build 2016-09-17 23:46:11 +01:00
Luck 23d53d39b4 Refactor permission calculation 2016-09-17 23:15:07 +01:00
Luck 30ea5a5b69 Fix no permissions applying when include-global is false, and better support per-server/per-world groups 2016-09-17 19:05:56 +01:00
Luck e7a210dc06 Update default configs 2016-09-16 22:09:55 +01:00
Luck 497de9824a Actually pass on a single wildcard node so it gets applied 2016-09-16 21:46:55 +01:00
Luck be74b50762 Fixes for offline mode servers 2016-09-16 21:24:25 +01:00
Luck f2e06b56e7 Cache permission lookups 2016-09-16 21:11:12 +01:00
Luck f6aa20c300 Fix compile 2016-09-15 17:12:19 +01:00
Luck 5e16134f6f Provide prefixes and suffixes through the PS options 2016-09-15 17:06:27 +01:00
Luck 6165fcbb21 Fix #getDefaults for users 2016-09-15 16:54:12 +01:00
Luck 831e882db0 Actually apply default permissions on Sponge 2016-09-15 16:53:03 +01:00
Luck feb8bf4bd3 Update readme 2016-09-14 20:16:36 +01:00
Luck ce3ee866ca Sponge fixes 2016-09-14 19:37:20 +01:00
Luck d29dd35689 Support the latest powerfulperms so-called "api" 2016-09-13 20:14:16 +01:00
Luck 3a0a89853f Apply negated wildcards 2016-09-13 17:23:16 +01:00
Luck e7ca79d251 Use Splitter instead of Patterns, don't split arguments in quotes 2016-09-12 19:00:37 +01:00
Luck 22426bab33 Update placeholders to use the latest API version 2016-09-11 15:36:19 +01:00
Luck 116094ea96 Maybe fix an issue with per-server prefixes/suffixes 2016-09-11 14:34:38 +01:00
Luck 9df39a9545 Inject PermissibleBase instead of using attachments. Resolves #14 2016-09-11 11:50:59 +01:00
Luck b8f44df723 Add missing headers 2016-09-10 22:40:36 +01:00
Luck 74f275eb69 Add more info the importer error output 2016-09-10 22:08:07 +01:00
Luck 1142cbcb2c Add platform type to the info command 2016-09-10 21:54:31 +01:00
Luck 37749e5829 Don't apply the default group when exporting users 2016-09-10 16:53:04 +01:00
Luck f9025c7a51 fix #getUniqueUsers for SQL storage types 2016-09-10 16:38:37 +01:00
Luck c7bfc38477 Build an extra jar with guava and gson shaded to support 1.7 2016-09-10 16:32:37 +01:00
Luck 7ca394d7b9 Add some extra console output to the debug command 2016-09-10 14:35:12 +01:00
Luck 31884ea59d Maybe fix race condition with messages not sending 2016-09-10 14:31:42 +01:00
Luck e79b7c7023 Update readme 2016-09-10 09:04:59 +01:00
Luck 1fdb4e8ade Merge pull request #11 from HeroCC/patch-1
Fix PermissionsEx false node migration
2016-09-09 21:49:43 +01:00
HeroCC 53db8c5928 Fix PermissionsEx false node migration
PermissionsEx false nodes begin with a '-' not a '!'
2016-09-09 16:39:13 -04:00
Luck 07514000a4 Fix global groups not being migrated from GroupManager 2016-09-09 18:41:15 +01:00
Luck f28721e55a Possible fix for permissions not applying on BungeeCord 2016-09-09 18:32:50 +01:00
Luck fc230291d3 Fix ops not receiving all permission nodes 2016-09-09 18:00:38 +01:00
Luck 89ff4cfa40 Add a much better system for exporting data 2016-09-08 21:01:37 +01:00
Luck d786b6abb2 Fix removing negative nodes 2016-09-08 17:28:31 +01:00
Luck fb0e694155 Add auto-op. Closes #8 2016-09-07 23:21:24 +01:00
Luck cd6cac1fb6 Fix bad grammar in config option comment 2016-09-07 21:01:03 +01:00
94 changed files with 2993 additions and 679 deletions

No files matched your search

+10 -1
View File
@@ -1,5 +1,6 @@
![alt text](https://i.imgur.com/7TwJZ5e.png "Banner")
# LuckPerms [![Build Status](https://ci.lucko.me/job/LuckPerms/badge/icon)](https://ci.lucko.me/job/LuckPerms/)
A permissions implementation for Bukkit/Spigot, BungeeCord and Sponge.
An advanced permissions implementation for Bukkit/Spigot, BungeeCord and Sponge.
## Links
* **Development Builds** - <https://ci.lucko.me/job/LuckPerms>
@@ -9,9 +10,17 @@ A permissions implementation for Bukkit/Spigot, BungeeCord and Sponge.
## Description
LuckPerms is an advanced permissions implementation aiming to be a flexible, performant and reliable alternative to other permission systems, providing all the features you could ever possibly need in a permissions plugin, along with an extensive and well documented API for developers.
* [Spigot Plugin Page](https://www.spigotmc.org/resources/luckperms-an-advanced-permissions-system.28140/ "Spigot Plugin Page")
* [Sponge Plugin Page](https://forums.spongepowered.org/t/luckperms-an-advanced-permissions-system/14274 "Sponge Plugin Page")
## LuckPerms vs Other Plugins
![alt text](https://static.lucko.me/luckperms-compare-copy.png "Feature comparison")
_Features checked on 23rd Aug 2016. If you find any inaccuracies, please do let me know. I tried to be as fair as possible, and copy all major features from the respective plugin pages._
## License
See LICENSE.md.
## Thanks to:
[![JProfiler](https://www.ej-technologies.com/images/product_banners/jprofiler_large.png)](http://www.ej-technologies.com/products/jprofiler/overview.html)
Thanks to ej-technologies for granting LuckPerms an open source licence to their [java profiling software](http://www.ej-technologies.com/products/jprofiler/overview.html "Java Profiler").
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.8-SNAPSHOT</version>
<version>2.9-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -62,6 +62,24 @@ public interface LPConfiguration {
*/
boolean getIncludeGlobalPerms();
/**
* @return if the users on this server will have their global world permissions applied
* @since 2.9
*/
boolean getIncludeGlobalWorldPerms();
/**
* @return true if the platform is applying global groups
* @since 2.9
*/
boolean getApplyGlobalGroups();
/**
* @return true if the platform is applying global world groups
* @since 2.9
*/
boolean getApplyGlobalWorldGroups();
/**
* @return the online mode setting in the config
*/
@@ -88,6 +106,12 @@ public interface LPConfiguration {
*/
boolean getLogNotify();
/**
* @return true if permission checks are being recorded / debugged
* @since 2.9
*/
boolean getDebugPermissionChecks();
/**
* @return true if the vanilla op system is enabled
* @since 2.8
@@ -100,6 +124,12 @@ public interface LPConfiguration {
*/
boolean getCommandsAllowOp();
/**
* @return true if auto op is enabled
* @since 2.9
*/
boolean getAutoOp();
/**
* @return the name of the server used within Vault operations
* @since 2.7
@@ -187,7 +187,7 @@ public interface LuckPermsApi {
Track getTrack(String name);
/**
* Gets a wrapped tracj object from the track storage. This method does not return null, unlike {@link #getTrack}
* Gets a wrapped track object from the track storage. This method does not return null, unlike {@link #getTrack}
* @param name the name of the track to get
* @return an optional {@link Track} object
* @throws NullPointerException if the name is null
@@ -30,7 +30,12 @@ public enum PlatformType {
BUKKIT("Bukkit"),
BUNGEE("Bungee"),
SPONGE("Sponge");
SPONGE("Sponge"),
/**
* @since 2.9
*/
STANDALONE("Standalone");
private final String friendlyName;
@@ -1,3 +1,25 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.exceptions;
/**
+119
View File
@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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>2.9-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>luckperms-bukkit-legacy</artifactId>
<build>
<defaultGoal>clean package</defaultGoal>
<finalName>LuckPerms-bukkit-legacy-${release.version}.${git.closest.tag.commit.count}</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>3.5.1</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>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>false</minimizeJar>
<artifactSet>
<excludes>
<exclude>org.xerial:*</exclude>
</excludes>
</artifactSet>
<relocations>
<relocation>
<pattern>org.slf4j</pattern>
<shadedPattern>me.lucko.luckperms.lib.slf4j</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>org.h2</pattern>
<shadedPattern>me.lucko.luckperms.lib.h2</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<goals>
<goal>revision</goal>
</goals>
</execution>
</executions>
<configuration>
<injectAllReactorProjects>true</injectAllReactorProjects>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- LuckPerms Bukkit -->
<dependency>
<groupId>me.lucko.luckperms</groupId>
<artifactId>luckperms-bukkit</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<!-- Shade gson and guava, as they are not included in earlier Bukkit releases -->
<!-- gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.7</version>
<scope>compile</scope>
</dependency>
<!-- guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
+3 -3
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.8-SNAPSHOT</version>
<version>2.9-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -30,8 +30,8 @@
<dependency>
<groupId>me.lucko.luckperms</groupId>
<artifactId>luckperms-api</artifactId>
<version>2.5</version>
<scope>compile</scope>
<version>2.8</version>
<scope>provided</scope>
</dependency>
<!-- BukkitAPI -->
<dependency>
@@ -25,15 +25,11 @@ package me.lucko.luckperms.api.placeholders;
import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.clip.placeholderapi.util.TimeUtil;
import me.lucko.luckperms.api.LuckPermsApi;
import me.lucko.luckperms.api.Track;
import me.lucko.luckperms.api.User;
import me.lucko.luckperms.api.*;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
/*
* PlaceholderAPI Expansion for LuckPerms, implemented using the LuckPerms API.
@@ -75,7 +71,7 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
@Override
public String getVersion() {
return "2.5";
return "2.8";
}
@Override
@@ -84,7 +80,7 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
return "";
}
Optional<User> u = api.getUserSafe(player.getUniqueId());
Optional<User> u = api.getUserSafe(api.getUuidCache().getUUID(player.getUniqueId()));
if (!u.isPresent()) {
return "";
}
@@ -127,17 +123,11 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
if (identifier.startsWith("expiry_time_") && identifier.length() > "expiry_time_".length()) {
String node = identifier.substring("expiry_time_".length());
long currentTime = System.currentTimeMillis() / 1000L;
Map<Map.Entry<String, Boolean>, Long> temps = user.getTemporaryNodes();
for (Map.Entry<Map.Entry<String, Boolean>, Long> e : temps.entrySet()) {
if (!e.getKey().getKey().equalsIgnoreCase(node)) {
continue;
for (Node n : user.getTemporaryPermissionNodes()) {
if (n.getPermission().equalsIgnoreCase(node)) {
return TimeUtil.getTime((int) (n.getExpiryUnixTime() - currentTime));
}
return TimeUtil.getTime((int) (e.getValue() - currentTime));
}
return "";
@@ -145,42 +135,27 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
if (identifier.startsWith("group_expiry_time_") && identifier.length() > "group_expiry_time_".length()) {
String node = "group." + identifier.substring("group_expiry_time_".length());
long currentTime = System.currentTimeMillis() / 1000L;
Map<Map.Entry<String, Boolean>, Long> temps = user.getTemporaryNodes();
for (Map.Entry<Map.Entry<String, Boolean>, Long> e : temps.entrySet()) {
if (!e.getKey().getKey().equalsIgnoreCase(node)) {
continue;
for (Node n : user.getTemporaryPermissionNodes()) {
if (n.getPermission().equalsIgnoreCase(node)) {
return TimeUtil.getTime((int) (n.getExpiryUnixTime() - currentTime));
}
return TimeUtil.getTime((int) (e.getValue() - currentTime));
}
return "";
}
if (identifier.equalsIgnoreCase("prefix")) {
return getChatMeta(PREFIX_PATTERN, user);
return MetaUtils.getPrefix(user, null, null, true);
}
if (identifier.equalsIgnoreCase("suffix")) {
return getChatMeta(SUFFIX_PATTERN, user);
return MetaUtils.getSuffix(user, null, null, true);
}
if (identifier.startsWith("meta_") && identifier.length() > "meta_".length()) {
String node = "meta." + escapeCharacters(identifier.substring("meta_".length())) + ".";
Map<String, Boolean> nodes = user.getNodes();
for (Map.Entry<String, Boolean> e : nodes.entrySet()) {
if (!e.getValue()) continue;
if (!e.getKey().toLowerCase().startsWith(node)) continue;
String meta = e.getKey().substring(node.length());
return unescapeCharacters(meta);
}
return "";
String node = identifier.substring("meta_".length());
return MetaUtils.getMeta(user, null, null, node, "", true);
}
return null;
@@ -204,44 +179,4 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
private static String formatBoolean(boolean b) {
return b ? PlaceholderAPIPlugin.booleanTrue() : PlaceholderAPIPlugin.booleanFalse();
}
private static String escapeCharacters(String s) {
s = s.replace(".", "{SEP}");
s = s.replace("/", "{FSEP}");
s = s.replace("$", "{DSEP}");
return s;
}
private static String unescapeCharacters(String s) {
s = s.replace("{SEP}", ".");
s = s.replace("{FSEP}", "/");
s = s.replace("{DSEP}", "$");
return s;
}
private static final Pattern PREFIX_PATTERN = Pattern.compile("(?i)prefix\\.\\d+\\..*");
private static final Pattern SUFFIX_PATTERN = Pattern.compile("(?i)suffix\\.\\d+\\..*");
private static final Pattern DOT_PATTERN = Pattern.compile("\\.");
private static String getChatMeta(Pattern pattern, User user) {
int priority = 0;
String meta = null;
for (Map.Entry<String, Boolean> e : user.getLocalPermissions(null, null).entrySet()) {
if (!e.getValue()) continue;
if (pattern.matcher(e.getKey()).matches()) {
String[] parts = DOT_PATTERN.split(e.getKey(), 3);
int p = Integer.parseInt(parts[1]);
if (meta == null || p > priority) {
meta = parts[2];
priority = p;
}
}
}
return meta == null ? "" : unescapeCharacters(meta);
}
}
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.8-SNAPSHOT</version>
<version>2.9-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -22,8 +22,12 @@
package me.lucko.luckperms;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.commands.CommandManager;
import me.lucko.luckperms.commands.Util;
import me.lucko.luckperms.constants.Patterns;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
@@ -39,7 +43,12 @@ class BukkitCommand extends CommandManager implements CommandExecutor, TabExecut
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
onCommand(BukkitSenderFactory.get().wrap(sender), label, Arrays.asList(args), Callback.empty());
onCommand(
BukkitSenderFactory.get().wrap(sender),
label,
Util.stripQuotes(Splitter.on(Patterns.COMMAND_SEPARATOR).omitEmptyStrings().splitToList(Joiner.on(' ').join(args))),
Callback.empty()
);
return true;
}
@@ -23,17 +23,18 @@
package me.lucko.luckperms;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.inject.Injector;
import me.lucko.luckperms.inject.LPPermissible;
import me.lucko.luckperms.users.BukkitUser;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.AbstractListener;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.*;
import org.bukkit.permissions.PermissionAttachment;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.UUID;
class BukkitListener extends AbstractListener implements Listener {
private final LPBukkitPlugin plugin;
@@ -43,7 +44,7 @@ class BukkitListener extends AbstractListener implements Listener {
this.plugin = plugin;
}
@EventHandler
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerPreLogin(AsyncPlayerPreLoginEvent e) {
if (!plugin.getDatastore().isAcceptingLogins()) {
// The datastore is disabled, prevent players from joining the server
@@ -54,7 +55,7 @@ class BukkitListener extends AbstractListener implements Listener {
onAsyncLogin(e.getUniqueId(), e.getName());
}
@EventHandler
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerLogin(PlayerLoginEvent e) {
final Player player = e.getPlayer();
final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(player.getUniqueId()));
@@ -67,42 +68,37 @@ class BukkitListener extends AbstractListener implements Listener {
if (user instanceof BukkitUser) {
BukkitUser u = (BukkitUser) user;
PermissionAttachment attachment = player.addAttachment(plugin);
Map<String, Boolean> newPermMap = new ConcurrentHashMap<>();
try {
/* Replace the standard LinkedHashMap in the attachment with a ConcurrentHashMap.
This means that we can iterate over and change the permissions within our attachment asynchronously,
without worrying about thread safety. The Bukkit side of things should still operate normally. Internal
permission stuff should work the same. This is by far the most easy and efficient way to do things, without
having to do tons of reflection. */
BukkitUser.getPermissionsField().set(attachment, newPermMap);
LPPermissible lpPermissible = new LPPermissible(player, plugin);
Injector.inject(player, lpPermissible);
u.setLpPermissible(lpPermissible);
} catch (Throwable t) {
t.printStackTrace();
}
u.setAttachment(new BukkitUser.PermissionAttachmentHolder(attachment, newPermMap));
}
plugin.doAsync(user::refreshPermissions);
}
@EventHandler
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerJoin(PlayerJoinEvent e) {
// Refresh permissions again
plugin.getUserManager().getWorldCache().put(e.getPlayer().getUniqueId(), e.getPlayer().getWorld().getName());
plugin.doAsync(() -> refreshPlayer(e.getPlayer().getUniqueId()));
UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId());
plugin.getUserManager().getWorldCache().put(internal, e.getPlayer().getWorld().getName());
plugin.doAsync(() -> refreshPlayer(internal));
}
@EventHandler
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerChangedWorld(PlayerChangedWorldEvent e) {
plugin.getUserManager().getWorldCache().put(e.getPlayer().getUniqueId(), e.getPlayer().getWorld().getName());
plugin.doAsync(() -> refreshPlayer(e.getPlayer().getUniqueId()));
UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId());
plugin.getUserManager().getWorldCache().put(internal, e.getPlayer().getWorld().getName());
plugin.doAsync(() -> refreshPlayer(internal));
}
@EventHandler
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerQuit(PlayerQuitEvent e) {
plugin.getUserManager().getWorldCache().remove(e.getPlayer().getUniqueId());
plugin.getUserManager().getWorldCache().remove(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()));
onLeave(e.getPlayer().getUniqueId());
}
@@ -34,8 +34,8 @@ import java.util.UUID;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BukkitSenderFactory extends SenderFactory<CommandSender> {
private static BukkitSenderFactory instance = null;
public static BukkitSenderFactory get() {
if (instance == null){
public static synchronized BukkitSenderFactory get() {
if (instance == null) {
instance = new BukkitSenderFactory();
}
return instance;
@@ -194,8 +194,11 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
}
@Override
public List<Sender> getSenders() {
return getServer().getOnlinePlayers().stream().map(p -> BukkitSenderFactory.get().wrap(p)).collect(Collectors.toList());
public List<Sender> getNotifyListeners() {
return getServer().getOnlinePlayers().stream()
.map(p -> BukkitSenderFactory.get().wrap(p, Collections.singleton(me.lucko.luckperms.constants.Permission.LOG_NOTIFY)))
.filter(me.lucko.luckperms.constants.Permission.LOG_NOTIFY::isAuthorized)
.collect(Collectors.toList());
}
@Override
@@ -267,11 +270,11 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
"luckperms." + e.getKey() + ".*", def,
e.getValue().stream()
.map(tag -> "luckperms." + e.getKey() + "." + tag)
.collect(Collectors.toMap(s -> s, s -> false))
.collect(Collectors.toMap(s -> s, s -> true))
)
);
}
pm.addPermission(new Permission("luckperms.*", def, all.stream().collect(Collectors.toMap(s -> s, s -> false))));
pm.addPermission(new Permission("luckperms.*", def, all.stream().collect(Collectors.toMap(s -> s, s -> true))));
}
}
@@ -30,6 +30,7 @@ import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.Contexts;
import net.milkbowl.vault.chat.Chat;
import java.util.Iterator;
@@ -118,10 +119,8 @@ public class VaultChatHook extends Chat {
continue;
}
if (!perms.getServer().equalsIgnoreCase("global")) {
if (!n.shouldApplyOnServer(perms.getServer(), perms.isIncludeGlobal(), false)) {
continue;
}
if (!n.shouldApplyOnServer(perms.getServer(), perms.isIncludeGlobal(), false)) {
continue;
}
if (!n.shouldApplyOnWorld(world, perms.isIncludeGlobal(), false)) {
@@ -164,22 +163,20 @@ public class VaultChatHook extends Chat {
int priority = Integer.MIN_VALUE;
String meta = null;
for (Node n : holder.getAllNodes(null)) {
for (Node n : holder.getAllNodes(null, Contexts.allowAll())) {
if (!n.getValue()) {
continue;
}
if (!perms.getServer().equalsIgnoreCase("global")) {
if (!n.shouldApplyOnServer(perms.getServer(), perms.isIncludeGlobal(), false)) {
continue;
}
}
if (!n.shouldApplyOnWorld(world, perms.isIncludeGlobal(), false)) {
if (prefix ? !n.isPrefix() : !n.isSuffix()) {
continue;
}
if (prefix ? !n.isPrefix() : !n.isSuffix()) {
if (!n.shouldApplyOnServer(perms.getServer(), perms.isIncludeGlobal(), false)) {
continue;
}
if (!n.shouldApplyOnWorld(world, perms.isIncludeGlobal(), false)) {
continue;
}
@@ -0,0 +1,108 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.inject;
import lombok.experimental.UtilityClass;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.PermissibleBase;
import java.lang.reflect.Field;
/**
* Injects a {@link LPPermissible} into a {@link Player}
*/
@UtilityClass
public class Injector {
private static Field HUMAN_ENTITY_FIELD;
static {
try {
HUMAN_ENTITY_FIELD = Class.forName(getInternalClassName("entity.CraftHumanEntity")).getDeclaredField("perm");
HUMAN_ENTITY_FIELD.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
public static boolean inject(CommandSender sender, PermissibleBase permissible) {
try {
Field f = getPermField(sender);
f.set(sender, permissible);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static boolean unInject(CommandSender sender) {
try {
Permissible permissible = getPermissible(sender);
if (permissible instanceof LPPermissible) {
getPermField(sender).set(sender, new PermissibleBase(sender));
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
private static Permissible getPermissible(CommandSender sender) {
try {
Field f = getPermField(sender);
return (Permissible) f.get(sender);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Field getPermField(CommandSender sender) {
if (sender instanceof Player) {
return HUMAN_ENTITY_FIELD;
}
throw new RuntimeException("Couldn't get perm field for sender " + sender.getClass().getName());
}
private static String getInternalClassName(String className) {
Class server = Bukkit.getServer().getClass();
if (!server.getSimpleName().equals("CraftServer")) {
throw new RuntimeException("Couldn't inject into server " + server);
}
String version;
if (server.getName().equals("org.bukkit.craftbukkit.CraftServer")) {
// Non versioned class
version = ".";
} else {
version = server.getName().substring("org.bukkit.craftbukkit".length());
version = version.substring(0, version.length() - "CraftServer".length());
}
return "org.bukkit.craftbukkit" + version + className;
}
}
@@ -0,0 +1,303 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.inject;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.utils.PermissionCalculator;
import me.lucko.luckperms.utils.PermissionProcessor;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.permissions.*;
import org.bukkit.plugin.Plugin;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.stream.Collectors;
/**
* Modified PermissibleBase for LuckPerms
*/
public class LPPermissible extends PermissibleBase {
@Getter
private final CommandSender parent;
private final PermissionCalculator calculator;
@Getter
private final Map<String, Boolean> luckPermsPermissions = new ConcurrentHashMap<>();
private final List<PermissionAttachment> attachments = new LinkedList<>();
private final Map<String, PermissionAttachmentInfo> attachmentPermissions = new HashMap<>();
public LPPermissible(@NonNull CommandSender sender, LuckPermsPlugin plugin) {
super(sender);
this.parent = sender;
List<PermissionProcessor> processors = new ArrayList<>(5);
processors.add(new PermissionCalculator.MapProcessor(luckPermsPermissions));
processors.add(new AttachmentProcessor(attachmentPermissions));
if (plugin.getConfiguration().getApplyWildcards()) {
processors.add(new PermissionCalculator.WildcardProcessor(luckPermsPermissions));
}
if (plugin.getConfiguration().getApplyRegex()) {
processors.add(new PermissionCalculator.RegexProcessor(luckPermsPermissions));
}
processors.add(new BukkitDefaultsProcessor(parent::isOp));
calculator = new PermissionCalculator(plugin, parent.getName(), plugin.getConfiguration().getDebugPermissionChecks(), processors);
}
public void invalidateCache() {
calculator.invalidateCache();
}
@Override
public boolean isOp() {
return parent.isOp();
}
@Override
public void setOp(boolean value) {
parent.setOp(value);
}
@Override
public boolean isPermissionSet(@NonNull String name) {
return luckPermsPermissions.containsKey(name.toLowerCase()) || attachmentPermissions.containsKey(name.toLowerCase());
}
@Override
public boolean isPermissionSet(@NonNull Permission perm) {
return isPermissionSet(perm.getName());
}
@Override
public boolean hasPermission(@NonNull String name) {
Tristate ts = calculator.getPermissionValue(name);
if (ts != Tristate.UNDEFINED) {
return ts.asBoolean();
}
return Permission.DEFAULT_PERMISSION.getValue(isOp());
}
@Override
public boolean hasPermission(@NonNull Permission perm) {
Tristate ts = calculator.getPermissionValue(perm.getName());
if (ts != Tristate.UNDEFINED) {
return ts.asBoolean();
}
return perm.getDefault().getValue(isOp());
}
@Override
public PermissionAttachment addAttachment(@NonNull Plugin plugin, @NonNull String name, boolean value) {
if (!plugin.isEnabled()) {
throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled");
}
PermissionAttachment result = addAttachment(plugin);
result.setPermission(name, value);
recalculatePermissions();
return result;
}
@Override
public PermissionAttachment addAttachment(@NonNull Plugin plugin) {
if (!plugin.isEnabled()) {
throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled");
}
PermissionAttachment result = new PermissionAttachment(plugin, parent);
attachments.add(result);
recalculatePermissions();
return result;
}
@Override
public PermissionAttachment addAttachment(@NonNull Plugin plugin, @NonNull String name, boolean value, int ticks) {
if (!plugin.isEnabled()) {
throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled");
}
PermissionAttachment result = addAttachment(plugin, ticks);
if (result != null) {
result.setPermission(name, value);
}
return result;
}
@Override
public PermissionAttachment addAttachment(@NonNull Plugin plugin, int ticks) {
if (!plugin.isEnabled()) {
throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled");
}
PermissionAttachment result = addAttachment(plugin);
if (Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new RemoveAttachmentRunnable(result), ticks) == -1) {
Bukkit.getServer().getLogger().log(Level.WARNING, "Could not add PermissionAttachment to " + parent + " for plugin " + plugin.getDescription().getFullName() + ": Scheduler returned -1");
result.remove();
return null;
} else {
return result;
}
}
@Override
public void removeAttachment(@NonNull PermissionAttachment attachment) {
if (attachments.contains(attachment)) {
attachments.remove(attachment);
PermissionRemovedExecutor ex = attachment.getRemovalCallback();
if (ex != null) {
ex.attachmentRemoved(attachment);
}
recalculatePermissions();
} else {
throw new IllegalArgumentException("Given attachment is not part of Permissible object " + parent);
}
}
@Override
public void recalculatePermissions() {
if (attachmentPermissions == null) {
return;
}
attachmentPermissions.clear();
Set<Permission> defaults = Bukkit.getServer().getPluginManager().getDefaultPermissions(isOp());
Bukkit.getServer().getPluginManager().subscribeToDefaultPerms(isOp(), parent);
for (Permission perm : defaults) {
String name = perm.getName().toLowerCase();
attachmentPermissions.put(name, new PermissionAttachmentInfo(parent, name, null, true));
Bukkit.getServer().getPluginManager().subscribeToPermission(name, parent);
calculateChildPermissions(perm.getChildren(), false, null);
}
for (PermissionAttachment attachment : attachments) {
calculateChildPermissions(attachment.getPermissions(), false, attachment);
}
invalidateCache();
}
@Override
public synchronized void clearPermissions() {
Set<String> perms = attachmentPermissions.keySet();
for (String name : perms) {
Bukkit.getServer().getPluginManager().unsubscribeFromPermission(name, parent);
}
Bukkit.getServer().getPluginManager().unsubscribeFromDefaultPerms(false, parent);
Bukkit.getServer().getPluginManager().unsubscribeFromDefaultPerms(true, parent);
attachmentPermissions.clear();
}
private void calculateChildPermissions(Map<String, Boolean> children, boolean invert, PermissionAttachment attachment) {
for (Map.Entry<String, Boolean> e : children.entrySet()) {
Permission perm = Bukkit.getServer().getPluginManager().getPermission(e.getKey());
boolean value = e.getValue() ^ invert;
String name = e.getKey().toLowerCase();
attachmentPermissions.put(name, new PermissionAttachmentInfo(parent, name, attachment, value));
Bukkit.getServer().getPluginManager().subscribeToPermission(name, parent);
if (perm != null) {
calculateChildPermissions(perm.getChildren(), !value, attachment);
}
}
}
@Override
public Set<PermissionAttachmentInfo> getEffectivePermissions() {
Set<PermissionAttachmentInfo> perms = new HashSet<>();
perms.addAll(attachmentPermissions.values());
perms.addAll(luckPermsPermissions.entrySet().stream()
.map(e -> new PermissionAttachmentInfo(parent, e.getKey(), null, e.getValue()))
.collect(Collectors.toList()));
return perms;
}
private class RemoveAttachmentRunnable implements Runnable {
private PermissionAttachment attachment;
private RemoveAttachmentRunnable(PermissionAttachment attachment) {
this.attachment = attachment;
}
public void run() {
attachment.remove();
}
}
@AllArgsConstructor
private static class AttachmentProcessor implements PermissionProcessor {
@Getter
private final Map<String, PermissionAttachmentInfo> map;
@Override
public Tristate hasPermission(String permission) {
if (map.containsKey(permission)) {
return Tristate.fromBoolean(map.get(permission).getValue());
}
return Tristate.UNDEFINED;
}
}
@AllArgsConstructor
private static class BukkitDefaultsProcessor implements PermissionProcessor {
private final Supplier<Boolean> isOp;
@Override
public Tristate hasPermission(String permission) {
Permission defPerm = Bukkit.getServer().getPluginManager().getPermission(permission);
if (defPerm != null) {
return Tristate.fromBoolean(defPerm.getDefault().getValue(isOp.get()));
} else {
return Tristate.UNDEFINED;
}
}
}
}
@@ -22,39 +22,25 @@
package me.lucko.luckperms.users;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import me.lucko.luckperms.LPBukkitPlugin;
import me.lucko.luckperms.api.event.events.UserPermissionRefreshEvent;
import me.lucko.luckperms.api.implementation.internal.UserLink;
import org.bukkit.permissions.PermissionAttachment;
import me.lucko.luckperms.inject.LPPermissible;
import me.lucko.luckperms.utils.Contexts;
import org.bukkit.permissions.Permissible;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;
public class BukkitUser extends User {
private static Field permissionsField = null;
public static Field getPermissionsField() {
if (permissionsField == null) {
try {
permissionsField = PermissionAttachment.class.getDeclaredField("permissions");
permissionsField.setAccessible(true);
} catch (SecurityException | NoSuchFieldException e) {
e.printStackTrace();
}
}
return permissionsField;
}
private final LPBukkitPlugin plugin;
@Getter
@Setter
private PermissionAttachmentHolder attachment = null;
private LPPermissible lpPermissible = null;
BukkitUser(UUID uuid, LPBukkitPlugin plugin) {
super(uuid, plugin);
@@ -68,24 +54,28 @@ public class BukkitUser extends User {
@SuppressWarnings("unchecked")
@Override
public void refreshPermissions() {
if (attachment == null) {
public synchronized void refreshPermissions() {
if (lpPermissible == null) {
return;
}
// Calculate the permissions that should be applied. This is done async, who cares about how long it takes or how often it's done.
Map<String, Boolean> toApply = exportNodes(
getPlugin().getConfiguration().getServer(),
plugin.getUserManager().getWorldCache().get(getUuid()),
null,
plugin.getConfiguration().getIncludeGlobalPerms(),
true,
plugin.getPossiblePermissions()
new Contexts(
getPlugin().getConfiguration().getServer(),
plugin.getUserManager().getWorldCache().get(getUuid()),
null,
getPlugin().getConfiguration().getIncludeGlobalPerms(),
getPlugin().getConfiguration().getIncludeGlobalWorldPerms(),
true,
getPlugin().getConfiguration().getApplyGlobalGroups(),
getPlugin().getConfiguration().getApplyGlobalWorldGroups()
),
Collections.emptyList()
);
try {
// The map in the LP PermissionAttachment is a ConcurrentHashMap. We can modify and iterate over its contents async.
Map<String, Boolean> existing = attachment.getPermissions();
Map<String, Boolean> existing = lpPermissible.getLuckPermsPermissions();
boolean different = false;
if (toApply.size() != existing.size()) {
@@ -103,26 +93,30 @@ public class BukkitUser extends User {
if (!different) return;
existing.clear();
lpPermissible.invalidateCache();
existing.putAll(toApply);
/* Must be called sync, as #recalculatePermissions is an unmodified Bukkit API call that is absolutely not thread safe.
Shouldn't be too taxing on the server. This only gets called when permissions have actually changed,
which is like once per user per login, assuming their permissions don't get modified. */
plugin.doSync(() -> {
attachment.getAttachment().getPermissible().recalculatePermissions();
plugin.getApiProvider().fireEventAsync(new UserPermissionRefreshEvent(new UserLink(this)));
});
if (plugin.getConfiguration().getAutoOp()) {
boolean op = false;
for (Map.Entry<String, Boolean> e : toApply.entrySet()) {
if (e.getKey().equalsIgnoreCase("luckperms.autoop") && e.getValue()) {
op = true;
break;
}
}
final boolean finalOp = op;
if (lpPermissible.isOp() != op) {
final Permissible parent = lpPermissible.getParent();
plugin.doSync(() -> parent.setOp(finalOp));
}
}
plugin.getApiProvider().fireEventAsync(new UserPermissionRefreshEvent(new UserLink(this)));
} catch (Exception e) {
e.printStackTrace();
}
}
@Getter
@EqualsAndHashCode
@AllArgsConstructor
public static class PermissionAttachmentHolder {
private final PermissionAttachment attachment;
private final Map<String, Boolean> permissions;
}
}
@@ -24,6 +24,7 @@ package me.lucko.luckperms.users;
import lombok.Getter;
import me.lucko.luckperms.LPBukkitPlugin;
import me.lucko.luckperms.inject.Injector;
import org.bukkit.entity.Player;
import java.util.Map;
@@ -47,14 +48,16 @@ public class BukkitUserManager extends UserManager {
public void preUnload(User user) {
if (user instanceof BukkitUser) {
BukkitUser u = (BukkitUser) user;
if (u.getAttachment() != null) {
Player player = plugin.getServer().getPlayer(plugin.getUuidCache().getExternalUUID(u.getUuid()));
if (player != null) {
player.removeAttachment(u.getAttachment().getAttachment());
Player player = plugin.getServer().getPlayer(plugin.getUuidCache().getExternalUUID(u.getUuid()));
if (player != null) {
if (u.getLpPermissible() != null) {
Injector.unInject(player);
u.setLpPermissible(null);
}
if (plugin.getConfiguration().getAutoOp()) {
player.setOp(false);
}
u.setAttachment(null);
}
}
}
+27 -6
View File
@@ -8,9 +8,20 @@
# 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/groups applied.
# 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 this server is in offline or online mode.
# This setting allows a player to have the same UUID across a network of offline mode/mixed servers.
@@ -30,13 +41,12 @@ online-mode: true
# 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. This will only work for plugins that define all of their permissions to the server.
# 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. This will only work for plugins that define all of their
# permissions to the server.
# apply all registered permissions matching the regex.
apply-regex: true
# If the plugin should complete and apply shorthand permissions.
@@ -46,10 +56,21 @@ apply-shorthand: true
# If the plugin should send log notifications to users whenever permissions are modified.
log-notify: true
# If LuckPerms should print to console every time a plugin checks if a player has a permission
debug-permission-checks: false
# 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 opped players should be allowed to use LuckPerms commands. Set to false to only allow users with the permission access
# 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 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
# The name of the server used within Vault operations. If you don't want Vault operations to be server specific, set this
@@ -84,4 +105,4 @@ data:
# Set to -1 to disable. If this is the only instance accessing the datastore, you can disable syncing.
# e.g. if you're using sqlite or flatfile, this can be set to -1 to save resources.
sync-minutes: 3
sync-minutes: 3
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.8-SNAPSHOT</version>
<version>2.9-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -22,8 +22,12 @@
package me.lucko.luckperms;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.commands.CommandManager;
import me.lucko.luckperms.commands.Util;
import me.lucko.luckperms.constants.Patterns;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.TabExecutor;
@@ -40,7 +44,12 @@ class BungeeCommand extends Command implements TabExecutor {
@Override
public void execute(CommandSender sender, String[] args) {
manager.onCommand(BungeeSenderFactory.get().wrap(sender), "bperms", Arrays.asList(args), Callback.empty());
manager.onCommand(
BungeeSenderFactory.get().wrap(sender),
"bperms",
Util.stripQuotes(Splitter.on(Patterns.COMMAND_SEPARATOR).omitEmptyStrings().splitToList(Joiner.on(' ').join(args))),
Callback.empty()
);
}
@Override
@@ -33,8 +33,6 @@ import net.md_5.bungee.api.event.*;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@@ -51,22 +49,18 @@ public class BungeeListener extends AbstractListener implements Listener {
@EventHandler
public void onPlayerPermissionCheck(PermissionCheckEvent e) {
if (!(e.getSender() instanceof ProxiedPlayer)) {
e.setHasPermission(true);
return;
}
final ProxiedPlayer player = ((ProxiedPlayer) e.getSender());
final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(player.getUniqueId()));
if (user == null) return;
final String server = player.getServer() == null ? null : (player.getServer().getInfo() == null ? null : player.getServer().getInfo().getName());
Map<String, Boolean> local = user.getLocalPermissions(plugin.getConfiguration().getServer(), server, null, Collections.singletonList(e.getPermission()));
for (Map.Entry<String, Boolean> en : local.entrySet()) {
if (en.getKey().equalsIgnoreCase(e.getPermission())) {
e.setHasPermission(en.getValue());
return;
}
BungeePlayerCache playerCache = plugin.getPlayerCache().get(plugin.getUuidCache().getUUID(player.getUniqueId()));
if (playerCache == null) {
return;
}
e.setHasPermission(playerCache.getPermissionValue(e.getPermission()));
}
@EventHandler
@@ -108,17 +102,20 @@ public class BungeeListener extends AbstractListener implements Listener {
@EventHandler
public void onPlayerPostLogin(PostLoginEvent e) {
final ProxiedPlayer player = e.getPlayer();
final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()));
final UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId());
final User user = plugin.getUserManager().get(internal);
if (user == null) {
plugin.getProxy().getScheduler().schedule(plugin, () -> player.sendMessage(WARN_MESSAGE), 3, TimeUnit.SECONDS);
} else {
plugin.getPlayerCache().put(internal, new BungeePlayerCache(plugin, e.getPlayer().getName()));
user.refreshPermissions();
}
}
@EventHandler
public void onPlayerQuit(PlayerDisconnectEvent e) {
plugin.getPlayerCache().remove(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()));
onLeave(e.getPlayer().getUniqueId());
}
@@ -0,0 +1,61 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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;
import lombok.Getter;
import me.lucko.luckperms.utils.PermissionCalculator;
import me.lucko.luckperms.utils.PermissionProcessor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class BungeePlayerCache {
private final PermissionCalculator calculator;
@Getter
private final Map<String, Boolean> permissions = new ConcurrentHashMap<>();
public BungeePlayerCache(LuckPermsPlugin plugin, String name) {
List<PermissionProcessor> processors = new ArrayList<>(3);
processors.add(new PermissionCalculator.MapProcessor(permissions));
if (plugin.getConfiguration().getApplyWildcards()) {
processors.add(new PermissionCalculator.WildcardProcessor(permissions));
}
if (plugin.getConfiguration().getApplyRegex()) {
processors.add(new PermissionCalculator.RegexProcessor(permissions));
}
calculator = new PermissionCalculator(plugin, name, plugin.getConfiguration().getDebugPermissionChecks(), processors);
}
public void invalidateCache() {
calculator.invalidateCache();
}
public boolean getPermissionValue(String permission) {
return calculator.getPermissionValue(permission).asBoolean();
}
}
@@ -35,8 +35,8 @@ import java.util.UUID;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BungeeSenderFactory extends SenderFactory<CommandSender> {
private static BungeeSenderFactory instance = null;
public static BungeeSenderFactory get() {
if (instance == null){
public static synchronized BungeeSenderFactory get() {
if (instance == null) {
instance = new BungeeSenderFactory();
}
return instance;
@@ -30,6 +30,7 @@ import me.lucko.luckperms.commands.CommandManager;
import me.lucko.luckperms.commands.ConsecutiveExecutor;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.core.LPConfiguration;
import me.lucko.luckperms.core.UuidCache;
import me.lucko.luckperms.data.Importer;
@@ -46,16 +47,14 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Plugin;
import java.io.File;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Getter
public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
private final Map<UUID, BungeePlayerCache> playerCache = new ConcurrentHashMap<>();
private final Set<UUID> ignoringLogs = ConcurrentHashMap.newKeySet();
private LPConfiguration configuration;
private UserManager userManager;
@@ -161,8 +160,11 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
}
@Override
public List<Sender> getSenders() {
return getProxy().getPlayers().stream().map(p -> BungeeSenderFactory.get().wrap(p)).collect(Collectors.toList());
public List<Sender> getNotifyListeners() {
return getProxy().getPlayers().stream()
.map(p -> BungeeSenderFactory.get().wrap(p, Collections.singleton(Permission.LOG_NOTIFY)))
.filter(Permission.LOG_NOTIFY::isAuthorized)
.collect(Collectors.toList());
}
@Override
@@ -22,21 +22,80 @@
package me.lucko.luckperms.users;
import me.lucko.luckperms.BungeePlayerCache;
import me.lucko.luckperms.LPBungeePlugin;
import me.lucko.luckperms.api.event.events.UserPermissionRefreshEvent;
import me.lucko.luckperms.api.implementation.internal.UserLink;
import me.lucko.luckperms.utils.Contexts;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;
public class BungeeUser extends User {
private final LPBungeePlugin plugin;
BungeeUser(UUID uuid, LPBungeePlugin plugin) {
super(uuid, plugin);
this.plugin = plugin;
}
BungeeUser(UUID uuid, String username, LPBungeePlugin plugin) {
super(uuid, username, plugin);
this.plugin = plugin;
}
@Override
public void refreshPermissions() {
// Do nothing. Permissions are applied when needed in a listener.
public synchronized void refreshPermissions() {
ProxiedPlayer player = plugin.getProxy().getPlayer(plugin.getUuidCache().getExternalUUID(getUuid()));
if (player == null) {
return;
}
BungeePlayerCache playerCache = plugin.getPlayerCache().get(getUuid());
if (playerCache == null) {
return;
}
final String server = player.getServer() == null ? null : (player.getServer().getInfo() == null ? null : player.getServer().getInfo().getName());
// Calculate the permissions that should be applied. This is done async.
Map<String, Boolean> toApply = exportNodes(
new Contexts(
plugin.getConfiguration().getServer(),
server,
null,
plugin.getConfiguration().getIncludeGlobalPerms(),
plugin.getConfiguration().getIncludeGlobalWorldPerms(),
true,
plugin.getConfiguration().getApplyGlobalGroups(),
plugin.getConfiguration().getApplyGlobalWorldGroups()
),
Collections.emptyList()
);
Map<String, Boolean> existing = playerCache.getPermissions();
boolean different = false;
if (toApply.size() != existing.size()) {
different = true;
} else {
for (Map.Entry<String, Boolean> e : existing.entrySet()) {
if (toApply.containsKey(e.getKey()) && toApply.get(e.getKey()) == e.getValue()) {
continue;
}
different = true;
break;
}
}
if (!different) return;
existing.clear();
playerCache.invalidateCache();
existing.putAll(toApply);
plugin.getApiProvider().fireEventAsync(new UserPermissionRefreshEvent(new UserLink(this)));
}
}
+17 -4
View File
@@ -8,9 +8,20 @@
# The name of the server, used for server specific permissions. Set to 'global' to disable.
server: bungee
# If users on this server should have their global permissions/groups applied.
# 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: false
# 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 this server is in offline or online mode.
# This setting allows a player to have the same UUID across a network of offline mode/mixed servers.
@@ -30,13 +41,12 @@ online-mode: true
# 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. This will only work for plugins that define all of their permissions to the server.
# 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. This will only work for plugins that define all of their
# permissions to the server.
# apply all registered permissions matching the regex.
apply-regex: true
# If the plugin should complete and apply shorthand permissions.
@@ -46,6 +56,9 @@ apply-shorthand: true
# If the plugin should send log notifications to users whenever permissions are modified.
log-notify: true
# If LuckPerms should print to console every time a plugin checks if a player has a permission
debug-permission-checks: false
# Which storage method the plugin should use.
# Currently supported: mysql, sqlite, h2, json, yaml, mongodb
# Fill out connection info below if you're using MySQL or MongoDB
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.8-SNAPSHOT</version>
<version>2.9-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -103,7 +103,7 @@ public interface LuckPermsPlugin {
/**
* @return a {@link List} of senders online on the platform
*/
List<Sender> getSenders();
List<Sender> getNotifyListeners();
/**
* @return the console sender of the instance
@@ -67,7 +67,7 @@ public class ApiProvider implements LuckPermsApi {
@Override
public double getApiVersion() {
return 2.8;
return 2.9;
}
@Override
@@ -61,6 +61,21 @@ public class LPConfigurationLink implements LPConfiguration {
return master.getIncludeGlobalPerms();
}
@Override
public boolean getIncludeGlobalWorldPerms() {
return master.getIncludeGlobalWorldPerms();
}
@Override
public boolean getApplyGlobalGroups() {
return master.getApplyGlobalGroups();
}
@Override
public boolean getApplyGlobalWorldGroups() {
return master.getApplyGlobalWorldGroups();
}
@Override
public boolean getOnlineMode() {
return master.getOnlineMode();
@@ -86,6 +101,11 @@ public class LPConfigurationLink implements LPConfiguration {
return master.getLogNotify();
}
@Override
public boolean getDebugPermissionChecks() {
return master.getDebugPermissionChecks();
}
@Override
public boolean getEnableOps() {
return master.getEnableOps();
@@ -96,6 +116,11 @@ public class LPConfigurationLink implements LPConfiguration {
return master.getCommandsAllowOp();
}
@Override
public boolean getAutoOp() {
return master.getAutoOp();
}
@Override
public String getVaultServer() {
return master.getVaultServer();
@@ -29,6 +29,7 @@ import me.lucko.luckperms.api.PermissionHolder;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.utils.Contexts;
import java.util.*;
@@ -67,7 +68,7 @@ public class PermissionHolderLink implements PermissionHolder {
@Override
public Set<Node> getAllNodes() {
return Collections.unmodifiableSet(master.getAllNodes(null));
return Collections.unmodifiableSet(master.getAllNodes(Collections.emptyList(), Contexts.allowAll()));
}
@Override
@@ -252,7 +253,7 @@ public class PermissionHolderLink implements PermissionHolder {
@Override
public Map<String, Boolean> getPermissions(String server, String world, Map<String, String> extraContext, boolean includeGlobal, List<String> possibleNodes, boolean applyGroups) {
return master.exportNodes(server, world, extraContext, includeGlobal, applyGroups, possibleNodes);
return master.exportNodes(new Contexts(server, world, extraContext, includeGlobal, includeGlobal, applyGroups, true, true), possibleNodes);
}
@Override
@@ -61,6 +61,7 @@ public class CommandManager {
.add(new InfoCommand())
.add(new DebugCommand())
.add(new ImportCommand())
.add(new ExportCommand())
.add(new QueueCommand())
.add(new MigrationMainCommand())
.add(new CreateGroup())
@@ -47,7 +47,11 @@ public abstract class SenderFactory<T> implements Runnable {
protected abstract boolean hasPermission(T t, String node);
public final Sender wrap(T t) {
return new SenderImp(t);
return new SenderImp(t, null);
}
public final Sender wrap(T t, Set<Permission> toCheck) {
return new SenderImp(t, toCheck);
}
@Override
@@ -81,15 +85,19 @@ public abstract class SenderFactory<T> implements Runnable {
private final boolean console;
private SenderImp(T t) {
private SenderImp(T t, Set<Permission> toCheck) {
this.tRef = new WeakReference<>(t);
this.name = factory.getName(t);
this.uuid = factory.getUuid(t);
this.console = this.uuid.equals(Constants.getConsoleUUID()) || this.uuid.equals(Constants.getImporterUUID());
if (!this.console) {
this.perms = ImmutableMap.copyOf(Arrays.stream(Permission.values())
.collect(Collectors.toMap(p -> p, p -> factory.hasPermission(t, p.getNode()))));
if (toCheck == null || toCheck.isEmpty()) {
this.perms = ImmutableMap.copyOf(Arrays.stream(Permission.values())
.collect(Collectors.toMap(p -> p, p -> factory.hasPermission(t, p.getNode()))));
} else {
this.perms = ImmutableMap.copyOf(toCheck.stream().collect(Collectors.toMap(p -> p, p -> factory.hasPermission(t, p.getNode()))));
}
}
}
@@ -37,6 +37,22 @@ public class Util {
sender.sendMessage(color(Message.PREFIX + message));
}
public static List<String> stripQuotes(List<String> input) {
input = new ArrayList<>(input);
ListIterator<String> iterator = input.listIterator();
while (iterator.hasNext()) {
String value = iterator.next();
if (!(value.length() >= 3)) {
continue;
}
if (value.charAt(0) == '"' && value.charAt(value.length() - 1) == '"') {
iterator.set(value.substring(1, value.length() - 1));
}
}
return input;
}
public static String color(String s) {
return translateAlternateColorCodes('&', s);
}
@@ -28,6 +28,7 @@ import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.Contexts;
import java.util.*;
@@ -42,7 +43,7 @@ public class GroupChatMeta extends SubCommand<Group> {
SortedSet<Map.Entry<Integer, String>> prefixes = new TreeSet<>(Util.getMetaComparator().reversed());
SortedSet<Map.Entry<Integer, String>> suffixes = new TreeSet<>(Util.getMetaComparator().reversed());
for (Node node : group.getAllNodes(null)) {
for (Node node : group.getAllNodes(null, Contexts.allowAll())) {
if (!node.isSuffix() && !node.isPrefix()) {
continue;
}
@@ -28,7 +28,6 @@ import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Patterns;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
@@ -53,7 +52,7 @@ public class GroupSetPermission extends SubCommand<Group> {
return CommandResult.INVALID_ARGS;
}
if (Patterns.GROUP_MATCH.matcher(node).matches()) {
if (node.toLowerCase().startsWith("group.")) {
Message.GROUP_USE_INHERIT.send(sender);
return CommandResult.INVALID_ARGS;
}
@@ -28,7 +28,6 @@ import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Patterns;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
@@ -55,7 +54,7 @@ public class GroupSetTempPermission extends SubCommand<Group> {
return CommandResult.INVALID_ARGS;
}
if (Patterns.GROUP_MATCH.matcher(node).matches()) {
if (node.toLowerCase().startsWith("group.")) {
Message.GROUP_USE_INHERIT.send(sender);
return CommandResult.INVALID_ARGS;
}
@@ -28,7 +28,6 @@ import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Patterns;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectLacksException;
@@ -52,7 +51,7 @@ public class GroupUnSetPermission extends SubCommand<Group> {
return CommandResult.INVALID_ARGS;
}
if (Patterns.GROUP_MATCH.matcher(node).matches()) {
if (node.toLowerCase().startsWith("group.")) {
Message.GROUP_USE_UNINHERIT.send(sender);
return CommandResult.INVALID_ARGS;
}
@@ -28,7 +28,6 @@ import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Patterns;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectLacksException;
@@ -53,7 +52,7 @@ public class GroupUnsetTempPermission extends SubCommand<Group> {
return CommandResult.INVALID_ARGS;
}
if (Patterns.GROUP_MATCH.matcher(node).matches()) {
if (node.toLowerCase().startsWith("group.")) {
Message.GROUP_USE_UNINHERIT.send(sender);
return CommandResult.INVALID_ARGS;
}
@@ -1,3 +1,25 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.commands.migration.subcommands;
import de.bananaco.bpermissions.api.*;
@@ -123,6 +123,7 @@ public class MigrationGroupManager extends SubCommand<Object> {
}
}
plugin.getDatastore().saveGroup(group);
}
Map<UUID, Map<Map.Entry<String, String>, Boolean>> users = new HashMap<>();
@@ -118,7 +118,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
try {
for (String node : group.getOwnPermissions(null)) {
boolean value = true;
if (node.startsWith("!")) {
if (node.startsWith("-")) {
node = node.substring(1);
value = false;
}
@@ -143,7 +143,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
for (String world : worlds) {
for (String node : group.getOwnPermissions(world)) {
boolean value = true;
if (node.startsWith("!")) {
if (node.startsWith("-")) {
node = node.substring(1);
value = false;
}
@@ -262,7 +262,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
try {
for (String node : user.getOwnPermissions(null)) {
boolean value = true;
if (node.startsWith("!")) {
if (node.startsWith("-")) {
node = node.substring(1);
value = false;
}
@@ -287,7 +287,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
for (String world : worlds) {
for (String node : user.getOwnPermissions(world)) {
boolean value = true;
if (node.startsWith("!")) {
if (node.startsWith("-")) {
node = node.substring(1);
value = false;
}
@@ -23,14 +23,17 @@
package me.lucko.luckperms.commands.migration.subcommands;
import com.github.cheesesoftware.PowerfulPermsAPI.*;
import com.google.common.util.concurrent.ListenableFuture;
import com.zaxxer.hikari.HikariDataSource;
import lombok.Cleanup;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.commands.migration.subcommands.utils.LPResultRunnable;
import me.lucko.luckperms.constants.Constants;
import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.data.LogEntry;
@@ -45,39 +48,72 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import static me.lucko.luckperms.constants.Permission.MIGRATION;
@SuppressWarnings("unchecked")
public class MigrationPowerfulPerms extends SubCommand<Object> {
/* <sadness>
The PowerfulPerms API is a complete joke. Seriously, it would probably be easier reflecting into the actual plugin.
Methods move about randomly every version...
What kind of API requires reflection to function with multiple versions...
Doesn't that just defeat the whole god damn point of having an API in the first place?
Whatever happened to the concept of depreciation and responsible API creation?
I tried to keep reflection to a minimum, but in some places there's no other option.
This class is a complete fucking mess for that reason. I sad now :(
</sadness> */
Whatever happened to depreciation?
</sadness> */
private static Method getPlayerPermissionsMethod = null;
private static Method getPlayerGroupsMethod = null;
private static Method getGroupMethod = null;
// lol
private static boolean superLegacy = false;
private static boolean legacy = false;
static {
try {
getPlayerGroupsMethod = PermissionManager.class.getMethod("getPlayerOwnGroups", UUID.class, ResultRunnable.class);
getPlayerGroupsMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
Class.forName("com.github.cheesesoftware.PowerfulPermsAPI.ResponseRunnable");
legacy = true;
} catch (ClassNotFoundException ignored) {}
if (legacy) {
try {
getPlayerGroupsMethod = PermissionManager.class.getMethod("getPlayerGroups", UUID.class, ResultRunnable.class);
getPlayerGroupsMethod.setAccessible(true);
} catch (NoSuchMethodException e1) {
e1.printStackTrace();
}
getPlayerPermissionsMethod = PermissionManager.class.getMethod("getPlayerOwnPermissions", UUID.class, ResultRunnable.class);
getPlayerPermissionsMethod.setAccessible(true);
} catch (NoSuchMethodException ignored) {}
} else {
try {
getPlayerPermissionsMethod = PermissionManager.class.getMethod("getPlayerOwnPermissions", UUID.class);
getPlayerPermissionsMethod.setAccessible(true);
} catch (NoSuchMethodException ignored) {}
}
try {
getGroupMethod = CachedGroup.class.getMethod("getGroup");
getGroupMethod.setAccessible(true);
legacy = true;
} catch (NoSuchMethodException ignored) {
superLegacy = true;
} catch (NoSuchMethodException ignored) {}
if (!legacy) {
try {
getPlayerGroupsMethod = PermissionManager.class.getMethod("getPlayerOwnGroups", UUID.class);
getPlayerGroupsMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
} else {
try {
getPlayerGroupsMethod = PermissionManager.class.getMethod("getPlayerOwnGroups", UUID.class, ResultRunnable.class);
getPlayerGroupsMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
try {
getPlayerGroupsMethod = PermissionManager.class.getMethod("getPlayerGroups", UUID.class, ResultRunnable.class);
getPlayerGroupsMethod.setAccessible(true);
} catch (NoSuchMethodException e1) {
e1.printStackTrace();
}
}
}
}
@@ -97,6 +133,42 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
}
}
private static void getPlayerPermissions(PermissionManager manager, UUID uuid, Callback<List<Permission>> callback) {
if (legacy) {
try {
getPlayerPermissionsMethod.invoke(manager, uuid, new LPResultRunnable<List<Permission>>() {
@Override
public void run() {
callback.onComplete(getResult());
}
});
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
} else {
try {
ListenableFuture<List<Permission>> lf = (ListenableFuture<List<Permission>>) getPlayerPermissionsMethod.invoke(manager, uuid);
try {
if (lf.isDone()) {
callback.onComplete(lf.get());
} else {
lf.addListener(() -> {
try {
callback.onComplete(lf.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}, Runnable::run);
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
private CommandResult run(LuckPermsPlugin plugin, List<String> args) {
final Logger log = plugin.getLog();
if (!plugin.isPluginLoaded("PowerfulPerms")) {
@@ -168,7 +240,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
// Groups first.
log.info("PowerfulPerms Migration: Starting group migration.");
Map<Integer, Group> groups = pm.getGroups();
Map<Integer, Group> groups = pm.getGroups(); // All versions
for (Group g : groups.values()) {
plugin.getDatastore().createAndLoadGroup(g.getName().toLowerCase());
final me.lucko.luckperms.groups.Group group = plugin.getGroupManager().get(g.getName().toLowerCase());
@@ -181,16 +253,16 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
ex.printStackTrace();
}
for (Permission p : g.getOwnPermissions()) {
for (Permission p : g.getOwnPermissions()) { // All versions
applyPerm(group, p, plugin);
}
for (Group parent : g.getParents()) {
for (Group parent : g.getParents()) { // All versions
try {
group.setPermission("group." + parent.getName().toLowerCase(), true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(group).action("setinherit " + parent.getName().toLowerCase())
.acted(group).action("setinherit " + parent.getName().toLowerCase()) // All versions
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
@@ -216,156 +288,176 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
User user = plugin.getUserManager().get(uuid);
// Get a list of Permissions held by the user from the PP API.
pm.getPlayerOwnPermissions(uuid, new LPResultRunnable<List<Permission>>() {
@Override
public void run() {
List<Permission> perms = this.getResult();
perms.forEach(p -> applyPerm(user, p, plugin));
getPlayerPermissions(pm, uuid, perms -> { // Changes each version
perms.forEach(p -> applyPerm(user, p, plugin));
// Update the progress so the user can be saved and unloaded.
synchronized (progress) {
progress.get(uuid).countDown();
if (progress.get(uuid).getCount() == 0) {
plugin.getDatastore().saveUser(user);
plugin.getUserManager().cleanup(user);
}
// Update the progress so the user can be saved and unloaded.
synchronized (progress) {
progress.get(uuid).countDown();
if (progress.get(uuid).getCount() == 0) {
plugin.getDatastore().saveUser(user);
plugin.getUserManager().cleanup(user);
}
}
});
// Migrate the user's groups to LuckPerms from PP.
try {
getPlayerGroupsMethod.invoke(pm, uuid, new LPResultRunnable<LinkedHashMap<String, List<CachedGroup>>>() {
@Override
public void run() {
Map<String, List<CachedGroup>> groups = getResult();
for (Map.Entry<String, List<CachedGroup>> e : groups.entrySet()) {
final String server;
if (e.getKey() != null && (e.getKey().equals("") || e.getKey().equalsIgnoreCase("all"))) {
server = null;
} else {
server = e.getKey();
}
// This is horrible. So many random API changes through versions, no depreciation.
if (legacy) {
e.getValue().stream()
.filter(cg -> !cg.isNegated())
.map(cg -> {
try {
return (Group) getGroupMethod.invoke(cg);
} catch (IllegalAccessException | InvocationTargetException e1) {
e1.printStackTrace();
return null;
}
})
.forEach(g -> {
if (g != null) {
if (server == null) {
try {
user.setPermission("group." + g.getName().toLowerCase(), true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addgroup " + g.getName().toLowerCase())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
} else {
try {
user.setPermission("group." + g.getName().toLowerCase(), true, server);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addgroup " + g.getName().toLowerCase() + " " + server)
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
}
});
} else {
e.getValue().stream()
.filter(g -> !g.hasExpired() && !g.isNegated())
.forEach(g -> {
final Group group = pm.getGroup(g.getGroupId());
if (g.willExpire()) {
if (server == null) {
try {
user.setPermission("group." + group.getName().toLowerCase(), true, g.getExpirationDate().getTime() / 1000L);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addtempgroup " + group.getName().toLowerCase() + " " + g.getExpirationDate().getTime() / 1000L)
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
} else {
try {
user.setPermission("group." + group.getName().toLowerCase(), true, server, g.getExpirationDate().getTime() / 1000L);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addtempgroup " + group.getName().toLowerCase() + " " + g.getExpirationDate().getTime() / 1000L + " " + server)
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
} else {
if (server == null) {
try {
user.setPermission("group." + group.getName().toLowerCase(), true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addgroup " + group.getName().toLowerCase())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
} else {
try {
user.setPermission("group." + group.getName().toLowerCase(), true, server);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addgroup " + group.getName().toLowerCase() + " " + server)
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
}
});
}
}
// Update the progress so the user can be saved and unloaded.
synchronized (progress) {
progress.get(uuid).countDown();
if (progress.get(uuid).getCount() == 0) {
plugin.getDatastore().saveUser(user);
plugin.getUserManager().cleanup(user);
}
}
Callback<Map<String, List<CachedGroup>>> callback = groups1 -> {
for (Map.Entry<String, List<CachedGroup>> e : groups1.entrySet()) {
final String server;
if (e.getKey() != null && (e.getKey().equals("") || e.getKey().equalsIgnoreCase("all"))) {
server = null;
} else {
server = e.getKey();
}
});
} catch (IllegalAccessException | InvocationTargetException e) {
log.info("PowerfulPerms Migration: Error");
e.printStackTrace();
if (superLegacy) {
e.getValue().stream()
.filter(cg -> !cg.isNegated())
.map(cg -> {
try {
return (Group) getGroupMethod.invoke(cg);
} catch (IllegalAccessException | InvocationTargetException e1) {
e1.printStackTrace();
return null;
}
})
.forEach(g -> {
if (g != null) {
if (server == null) {
try {
user.setPermission("group." + g.getName().toLowerCase(), true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addgroup " + g.getName().toLowerCase())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
} else {
try {
user.setPermission("group." + g.getName().toLowerCase(), true, server);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addgroup " + g.getName().toLowerCase() + " " + server)
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
}
});
} else {
e.getValue().stream()
.filter(g -> !g.hasExpired() && !g.isNegated())
.forEach(g -> {
final Group group = pm.getGroup(g.getGroupId());
if (g.willExpire()) {
if (server == null) {
try {
user.setPermission("group." + group.getName().toLowerCase(), true, g.getExpirationDate().getTime() / 1000L);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addtempgroup " + group.getName().toLowerCase() + " " + g.getExpirationDate().getTime() / 1000L)
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
} else {
try {
user.setPermission("group." + group.getName().toLowerCase(), true, server, g.getExpirationDate().getTime() / 1000L);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addtempgroup " + group.getName().toLowerCase() + " " + g.getExpirationDate().getTime() / 1000L + " " + server)
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
} else {
if (server == null) {
try {
user.setPermission("group." + group.getName().toLowerCase(), true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addgroup " + group.getName().toLowerCase())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
} else {
try {
user.setPermission("group." + group.getName().toLowerCase(), true, server);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addgroup " + group.getName().toLowerCase() + " " + server)
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
}
});
}
}
// Update the progress so the user can be saved and unloaded.
synchronized (progress) {
progress.get(uuid).countDown();
if (progress.get(uuid).getCount() == 0) {
plugin.getDatastore().saveUser(user);
plugin.getUserManager().cleanup(user);
}
}
};
if (!legacy) {
try {
ListenableFuture<LinkedHashMap<String, List<CachedGroup>>> future = (ListenableFuture<LinkedHashMap<String, List<CachedGroup>>>) getPlayerGroupsMethod.invoke(pm, uuid);
try {
if (future.isDone()) {
callback.onComplete(future.get());
} else {
future.addListener(() -> {
try {
callback.onComplete(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}, Runnable::run);
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
} catch (IllegalAccessException | InvocationTargetException e) {
log.info("PowerfulPerms Migration: Error");
e.printStackTrace();
}
} else {
try {
getPlayerGroupsMethod.invoke(pm, uuid, new LPResultRunnable<LinkedHashMap<String, List<CachedGroup>>>() {
@Override
public void run() {
callback.onComplete(getResult());
}
});
} catch (IllegalAccessException | InvocationTargetException e) {
log.info("PowerfulPerms Migration: Error");
e.printStackTrace();
}
}
}
@@ -417,7 +509,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
}
long expireAt = 0L;
if (!legacy) {
if (!superLegacy) {
if (p.willExpire()) {
expireAt = p.getExpirationDate().getTime() / 1000L;
}
@@ -508,22 +600,4 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
}
}
}
/**
* Overrides the default ResultRunnable, callbacks will always run in the same thread. (an async one, hopefully.)
* @param <T> type
*/
@SuppressWarnings("WeakerAccess")
public abstract class LPResultRunnable<T> extends ResultRunnable<T> {
public LPResultRunnable() {
super();
super.sameThread = true;
}
public T getResult() {
return super.result;
}
}
}
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.commands.migration.subcommands.utils;
import com.github.cheesesoftware.PowerfulPermsAPI.ResultRunnable;
/**
* Overrides the default ResultRunnable, callbacks will always run in the same thread. (an async one, hopefully.)
* @param <T> type
*/
public abstract class LPResultRunnable<T> extends ResultRunnable<T> {
public LPResultRunnable() {
super();
super.sameThread = true;
}
public T getResult() {
return super.result;
}
}
@@ -38,6 +38,7 @@ public class DebugCommand extends SingleMainCommand {
@Override
protected CommandResult execute(LuckPermsPlugin plugin, Sender sender, List<String> args, String label) {
plugin.getLog().info(sender.getName() + " used the debug command.");
Message.DEBUG.send(sender,
plugin.getPlayerCount(),
plugin.getUserManager().getAll().size(),
@@ -0,0 +1,209 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.commands.misc;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SingleMainCommand;
import me.lucko.luckperms.constants.Constants;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.storage.Datastore;
import me.lucko.luckperms.tracks.Track;
import me.lucko.luckperms.users.User;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public class ExportCommand extends SingleMainCommand {
public ExportCommand() {
super("Export", "/%s export <file>", 1, Permission.MIGRATION);
}
@Override
protected CommandResult execute(LuckPermsPlugin plugin, Sender sender, List<String> args, String label) {
final Logger log = plugin.getLog();
if (!sender.getUuid().equals(Constants.getConsoleUUID())) {
Message.MIGRATION_NOT_CONSOLE.send(sender);
return CommandResult.NO_PERMISSION;
}
File f = new File(plugin.getMainDir(), args.get(0));
if (f.exists()) {
Message.LOG_EXPORT_ALREADY_EXISTS.send(sender, f.getAbsolutePath());
return CommandResult.INVALID_ARGS;
}
try {
f.createNewFile();
} catch (IOException e) {
Message.LOG_EXPORT_FAILURE.send(sender);
e.printStackTrace();
return CommandResult.FAILURE;
}
if (!Files.isWritable(f.toPath())) {
Message.LOG_EXPORT_NOT_WRITABLE.send(sender, f.getAbsolutePath());
return CommandResult.FAILURE;
}
try (FileWriter fWriter = new FileWriter(f, true); BufferedWriter writer = new BufferedWriter(fWriter)) {
log.info("Export: Starting export process.");
// Export Groups
log.info("Export: Exporting all groups.");
// Create the groups first
for (Group group : plugin.getGroupManager().getAll().values()) {
write(writer, "/luckperms creategroup " + group.getName());
}
int groupCount = 0;
for (Group group : plugin.getGroupManager().getAll().values()) {
groupCount++;
for (Node node : group.getNodes()) {
write(writer, nodeToString(node, group.getName(), true));
}
}
log.info("Export: Exported " + groupCount + " groups.");
// Export tracks
log.info("Export: Exporting all tracks.");
// Create the tracks first
for (Track track : plugin.getTrackManager().getAll().values()) {
write(writer, "/luckperms createtrack " + track.getName());
}
int trackCount = 0;
for (Track track : plugin.getTrackManager().getAll().values()) {
trackCount++;
for (String group : track.getGroups()) {
write(writer, "/luckperms track " + track.getName() + " append " + group);
}
}
log.info("Export: Exported " + trackCount + " tracks.");
// Export users
log.info("Export: Exporting all users. Finding a list of unique users to export.");
Datastore ds = plugin.getDatastore();
Set<UUID> users = ds.getUniqueUsers();
log.info("Export: Found " + users.size() + " unique users to export.");
int userCount = 0;
for (UUID uuid : users) {
userCount++;
plugin.getDatastore().loadUser(uuid, "null");
User user = plugin.getUserManager().get(uuid);
for (Node node : user.getNodes()) {
if (node.isGroupNode() && node.getGroupName().equalsIgnoreCase("default")) {
continue;
}
write(writer, nodeToString(node, user.getUuid().toString(), false));
}
write(writer, "/luckperms user " + user.getUuid().toString() + " setprimarygroup " + user.getPrimaryGroup());
plugin.getUserManager().cleanup(user);
}
log.info("Export: Exported " + userCount + " users.");
try {
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
Message.LOG_EXPORT_SUCCESS.send(sender, f.getAbsolutePath());
return CommandResult.SUCCESS;
} catch (Throwable t) {
t.printStackTrace();
return CommandResult.FAILURE;
}
}
@Override
protected boolean isAuthorized(Sender sender) {
return sender.getUuid().equals(Constants.getConsoleUUID());
}
private static void write(BufferedWriter writer, String s) {
try {
writer.write(s);
writer.newLine();
} catch (IOException e) {
e.printStackTrace();
}
}
private static String nodeToString(Node node, String id, boolean group) {
StringBuilder sb = new StringBuilder();
sb.append("/luckperms ").append(group ? "group " : "user ").append(id).append(" ");
if (node.isGroupNode()) {
if (node.isTemporary()) {
sb.append(group ? "settempinherit " : "addtempgroup ");
sb.append(node.getGroupName());
sb.append(" ").append(node.getExpiryUnixTime());
} else {
sb.append(group ? "setinherit " : "addgroup ");
sb.append(node.getGroupName());
}
if (node.isWorldSpecific()) {
sb.append(" ").append(node.getServer().get()).append(" ").append(node.getWorld().get());
} else if (node.isServerSpecific()) {
sb.append(" ").append(node.getServer().get());
}
return sb.toString();
}
sb.append(node.isTemporary() ? "settemp " : "set ");
sb.append(node.getPermission()).append(" ").append(node.getValue());
if (node.isTemporary()) {
sb.append(" ").append(node.getExpiryUnixTime());
}
if (node.isWorldSpecific()) {
sb.append(" ").append(node.getServer().get()).append(" ").append(node.getWorld().get());
} else if (node.isServerSpecific()) {
sb.append(" ").append(node.getServer().get());
}
return sb.toString();
}
}
@@ -44,10 +44,14 @@ public class InfoCommand extends SingleMainCommand {
final LPConfiguration c = plugin.getConfiguration();
Message.INFO.send(sender,
plugin.getVersion(),
plugin.getType().getFriendlyName(),
plugin.getDatastore().getName(),
c.getServer(),
c.getSyncTime(),
formatBoolean(c.getIncludeGlobalPerms()),
formatBoolean(c.getIncludeGlobalWorldPerms()),
formatBoolean(c.getApplyGlobalGroups()),
formatBoolean(c.getApplyGlobalWorldGroups()),
formatBoolean(c.getOnlineMode()),
formatBoolean(c.getApplyWildcards()),
formatBoolean(c.getApplyRegex()),
@@ -28,6 +28,7 @@ import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.Contexts;
import java.util.AbstractMap.SimpleEntry;
import java.util.List;
@@ -46,7 +47,7 @@ public class UserChatMeta extends SubCommand<User> {
SortedSet<Map.Entry<Integer, String>> prefixes = new TreeSet<>(Util.getMetaComparator().reversed());
SortedSet<Map.Entry<Integer, String>> suffixes = new TreeSet<>(Util.getMetaComparator().reversed());
for (Node node : user.getAllNodes(null)) {
for (Node node : user.getAllNodes(null, Contexts.allowAll())) {
if (!node.isSuffix() && !node.isPrefix()) {
continue;
}
@@ -28,7 +28,6 @@ import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Patterns;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
@@ -54,7 +53,7 @@ public class UserSetPermission extends SubCommand<User> {
return CommandResult.INVALID_ARGS;
}
if (Patterns.GROUP_MATCH.matcher(node).matches()) {
if (node.toLowerCase().startsWith("group.")) {
Message.USER_USE_ADDGROUP.send(sender);
return CommandResult.INVALID_ARGS;
}
@@ -28,7 +28,6 @@ import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Patterns;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
@@ -55,7 +54,7 @@ public class UserSetTempPermission extends SubCommand<User> {
return CommandResult.INVALID_ARGS;
}
if (Patterns.GROUP_MATCH.matcher(node).matches()) {
if (node.toLowerCase().startsWith("group.")) {
Message.USER_USE_ADDGROUP.send(sender);
return CommandResult.INVALID_ARGS;
}
@@ -28,7 +28,6 @@ import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Patterns;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectLacksException;
@@ -52,7 +51,7 @@ public class UserUnSetPermission extends SubCommand<User> {
return CommandResult.INVALID_ARGS;
}
if (Patterns.GROUP_MATCH.matcher(node).matches()) {
if (node.toLowerCase().startsWith("group.")) {
Message.USER_USE_REMOVEGROUP.send(sender);
return CommandResult.INVALID_ARGS;
}
@@ -28,7 +28,6 @@ import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Patterns;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectLacksException;
@@ -53,7 +52,7 @@ public class UserUnsetTempPermission extends SubCommand<User> {
return CommandResult.INVALID_ARGS;
}
if (Patterns.GROUP_MATCH.matcher(node).matches()) {
if (node.toLowerCase().startsWith("group.")) {
Message.USER_USE_REMOVEGROUP.send(sender);
return CommandResult.INVALID_ARGS;
}
@@ -123,10 +123,14 @@ public enum Message {
UPDATE_TASK_RUN("&bRunning update task for all online users.", true),
INFO(
PREFIX + "&6Running &bLuckPerms v%s&6 by &bLuck&6." + "\n" +
PREFIX + "&f-> &ePlatform: &6%s" + "\n" +
PREFIX + "&f-> &eStorage Method: &6%s" + "\n" +
PREFIX + "&f-> &eServer Name: &6%s" + "\n" +
PREFIX + "&f-> &eSync Interval: &6%s minutes" + "\n" +
PREFIX + "&f-> &eInclude Global Perms: &6%s" + "\n" +
PREFIX + "&f-> &eInclude Global: &6%s" + "\n" +
PREFIX + "&f-> &eInclude Global World: &6%s" + "\n" +
PREFIX + "&f-> &eApply Global Groups: &6%s" + "\n" +
PREFIX + "&f-> &eApply Global World Groups: &6%s" + "\n" +
PREFIX + "&f-> &eOnline Mode: &6%s" + "\n" +
PREFIX + "&f-> &eApply Wildcards: &6%s" + "\n" +
PREFIX + "&f-> &eApply Regex: &6%s" + "\n" +
@@ -332,6 +336,7 @@ public enum Message {
IMPORT_END_ERROR_HEADER(
PREFIX + "&e(Import) &7------------> &6Showing Error #&e%s &7<------------" + "\n" +
PREFIX + "&e(Import) &6Whilst executing: &fCommand #%s" + "\n" +
PREFIX + "&e(Import) &6Command: &7%s" + "\n" +
PREFIX + "&e(Import) &6Type: &f%s" + "\n" +
PREFIX + "&e(Import) &6Output:",
false
@@ -33,32 +33,25 @@ import java.util.regex.PatternSyntaxException;
public class Patterns {
private static final Map<String, Pattern> CACHE = new ConcurrentHashMap<>();
public static final Pattern SPACE = Pattern.compile(" ");
public static final Pattern SERVER_DELIMITER = Pattern.compile("\\/");
public static final Pattern WORLD_DELIMITER = Pattern.compile("\\-");
public static final Pattern TEMP_DELIMITER = Pattern.compile("\\$");
public static final Pattern DOT = Pattern.compile("\\.");
public static final Pattern VERTICAL_BAR = Pattern.compile("\\|");
public static final Pattern GROUP_MATCH = Pattern.compile("group\\..*");
public static final Pattern NON_ALPHA_NUMERIC = Pattern.compile("[\\/\\$\\.\\-]");
public static final Pattern NON_USERNAME = Pattern.compile("[^A-Za-z0-9_]");
public static final Pattern COMMAND_SEPARATOR = Pattern.compile(" (?=([^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*$)");
public static final Pattern NON_ALPHA_NUMERIC = Pattern.compile("[\\/\\$\\.\\- ]");
public static final Pattern NON_USERNAME = Pattern.compile("[^A-Za-z0-9_ ]");
public static final Pattern SHORTHAND_NODE = Pattern.compile("\\.\\([^.]+\\)");
public static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("(?i)" + String.valueOf('§') + "[0-9A-FK-OR]");
public static final Pattern NODE_CONTEXTS = Pattern.compile("\\(.+\\).*");
public static Pattern compile(String regex) throws PatternSyntaxException {
if (!CACHE.containsKey(regex)) {
Pattern p;
try {
p = Pattern.compile(regex);
} catch (PatternSyntaxException e) {
return null;
}
CACHE.put(regex, p);
public static Pattern compile(String regex) {
try {
return CACHE.computeIfAbsent(regex, s -> {
try {
return Pattern.compile(regex);
} catch (PatternSyntaxException e) {
return null;
}
});
} catch (NullPointerException e) {
return null;
}
return CACHE.get(regex);
}
}
@@ -91,6 +91,18 @@ public abstract class LPConfiguration<T extends LuckPermsPlugin> {
return getBoolean("include-global", defaultIncludeGlobal);
}
public boolean getIncludeGlobalWorldPerms() {
return getBoolean("include-global-world", true);
}
public boolean getApplyGlobalGroups() {
return getBoolean("apply-global-groups", true);
}
public boolean getApplyGlobalWorldGroups() {
return getBoolean("apply-global-world-groups", true);
}
public boolean getOnlineMode() {
return getBoolean("online-mode", true);
}
@@ -111,14 +123,22 @@ public abstract class LPConfiguration<T extends LuckPermsPlugin> {
return getBoolean("log-notify", true);
}
public boolean getDebugPermissionChecks() {
return getBoolean("debug-permission-checks", false);
}
public boolean getEnableOps() {
return getBoolean("enable-ops", true);
return !getAutoOp() && getBoolean("enable-ops", true);
}
public boolean getCommandsAllowOp() {
return getBoolean("commands-allow-op", true);
}
public boolean getAutoOp() {
return getBoolean("auto-op", false);
}
public String getVaultServer() {
return getString("vault-server", "global");
}
@@ -36,6 +36,7 @@ import me.lucko.luckperms.api.implementation.internal.PermissionHolderLink;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.Contexts;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@@ -143,7 +144,7 @@ public abstract class PermissionHolder {
* @param excludedGroups a list of groups to exclude
* @return a set of nodes
*/
public SortedSet<Node> getAllNodes(List<String> excludedGroups) {
public SortedSet<Node> getAllNodes(List<String> excludedGroups, Contexts context) {
SortedSet<Node> all = getPermissions(true);
if (excludedGroups == null) {
@@ -152,13 +153,27 @@ public abstract class PermissionHolder {
excludedGroups.add(getObjectName().toLowerCase());
Set<String> parents = getPermissions(true).stream()
Set<Node> parents = getPermissions(true).stream()
.filter(Node::isGroupNode)
.map(Node::getGroupName)
.collect(Collectors.toSet());
for (String parent : parents) {
Group group = plugin.getGroupManager().get(parent);
Iterator<Node> iterator = parents.iterator();
while (iterator.hasNext()) {
Node node = iterator.next();
if (!node.shouldApplyOnServer(context.getServer(), context.isApplyGlobalGroups(), plugin.getConfiguration().getApplyRegex())) {
iterator.remove();
continue;
}
if (!node.shouldApplyOnWorld(context.getWorld(), context.isApplyGlobalWorldGroups(), plugin.getConfiguration().getApplyRegex())) {
iterator.remove();
continue;
}
}
for (Node parent : parents) {
Group group = plugin.getGroupManager().get(parent.getGroupName());
if (group == null) {
continue;
}
@@ -168,7 +183,7 @@ public abstract class PermissionHolder {
}
inherited:
for (Node inherited : group.getAllNodes(excludedGroups)) {
for (Node inherited : group.getAllNodes(excludedGroups, context)) {
for (Node existing : all) {
if (existing.almostEquals(inherited)) {
continue inherited;
@@ -184,34 +199,30 @@ public abstract class PermissionHolder {
/**
* Gets all of the nodes that this holder has (and inherits), given the context
* @param server the server
* @param world the world
* @param extraContext any extra context to filter by
* @param includeGlobal whether to include global nodes
* @param applyGroups if inherited group permissions should be included
* @param context the context for this request
* @return a map of permissions
*/
public Set<Node> getAllNodesFiltered(String server, String world, Map<String, String> extraContext, boolean includeGlobal, boolean applyGroups) {
public Set<Node> getAllNodesFiltered(Contexts context) {
Set<Node> perms = ConcurrentHashMap.newKeySet();
SortedSet<Node> allNodes;
if (applyGroups) {
allNodes = getAllNodes(null);
if (context.isApplyGroups()) {
allNodes = getAllNodes(null, context);
} else {
allNodes = getPermissions(true);
}
all:
for (Node node : allNodes) {
if (!node.shouldApplyOnServer(server, includeGlobal, plugin.getConfiguration().getApplyRegex())) {
if (!node.shouldApplyOnServer(context.getServer(), context.isIncludeGlobal(), plugin.getConfiguration().getApplyRegex())) {
continue;
}
if (!node.shouldApplyOnWorld(world, includeGlobal, plugin.getConfiguration().getApplyRegex())) {
if (!node.shouldApplyOnWorld(context.getWorld(), context.isIncludeGlobalWorld(), plugin.getConfiguration().getApplyRegex())) {
continue;
}
if (!node.shouldApplyWithContext(extraContext)) {
if (!node.shouldApplyWithContext(context.getExtraContext())) {
continue;
}
@@ -229,23 +240,20 @@ public abstract class PermissionHolder {
}
/**
* Converts the output of {@link #getAllNodesFiltered(String, String, Map, boolean, boolean)}, and expands wildcards/regex/shorthand perms
* @param server the server
* @param world the world
* @param extraContext any extra context to filter by
* @param includeGlobal whether to include global nodes
* @param applyGroups if inherited group permissions should be included
* Converts the output of {@link #getAllNodesFiltered(Contexts)}, and expands wildcards/regex/shorthand perms
* @param context the context for this request
* @param possibleNodes a list of possible nodes for wildcards and regex permissions
* @return a map of permissions
*/
public Map<String, Boolean> exportNodes(String server, String world, Map<String, String> extraContext, boolean includeGlobal, boolean applyGroups, List<String> possibleNodes) {
public Map<String, Boolean> exportNodes(Contexts context, List<String> possibleNodes) {
Map<String, Boolean> perms = new HashMap<>();
for (Node node : getAllNodesFiltered(server, world, extraContext, includeGlobal, applyGroups)) {
if (node.getPermission().equals("*") || node.getPermission().equals("'*'")) {
if (possibleNodes != null && plugin.getConfiguration().getApplyWildcards()) {
possibleNodes.forEach(n -> perms.put(n, true));
continue;
for (Node node : getAllNodesFiltered(context)) {
if (possibleNodes != null && !possibleNodes.isEmpty()) {
if (node.getPermission().equals("*") || node.getPermission().equals("'*'")) {
if (plugin.getConfiguration().getApplyWildcards()) {
possibleNodes.forEach(n -> perms.put(n, true));
}
}
}
@@ -257,10 +265,12 @@ public abstract class PermissionHolder {
.forEach(s -> perms.put(s, node.getValue()));
}
if (plugin.getConfiguration().getApplyWildcards()) {
node.resolveWildcard(possibleNodes).stream()
.filter(s -> !perms.containsKey(s))
.forEach(s -> perms.put(s, node.getValue()));
if (possibleNodes != null && !possibleNodes.isEmpty()) {
if (plugin.getConfiguration().getApplyWildcards()) {
node.resolveWildcard(possibleNodes).stream()
.filter(s -> !perms.containsKey(s))
.forEach(s -> perms.put(s, node.getValue()));
}
}
}
@@ -357,7 +367,7 @@ public abstract class PermissionHolder {
* @return a tristate
*/
public Tristate inheritsPermission(Node node) {
return hasPermission(getAllNodes(null), node);
return hasPermission(getAllNodes(null, Contexts.allowAll()), node);
}
public boolean inheritsPermission(String node, boolean b) {
@@ -390,7 +400,7 @@ public abstract class PermissionHolder {
* @throws ObjectAlreadyHasException if the holder has this permission already
*/
public void setPermission(Node node) throws ObjectAlreadyHasException {
if (hasPermission(node, false) == Tristate.TRUE) {
if (hasPermission(node, false) != Tristate.UNDEFINED) {
throw new ObjectAlreadyHasException();
}
@@ -404,7 +414,7 @@ public abstract class PermissionHolder {
* @throws ObjectAlreadyHasException if the holder has this permission already
*/
public void setTransientPermission(Node node) throws ObjectAlreadyHasException {
if (hasPermission(node, true) == Tristate.TRUE) {
if (hasPermission(node, true) != Tristate.UNDEFINED) {
throw new ObjectAlreadyHasException();
}
@@ -442,7 +452,7 @@ public abstract class PermissionHolder {
* @throws ObjectLacksException if the holder doesn't have this node already
*/
public void unsetPermission(Node node) throws ObjectLacksException {
if (hasPermission(node, false) != Tristate.TRUE) {
if (hasPermission(node, false) == Tristate.UNDEFINED) {
throw new ObjectLacksException();
}
@@ -468,7 +478,7 @@ public abstract class PermissionHolder {
* @throws ObjectLacksException if the holder doesn't have this node already
*/
public void unsetTransientPermission(Node node) throws ObjectLacksException {
if (hasPermission(node, true) == Tristate.FALSE) {
if (hasPermission(node, true) == Tristate.UNDEFINED) {
throw new ObjectLacksException();
}
@@ -540,18 +550,24 @@ public abstract class PermissionHolder {
/*
* Don't use these methods, only here for compat reasons
*/
@Deprecated
public Map<String, Boolean> getLocalPermissions(String server, String world, List<String> excludedGroups, List<String> possibleNodes) {
return exportNodes(server, world, null, plugin.getConfiguration().getIncludeGlobalPerms(), true, possibleNodes);
return exportNodes(new Contexts(server, world, Collections.emptyMap(), plugin.getConfiguration().getIncludeGlobalPerms(), true, true, true, true), Collections.emptyList());
}
@Deprecated
public Map<String, Boolean> getLocalPermissions(String server, String world, List<String> excludedGroups) {
return exportNodes(server, world, null, plugin.getConfiguration().getIncludeGlobalPerms(), true, null);
return exportNodes(new Contexts(server, world, Collections.emptyMap(), plugin.getConfiguration().getIncludeGlobalPerms(), true, true, true, true), Collections.emptyList());
}
@SuppressWarnings("deprecation")
@Deprecated
public Map<String, Boolean> getLocalPermissions(String server, List<String> excludedGroups, List<String> possibleNodes) {
return getLocalPermissions(server, null, excludedGroups, possibleNodes);
}
@SuppressWarnings("deprecation")
@Deprecated
public Map<String, Boolean> getLocalPermissions(String server, List<String> excludedGroups) {
return getLocalPermissions(server, null, excludedGroups, null);
}
@@ -30,14 +30,14 @@ import java.util.Comparator;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class PriorityComparator implements Comparator<Node> {
private static final PriorityComparator instance = new PriorityComparator();
private static final PriorityComparator INSTANCE = new PriorityComparator();
public static Comparator<Node> get() {
return instance;
return INSTANCE;
}
public static Comparator<Node> reverse() {
return instance.reversed();
return INSTANCE.reversed();
}
@Override
@@ -74,6 +74,6 @@ public class PriorityComparator implements Comparator<Node> {
return o1.getWildcardLevel() > o2.getWildcardLevel() ? 1 : -1;
}
return 1;
return o1.getPermission().compareToIgnoreCase(o2.getPermission()) == 1 ? -1 : 1;
}
}
@@ -22,12 +22,12 @@
package me.lucko.luckperms.core;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Maps;
import lombok.Getter;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* @see me.lucko.luckperms.api.UuidCache for docs
@@ -35,7 +35,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class UuidCache {
// External UUID --> Internal UUID
private Map<UUID, UUID> cache;
private BiMap<UUID, UUID> cache;
@Getter
private final boolean onlineMode;
@@ -44,23 +44,16 @@ public class UuidCache {
this.onlineMode = onlineMode;
if (!onlineMode) {
cache = new ConcurrentHashMap<>();
cache = Maps.synchronizedBiMap(HashBiMap.create());
}
}
public UUID getUUID(UUID external) {
return onlineMode ? external : (cache.containsKey(external) ? cache.get(external) : external);
return onlineMode ? external : cache.getOrDefault(external, external);
}
public UUID getExternalUUID(UUID internal) {
if (onlineMode) return internal;
Optional<UUID> external = cache.entrySet().stream()
.filter(e -> e.getValue().equals(internal))
.map(Map.Entry::getKey)
.findFirst();
return external.isPresent() ? external.get() : internal;
return onlineMode ? internal : cache.inverse().getOrDefault(internal, internal);
}
public void addToCache(UUID external, UUID internal) {
@@ -22,6 +22,7 @@
package me.lucko.luckperms.data;
import com.google.common.base.Splitter;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
@@ -31,7 +32,6 @@ import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.Util;
import me.lucko.luckperms.constants.Constants;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Patterns;
import me.lucko.luckperms.constants.Permission;
import java.util.*;
@@ -88,7 +88,7 @@ public class Importer {
Message.IMPORT_START.send(executor);
final Sender fake = new FakeSender(this);
int index = 0;
int index = 1;
for (String command : commands) {
if (lastMsg < (System.currentTimeMillis() / 1000) - 5) {
lastMsg = System.currentTimeMillis() / 1000;
@@ -98,11 +98,11 @@ public class Importer {
executing = index;
try {
CommandResult result = commandManager.onCommand(fake, "perms", Arrays.asList(Patterns.SPACE.split(command)));
getResult(index).setResult(result);
CommandResult result = commandManager.onCommand(fake, "perms", Splitter.on(' ').splitToList(command));
getResult(index, command).setResult(result);
} catch (Exception e) {
getResult(index).setResult(CommandResult.FAILURE);
getResult(index, command).setResult(CommandResult.FAILURE);
e.printStackTrace();
}
index++;
@@ -129,7 +129,7 @@ public class Importer {
int errIndex = 1;
for (Map.Entry<Integer, Result> e : cmdResult.entrySet()) {
if (e.getValue().getResult() != null && !e.getValue().getResult().booleanValue()) {
Message.IMPORT_END_ERROR_HEADER.send(executor, errIndex, e.getKey(), e.getValue().getResult().toString());
Message.IMPORT_END_ERROR_HEADER.send(executor, errIndex, e.getKey(), e.getValue().getCommand(), e.getValue().getResult().toString());
for (String s : e.getValue().getOutput()) {
Message.IMPORT_END_ERROR_CONTENT.send(executor, s);
}
@@ -158,22 +158,28 @@ public class Importer {
}
}
private Result getResult(int executing) {
private Result getResult(int executing, String command) {
if (!cmdResult.containsKey(executing)) {
cmdResult.put(executing, new Result());
cmdResult.put(executing, new Result(command));
}
return cmdResult.get(executing);
Result existing = cmdResult.get(executing);
if (!command.equals("") && existing.getCommand().equals("")) {
existing.setCommand(command);
}
return existing;
}
private void logMessage(String msg) {
getResult(executing).getOutput().add(Util.stripColor(msg));
getResult(executing, "").getOutput().add(Util.stripColor(msg));
}
private static class FakeSender implements Sender {
private final Importer instance;
public FakeSender(Importer instance) {
private FakeSender(Importer instance) {
this.instance = instance;
}
@@ -199,6 +205,9 @@ public class Importer {
}
private static class Result {
@Getter
@Setter
private String command;
@Getter
private final List<String> output = new ArrayList<>();
@@ -206,6 +215,10 @@ public class Importer {
@Getter
@Setter
private CommandResult result = CommandResult.FAILURE;
private Result(String command) {
this.command = command;
}
}
}
@@ -26,14 +26,12 @@ import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.event.events.LogNotifyEvent;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.tracks.Track;
import me.lucko.luckperms.users.User;
import java.util.List;
import java.util.stream.Collectors;
public class LogEntry extends me.lucko.luckperms.api.LogEntry {
public static LogEntryBuilder build() {
@@ -58,9 +56,7 @@ public class LogEntry extends me.lucko.luckperms.api.LogEntry {
final String msg = super.getFormatted();
List<Sender> senders = plugin.getSenders().stream()
.filter(Permission.LOG_NOTIFY::isAuthorized)
.collect(Collectors.toList());
List<Sender> senders = plugin.getNotifyListeners();
senders.add(plugin.getConsoleSender());
if (sender == null) {
@@ -1,3 +1,25 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.storage;
import com.google.common.collect.ImmutableMap;
@@ -124,7 +124,7 @@ abstract class FlatfileDatastore extends Datastore {
@Override
public Log getLog() {
// TODO Add log viewing support for flatfile D:
// Flatfile doesn't support viewing log data from in-game. You can just read the file in a text editor.
return Log.builder().build();
}
@@ -38,6 +38,7 @@ public class H2Datastore extends SQLDatastore {
private final File file;
private Connection connection = null;
private final Object connectionLock = new Object();
public H2Datastore(LuckPermsPlugin plugin, File file) {
super(plugin, "H2");
@@ -107,12 +108,14 @@ public class H2Datastore extends SQLDatastore {
@Override
Connection getConnection() throws SQLException {
if (connection == null || connection.isClosed()) {
try {
Class.forName("org.h2.Driver");
} catch (ClassNotFoundException ignored) {}
synchronized (connectionLock) {
if (connection == null || connection.isClosed()) {
try {
Class.forName("org.h2.Driver");
} catch (ClassNotFoundException ignored) {}
connection = DriverManager.getConnection("jdbc:h2:" + file.getAbsolutePath());
connection = DriverManager.getConnection("jdbc:h2:" + file.getAbsolutePath());
}
}
return connection;
@@ -22,6 +22,7 @@
package me.lucko.luckperms.storage.methods;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import lombok.Cleanup;
import me.lucko.luckperms.LuckPermsPlugin;
@@ -50,20 +51,32 @@ public class MySQLDatastore extends SQLDatastore {
@Override
public void init() {
hikari = new HikariDataSource();
HikariConfig config = new HikariConfig();
final String address = configuration.getAddress();
final String database = configuration.getDatabase();
final String username = configuration.getUsername();
final String password = configuration.getPassword();
hikari.setMaximumPoolSize(10);
hikari.setDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlDataSource");
hikari.addDataSourceProperty("serverName", address.split(":")[0]);
hikari.addDataSourceProperty("port", address.split(":")[1]);
hikari.addDataSourceProperty("databaseName", database);
hikari.addDataSourceProperty("user", username);
hikari.addDataSourceProperty("password", password);
config.setMaximumPoolSize(10);
config.setPoolName("luckperms");
config.setDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlDataSource");
config.addDataSourceProperty("serverName", address.split(":")[0]);
config.addDataSourceProperty("port", address.split(":")[1]);
config.addDataSourceProperty("databaseName", database);
config.addDataSourceProperty("user", username);
config.addDataSourceProperty("password", password);
config.addDataSourceProperty("cachePrepStmts", true);
config.addDataSourceProperty("prepStmtCacheSize", 250);
config.addDataSourceProperty("prepStmtCacheSqlLimit", 2048);
config.addDataSourceProperty("useServerPrepStmts", true);
config.addDataSourceProperty("cacheCallableStmts", true);
config.addDataSourceProperty("alwaysSendSetIsolation", false);
config.addDataSourceProperty("cacheServerConfiguration", true);
config.addDataSourceProperty("elideSetAutoCommits", true);
config.addDataSourceProperty("useLocalSessionState", true);
hikari = new HikariDataSource(config);
if (!setupTables(CREATETABLE_UUID, CREATETABLE_USERS, CREATETABLE_GROUPS, CREATETABLE_TRACKS, CREATETABLE_ACTION)) {
plugin.getLog().severe("Error occurred whilst initialising the database.");
@@ -265,7 +265,7 @@ abstract class SQLDatastore extends Datastore {
String uuid = resultSet.getString("uuid");
uuids.add(UUID.fromString(uuid));
}
return false;
return true;
}
});
@@ -38,6 +38,7 @@ public class SQLiteDatastore extends SQLDatastore {
private final File file;
private Connection connection = null;
private final Object connectionLock = new Object();
public SQLiteDatastore(LuckPermsPlugin plugin, File file) {
super(plugin, "SQLite");
@@ -107,12 +108,14 @@ public class SQLiteDatastore extends SQLDatastore {
@Override
Connection getConnection() throws SQLException {
if (connection == null || connection.isClosed()) {
try {
Class.forName("org.sqlite.JDBC");
} catch (ClassNotFoundException ignored) {}
synchronized (connectionLock) {
if (connection == null || connection.isClosed()) {
try {
Class.forName("org.sqlite.JDBC");
} catch (ClassNotFoundException ignored) {}
connection = DriverManager.getConnection("jdbc:sqlite:" + file.getAbsolutePath());
connection = DriverManager.getConnection("jdbc:sqlite:" + file.getAbsolutePath());
}
}
return connection;
@@ -69,11 +69,11 @@ public class AbstractListener {
protected void onLeave(UUID uuid) {
final UuidCache cache = plugin.getUuidCache();
// Unload the user from memory when they disconnect;
cache.clearCache(uuid);
final User user = plugin.getUserManager().get(cache.getUUID(uuid));
plugin.getUserManager().unload(user);
// Unload the user from memory when they disconnect;
cache.clearCache(uuid);
}
protected void refreshPlayer(UUID uuid) {
@@ -0,0 +1,61 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.utils;
import lombok.AllArgsConstructor;
import lombok.Getter;
import me.lucko.luckperms.core.LPConfiguration;
import java.util.Collections;
import java.util.Map;
@Getter
@AllArgsConstructor
public class Contexts {
public static Contexts fromConfig(LPConfiguration configuration) {
return new Contexts(
configuration.getServer(),
null,
Collections.emptyMap(),
configuration.getIncludeGlobalPerms(),
configuration.getIncludeGlobalWorldPerms(),
true,
configuration.getApplyGlobalGroups(),
configuration.getApplyGlobalWorldGroups()
);
}
public static Contexts allowAll() {
return new Contexts(null, null, Collections.emptyMap(), true, true, true, true, true);
}
private final String server;
private final String world;
private final Map<String, String> extraContext;
private final boolean includeGlobal;
private final boolean includeGlobalWorld;
private final boolean applyGroups;
private final boolean applyGlobalGroups;
private final boolean applyGlobalWorldGroups;
}
@@ -22,6 +22,7 @@
package me.lucko.luckperms.utils;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
import lombok.*;
import me.lucko.luckperms.api.Tristate;
@@ -41,6 +42,7 @@ import java.util.stream.IntStream;
public class Node implements me.lucko.luckperms.api.Node {
private static final Pattern PREFIX_PATTERN = Pattern.compile("(?i)prefix\\.-?\\d+\\..*");
private static final Pattern SUFFIX_PATTERN = Pattern.compile("(?i)suffix\\.-?\\d+\\..*");
private static final Pattern META_PATTERN = Pattern.compile("meta\\..*\\..*");
public static me.lucko.luckperms.api.Node fromSerialisedNode(String s, Boolean b) {
return builderFromSerialisedNode(s, b).build();
@@ -48,36 +50,36 @@ public class Node implements me.lucko.luckperms.api.Node {
public static me.lucko.luckperms.api.Node.Builder builderFromSerialisedNode(String s, Boolean b) {
if (s.contains("/")) {
String[] parts = Patterns.SERVER_DELIMITER.split(s, 2);
List<String> parts = Splitter.on('/').limit(2).splitToList(s);
// 0=server(+world) 1=node
// WORLD SPECIFIC
if (parts[0].contains("-")) {
String[] serverParts = Patterns.WORLD_DELIMITER.split(parts[0], 2);
if (parts.get(0).contains("-")) {
List<String> serverParts = Splitter.on('-').limit(2).splitToList(parts.get(0));
// 0=server 1=world
if (parts[1].contains("$")) {
String[] tempParts = Patterns.TEMP_DELIMITER.split(parts[1], 2);
return new Node.Builder(tempParts[0], true).setServerRaw(serverParts[0]).setWorld(serverParts[1])
.setExpiry(Long.parseLong(tempParts[1])).setValue(b);
if (parts.get(1).contains("$")) {
List<String> tempParts = Splitter.on('$').limit(2).splitToList(parts.get(1));
return new Node.Builder(tempParts.get(0), true).setServerRaw(serverParts.get(0)).setWorld(serverParts.get(1))
.setExpiry(Long.parseLong(tempParts.get(1))).setValue(b);
} else {
return new Node.Builder(parts[1], true).setServerRaw(serverParts[0]).setWorld(serverParts[1]).setValue(b);
return new Node.Builder(parts.get(1), true).setServerRaw(serverParts.get(0)).setWorld(serverParts.get(1)).setValue(b);
}
} else {
// SERVER BUT NOT WORLD SPECIFIC
if (parts[1].contains("$")) {
String[] tempParts = Patterns.TEMP_DELIMITER.split(parts[1], 2);
return new Node.Builder(tempParts[0], true).setServerRaw(parts[0]).setExpiry(Long.parseLong(tempParts[1])).setValue(b);
if (parts.get(1).contains("$")) {
List<String> tempParts = Splitter.on('$').limit(2).splitToList(parts.get(1));
return new Node.Builder(tempParts.get(0), true).setServerRaw(parts.get(0)).setExpiry(Long.parseLong(tempParts.get(1))).setValue(b);
} else {
return new Node.Builder(parts[1], true).setServerRaw(parts[0]).setValue(b);
return new Node.Builder(parts.get(1), true).setServerRaw(parts.get(0)).setValue(b);
}
}
} else {
// NOT SERVER SPECIFIC
if (s.contains("$")) {
String[] tempParts = Patterns.TEMP_DELIMITER.split(s, 2);
return new Node.Builder(tempParts[0], true).setExpiry(Long.parseLong(tempParts[1])).setValue(b);
List<String> tempParts = Splitter.on('$').limit(2).splitToList(s);
return new Node.Builder(tempParts.get(0), true).setExpiry(Long.parseLong(tempParts.get(1))).setValue(b);
} else {
return new Node.Builder(s, true).setValue(b);
}
@@ -100,6 +102,11 @@ public class Node implements me.lucko.luckperms.api.Node {
private final Map<String, String> extraContexts = new HashMap<>();
// Cache the state
private Tristate isPrefix = Tristate.UNDEFINED;
private Tristate isSuffix = Tristate.UNDEFINED;
private Tristate isMeta = Tristate.UNDEFINED;
/**
* Make an immutable node instance
* @param permission the actual permission node
@@ -148,6 +155,9 @@ public class Node implements me.lucko.luckperms.api.Node {
}
public Optional<String> getServer() {
if (isWorldSpecific() && server == null) {
return Optional.of("global");
}
return Optional.ofNullable(server);
}
@@ -200,7 +210,7 @@ public class Node implements me.lucko.luckperms.api.Node {
if (world.startsWith("(") && world.endsWith(")") && world.contains("|")) {
final String bits = world.substring(1, world.length() - 1);
String[] parts = Patterns.VERTICAL_BAR.split(bits);
Iterable<String> parts = Splitter.on('|').split(bits);
for (String s : parts) {
if (s.equalsIgnoreCase(thisWorld)) {
@@ -310,7 +320,7 @@ public class Node implements me.lucko.luckperms.api.Node {
return Collections.emptyList();
}
String[] parts = Patterns.DOT.split(getPermission());
Iterable<String> parts = Splitter.on('.').split(getPermission());
List<Set<String>> nodeParts = new ArrayList<>();
for (String s : parts) {
@@ -321,13 +331,13 @@ public class Node implements me.lucko.luckperms.api.Node {
final String bits = s.substring(1, s.length() - 1);
if (s.contains("|")) {
nodeParts.add(new HashSet<>(Arrays.asList(Patterns.VERTICAL_BAR.split(bits))));
nodeParts.add(new HashSet<>(Splitter.on('|').splitToList(bits)));
} else {
String[] range = Patterns.WORLD_DELIMITER.split(bits, 2);
if (isChar(range[0], range[1])) {
nodeParts.add(getCharRange(range[0].charAt(0), range[1].charAt(0)));
} else if (isInt(range[0], range[1])) {
nodeParts.add(IntStream.rangeClosed(Integer.parseInt(range[0]), Integer.parseInt(range[1])).boxed()
List<String> range = Splitter.on('-').limit(2).splitToList(bits);
if (isChar(range.get(0), range.get(1))) {
nodeParts.add(getCharRange(range.get(0).charAt(0), range.get(1).charAt(0)));
} else if (isInt(range.get(0), range.get(1))) {
nodeParts.add(IntStream.rangeClosed(Integer.parseInt(range.get(0)), Integer.parseInt(range.get(1))).boxed()
.map(i -> "" + i)
.collect(Collectors.toSet())
);
@@ -420,7 +430,7 @@ public class Node implements me.lucko.luckperms.api.Node {
@Override
public boolean isGroupNode() {
return Patterns.GROUP_MATCH.matcher(getPermission()).matches();
return getPermission().toLowerCase().startsWith("group.");
}
@Override
@@ -444,7 +454,11 @@ public class Node implements me.lucko.luckperms.api.Node {
@Override
public boolean isMeta() {
return getPermission().matches("meta\\..*\\..*");
if (isMeta == Tristate.UNDEFINED) {
isMeta = Tristate.fromBoolean(META_PATTERN.matcher(getPermission()).matches());
}
return isMeta.asBoolean();
}
@Override
@@ -459,7 +473,11 @@ public class Node implements me.lucko.luckperms.api.Node {
@Override
public boolean isPrefix() {
return PREFIX_PATTERN.matcher(getPermission()).matches();
if (isPrefix == Tristate.UNDEFINED) {
isPrefix = Tristate.fromBoolean(PREFIX_PATTERN.matcher(getPermission()).matches());
}
return isPrefix.asBoolean();
}
@Override
@@ -468,15 +486,18 @@ public class Node implements me.lucko.luckperms.api.Node {
throw new IllegalStateException();
}
String[] prefixPart = Patterns.DOT.split(getPermission().substring("prefix.".length()), 2);
Integer i = Integer.parseInt(prefixPart[0]);
return new AbstractMap.SimpleEntry<>(i, prefixPart[1]);
List<String> prefixPart = Splitter.on('.').limit(2).splitToList(getPermission().substring("prefix.".length()));
Integer i = Integer.parseInt(prefixPart.get(0));
return new AbstractMap.SimpleEntry<>(i, prefixPart.get(1));
}
@Override
public boolean isSuffix() {
return SUFFIX_PATTERN.matcher(getPermission()).matches();
if (isSuffix == Tristate.UNDEFINED) {
isSuffix = Tristate.fromBoolean(SUFFIX_PATTERN.matcher(getPermission()).matches());
}
return isSuffix.asBoolean();
}
@Override
@@ -485,10 +506,9 @@ public class Node implements me.lucko.luckperms.api.Node {
throw new IllegalStateException();
}
String[] suffixPart = Patterns.DOT.split(getPermission().substring("suffix.".length()), 2);
Integer i = Integer.parseInt(suffixPart[0]);
return new AbstractMap.SimpleEntry<>(i, suffixPart[1]);
List<String> suffixPart = Splitter.on('.').limit(2).splitToList(getPermission().substring("suffix.".length()));
Integer i = Integer.parseInt(suffixPart.get(0));
return new AbstractMap.SimpleEntry<>(i, suffixPart.get(1));
}
@Override
@@ -632,18 +652,14 @@ public class Node implements me.lucko.luckperms.api.Node {
if (!Patterns.NODE_CONTEXTS.matcher(permission).matches()) {
this.permission = permission;
} else {
String[] contextParts = permission.substring(1).split("\\)", 2);
List<String> contextParts = Splitter.on(')').limit(2).splitToList(permission.substring(1));
// 0 = context, 1 = node
this.permission = contextParts[1];
for (String s : contextParts[0].split("\\,")) {
if (!s.contains("=")) {
// Not valid
continue;
}
String[] context = s.split("\\=", 2);
extraContexts.put(context[0], context[1]);
this.permission = contextParts.get(1);
try {
extraContexts.putAll(Splitter.on(',').withKeyValueSeparator('=').split(contextParts.get(0)));
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,165 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.utils;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.constants.Patterns;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
/**
* Calculates and caches permissions
*/
@RequiredArgsConstructor
public class PermissionCalculator {
private final LuckPermsPlugin plugin;
private final String objectName;
private final boolean debug;
private final List<PermissionProcessor> processors;
private final Map<String, Tristate> cache = new ConcurrentHashMap<>();
public void invalidateCache() {
cache.clear();
}
public Tristate getPermissionValue(String permission) {
if (debug) {
plugin.getLog().info("Checking if " + objectName + " has permission: " + permission);
}
permission = permission.toLowerCase();
return cache.computeIfAbsent(permission, this::lookupPermissionValue);
}
private Tristate lookupPermissionValue(String permission) {
for (PermissionProcessor processor : processors) {
Tristate v = processor.hasPermission(permission);
if (v == Tristate.UNDEFINED) {
continue;
}
return v;
}
return Tristate.UNDEFINED;
}
@AllArgsConstructor
public static class MapProcessor implements PermissionProcessor {
@Getter
private final Map<String, Boolean> map;
@Override
public Tristate hasPermission(String permission) {
if (map.containsKey(permission)) {
return Tristate.fromBoolean(map.get(permission));
}
return Tristate.UNDEFINED;
}
}
@AllArgsConstructor
public static class RegexProcessor implements PermissionProcessor {
@Getter
private final Map<String, Boolean> map;
@Override
public Tristate hasPermission(String permission) {
for (Map.Entry<String, Boolean> e : map.entrySet()) {
if (e.getKey().toLowerCase().startsWith("r=")) {
Pattern p = Patterns.compile(e.getKey().substring(2));
if (p == null) {
continue;
}
if (p.matcher(permission).matches()) {
return Tristate.fromBoolean(e.getValue());
}
}
}
return Tristate.UNDEFINED;
}
}
@AllArgsConstructor
public static class WildcardProcessor implements PermissionProcessor {
@Getter
private final Map<String, Boolean> map;
@Override
public Tristate hasPermission(String permission) {
String node = permission;
while (node.contains(".")) {
int endIndex = node.lastIndexOf('.');
if (endIndex == -1) {
break;
}
node = node.substring(0, endIndex);
if (!isEmpty(node)) {
if (map.containsKey(node + ".*")) {
return Tristate.fromBoolean(map.get(node + ".*"));
}
}
}
if (map.containsKey("'*'")) {
return Tristate.fromBoolean(map.get("'*'"));
}
if (map.containsKey("*")) {
return Tristate.fromBoolean(map.get("*"));
}
return Tristate.UNDEFINED;
}
private static boolean isEmpty(String s) {
if (s.equals("")) {
return true;
}
char[] chars = s.toCharArray();
for (char c : chars) {
if (c != '.') {
return false;
}
}
return true;
}
}
}
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.utils;
import me.lucko.luckperms.api.Tristate;
public interface PermissionProcessor {
Tristate hasPermission(String permission);
}
+4 -2
View File
@@ -6,11 +6,13 @@
<groupId>me.lucko.luckperms</groupId>
<artifactId>luckperms</artifactId>
<version>2.8-SNAPSHOT</version>
<version>2.9-SNAPSHOT</version>
<modules>
<module>common</module>
<module>api</module>
<module>standalone</module>
<module>bukkit</module>
<module>bukkit-legacy</module>
<module>bukkit-placeholders</module>
<module>bungee</module>
<module>sponge</module>
@@ -37,7 +39,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<release.version>2.8</release.version>
<release.version>2.9</release.version>
</properties>
<distributionManagement>
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.8-SNAPSHOT</version>
<version>2.9-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -217,8 +217,11 @@ public class LPSpongePlugin implements LuckPermsPlugin {
}
@Override
public List<Sender> getSenders() {
return game.getServer().getOnlinePlayers().stream().map(s -> SpongeSenderFactory.get().wrap(s)).collect(Collectors.toList());
public List<Sender> getNotifyListeners() {
return game.getServer().getOnlinePlayers().stream()
.map(s -> SpongeSenderFactory.get().wrap(s, Collections.singleton(Permission.LOG_NOTIFY)))
.filter(Permission.LOG_NOTIFY::isAuthorized)
.collect(Collectors.toList());
}
@Override
@@ -22,8 +22,10 @@
package me.lucko.luckperms;
import com.google.common.base.Splitter;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.commands.CommandManager;
import me.lucko.luckperms.commands.Util;
import me.lucko.luckperms.constants.Patterns;
import org.spongepowered.api.command.CommandCallable;
import org.spongepowered.api.command.CommandException;
@@ -34,8 +36,6 @@ import org.spongepowered.api.world.Location;
import org.spongepowered.api.world.World;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@@ -47,27 +47,22 @@ class SpongeCommand extends CommandManager implements CommandCallable {
@Override
public CommandResult process(CommandSource source, String s) throws CommandException {
onCommand(SpongeSenderFactory.get().wrap(source), "perms", Arrays.asList(Patterns.SPACE.split(s)), Callback.empty());
onCommand(
SpongeSenderFactory.get().wrap(source),
"perms",
Util.stripQuotes(Splitter.on(Patterns.COMMAND_SEPARATOR).omitEmptyStrings().splitToList(s)),
Callback.empty()
);
return CommandResult.success();
}
@Override
public List<String> getSuggestions(CommandSource source, String s, @Nullable Location<World> location) throws CommandException {
List<String> args = new ArrayList<>(Arrays.asList(Patterns.SPACE.split(s)));
if (s.endsWith(" ")) {
args.add("");
}
return onTabComplete(SpongeSenderFactory.get().wrap(source), args);
return onTabComplete(SpongeSenderFactory.get().wrap(source), Splitter.on(' ').splitToList(s));
}
public List<String> getSuggestions(CommandSource source, String s) throws CommandException {
List<String> args = new ArrayList<>(Arrays.asList(Patterns.SPACE.split(s)));
if (s.endsWith(" ")) {
args.add("");
}
return onTabComplete(SpongeSenderFactory.get().wrap(source), args);
return onTabComplete(SpongeSenderFactory.get().wrap(source), Splitter.on(' ').splitToList(s));
}
@Override
@@ -22,7 +22,7 @@
package me.lucko.luckperms;
import me.lucko.luckperms.constants.Patterns;
import com.google.common.base.Splitter;
import me.lucko.luckperms.core.LPConfiguration;
import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.commented.CommentedConfigurationNode;
@@ -70,7 +70,7 @@ class SpongeConfig extends LPConfiguration<LPSpongePlugin> {
}
private ConfigurationNode getNode(String path) {
String[] paths = Patterns.DOT.split(path);
Iterable<String> paths = Splitter.on('.').split(path);
ConfigurationNode node = root;
for (String s : paths) {
@@ -35,8 +35,8 @@ import java.util.UUID;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class SpongeSenderFactory extends SenderFactory<CommandSource> {
private static SpongeSenderFactory instance = null;
public static SpongeSenderFactory get() {
if (instance == null){
public static synchronized SpongeSenderFactory get() {
if (instance == null) {
instance = new SpongeSenderFactory();
}
return instance;
@@ -35,9 +35,8 @@ import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.users.User;
import org.spongepowered.api.Sponge;
import me.lucko.luckperms.utils.Contexts;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectCollection;
@@ -60,9 +59,9 @@ public class LuckPermsSubject implements Subject {
private final PermissionHolder holder;
private final EnduringData enduringData;
private final TransientData transientData;
private final LuckPermsService service;
protected final LuckPermsService service;
private LuckPermsSubject(PermissionHolder holder, LuckPermsService service) {
LuckPermsSubject(PermissionHolder holder, LuckPermsService service) {
this.holder = holder;
this.enduringData = new EnduringData(this, service, holder);
this.transientData = new TransientData(service, holder);
@@ -86,15 +85,6 @@ public class LuckPermsSubject implements Subject {
@Override
public Optional<CommandSource> getCommandSource() {
if (holder instanceof User) {
final UUID uuid = ((User) holder).getUuid();
Optional<Player> p = Sponge.getServer().getPlayer(uuid);
if (p.isPresent()) {
return Optional.of(p.get());
}
}
return Optional.empty();
}
@@ -171,6 +161,20 @@ public class LuckPermsSubject implements Subject {
@Override
public Optional<String> getOption(Set<Context> set, String s) {
if (s.equalsIgnoreCase("prefix")) {
String prefix = getChatMeta(true, holder);
if (!prefix.equals("")) {
return Optional.of(prefix);
}
}
if (s.equalsIgnoreCase("suffix")) {
String suffix = getChatMeta(false, holder);
if (!suffix.equals("")) {
return Optional.of(suffix);
}
}
Map<String, String> transientOptions = enduringData.getOptions(set);
if (transientOptions.containsKey(s)) {
return Optional.of(transientOptions.get(s));
@@ -194,6 +198,41 @@ public class LuckPermsSubject implements Subject {
return SubjectData.GLOBAL_CONTEXT;
}
private String getChatMeta(boolean prefix, PermissionHolder holder) {
if (holder == null) return "";
int priority = Integer.MIN_VALUE;
String meta = null;
for (Node n : holder.getAllNodes(null, Contexts.allowAll())) {
if (!n.getValue()) {
continue;
}
if (prefix ? !n.isPrefix() : !n.isSuffix()) {
continue;
}
if (!n.shouldApplyOnServer(service.getPlugin().getConfiguration().getVaultServer(), service.getPlugin().getConfiguration().getVaultIncludeGlobal(), false)) {
continue;
}
/* TODO per world
if (!n.shouldApplyOnWorld(world, service.getPlugin().getConfiguration().getVaultIncludeGlobal(), false)) {
continue;
}
*/
Map.Entry<Integer, String> value = prefix ? n.getPrefix() : n.getSuffix();
if (value.getKey() > priority) {
meta = value.getValue();
priority = value.getKey();
}
}
return meta == null ? "" : unescapeCharacters(meta);
}
@AllArgsConstructor
public static class EnduringData implements SubjectData {
private final LuckPermsSubject superClass;
@@ -305,7 +344,7 @@ public class LuckPermsSubject implements Subject {
public Map<Set<Context>, List<Subject>> getAllParents() {
Map<Set<Context>, List<Subject>> parents = new HashMap<>();
for (Node n : holder.getAllNodes(null)) {
for (Node n : holder.getAllNodes(null, Contexts.allowAll())) {
if (!n.isGroupNode()) {
continue;
}
@@ -432,7 +471,7 @@ public class LuckPermsSubject implements Subject {
public Map<Set<Context>, Map<String, String>> getAllOptions() {
Map<Set<Context>, Map<String, String>> options = new HashMap<>();
for (Node n : holder.getAllNodes(null)) {
for (Node n : holder.getAllNodes(null, Contexts.allowAll())) {
if (!n.isMeta()) {
continue;
}
@@ -0,0 +1,168 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.api.sponge;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.PermissionCalculator;
import me.lucko.luckperms.utils.PermissionProcessor;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.util.Tristate;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class LuckPermsUserSubject extends LuckPermsSubject {
public static LuckPermsUserSubject wrapUser(User user, LuckPermsService service) {
return new LuckPermsUserSubject(user, service);
}
@Getter
private final User user;
private final PermissionCalculator calculator;
@Getter
private final Map<String, Boolean> permissionCache = new ConcurrentHashMap<>();
private LuckPermsUserSubject(User user, LuckPermsService service) {
super(user, service);
this.user = user;
List<PermissionProcessor> processors = new ArrayList<>(5);
processors.add(new PermissionCalculator.MapProcessor(permissionCache));
if (service.getPlugin().getConfiguration().getApplyWildcards()) {
processors.add(new SpongeWildcardProcessor(permissionCache));
processors.add(new PermissionCalculator.WildcardProcessor(permissionCache));
}
if (service.getPlugin().getConfiguration().getApplyRegex()) {
processors.add(new PermissionCalculator.RegexProcessor(permissionCache));
}
processors.add(new SpongeDefaultsProcessor(service));
calculator = new PermissionCalculator(service.getPlugin(), user.getName(), service.getPlugin().getConfiguration().getDebugPermissionChecks(), processors);
}
public void invalidateCache() {
calculator.invalidateCache();
}
// TODO don't ignore context
@Override
public Tristate getPermissionValue(@NonNull Set<Context> contexts, @NonNull String permission) {
me.lucko.luckperms.api.Tristate t = calculator.getPermissionValue(permission);
if (t != me.lucko.luckperms.api.Tristate.UNDEFINED) {
return Tristate.fromBoolean(t.asBoolean());
} else {
return Tristate.UNDEFINED;
}
}
@Override
public String getIdentifier() {
return service.getPlugin().getUuidCache().getExternalUUID(user.getUuid()).toString();
}
@Override
public Optional<CommandSource> getCommandSource() {
final UUID uuid = service.getPlugin().getUuidCache().getExternalUUID(user.getUuid());
Optional<Player> p = Sponge.getServer().getPlayer(uuid);
if (p.isPresent()) {
return Optional.of(p.get());
}
return Optional.empty();
}
@AllArgsConstructor
private static class SpongeWildcardProcessor implements PermissionProcessor {
@Getter
private final Map<String, Boolean> map;
@Override
public me.lucko.luckperms.api.Tristate hasPermission(String permission) {
String node = permission;
while (node.contains(".")) {
int endIndex = node.lastIndexOf('.');
if (endIndex == -1) {
break;
}
node = node.substring(0, endIndex);
if (!isEmpty(node)) {
if (map.containsKey(node)) {
return me.lucko.luckperms.api.Tristate.fromBoolean(map.get(node));
}
}
}
if (map.containsKey("'*'")) {
return me.lucko.luckperms.api.Tristate.fromBoolean(map.get("'*'"));
}
if (map.containsKey("*")) {
return me.lucko.luckperms.api.Tristate.fromBoolean(map.get("*"));
}
return me.lucko.luckperms.api.Tristate.UNDEFINED;
}
private static boolean isEmpty(String s) {
if (s.equals("")) {
return true;
}
char[] chars = s.toCharArray();
for (char c : chars) {
if (c != '.') {
return false;
}
}
return true;
}
}
@AllArgsConstructor
private static class SpongeDefaultsProcessor implements PermissionProcessor {
private final LuckPermsService service;
@Override
public me.lucko.luckperms.api.Tristate hasPermission(String permission) {
Tristate t = service.getDefaults().getPermissionValue(Collections.emptySet(), permission);
if (t != Tristate.UNDEFINED) {
return me.lucko.luckperms.api.Tristate.fromBoolean(t.asBoolean());
} else {
return me.lucko.luckperms.api.Tristate.UNDEFINED;
}
}
}
}
@@ -22,11 +22,10 @@
package me.lucko.luckperms.api.sponge.collections;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import me.lucko.luckperms.api.sponge.LuckPermsService;
import me.lucko.luckperms.api.sponge.LuckPermsSubject;
import me.lucko.luckperms.api.sponge.simple.SimpleSubject;
import me.lucko.luckperms.api.sponge.simple.SimpleCollection;
import me.lucko.luckperms.groups.GroupManager;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.PermissionService;
@@ -38,10 +37,16 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@AllArgsConstructor
public class GroupCollection implements SubjectCollection {
private final LuckPermsService service;
private final GroupManager manager;
private final SimpleCollection fallback;
public GroupCollection(LuckPermsService service, GroupManager manager) {
this.service = service;
this.manager = manager;
this.fallback = new SimpleCollection(service, "fallback-groups");
}
@Override
public String getIdentifier() {
@@ -54,7 +59,7 @@ public class GroupCollection implements SubjectCollection {
return LuckPermsSubject.wrapHolder(manager.get(id), service);
}
return new SimpleSubject(id, service, this);
return fallback.get(id);
}
@Override
@@ -84,6 +89,6 @@ public class GroupCollection implements SubjectCollection {
@Override
public Subject getDefaults() {
return new SimpleSubject("default", service, this);
return service.getDefaults();
}
}
@@ -22,12 +22,11 @@
package me.lucko.luckperms.api.sponge.collections;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import me.lucko.luckperms.api.sponge.LuckPermsService;
import me.lucko.luckperms.api.sponge.LuckPermsSubject;
import me.lucko.luckperms.api.sponge.simple.SimpleSubject;
import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.api.sponge.LuckPermsUserSubject;
import me.lucko.luckperms.api.sponge.simple.SimpleCollection;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.users.UserManager;
import org.spongepowered.api.service.context.Context;
@@ -39,50 +38,75 @@ import org.spongepowered.api.service.permission.SubjectData;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@AllArgsConstructor
public class UserCollection implements SubjectCollection {
private final LuckPermsService service;
private final UserManager manager;
private final SimpleCollection fallback;
@Getter
private final Map<UUID, LuckPermsUserSubject> users = new ConcurrentHashMap<>();
public UserCollection(LuckPermsService service, UserManager manager) {
this.service = service;
this.manager = manager;
this.fallback = new SimpleCollection(service, "fallback-users");
}
@Override
public String getIdentifier() {
return PermissionService.SUBJECTS_USER;
}
private void load(UUID uuid) {
UUID internal = service.getPlugin().getUuidCache().getUUID(uuid);
if (!manager.isLoaded(internal)) {
return;
}
User user = manager.get(internal);
users.put(internal, LuckPermsUserSubject.wrapUser(user, service));
}
public void unload(UUID uuid) {
users.remove(service.getPlugin().getUuidCache().getUUID(uuid));
}
@Override
public Subject get(@NonNull String id) {
PermissionHolder holder = null;
public synchronized Subject get(@NonNull String id) {
try {
UUID u = UUID.fromString(id);
UUID u = service.getPlugin().getUuidCache().getUUID(UUID.fromString(id));
if (users.containsKey(u)) {
return users.get(u);
}
if (manager.isLoaded(u)) {
holder = manager.get(u);
load(u);
return users.get(u);
}
} catch (IllegalArgumentException e) {
User user = manager.get(id);
if (user != null) {
holder = user;
for (LuckPermsUserSubject subject : users.values()) {
if (subject.getUser().getName().equals(id)) {
return subject;
}
}
}
if (holder != null) {
return LuckPermsSubject.wrapHolder(holder, service);
}
service.getPlugin().getLog().warn("Couldn't get subject for: " + id);
// What am I meant to do here? What if no user is loaded? Load it? Create it?
// If I do load/create it, this method should always be called async??.... errr.
return new SimpleSubject(id, service, this);
return fallback.get(id);
}
@Override
public boolean hasRegistered(@NonNull String id) {
try {
UUID u = UUID.fromString(id);
return manager.isLoaded(u);
return manager.isLoaded(service.getPlugin().getUuidCache().getUUID(u));
} catch (IllegalArgumentException e) {
User user = manager.get(id);
return user != null;
@@ -91,9 +115,7 @@ public class UserCollection implements SubjectCollection {
@Override
public Iterable<Subject> getAllSubjects() {
return manager.getAll().values().stream()
.map(u -> LuckPermsSubject.wrapHolder(u, service))
.collect(Collectors.toList());
return users.values().stream().collect(Collectors.toList());
}
@Override
@@ -103,14 +125,13 @@ public class UserCollection implements SubjectCollection {
@Override
public Map<Subject, Boolean> getAllWithPermission(@NonNull Set<Context> contexts, @NonNull String node) {
return manager.getAll().values().stream()
.map(u -> LuckPermsSubject.wrapHolder(u, service))
return users.values().stream()
.filter(sub -> sub.hasPermission(contexts, node))
.collect(Collectors.toMap(sub -> sub, sub -> sub.getPermissionValue(contexts, node).asBoolean()));
}
@Override
public Subject getDefaults() {
return new SimpleSubject("default", service, this);
return service.getDefaults();
}
}
@@ -47,7 +47,7 @@ public class SimpleCollection implements SubjectCollection {
private final Map<String, Subject> subjects = new ConcurrentHashMap<>();
@Override
public Subject get(@NonNull String id) {
public synchronized Subject get(@NonNull String id) {
if (!subjects.containsKey(id)) {
subjects.put(id, new SimpleSubject(id, service, this));
}
@@ -23,7 +23,14 @@
package me.lucko.luckperms.users;
import me.lucko.luckperms.LPSpongePlugin;
import me.lucko.luckperms.api.event.events.UserPermissionRefreshEvent;
import me.lucko.luckperms.api.implementation.internal.UserLink;
import me.lucko.luckperms.api.sponge.LuckPermsUserSubject;
import me.lucko.luckperms.api.sponge.collections.UserCollection;
import me.lucko.luckperms.utils.Contexts;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;
class SpongeUser extends User {
@@ -40,25 +47,53 @@ class SpongeUser extends User {
}
@Override
public void refreshPermissions() {
// Do nothing. Should be grabbed from PermissionService.
/*
plugin.doSync(() -> {
Optional<Player> p = plugin.getGame().getServer().getPlayer(plugin.getUuidCache().getExternalUUID(getUuid()));
if (!p.isPresent()) return;
public synchronized void refreshPermissions() {
UserCollection uc = plugin.getService().getUserSubjects();
if (!uc.getUsers().containsKey(getUuid())) {
return;
}
final Player player = p.get();
// Calculate the permissions that should be applied. This is done async, who cares about how long it takes or how often it's done.
Map<String, Boolean> toApply = exportNodes(
new Contexts(
plugin.getConfiguration().getServer(),
null, // TODO per world perms
null,
plugin.getConfiguration().getIncludeGlobalPerms(),
plugin.getConfiguration().getIncludeGlobalWorldPerms(),
true,
plugin.getConfiguration().getApplyGlobalGroups(),
plugin.getConfiguration().getApplyGlobalWorldGroups()
),
Collections.emptyList()
);
// Clear existing permissions
player.getSubjectData().clearParents();
player.getSubjectData().clearPermissions();
try {
LuckPermsUserSubject us = uc.getUsers().get(getUuid());
Map<String, Boolean> existing = us.getPermissionCache();
boolean different = false;
if (toApply.size() != existing.size()) {
different = true;
} else {
for (Map.Entry<String, Boolean> e : existing.entrySet()) {
if (toApply.containsKey(e.getKey()) && toApply.get(e.getKey()) == e.getValue()) {
continue;
}
different = true;
break;
}
}
if (!different) return;
existing.clear();
us.invalidateCache();
existing.putAll(toApply);
// Re-add all defined permissions for the user
final String world = player.getWorld().getName();
Map<String, Boolean> local = getLocalPermissions(getPlugin().getConfiguration().getServer(), world, null);
local.entrySet().forEach(e -> player.getSubjectData().setPermission(Collections.emptySet(), e.getKey(), Tristate.fromBoolean(e.getValue())));
plugin.getApiProvider().fireEventAsync(new UserPermissionRefreshEvent(new UserLink(this)));
});
*/
} catch (Exception e) {
e.printStackTrace();
}
}
}
@@ -38,6 +38,7 @@ public class SpongeUserManager extends UserManager {
public void cleanup(User user) {
if (!plugin.getGame().getServer().getPlayer(plugin.getUuidCache().getExternalUUID(user.getUuid())).isPresent()) {
unload(user);
plugin.getService().getUserSubjects().unload(user.getUuid());
}
}
+17 -4
View File
@@ -8,9 +8,20 @@
# 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/groups applied.
# 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 this server is in offline or online mode.
# This setting allows a player to have the same UUID across a network of offline mode/mixed servers.
@@ -26,19 +37,21 @@ online-mode=true
# 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. This will only work for plugins that define all of their permissions to the server.
# 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. This will only work for plugins that define all of their
# permissions to the server.
# 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 LuckPerms should print to console every time a plugin checks if a player has a permission
debug-permission-checks=false
# If the plugin should send log notifications to users whenever permissions are modified.
log-notify=true
+145
View File
@@ -0,0 +1,145 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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>2.9-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>luckperms-standalone</artifactId>
<packaging>jar</packaging>
<build>
<defaultGoal>clean package</defaultGoal>
<finalName>LuckPermsStandalone-${release.version}.${git.closest.tag.commit.count}</finalName>
<sourceDirectory>${basedir}/src/main/java</sourceDirectory>
<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>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<archive>
<manifest>
<mainClass>me.lucko.luckperms.LPStandaloneApp</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>false</minimizeJar>
<relocations>
<relocation>
<pattern>org.slf4j</pattern>
<shadedPattern>me.lucko.luckperms.lib.slf4j</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>org.h2</pattern>
<shadedPattern>me.lucko.luckperms.lib.h2</shadedPattern>
</relocation>
<relocation>
<pattern>org.sqlite</pattern>
<shadedPattern>me.lucko.luckperms.lib.sqlite</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<goals>
<goal>revision</goal>
</goals>
</execution>
</executions>
<configuration>
<injectAllReactorProjects>true</injectAllReactorProjects>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- LuckPerms Common -->
<dependency>
<groupId>me.lucko.luckperms</groupId>
<artifactId>luckperms-common</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<!-- LuckPerms API -->
<dependency>
<groupId>me.lucko.luckperms</groupId>
<artifactId>luckperms-api</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
<scope>provided</scope>
</dependency>
<!-- gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.7</version>
<scope>compile</scope>
</dependency>
<!-- guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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;
import me.lucko.luckperms.internal.StandaloneBase;
public class LPStandaloneApp {
private StandaloneBase standaloneBase;
public static void main(String[] args) {
new LPStandaloneApp().start();
}
public void start() {
standaloneBase = new StandaloneBase(this);
LuckPerms.registerProvider(standaloneBase.getApiProvider());
}
public void stop() {
standaloneBase.shutdown();
}
}
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.gui;
public class InterfaceManager {
// TODO
}
@@ -0,0 +1,220 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.internal;
import lombok.Getter;
import me.lucko.luckperms.LPStandaloneApp;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.implementation.ApiProvider;
import me.lucko.luckperms.commands.ConsecutiveExecutor;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.constants.Constants;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.core.LPConfiguration;
import me.lucko.luckperms.core.UuidCache;
import me.lucko.luckperms.data.Importer;
import me.lucko.luckperms.groups.GroupManager;
import me.lucko.luckperms.storage.Datastore;
import me.lucko.luckperms.tracks.TrackManager;
import me.lucko.luckperms.users.StandaloneUserManager;
import me.lucko.luckperms.users.UserManager;
import me.lucko.luckperms.utils.LogFactory;
import java.io.File;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Getter
public class StandaloneBase implements LuckPermsPlugin {
private final ExecutorService threadPool = Executors.newCachedThreadPool();
private final java.util.logging.Logger logger;
private final UserManager userManager;
private final GroupManager groupManager;
private final TrackManager trackManager;
private final LPConfiguration configuration;
private final Datastore datastore;
private final Logger log;
private final UuidCache uuidCache;
private final ApiProvider apiProvider;
public StandaloneBase(LPStandaloneApp app) {
logger = java.util.logging.Logger.getGlobal();
log = LogFactory.wrap(logger);
configuration = new StandaloneConfiguration(this);
// TODO datastore
datastore = null;
// Load everything, including all users.
uuidCache = new UuidCache(true);
userManager = new StandaloneUserManager(this);
groupManager = new GroupManager(this);
trackManager = new TrackManager();
apiProvider = new ApiProvider(this);
// TODO callback to the app, start gui stuff?
}
public void shutdown() {
datastore.shutdown();
}
@Override
public String getVersion() {
return "2.9.0"; // TODO set dynamically
}
@Override
public PlatformType getType() {
return PlatformType.STANDALONE;
}
@Override
public File getMainDir() {
return null; // Is this needed? TODO
}
@Override
public File getDataFolder() {
return null; // Is this needed? TODO
}
@Override
public void runUpdateTask() {
// Is this needed?? TODO
}
@Override
public void doAsync(Runnable r) {
threadPool.execute(r);
}
@Override
public void doSync(Runnable r) {
threadPool.execute(r);
}
/*
* Methods below are only required in plugins.
* They're just left empty / default.
*/
@Override
public Importer getImporter() {
return null;
}
@Override
public ConsecutiveExecutor getConsecutiveExecutor() {
return null;
}
@Override
public Message getPlayerStatus(UUID uuid) {
return Message.PLAYER_OFFLINE;
}
@Override
public int getPlayerCount() {
return 0;
}
@Override
public List<String> getPlayerList() {
return Collections.emptyList();
}
@Override
public List<Sender> getNotifyListeners() {
return Collections.emptyList();
}
@Override
public Sender getConsoleSender() {
return new Sender() {
@Override
public String getName() {
return Constants.getConsoleName();
}
@Override
public UUID getUuid() {
return Constants.getConsoleUUID();
}
@Override
public void sendMessage(String s) {
getLogger().info(s);
}
@Override
public boolean hasPermission(Permission permission) {
return true;
}
};
}
@Override
public List<String> getPossiblePermissions() {
return Collections.emptyList();
}
@Override
public Set<UUID> getIgnoringLogs() {
return Collections.emptySet();
}
@Override
public Object getPlugin(String name) {
return null;
}
@Override
public Object getService(Class clazz) {
return null;
}
@Override
public UUID getUUID(String playerName) {
return null;
}
@Override
public boolean isPluginLoaded(String name) {
return false;
}
}
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.internal;
import me.lucko.luckperms.core.LPConfiguration;
public class StandaloneConfiguration extends LPConfiguration<StandaloneBase> {
public StandaloneConfiguration(StandaloneBase plugin) {
super(plugin, "global", true, "null");
}
@Override
protected void init() {
}
@Override
protected void set(String path, Object value) {
}
@Override
protected String getString(String path, String def) {
return def;
}
@Override
protected int getInt(String path, int def) {
return def;
}
@Override
protected boolean getBoolean(String path, boolean def) {
return def;
}
}
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.users;
import me.lucko.luckperms.LuckPermsPlugin;
import java.util.UUID;
public class StandaloneUser extends User {
StandaloneUser(UUID uuid, LuckPermsPlugin plugin) {
super(uuid, plugin);
}
StandaloneUser(UUID uuid, String username, LuckPermsPlugin plugin) {
super(uuid, username, plugin);
}
@Override
public void refreshPermissions() {
// Do nothing.
}
}
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.users;
import me.lucko.luckperms.LuckPermsPlugin;
import java.util.UUID;
public class StandaloneUserManager extends UserManager {
private final LuckPermsPlugin plugin;
public StandaloneUserManager(LuckPermsPlugin plugin) {
super(plugin);
this.plugin = plugin;
}
@Override
public void cleanup(User user) {
// unload(user);
// never unload????? TODO
}
@Override
public User make(UUID id) {
return new StandaloneUser(id, plugin);
}
@Override
public User make(UUID uuid, String username) {
return new StandaloneUser(uuid, username, plugin);
}
@Override
public void updateAllUsers() {
getAll().values().forEach(u -> plugin.getDatastore().loadUser(u.getUuid(), u.getName()));
}
}