Cleanup parts of the sponge service implementation

This commit is contained in:
Luck 2018-03-01 08:48:39 +00:00
parent 98fb9946e4
commit fbe84322b5
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
32 changed files with 632 additions and 884 deletions

View File

@ -45,7 +45,7 @@ public final class BukkitMigrationUtils {
} }
} }
if (uuid == null) { if (uuid == null) {
log.logErr("Unable to get a UUID for user identifier: " + s); log.logError("Unable to get a UUID for user identifier: " + s);
} }
return uuid; return uuid;
} }

View File

@ -85,7 +85,7 @@ public class MigrationBPermissions extends SubCommand<Object> {
WorldManager worldManager = WorldManager.getInstance(); WorldManager worldManager = WorldManager.getInstance();
if (worldManager == null) { if (worldManager == null) {
log.logErr("Plugin not loaded."); log.logError("Plugin not loaded.");
return CommandResult.STATE_ERROR; return CommandResult.STATE_ERROR;
} }
@ -111,7 +111,7 @@ public class MigrationBPermissions extends SubCommand<Object> {
Set<String> users = configSection.getKeys(false); Set<String> users = configSection.getKeys(false);
if (users == null) { if (users == null) {
log.logErr("Couldn't get a list of users."); log.logError("Couldn't get a list of users.");
return CommandResult.FAILURE; return CommandResult.FAILURE;
} }
AtomicInteger userLoadCount = new AtomicInteger(0); AtomicInteger userLoadCount = new AtomicInteger(0);

View File

@ -74,14 +74,14 @@ public class MigrationGroupManager extends SubCommand<Object> {
log.log("Starting."); log.log("Starting.");
if (!args.get(0).equalsIgnoreCase("true") && !args.get(0).equalsIgnoreCase("false")) { if (!args.get(0).equalsIgnoreCase("true") && !args.get(0).equalsIgnoreCase("false")) {
log.logErr("Was expecting true/false, but got " + args.get(0) + " instead."); log.logError("Was expecting true/false, but got " + args.get(0) + " instead.");
return CommandResult.STATE_ERROR; return CommandResult.STATE_ERROR;
} }
final boolean migrateAsGlobal = Boolean.parseBoolean(args.get(0)); final boolean migrateAsGlobal = Boolean.parseBoolean(args.get(0));
final Function<String, String> worldMappingFunc = s -> migrateAsGlobal ? null : s; final Function<String, String> worldMappingFunc = s -> migrateAsGlobal ? null : s;
if (!Bukkit.getPluginManager().isPluginEnabled("GroupManager")) { if (!Bukkit.getPluginManager().isPluginEnabled("GroupManager")) {
log.logErr("Plugin not loaded."); log.logError("Plugin not loaded.");
return CommandResult.STATE_ERROR; return CommandResult.STATE_ERROR;
} }

View File

@ -66,7 +66,7 @@ public class MigrationPermissionsBukkit extends SubCommand<Object> {
log.log("Starting."); log.log("Starting.");
if (!Bukkit.getPluginManager().isPluginEnabled("PermissionsBukkit")) { if (!Bukkit.getPluginManager().isPluginEnabled("PermissionsBukkit")) {
log.logErr("Plugin not loaded."); log.logError("Plugin not loaded.");
return CommandResult.STATE_ERROR; return CommandResult.STATE_ERROR;
} }

View File

@ -99,7 +99,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
log.log("Starting."); log.log("Starting.");
if (!Bukkit.getPluginManager().isPluginEnabled("PermissionsEx")) { if (!Bukkit.getPluginManager().isPluginEnabled("PermissionsEx")) {
log.logErr("Plugin not loaded."); log.logError("Plugin not loaded.");
return CommandResult.STATE_ERROR; return CommandResult.STATE_ERROR;
} }

View File

@ -85,7 +85,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
log.log("Starting."); log.log("Starting.");
if (!Bukkit.getPluginManager().isPluginEnabled("PowerfulPerms")) { if (!Bukkit.getPluginManager().isPluginEnabled("PowerfulPerms")) {
log.logErr("Plugin not loaded."); log.logError("Plugin not loaded.");
return CommandResult.STATE_ERROR; return CommandResult.STATE_ERROR;
} }
@ -143,7 +143,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
} }
if (uuids.isEmpty()) { if (uuids.isEmpty()) {
log.logErr("Unable to find any UUIDs to migrate."); log.logError("Unable to find any UUIDs to migrate.");
return CommandResult.FAILURE; return CommandResult.FAILURE;
} }

View File

@ -76,12 +76,12 @@ public class MigrationZPermissions extends SubCommand<Object> {
log.log("Starting."); log.log("Starting.");
if (!Bukkit.getPluginManager().isPluginEnabled("zPermissions")) { if (!Bukkit.getPluginManager().isPluginEnabled("zPermissions")) {
log.logErr("Plugin not loaded."); log.logError("Plugin not loaded.");
return CommandResult.STATE_ERROR; return CommandResult.STATE_ERROR;
} }
if (!Bukkit.getServicesManager().isProvidedFor(ZPermissionsService.class)) { if (!Bukkit.getServicesManager().isProvidedFor(ZPermissionsService.class)) {
log.logErr("Plugin not loaded."); log.logError("Plugin not loaded.");
return CommandResult.STATE_ERROR; return CommandResult.STATE_ERROR;
} }

View File

@ -66,7 +66,7 @@ public class MigrationBungeePerms extends SubCommand<Object> {
// Get BungeePerms instance // Get BungeePerms instance
BungeePerms bp = BungeePerms.getInstance(); BungeePerms bp = BungeePerms.getInstance();
if (bp == null) { if (bp == null) {
log.logErr("Plugin not loaded."); log.logError("Plugin not loaded.");
return CommandResult.STATE_ERROR; return CommandResult.STATE_ERROR;
} }
@ -106,7 +106,7 @@ public class MigrationBungeePerms extends SubCommand<Object> {
SafeIteration.iterate(bp.getPermissionsManager().getBackEnd().loadUsers(), u -> { SafeIteration.iterate(bp.getPermissionsManager().getBackEnd().loadUsers(), u -> {
if (u.getUUID() == null) { if (u.getUUID() == null) {
log.logErr("Could not parse UUID for user: " + u.getName()); log.logError("Could not parse UUID for user: " + u.getName());
return; return;
} }

View File

@ -62,7 +62,7 @@ public class ProgressLogger {
} }
} }
public void logErr(String msg) { public void logError(String msg) {
if (this.pluginName == null) { if (this.pluginName == null) {
this.listeners.forEach(s -> this.logMessage.send(s, "Error -> " + msg)); this.listeners.forEach(s -> this.logMessage.send(s, "Error -> " + msg));
} else { } else {
@ -80,7 +80,6 @@ public class ProgressLogger {
public void logProgress(String msg, int amount) { public void logProgress(String msg, int amount) {
if (amount % NOTIFY_FREQUENCY == 0) { if (amount % NOTIFY_FREQUENCY == 0) {
// migrated {} groups so far.
logAllProgress(msg, amount); logAllProgress(msg, amount);
} }
} }

View File

@ -25,8 +25,30 @@
package me.lucko.luckperms.common.model; package me.lucko.luckperms.common.model;
import java.util.function.Supplier;
public enum NodeMapType { public enum NodeMapType {
ENDURING, TRANSIENT ENDURING,
TRANSIENT;
// useful methods for fluent/conditional execution
public void run(Runnable ifEnduring, Runnable ifTransient) {
if (this == ENDURING) {
ifEnduring.run();
} else {
ifTransient.run();
}
}
public <T> T supply(Supplier<T> ifEnduring, Supplier<T> ifTransient) {
if (this == ENDURING) {
return ifEnduring.get();
} else {
return ifTransient.get();
}
}
} }

View File

@ -262,6 +262,10 @@ public abstract class PermissionHolder {
return this.transientNodes; return this.transientNodes;
} }
public ImmutableSetMultimap<ImmutableContextSet, Node> getNodes(NodeMapType type) {
return getData(type).immutable();
}
public ImmutableSetMultimap<ImmutableContextSet, Node> getEnduringNodes() { public ImmutableSetMultimap<ImmutableContextSet, Node> getEnduringNodes() {
return this.enduringNodes.immutable(); return this.enduringNodes.immutable();
} }

View File

@ -36,7 +36,6 @@ import me.lucko.luckperms.common.processors.MapProcessor;
import me.lucko.luckperms.common.processors.PermissionProcessor; import me.lucko.luckperms.common.processors.PermissionProcessor;
import me.lucko.luckperms.common.processors.RegexProcessor; import me.lucko.luckperms.common.processors.RegexProcessor;
import me.lucko.luckperms.common.processors.WildcardProcessor; import me.lucko.luckperms.common.processors.WildcardProcessor;
import me.lucko.luckperms.common.references.HolderType;
import me.lucko.luckperms.sponge.LPSpongePlugin; import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.processors.GroupDefaultsProcessor; import me.lucko.luckperms.sponge.processors.GroupDefaultsProcessor;
import me.lucko.luckperms.sponge.processors.SpongeWildcardProcessor; import me.lucko.luckperms.sponge.processors.SpongeWildcardProcessor;
@ -68,9 +67,9 @@ public class SpongeCalculatorFactory extends AbstractCalculatorFactory {
} }
if (this.plugin.getConfiguration().get(ConfigKeys.APPLY_SPONGE_DEFAULT_SUBJECTS)) { if (this.plugin.getConfiguration().get(ConfigKeys.APPLY_SPONGE_DEFAULT_SUBJECTS)) {
if (metadata.getHolderType() == HolderType.USER) { if (metadata.getHolderType().isUser()) {
processors.add(new UserDefaultsProcessor(this.plugin.getService(), contexts.getContexts().makeImmutable())); processors.add(new UserDefaultsProcessor(this.plugin.getService(), contexts.getContexts().makeImmutable()));
} else if (metadata.getHolderType() == HolderType.GROUP) { } else if (metadata.getHolderType().isGroup()) {
processors.add(new GroupDefaultsProcessor(this.plugin.getService(), contexts.getContexts().makeImmutable())); processors.add(new GroupDefaultsProcessor(this.plugin.getService(), contexts.getContexts().makeImmutable()));
} }
} }

View File

@ -62,14 +62,14 @@ public class SpongeConnectionListener extends AbstractLoginListener {
/* Called when the player first attempts a connection with the server. /* Called when the player first attempts a connection with the server.
Listening on AFTER_PRE priority to allow plugins to modify username / UUID data here. (auth plugins) */ Listening on AFTER_PRE priority to allow plugins to modify username / UUID data here. (auth plugins) */
final GameProfile p = e.getProfile(); final GameProfile profile = e.getProfile();
final String username = p.getName().orElseThrow(() -> new RuntimeException("No username present for user " + p.getUniqueId())); final String username = profile.getName().orElseThrow(() -> new RuntimeException("No username present for user " + profile.getUniqueId()));
if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) { if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
this.plugin.getLog().info("Processing auth event for " + p.getUniqueId() + " - " + p.getName()); this.plugin.getLog().info("Processing auth event for " + profile.getUniqueId() + " - " + profile.getName());
} }
this.plugin.getUniqueConnections().add(p.getUniqueId()); this.plugin.getUniqueConnections().add(profile.getUniqueId());
/* Actually process the login for the connection. /* Actually process the login for the connection.
We do this here to delay the login until the data is ready. We do this here to delay the login until the data is ready.
@ -81,13 +81,13 @@ public class SpongeConnectionListener extends AbstractLoginListener {
- creating a user instance in the UserManager for this connection. - creating a user instance in the UserManager for this connection.
- setting up cached data. */ - setting up cached data. */
try { try {
User user = loadUser(p.getUniqueId(), username); User user = loadUser(profile.getUniqueId(), username);
this.plugin.getEventFactory().handleUserLoginProcess(p.getUniqueId(), username, user); this.plugin.getEventFactory().handleUserLoginProcess(profile.getUniqueId(), username, user);
} catch (Exception ex) { } catch (Exception ex) {
this.plugin.getLog().severe("Exception occurred whilst loading data for " + p.getUniqueId() + " - " + p.getName()); this.plugin.getLog().severe("Exception occurred whilst loading data for " + profile.getUniqueId() + " - " + profile.getName());
ex.printStackTrace(); ex.printStackTrace();
this.deniedAsyncLogin.add(p.getUniqueId()); this.deniedAsyncLogin.add(profile.getUniqueId());
e.setCancelled(true); e.setCancelled(true);
e.setMessageCancelled(false); e.setMessageCancelled(false);
@ -120,19 +120,19 @@ public class SpongeConnectionListener extends AbstractLoginListener {
At this point, the users data should be present and loaded. At this point, the users data should be present and loaded.
Listening on LOW priority to allow plugins to further modify data here. (auth plugins, etc.) */ Listening on LOW priority to allow plugins to further modify data here. (auth plugins, etc.) */
final GameProfile player = e.getProfile(); final GameProfile profile = e.getProfile();
if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) { if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
this.plugin.getLog().info("Processing login event for " + player.getUniqueId() + " - " + player.getName()); this.plugin.getLog().info("Processing login event for " + profile.getUniqueId() + " - " + profile.getName());
} }
final User user = this.plugin.getUserManager().getIfLoaded(player.getUniqueId()); final User user = this.plugin.getUserManager().getIfLoaded(profile.getUniqueId());
/* User instance is null for whatever reason. Could be that it was unloaded between asyncpre and now. */ /* User instance is null for whatever reason. Could be that it was unloaded between asyncpre and now. */
if (user == null) { if (user == null) {
this.deniedLogin.add(player.getUniqueId()); this.deniedLogin.add(profile.getUniqueId());
this.plugin.getLog().warn("User " + player.getUniqueId() + " - " + player.getName() + " doesn't have data pre-loaded. - denying login."); this.plugin.getLog().warn("User " + profile.getUniqueId() + " - " + profile.getName() + " doesn't have data pre-loaded. - denying login.");
e.setCancelled(true); e.setCancelled(true);
e.setMessageCancelled(false); e.setMessageCancelled(false);
//noinspection deprecation //noinspection deprecation

View File

@ -73,7 +73,7 @@ public class MigrationPermissionManager extends SubCommand<Object> {
Optional<PluginContainer> pm = Sponge.getPluginManager().getPlugin("permissionmanager"); Optional<PluginContainer> pm = Sponge.getPluginManager().getPlugin("permissionmanager");
if (!pm.isPresent()) { if (!pm.isPresent()) {
log.logErr("Plugin not loaded."); log.logError("Plugin not loaded.");
return CommandResult.STATE_ERROR; return CommandResult.STATE_ERROR;
} }
@ -120,7 +120,7 @@ public class MigrationPermissionManager extends SubCommand<Object> {
SafeIteration.iterate(pmService.getUserSubjects().getAllSubjects(), pmUser -> { SafeIteration.iterate(pmService.getUserSubjects().getAllSubjects(), pmUser -> {
UUID uuid = Uuids.parseNullable(pmUser.getIdentifier()); UUID uuid = Uuids.parseNullable(pmUser.getIdentifier());
if (uuid == null) { if (uuid == null) {
log.logErr("Could not parse UUID for user: " + pmUser.getIdentifier()); log.logError("Could not parse UUID for user: " + pmUser.getIdentifier());
return; return;
} }

View File

@ -79,7 +79,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
Optional<PluginContainer> pex = Sponge.getPluginManager().getPlugin("permissionsex"); Optional<PluginContainer> pex = Sponge.getPluginManager().getPlugin("permissionsex");
if (!pex.isPresent()) { if (!pex.isPresent()) {
log.logErr("Plugin not loaded."); log.logError("Plugin not loaded.");
return CommandResult.STATE_ERROR; return CommandResult.STATE_ERROR;
} }
@ -172,7 +172,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
SafeIteration.iterate(pexService.getUserSubjects().getAllSubjects(), pexUser -> { SafeIteration.iterate(pexService.getUserSubjects().getAllSubjects(), pexUser -> {
UUID uuid = Uuids.parseNullable(pexUser.getIdentifier()); UUID uuid = Uuids.parseNullable(pexUser.getIdentifier());
if (uuid == null) { if (uuid == null) {
log.logErr("Could not parse UUID for user: " + pexUser.getIdentifier()); log.logError("Could not parse UUID for user: " + pexUser.getIdentifier());
return; return;
} }

View File

@ -25,32 +25,11 @@
package me.lucko.luckperms.sponge.model; package me.lucko.luckperms.sponge.model;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.caching.MetaData;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.node.NodeFactory;
import me.lucko.luckperms.common.verbose.CheckOrigin;
import me.lucko.luckperms.sponge.LPSpongePlugin; import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.service.LuckPermsService; import me.lucko.luckperms.sponge.service.internal.GroupSubject;
import me.lucko.luckperms.sponge.service.LuckPermsSubjectData;
import me.lucko.luckperms.sponge.service.ProxyFactory;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.reference.LPSubjectReference;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.service.permission.PermissionService;
import org.spongepowered.api.service.permission.Subject;
import java.util.Map;
import java.util.Optional;
public class SpongeGroup extends Group {
public class SpongeGroup extends Group implements SpongePermissionHolder {
private final GroupSubject spongeData; private final GroupSubject spongeData;
public SpongeGroup(String name, LPSpongePlugin plugin) { public SpongeGroup(String name, LPSpongePlugin plugin) {
@ -58,133 +37,9 @@ public class SpongeGroup extends Group {
this.spongeData = new GroupSubject(plugin, this); this.spongeData = new GroupSubject(plugin, this);
} }
@Override
public GroupSubject sponge() { public GroupSubject sponge() {
return this.spongeData; return this.spongeData;
} }
public static class GroupSubject implements LPSubject {
private final SpongeGroup parent;
private final LPSpongePlugin plugin;
private final LuckPermsSubjectData subjectData;
private final LuckPermsSubjectData transientSubjectData;
private GroupSubject(LPSpongePlugin plugin, SpongeGroup parent) {
this.parent = parent;
this.plugin = plugin;
this.subjectData = new LuckPermsSubjectData(true, plugin.getService(), parent, this);
this.transientSubjectData = new LuckPermsSubjectData(false, plugin.getService(), parent, this);
}
@Override
public String getIdentifier() {
return this.parent.getObjectName();
}
@Override
public Optional<String> getFriendlyIdentifier() {
return this.parent.getDisplayName();
}
@Override
public Optional<CommandSource> getCommandSource() {
return Optional.empty();
}
@Override
public LPSubjectCollection getParentCollection() {
return this.plugin.getService().getGroupSubjects();
}
@Override
public Subject sponge() {
return ProxyFactory.toSponge(this);
}
@Override
public LuckPermsService getService() {
return this.plugin.getService();
}
@Override
public LuckPermsSubjectData getSubjectData() {
return this.subjectData;
}
@Override
public LuckPermsSubjectData getTransientSubjectData() {
return this.transientSubjectData;
}
@Override
public Tristate getPermissionValue(ImmutableContextSet contexts, String permission) {
return this.parent.getCachedData().getPermissionData(this.plugin.getContextManager().formContexts(contexts)).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK);
}
@Override
public boolean isChildOf(ImmutableContextSet contexts, LPSubjectReference parent) {
return parent.getCollectionIdentifier().equals(PermissionService.SUBJECTS_GROUP) && getPermissionValue(contexts, NodeFactory.groupNode(parent.getSubjectIdentifier())).asBoolean();
}
@Override
public ImmutableList<LPSubjectReference> getParents(ImmutableContextSet contexts) {
ImmutableSet.Builder<LPSubjectReference> subjects = ImmutableSet.builder();
for (Map.Entry<String, Boolean> entry : this.parent.getCachedData().getPermissionData(this.plugin.getContextManager().formContexts(contexts)).getImmutableBacking().entrySet()) {
if (!entry.getValue()) {
continue;
}
String groupName = NodeFactory.parseGroupNode(entry.getKey());
if (groupName == null) {
continue;
}
if (this.plugin.getGroupManager().isLoaded(groupName)) {
subjects.add(this.plugin.getService().getGroupSubjects().loadSubject(groupName).join().toReference());
}
}
subjects.addAll(this.plugin.getService().getGroupSubjects().getDefaults().getParents(contexts));
subjects.addAll(this.plugin.getService().getDefaults().getParents(contexts));
return getService().sortSubjects(subjects.build());
}
@Override
public Optional<String> getOption(ImmutableContextSet contexts, String s) {
MetaData data = this.parent.getCachedData().getMetaData(this.plugin.getContextManager().formContexts(contexts));
if (s.equalsIgnoreCase(NodeFactory.PREFIX_KEY)) {
if (data.getPrefix() != null) {
return Optional.of(data.getPrefix());
}
}
if (s.equalsIgnoreCase(NodeFactory.SUFFIX_KEY)) {
if (data.getSuffix() != null) {
return Optional.of(data.getSuffix());
}
}
String val = data.getMeta().get(s);
if (val != null) {
return Optional.of(val);
}
Optional<String> v = this.plugin.getService().getGroupSubjects().getDefaults().getOption(contexts, s);
if (v.isPresent()) {
return v;
}
return this.plugin.getService().getDefaults().getOption(contexts, s);
}
@Override
public void invalidateCaches(CacheLevel cacheLevel) {
// invalidate for all changes
this.parent.getCachedData().invalidateCaches();
}
}
} }

View File

@ -0,0 +1,43 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.sponge.model;
import me.lucko.luckperms.common.model.PermissionHolder;
import me.lucko.luckperms.sponge.service.model.LPSubject;
/**
* A sponge specific extension of {@link PermissionHolder}.
*/
public interface SpongePermissionHolder {
/**
* Gets a {@link LPSubject} representation of this holder.
*
* @return a subject
*/
LPSubject sponge();
}

View File

@ -25,35 +25,17 @@
package me.lucko.luckperms.sponge.model; package me.lucko.luckperms.sponge.model;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.caching.MetaData;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.node.NodeFactory;
import me.lucko.luckperms.common.verbose.CheckOrigin;
import me.lucko.luckperms.sponge.LPSpongePlugin; import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.service.LuckPermsService; import me.lucko.luckperms.sponge.service.internal.UserSubject;
import me.lucko.luckperms.sponge.service.LuckPermsSubjectData;
import me.lucko.luckperms.sponge.service.ProxyFactory;
import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.reference.LPSubjectReference;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.service.permission.PermissionService;
import org.spongepowered.api.service.permission.Subject;
import java.util.Map;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.function.Function;
public class SpongeUser extends User {
/**
* Extension of {@link User} to hold an implementation for {@link LPSubject}.
*/
public class SpongeUser extends User implements SpongePermissionHolder {
private final UserSubject spongeData; private final UserSubject spongeData;
public SpongeUser(UUID uuid, LPSpongePlugin plugin) { public SpongeUser(UUID uuid, LPSpongePlugin plugin) {
@ -66,134 +48,9 @@ public class SpongeUser extends User {
this.spongeData = new UserSubject(plugin, this); this.spongeData = new UserSubject(plugin, this);
} }
@Override
public UserSubject sponge() { public UserSubject sponge() {
return this.spongeData; return this.spongeData;
} }
public static final class UserSubject implements LPSubject {
private final SpongeUser parent;
private final LPSpongePlugin plugin;
private final LuckPermsSubjectData subjectData;
private final LuckPermsSubjectData transientSubjectData;
private UserSubject(LPSpongePlugin plugin, SpongeUser parent) {
this.parent = parent;
this.plugin = plugin;
this.subjectData = new LuckPermsSubjectData(true, plugin.getService(), parent, this);
this.transientSubjectData = new LuckPermsSubjectData(false, plugin.getService(), parent, this);
}
@Override
public String getIdentifier() {
return this.parent.getUuid().toString();
}
@Override
public Optional<String> getFriendlyIdentifier() {
return this.parent.getName();
}
@Override
public Optional<CommandSource> getCommandSource() {
final UUID uuid = this.parent.getUuid();
return Sponge.getServer().getPlayer(uuid).map(Function.identity());
}
@Override
public LPSubjectCollection getParentCollection() {
return this.plugin.getService().getUserSubjects();
}
@Override
public Subject sponge() {
return ProxyFactory.toSponge(this);
}
@Override
public LuckPermsService getService() {
return this.plugin.getService();
}
@Override
public LuckPermsSubjectData getSubjectData() {
return this.subjectData;
}
@Override
public LuckPermsSubjectData getTransientSubjectData() {
return this.transientSubjectData;
}
@Override
public Tristate getPermissionValue(ImmutableContextSet contexts, String permission) {
return this.parent.getCachedData().getPermissionData(this.plugin.getContextManager().formContexts(contexts)).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK);
}
@Override
public boolean isChildOf(ImmutableContextSet contexts, LPSubjectReference parent) {
return parent.getCollectionIdentifier().equals(PermissionService.SUBJECTS_GROUP) && getPermissionValue(contexts, NodeFactory.groupNode(parent.getSubjectIdentifier())).asBoolean();
}
@Override
public ImmutableList<LPSubjectReference> getParents(ImmutableContextSet contexts) {
ImmutableSet.Builder<LPSubjectReference> subjects = ImmutableSet.builder();
for (Map.Entry<String, Boolean> entry : this.parent.getCachedData().getPermissionData(this.plugin.getContextManager().formContexts(contexts)).getImmutableBacking().entrySet()) {
if (!entry.getValue()) {
continue;
}
String groupName = NodeFactory.parseGroupNode(entry.getKey());
if (groupName == null) {
continue;
}
if (this.plugin.getGroupManager().isLoaded(groupName)) {
subjects.add(this.plugin.getService().getGroupSubjects().loadSubject(groupName).join().toReference());
}
}
subjects.addAll(this.plugin.getService().getUserSubjects().getDefaults().getParents(contexts));
subjects.addAll(this.plugin.getService().getDefaults().getParents(contexts));
return getService().sortSubjects(subjects.build());
}
@Override
public Optional<String> getOption(ImmutableContextSet contexts, String s) {
MetaData data = this.parent.getCachedData().getMetaData(this.plugin.getContextManager().formContexts(contexts));
if (s.equalsIgnoreCase(NodeFactory.PREFIX_KEY)) {
if (data.getPrefix() != null) {
return Optional.of(data.getPrefix());
}
}
if (s.equalsIgnoreCase(NodeFactory.SUFFIX_KEY)) {
if (data.getSuffix() != null) {
return Optional.of(data.getSuffix());
}
}
String val = data.getMeta().get(s);
if (val != null) {
return Optional.of(val);
}
Optional<String> v = this.plugin.getService().getUserSubjects().getDefaults().getOption(contexts, s);
if (v.isPresent()) {
return v;
}
return this.plugin.getService().getDefaults().getOption(contexts, s);
}
@Override
public void invalidateCaches(CacheLevel cacheLevel) {
// invalidate for all changes
this.parent.getCachedData().invalidateCaches();
}
}
} }

View File

@ -43,7 +43,6 @@ import java.util.concurrent.CompletableFuture;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public final class LuckPermsPermissionDescription implements LPPermissionDescription { public final class LuckPermsPermissionDescription implements LPPermissionDescription {
private final LPPermissionService service; private final LPPermissionService service;
private final String id; private final String id;

View File

@ -37,7 +37,6 @@ import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.contexts.SpongeProxiedContextCalculator; import me.lucko.luckperms.sponge.contexts.SpongeProxiedContextCalculator;
import me.lucko.luckperms.sponge.managers.SpongeGroupManager; import me.lucko.luckperms.sponge.managers.SpongeGroupManager;
import me.lucko.luckperms.sponge.managers.SpongeUserManager; import me.lucko.luckperms.sponge.managers.SpongeUserManager;
import me.lucko.luckperms.sponge.service.legacy.LegacyDataMigrator;
import me.lucko.luckperms.sponge.service.model.LPPermissionDescription; import me.lucko.luckperms.sponge.service.model.LPPermissionDescription;
import me.lucko.luckperms.sponge.service.model.LPPermissionService; import me.lucko.luckperms.sponge.service.model.LPPermissionService;
import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubject;
@ -90,7 +89,6 @@ public class LuckPermsService implements LPPermissionService {
this.spongeProxy = ProxyFactory.toSponge(this); this.spongeProxy = ProxyFactory.toSponge(this);
this.storage = new SubjectStorage(this, new File(plugin.getDataDirectory(), "sponge-data")); this.storage = new SubjectStorage(this, new File(plugin.getDataDirectory(), "sponge-data"));
new LegacyDataMigrator(plugin, new File(plugin.getDataDirectory(), "local"), this.storage).run();
this.userSubjects = plugin.getUserManager(); this.userSubjects = plugin.getUserManager();
this.groupSubjects = plugin.getGroupManager(); this.groupSubjects = plugin.getGroupManager();

View File

@ -29,7 +29,6 @@ import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache; import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.api.context.ImmutableContextSet;
@ -38,7 +37,6 @@ import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata;
import me.lucko.luckperms.common.contexts.ContextSetComparator; import me.lucko.luckperms.common.contexts.ContextSetComparator;
import me.lucko.luckperms.common.processors.MapProcessor; import me.lucko.luckperms.common.processors.MapProcessor;
import me.lucko.luckperms.common.processors.PermissionProcessor; import me.lucko.luckperms.common.processors.PermissionProcessor;
import me.lucko.luckperms.common.references.HolderType;
import me.lucko.luckperms.common.verbose.CheckOrigin; import me.lucko.luckperms.common.verbose.CheckOrigin;
import me.lucko.luckperms.sponge.processors.SpongeWildcardProcessor; import me.lucko.luckperms.sponge.processors.SpongeWildcardProcessor;
import me.lucko.luckperms.sponge.service.model.LPPermissionService; import me.lucko.luckperms.sponge.service.model.LPPermissionService;
@ -52,6 +50,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.SortedMap; import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -60,33 +59,28 @@ import java.util.concurrent.TimeUnit;
* In-memory implementation of {@link LPSubjectData}. * In-memory implementation of {@link LPSubjectData}.
*/ */
public class CalculatedSubjectData implements LPSubjectData { public class CalculatedSubjectData implements LPSubjectData {
private final LPSubject parentSubject; private final LPSubject parentSubject;
private final LPPermissionService service; private final LPPermissionService service;
private final String calculatorDisplayName;
private final Map<ImmutableContextSet, Map<String, Boolean>> permissions = new ConcurrentHashMap<>(); private final Map<ImmutableContextSet, Map<String, Boolean>> permissions = new ConcurrentHashMap<>();
private final Map<ImmutableContextSet, Set<LPSubjectReference>> parents = new ConcurrentHashMap<>(); private final Map<ImmutableContextSet, Set<LPSubjectReference>> parents = new ConcurrentHashMap<>();
private final Map<ImmutableContextSet, Map<String, String>> options = new ConcurrentHashMap<>(); private final Map<ImmutableContextSet, Map<String, String>> options = new ConcurrentHashMap<>();
private final LoadingCache<ImmutableContextSet, CalculatorHolder> permissionCache = Caffeine.newBuilder() private final LoadingCache<ImmutableContextSet, CalculatorHolder> permissionCache;
.expireAfterAccess(10, TimeUnit.MINUTES)
.build(contexts -> {
ImmutableList.Builder<PermissionProcessor> processors = ImmutableList.builder();
processors.add(new MapProcessor());
processors.add(new SpongeWildcardProcessor());
CalculatorHolder holder = new CalculatorHolder(new PermissionCalculator(CalculatedSubjectData.this.service.getPlugin(), PermissionCalculatorMetadata.of(HolderType.GROUP, CalculatedSubjectData.this.calculatorDisplayName, contexts), processors.build()));
holder.setPermissions(flattenMap(getRelevantEntries(contexts, CalculatedSubjectData.this.permissions)));
return holder;
});
public CalculatedSubjectData(LPSubject parentSubject, LPPermissionService service, String calculatorDisplayName) { public CalculatedSubjectData(LPSubject parentSubject, LPPermissionService service, String calculatorDisplayName) {
this.parentSubject = parentSubject; this.parentSubject = parentSubject;
this.service = service; this.service = service;
this.calculatorDisplayName = calculatorDisplayName; this.permissionCache = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.build(contexts -> {
ImmutableList<PermissionProcessor> processors = ImmutableList.of(new MapProcessor(), new SpongeWildcardProcessor());
PermissionCalculatorMetadata calcMetadata = PermissionCalculatorMetadata.of(null, calculatorDisplayName, contexts);
CalculatorHolder holder = new CalculatorHolder(new PermissionCalculator(this.service.getPlugin(), calcMetadata, processors));
holder.setPermissions(processPermissionsMap(contexts, this.permissions));
return holder;
});
} }
@Override @Override
@ -103,7 +97,8 @@ public class CalculatedSubjectData implements LPSubjectData {
} }
public Tristate getPermissionValue(ImmutableContextSet contexts, String permission) { public Tristate getPermissionValue(ImmutableContextSet contexts, String permission) {
return this.permissionCache.get(contexts).getCalculator().getPermissionValue(permission, CheckOrigin.INTERNAL); CalculatorHolder calculatorHolder = Objects.requireNonNull(this.permissionCache.get(contexts));
return calculatorHolder.getCalculator().getPermissionValue(permission, CheckOrigin.INTERNAL);
} }
public void replacePermissions(Map<ImmutableContextSet, Map<String, Boolean>> map) { public void replacePermissions(Map<ImmutableContextSet, Map<String, Boolean>> map) {
@ -291,30 +286,25 @@ public class CalculatedSubjectData implements LPSubjectData {
return CompletableFuture.completedFuture(!map.isEmpty()); return CompletableFuture.completedFuture(!map.isEmpty());
} }
private static <V> Map<String, V> flattenMap(SortedMap<ImmutableContextSet, Map<String, V>> data) { private static Map<String, Boolean> processPermissionsMap(ImmutableContextSet filter, Map<ImmutableContextSet, Map<String, Boolean>> input) {
Map<String, V> map = new HashMap<>(); // get relevant entries
SortedMap<ImmutableContextSet, Map<String, Boolean>> sorted = new TreeMap<>(ContextSetComparator.reverse());
for (Map<String, V> m : data.values()) { for (Map.Entry<ImmutableContextSet, Map<String, Boolean>> entry : input.entrySet()) {
for (Map.Entry<String, V> e : m.entrySet()) { if (!entry.getKey().isSatisfiedBy(filter)) {
map.putIfAbsent(e.getKey(), e.getValue());
}
}
return ImmutableMap.copyOf(map);
}
private static <K, V> SortedMap<ImmutableContextSet, Map<K, V>> getRelevantEntries(ImmutableContextSet set, Map<ImmutableContextSet, Map<K, V>> map) {
ImmutableSortedMap.Builder<ImmutableContextSet, Map<K, V>> perms = ImmutableSortedMap.orderedBy(ContextSetComparator.reverse());
for (Map.Entry<ImmutableContextSet, Map<K, V>> e : map.entrySet()) {
if (!e.getKey().isSatisfiedBy(set)) {
continue; continue;
} }
perms.put(e.getKey(), ImmutableMap.copyOf(e.getValue())); sorted.put(entry.getKey(), entry.getValue());
} }
return perms.build(); // flatten
Map<String, Boolean> result = new HashMap<>();
for (Map<String, Boolean> map : sorted.values()) {
for (Map.Entry<String, Boolean> e : map.entrySet()) {
result.putIfAbsent(e.getKey(), e.getValue());
}
}
return ImmutableMap.copyOf(result);
} }
private static boolean stringEquals(String a, String b) { private static boolean stringEquals(String a, String b) {
@ -322,9 +312,7 @@ public class CalculatedSubjectData implements LPSubjectData {
} }
private static class CalculatorHolder { private static class CalculatorHolder {
private final PermissionCalculator calculator; private final PermissionCalculator calculator;
private final Map<String, Boolean> permissions; private final Map<String, Boolean> permissions;
public CalculatorHolder(PermissionCalculator calculator) { public CalculatorHolder(PermissionCalculator calculator) {

View File

@ -23,52 +23,42 @@
* SOFTWARE. * SOFTWARE.
*/ */
package me.lucko.luckperms.sponge.service.persisted; package me.lucko.luckperms.sponge.service.internal;
import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.model.SpongeGroup;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
public final class OptionLookupKey { import org.spongepowered.api.command.CommandSource;
public static OptionLookupKey of(String key, ImmutableContextSet contexts) { import java.util.Optional;
return new OptionLookupKey(key, contexts);
}
private final String key; /**
private final ImmutableContextSet contexts; * Implements {@link LPSubject} for a {@link SpongeGroup}.
*/
private OptionLookupKey(String key, ImmutableContextSet contexts) { public class GroupSubject extends HolderSubject<SpongeGroup> implements LPSubject {
this.key = key; public GroupSubject(LPSpongePlugin plugin, SpongeGroup parent) {
this.contexts = contexts; super(plugin, parent);
}
public String getKey() {
return this.key;
}
public ImmutableContextSet getContexts() {
return this.contexts;
} }
@Override @Override
public boolean equals(Object o) { public String getIdentifier() {
if (o == this) return true; return this.parent.getObjectName();
if (!(o instanceof OptionLookupKey)) return false;
final OptionLookupKey other = (OptionLookupKey) o;
return this.getKey().equals(other.getKey()) && this.getContexts().equals(other.getContexts());
} }
@Override @Override
public int hashCode() { public Optional<String> getFriendlyIdentifier() {
final int PRIME = 59; return this.parent.getDisplayName();
int result = 1;
result = result * PRIME + this.getKey().hashCode();
result = result * PRIME + this.getContexts().hashCode();
return result;
} }
@Override @Override
public String toString() { public Optional<CommandSource> getCommandSource() {
return "OptionLookupKey(key=" + this.getKey() + ", contexts=" + this.getContexts() + ")"; return Optional.empty();
}
@Override
public LPSubjectCollection getParentCollection() {
return this.plugin.getService().getGroupSubjects();
} }
} }

View File

@ -0,0 +1,156 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.sponge.service.internal;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.caching.MetaData;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.model.NodeMapType;
import me.lucko.luckperms.common.model.PermissionHolder;
import me.lucko.luckperms.common.node.NodeFactory;
import me.lucko.luckperms.common.verbose.CheckOrigin;
import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.ProxyFactory;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.reference.LPSubjectReference;
import org.spongepowered.api.service.permission.PermissionService;
import org.spongepowered.api.service.permission.Subject;
import java.util.Map;
import java.util.Optional;
/**
* Implements {@link LPSubject} for a {@link PermissionHolder}.
*/
public abstract class HolderSubject<T extends PermissionHolder> implements LPSubject {
protected final T parent;
protected final LPSpongePlugin plugin;
private final HolderSubjectData subjectData;
private final HolderSubjectData transientSubjectData;
HolderSubject(LPSpongePlugin plugin, T parent) {
this.parent = parent;
this.plugin = plugin;
this.subjectData = new HolderSubjectData(plugin.getService(), NodeMapType.ENDURING, parent, this);
this.transientSubjectData = new HolderSubjectData(plugin.getService(), NodeMapType.TRANSIENT, parent, this);
}
@Override
public Subject sponge() {
return ProxyFactory.toSponge(this);
}
@Override
public LuckPermsService getService() {
return this.plugin.getService();
}
@Override
public HolderSubjectData getSubjectData() {
return this.subjectData;
}
@Override
public HolderSubjectData getTransientSubjectData() {
return this.transientSubjectData;
}
@Override
public Tristate getPermissionValue(ImmutableContextSet contexts, String permission) {
return this.parent.getCachedData().getPermissionData(this.plugin.getContextManager().formContexts(contexts)).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK);
}
@Override
public boolean isChildOf(ImmutableContextSet contexts, LPSubjectReference parent) {
return parent.getCollectionIdentifier().equals(PermissionService.SUBJECTS_GROUP) && getPermissionValue(contexts, NodeFactory.groupNode(parent.getSubjectIdentifier())).asBoolean();
}
@Override
public ImmutableList<LPSubjectReference> getParents(ImmutableContextSet contexts) {
ImmutableSet.Builder<LPSubjectReference> subjects = ImmutableSet.builder();
for (Map.Entry<String, Boolean> entry : this.parent.getCachedData().getPermissionData(this.plugin.getContextManager().formContexts(contexts)).getImmutableBacking().entrySet()) {
if (!entry.getValue()) {
continue;
}
String groupName = NodeFactory.parseGroupNode(entry.getKey());
if (groupName == null) {
continue;
}
if (this.plugin.getGroupManager().isLoaded(groupName)) {
subjects.add(this.plugin.getService().getGroupSubjects().loadSubject(groupName).join().toReference());
}
}
subjects.addAll(getParentCollection().getDefaults().getParents(contexts));
subjects.addAll(this.plugin.getService().getDefaults().getParents(contexts));
return getService().sortSubjects(subjects.build());
}
@Override
public Optional<String> getOption(ImmutableContextSet contexts, String s) {
MetaData data = this.parent.getCachedData().getMetaData(this.plugin.getContextManager().formContexts(contexts));
if (s.equalsIgnoreCase(NodeFactory.PREFIX_KEY)) {
if (data.getPrefix() != null) {
return Optional.of(data.getPrefix());
}
}
if (s.equalsIgnoreCase(NodeFactory.SUFFIX_KEY)) {
if (data.getSuffix() != null) {
return Optional.of(data.getSuffix());
}
}
String val = data.getMeta().get(s);
if (val != null) {
return Optional.of(val);
}
Optional<String> v = getParentCollection().getDefaults().getOption(contexts, s);
if (v.isPresent()) {
return v;
}
return this.plugin.getService().getDefaults().getOption(contexts, s);
}
@Override
public void invalidateCaches(CacheLevel cacheLevel) {
// invalidate for all changes
this.parent.getCachedData().invalidateCaches();
}
}

View File

@ -23,7 +23,7 @@
* SOFTWARE. * SOFTWARE.
*/ */
package me.lucko.luckperms.sponge.service; package me.lucko.luckperms.sponge.service.internal;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
@ -35,9 +35,11 @@ import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.caching.type.MetaAccumulator; import me.lucko.luckperms.common.caching.type.MetaAccumulator;
import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.NodeMapType;
import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.model.PermissionHolder;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.node.NodeFactory; import me.lucko.luckperms.common.node.NodeFactory;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectData; import me.lucko.luckperms.sponge.service.model.LPSubjectData;
import me.lucko.luckperms.sponge.service.reference.LPSubjectReference; import me.lucko.luckperms.sponge.service.reference.LPSubjectReference;
@ -50,25 +52,27 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
public class LuckPermsSubjectData implements LPSubjectData { public class HolderSubjectData implements LPSubjectData {
private final boolean enduring;
private final LuckPermsService service; private final LuckPermsService service;
private final NodeMapType type;
private final PermissionHolder holder; private final PermissionHolder holder;
private final LPSubject parentSubject; private final LPSubject parentSubject;
public LuckPermsSubjectData(boolean enduring, LuckPermsService service, PermissionHolder holder, LPSubject parentSubject) { public HolderSubjectData(LuckPermsService service, NodeMapType type, PermissionHolder holder, LPSubject parentSubject) {
this.enduring = enduring; this.type = type;
this.service = service; this.service = service;
this.holder = holder; this.holder = holder;
this.parentSubject = parentSubject; this.parentSubject = parentSubject;
} }
private Stream<Node> streamNodes() {
return this.holder.getNodes(this.type).values().stream();
}
@Override @Override
public LPSubject getParentSubject() { public LPSubject getParentSubject() {
return this.parentSubject; return this.parentSubject;
@ -76,21 +80,15 @@ public class LuckPermsSubjectData implements LPSubjectData {
@Override @Override
public ImmutableMap<ImmutableContextSet, ImmutableMap<String, Boolean>> getAllPermissions() { public ImmutableMap<ImmutableContextSet, ImmutableMap<String, Boolean>> getAllPermissions() {
Map<ImmutableContextSet, ImmutableMap.Builder<String, Boolean>> perms = new HashMap<>(); ImmutableMap.Builder<ImmutableContextSet, ImmutableMap<String, Boolean>> ret = ImmutableMap.builder();
for (Map.Entry<ImmutableContextSet, Collection<Node>> entry : this.holder.getNodes(this.type).asMap().entrySet()) {
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : (this.enduring ? this.holder.getEnduringNodes() : this.holder.getTransientNodes()).asMap().entrySet()) { ImmutableMap.Builder<String, Boolean> builder = ImmutableMap.builder();
ImmutableMap.Builder<String, Boolean> results = ImmutableMap.builder(); for (Node n : entry.getValue()) {
for (Node n : e.getValue()) { builder.put(n.getPermission(), n.getValuePrimitive());
results.put(n.getPermission(), n.getValuePrimitive());
} }
perms.put(e.getKey(), results); ret.put(entry.getKey(), builder.build());
} }
return ret.build();
ImmutableMap.Builder<ImmutableContextSet, ImmutableMap<String, Boolean>> map = ImmutableMap.builder();
for (Map.Entry<ImmutableContextSet, ImmutableMap.Builder<String, Boolean>> e : perms.entrySet()) {
map.put(e.getKey(), e.getValue().build());
}
return map.build();
} }
@Override @Override
@ -102,42 +100,35 @@ public class LuckPermsSubjectData implements LPSubjectData {
if (tristate == Tristate.UNDEFINED) { if (tristate == Tristate.UNDEFINED) {
// Unset // Unset
Node node = NodeFactory.builder(permission).withExtraContext(contexts).build(); Node node = NodeFactory.builder(permission).withExtraContext(contexts).build();
this.type.run(
if (this.enduring) { () -> this.holder.unsetPermission(node),
this.holder.unsetPermission(node); () -> this.holder.unsetTransientPermission(node)
} else { );
this.holder.unsetTransientPermission(node);
}
return objectSave(this.holder).thenApply(v -> true); return objectSave(this.holder).thenApply(v -> true);
} }
Node node = NodeFactory.builder(permission).setValue(tristate.asBoolean()).withExtraContext(contexts).build(); Node node = NodeFactory.builder(permission).setValue(tristate.asBoolean()).withExtraContext(contexts).build();
this.type.run(
// Workaround: unset the inverse, to allow false -> true, true -> false overrides. () -> {
if (this.enduring) { // unset the inverse, to allow false -> true, true -> false overrides.
this.holder.unsetPermission(node); this.holder.unsetPermission(node);
} else { this.holder.setPermission(node);
this.holder.unsetTransientPermission(node); },
} () -> {
// unset the inverse, to allow false -> true, true -> false overrides.
if (this.enduring) { this.holder.unsetTransientPermission(node);
this.holder.setPermission(node); this.holder.setTransientPermission(node);
} else { }
this.holder.setTransientPermission(node); );
}
return objectSave(this.holder).thenApply(v -> true); return objectSave(this.holder).thenApply(v -> true);
} }
@Override @Override
public CompletableFuture<Boolean> clearPermissions() { public CompletableFuture<Boolean> clearPermissions() {
boolean ret; boolean ret = this.type.supply(
if (this.enduring) { this.holder::clearNodes,
ret = this.holder.clearNodes(); this.holder::clearTransientNodes
} else { );
ret = this.holder.clearTransientNodes();
}
if (!ret) { if (!ret) {
return CompletableFuture.completedFuture(false); return CompletableFuture.completedFuture(false);
@ -153,18 +144,17 @@ public class LuckPermsSubjectData implements LPSubjectData {
@Override @Override
public CompletableFuture<Boolean> clearPermissions(ImmutableContextSet contexts) { public CompletableFuture<Boolean> clearPermissions(ImmutableContextSet contexts) {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
boolean ret = this.type.supply(
() -> this.holder.clearNodes(contexts),
() -> {
List<Node> toRemove = streamNodes()
.filter(n -> n.getFullContexts().equals(contexts))
.collect(Collectors.toList());
boolean ret; toRemove.forEach(this.holder::unsetTransientPermission);
if (this.enduring) { return !toRemove.isEmpty();
ret = this.holder.clearNodes(contexts); }
} else { );
List<Node> toRemove = streamNodes(false)
.filter(n -> n.getFullContexts().equals(contexts))
.collect(Collectors.toList());
toRemove.forEach(makeUnsetConsumer(false));
ret = !toRemove.isEmpty();
}
if (!ret) { if (!ret) {
return CompletableFuture.completedFuture(false); return CompletableFuture.completedFuture(false);
@ -179,23 +169,17 @@ public class LuckPermsSubjectData implements LPSubjectData {
@Override @Override
public ImmutableMap<ImmutableContextSet, ImmutableList<LPSubjectReference>> getAllParents() { public ImmutableMap<ImmutableContextSet, ImmutableList<LPSubjectReference>> getAllParents() {
Map<ImmutableContextSet, ImmutableList.Builder<LPSubjectReference>> parents = new HashMap<>(); ImmutableMap.Builder<ImmutableContextSet, ImmutableList<LPSubjectReference>> ret = ImmutableMap.builder();
for (Map.Entry<ImmutableContextSet, Collection<Node>> entry : this.holder.getNodes(this.type).asMap().entrySet()) {
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : (this.enduring ? this.holder.getEnduringNodes() : this.holder.getTransientNodes()).asMap().entrySet()) { ImmutableList.Builder<LPSubjectReference> builder = ImmutableList.builder();
ImmutableList.Builder<LPSubjectReference> results = ImmutableList.builder(); for (Node n : entry.getValue()) {
for (Node n : e.getValue()) {
if (n.isGroupNode()) { if (n.isGroupNode()) {
results.add(this.service.getGroupSubjects().loadSubject(n.getGroupName()).join().toReference()); builder.add(this.service.getGroupSubjects().loadSubject(n.getGroupName()).join().toReference());
} }
} }
parents.put(e.getKey(), results); ret.put(entry.getKey(), builder.build());
} }
return ret.build();
ImmutableMap.Builder<ImmutableContextSet, ImmutableList<LPSubjectReference>> map = ImmutableMap.builder();
for (Map.Entry<ImmutableContextSet, ImmutableList.Builder<LPSubjectReference>> e : parents.entrySet()) {
map.put(e.getKey(), e.getValue().build());
}
return map.build();
} }
@Override @Override
@ -203,28 +187,24 @@ public class LuckPermsSubjectData implements LPSubjectData {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
Objects.requireNonNull(subject, "subject"); Objects.requireNonNull(subject, "subject");
if (subject.getCollectionIdentifier().equals(PermissionService.SUBJECTS_GROUP)) { if (!subject.getCollectionIdentifier().equals(PermissionService.SUBJECTS_GROUP)) {
return subject.resolveLp().thenCompose(sub -> { return CompletableFuture.completedFuture(false);
DataMutateResult result;
if (this.enduring) {
result = this.holder.setPermission(NodeFactory.buildGroupNode(sub.getIdentifier())
.withExtraContext(contexts)
.build());
} else {
result = this.holder.setTransientPermission(NodeFactory.buildGroupNode(sub.getIdentifier())
.withExtraContext(contexts)
.build());
}
if (!result.asBoolean()) {
return CompletableFuture.completedFuture(false);
}
return objectSave(this.holder).thenApply(v -> true);
});
} }
return CompletableFuture.completedFuture(false);
Node node = NodeFactory.buildGroupNode(subject.getSubjectIdentifier())
.withExtraContext(contexts)
.build();
DataMutateResult result = this.type.supply(
() -> this.holder.setPermission(node),
() -> this.holder.setTransientPermission(node)
);
if (!result.asBoolean()) {
return CompletableFuture.completedFuture(false);
}
return objectSave(this.holder).thenApply(v -> true);
} }
@Override @Override
@ -232,47 +212,39 @@ public class LuckPermsSubjectData implements LPSubjectData {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
Objects.requireNonNull(subject, "subject"); Objects.requireNonNull(subject, "subject");
if (subject.getCollectionIdentifier().equals(PermissionService.SUBJECTS_GROUP)) { if (!subject.getCollectionIdentifier().equals(PermissionService.SUBJECTS_GROUP)) {
subject.resolveLp().thenCompose(sub -> { return CompletableFuture.completedFuture(false);
DataMutateResult result;
if (this.enduring) {
result = this.holder.unsetPermission(NodeFactory.buildGroupNode(sub.getIdentifier())
.withExtraContext(contexts)
.build());
} else {
result = this.holder.unsetTransientPermission(NodeFactory.buildGroupNode(sub.getIdentifier())
.withExtraContext(contexts)
.build());
}
if (!result.asBoolean()) {
return CompletableFuture.completedFuture(false);
}
return objectSave(this.holder).thenApply(v -> true);
});
} }
return CompletableFuture.completedFuture(false);
Node node = NodeFactory.buildGroupNode(subject.getSubjectIdentifier())
.withExtraContext(contexts)
.build();
DataMutateResult result = this.type.supply(
() -> this.holder.unsetPermission(node),
() -> this.holder.unsetTransientPermission(node)
);
if (!result.asBoolean()) {
return CompletableFuture.completedFuture(false);
}
return objectSave(this.holder).thenApply(v -> true);
} }
@Override @Override
public CompletableFuture<Boolean> clearParents() { public CompletableFuture<Boolean> clearParents() {
boolean ret; boolean ret = this.type.supply(
if (this.enduring) { () -> this.holder.clearParents(true),
ret = this.holder.clearParents(true); () -> {
} else { List<Node> toRemove = streamNodes()
List<Node> toRemove = streamNodes(false) .filter(Node::isGroupNode)
.filter(Node::isGroupNode) .collect(Collectors.toList());
.collect(Collectors.toList());
toRemove.forEach(makeUnsetConsumer(false)); toRemove.forEach(this.holder::unsetTransientPermission);
ret = !toRemove.isEmpty(); return !toRemove.isEmpty();
}
if (ret && this.holder.getType().isUser()) { );
this.service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) this.holder), false);
}
}
if (!ret) { if (!ret) {
return CompletableFuture.completedFuture(false); return CompletableFuture.completedFuture(false);
@ -284,23 +256,18 @@ public class LuckPermsSubjectData implements LPSubjectData {
@Override @Override
public CompletableFuture<Boolean> clearParents(ImmutableContextSet contexts) { public CompletableFuture<Boolean> clearParents(ImmutableContextSet contexts) {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
boolean ret = this.type.supply(
() -> this.holder.clearParents(contexts, true),
() -> {
List<Node> toRemove = streamNodes()
.filter(Node::isGroupNode)
.filter(n -> n.getFullContexts().equals(contexts))
.collect(Collectors.toList());
boolean ret; toRemove.forEach(this.holder::unsetTransientPermission);
if (this.enduring) { return !toRemove.isEmpty();
ret = this.holder.clearParents(contexts, true); }
} else { );
List<Node> toRemove = streamNodes(false)
.filter(Node::isGroupNode)
.filter(n -> n.getFullContexts().equals(contexts))
.collect(Collectors.toList());
toRemove.forEach(makeUnsetConsumer(false));
ret = !toRemove.isEmpty();
if (ret && this.holder.getType().isUser()) {
this.service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) this.holder), false);
}
}
if (!ret) { if (!ret) {
return CompletableFuture.completedFuture(false); return CompletableFuture.completedFuture(false);
@ -315,7 +282,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
Map<ImmutableContextSet, Integer> minPrefixPriority = new HashMap<>(); Map<ImmutableContextSet, Integer> minPrefixPriority = new HashMap<>();
Map<ImmutableContextSet, Integer> minSuffixPriority = new HashMap<>(); Map<ImmutableContextSet, Integer> minSuffixPriority = new HashMap<>();
for (Node n : this.enduring ? this.holder.getEnduringNodes().values() : this.holder.getTransientNodes().values()) { for (Node n : this.holder.getNodes(this.type).values()) {
if (!n.getValuePrimitive()) continue; if (!n.getValuePrimitive()) continue;
if (!n.isMeta() && !n.isPrefix() && !n.isSuffix()) continue; if (!n.isMeta() && !n.isPrefix() && !n.isSuffix()) continue;
@ -364,44 +331,46 @@ public class LuckPermsSubjectData implements LPSubjectData {
Objects.requireNonNull(key, "key"); Objects.requireNonNull(key, "key");
Objects.requireNonNull(value, "value"); Objects.requireNonNull(value, "value");
Node node;
if (key.equalsIgnoreCase(NodeFactory.PREFIX_KEY) || key.equalsIgnoreCase(NodeFactory.SUFFIX_KEY)) { if (key.equalsIgnoreCase(NodeFactory.PREFIX_KEY) || key.equalsIgnoreCase(NodeFactory.SUFFIX_KEY)) {
// special handling. // special handling.
ChatMetaType type = ChatMetaType.valueOf(key.toUpperCase()); ChatMetaType type = ChatMetaType.valueOf(key.toUpperCase());
// remove all prefixes/suffixes from the user // remove all prefixes/suffixes from the user
List<Node> toRemove = streamNodes(this.enduring) List<Node> toRemove = streamNodes()
.filter(type::matches) .filter(type::matches)
.filter(n -> n.getFullContexts().equals(contexts)) .filter(n -> n.getFullContexts().equals(contexts))
.collect(Collectors.toList()); .collect(Collectors.toList());
toRemove.forEach(makeUnsetConsumer(this.enduring)); toRemove.forEach(n -> this.type.run(
() -> this.holder.unsetPermission(n),
() -> this.holder.unsetTransientPermission(n)
));
MetaAccumulator metaAccumulator = this.holder.accumulateMeta(null, this.service.getPlugin().getContextManager().formContexts(contexts)); MetaAccumulator metaAccumulator = this.holder.accumulateMeta(null, this.service.getPlugin().getContextManager().formContexts(contexts));
int priority = metaAccumulator.getChatMeta(type).keySet().stream().mapToInt(e -> e).max().orElse(0); int priority = metaAccumulator.getChatMeta(type).keySet().stream().mapToInt(e -> e).max().orElse(0);
priority += 10; priority += 10;
if (this.enduring) { node = NodeFactory.buildChatMetaNode(type, priority, value).withExtraContext(contexts).build();
this.holder.setPermission(NodeFactory.buildChatMetaNode(type, priority, value).withExtraContext(contexts).build());
} else {
this.holder.setTransientPermission(NodeFactory.buildChatMetaNode(type, priority, value).withExtraContext(contexts).build());
}
} else { } else {
// standard remove // standard remove
List<Node> toRemove = streamNodes(this.enduring) List<Node> toRemove = streamNodes()
.filter(n -> n.isMeta() && n.getMeta().getKey().equals(key)) .filter(n -> n.isMeta() && n.getMeta().getKey().equals(key))
.filter(n -> n.getFullContexts().equals(contexts)) .filter(n -> n.getFullContexts().equals(contexts))
.collect(Collectors.toList()); .collect(Collectors.toList());
toRemove.forEach(makeUnsetConsumer(this.enduring)); toRemove.forEach(n -> this.type.run(
() -> this.holder.unsetPermission(n),
() -> this.holder.unsetTransientPermission(n)
));
if (this.enduring) { node = NodeFactory.buildMetaNode(key, value).withExtraContext(contexts).build();
this.holder.setPermission(NodeFactory.buildMetaNode(key, value).withExtraContext(contexts).build());
} else {
this.holder.setTransientPermission(NodeFactory.buildMetaNode(key, value).withExtraContext(contexts).build());
}
} }
this.type.run(
() -> this.holder.setPermission(node),
() -> this.holder.setTransientPermission(node)
);
return objectSave(this.holder).thenApply(v -> true); return objectSave(this.holder).thenApply(v -> true);
} }
@ -410,7 +379,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
Objects.requireNonNull(key, "key"); Objects.requireNonNull(key, "key");
List<Node> toRemove = streamNodes(this.enduring) List<Node> toRemove = streamNodes()
.filter(n -> { .filter(n -> {
if (key.equalsIgnoreCase(NodeFactory.PREFIX_KEY)) { if (key.equalsIgnoreCase(NodeFactory.PREFIX_KEY)) {
return n.isPrefix(); return n.isPrefix();
@ -423,7 +392,10 @@ public class LuckPermsSubjectData implements LPSubjectData {
.filter(n -> n.getFullContexts().equals(contexts)) .filter(n -> n.getFullContexts().equals(contexts))
.collect(Collectors.toList()); .collect(Collectors.toList());
toRemove.forEach(makeUnsetConsumer(this.enduring)); toRemove.forEach(node -> this.type.run(
() -> this.holder.unsetPermission(node),
() -> this.holder.unsetTransientPermission(node)
));
return objectSave(this.holder).thenApply(v -> true); return objectSave(this.holder).thenApply(v -> true);
} }
@ -432,43 +404,36 @@ public class LuckPermsSubjectData implements LPSubjectData {
public CompletableFuture<Boolean> clearOptions(ImmutableContextSet contexts) { public CompletableFuture<Boolean> clearOptions(ImmutableContextSet contexts) {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
List<Node> toRemove = streamNodes(this.enduring) List<Node> toRemove = streamNodes()
.filter(n -> n.isMeta() || n.isPrefix() || n.isSuffix()) .filter(n -> n.isMeta() || n.isPrefix() || n.isSuffix())
.filter(n -> n.getFullContexts().equals(contexts)) .filter(n -> n.getFullContexts().equals(contexts))
.collect(Collectors.toList()); .collect(Collectors.toList());
toRemove.forEach(makeUnsetConsumer(this.enduring)); toRemove.forEach(node -> this.type.run(
() -> this.holder.unsetPermission(node),
() -> this.holder.unsetTransientPermission(node)
));
return objectSave(this.holder).thenApply(v -> !toRemove.isEmpty()); return objectSave(this.holder).thenApply(v -> !toRemove.isEmpty());
} }
@Override @Override
public CompletableFuture<Boolean> clearOptions() { public CompletableFuture<Boolean> clearOptions() {
List<Node> toRemove = streamNodes(this.enduring) List<Node> toRemove = streamNodes()
.filter(n -> n.isMeta() || n.isPrefix() || n.isSuffix()) .filter(n -> n.isMeta() || n.isPrefix() || n.isSuffix())
.collect(Collectors.toList()); .collect(Collectors.toList());
toRemove.forEach(makeUnsetConsumer(this.enduring)); toRemove.forEach(node -> this.type.run(
() -> this.holder.unsetPermission(node),
() -> this.holder.unsetTransientPermission(node)
));
return objectSave(this.holder).thenApply(v -> !toRemove.isEmpty()); return objectSave(this.holder).thenApply(v -> !toRemove.isEmpty());
} }
private Stream<Node> streamNodes(boolean enduring) {
return (enduring ? this.holder.getEnduringNodes() : this.holder.getTransientNodes()).values().stream();
}
private Consumer<Node> makeUnsetConsumer(boolean enduring) {
return n -> {
if (enduring) {
this.holder.unsetPermission(n);
} else {
this.holder.unsetTransientPermission(n);
}
};
}
private CompletableFuture<Void> objectSave(PermissionHolder t) { private CompletableFuture<Void> objectSave(PermissionHolder t) {
if (!this.enduring) { // handle transient first
if (this.type == NodeMapType.TRANSIENT) {
// don't bother saving to primary storage. just refresh // don't bother saving to primary storage. just refresh
if (t.getType().isUser()) { if (t.getType().isUser()) {
User user = ((User) t); User user = ((User) t);
@ -476,30 +441,31 @@ public class LuckPermsSubjectData implements LPSubjectData {
} else { } else {
return this.service.getPlugin().getUpdateTaskBuffer().request(); return this.service.getPlugin().getUpdateTaskBuffer().request();
} }
}
// handle enduring
if (t.getType().isUser()) {
User user = ((User) t);
CompletableFuture<Void> fut = new CompletableFuture<>();
this.service.getPlugin().getStorage().saveUser(user).whenCompleteAsync((v, ex) -> {
if (ex != null) {
fut.complete(null);
}
user.getRefreshBuffer().request().thenAccept(fut::complete);
}, this.service.getPlugin().getScheduler().async());
return fut;
} else { } else {
if (t.getType().isUser()) { Group group = ((Group) t);
User user = ((User) t); CompletableFuture<Void> fut = new CompletableFuture<>();
CompletableFuture<Void> fut = new CompletableFuture<>(); this.service.getPlugin().getStorage().saveGroup(group).whenCompleteAsync((v, ex) -> {
this.service.getPlugin().getStorage().saveUser(user).whenCompleteAsync((v, ex) -> { if (ex != null) {
if (ex != null) { fut.complete(null);
fut.complete(null); }
}
user.getRefreshBuffer().request().thenAccept(fut::complete); this.service.getPlugin().getUpdateTaskBuffer().request().thenAccept(fut::complete);
}, this.service.getPlugin().getScheduler().async()); }, this.service.getPlugin().getScheduler().async());
return fut; return fut;
} else {
Group group = ((Group) t);
CompletableFuture<Void> fut = new CompletableFuture<>();
this.service.getPlugin().getStorage().saveGroup(group).whenCompleteAsync((v, ex) -> {
if (ex != null) {
fut.complete(null);
}
this.service.getPlugin().getUpdateTaskBuffer().request().thenAccept(fut::complete);
}, this.service.getPlugin().getScheduler().async());
return fut;
}
} }
} }
} }

View File

@ -0,0 +1,68 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.sponge.service.internal;
import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.model.SpongeUser;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandSource;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Function;
/**
* Implements {@link LPSubject} for a {@link SpongeUser}.
*/
public final class UserSubject extends HolderSubject<SpongeUser> implements LPSubject {
public UserSubject(LPSpongePlugin plugin, SpongeUser parent) {
super(plugin, parent);
}
@Override
public String getIdentifier() {
return this.parent.getUuid().toString();
}
@Override
public Optional<String> getFriendlyIdentifier() {
return this.parent.getName();
}
@Override
public Optional<CommandSource> getCommandSource() {
final UUID uuid = this.parent.getUuid();
return Sponge.getServer().getPlayer(uuid).map(Function.identity());
}
@Override
public LPSubjectCollection getParentCollection() {
return this.plugin.getService().getUserSubjects();
}
}

View File

@ -1,88 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.sponge.service.legacy;
import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.service.storage.SubjectStorage;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@SuppressWarnings("deprecation")
public class LegacyDataMigrator implements Runnable {
private final LPSpongePlugin plugin;
private final File oldDirectory;
private final SubjectStorage storage;
public LegacyDataMigrator(LPSpongePlugin plugin, File oldDirectory, SubjectStorage storage) {
this.plugin = plugin;
this.oldDirectory = oldDirectory;
this.storage = storage;
}
@Override
public void run() {
if (!this.oldDirectory.exists() || !this.oldDirectory.isDirectory()) {
return;
}
this.plugin.getLog().warn("Migrating old sponge data... Please wait.");
File[] collections = this.oldDirectory.listFiles(File::isDirectory);
if (collections == null) {
return;
}
for (File collectionDir : collections) {
File[] subjects = collectionDir.listFiles((dir, name) -> name.endsWith(".json"));
if (subjects == null) {
continue;
}
for (File subjectFile : subjects) {
String subjectName = subjectFile.getName().substring(0, subjectFile.getName().length() - ".json".length());
try (BufferedReader reader = Files.newBufferedReader(subjectFile.toPath(), StandardCharsets.UTF_8)) {
SubjectDataHolder holder = this.storage.getGson().fromJson(reader, SubjectDataHolder.class);
this.storage.saveToFile(holder.asSubjectModel(this.plugin.getService()), this.storage.resolveFile(collectionDir.getName(), subjectName));
} catch (IOException e) {
e.printStackTrace();
}
subjectFile.delete();
}
collectionDir.delete();
}
this.oldDirectory.delete();
}
}

View File

@ -1,71 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.sponge.service.legacy;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
import me.lucko.luckperms.sponge.service.reference.SubjectReferenceFactory;
import me.lucko.luckperms.sponge.service.storage.SubjectStorageModel;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @deprecated Because this format is no longer being used to store data.
* @see SubjectStorageModel
*/
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
public class SubjectDataHolder {
private Map<Map<String, String>, Map<String, Boolean>> permissions;
private Map<Map<String, String>, Map<String, String>> options;
private Map<Map<String, String>, List<String>> parents;
public SubjectDataHolder() {
// For gson
}
public SubjectStorageModel asSubjectModel(LPPermissionService service) {
return new SubjectStorageModel(service,
this.permissions.entrySet().stream()
.collect(Collectors.toMap(
k -> ImmutableContextSet.fromMap(k.getKey()),
Map.Entry::getValue
)),
this.options.entrySet().stream()
.collect(Collectors.toMap(
k -> ImmutableContextSet.fromMap(k.getKey()),
Map.Entry::getValue
)),
this.parents.entrySet().stream()
.collect(Collectors.toMap(
k -> ImmutableContextSet.fromMap(k.getKey()),
v -> v.getValue().stream().map(s -> SubjectReferenceFactory.deserialize(service, s)).collect(Collectors.toList())
))
);
}
}

View File

@ -1,74 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.sponge.service.persisted;
import me.lucko.luckperms.api.context.ImmutableContextSet;
public final class PermissionLookupKey {
public static PermissionLookupKey of(String node, ImmutableContextSet contexts) {
return new PermissionLookupKey(node, contexts);
}
private final String node;
private final ImmutableContextSet contexts;
private PermissionLookupKey(String node, ImmutableContextSet contexts) {
this.node = node;
this.contexts = contexts;
}
public String getNode() {
return this.node;
}
public ImmutableContextSet getContexts() {
return this.contexts;
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof PermissionLookupKey)) return false;
final PermissionLookupKey other = (PermissionLookupKey) o;
return this.getNode().equals(other.getNode()) && this.getContexts().equals(other.getContexts());
}
@Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
result = result * PRIME + this.getNode().hashCode();
result = result * PRIME + this.getContexts().hashCode();
return result;
}
@Override
public String toString() {
return "PermissionLookupKey(node=" + this.getNode() + ", contexts=" + this.getContexts() + ")";
}
}

View File

@ -62,7 +62,7 @@ public class PersistedCollection implements LPSubjectCollection {
private final SubjectCollection spongeProxy; private final SubjectCollection spongeProxy;
private final LoadingCache<String, PersistedSubject> subjects = Caffeine.newBuilder() private final LoadingCache<String, PersistedSubject> subjects = Caffeine.newBuilder()
.build(s -> new PersistedSubject(s, getService(), PersistedCollection.this)); .build(s -> new PersistedSubject(s, getService(), this));
public PersistedCollection(LuckPermsService service, String identifier) { public PersistedCollection(LuckPermsService service, String identifier) {
this.service = service; this.service = service;

View File

@ -64,7 +64,7 @@ public class PersistedSubject implements LPSubject {
private final LoadingCache<PermissionLookupKey, Tristate> permissionLookupCache = Caffeine.newBuilder() private final LoadingCache<PermissionLookupKey, Tristate> permissionLookupCache = Caffeine.newBuilder()
.expireAfterAccess(20, TimeUnit.MINUTES) .expireAfterAccess(20, TimeUnit.MINUTES)
.build(lookup -> lookupPermissionValue(lookup.getContexts(), lookup.getNode())); .build(lookup -> lookupPermissionValue(lookup.contexts, lookup.node));
private final LoadingCache<ImmutableContextSet, ImmutableList<LPSubjectReference>> parentLookupCache = Caffeine.newBuilder() private final LoadingCache<ImmutableContextSet, ImmutableList<LPSubjectReference>> parentLookupCache = Caffeine.newBuilder()
.expireAfterAccess(20, TimeUnit.MINUTES) .expireAfterAccess(20, TimeUnit.MINUTES)
@ -72,7 +72,7 @@ public class PersistedSubject implements LPSubject {
private final LoadingCache<OptionLookupKey, Optional<String>> optionLookupCache = Caffeine.newBuilder() private final LoadingCache<OptionLookupKey, Optional<String>> optionLookupCache = Caffeine.newBuilder()
.expireAfterAccess(20, TimeUnit.MINUTES) .expireAfterAccess(20, TimeUnit.MINUTES)
.build(lookup -> lookupOptionValue(lookup.getContexts(), lookup.getKey())); .build(lookup -> lookupOptionValue(lookup.contexts, lookup.key));
private final BufferedRequest<Void> saveBuffer = new BufferedRequest<Void>(1000L, 500L, r -> PersistedSubject.this.service.getPlugin().getScheduler().doAsync(r)) { private final BufferedRequest<Void> saveBuffer = new BufferedRequest<Void>(1000L, 500L, r -> PersistedSubject.this.service.getPlugin().getScheduler().doAsync(r)) {
@Override @Override
@ -91,8 +91,9 @@ public class PersistedSubject implements LPSubject {
this.service = service; this.service = service;
this.parentCollection = parentCollection; this.parentCollection = parentCollection;
this.subjectData = new PersistedSubjectData(service, parentCollection.getIdentifier() + "/" + identifier + "/p", this); String displayName = parentCollection.getIdentifier() + "/" + identifier;
this.transientSubjectData = new CalculatedSubjectData(this, service, parentCollection.getIdentifier() + "/" + identifier + "/t"); this.subjectData = new PersistedSubjectData(service, displayName + "/p", this);
this.transientSubjectData = new CalculatedSubjectData(this, service, displayName + "/t");
} }
@Override @Override
@ -277,7 +278,7 @@ public class PersistedSubject implements LPSubject {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
Objects.requireNonNull(node, "node"); Objects.requireNonNull(node, "node");
Tristate t = this.permissionLookupCache.get(PermissionLookupKey.of(node, contexts)); Tristate t = this.permissionLookupCache.get(new PermissionLookupKey(node, contexts));
this.service.getPlugin().getVerboseHandler().offerCheckData(CheckOrigin.INTERNAL, getParentCollection().getIdentifier() + "/" + this.identifier, contexts, node, t); this.service.getPlugin().getVerboseHandler().offerCheckData(CheckOrigin.INTERNAL, getParentCollection().getIdentifier() + "/" + this.identifier, contexts, node, t);
return t; return t;
} }
@ -306,6 +307,54 @@ public class PersistedSubject implements LPSubject {
@Override @Override
public Optional<String> getOption(ImmutableContextSet contexts, String key) { public Optional<String> getOption(ImmutableContextSet contexts, String key) {
return this.optionLookupCache.get(OptionLookupKey.of(key, contexts)); return this.optionLookupCache.get(new OptionLookupKey(key, contexts));
}
private static final class PermissionLookupKey {
private final String node;
private final ImmutableContextSet contexts;
public PermissionLookupKey(String node, ImmutableContextSet contexts) {
this.node = node;
this.contexts = contexts;
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof PermissionLookupKey)) return false;
final PermissionLookupKey other = (PermissionLookupKey) o;
return this.node.equals(other.node) && this.contexts.equals(other.contexts);
}
@Override
public int hashCode() {
return Objects.hash(this.node, this.contexts);
}
}
public static final class OptionLookupKey {
private final String key;
private final ImmutableContextSet contexts;
public OptionLookupKey(String key, ImmutableContextSet contexts) {
this.key = key;
this.contexts = contexts;
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof OptionLookupKey)) return false;
final OptionLookupKey other = (OptionLookupKey) o;
return this.key.equals(other.key) && this.contexts.equals(other.contexts);
}
@Override
public int hashCode() {
return Objects.hash(this.key, this.contexts);
}
} }
} }

View File

@ -37,11 +37,16 @@ import java.util.function.Function;
/** /**
* Extension of MemorySubjectData which persists data when modified * Extension of MemorySubjectData which persists data when modified
*/ */
public class PersistedSubjectData extends CalculatedSubjectData implements Function<Boolean, Boolean> { public class PersistedSubjectData extends CalculatedSubjectData {
private final PersistedSubject subject; private final PersistedSubject subject;
private boolean save = true; private boolean save = true;
private final Function<Boolean, Boolean> saveFunction = b -> {
save();
return b;
};
public PersistedSubjectData(LuckPermsService service, String calculatorDisplayName, PersistedSubject subject) { public PersistedSubjectData(LuckPermsService service, String calculatorDisplayName, PersistedSubject subject) {
super(subject, service, calculatorDisplayName); super(subject, service, calculatorDisplayName);
this.subject = subject; this.subject = subject;
@ -57,72 +62,62 @@ public class PersistedSubjectData extends CalculatedSubjectData implements Funct
} }
} }
@Override public void setSave(boolean save) {
public Boolean apply(Boolean b) { this.save = save;
save();
return b;
} }
@Override @Override
public CompletableFuture<Boolean> setPermission(ImmutableContextSet contexts, String permission, Tristate value) { public CompletableFuture<Boolean> setPermission(ImmutableContextSet contexts, String permission, Tristate value) {
return super.setPermission(contexts, permission, value).thenApply(this); return super.setPermission(contexts, permission, value).thenApply(this.saveFunction);
} }
@Override @Override
public CompletableFuture<Boolean> clearPermissions() { public CompletableFuture<Boolean> clearPermissions() {
return super.clearPermissions().thenApply(this); return super.clearPermissions().thenApply(this.saveFunction);
} }
@Override @Override
public CompletableFuture<Boolean> clearPermissions(ImmutableContextSet contexts) { public CompletableFuture<Boolean> clearPermissions(ImmutableContextSet contexts) {
return super.clearPermissions(contexts).thenApply(this); return super.clearPermissions(contexts).thenApply(this.saveFunction);
} }
@Override @Override
public CompletableFuture<Boolean> addParent(ImmutableContextSet contexts, LPSubjectReference parent) { public CompletableFuture<Boolean> addParent(ImmutableContextSet contexts, LPSubjectReference parent) {
return super.addParent(contexts, parent).thenApply(this); return super.addParent(contexts, parent).thenApply(this.saveFunction);
} }
@Override @Override
public CompletableFuture<Boolean> removeParent(ImmutableContextSet contexts, LPSubjectReference parent) { public CompletableFuture<Boolean> removeParent(ImmutableContextSet contexts, LPSubjectReference parent) {
return super.removeParent(contexts, parent).thenApply(this); return super.removeParent(contexts, parent).thenApply(this.saveFunction);
} }
@Override @Override
public CompletableFuture<Boolean> clearParents() { public CompletableFuture<Boolean> clearParents() {
return super.clearParents().thenApply(this); return super.clearParents().thenApply(this.saveFunction);
} }
@Override @Override
public CompletableFuture<Boolean> clearParents(ImmutableContextSet contexts) { public CompletableFuture<Boolean> clearParents(ImmutableContextSet contexts) {
return super.clearParents(contexts).thenApply(this); return super.clearParents(contexts).thenApply(this.saveFunction);
} }
@Override @Override
public CompletableFuture<Boolean> setOption(ImmutableContextSet contexts, String key, String value) { public CompletableFuture<Boolean> setOption(ImmutableContextSet contexts, String key, String value) {
return super.setOption(contexts, key, value).thenApply(this); return super.setOption(contexts, key, value).thenApply(this.saveFunction);
} }
@Override @Override
public CompletableFuture<Boolean> unsetOption(ImmutableContextSet contexts, String key) { public CompletableFuture<Boolean> unsetOption(ImmutableContextSet contexts, String key) {
return super.unsetOption(contexts, key).thenApply(this); return super.unsetOption(contexts, key).thenApply(this.saveFunction);
} }
@Override @Override
public CompletableFuture<Boolean> clearOptions() { public CompletableFuture<Boolean> clearOptions() {
return super.clearOptions().thenApply(this); return super.clearOptions().thenApply(this.saveFunction);
} }
@Override @Override
public CompletableFuture<Boolean> clearOptions(ImmutableContextSet contexts) { public CompletableFuture<Boolean> clearOptions(ImmutableContextSet contexts) {
return super.clearOptions(contexts).thenApply(this); return super.clearOptions(contexts).thenApply(this.saveFunction);
}
public boolean isSave() {
return this.save;
}
public void setSave(boolean save) {
this.save = save;
} }
} }

View File

@ -50,11 +50,8 @@ import java.util.stream.Collectors;
* Handles persisted Subject I/O and (de)serialization * Handles persisted Subject I/O and (de)serialization
*/ */
public class SubjectStorage { public class SubjectStorage {
private final LPPermissionService service; private final LPPermissionService service;
private final Gson gson; private final Gson gson;
private final File container; private final File container;
public SubjectStorage(LPPermissionService service, File container) { public SubjectStorage(LPPermissionService service, File container) {
@ -158,8 +155,4 @@ public class SubjectStorage {
return Maps.immutableEntry(subjectName, model); return Maps.immutableEntry(subjectName, model);
} }
} }
public Gson getGson() {
return this.gson;
}
} }