diff --git a/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java b/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java index 23ea27edc..7846fcfe7 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java +++ b/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java @@ -194,8 +194,11 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { } @Override - public List getSenders() { - return getServer().getOnlinePlayers().stream().map(p -> BukkitSenderFactory.get().wrap(p)).collect(Collectors.toList()); + public List 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 diff --git a/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java b/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java index 29c49372d..b6fbd1393 100644 --- a/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java +++ b/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java @@ -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; @@ -161,8 +162,11 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin { } @Override - public List getSenders() { - return getProxy().getPlayers().stream().map(p -> BungeeSenderFactory.get().wrap(p)).collect(Collectors.toList()); + public List 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 diff --git a/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java index af2da9a20..47df48f56 100644 --- a/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java @@ -103,7 +103,7 @@ public interface LuckPermsPlugin { /** * @return a {@link List} of senders online on the platform */ - List getSenders(); + List getNotifyListeners(); /** * @return the console sender of the instance diff --git a/common/src/main/java/me/lucko/luckperms/commands/SenderFactory.java b/common/src/main/java/me/lucko/luckperms/commands/SenderFactory.java index c79dfd338..a804b2ed7 100644 --- a/common/src/main/java/me/lucko/luckperms/commands/SenderFactory.java +++ b/common/src/main/java/me/lucko/luckperms/commands/SenderFactory.java @@ -47,7 +47,11 @@ public abstract class SenderFactory 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 toCheck) { + return new SenderImp(t, toCheck); } @Override @@ -81,15 +85,19 @@ public abstract class SenderFactory implements Runnable { private final boolean console; - private SenderImp(T t) { + private SenderImp(T t, Set 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())))); + } } } diff --git a/common/src/main/java/me/lucko/luckperms/data/LogEntry.java b/common/src/main/java/me/lucko/luckperms/data/LogEntry.java index c62724f5b..4905bd9a6 100644 --- a/common/src/main/java/me/lucko/luckperms/data/LogEntry.java +++ b/common/src/main/java/me/lucko/luckperms/data/LogEntry.java @@ -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 senders = plugin.getSenders().stream() - .filter(Permission.LOG_NOTIFY::isAuthorized) - .collect(Collectors.toList()); + List senders = plugin.getNotifyListeners(); senders.add(plugin.getConsoleSender()); if (sender == null) { diff --git a/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java b/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java index 4cc95dd8c..2360a4ac7 100644 --- a/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java +++ b/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java @@ -217,8 +217,11 @@ public class LPSpongePlugin implements LuckPermsPlugin { } @Override - public List getSenders() { - return game.getServer().getOnlinePlayers().stream().map(s -> SpongeSenderFactory.get().wrap(s)).collect(Collectors.toList()); + public List 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 diff --git a/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsSubject.java b/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsSubject.java index f8b625508..d04274633 100644 --- a/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsSubject.java @@ -60,9 +60,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); diff --git a/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsUserSubject.java b/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsUserSubject.java new file mode 100644 index 000000000..ade70224d --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsUserSubject.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * 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 com.google.common.base.Splitter; +import lombok.Getter; +import lombok.NonNull; +import me.lucko.luckperms.users.User; +import org.spongepowered.api.service.context.Context; +import org.spongepowered.api.util.Tristate; + +import java.util.Map; +import java.util.Set; +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; + + @Getter + private final Map permissionCache = new ConcurrentHashMap<>(); + + private LuckPermsUserSubject(User user, LuckPermsService service) { + super(user, service); + this.user = user; + } + + // TODO don't ignore context + @Override + public Tristate getPermissionValue(@NonNull Set contexts, @NonNull String permission) { + if (service.getPlugin().getConfiguration().getDebugPermissionChecks()) { + service.getPlugin().getLog().info("Checking if " + user.getName() + " has permission: " + permission); + } + + permission = permission.toLowerCase(); + + if (permissionCache.containsKey(permission)) { + return Tristate.fromBoolean(permissionCache.get(permission)); + } + + if (service.getPlugin().getConfiguration().getApplyWildcards()) { + if (permissionCache.containsKey("*")) { + return Tristate.fromBoolean(permissionCache.get("*")); + } + if (permissionCache.containsKey("'*'")) { + return Tristate.fromBoolean(permissionCache.get("'*'")); + } + + String node = ""; + Iterable permParts = Splitter.on('.').split(permission); + for (String s : permParts) { + if (node.equals("")) { + node = s; + } else { + node = node + "." + s; + } + + if (permissionCache.containsKey(node + ".*")) { + return Tristate.fromBoolean(permissionCache.get(node + ".*")); + } + } + } + + return Tristate.UNDEFINED; + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/api/sponge/collections/UserCollection.java b/sponge/src/main/java/me/lucko/luckperms/api/sponge/collections/UserCollection.java index 06f3ae744..c7677857b 100644 --- a/sponge/src/main/java/me/lucko/luckperms/api/sponge/collections/UserCollection.java +++ b/sponge/src/main/java/me/lucko/luckperms/api/sponge/collections/UserCollection.java @@ -23,11 +23,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.LuckPermsUserSubject; import me.lucko.luckperms.api.sponge.simple.SimpleSubject; -import me.lucko.luckperms.core.PermissionHolder; import me.lucko.luckperms.users.User; import me.lucko.luckperms.users.UserManager; import org.spongepowered.api.service.context.Context; @@ -39,6 +39,7 @@ 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 @@ -46,31 +47,48 @@ public class UserCollection implements SubjectCollection { private final LuckPermsService service; private final UserManager manager; + @Getter + private final Map users = new ConcurrentHashMap<>(); + @Override public String getIdentifier() { return PermissionService.SUBJECTS_USER; } + private void load(UUID uuid) { + if (!manager.isLoaded(uuid)) { + return; + } + + User user = manager.get(uuid); + users.put(uuid, LuckPermsUserSubject.wrapUser(user, service)); + } + + public void unload(UUID uuid) { + users.remove(uuid); + } + @Override - public Subject get(@NonNull String id) { - PermissionHolder holder = null; + public synchronized Subject get(@NonNull String id) { try { UUID u = 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? @@ -91,9 +109,7 @@ public class UserCollection implements SubjectCollection { @Override public Iterable getAllSubjects() { - return manager.getAll().values().stream() - .map(u -> LuckPermsSubject.wrapHolder(u, service)) - .collect(Collectors.toList()); + return users.values().stream().collect(Collectors.toList()); } @Override @@ -103,8 +119,7 @@ public class UserCollection implements SubjectCollection { @Override public Map getAllWithPermission(@NonNull Set 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())); } diff --git a/sponge/src/main/java/me/lucko/luckperms/users/SpongeUser.java b/sponge/src/main/java/me/lucko/luckperms/users/SpongeUser.java index de4592a8a..264a9ddce 100644 --- a/sponge/src/main/java/me/lucko/luckperms/users/SpongeUser.java +++ b/sponge/src/main/java/me/lucko/luckperms/users/SpongeUser.java @@ -23,7 +23,12 @@ 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.collections.UserCollection; +import java.util.Collections; +import java.util.Map; import java.util.UUID; class SpongeUser extends User { @@ -41,24 +46,45 @@ class SpongeUser extends User { @Override public void refreshPermissions() { - // Do nothing. Should be grabbed from PermissionService. - /* - plugin.doSync(() -> { - Optional p = plugin.getGame().getServer().getPlayer(plugin.getUuidCache().getExternalUUID(getUuid())); - if (!p.isPresent()) return; + 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 toApply = exportNodes( + getPlugin().getConfiguration().getServer(), + null, // TODO per world perms + null, + plugin.getConfiguration().getIncludeGlobalPerms(), + true, + Collections.emptyList() + ); - // Clear existing permissions - player.getSubjectData().clearParents(); - player.getSubjectData().clearPermissions(); + try { + Map existing = uc.getUsers().get(getUuid()).getPermissionCache(); + + boolean different = false; + if (toApply.size() != existing.size()) { + different = true; + } else { + for (Map.Entry e : existing.entrySet()) { + if (toApply.containsKey(e.getKey()) && toApply.get(e.getKey()) == e.getValue()) { + continue; + } + different = true; + break; + } + } + + if (!different) return; + + existing.clear(); + existing.putAll(toApply); - // Re-add all defined permissions for the user - final String world = player.getWorld().getName(); - Map 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(); + } } } diff --git a/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java b/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java index 964aaa818..231661d15 100644 --- a/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java +++ b/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java @@ -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()); } } diff --git a/sponge/src/main/resources/luckperms.conf b/sponge/src/main/resources/luckperms.conf index 1c2de149f..d49a51aaa 100644 --- a/sponge/src/main/resources/luckperms.conf +++ b/sponge/src/main/resources/luckperms.conf @@ -39,6 +39,9 @@ apply-regex=true # 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