/* * This file is part of GriefDefender, licensed under the MIT License (MIT). * * Copyright (c) bloodmc * 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 com.griefdefender.provider; import com.google.common.collect.ImmutableSet; import com.griefdefender.GDPlayerData; import com.griefdefender.GriefDefenderPlugin; import com.griefdefender.api.Tristate; import com.griefdefender.api.claim.Claim; import com.griefdefender.api.permission.Context; import com.griefdefender.api.permission.ContextKeys; import com.griefdefender.api.permission.PermissionResult; import com.griefdefender.api.permission.ResultTypes; import com.griefdefender.api.permission.flag.FlagData; import com.griefdefender.api.permission.flag.FlagDefinition; import com.griefdefender.api.permission.option.Option; import com.griefdefender.cache.PermissionHolderCache; import com.griefdefender.claim.ClaimContextCalculator; import com.griefdefender.claim.GDClaim; import com.griefdefender.listener.LuckPermsEventHandler; import com.griefdefender.permission.GDPermissionGroup; import com.griefdefender.permission.GDPermissionHolder; import com.griefdefender.permission.GDPermissionResult; import com.griefdefender.permission.GDPermissionUser; import com.griefdefender.registry.OptionRegistryModule; import com.griefdefender.util.PermissionUtil; import net.kyori.text.TextComponent; import net.luckperms.api.LuckPerms; import net.luckperms.api.cacheddata.CachedMetaData; import net.luckperms.api.cacheddata.CachedPermissionData; import net.luckperms.api.context.ContextSet; import net.luckperms.api.context.ImmutableContextSet; import net.luckperms.api.context.MutableContextSet; import net.luckperms.api.model.PermissionHolder; import net.luckperms.api.model.PermissionHolder.Identifier; import net.luckperms.api.model.data.DataMutateResult; import net.luckperms.api.model.data.DataType; import net.luckperms.api.model.group.Group; import net.luckperms.api.model.user.User; import net.luckperms.api.node.Node; import net.luckperms.api.node.NodeType; import net.luckperms.api.node.types.MetaNode; import net.luckperms.api.node.types.PermissionNode; import net.luckperms.api.query.QueryMode; import net.luckperms.api.query.QueryOptions; import net.luckperms.api.query.dataorder.DataQueryOrder; import net.luckperms.api.query.dataorder.DataQueryOrderFunction; import net.luckperms.api.query.dataorder.DataTypeFilter; import net.luckperms.api.query.dataorder.DataTypeFilterFunction; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.UUID; import java.util.Map.Entry; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.function.Predicate; import org.checkerframework.checker.nullness.qual.NonNull; import org.spongepowered.api.Sponge; import org.spongepowered.api.service.ProviderRegistration; public class LuckPermsProvider implements PermissionProvider { public static Comparator> CONTEXT_COMPARATOR = new Comparator>() { @Override public int compare(Set s1, Set s2) { if (s1.size() > s2.size()) { return -1; } if (s1.size() < s2.size()) { return 1; } return s1.equals(s2) ? 0 : -1; } }; private final LuckPerms luckPermsApi; private final static DefaultDataQueryOrderFunction DEFAULT_DATA_QUERY_ORDER = new DefaultDataQueryOrderFunction(); private final static DefaultPersistentOnlyDataFilter DEFAULT_PERSISTENT_ONLY = new DefaultPersistentOnlyDataFilter(); private final static DefaultTransientOnlyDataFilter DEFAULT_TRANSIENT_ONLY = new DefaultTransientOnlyDataFilter(); private final static UserPersistentOnlyDataFilter USER_PERSISTENT_ONLY = new UserPersistentOnlyDataFilter(); public LuckPermsProvider() { final ProviderRegistration service = Sponge.getServiceManager().getRegistration(LuckPerms.class).orElse(null); this.luckPermsApi = service.getProvider(); new LuckPermsEventHandler(this.luckPermsApi); } public LuckPerms getApi() { return this.luckPermsApi; } @Override public String getServerName() { return this.luckPermsApi.getServerName(); } @Override public boolean hasGroupSubject(String identifier) { return this.getGroupSubject(identifier) != null; } public PermissionHolder getLuckPermsHolder(GDPermissionHolder holder) { if (holder instanceof GDPermissionUser) { return this.getLuckPermsUser(holder.getIdentifier()); } return this.getLuckPermsGroup(holder.getIdentifier()); } public User getLuckPermsUser(String identifier) { User user = null; UUID uuid = null; if (identifier.length() == 36) { try { uuid = UUID.fromString(identifier); } catch (IllegalArgumentException e) { e.printStackTrace(); } } else { user = this.luckPermsApi.getUserManager().getUser(identifier); if (user == null) { try { uuid = this.luckPermsApi.getUserManager().lookupUniqueId(identifier).get(); } catch (Throwable t) { // ignore } } } if (uuid != null) { user = this.getUserSubject(uuid); } return user; } public boolean createDefaultGroup(String identifier) { Group group = this.luckPermsApi.getGroupManager().getGroup(identifier); if (group == null) { try { GriefDefenderPlugin.getInstance().getLogger().info("Creating group '" + identifier + "' in LuckPerms..."); final String dataType = identifier.replaceAll("griefdefender_", ""); group = this.luckPermsApi.getGroupManager().createAndLoadGroup(identifier).get(); GriefDefenderPlugin.getInstance().getLogger().info("Group created successfully."); if (group != null) { final Group defaultGroup = this.luckPermsApi.getGroupManager().getGroup("default"); if (defaultGroup != null) { GriefDefenderPlugin.getInstance().getLogger().info("Migrating legacy " + dataType + " permissions from 'default' to '" + identifier + "'..."); final Node node = this.luckPermsApi.getNodeBuilderRegistry().forInheritance().group(group).build(); // move all existing GD permissions to new group List nodesToRemove = new ArrayList<>(); for (Node permNode : defaultGroup.data().toCollection()) { if (!permNode.getKey().contains("griefdefender")) { continue; } if (permNode.getType() == NodeType.META) { if (permNode.getKey().contains("griefdefender") && identifier.equals(GriefDefenderPlugin.GD_OPTION_GROUP_NAME)) { GriefDefenderPlugin.getInstance().getLogger().info("Found legacy option node [Key: " + permNode.getKey() + ", Value: " + permNode.getValue() + ", Contexts: " + permNode.getContexts() + "].\nAdding option node to group '" + identifier + "'..."); group.data().add(permNode); nodesToRemove.add(permNode); } continue; } if (permNode.getType() != NodeType.PERMISSION) { continue; } if (identifier.equals(GriefDefenderPlugin.GD_OVERRIDE_GROUP_NAME)) { if (permNode.getContexts().containsKey(ContextKeys.CLAIM_OVERRIDE)) { GriefDefenderPlugin.getInstance().getLogger().info("Found legacy override permission node [Key: " + permNode.getKey() + ", Value: " + permNode.getValue() + ", Contexts: " + permNode.getContexts() + "].\nAdding permission node to group '" + identifier + "'..."); group.data().add(permNode); nodesToRemove.add(permNode); } continue; } else if (identifier.equals(GriefDefenderPlugin.GD_CLAIM_GROUP_NAME)) { if (permNode.getContexts().containsKey(ContextKeys.CLAIM)) { GriefDefenderPlugin.getInstance().getLogger().info("Found legacy claim permission node [Key: " + permNode.getKey() + ", Value: " + permNode.getValue() + ", Contexts: " + permNode.getContexts() + "].\nAdding permission node to group '" + identifier + "'..."); group.data().add(permNode); nodesToRemove.add(permNode); } continue; } else if (identifier.equals(GriefDefenderPlugin.GD_DEFAULT_GROUP_NAME)) { if (!permNode.getContexts().isEmpty()) { GriefDefenderPlugin.getInstance().getLogger().info("Found legacy default permission node [Key: " + permNode.getKey() + ", Value: " + permNode.getValue() + ", Contexts: " + permNode.getContexts() + "].\nAdding permission node to group '" + identifier + "'..."); group.data().add(permNode); nodesToRemove.add(permNode); } } } if (!nodesToRemove.isEmpty()) { GriefDefenderPlugin.getInstance().getLogger().info("Removing legacy permission nodes from 'default' group..."); } for (Node rem : nodesToRemove) { defaultGroup.data().remove(rem); } if (!nodesToRemove.isEmpty()) { GriefDefenderPlugin.getInstance().getLogger().info("Cleanup complete."); } final DataMutateResult result = defaultGroup.data().add(node); GriefDefenderPlugin.getInstance().getLogger().info("Saving permission changes to 'default'..."); this.luckPermsApi.getGroupManager().saveGroup(defaultGroup).get(); GriefDefenderPlugin.getInstance().getLogger().info("Saving permission changes to '" + identifier + "'..."); this.luckPermsApi.getGroupManager().saveGroup(group).get(); GriefDefenderPlugin.getInstance().getLogger().info("Migration to group '" + identifier + "' complete."); } } } catch (Throwable t) { t.printStackTrace(); } } return group != null; } public Group getLuckPermsGroup(String identifier) { return this.luckPermsApi.getGroupManager().getGroup(identifier); } public Group getGroupSubject(String identifier) { final Group group = this.luckPermsApi.getGroupManager().getGroup(identifier); if (group != null) { return group; } try { return this.luckPermsApi.getGroupManager().loadGroup(identifier).get().orElse(null); } catch (InterruptedException e) { return null; } catch (ExecutionException e) { return null; } } public UUID lookupUserUniqueId(String name) { final User user = this.getLuckPermsUser(name); if (user != null) { return user.getUniqueId(); } return null; } public User getUserSubject(UUID uuid) { User user = this.luckPermsApi.getUserManager().getUser(uuid); if (user != null) { return user; } try { user = this.luckPermsApi.getUserManager().loadUser(uuid).get(); if (user != null) { return user; } } catch (InterruptedException e) { return null; } catch (ExecutionException e) { return null; } return null; } public List getAllLoadedPlayerNames() { List subjectList = new ArrayList<>(); for (User user : this.luckPermsApi.getUserManager().getLoadedUsers()) { final String name = user.getUsername(); if (name != null) { subjectList.add(name); } } if (!subjectList.contains("public")) { subjectList.add("public"); } return subjectList; } public List getAllLoadedGroupNames() { List subjectList = new ArrayList<>(); for (Group group : this.luckPermsApi.getGroupManager().getLoadedGroups()) { final String name = group.getName(); if (name != null) { subjectList.add(name); } } if (!subjectList.contains("public")) { subjectList.add("public"); } return subjectList; } public void addActiveContexts(Set contexts, GDPermissionHolder permissionHolder) { addActiveContexts(contexts, permissionHolder, null, null); } public void addActiveContexts(Set contexts, GDPermissionHolder permissionHolder, GDPlayerData playerData, Claim claim) { if (playerData != null) { playerData.ignoreActiveContexts = true; } final PermissionHolder luckPermsHolder = this.getLuckPermsHolder(permissionHolder); if (luckPermsHolder instanceof Group) { contexts.addAll(this.getGDContexts(this.luckPermsApi.getContextManager().getStaticContext().mutableCopy())); return; } ImmutableContextSet contextSet = this.luckPermsApi.getContextManager().getContext((User) luckPermsHolder).orElse(null); if (contextSet == null) { contextSet = this.luckPermsApi.getContextManager().getStaticContext(); } if (contextSet == null) { return; } MutableContextSet activeContexts = contextSet.mutableCopy(); if (playerData != null && claim != null) { final Claim parent = claim.getParent().orElse(null); if (parent != null && claim.getData() != null && claim.getData().doesInheritParent()) { activeContexts.remove(parent.getContext().getKey(), parent.getContext().getValue()); } else { activeContexts.remove(claim.getContext().getKey(), claim.getContext().getValue()); } } contexts.addAll(this.getGDContexts(activeContexts)); } public void clearPermissions(GDClaim claim) { // check default holder this.clearPermission(claim.getUniqueId(), GriefDefenderPlugin.GD_CLAIM_HOLDER); // check loaded groups for (Group group : this.luckPermsApi.getGroupManager().getLoadedGroups()) { if (group.getName().equalsIgnoreCase(GriefDefenderPlugin.DEFAULT_GROUP_NAME) || group.getName().startsWith("griefdefender_")) { continue; } final GDPermissionHolder holder = PermissionHolderCache.getInstance().getOrCreateGroup(group.getName()); if (holder == null) { continue; } this.clearPermission(claim.getUniqueId(), holder); } // check user trusts for (UUID uuid : claim.getUserTrusts()) { if (uuid.equals(GriefDefenderPlugin.PUBLIC_UUID) || uuid.equals(GriefDefenderPlugin.WORLD_USER_UUID) || uuid.equals(GriefDefenderPlugin.ADMIN_USER_UUID)) { continue; } final GDPermissionHolder holder = PermissionHolderCache.getInstance().getOrCreateUser(uuid); if (holder == null) { continue; } this.clearPermission(claim.getUniqueId(), holder); } } private void clearPermission(UUID claimUniqueId, GDPermissionHolder holder) { Map, Map> permissionMap = this.getPermanentPermissions(holder); for (Entry, Map> mapEntry : permissionMap.entrySet()) { for (Context context : mapEntry.getKey()) { if (context.getKey().equalsIgnoreCase("gd_claim") && context.getValue().equalsIgnoreCase(claimUniqueId.toString())) { this.clearPermissions(holder, mapEntry.getKey()); break; } } } Map, Map>> optionMap = this.getPermanentOptions(holder); for (Entry, Map>> mapEntry : optionMap.entrySet()) { for (Context context : mapEntry.getKey()) { if (context.getKey().equalsIgnoreCase("gd_claim") && context.getValue().equalsIgnoreCase(claimUniqueId.toString())) { this.clearPermissions(holder, mapEntry.getKey()); break; } } } } public void clearPermissions(GDPermissionHolder holder, Context context) { clearPermissions(holder, ImmutableSet.of(context)); } public void clearPermissions(GDPermissionHolder holder, Set contexts) { final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder); if (permissionHolder == null) { return; } ImmutableContextSet set = this.getLPContexts(contexts).immutableCopy(); permissionHolder.data().clear(set); this.savePermissionHolder(permissionHolder); } public boolean holderHasPermission(GDPermissionHolder holder, String permission) { final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder); if (permissionHolder == null) { return false; } final QueryOptions query = QueryOptions.builder(QueryMode.CONTEXTUAL).option(DataQueryOrderFunction.KEY, DEFAULT_DATA_QUERY_ORDER).build(); return permissionHolder.getCachedData().getPermissionData(query).checkPermission(permission).asBoolean(); } public Map getPermissions(GDPermissionHolder holder, Set contexts) { ImmutableContextSet set = this.getLPContexts(contexts).immutableCopy(); final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder); if (permissionHolder == null) { return new HashMap<>(); } final QueryOptions query = QueryOptions.builder(QueryMode.CONTEXTUAL).option(DataQueryOrderFunction.KEY, DEFAULT_DATA_QUERY_ORDER).context(set).build(); CachedPermissionData cachedData = permissionHolder.getCachedData().getPermissionData(query); return cachedData.getPermissionMap(); } public Map> getOptions(GDPermissionHolder holder, Set contexts) { ImmutableContextSet set = this.getLPContexts(contexts).immutableCopy(); final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder); if (permissionHolder == null) { return new HashMap<>(); } final QueryOptions query = QueryOptions.builder(QueryMode.CONTEXTUAL).option(DataQueryOrderFunction.KEY, DEFAULT_DATA_QUERY_ORDER).context(set).build(); CachedMetaData cachedData = permissionHolder.getCachedData().getMetaData(query); // TODO Map> metaMap = new HashMap<>(); for (Map.Entry> mapEntry : cachedData.getMeta().entrySet()) { metaMap.put(mapEntry.getKey(), mapEntry.getValue()); } return metaMap; } public Map, Map> getPermanentPermissions(GDPermissionHolder holder) { final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder); if (permissionHolder == null) { return new HashMap<>(); } final Collection nodes = permissionHolder.data().toCollection(); Map, Map> permanentPermissionMap = new TreeMap, Map>(CONTEXT_COMPARATOR); for (Node node : nodes) { if (node.getType() != NodeType.PERMISSION) { continue; } final PermissionNode permissionNode = (PermissionNode) node; final Set contexts = getGPContexts(node.getContexts()); Map permissionEntry = permanentPermissionMap.get(contexts); if (permissionEntry == null) { permissionEntry = new HashMap<>(); permissionEntry.put(permissionNode.getPermission(), node.getValue()); permanentPermissionMap.put(contexts, permissionEntry); } else { permissionEntry.put(permissionNode.getPermission(), node.getValue()); } } return permanentPermissionMap; } public Map, Map> getAllPermanentPermissions() { Map, Map> permanentPermissionMap = new TreeMap, Map>(CONTEXT_COMPARATOR); this.addAllPermanentPermissions(this.getLuckPermsHolder(GriefDefenderPlugin.GD_CLAIM_HOLDER), permanentPermissionMap); this.addAllPermanentPermissions(this.getLuckPermsHolder(GriefDefenderPlugin.GD_DEFAULT_HOLDER), permanentPermissionMap); this.addAllPermanentPermissions(this.getLuckPermsHolder(GriefDefenderPlugin.GD_DEFINITION_HOLDER), permanentPermissionMap); this.addAllPermanentPermissions(this.getLuckPermsHolder(GriefDefenderPlugin.GD_OPTION_HOLDER), permanentPermissionMap); this.addAllPermanentPermissions(this.getLuckPermsHolder(GriefDefenderPlugin.GD_OVERRIDE_HOLDER), permanentPermissionMap); return permanentPermissionMap; } private Map, Map> addAllPermanentPermissions(PermissionHolder holder, Map, Map> permanentPermissionMap) { final Collection nodes = holder.data().toCollection(); for (Node node : nodes) { if (node.getType() != NodeType.PERMISSION) { continue; } final PermissionNode permissionNode = (PermissionNode) node; final Set contexts = getGPContexts(node.getContexts()); Map permissionEntry = permanentPermissionMap.get(contexts); if (permissionEntry == null) { permissionEntry = new HashMap<>(); permissionEntry.put(permissionNode.getPermission(), node.getValue()); permanentPermissionMap.put(contexts, permissionEntry); } else { permissionEntry.put(permissionNode.getPermission(), node.getValue()); } } return permanentPermissionMap; } public Map, Map> getTransientPermissions(GDPermissionHolder holder) { final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder); if (permissionHolder == null) { return new HashMap<>(); } final Collection nodes = permissionHolder.transientData().toCollection(); Map, Map> transientPermissionMap = new TreeMap, Map>(CONTEXT_COMPARATOR); for (Node node : nodes) { if (node.getType() != NodeType.PERMISSION) { continue; } final PermissionNode permissionNode = (PermissionNode) node; final Set contexts = getGPContexts(node.getContexts()); Map permissionEntry = transientPermissionMap.get(contexts); if (permissionEntry == null) { permissionEntry = new HashMap<>(); permissionEntry.put(permissionNode.getPermission(), node.getValue()); transientPermissionMap.put(contexts, permissionEntry); } else { permissionEntry.put(permissionNode.getPermission(), node.getValue()); } } return transientPermissionMap; } public Map, Map>> getPermanentOptions(GDPermissionHolder holder) { final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder); if (permissionHolder == null) { return new HashMap<>(); } final Collection nodes = permissionHolder.data().toCollection(); Map, Map>> permanentPermissionMap = new TreeMap, Map>>(CONTEXT_COMPARATOR); for (Node node : nodes) { if (node.getType() != NodeType.META) { continue; } final MetaNode metaNode = (MetaNode) node; final Set contexts = getGPContexts(node.getContexts()); Map> metaEntry = permanentPermissionMap.get(contexts); if (metaEntry == null) { metaEntry = new HashMap<>(); final List values = new ArrayList<>(); values.add(metaNode.getMetaValue()); metaEntry.put(metaNode.getMetaKey(), values); permanentPermissionMap.put(contexts, metaEntry); } else { List values = metaEntry.get(metaNode.getMetaKey()); if (values == null) { values = new ArrayList<>(); values.add(metaNode.getMetaValue()); metaEntry.put(metaNode.getMetaKey(), values); } else { values.add(metaNode.getMetaValue()); } } } return permanentPermissionMap; } public Map, Map>> getTransientOptions(GDPermissionHolder holder) { final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder); if (permissionHolder == null) { return new HashMap<>(); } final Collection nodes = permissionHolder.transientData().toCollection(); Map, Map>> permanentPermissionMap = new TreeMap, Map>>(CONTEXT_COMPARATOR); for (Node node : nodes) { if (node.getType() != NodeType.META) { continue; } final MetaNode metaNode = (MetaNode) node; final Set contexts = getGPContexts(node.getContexts()); Map> metaEntry = permanentPermissionMap.get(contexts); if (metaEntry == null) { metaEntry = new HashMap<>(); final List values = new ArrayList<>(); values.add(metaNode.getMetaValue()); metaEntry.put(metaNode.getMetaKey(), values); permanentPermissionMap.put(contexts, metaEntry); } else { List values = metaEntry.get(metaNode.getMetaKey()); if (values == null) { values = new ArrayList<>(); values.add(metaNode.getMetaValue()); metaEntry.put(metaNode.getMetaKey(), values); } else { values.add(metaNode.getMetaValue()); } } } return permanentPermissionMap; } public Map, Map> getAllPermissions(GDPermissionHolder holder) { final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder); if (permissionHolder == null) { return new HashMap<>(); } final Collection nodes = permissionHolder.getNodes(); Map, Map> permissionMap = new HashMap<>(); Map> contextMap = new HashMap<>(); for (Node node : nodes) { Set contexts = null; if (contextMap.get(node.getContexts()) == null) { contexts = getGPContexts(node.getContexts()); contextMap.put(node.getContexts(), contexts); } else { contexts = contextMap.get(node.getContexts()); } Map permissionEntry = permissionMap.get(contexts); if (permissionEntry == null) { permissionEntry = new HashMap<>(); permissionEntry.put(node.getKey(), node.getValue()); permissionMap.put(contexts, permissionEntry); } else { permissionEntry.put(node.getKey(), node.getValue()); } } return permissionMap; } public Set getGPContexts(ContextSet contextSet) { final Set gpContexts = new HashSet<>(); for (net.luckperms.api.context.Context context : contextSet.toSet()) { gpContexts.add(new Context(context.getKey(), context.getValue())); } return gpContexts; } @Override public Tristate getPermissionValue(GDPermissionHolder holder, String permission, Set contexts) { return this.getPermissionValue(holder, permission, contexts, PermissionDataType.PERSISTENT); } @Override public Tristate getPermissionValue(GDClaim claim, GDPermissionHolder holder, String permission, Set contexts) { return this.getPermissionValue(holder, permission, contexts); } @Override public Tristate getPermissionValue(GDClaim claim, GDPermissionHolder holder, String permission, Set contexts, PermissionDataType type) { return this.getPermissionValue(holder, permission, contexts, type); } public Tristate getPermissionValue(GDPermissionHolder holder, String permission) { final Set contexts = new HashSet<>(); this.checkServerContext(contexts); ImmutableContextSet set = this.getLPContexts(contexts).immutableCopy(); return this.getPermissionValue(holder, permission, set); } public Tristate getPermissionValue(GDPermissionHolder holder, String permission, MutableContextSet contexts) { return this.getPermissionValue(holder, permission, this.getGDContexts(contexts)); } public Tristate getPermissionValue(GDPermissionHolder holder, String permission, Set contexts, PermissionDataType type) { this.checkServerContext(contexts); ImmutableContextSet contextSet = this.getLPContexts(contexts).immutableCopy(); return this.getPermissionValue(holder, permission, contextSet, type); } public Tristate getPermissionValue(GDPermissionHolder holder, String permission, ContextSet contexts) { return this.getPermissionValue(holder, permission, contexts, PermissionDataType.PERSISTENT); } public Tristate getPermissionValue(GDPermissionHolder holder, String permission, ContextSet contexts, PermissionDataType type) { final PermissionHolder permissionHolder = type == PermissionDataType.TRANSIENT ? this.getLuckPermsHolder(GriefDefenderPlugin.GD_DEFAULT_HOLDER) : this.getLuckPermsHolder(holder); if (permissionHolder == null) { return Tristate.UNDEFINED; } QueryOptions query = null; if (type == PermissionDataType.TRANSIENT) { query = QueryOptions.builder(QueryMode.CONTEXTUAL).option(DataQueryOrderFunction.KEY, DEFAULT_DATA_QUERY_ORDER).option(DataTypeFilterFunction.KEY, DEFAULT_TRANSIENT_ONLY).context(contexts).build(); } else if (type == PermissionDataType.PERSISTENT) { query = QueryOptions.builder(QueryMode.CONTEXTUAL).option(DataQueryOrderFunction.KEY, DEFAULT_DATA_QUERY_ORDER).option(DataTypeFilterFunction.KEY, DEFAULT_PERSISTENT_ONLY).context(contexts).build(); } else if (type == PermissionDataType.USER_PERSISTENT) { query = QueryOptions.builder(QueryMode.CONTEXTUAL).option(DataQueryOrderFunction.KEY, DEFAULT_DATA_QUERY_ORDER).option(DataTypeFilterFunction.KEY, USER_PERSISTENT_ONLY).context(contexts).build(); } else { query = QueryOptions.builder(QueryMode.CONTEXTUAL).option(DataQueryOrderFunction.KEY, DEFAULT_DATA_QUERY_ORDER).context(contexts).build(); } CachedPermissionData cachedData = permissionHolder.getCachedData().getPermissionData(query); return getGDTristate(cachedData.checkPermission(permission)); } // To set options, pass "meta.option". @Override public String getOptionValue(GDPermissionHolder holder, Option option, Set contexts) { // If no server context exists, add global this.checkServerContext(contexts); ImmutableContextSet set = this.getLPContexts(contexts).immutableCopy(); final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder); if (permissionHolder == null) { return null; } final QueryOptions query = QueryOptions.builder(QueryMode.CONTEXTUAL).option(DataQueryOrderFunction.KEY, DEFAULT_DATA_QUERY_ORDER).context(set).build(); CachedMetaData metaData = permissionHolder.getCachedData().getMetaData(query); return metaData.getMetaValue(option.getPermission()); } @Override public List getOptionValueList(GDPermissionHolder holder, Option option, Set contexts) { // If no server context exists, add global this.checkServerContext(contexts); ImmutableContextSet set = this.getLPContexts(contexts).immutableCopy(); final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder); if (permissionHolder == null) { return null; } // First check user persistent data QueryOptions query = QueryOptions.builder(QueryMode.CONTEXTUAL).option(DataQueryOrderFunction.KEY, DEFAULT_DATA_QUERY_ORDER).option(DataTypeFilterFunction.KEY, USER_PERSISTENT_ONLY).context(set).build(); CachedMetaData metaData = permissionHolder.getCachedData().getMetaData(query); List list = metaData.getMeta().get(option.getPermission()); if (list != null) { return list; } // Now check default persistent data query = QueryOptions.builder(QueryMode.CONTEXTUAL).option(DataQueryOrderFunction.KEY, DEFAULT_DATA_QUERY_ORDER).option(DataTypeFilterFunction.KEY, DEFAULT_PERSISTENT_ONLY).context(set).build(); metaData = permissionHolder.getCachedData().getMetaData(query); list = metaData.getMeta().get(option.getPermission()); if (list == null) { return new ArrayList<>(); } return list; } public CompletableFuture setOptionValue(GDPermissionHolder holder, String key, String value, Set contexts, boolean check) { if (check) { // If no server context exists, add global this.checkServerContext(contexts); } if (holder == GriefDefenderPlugin.DEFAULT_HOLDER) { holder = GriefDefenderPlugin.GD_OPTION_HOLDER; } ImmutableContextSet set = this.getLPContexts(contexts).immutableCopy(); final Node node = MetaNode.builder().key(key).value(value).context(set).build(); return this.createOptionFuture(holder, node, key, value, false); } public CompletableFuture setPermissionValue(GDPermissionHolder holder, String permission, Tristate value, Set contexts, boolean check, boolean save) { if (check) { // If no server context exists, add global this.checkServerContext(contexts); } ImmutableContextSet set = this.getLPContexts(contexts).immutableCopy(); final Node node = this.luckPermsApi.getNodeBuilderRegistry().forPermission().permission(permission).value(value.asBoolean()).context(set).build(); return this.createPermissionFuture(PermissionUtil.getInstance().getGDPermissionHolder(holder, contexts), Arrays.asList(node), value, false); } @Override public CompletableFuture setFlagDefinition(GDPermissionHolder holder, FlagDefinition definition, Tristate value, Set contexts, boolean isTransient) { final List nodes = new ArrayList<>(); for (FlagData flagData : definition.getFlagData()) { for (Context context : contexts) { Set permissionContexts = new HashSet<>(flagData.getContexts()); permissionContexts.add(context); // If no server context exists, add global this.checkServerContext(permissionContexts); ImmutableContextSet set = this.getLPContexts(permissionContexts).immutableCopy(); final Node node = this.luckPermsApi.getNodeBuilderRegistry().forPermission().permission(flagData.getFlag().getPermission()).value(value.asBoolean()).context(set).build(); nodes.add(node); } } return this.createPermissionFuture(GriefDefenderPlugin.GD_DEFINITION_HOLDER, nodes, value, isTransient); } public CompletableFuture setTransientOption(GDPermissionHolder holder, String key, String value, Set contexts) { // If no server context exists, add global this.checkServerContext(contexts); MutableContextSet contextSet = this.getLPContexts(contexts); if (holder == GriefDefenderPlugin.DEFAULT_HOLDER) { holder = GriefDefenderPlugin.GD_OPTION_HOLDER; } Node node = null; if (value == null) { node = MetaNode.builder().key(key).context(contextSet).build(); } else { node = MetaNode.builder().key(key).value(value).context(contextSet).build(); } return this.createOptionFuture(holder, node, key, value, true); } public CompletableFuture setTransientPermission(GDPermissionHolder holder, String permission, Tristate value, Set contexts) { // If no server context exists, add global this.checkServerContext(contexts); MutableContextSet contextSet = this.getLPContexts(contexts); if (holder == GriefDefenderPlugin.DEFAULT_HOLDER) { holder = GriefDefenderPlugin.GD_OPTION_HOLDER; } final Node node = this.luckPermsApi.getNodeBuilderRegistry().forPermission().permission(permission).value(value.asBoolean()).context(contextSet).build(); return this.createPermissionFuture(PermissionUtil.getInstance().getGDPermissionHolder(holder, contexts), Arrays.asList(node), value, true); } private CompletableFuture createOptionFuture(GDPermissionHolder holder, Node node, String key, String value, boolean isTransient) { if (holder instanceof GDPermissionGroup) { return CompletableFuture.supplyAsync(() -> { Group group = null; try { group = this.luckPermsApi.getGroupManager().loadGroup(holder.getFriendlyName()).get().orElse(null); } catch (Throwable t) { t.printStackTrace(); } if (group != null) { if (isTransient) { return this.applyTransientOptionNode(holder, group, node, value); } return this.applyOptionNode(holder, group, node, key, value); } return new GDPermissionResult(ResultTypes.FAILURE, TextComponent.builder().append("COULD NOT LOAD GROUP " + holder.getFriendlyName()).build()); }, GriefDefenderPlugin.getInstance().executor); } else { return CompletableFuture.supplyAsync(() -> { User user = null; try { user = this.luckPermsApi.getUserManager().loadUser(((GDPermissionUser) holder).getUniqueId()).get(); } catch (Throwable t) { t.printStackTrace(); } if (user != null) { if (isTransient) { return this.applyTransientOptionNode(holder, user, node, value); } return this.applyOptionNode(holder, user, node, key, value); } return new GDPermissionResult(ResultTypes.FAILURE, TextComponent.builder().append("COULD NOT LOAD USER " + holder.getFriendlyName()).build()); }, GriefDefenderPlugin.getInstance().executor); } } private CompletableFuture createPermissionFuture(GDPermissionHolder holder, List nodes, Tristate value, boolean isTransient) { if (holder instanceof GDPermissionGroup) { return CompletableFuture.supplyAsync(() -> { Group group = null; try { group = this.luckPermsApi.getGroupManager().loadGroup(holder.getFriendlyName()).get().orElse(null); } catch (Throwable t) { t.printStackTrace(); } if (group != null) { if (isTransient) { return this.applyTransientPermissionNodes(holder, group, nodes, value); } return this.applyPermissionNodes(holder, group, nodes, value, true); } return new GDPermissionResult(ResultTypes.FAILURE, TextComponent.builder().append("COULD NOT LOAD GROUP " + holder.getFriendlyName()).build()); }, GriefDefenderPlugin.getInstance().executor); } else { return CompletableFuture.supplyAsync(() -> { User user = null; try { user = this.luckPermsApi.getUserManager().loadUser(((GDPermissionUser) holder).getUniqueId()).get(); } catch (Throwable t) { t.printStackTrace(); } if (user != null) { if (isTransient) { return this.applyTransientPermissionNodes(holder, user, nodes, value); } return this.applyPermissionNodes(holder, user, nodes, value, true); } return new GDPermissionResult(ResultTypes.FAILURE, TextComponent.builder().append("COULD NOT LOAD USER " + holder.getFriendlyName()).build()); }, GriefDefenderPlugin.getInstance().executor); } } public PermissionResult applyOptionNode(GDPermissionHolder holder, PermissionHolder lpHolder, Node node, String key, String value) { final Option option = OptionRegistryModule.getInstance().getById(key).orElse(null); if (option == null) { new GDPermissionResult(ResultTypes.FAILURE); } DataMutateResult result = null; if (!value.equalsIgnoreCase("undefined")) { if (!option.multiValued()) { this.clearMeta(lpHolder, option.getPermission(), node.getContexts()); } result = lpHolder.data().add(node); } else { this.clearMeta(lpHolder, option.getPermission(), node.getContexts()); this.savePermissionHolder(lpHolder); return new GDPermissionResult(ResultTypes.SUCCESS); } if (result != null) { if (result.wasSuccessful()) { this.savePermissionHolder(lpHolder); return new GDPermissionResult(ResultTypes.SUCCESS, TextComponent.builder().append(result.name()).build()); } return new GDPermissionResult(ResultTypes.FAILURE, TextComponent.builder().append(result.name()).build()); } return new GDPermissionResult(ResultTypes.FAILURE); } public PermissionResult applyTransientOptionNode(GDPermissionHolder holder, PermissionHolder lpHolder, Node node, String value) { DataMutateResult result = null; if (value == null) { result = lpHolder.transientData().remove(node); } else { result = lpHolder.transientData().add(node); } if (result != null) { if (result.wasSuccessful()) { return new GDPermissionResult(ResultTypes.SUCCESS, TextComponent.builder().append(result.name()).build()); } return new GDPermissionResult(ResultTypes.FAILURE, TextComponent.builder().append(result.name()).build()); } return new GDPermissionResult(ResultTypes.FAILURE); } public PermissionResult applyPermissionNodes(GDPermissionHolder holder, PermissionHolder lpHolder, List nodes, Tristate value, boolean save) { DataMutateResult result = null; for (Node node : nodes) { if (value == Tristate.UNDEFINED) { result = lpHolder.data().remove(node); } else { result = lpHolder.data().add(node); } } if (result.wasSuccessful()) { if (save) { this.savePermissionHolder(lpHolder); } return new GDPermissionResult(ResultTypes.SUCCESS, TextComponent.builder().append(result.name()).build()); } return new GDPermissionResult(ResultTypes.FAILURE, TextComponent.builder().append(result.name()).build()); } public PermissionResult applyTransientPermissionNodes(GDPermissionHolder holder, PermissionHolder lpHolder, List nodes, Tristate value) { DataMutateResult result = null; for (Node node : nodes) { if (value == null || value == Tristate.UNDEFINED) { result = lpHolder.transientData().remove(node); } else { result = lpHolder.transientData().add(node); } } if (result.wasSuccessful()) { return new GDPermissionResult(ResultTypes.SUCCESS, TextComponent.builder().append(result.name()).build()); } return new GDPermissionResult(ResultTypes.FAILURE, TextComponent.builder().append(result.name()).build()); } public void savePermissionHolder(PermissionHolder holder) { // Always wait for completion to avoid save race conditions if (holder instanceof User) { try { this.luckPermsApi.getUserManager().saveUser((User) holder).get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } else { try { this.luckPermsApi.getGroupManager().saveGroup((Group) holder).get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } public void refreshCachedData(GDPermissionHolder holder) { final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder); if (permissionHolder == null) { return; } permissionHolder.getCachedData().invalidate(); } @Override public CompletableFuture save(GDPermissionHolder holder) { final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder); if (permissionHolder == null) { return new CompletableFuture<>(); } if (permissionHolder instanceof User) { return this.luckPermsApi.getUserManager().saveUser((User) permissionHolder); } else { return this.luckPermsApi.getGroupManager().saveGroup((Group) permissionHolder); } } public Set getGDContexts(ContextSet contexts) { final Set gdContexts = new HashSet<>(); contexts.forEach(entry -> { gdContexts.add(new Context(entry.getKey(), entry.getValue())); }); return gdContexts; } public MutableContextSet getLPContexts(Set contexts) { MutableContextSet lpContexts = MutableContextSet.create(); contexts.forEach(entry -> { lpContexts.add(entry.getKey(), entry.getValue()); }); return lpContexts; } public Tristate getGDTristate(net.luckperms.api.util.Tristate state) { if (state == net.luckperms.api.util.Tristate.TRUE) { return Tristate.TRUE; } if (state == net.luckperms.api.util.Tristate.FALSE) { return Tristate.FALSE; } return Tristate.UNDEFINED; } private void clearMeta(PermissionHolder holder, String metaKey, ContextSet set) { if (set.size() == 1 && set.containsKey("server")) { if (set.getAnyValue("server").get().equalsIgnoreCase("global")) { // LP does not remove meta if passing only global context so we need to make sure to pass none holder.data().clear(NodeType.META.predicate(node -> node.getMetaKey().equals(metaKey))); return; } } holder.data().clear(set, NodeType.META.predicate(node -> node.getMetaKey().equals(metaKey))); } private void checkServerContext(Set contexts) { for (Context context : contexts) { if (context.getKey().equalsIgnoreCase("server")) { return; } } final String serverName = this.luckPermsApi.getServerName(); if (serverName != null) { contexts.add(new Context("server", serverName)); } else { contexts.add(new Context("server", "global")); } } private static class DefaultDataQueryOrderFunction implements DataQueryOrderFunction { @Override public Comparator getOrderComparator(Identifier identifier) { if (identifier.getType() == Identifier.GROUP_TYPE && (identifier.getName().equalsIgnoreCase(GriefDefenderPlugin.DEFAULT_GROUP_NAME) || identifier.getName().startsWith("griefdefender_"))) { return DataQueryOrder.TRANSIENT_LAST; } return DataQueryOrder.TRANSIENT_FIRST; } } private static class DefaultTransientOnlyDataFilter implements DataTypeFilterFunction { @Override public @NonNull Predicate getTypeFilter(@NonNull Identifier identifier) { if (identifier.getType() == Identifier.GROUP_TYPE && (identifier.getName().equalsIgnoreCase(GriefDefenderPlugin.DEFAULT_GROUP_NAME) || identifier.getName().startsWith("griefdefender_"))) { return DataTypeFilter.TRANSIENT_ONLY; } return DataTypeFilter.ALL; } } private static class DefaultPersistentOnlyDataFilter implements DataTypeFilterFunction { @Override public @NonNull Predicate getTypeFilter(@NonNull Identifier identifier) { if (identifier.getType() == Identifier.GROUP_TYPE && (identifier.getName().equalsIgnoreCase(GriefDefenderPlugin.DEFAULT_GROUP_NAME) || identifier.getName().startsWith("griefdefender_"))) { return DataTypeFilter.NORMAL_ONLY; } return DataTypeFilter.ALL; } } private static class UserPersistentOnlyDataFilter implements DataTypeFilterFunction { @Override public @NonNull Predicate getTypeFilter(@NonNull Identifier identifier) { if (identifier.getType() == Identifier.GROUP_TYPE && (identifier.getName().equalsIgnoreCase(GriefDefenderPlugin.DEFAULT_GROUP_NAME) || identifier.getName().startsWith("griefdefender_"))) { return DataTypeFilter.NONE; } return DataTypeFilter.NORMAL_ONLY; } } }