Refactor contexts, expose cached data in the API & release 2.13

This commit is contained in:
Luck 2016-10-23 16:46:53 +01:00
parent 33c78e4a17
commit 383276f47a
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
52 changed files with 1263 additions and 517 deletions

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.12-SNAPSHOT</version>
<version>2.13-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -22,9 +22,8 @@
package me.lucko.luckperms.api;
import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.api.context.ContextSet;
import java.util.Collections;
import java.util.Map;
/**
@ -41,23 +40,19 @@ public class Contexts {
* @return a context that will not apply any filters
*/
public static Contexts allowAll() {
return new Contexts(Collections.emptyMap(), true, true, true, true, true, true);
return new Contexts(ContextSet.empty(), true, true, true, true, true, true);
}
public static Contexts of(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups) {
return new Contexts(context, includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups);
}
public static Contexts of(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) {
public static Contexts of(ContextSet context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) {
return new Contexts(context, includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups, op);
}
public Contexts(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) {
public Contexts(ContextSet context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) {
if (context == null) {
throw new NullPointerException("context");
}
this.context = ImmutableMap.copyOf(context);
this.context = context.makeImmutable();
this.includeGlobal = includeGlobal;
this.includeGlobalWorld = includeGlobalWorld;
this.applyGroups = applyGroups;
@ -66,16 +61,34 @@ public class Contexts {
this.op = op;
}
@SuppressWarnings("deprecation")
@Deprecated
public static Contexts of(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups) {
return new Contexts(context, includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups);
}
@SuppressWarnings("deprecation")
@Deprecated
public static Contexts of(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) {
return new Contexts(context, includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups, op);
}
@Deprecated
public Contexts(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) {
this(context == null ? null : ContextSet.fromMap(context), includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups, op);
}
@SuppressWarnings("deprecation")
@Deprecated
public Contexts(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups) {
this(context, includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups, false);
}
/**
* The contexts that apply for this lookup
*
* The keys for servers and worlds are defined as static values.
*/
private final Map<String, String> context;
private final ContextSet context;
/**
* The mode to parse defaults on Bukkit
@ -110,12 +123,23 @@ public class Contexts {
/**
* Gets the contexts that apply for this lookup
* @return an immutable map of context key value pairs
* @return an immutable set of context key value pairs
* @since 2.13
*/
public Map<String, String> getContext() {
public ContextSet getContexts() {
return this.context;
}
/**
* Gets the contexts that apply for this lookup
* @return an immutable map of context key value pairs
* @deprecated in favour of {@link #getContexts()}
*/
@Deprecated
public Map<String, String> getContext() {
return this.context.toMap();
}
/**
* Gets if OP defaults should be included
* @return true if op defaults should be included
@ -166,7 +190,7 @@ public class Contexts {
public String toString() {
return "Contexts(" +
"context=" + this.getContext() + ", " +
"context=" + this.getContexts() + ", " +
"op=" + this.isOp() + ", " +
"includeGlobal=" + this.isIncludeGlobal() + ", " +
"includeGlobalWorld=" + this.isIncludeGlobalWorld() + ", " +
@ -190,8 +214,8 @@ public class Contexts {
if (o == this) return true;
if (!(o instanceof Contexts)) return false;
final Contexts other = (Contexts) o;
final Object this$context = this.getContext();
final Object other$context = other.getContext();
final Object this$context = this.getContexts();
final Object other$context = other.getContexts();
if (this$context == null ? other$context != null : !this$context.equals(other$context)) return false;
if (this.isOp() != other.isOp()) return false;
if (this.isIncludeGlobal() != other.isIncludeGlobal()) return false;
@ -210,7 +234,7 @@ public class Contexts {
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $context = this.getContext();
final Object $context = this.getContexts();
result = result * PRIME + ($context == null ? 43 : $context.hashCode());
result = result * PRIME + (this.isOp() ? 79 : 97);
result = result * PRIME + (this.isIncludeGlobal() ? 79 : 97);

View File

@ -258,7 +258,7 @@ public class MetaUtils {
int priority = Integer.MIN_VALUE;
String meta = null;
for (Node n : holder.getAllNodes()) {
for (Node n : holder.getAllNodes(Contexts.allowAll())) {
if (!n.getValue()) {
continue;
}

View File

@ -22,10 +22,9 @@
package me.lucko.luckperms.api;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import me.lucko.luckperms.api.context.ContextSet;
import java.util.*;
/**
* Represents an immutable node object
@ -109,15 +108,40 @@ public interface Node extends Map.Entry<String, Boolean> {
* @param context the context key value pairs
* @param worldAndServer if world and server contexts should be checked
* @return true if the node should apply
* @since 2.13
*/
boolean shouldApplyWithContext(Map<String, String> context, boolean worldAndServer);
boolean shouldApplyWithContext(ContextSet context, boolean worldAndServer);
/**
* If this node should apply in the given context
* @param context the context key value pairs
* @return true if the node should apply
* @since 2.13
*/
boolean shouldApplyWithContext(Map<String, String> context);
boolean shouldApplyWithContext(ContextSet context);
/**
* If this node should apply in the given context
* @param context the context key value pairs
* @param worldAndServer if world and server contexts should be checked
* @return true if the node should apply
* @deprecated in favour of {@link #shouldApplyWithContext(ContextSet, boolean)}
*/
@Deprecated
default boolean shouldApplyWithContext(Map<String, String> context, boolean worldAndServer) {
return shouldApplyWithContext(ContextSet.fromMap(context), worldAndServer);
}
/**
* If this node should apply in the given context
* @param context the context key value pairs
* @return true if the node should apply
* @deprecated in favour of {@link #shouldApplyWithContext(ContextSet)}
*/
@Deprecated
default boolean shouldApplyWithContext(Map<String, String> context) {
return shouldApplyWithContext(ContextSet.fromMap(context));
}
/**
* Similar to {@link #shouldApplyOnServer(String, boolean, boolean)}, except this method accepts a List
@ -186,8 +210,18 @@ public interface Node extends Map.Entry<String, Boolean> {
/**
* @return the extra contexts required for this node to apply
* @deprecated in favour of {@link #getContexts()}
*/
Map<String, String> getExtraContexts();
@Deprecated
default Map<String, String> getExtraContexts() {
return getContexts().toMap();
}
/**
* @return the extra contexts required for this node to apply
* @since 2.13
*/
ContextSet getContexts();
/**
* Converts this node into a serialized form
@ -302,7 +336,9 @@ public interface Node extends Map.Entry<String, Boolean> {
Builder setServer(String server) throws IllegalArgumentException;
Builder withExtraContext(String key, String value);
Builder withExtraContext(Map<String, String> map);
Builder withExtraContext(Set<Map.Entry<String, String>> context);
Builder withExtraContext(Map.Entry<String, String> entry);
Builder withExtraContext(ContextSet set);
Node build();
}

View File

@ -22,10 +22,12 @@
package me.lucko.luckperms.api;
import me.lucko.luckperms.api.caching.UserData;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
/**
@ -64,6 +66,13 @@ public interface User extends PermissionHolder {
*/
void refreshPermissions();
/**
* Gets the user's {@link UserData} cache, if they have one setup.
* @return an optional, possibly containing the user's cached lookup data.
* @since 2.13
*/
Optional<UserData> getUserDataCache();
/**
* Check to see if the user is a member of a group
* @param group The group to check membership of

View File

@ -0,0 +1,64 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.caching;
import java.util.Map;
import java.util.SortedMap;
/**
* Holds cached Meta lookup data for a specific set of contexts
* @since 2.13
*/
public interface MetaData {
/**
* Gets an immutable copy of the meta this user has
* @return an immutable map of meta
*/
Map<String, String> getMeta();
/**
* Gets an immutable sorted map of all of the prefixes the user has, whereby the first value is the highest priority prefix.
* @return a sorted map of prefixes
*/
SortedMap<Integer, String> getPrefixes();
/**
* Gets an immutable sorted map of all of the suffixes the user has, whereby the first value is the highest priority suffix.
* @return a sorted map of suffixes
*/
SortedMap<Integer, String> getSuffixes();
/**
* Gets the user's highest priority prefix, or null if the user has no prefixes
* @return a prefix string, or null
*/
String getPrefix();
/**
* Gets the user's highest priority suffix, or null if the user has no suffixes
* @return a suffix string, or null
*/
String getSuffix();
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.caching;
import me.lucko.luckperms.api.Tristate;
import java.util.Map;
/**
* Holds cached Permission lookup data for a specific set of contexts
* @since 2.13
*/
public interface PermissionData {
/**
* Gets a permission value for the given permission node
* @param permission the permission node
* @return a tristate result
* @throws NullPointerException if permission is null
*/
Tristate getPermissionValue(String permission);
/**
* Invalidates the underlying permission calculator cache.
* Can be called to allow for an update in defaults.
*/
void invalidateCache();
/**
* Gets an immutable copy of the permission map backing the permission calculator
* @return an immutable set of permissions
*/
Map<String, Boolean> getImmutableBacking();
}

View File

@ -0,0 +1,119 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.caching;
import me.lucko.luckperms.api.Contexts;
import java.util.Set;
/**
* Holds cached permission and meta lookup data for a {@link me.lucko.luckperms.api.User}.
* Data is only likely to be available for online users. All calls will account for inheritance, as well as any
* default data provided by the platform. This calls are heavily cached and are therefore fast.
*
* @since 2.13
*/
public interface UserData {
/**
* Gets PermissionData from the cache, given a specified context.
* If the data is not cached, it is calculated. Therefore, this call could be costly.
* @param contexts the contexts to get the permission data in
* @return a permission data instance
* @throws NullPointerException if contexts is null
*/
PermissionData getPermissionData(Contexts contexts);
/**
* Gets MetaData from the cache, given a specified context.
* If the data is not cached, it is calculated. Therefore, this call could be costly.
* @param contexts the contexts to get the permission data in
* @return a meta data instance
* @throws NullPointerException if contexts is null
*/
MetaData getMetaData(Contexts contexts);
/**
* Calculates permission data, bypassing the cache.
* @param contexts the contexts to get permission data in
* @return a permission data instance
* @throws NullPointerException if contexts is null
*/
PermissionData calculatePermissions(Contexts contexts);
/**
* Calculates meta data, bypassing the cache.
* @param contexts the contexts to get meta data in
* @return a meta data instance
* @throws NullPointerException if contexts is null
*/
MetaData calculateMeta(Contexts contexts);
/**
* Calculates permission data and stores it in the cache. If there is already data cached for the given contexts,
* and if the resultant output is different, the cached value is updated.
* @param contexts the contexts to recalculate in.
* @throws NullPointerException if contexts is null
*/
void recalculatePermissions(Contexts contexts);
/**
* Calculates meta data and stores it in the cache. If there is already data cached for the given contexts,
* and if the resultant output is different, the cached value is updated.
* @param contexts the contexts to recalculate in.
* @throws NullPointerException if contexts is null
*/
void recalculateMeta(Contexts contexts);
/**
* Calls {@link #recalculatePermissions(Contexts)} for all current loaded contexts
*/
void recalculatePermissions();
/**
* Calls {@link #recalculateMeta(Contexts)} for all current loaded contexts
*/
void recalculateMeta();
/**
* Calls {@link #preCalculate(Contexts)} for the given contexts
* @param contexts a set of contexts
* @throws NullPointerException if contexts is null
*/
void preCalculate(Set<Contexts> contexts);
/**
* Ensures that PermissionData and MetaData is cached for a context. If the cache does not contain any data for the
* context, it will be calculated and saved.
* @param contexts the contexts to pre-calculate for
* @throws NullPointerException if contexts is null
*/
void preCalculate(Contexts contexts);
/**
* Invalidates all of the underlying Permission calculators.
* Can be called to allow for an update in defaults.
*/
void invalidatePermissionCalculators();
}

View File

@ -0,0 +1,306 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.context;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Holds contexts.
* All contained contexts are immutable, and unlike {@link MutableContextSet}, contexts cannot be added or removed.
*
* @since 2.13
*/
public class ContextSet {
/**
* Make a singleton ContextSet from a context pair
* @param key the key
* @param value the value
* @return a new ContextSet containing one KV pair
* @throws NullPointerException if key or value is null
*/
public static ContextSet singleton(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
MutableContextSet set = new MutableContextSet();
set.add(key, value);
return set.immutableCopy();
}
/**
* Creates a ContextSet from an existing map
* @param map the map to copy from
* @return a new ContextSet representing the pairs from the map
* @throws NullPointerException if the map is null
*/
public static ContextSet fromMap(Map<String, String> map) {
if (map == null) {
throw new NullPointerException("map");
}
MutableContextSet set = new MutableContextSet();
set.addAll(map);
return set.immutableCopy();
}
/**
* Creates a ContextSet from an existing iterable of Map Entries
* @param iterable the iterable to copy from
* @return a new ContextSet representing the pairs in the iterable
* @throws NullPointerException if the iterable is null
*/
public static ContextSet fromEntries(Iterable<Map.Entry<String, String>> iterable) {
if (iterable == null) {
throw new NullPointerException("iterable");
}
MutableContextSet set = new MutableContextSet();
set.addAll(iterable);
return set.immutableCopy();
}
/**
* Creates a new ContextSet from an existing set.
* Only really useful for converting between mutable and immutable types.
* @param contextSet the context set to copy from
* @return a new ContextSet with the same content and the one provided
* @throws NullPointerException if contextSet is null
*/
public static ContextSet fromSet(ContextSet contextSet) {
if (contextSet == null) {
throw new NullPointerException("contextSet");
}
MutableContextSet set = new MutableContextSet();
set.addAll(contextSet.toSet());
return set.immutableCopy();
}
/**
* Creates a new empty ContextSet.
* @return a new ContextSet
*/
public static ContextSet empty() {
return new ContextSet();
}
final Set<Map.Entry<String, String>> contexts;
public ContextSet() {
this.contexts = new HashSet<>();
}
protected ContextSet(Set<Map.Entry<String, String>> contexts) {
this.contexts = contexts;
}
/**
* Check to see if this set is in an immutable form
* @return true if the set is immutable
*/
public boolean isImmutable() {
return true;
}
/**
* If the set is mutable, this method will return an immutable copy. Otherwise just returns itself.
* @return an immutable ContextSet
*/
public ContextSet makeImmutable() {
return this;
}
/**
* Converts this ContextSet to an immutable {@link Set} of {@link Map.Entry}s.
* @return an immutable set
*/
public Set<Map.Entry<String, String>> toSet() {
synchronized (contexts) {
return ImmutableSet.copyOf(contexts);
}
}
/**
* Converts this ContextSet to an immutable {@link Map}
*
* <b>NOTE: Use of this method may result in data being lost. ContextSets can contain lots of different values for
* one key.</b>
*
* @return an immutable map
*/
public Map<String, String> toMap() {
synchronized (contexts) {
return ImmutableMap.copyOf(contexts.stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
}
}
/**
* Check if the set contains at least one value for the given key.
* @param key the key to check for
* @return true if the set contains a value for the key
* @throws NullPointerException if the key is null
*/
public boolean containsKey(String key) {
if (key == null) {
throw new NullPointerException("key");
}
synchronized (contexts) {
for (Map.Entry<String, String> e : contexts) {
if (e.getKey().equalsIgnoreCase(key)) {
return true;
}
}
}
return false;
}
/**
* Gets a set of all of the values mapped to the given key
* @param key the key to find values for
* @return a set of values
* @throws NullPointerException if the key is null
*/
public Set<String> getValues(String key) {
if (key == null) {
throw new NullPointerException("key");
}
synchronized (contexts) {
return ImmutableSet.copyOf(contexts.stream()
.filter(e -> e.getKey().equalsIgnoreCase(key))
.map(Map.Entry::getValue)
.collect(Collectors.toSet())
);
}
}
/**
* Check if thr set contains a given key mapped to a given value
* @param key the key to look for
* @param value the value to look for (case sensitive)
* @return true if the set contains the KV pair
* @throws NullPointerException if the key or value is null
*/
public boolean has(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
synchronized (contexts) {
for (Map.Entry<String, String> e : contexts) {
if (!e.getKey().equalsIgnoreCase(key)) {
continue;
}
if (!e.getValue().equals(value)) {
continue;
}
return true;
}
}
return false;
}
/**
* Same as {@link #has(String, String)}, except ignores the case of the value.
* @param key the key to look for
* @param value the value to look for
* @return true if the set contains the KV pair
* @throws NullPointerException if the key or value is null
*/
public boolean hasIgnoreCase(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
synchronized (contexts) {
for (Map.Entry<String, String> e : contexts) {
if (!e.getKey().equalsIgnoreCase(key)) {
continue;
}
if (!e.getValue().equalsIgnoreCase(value)) {
continue;
}
return true;
}
}
return false;
}
/**
* Check if the set is empty
* @return true if the set is empty
*/
public boolean isEmpty() {
synchronized (contexts) {
return contexts.isEmpty();
}
}
/**
* Gets the number of key-value context pairs in the set
* @return the size of the set
*/
public int size() {
synchronized (contexts) {
return contexts.size();
}
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof ContextSet)) return false;
final ContextSet other = (ContextSet) o;
final Object thisContexts = this.contexts;
final Object otherContexts = other.contexts;
return thisContexts == null ? otherContexts == null : thisContexts.equals(otherContexts);
}
@Override
public int hashCode() {
return 59 + (this.contexts == null ? 43 : this.contexts.hashCode());
}
}

View File

@ -22,6 +22,7 @@
package me.lucko.luckperms.api.context;
import java.util.HashMap;
import java.util.Map;
/**
@ -34,11 +35,40 @@ public interface IContextCalculator<T> {
/**
* Gives the subject all of the applicable contexts they meet
* @param subject the subject to add contexts tp
* @param subject the subject to add contexts to
* @param accumulator a map of contexts to add to
* @return the map
* @deprecated in favour of {@link #giveApplicableContext(Object, MutableContextSet)}. Older implementations of this interface
* will still work, as the replacement method is given as a default, and falls back to using this method.
*/
Map<String, String> giveApplicableContext(T subject, Map<String, String> accumulator);
@Deprecated
default Map<String, String> giveApplicableContext(T subject, Map<String, String> accumulator) {
MutableContextSet acc = new MutableContextSet();
giveApplicableContext(subject, acc);
accumulator.putAll(acc.toMap());
return accumulator;
}
/**
* Gives the subject all of the applicable contexts they meet
*
* <p><b>You MUST implement this method. The default is only provided for backwards compatibility with
* {@link #giveApplicableContext(Object, Map)}.</b>
*
* @param subject the subject to add contexts to
* @param accumulator a map of contexts to add to
* @return the map
* @since 2.13
*/
@SuppressWarnings("deprecation")
default MutableContextSet giveApplicableContext(T subject, MutableContextSet accumulator) {
Map<String, String> acc = new HashMap<>();
giveApplicableContext(subject, acc);
accumulator.addAll(acc.entrySet());
return accumulator;
}
/**
* Checks to see if a context is applicable to a subject

View File

@ -0,0 +1,272 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.context;
import com.google.common.collect.Maps;
import java.util.HashSet;
import java.util.Map;
/**
* Holds contexts
* All contained contexts are immutable, but contexts can be added or removed from the set.
*
* @since 2.13
*/
public class MutableContextSet extends ContextSet {
/**
* Make a singleton MutableContextSet from a context pair
* @param key the key
* @param value the value
* @return a new MutableContextSet containing one KV pair
* @throws NullPointerException if key or value is null
*/
public static MutableContextSet singleton(String key, String value) {
MutableContextSet set = new MutableContextSet();
set.add(key, value);
return set;
}
/**
* Creates a MutableContextSet from an existing map
* @param map the map to copy from
* @return a new MutableContextSet representing the pairs from the map
* @throws NullPointerException if the map is null
*/
public static MutableContextSet fromMap(Map<String, String> map) {
MutableContextSet set = new MutableContextSet();
set.addAll(map);
return set;
}
/**
* Creates a MutableContextSet from an existing iterable of Map Entries
* @param iterable the iterable to copy from
* @return a new MutableContextSet representing the pairs in the iterable
* @throws NullPointerException if the iterable is null
*/
public static MutableContextSet fromEntries(Iterable<Map.Entry<String, String>> iterable) {
MutableContextSet set = new MutableContextSet();
set.addAll(iterable);
return set;
}
/**
* Creates a new MutableContextSet from an existing set.
* Only really useful for converting between mutable and immutable types.
* @param contextSet the context set to copy from
* @return a new MutableContextSet with the same content and the one provided
* @throws NullPointerException if contextSet is null
*/
public static MutableContextSet fromSet(ContextSet contextSet) {
MutableContextSet set = new MutableContextSet();
set.addAll(contextSet.toSet());
return set;
}
/**
* Creates a new empty MutableContextSet.
* @return a new MutableContextSet
*/
public static MutableContextSet empty() {
return new MutableContextSet();
}
@Override
public boolean isImmutable() {
return false;
}
@Override
public ContextSet makeImmutable() {
return immutableCopy();
}
/**
* Returns an immutable copy of this set.
* @return an immutable copy of this set
*/
public ContextSet immutableCopy() {
synchronized (contexts) {
return new ContextSet(new HashSet<>(contexts));
}
}
/**
* Adds a new key value pair to the set
* @param key the key to add
* @param value the value to add
* @throws NullPointerException if the key or value is null
*/
public void add(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
synchronized (contexts) {
contexts.add(Maps.immutableEntry(key, value));
}
}
/**
* Adds a new key value pair to the set
* @param entry the entry to add
* @throws NullPointerException if the entry is null
*/
public void add(Map.Entry<String, String> entry) {
if (entry == null) {
throw new NullPointerException("context");
}
synchronized (contexts) {
contexts.add(Maps.immutableEntry(entry.getKey(), entry.getValue()));
}
}
/**
* Adds an iterable containing contexts to the set
* @param iterable an iterable of key value context pairs
* @throws NullPointerException if iterable is null
*/
public void addAll(Iterable<Map.Entry<String, String>> iterable) {
if (iterable == null) {
throw new NullPointerException("contexts");
}
synchronized (this.contexts) {
for (Map.Entry<String, String> e : iterable) {
this.contexts.add(Maps.immutableEntry(e.getKey(), e.getValue()));
}
}
}
/**
* Adds the entry set of a map to the set
* @param map the map to add from
* @throws NullPointerException if the map is null
*/
public void addAll(Map<String, String> map) {
if (map == null) {
throw new NullPointerException("contexts");
}
addAll(map.entrySet());
}
/**
* Adds of of the values in another ContextSet to this set
* @param contextSet the set to add from
* @throws NullPointerException if the contextSet is null
*/
public void addAll(ContextSet contextSet) {
if (contextSet == null) {
throw new NullPointerException("contextSet");
}
synchronized (this.contexts) {
this.contexts.addAll(contextSet.toSet());
}
}
/**
* Remove a key value pair from this set
* @param key the key to remove
* @param value the value to remove (case sensitive)
* @throws NullPointerException if the key or value is null
*/
public void remove(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
synchronized (contexts) {
contexts.removeIf(e -> e.getKey().equalsIgnoreCase(key) && e.getValue().equals(value));
}
}
/**
* Same as {@link #remove(String, String)}, except ignores the case of the value
* @param key the key to remove
* @param value the value to remove
* @throws NullPointerException if the key or value is null
*/
public void removeIgnoreCase(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
synchronized (contexts) {
contexts.removeIf(e -> e.getKey().equalsIgnoreCase(key) && e.getValue().equalsIgnoreCase(value));
}
}
/**
* Removes all pairs with the given key
* @param key the key to remove
* @throws NullPointerException if the key is null
*/
public void removeAll(String key) {
if (key == null) {
throw new NullPointerException("key");
}
synchronized (contexts) {
contexts.removeIf(e -> e.getKey().equalsIgnoreCase(key));
}
}
/**
* Clears the set
*/
public void clear() {
synchronized (contexts) {
contexts.clear();
}
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof ContextSet)) return false;
final ContextSet other = (ContextSet) o;
final Object thisContexts = this.contexts;
final Object otherContexts = other.contexts;
return thisContexts == null ? otherContexts == null : thisContexts.equals(otherContexts);
}
@Override
public int hashCode() {
return 59 + (this.contexts == null ? 43 : this.contexts.hashCode());
}
}

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.12-SNAPSHOT</version>
<version>2.13-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.12-SNAPSHOT</version>
<version>2.13-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.12-SNAPSHOT</version>
<version>2.13-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -28,6 +28,8 @@ import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.LuckPermsApi;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.bukkit.calculators.AutoOPListener;
import me.lucko.luckperms.bukkit.calculators.DefaultsProvider;
import me.lucko.luckperms.bukkit.vault.VaultHook;
@ -274,32 +276,32 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
@Override
public Set<Contexts> getPreProcessContexts(boolean op) {
Set<Map<String, String>> c = new HashSet<>();
c.add(Collections.emptyMap());
c.add(Collections.singletonMap("server", getConfiguration().getServer()));
Set<ContextSet> c = new HashSet<>();
c.add(ContextSet.empty());
c.add(ContextSet.singleton("server", getConfiguration().getServer()));
// Pre process all worlds
c.addAll(getServer().getWorlds().stream()
.map(World::getName)
.map(s -> {
Map<String, String> map = new HashMap<>();
map.put("server", getConfiguration().getServer());
map.put("world", s);
return map;
MutableContextSet set = new MutableContextSet();
set.add("server", getConfiguration().getServer());
set.add("world", s);
return set.makeImmutable();
})
.collect(Collectors.toList())
);
// Pre process the separate Vault server, if any
if (!getConfiguration().getServer().equals(getConfiguration().getVaultServer())) {
c.add(Collections.singletonMap("server", getConfiguration().getVaultServer()));
c.add(ContextSet.singleton("server", getConfiguration().getVaultServer()));
c.addAll(getServer().getWorlds().stream()
.map(World::getName)
.map(s -> {
Map<String, String> map = new HashMap<>();
map.put("server", getConfiguration().getVaultServer());
map.put("world", s);
return map;
MutableContextSet set = new MutableContextSet();
set.add("server", getConfiguration().getVaultServer());
set.add("world", s);
return set.makeImmutable();
})
.collect(Collectors.toList())
);
@ -309,8 +311,8 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
// Convert to full Contexts
contexts.addAll(c.stream()
.map(map -> new Contexts(
map,
.map(set -> new Contexts(
set,
getConfiguration().isIncludingGlobalPerms(),
getConfiguration().isIncludingGlobalWorldPerms(),
true,

View File

@ -26,6 +26,7 @@ import com.google.common.collect.Maps;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.context.ContextCalculator;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.common.LuckPermsPlugin;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@ -48,11 +49,11 @@ public class WorldCalculator extends ContextCalculator<Player> implements Listen
private final Map<UUID, String> worldCache = new ConcurrentHashMap<>();
@Override
public Map<String, String> giveApplicableContext(Player subject, Map<String, String> accumulator) {
public MutableContextSet giveApplicableContext(Player subject, MutableContextSet accumulator) {
String world = getWorld(subject);
if (world != null) {
accumulator.put(WORLD_KEY, world);
accumulator.add(Maps.immutableEntry(WORLD_KEY, world));
}
return accumulator;

View File

@ -26,6 +26,7 @@ import lombok.Getter;
import lombok.NonNull;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.common.users.User;
import org.bukkit.Bukkit;
@ -66,7 +67,7 @@ public class LPPermissible extends PermissibleBase {
public Contexts calculateContexts() {
return new Contexts(
plugin.getContextManager().giveApplicableContext(parent, new HashMap<>()),
plugin.getContextManager().giveApplicableContext(parent, new MutableContextSet()),
plugin.getConfiguration().isIncludingGlobalPerms(),
plugin.getConfiguration().isIncludingGlobalWorldPerms(),
true,

View File

@ -25,7 +25,8 @@ package me.lucko.luckperms.bukkit.vault;
import lombok.NonNull;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.common.caching.MetaData;
import me.lucko.luckperms.api.caching.MetaData;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.core.PermissionHolder;
import me.lucko.luckperms.common.groups.Group;
import me.lucko.luckperms.common.users.User;
@ -214,7 +215,7 @@ public class VaultChatHook extends Chat {
context.put("world", world);
}
for (Node n : group.getAllNodes(null, new Contexts(context, perms.isIncludeGlobal(), true, true, true, true))) {
for (Node n : group.getAllNodes(null, new Contexts(ContextSet.fromMap(context), perms.isIncludeGlobal(), true, true, true, true, false))) {
if (!n.getValue()) {
continue;
}

View File

@ -27,6 +27,7 @@ import lombok.NonNull;
import lombok.Setter;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.common.core.PermissionHolder;
import me.lucko.luckperms.common.groups.Group;
@ -146,7 +147,7 @@ public class VaultPermissionHook extends Permission {
context.put("world", world);
}
context.put("server", server);
return new Contexts(context, isIncludeGlobal(), true, true, true, true);
return new Contexts(ContextSet.fromMap(context), isIncludeGlobal(), true, true, true, true, false);
}
@Override

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.12-SNAPSHOT</version>
<version>2.13-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -24,6 +24,7 @@ package me.lucko.luckperms.bungee;
import com.google.common.collect.Maps;
import me.lucko.luckperms.api.context.ContextCalculator;
import me.lucko.luckperms.api.context.MutableContextSet;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.ServerSwitchEvent;
import net.md_5.bungee.api.plugin.Listener;
@ -35,11 +36,11 @@ public class BackendServerCalculator extends ContextCalculator<ProxiedPlayer> im
private static final String WORLD_KEY = "world";
@Override
public Map<String, String> giveApplicableContext(ProxiedPlayer subject, Map<String, String> accumulator) {
public MutableContextSet giveApplicableContext(ProxiedPlayer subject, MutableContextSet accumulator) {
String server = getServer(subject);
if (server != null) {
accumulator.put(WORLD_KEY, server);
accumulator.add(Maps.immutableEntry(WORLD_KEY, server));
}
return accumulator;

View File

@ -23,6 +23,7 @@
package me.lucko.luckperms.bungee;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.api.event.events.UserFirstLoginEvent;
import me.lucko.luckperms.common.constants.Message;
import me.lucko.luckperms.common.core.UuidCache;
@ -38,7 +39,6 @@ import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import java.util.HashMap;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@ -72,12 +72,13 @@ public class BungeeListener extends AbstractListener implements Listener {
}
Contexts contexts = new Contexts(
plugin.getContextManager().giveApplicableContext(player, new HashMap<>()),
plugin.getContextManager().giveApplicableContext(player, MutableContextSet.empty()),
plugin.getConfiguration().isIncludingGlobalPerms(),
plugin.getConfiguration().isIncludingGlobalWorldPerms(),
true,
plugin.getConfiguration().isApplyingGlobalGroups(),
plugin.getConfiguration().isApplyingGlobalWorldGroups()
plugin.getConfiguration().isApplyingGlobalWorldGroups(),
false
);
e.setHasPermission(user.getUserData().getPermissionData(contexts).getPermissionValue(e.getPermission()).asBoolean());

View File

@ -27,6 +27,8 @@ import me.lucko.luckperms.ApiHandler;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.api.ApiProvider;
import me.lucko.luckperms.common.calculators.CalculatorFactory;
@ -212,28 +214,29 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
@Override
public Set<Contexts> getPreProcessContexts(boolean op) {
Set<Map<String, String>> c = new HashSet<>();
c.add(Collections.emptyMap());
c.add(Collections.singletonMap("server", getConfiguration().getServer()));
Set<ContextSet> c = new HashSet<>();
c.add(ContextSet.empty());
c.add(ContextSet.singleton("server", getConfiguration().getServer()));
c.addAll(getProxy().getServers().values().stream()
.map(ServerInfo::getName)
.map(s -> {
Map<String, String> map = new HashMap<>();
map.put("server", getConfiguration().getServer());
map.put("world", s);
return map;
MutableContextSet set = new MutableContextSet();
set.add("server", getConfiguration().getServer());
set.add("world", s);
return set.makeImmutable();
})
.collect(Collectors.toList())
);
return c.stream()
.map(map -> new Contexts(
map,
.map(set -> new Contexts(
set,
getConfiguration().isIncludingGlobalPerms(),
getConfiguration().isIncludingGlobalWorldPerms(),
true,
getConfiguration().isApplyingGlobalGroups(),
getConfiguration().isApplyingGlobalWorldGroups()
getConfiguration().isApplyingGlobalWorldGroups(),
false
))
.collect(Collectors.toSet());
}

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.12-SNAPSHOT</version>
<version>2.13-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -69,7 +69,7 @@ public class ApiProvider implements LuckPermsApi {
@Override
public double getApiVersion() {
return 2.12;
return 2.13;
}
@Override

View File

@ -25,6 +25,7 @@ package me.lucko.luckperms.common.api.internal;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import me.lucko.luckperms.api.*;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
@ -247,7 +248,7 @@ public class PermissionHolderLink implements PermissionHolder {
if (world != null && !world.equals("")) {
context.put("world", world);
}
return master.exportNodes(new Contexts(context, true, true, true, true, true), false);
return master.exportNodes(new Contexts(ContextSet.fromMap(context), true, true, true, true, true, false), false);
}
@Override
@ -259,7 +260,7 @@ public class PermissionHolderLink implements PermissionHolder {
if (world != null && !world.equals("")) {
context.put("world", world);
}
return master.exportNodes(new Contexts(context, true, true, true, true, true), false);
return master.exportNodes(new Contexts(ContextSet.fromMap(context), true, true, true, true, true, false), false);
}
@Override
@ -283,7 +284,7 @@ public class PermissionHolderLink implements PermissionHolder {
if (world != null && !world.equals("")) {
extraContext.put("world", world);
}
return master.exportNodes(new Contexts(extraContext, includeGlobal, includeGlobal, applyGroups, true, true), false);
return master.exportNodes(new Contexts(ContextSet.fromMap(extraContext), includeGlobal, includeGlobal, applyGroups, true, true, false), false);
}
@Override

View File

@ -27,10 +27,12 @@ import lombok.Getter;
import lombok.NonNull;
import me.lucko.luckperms.api.Group;
import me.lucko.luckperms.api.User;
import me.lucko.luckperms.api.caching.UserData;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import static me.lucko.luckperms.common.api.internal.Utils.*;
@ -82,6 +84,11 @@ public class UserLink extends PermissionHolderLink implements User {
master.getRefreshBuffer().requestDirectly();
}
@Override
public Optional<UserData> getUserDataCache() {
return Optional.ofNullable(master.getUserData());
}
@Override
public boolean isInGroup(@NonNull Group group) {
checkGroup(group);

View File

@ -28,6 +28,8 @@ import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.caching.MetaData;
import me.lucko.luckperms.api.context.MutableContextSet;
import java.util.*;
@ -35,7 +37,7 @@ import java.util.*;
* Holds a user's cached meta for a given context
*/
@RequiredArgsConstructor
public class MetaData {
public class MetaCache implements MetaData {
private final Contexts contexts;
private final SortedMap<Integer, String> prefixes = new TreeMap<>(Comparator.reverseOrder());
@ -46,9 +48,11 @@ public class MetaData {
public void loadMeta(SortedSet<LocalizedNode> nodes) {
invalidateCache();
Map<String, String> contexts = new HashMap<>(this.contexts.getContext());
String server = contexts.remove("server");
String world = contexts.remove("world");
MutableContextSet contexts = MutableContextSet.fromSet(this.contexts.getContexts());
String server = contexts.getValues("server").stream().findAny().orElse(null);
String world = contexts.getValues("world").stream().findAny().orElse(null);
contexts.removeAll("server");
contexts.removeAll("world");
for (LocalizedNode ln : nodes) {
Node n = ln.getNode();
@ -105,7 +109,7 @@ public class MetaData {
}
}
public void invalidateCache() {
private void invalidateCache() {
synchronized (meta) {
meta.clear();
}
@ -117,24 +121,28 @@ public class MetaData {
}
}
@Override
public Map<String, String> getMeta() {
synchronized (meta) {
return ImmutableMap.copyOf(meta);
}
}
@Override
public SortedMap<Integer, String> getPrefixes() {
synchronized (prefixes) {
return ImmutableSortedMap.copyOfSorted(prefixes);
}
}
@Override
public SortedMap<Integer, String> getSuffixes() {
synchronized (suffixes) {
return ImmutableSortedMap.copyOfSorted(suffixes);
}
}
@Override
public String getPrefix() {
synchronized (prefixes) {
if (prefixes.isEmpty()) {
@ -145,6 +153,7 @@ public class MetaData {
}
}
@Override
public String getSuffix() {
synchronized (suffixes) {
if (suffixes.isEmpty()) {

View File

@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableMap;
import lombok.NonNull;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.caching.PermissionData;
import me.lucko.luckperms.common.calculators.CalculatorFactory;
import me.lucko.luckperms.common.calculators.PermissionCalculator;
import me.lucko.luckperms.common.users.User;
@ -36,7 +37,7 @@ import java.util.concurrent.ConcurrentHashMap;
/**
* Holds a user's cached permissions for a given context
*/
public class PermissionData {
public class PermissionCache implements PermissionData {
/**
* The raw set of permission strings.
@ -50,11 +51,12 @@ public class PermissionData {
*/
private final PermissionCalculator calculator;
public PermissionData(Contexts contexts, User user, CalculatorFactory calculatorFactory) {
public PermissionCache(Contexts contexts, User user, CalculatorFactory calculatorFactory) {
permissions = new ConcurrentHashMap<>();
calculator = calculatorFactory.build(contexts, user, permissions);
}
@Override
public void invalidateCache() {
calculator.invalidateCache();
}
@ -71,10 +73,12 @@ public class PermissionData {
}
}
@Override
public Map<String, Boolean> getImmutableBacking() {
return ImmutableMap.copyOf(permissions);
}
@Override
public Tristate getPermissionValue(@NonNull String permission) {
return calculator.getPermissionValue(permission);
}

View File

@ -28,8 +28,12 @@ import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.caching.MetaData;
import me.lucko.luckperms.api.caching.PermissionData;
import me.lucko.luckperms.api.caching.UserData;
import me.lucko.luckperms.common.calculators.CalculatorFactory;
import me.lucko.luckperms.common.users.User;
@ -39,7 +43,7 @@ import java.util.Set;
* Holds an easily accessible cache of a user's data in a number of contexts
*/
@RequiredArgsConstructor
public class UserData {
public class UserCache implements UserData {
/**
* The user whom this data instance is representing
@ -51,124 +55,87 @@ public class UserData {
*/
private final CalculatorFactory calculatorFactory;
private final LoadingCache<Contexts, PermissionData> permission = CacheBuilder.newBuilder()
.build(new CacheLoader<Contexts, PermissionData>() {
private final LoadingCache<Contexts, PermissionCache> permission = CacheBuilder.newBuilder()
.build(new CacheLoader<Contexts, PermissionCache>() {
@Override
public PermissionData load(Contexts contexts) {
public PermissionCache load(Contexts contexts) {
return calculatePermissions(contexts);
}
@Override
public ListenableFuture<PermissionData> reload(Contexts contexts, PermissionData oldData) {
public ListenableFuture<PermissionCache> reload(Contexts contexts, PermissionCache oldData) {
oldData.comparePermissions(user.exportNodes(contexts, true));
return Futures.immediateFuture(oldData);
}
});
private final LoadingCache<Contexts, MetaData> meta = CacheBuilder.newBuilder()
.build(new CacheLoader<Contexts, MetaData>() {
private final LoadingCache<Contexts, MetaCache> meta = CacheBuilder.newBuilder()
.build(new CacheLoader<Contexts, MetaCache>() {
@Override
public MetaData load(Contexts contexts) {
public MetaCache load(Contexts contexts) {
return calculateMeta(contexts);
}
@Override
public ListenableFuture<MetaData> reload(Contexts contexts, MetaData oldData) {
public ListenableFuture<MetaCache> reload(Contexts contexts, MetaCache oldData) {
oldData.loadMeta(user.getAllNodes(null, contexts));
return Futures.immediateFuture(oldData);
}
});
/**
* Gets PermissionData from the cache, given a specified context.
* If the data is not cached, it is calculated. Therefore, this call could be costly.
* @param contexts the contexts to get the permission data in
* @return a permission data instance
*/
public PermissionData getPermissionData(Contexts contexts) {
@Override
public PermissionData getPermissionData(@NonNull Contexts contexts) {
return permission.getUnchecked(contexts);
}
/**
* Gets MetaData from the cache, given a specified context.
* If the data is not cached, it is calculated. Therefore, this call could be costly.
* @param contexts the contexts to get the permission data in
* @return a meta data instance
*/
public MetaData getMetaData(Contexts contexts) {
@Override
public MetaData getMetaData(@NonNull Contexts contexts) {
return meta.getUnchecked(contexts);
}
/**
* Calculates permission data, bypassing the cache.
* @param contexts the contexts to get permission data in
* @return a permission data instance
*/
public PermissionData calculatePermissions(Contexts contexts) {
PermissionData data = new PermissionData(contexts, user, calculatorFactory);
@Override
public PermissionCache calculatePermissions(@NonNull Contexts contexts) {
PermissionCache data = new PermissionCache(contexts, user, calculatorFactory);
data.setPermissions(user.exportNodes(contexts, true));
return data;
}
/**
* Calculates meta data, bypassing the cache.
* @param contexts the contexts to get meta data in
* @return a meta data instance
*/
public MetaData calculateMeta(Contexts contexts) {
MetaData data = new MetaData(contexts);
@Override
public MetaCache calculateMeta(@NonNull Contexts contexts) {
MetaCache data = new MetaCache(contexts);
data.loadMeta(user.getAllNodes(null, contexts));
return data;
}
/**
* Calculates permission data and stores it in the cache. If there is already data cached for the given contexts,
* and if the resultant output is different, the cached value is updated.
* @param contexts the contexts to recalculate in.
*/
public void recalculatePermissions(Contexts contexts) {
@Override
public void recalculatePermissions(@NonNull Contexts contexts) {
permission.refresh(contexts);
}
/**
* Calculates meta data and stores it in the cache. If there is already data cached for the given contexts,
* and if the resultant output is different, the cached value is updated.
* @param contexts the contexts to recalculate in.
*/
public void recalculateMeta(Contexts contexts) {
@Override
public void recalculateMeta(@NonNull Contexts contexts) {
meta.refresh(contexts);
}
/**
* Calls {@link #recalculatePermissions(Contexts)} for all current loaded contexts
*/
@Override
public void recalculatePermissions() {
Set<Contexts> keys = ImmutableSet.copyOf(permission.asMap().keySet());
keys.forEach(permission::refresh);
}
/**
* Calls {@link #recalculateMeta(Contexts)} for all current loaded contexts
*/
@Override
public void recalculateMeta() {
Set<Contexts> keys = ImmutableSet.copyOf(meta.asMap().keySet());
keys.forEach(meta::refresh);
}
/**
* Calls {@link #preCalculate(Contexts)} for the given contexts
* @param contexts a set of contexts
*/
public void preCalculate(Set<Contexts> contexts) {
@Override
public void preCalculate(@NonNull Set<Contexts> contexts) {
contexts.forEach(this::preCalculate);
}
/**
* Ensures that PermissionData and MetaData is cached for a context. If the cache does not contain any data for the
* context, it will be calculated and saved.
* @param contexts the contexts to pre-calculate for
*/
public void preCalculate(Contexts contexts) {
@Override
public void preCalculate(@NonNull Contexts contexts) {
permission.getUnchecked(contexts);
meta.getUnchecked(contexts);
}
@ -178,6 +145,7 @@ public class UserData {
meta.invalidateAll();
}
@Override
public void invalidatePermissionCalculators() {
permission.asMap().values().forEach(PermissionData::invalidateCache);
}

View File

@ -24,6 +24,7 @@ package me.lucko.luckperms.common.contexts;
import me.lucko.luckperms.api.context.ContextListener;
import me.lucko.luckperms.api.context.IContextCalculator;
import me.lucko.luckperms.api.context.MutableContextSet;
import java.util.List;
import java.util.Map;
@ -47,7 +48,7 @@ public class ContextManager<T> {
listeners.add(listener);
}
public Map<String, String> giveApplicableContext(T subject, Map<String, String> accumulator) {
public MutableContextSet giveApplicableContext(T subject, MutableContextSet accumulator) {
for (IContextCalculator<T> calculator : calculators) {
calculator.giveApplicableContext(subject, accumulator);
}

View File

@ -22,8 +22,10 @@
package me.lucko.luckperms.common.contexts;
import com.google.common.collect.Maps;
import lombok.AllArgsConstructor;
import me.lucko.luckperms.api.context.ContextCalculator;
import me.lucko.luckperms.api.context.MutableContextSet;
import java.util.Map;
@ -32,8 +34,8 @@ public class ServerCalculator<T> extends ContextCalculator<T> {
private final String server;
@Override
public Map<String, String> giveApplicableContext(T subject, Map<String, String> accumulator) {
accumulator.put("server", server);
public MutableContextSet giveApplicableContext(T subject, MutableContextSet accumulator) {
accumulator.add(Maps.immutableEntry("server", server));
return accumulator;
}

View File

@ -23,9 +23,10 @@
package me.lucko.luckperms.common.core;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
import lombok.*;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.common.constants.Patterns;
import me.lucko.luckperms.common.utils.ArgumentChecker;
@ -60,7 +61,8 @@ public class Node implements me.lucko.luckperms.api.Node {
private long expireAt = 0L;
private final Map<String, String> extraContexts;
@Getter
private final ContextSet contexts;
// Cache the state
private Tristate isPrefix = Tristate.UNDEFINED;
@ -74,9 +76,9 @@ public class Node implements me.lucko.luckperms.api.Node {
* @param expireAt the time when the node will expire
* @param server the server this node applies on
* @param world the world this node applies on
* @param extraContexts any additional contexts applying to this node
* @param contexts any additional contexts applying to this node
*/
public Node(String permission, boolean value, boolean override, long expireAt, String server, String world, Map<String, String> extraContexts) {
public Node(String permission, boolean value, boolean override, long expireAt, String server, String world, ContextSet contexts) {
if (permission == null || permission.equals("")) {
throw new IllegalArgumentException("Empty permission");
}
@ -99,12 +101,7 @@ public class Node implements me.lucko.luckperms.api.Node {
this.expireAt = expireAt;
this.server = server;
this.world = world;
ImmutableMap.Builder<String, String> contexts = ImmutableMap.builder();
if (extraContexts != null) {
contexts.putAll(extraContexts);
}
this.extraContexts = contexts.build();
this.contexts = contexts == null ? ContextSet.empty() : contexts.makeImmutable();
}
@Override
@ -176,11 +173,6 @@ public class Node implements me.lucko.luckperms.api.Node {
return isTemporary() && expireAt < (System.currentTimeMillis() / 1000L);
}
@Override
public Map<String, String> getExtraContexts() {
return ImmutableMap.copyOf(extraContexts);
}
@Override
public boolean isGroupNode() {
return getPermission().toLowerCase().startsWith("group.");
@ -316,31 +308,28 @@ public class Node implements me.lucko.luckperms.api.Node {
}
@Override
public boolean shouldApplyWithContext(Map<String, String> context, boolean worldAndServer) {
if (extraContexts.isEmpty() && !isServerSpecific() && !isWorldSpecific()) {
public boolean shouldApplyWithContext(ContextSet context, boolean worldAndServer) {
if (contexts.isEmpty() && !isServerSpecific() && !isWorldSpecific()) {
return true;
}
if (worldAndServer) {
if (isWorldSpecific()) {
if (context == null) return false;
if (!context.containsKey("world")) return false;
if (!context.get("world").equalsIgnoreCase(world)) return false;
if (!context.hasIgnoreCase("world", world)) return false;
}
if (isServerSpecific()) {
if (context == null) return false;
if (!context.containsKey("server")) return false;
if (!context.get("server").equalsIgnoreCase(server)) return false;
if (!context.hasIgnoreCase("server", server)) return false;
}
}
if (!extraContexts.isEmpty()) {
if (!contexts.isEmpty()) {
if (context == null) return false;
for (Map.Entry<String, String> c : extraContexts.entrySet()) {
if (!context.containsKey(c.getKey())) return false;
if (!context.get(c.getKey()).equalsIgnoreCase(c.getValue())) return false;
for (Map.Entry<String, String> c : contexts.toSet()) {
if (!context.hasIgnoreCase(c.getKey(), c.getValue())) return false;
}
}
@ -348,7 +337,7 @@ public class Node implements me.lucko.luckperms.api.Node {
}
@Override
public boolean shouldApplyWithContext(Map<String, String> context) {
public boolean shouldApplyWithContext(ContextSet context) {
return shouldApplyWithContext(context, true);
}
@ -478,9 +467,9 @@ public class Node implements me.lucko.luckperms.api.Node {
}
}
if (!extraContexts.isEmpty()) {
if (!contexts.isEmpty()) {
builder.append("(");
for (Map.Entry<String, String> entry : extraContexts.entrySet()) {
for (Map.Entry<String, String> entry : contexts.toSet()) {
builder.append(entry.getKey()).append("=").append(entry.getValue()).append(",");
}
@ -533,7 +522,7 @@ public class Node implements me.lucko.luckperms.api.Node {
return false;
}
if (!other.getExtraContexts().equals(this.getExtraContexts())) {
if (!other.getContexts().equals(this.getContexts())) {
return false;
}
@ -570,7 +559,7 @@ public class Node implements me.lucko.luckperms.api.Node {
return false;
}
if (!other.getExtraContexts().equals(this.getExtraContexts())) {
if (!other.getContexts().equals(this.getContexts())) {
return false;
}
@ -603,7 +592,7 @@ public class Node implements me.lucko.luckperms.api.Node {
return false;
}
if (!other.getExtraContexts().equals(this.getExtraContexts())) {
if (!other.getContexts().equals(this.getContexts())) {
return false;
}
@ -681,8 +670,7 @@ public class Node implements me.lucko.luckperms.api.Node {
private String server = null;
private String world = null;
private long expireAt = 0L;
private final Map<String, String> extraContexts = new HashMap<>();
private final MutableContextSet extraContexts = new MutableContextSet();
Builder(String permission, boolean shouldConvertContexts) {
if (!shouldConvertContexts) {
@ -696,7 +684,7 @@ public class Node implements me.lucko.luckperms.api.Node {
this.permission = contextParts.get(1);
try {
extraContexts.putAll(Splitter.on(',').withKeyValueSeparator('=').split(contextParts.get(0)));
extraContexts.addAll(Splitter.on(',').withKeyValueSeparator('=').split(contextParts.get(0)));
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
@ -711,6 +699,7 @@ public class Node implements me.lucko.luckperms.api.Node {
this.server = other.getServer().orElse(null);
this.world = other.getWorld().orElse(null);
this.expireAt = other.isPermanent() ? 0L : other.getExpiryUnixTime();
this.extraContexts.addAll(other.getContexts());
}
@Override
@ -760,7 +749,7 @@ public class Node implements me.lucko.luckperms.api.Node {
@Override
public me.lucko.luckperms.api.Node.Builder withExtraContext(@NonNull String key, @NonNull String value) {
switch (key) {
switch (key.toLowerCase()) {
case "server":
setServer(value);
break;
@ -768,25 +757,37 @@ public class Node implements me.lucko.luckperms.api.Node {
setWorld(value);
break;
default:
this.extraContexts.put(key, value);
this.extraContexts.add(key, value);
break;
}
return this;
}
@Override
public me.lucko.luckperms.api.Node.Builder withExtraContext(Map<String, String> map) {
map.entrySet().forEach(this::withExtraContext);
return this;
}
@Override
public me.lucko.luckperms.api.Node.Builder withExtraContext(Map.Entry<String, String> entry) {
withExtraContext(entry.getKey(), entry.getValue());
return this;
}
@Override
public me.lucko.luckperms.api.Node.Builder withExtraContext(Map<String, String> map) {
withExtraContext(ContextSet.fromMap(map));
return this;
}
@Override
public me.lucko.luckperms.api.Node.Builder withExtraContext(Set<Map.Entry<String, String>> context) {
withExtraContext(ContextSet.fromEntries(context));
return this;
}
@Override
public me.lucko.luckperms.api.Node.Builder withExtraContext(ContextSet set) {
set.toSet().forEach(this::withExtraContext);
return this;
}
@Override
public me.lucko.luckperms.api.Node build() {
return new Node(permission, value, override, expireAt, server, world, extraContexts);

View File

@ -32,6 +32,7 @@ import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.api.event.events.*;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.api.internal.GroupLink;
@ -368,11 +369,11 @@ public abstract class PermissionHolder {
.filter(Node::isGroupNode)
.collect(Collectors.toSet());
Map<String, String> contexts = new HashMap<>(context.getContext());
String server = contexts.get("server");
String world = contexts.get("world");
contexts.remove("server");
contexts.remove("world");
MutableContextSet contexts = MutableContextSet.fromSet(context.getContexts());
String server = contexts.getValues("server").stream().findAny().orElse(null);
String world = contexts.getValues("world").stream().findAny().orElse(null);
contexts.removeAll("server");
contexts.removeAll("world");
parents.removeIf(node ->
!node.shouldApplyOnServer(server, context.isApplyGlobalGroups(), plugin.getConfiguration().isApplyingRegex()) ||
@ -419,11 +420,11 @@ public abstract class PermissionHolder {
allNodes = new TreeSet<>((SortedSet<LocalizedNode>) getPermissions(true));
}
Map<String, String> contexts = new HashMap<>(context.getContext());
String server = contexts.get("server");
String world = contexts.get("world");
contexts.remove("server");
contexts.remove("world");
MutableContextSet contexts = MutableContextSet.fromSet(context.getContexts());
String server = contexts.getValues("server").stream().findAny().orElse(null);
String world = contexts.getValues("world").stream().findAny().orElse(null);
contexts.removeAll("server");
contexts.removeAll("world");
allNodes.removeIf(node ->
!node.shouldApplyOnServer(server, context.isIncludeGlobal(), plugin.getConfiguration().isApplyingRegex()) ||

View File

@ -26,10 +26,11 @@ import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import me.lucko.luckperms.api.caching.UserData;
import me.lucko.luckperms.api.event.events.UserPermissionRefreshEvent;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.api.internal.UserLink;
import me.lucko.luckperms.common.caching.UserData;
import me.lucko.luckperms.common.caching.UserCache;
import me.lucko.luckperms.common.core.PermissionHolder;
import me.lucko.luckperms.common.utils.BufferedRequest;
import me.lucko.luckperms.common.utils.Identifiable;
@ -61,7 +62,7 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
private String primaryGroup = null;
@Getter
private UserData userData = null;
private UserCache userData = null;
@Getter
private BufferedRequest<Void> refreshBuffer = new BufferedRequest<Void>(1000L, r -> getPlugin().doAsync(r)) {
@ -103,7 +104,7 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
return;
}
userData = new UserData(this, getPlugin().getCalculatorFactory());
userData = new UserCache(this, getPlugin().getCalculatorFactory());
userData.preCalculate(getPlugin().getPreProcessContexts(op));
}

View File

@ -23,13 +23,8 @@
package me.lucko.luckperms.common.utils;
import lombok.*;
import lombok.experimental.Delegate;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.Tristate;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* Holds a Node and where it was inherited from
@ -38,11 +33,12 @@ import java.util.Optional;
@ToString
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class LocalizedNode implements me.lucko.luckperms.api.LocalizedNode {
public static LocalizedNode of(@NonNull me.lucko.luckperms.api.Node node, @NonNull String location) {
public static LocalizedNode of(@NonNull Node node, @NonNull String location) {
return new LocalizedNode(node, location);
}
private final me.lucko.luckperms.api.Node node;
@Delegate
private final Node node;
private final String location;
@Override
@ -54,204 +50,4 @@ public class LocalizedNode implements me.lucko.luckperms.api.LocalizedNode {
public boolean equals(Object obj) {
return node.equals(obj);
}
@Override
public String getPermission() {
return node.getPermission();
}
@Override
public String getKey() {
return node.getKey();
}
@Override
public Boolean getValue() {
return node.getValue();
}
@Override
public Boolean setValue(Boolean value) {
return node.setValue(value);
}
@Override
public Tristate getTristate() {
return node.getTristate();
}
@Override
public boolean isNegated() {
return node.isNegated();
}
@Override
public boolean isOverride() {
return node.isOverride();
}
@Override
public Optional<String> getServer() {
return node.getServer();
}
@Override
public Optional<String> getWorld() {
return node.getWorld();
}
@Override
public boolean isServerSpecific() {
return node.isServerSpecific();
}
@Override
public boolean isWorldSpecific() {
return node.isWorldSpecific();
}
@Override
public boolean shouldApplyOnServer(String server, boolean includeGlobal, boolean applyRegex) {
return node.shouldApplyOnServer(server, includeGlobal, applyRegex);
}
@Override
public boolean shouldApplyOnWorld(String world, boolean includeGlobal, boolean applyRegex) {
return node.shouldApplyOnWorld(world, includeGlobal, applyRegex);
}
@Override
public boolean shouldApplyWithContext(Map<String, String> context, boolean worldAndServer) {
return node.shouldApplyWithContext(context, worldAndServer);
}
@Override
public boolean shouldApplyWithContext(Map<String, String> context) {
return node.shouldApplyWithContext(context);
}
@Override
public boolean shouldApplyOnAnyServers(List<String> servers, boolean includeGlobal) {
return node.shouldApplyOnAnyServers(servers, includeGlobal);
}
@Override
public boolean shouldApplyOnAnyWorlds(List<String> worlds, boolean includeGlobal) {
return node.shouldApplyOnAnyWorlds(worlds, includeGlobal);
}
@Override
public List<String> resolveWildcard(List<String> possibleNodes) {
return node.resolveWildcard(possibleNodes);
}
@Override
public List<String> resolveShorthand() {
return node.resolveShorthand();
}
@Override
public boolean isTemporary() {
return node.isTemporary();
}
@Override
public boolean isPermanent() {
return node.isPermanent();
}
@Override
public long getExpiryUnixTime() {
return node.getExpiryUnixTime();
}
@Override
public Date getExpiry() {
return node.getExpiry();
}
@Override
public long getSecondsTilExpiry() {
return node.getSecondsTilExpiry();
}
@Override
public boolean hasExpired() {
return node.hasExpired();
}
@Override
public Map<String, String> getExtraContexts() {
return node.getExtraContexts();
}
@Override
public String toSerializedNode() {
return node.toSerializedNode();
}
@Override
public boolean isGroupNode() {
return node.isGroupNode();
}
@Override
public String getGroupName() {
return node.getGroupName();
}
@Override
public boolean isWildcard() {
return node.isWildcard();
}
@Override
public int getWildcardLevel() {
return node.getWildcardLevel();
}
@Override
public boolean isMeta() {
return node.isMeta();
}
@Override
public Map.Entry<String, String> getMeta() {
return node.getMeta();
}
@Override
public boolean isPrefix() {
return node.isPrefix();
}
@Override
public Map.Entry<Integer, String> getPrefix() {
return node.getPrefix();
}
@Override
public boolean isSuffix() {
return node.isSuffix();
}
@Override
public Map.Entry<Integer, String> getSuffix() {
return node.getSuffix();
}
@Override
public boolean equalsIgnoringValue(Node other) {
return node.equalsIgnoringValue(other);
}
@Override
public boolean almostEquals(Node other) {
return node.almostEquals(other);
}
@Override
public boolean equalsIgnoringValueOrTemp(Node other) {
return node.equalsIgnoringValueOrTemp(other);
}
}

View File

@ -6,7 +6,7 @@
<groupId>me.lucko.luckperms</groupId>
<artifactId>luckperms</artifactId>
<version>2.12-SNAPSHOT</version>
<version>2.13-SNAPSHOT</version>
<modules>
<module>common</module>
<module>api</module>
@ -38,7 +38,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<release.version>2.12</release.version>
<release.version>2.13</release.version>
</properties>
<distributionManagement>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.12-SNAPSHOT</version>
<version>2.13-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -49,7 +49,7 @@ public class SpongeCalculatorFactory implements CalculatorFactory {
if (plugin.getConfiguration().isApplyingRegex()) {
processors.add(new RegexProcessor(map));
}
processors.add(new DefaultsProcessor(plugin.getService(), LuckPermsService.convertContexts(contexts.getContext())));
processors.add(new DefaultsProcessor(plugin.getService(), LuckPermsService.convertContexts(contexts.getContexts())));
return new PermissionCalculator(plugin, user.getName(), plugin.getConfiguration().isDebugPermissionChecks(), processors);
}

View File

@ -22,7 +22,8 @@
package me.lucko.luckperms.sponge;
import me.lucko.luckperms.common.caching.UserData;
import me.lucko.luckperms.api.caching.UserData;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.common.constants.Message;
import me.lucko.luckperms.common.users.User;
import me.lucko.luckperms.common.utils.AbstractListener;
@ -34,9 +35,7 @@ import org.spongepowered.api.profile.GameProfile;
import org.spongepowered.api.text.serializer.TextSerializers;
import org.spongepowered.api.world.World;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
@ -77,7 +76,7 @@ public class SpongeListener extends AbstractListener {
// Attempt to pre-process some permissions for the user to save time later. Might not work, but it's better than nothing.
Optional<Player> p = e.getCause().first(Player.class);
if (p.isPresent()) {
Map<String, String> context = plugin.getContextManager().giveApplicableContext(p.get(), new HashMap<>());
MutableContextSet context = plugin.getContextManager().giveApplicableContext(p.get(), MutableContextSet.empty());
List<String> worlds = plugin.getGame().getServer().getWorlds().stream()
.map(World::getName)
@ -88,8 +87,9 @@ public class SpongeListener extends AbstractListener {
data.preCalculate(plugin.getService().calculateContexts(LuckPermsService.convertContexts(context)));
for (String world : worlds) {
Map<String, String> modified = new HashMap<>(context);
modified.put("world", world);
MutableContextSet modified = MutableContextSet.fromSet(context);
modified.removeAll("world");
modified.add("world", world);
data.preCalculate(plugin.getService().calculateContexts(LuckPermsService.convertContexts(modified)));
}
});

View File

@ -24,6 +24,7 @@ package me.lucko.luckperms.sponge.contexts;
import lombok.AllArgsConstructor;
import me.lucko.luckperms.api.context.ContextCalculator;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.Subject;
@ -36,12 +37,12 @@ public class SpongeCalculatorLink extends ContextCalculator<Subject> {
private final org.spongepowered.api.service.context.ContextCalculator<Subject> calculator;
@Override
public Map<String, String> giveApplicableContext(Subject subject, Map<String, String> accumulator) {
public MutableContextSet giveApplicableContext(Subject subject, MutableContextSet accumulator) {
Set<Context> contexts = LuckPermsService.convertContexts(accumulator);
calculator.accumulateContexts(subject, contexts);
accumulator.clear();
accumulator.putAll(LuckPermsService.convertContexts(contexts));
accumulator.addAll(LuckPermsService.convertContexts(contexts));
return accumulator;
}

View File

@ -24,6 +24,7 @@ package me.lucko.luckperms.sponge.contexts;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.context.ContextCalculator;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.common.commands.Util;
import me.lucko.luckperms.sponge.LPSpongePlugin;
import org.spongepowered.api.entity.living.player.Player;
@ -39,7 +40,7 @@ public class WorldCalculator extends ContextCalculator<Subject> {
private final LPSpongePlugin plugin;
@Override
public Map<String, String> giveApplicableContext(Subject subject, Map<String, String> accumulator) {
public MutableContextSet giveApplicableContext(Subject subject, MutableContextSet accumulator) {
UUID uuid = Util.parseUuid(subject.getIdentifier());
if (uuid == null) {
return accumulator;
@ -50,7 +51,7 @@ public class WorldCalculator extends ContextCalculator<Subject> {
return accumulator;
}
accumulator.put(Context.WORLD_KEY, p.get().getWorld().getName());
accumulator.add(Context.WORLD_KEY, p.get().getWorld().getName());
return accumulator;
}

View File

@ -28,13 +28,13 @@ import lombok.Getter;
import lombok.NonNull;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.common.groups.Group;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.NodeTree;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectCollection;
import org.spongepowered.api.service.permission.SubjectData;
import org.spongepowered.api.util.Tristate;
import java.util.List;
@ -156,7 +156,7 @@ public class LuckPermsGroupSubject implements Subject {
@Override
public Set<Context> getActiveContexts() {
return SubjectData.GLOBAL_CONTEXT;
return LuckPermsService.convertContexts(service.getPlugin().getContextManager().giveApplicableContext(this, MutableContextSet.empty()));
}
private Optional<String> getChatMeta(Set<Context> contexts, boolean prefix) {

View File

@ -22,10 +22,15 @@
package me.lucko.luckperms.sponge.service;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import lombok.*;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.contexts.SpongeCalculatorLink;
import me.lucko.luckperms.sponge.service.collections.GroupCollection;
@ -48,28 +53,25 @@ import java.util.stream.Collectors;
/**
* The LuckPerms implementation of the Sponge Permission Service
*/
@Getter
public class LuckPermsService implements PermissionService {
public static final String SERVER_CONTEXT = "server";
@Getter
private final LPSpongePlugin plugin;
@Getter
private final SubjectStorage storage;
@Getter
private final UserCollection userSubjects;
@Getter
private final GroupCollection groupSubjects;
@Getter
private final PersistedCollection defaultSubjects;
@Getter
private final Set<PermissionDescription> descriptionSet;
private final Map<String, SubjectCollection> subjects;
@Getter(value = AccessLevel.NONE)
private final LoadingCache<String, SubjectCollection> collections = CacheBuilder.newBuilder()
.build(new CacheLoader<String, SubjectCollection>() {
@Override
public SubjectCollection load(String s) {
return new SimpleCollection(LuckPermsService.this, s);
}
});
public LuckPermsService(LPSpongePlugin plugin) {
this.plugin = plugin;
@ -81,10 +83,9 @@ public class LuckPermsService implements PermissionService {
defaultSubjects = new PersistedCollection(this, "defaults");
defaultSubjects.loadAll();
subjects = new ConcurrentHashMap<>();
subjects.put(PermissionService.SUBJECTS_USER, userSubjects);
subjects.put(PermissionService.SUBJECTS_GROUP, groupSubjects);
subjects.put("defaults", defaultSubjects);
collections.put(PermissionService.SUBJECTS_USER, userSubjects);
collections.put(PermissionService.SUBJECTS_GROUP, groupSubjects);
collections.put("defaults", defaultSubjects);
descriptionSet = ConcurrentHashMap.newKeySet();
}
@ -100,16 +101,12 @@ public class LuckPermsService implements PermissionService {
@Override
public SubjectCollection getSubjects(String s) {
if (!subjects.containsKey(s)) {
subjects.put(s, new SimpleCollection(this, s));
}
return subjects.get(s);
return collections.getUnchecked(s.toLowerCase());
}
@Override
public Map<String, SubjectCollection> getKnownSubjects() {
return ImmutableMap.copyOf(subjects);
return ImmutableMap.copyOf(collections.asMap());
}
@Override
@ -150,16 +147,17 @@ public class LuckPermsService implements PermissionService {
plugin.getConfiguration().isIncludingGlobalWorldPerms(),
true,
plugin.getConfiguration().isApplyingGlobalGroups(),
plugin.getConfiguration().isApplyingGlobalWorldGroups()
plugin.getConfiguration().isApplyingGlobalWorldGroups(),
false
);
}
public static Map<String, String> convertContexts(Set<Context> contexts) {
return contexts.stream().collect(Collectors.toMap(Context::getKey, Context::getValue));
public static ContextSet convertContexts(Set<Context> contexts) {
return ContextSet.fromEntries(contexts.stream().map(c -> Maps.immutableEntry(c.getKey(), c.getValue())).collect(Collectors.toSet()));
}
public static Set<Context> convertContexts(Map<String, String> contexts) {
return contexts.entrySet().stream().map(e -> new Context(e.getKey(), e.getValue())).collect(Collectors.toSet());
public static Set<Context> convertContexts(ContextSet contexts) {
return contexts.toSet().stream().map(e -> new Context(e.getKey(), e.getValue())).collect(Collectors.toSet());
}
public static Tristate convertTristate(me.lucko.luckperms.api.Tristate tristate) {

View File

@ -28,6 +28,7 @@ import com.google.common.collect.ImmutableSet;
import lombok.AllArgsConstructor;
import lombok.Getter;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.core.PermissionHolder;
import me.lucko.luckperms.common.groups.Group;
import me.lucko.luckperms.common.users.User;
@ -65,7 +66,7 @@ public class LuckPermsSubjectData implements SubjectData {
Map<Set<Context>, Map<String, Boolean>> perms = new HashMap<>();
for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) {
Set<Context> contexts = LuckPermsService.convertContexts(n.getExtraContexts());
Set<Context> contexts = LuckPermsService.convertContexts(n.getContexts());
if (n.isServerSpecific()) {
contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
@ -185,7 +186,7 @@ public class LuckPermsSubjectData implements SubjectData {
continue;
}
Set<Context> contexts = LuckPermsService.convertContexts(n.getExtraContexts());
Set<Context> contexts = LuckPermsService.convertContexts(n.getContexts());
if (n.isServerSpecific()) {
contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
@ -225,7 +226,7 @@ public class LuckPermsSubjectData implements SubjectData {
public boolean addParent(Set<Context> set, Subject subject) {
if (subject instanceof LuckPermsGroupSubject) {
LuckPermsGroupSubject permsSubject = ((LuckPermsGroupSubject) subject);
Map<String, String> contexts = LuckPermsService.convertContexts(set);
ContextSet contexts = LuckPermsService.convertContexts(set);
try {
if (enduring) {
@ -249,7 +250,7 @@ public class LuckPermsSubjectData implements SubjectData {
public boolean removeParent(Set<Context> set, Subject subject) {
if (subject instanceof LuckPermsGroupSubject) {
LuckPermsGroupSubject permsSubject = ((LuckPermsGroupSubject) subject);
Map<String, String> contexts = LuckPermsService.convertContexts(set);
ContextSet contexts = LuckPermsService.convertContexts(set);
try {
if (enduring) {
@ -295,7 +296,7 @@ public class LuckPermsSubjectData implements SubjectData {
@Override
public boolean clearParents(Set<Context> set) {
Map<String, String> context = LuckPermsService.convertContexts(set);
ContextSet context = LuckPermsService.convertContexts(set);
List<Node> toRemove = (enduring ? holder.getNodes() : holder.getTransientNodes()).stream()
.filter(Node::isGroupNode)
@ -336,7 +337,7 @@ public class LuckPermsSubjectData implements SubjectData {
continue;
}
Set<Context> contexts = LuckPermsService.convertContexts(n.getExtraContexts());
Set<Context> contexts = LuckPermsService.convertContexts(n.getContexts());
if (n.isServerSpecific()) {
contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
@ -384,7 +385,7 @@ public class LuckPermsSubjectData implements SubjectData {
@Override
public Map<String, String> getOptions(Set<Context> set) {
ImmutableMap.Builder<String, String> options = ImmutableMap.builder();
Map<String, String> contexts = LuckPermsService.convertContexts(set);
ContextSet contexts = LuckPermsService.convertContexts(set);
int prefixPriority = Integer.MIN_VALUE;
int suffixPriority = Integer.MIN_VALUE;
@ -431,7 +432,7 @@ public class LuckPermsSubjectData implements SubjectData {
@Override
public boolean setOption(Set<Context> set, String key, String value) {
Map<String, String> context = LuckPermsService.convertContexts(set);
ContextSet context = LuckPermsService.convertContexts(set);
key = escapeCharacters(key);
value = escapeCharacters(value);
@ -455,7 +456,7 @@ public class LuckPermsSubjectData implements SubjectData {
@Override
public boolean clearOptions(Set<Context> set) {
Map<String, String> context = LuckPermsService.convertContexts(set);
ContextSet context = LuckPermsService.convertContexts(set);
List<Node> toRemove = (enduring ? holder.getNodes() : holder.getTransientNodes()).stream()
.filter(n -> n.isMeta() || n.isPrefix() || n.isSuffix())

View File

@ -26,7 +26,8 @@ import com.google.common.collect.ImmutableList;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import me.lucko.luckperms.common.caching.MetaData;
import me.lucko.luckperms.api.caching.MetaData;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.common.users.User;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandSource;
@ -36,8 +37,10 @@ import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectCollection;
import org.spongepowered.api.util.Tristate;
import java.util.*;
import java.util.stream.Collectors;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
@EqualsAndHashCode(of = "user")
public class LuckPermsUserSubject implements Subject {
@ -171,8 +174,6 @@ public class LuckPermsUserSubject implements Subject {
@Override
public Set<Context> getActiveContexts() {
return service.getPlugin().getContextManager().giveApplicableContext(this, new HashMap<>()).entrySet().stream()
.map(e -> new Context(e.getKey(), e.getValue()))
.collect(Collectors.toSet());
return LuckPermsService.convertContexts(service.getPlugin().getContextManager().giveApplicableContext(this, MutableContextSet.empty()));
}
}

View File

@ -22,6 +22,9 @@
package me.lucko.luckperms.sponge.service.persisted;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@ -35,7 +38,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
@ -48,34 +50,35 @@ public class PersistedCollection implements SubjectCollection {
@Getter
private final String identifier;
private final Map<String, PersistedSubject> subjects = new ConcurrentHashMap<>();
private final LoadingCache<String, PersistedSubject> subjects = CacheBuilder.newBuilder()
.build(new CacheLoader<String, PersistedSubject>() {
@Override
public PersistedSubject load(String s) {
return new PersistedSubject(s, service, PersistedCollection.this);
}
});
public void loadAll() {
Map<String, SubjectDataHolder> holders = service.getStorage().loadAllFromFile(identifier);
for (Map.Entry<String, SubjectDataHolder> e : holders.entrySet()) {
PersistedSubject subject = new PersistedSubject(e.getKey(), service, this);
PersistedSubject subject = get(e.getKey());
subject.loadData(e.getValue());
subjects.put(e.getKey(), subject);
}
}
@Override
public synchronized Subject get(@NonNull String id) {
if (!subjects.containsKey(id)) {
subjects.put(id, new PersistedSubject(id, service, this));
}
return subjects.get(id);
public PersistedSubject get(@NonNull String id) {
return subjects.getUnchecked(id.toLowerCase());
}
@Override
public boolean hasRegistered(@NonNull String id) {
return subjects.containsKey(id);
return subjects.asMap().containsKey(id.toLowerCase());
}
@Override
public Iterable<Subject> getAllSubjects() {
return subjects.values().stream().map(s -> (Subject) s).collect(Collectors.toList());
return subjects.asMap().values().stream().map(s -> (Subject) s).collect(Collectors.toList());
}
@Override
@ -86,7 +89,7 @@ public class PersistedCollection implements SubjectCollection {
@Override
public Map<Subject, Boolean> getAllWithPermission(@NonNull Set<Context> contexts, @NonNull String node) {
Map<Subject, Boolean> m = new HashMap<>();
for (Subject subject : subjects.values()) {
for (Subject subject : subjects.asMap().values()) {
Tristate ts = subject.getPermissionValue(contexts, node);
if (ts != Tristate.UNDEFINED) {
m.put(subject, ts.asBoolean());

View File

@ -25,13 +25,14 @@ package me.lucko.luckperms.sponge.service.persisted;
import com.google.common.collect.ImmutableList;
import lombok.Getter;
import lombok.NonNull;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.common.utils.BufferedRequest;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.MemorySubjectData;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectCollection;
import org.spongepowered.api.service.permission.SubjectData;
import org.spongepowered.api.util.Tristate;
import java.io.IOException;
@ -51,6 +52,19 @@ public class PersistedSubject implements Subject {
private final SubjectCollection containingCollection;
private final PersistedSubjectData subjectData;
private final MemorySubjectData transientSubjectData;
private final BufferedRequest<Void> saveBuffer = new BufferedRequest<Void>(1000L, r -> PersistedSubject.this.service.getPlugin().doAsync(r)) {
@Override
protected Void perform() {
service.getPlugin().doAsync(() -> {
try {
service.getStorage().saveToFile(PersistedSubject.this);
} catch (IOException e) {
e.printStackTrace();
}
});
return null;
}
};
public PersistedSubject(String identifier, LuckPermsService service, SubjectCollection containingCollection) {
this.identifier = identifier;
@ -67,13 +81,7 @@ public class PersistedSubject implements Subject {
}
public void save() {
service.getPlugin().doAsync(() -> {
try {
service.getStorage().saveToFile(this);
} catch (IOException e) {
e.printStackTrace();
}
});
saveBuffer.request();
}
@Override
@ -178,6 +186,6 @@ public class PersistedSubject implements Subject {
@Override
public Set<Context> getActiveContexts() {
return SubjectData.GLOBAL_CONTEXT;
return LuckPermsService.convertContexts(service.getPlugin().getContextManager().giveApplicableContext(this, MutableContextSet.empty()));
}
}

View File

@ -22,6 +22,8 @@
package me.lucko.luckperms.sponge.service.persisted;
import lombok.ToString;
import me.lucko.luckperms.api.context.ContextSet;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.MemorySubjectData;
import org.spongepowered.api.service.permission.PermissionService;
@ -35,6 +37,7 @@ import static me.lucko.luckperms.sponge.service.LuckPermsService.convertContexts
/**
* Holds SubjectData in a "gson friendly" format for serialization
*/
@ToString
public class SubjectDataHolder {
private final Map<Map<String, String>, Map<String, Boolean>> permissions;
private final Map<Map<String, String>, Map<String, String>> options;
@ -43,17 +46,17 @@ public class SubjectDataHolder {
public SubjectDataHolder(Map<Set<Context>, Map<String, String>> options, Map<Set<Context>, Map<String, Boolean>> permissions, Map<Set<Context>, List<Map.Entry<String, String>>> parents) {
this.options = new HashMap<>();
for (Map.Entry<Set<Context>, Map<String, String>> e : options.entrySet()) {
this.options.put(convertContexts(e.getKey()), new HashMap<>(e.getValue()));
this.options.put(convertContexts(e.getKey()).toMap(), new HashMap<>(e.getValue()));
}
this.permissions = new HashMap<>();
for (Map.Entry<Set<Context>, Map<String, Boolean>> e : permissions.entrySet()) {
this.permissions.put(convertContexts(e.getKey()), new HashMap<>(e.getValue()));
this.permissions.put(convertContexts(e.getKey()).toMap(), new HashMap<>(e.getValue()));
}
this.parents = new HashMap<>();
for (Map.Entry<Set<Context>, List<Map.Entry<String, String>>> e : parents.entrySet()) {
this.parents.put(convertContexts(e.getKey()), e.getValue().stream().map(p -> new AbstractMap.SimpleEntry<>(p.getKey(), p.getValue())).collect(Collectors.toList()));
this.parents.put(convertContexts(e.getKey()).toMap(), e.getValue().stream().map(p -> new AbstractMap.SimpleEntry<>(p.getKey(), p.getValue())).collect(Collectors.toList()));
}
}
@ -77,20 +80,23 @@ public class SubjectDataHolder {
public void copyTo(MemorySubjectData subjectData, PermissionService service) {
for (Map.Entry<Map<String, String>, Map<String, Boolean>> e : permissions.entrySet()) {
Set<Context> contexts = convertContexts(ContextSet.fromMap(e.getKey()));
for (Map.Entry<String, Boolean> perm : e.getValue().entrySet()) {
subjectData.setPermission(convertContexts(e.getKey()), perm.getKey(), Tristate.fromBoolean(perm.getValue()));
subjectData.setPermission(contexts, perm.getKey(), Tristate.fromBoolean(perm.getValue()));
}
}
for (Map.Entry<Map<String, String>, Map<String, String>> e : options.entrySet()) {
Set<Context> contexts = convertContexts(ContextSet.fromMap(e.getKey()));
for (Map.Entry<String, String> option : e.getValue().entrySet()) {
subjectData.setOption(convertContexts(e.getKey()), option.getKey(), option.getValue());
subjectData.setOption(contexts, option.getKey(), option.getValue());
}
}
for (Map.Entry<Map<String, String>, List<Map.Entry<String, String>>> e : parents.entrySet()) {
Set<Context> contexts = convertContexts(ContextSet.fromMap(e.getKey()));
for (Map.Entry<String, String> parent : e.getValue()) {
subjectData.addParent(convertContexts(e.getKey()), service.getSubjects(parent.getKey()).get(parent.getValue()));
subjectData.addParent(contexts, service.getSubjects(parent.getKey()).get(parent.getValue()));
}
}
}

View File

@ -58,7 +58,7 @@ public class SubjectStorage {
collection.mkdirs();
}
File subjectFile = new File(collection, subject.getIdentifier().toLowerCase() + ".json");
File subjectFile = new File(collection, subject.getIdentifier() + ".json");
saveToFile(subject, subjectFile);
}
@ -110,8 +110,8 @@ public class SubjectStorage {
return null;
}
File subject = new File(collection, subjectName.toLowerCase() + ".json");
return new AbstractMap.SimpleEntry<>(subjectName.toLowerCase(), loadFromFile(subject).getValue());
File subject = new File(collection, subjectName + ".json");
return new AbstractMap.SimpleEntry<>(subjectName, loadFromFile(subject).getValue());
}
public Map.Entry<String, SubjectDataHolder> loadFromFile(File file) throws IOException {
@ -120,7 +120,7 @@ public class SubjectStorage {
}
String s = Files.toString(file, Charset.defaultCharset());
return new AbstractMap.SimpleEntry<>(file.getName().substring(file.getName().length() - 5).toLowerCase(), loadFromString(s));
return new AbstractMap.SimpleEntry<>(file.getName().substring(0, file.getName().length() - 5), loadFromString(s));
}
public SubjectDataHolder loadFromString(String s) {

View File

@ -22,6 +22,9 @@
package me.lucko.luckperms.sponge.service.simple;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@ -35,7 +38,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* Super simple SubjectCollection implementation
@ -47,25 +50,27 @@ public class SimpleCollection implements SubjectCollection {
@Getter
private final String identifier;
private final Map<String, Subject> subjects = new ConcurrentHashMap<>();
private final LoadingCache<String, SimpleSubject> subjects = CacheBuilder.newBuilder()
.build(new CacheLoader<String, SimpleSubject>() {
@Override
public SimpleSubject load(String s) {
return new SimpleSubject(s, service, SimpleCollection.this);
}
});
@Override
public synchronized Subject get(@NonNull String id) {
if (!subjects.containsKey(id)) {
subjects.put(id, new SimpleSubject(id, service, this));
}
return subjects.get(id);
public Subject get(@NonNull String id) {
return subjects.getUnchecked(id.toLowerCase());
}
@Override
public boolean hasRegistered(@NonNull String id) {
return subjects.containsKey(id);
return subjects.asMap().containsKey(id.toLowerCase());
}
@Override
public Iterable<Subject> getAllSubjects() {
return subjects.values();
return subjects.asMap().values().stream().map(s -> (Subject) s).collect(Collectors.toList());
}
@Override
@ -76,7 +81,7 @@ public class SimpleCollection implements SubjectCollection {
@Override
public Map<Subject, Boolean> getAllWithPermission(@NonNull Set<Context> contexts, @NonNull String node) {
Map<Subject, Boolean> m = new HashMap<>();
for (Subject subject : subjects.values()) {
for (Subject subject : subjects.asMap().values()) {
Tristate ts = subject.getPermissionValue(contexts, node);
if (ts != Tristate.UNDEFINED) {
m.put(subject, ts.asBoolean());

View File

@ -25,9 +25,14 @@ package me.lucko.luckperms.sponge.service.simple;
import com.google.common.collect.ImmutableList;
import lombok.Getter;
import lombok.NonNull;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.*;
import org.spongepowered.api.service.permission.MemorySubjectData;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectCollection;
import org.spongepowered.api.service.permission.SubjectData;
import org.spongepowered.api.util.Tristate;
import java.util.ArrayList;
@ -42,11 +47,11 @@ import java.util.Set;
public class SimpleSubject implements Subject {
private final String identifier;
private final PermissionService service;
private final LuckPermsService service;
private final SubjectCollection containingCollection;
private final MemorySubjectData subjectData;
public SimpleSubject(String identifier, PermissionService service, SubjectCollection containingCollection) {
public SimpleSubject(String identifier, LuckPermsService service, SubjectCollection containingCollection) {
this.identifier = identifier;
this.service = service;
this.containingCollection = containingCollection;
@ -147,6 +152,6 @@ public class SimpleSubject implements Subject {
@Override
public Set<Context> getActiveContexts() {
return SubjectData.GLOBAL_CONTEXT;
return LuckPermsService.convertContexts(service.getPlugin().getContextManager().giveApplicableContext(this, MutableContextSet.empty()));
}
}