From a927ca659f0e5d3bbbb4568adcf8292014c64e9b Mon Sep 17 00:00:00 2001 From: Luck Date: Sat, 12 May 2018 01:34:34 +0100 Subject: [PATCH] Reduce the throughput on the contexts cache in ContextManager (#929) --- .../model/permissible/LPPermissible.java | 26 ++--- .../common/buffers/ExpiringCache.java | 80 +++++++++++++ .../contexts/AbstractContextManager.java | 105 +++++++++--------- .../common/contexts/ContextManager.java | 8 ++ .../common/contexts/ContextsCache.java | 67 +++++++++++ .../model/permissible/LPPermissible.java | 26 ++--- .../service/proxy/api6/SubjectProxy.java | 9 +- .../service/proxy/api7/SubjectProxy.java | 9 +- 8 files changed, 245 insertions(+), 85 deletions(-) create mode 100644 common/src/main/java/me/lucko/luckperms/common/buffers/ExpiringCache.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/contexts/ContextsCache.java diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/permissible/LPPermissible.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/permissible/LPPermissible.java index b937ffd0c..676140882 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/permissible/LPPermissible.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/permissible/LPPermissible.java @@ -25,10 +25,10 @@ package me.lucko.luckperms.bukkit.model.permissible; -import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.bukkit.LPBukkitPlugin; import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.contexts.ContextsCache; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.verbose.CheckOrigin; @@ -72,6 +72,9 @@ public class LPPermissible extends PermissibleBase { // the luckperms plugin instance private final LPBukkitPlugin plugin; + // caches context lookups for the player + private final ContextsCache contextsCache; + // the players previous permissible. (the one they had before this one was injected) private PermissibleBase oldPermissible = null; @@ -87,6 +90,7 @@ public class LPPermissible extends PermissibleBase { this.user = Objects.requireNonNull(user, "user"); this.player = Objects.requireNonNull(player, "player"); this.plugin = Objects.requireNonNull(plugin, "plugin"); + this.contextsCache = plugin.getContextManager().getCacheFor(player); } @Override @@ -95,7 +99,7 @@ public class LPPermissible extends PermissibleBase { throw new NullPointerException("permission"); } - Tristate ts = this.user.getCachedData().getPermissionData(calculateContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK); + Tristate ts = this.user.getCachedData().getPermissionData(this.contextsCache.getContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK); return ts != Tristate.UNDEFINED || Permission.DEFAULT_PERMISSION.getValue(isOp()); } @@ -105,7 +109,7 @@ public class LPPermissible extends PermissibleBase { throw new NullPointerException("permission"); } - Tristate ts = this.user.getCachedData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_LOOKUP_CHECK); + Tristate ts = this.user.getCachedData().getPermissionData(this.contextsCache.getContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_LOOKUP_CHECK); if (ts != Tristate.UNDEFINED) { return true; } @@ -123,7 +127,7 @@ public class LPPermissible extends PermissibleBase { throw new NullPointerException("permission"); } - Tristate ts = this.user.getCachedData().getPermissionData(calculateContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_PERMISSION_CHECK); + Tristate ts = this.user.getCachedData().getPermissionData(this.contextsCache.getContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_PERMISSION_CHECK); return ts != Tristate.UNDEFINED ? ts.asBoolean() : Permission.DEFAULT_PERMISSION.getValue(isOp()); } @@ -133,7 +137,7 @@ public class LPPermissible extends PermissibleBase { throw new NullPointerException("permission"); } - Tristate ts = this.user.getCachedData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_PERMISSION_CHECK); + Tristate ts = this.user.getCachedData().getPermissionData(this.contextsCache.getContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_PERMISSION_CHECK); if (ts != Tristate.UNDEFINED) { return ts.asBoolean(); } @@ -156,16 +160,6 @@ public class LPPermissible extends PermissibleBase { } } - /** - * Obtains a {@link Contexts} instance for the player. - * Values are determined using the plugins ContextManager. - * - * @return the calculated contexts for the player. - */ - private Contexts calculateContexts() { - return this.plugin.getContextManager().getApplicableContexts(this.player); - } - @Override public void setOp(boolean value) { this.player.setOp(value); @@ -173,7 +167,7 @@ public class LPPermissible extends PermissibleBase { @Override public Set getEffectivePermissions() { - Set> permissions = this.user.getCachedData().getPermissionData(calculateContexts()).getImmutableBacking().entrySet(); + Set> permissions = this.user.getCachedData().getPermissionData(this.contextsCache.getContexts()).getImmutableBacking().entrySet(); Set ret = new HashSet<>(permissions.size()); for (Map.Entry entry : permissions) { diff --git a/common/src/main/java/me/lucko/luckperms/common/buffers/ExpiringCache.java b/common/src/main/java/me/lucko/luckperms/common/buffers/ExpiringCache.java new file mode 100644 index 000000000..c2b9bd0ac --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/buffers/ExpiringCache.java @@ -0,0 +1,80 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.buffers; + +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +import javax.annotation.Nonnull; + +/** + * An expiring supplier extension. + * + *

The delegate supplier is only called on executions of {@link #get()} if the + * result isn't already calculated.

+ * + * @param the supplied type + */ +public abstract class ExpiringCache implements Supplier { + private final long durationNanos; + + private volatile T value; + + // when to expire. 0 means "not yet initialized". + private volatile long expirationNanos; + + protected ExpiringCache(long duration, TimeUnit unit) { + this.durationNanos = unit.toNanos(duration); + } + + @Nonnull + protected abstract T supply(); + + @Override + public T get() { + long nanos = this.expirationNanos; + long now = System.nanoTime(); + + if (nanos == 0 || now - nanos >= 0) { + synchronized (this) { + if (nanos == this.expirationNanos) { // recheck for lost race + // compute the value using the delegate + T t = supply(); + this.value = t; + + // reset expiration timer + nanos = now + this.durationNanos; + // In the very unlikely event that nanos is 0, set it to 1; + // no one will notice 1 ns of tardiness. + this.expirationNanos = (nanos == 0) ? 1 : nanos; + return t; + } + } + } + return this.value; + } +} + diff --git a/common/src/main/java/me/lucko/luckperms/common/contexts/AbstractContextManager.java b/common/src/main/java/me/lucko/luckperms/common/contexts/AbstractContextManager.java index e38477293..e69dfeba4 100644 --- a/common/src/main/java/me/lucko/luckperms/common/contexts/AbstractContextManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/contexts/AbstractContextManager.java @@ -25,11 +25,8 @@ package me.lucko.luckperms.common.contexts; -import com.github.benmanes.caffeine.cache.CacheLoader; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; -import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import me.lucko.luckperms.api.Contexts; @@ -38,6 +35,7 @@ import me.lucko.luckperms.api.context.ContextCalculator; import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.api.context.MutableContextSet; import me.lucko.luckperms.api.context.StaticContextCalculator; +import me.lucko.luckperms.common.buffers.ExpiringCache; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; @@ -64,14 +62,14 @@ public abstract class AbstractContextManager implements ContextManager { private final List> calculators = new CopyOnWriteArrayList<>(); private final List staticCalculators = new CopyOnWriteArrayList<>(); - // caches context lookups - private final LoadingCache lookupCache = Caffeine.newBuilder() - .expireAfterWrite(50L, TimeUnit.MILLISECONDS) // expire roughly every tick - .build(new Loader()); + // caches the creation of cache instances. cache-ception. + // we want to encourage re-use of these instances, it's faster that way + private final LoadingCache> subjectCaches = Caffeine.newBuilder() + .weakKeys() + .build(key -> new ContextsCache<>(key, this)); // caches static context lookups - @SuppressWarnings("Guava") - private final Supplier staticLookupCache = Suppliers.memoizeWithExpiration(new StaticLoader(), 50L, TimeUnit.MILLISECONDS); + private final StaticLookupCache staticLookupCache = new StaticLookupCache(); protected AbstractContextManager(LuckPermsPlugin plugin, Class subjectClass) { this.plugin = plugin; @@ -95,21 +93,20 @@ public abstract class AbstractContextManager implements ContextManager { @Override public ImmutableContextSet getApplicableContext(T subject) { - if (subject == null) { - throw new NullPointerException("subject"); - } - - // this is actually already immutable, but the Contexts method signature returns the interface. - // using the makeImmutable method is faster than casting - return getApplicableContexts(subject).getContexts().makeImmutable(); + return getCacheFor(subject).getContextSet(); } @Override public Contexts getApplicableContexts(T subject) { + return getCacheFor(subject).getContexts(); + } + + @Override + public ContextsCache getCacheFor(T subject) { if (subject == null) { throw new NullPointerException("subject"); } - return this.lookupCache.get(subject); + return this.subjectCaches.get(subject); } @Override @@ -181,52 +178,58 @@ public abstract class AbstractContextManager implements ContextManager { throw new NullPointerException("subject"); } - this.lookupCache.invalidate(subject); + this.subjectCaches.invalidate(subject); } - private final class Loader implements CacheLoader { - @Override - public Contexts load(@Nonnull T subject) { - MutableContextSet accumulator = MutableContextSet.create(); + Contexts calculate(T subject) { + MutableContextSet accumulator = MutableContextSet.create(); - for (ContextCalculator calculator : AbstractContextManager.this.calculators) { - try { - MutableContextSet ret = calculator.giveApplicableContext(subject, accumulator); - //noinspection ConstantConditions - if (ret == null) { - throw new IllegalStateException(calculator.getClass() + " returned a null context set"); - } - accumulator = ret; - } catch (Exception e) { - AbstractContextManager.this.plugin.getLogger().warn("An exception was thrown by " + getCalculatorClass(calculator) + " whilst calculating the context of subject " + subject); - e.printStackTrace(); + for (ContextCalculator calculator : AbstractContextManager.this.calculators) { + try { + MutableContextSet ret = calculator.giveApplicableContext(subject, accumulator); + //noinspection ConstantConditions + if (ret == null) { + throw new IllegalStateException(calculator.getClass() + " returned a null context set"); } + accumulator = ret; + } catch (Exception e) { + AbstractContextManager.this.plugin.getLogger().warn("An exception was thrown by " + getCalculatorClass(calculator) + " whilst calculating the context of subject " + subject); + e.printStackTrace(); } - - return formContexts(subject, accumulator.makeImmutable()); } + + return formContexts(subject, accumulator.makeImmutable()); } - private final class StaticLoader implements Supplier { - @Override - public Contexts get() { - MutableContextSet accumulator = MutableContextSet.create(); + private Contexts calculateStatic() { + MutableContextSet accumulator = MutableContextSet.create(); - for (StaticContextCalculator calculator : AbstractContextManager.this.staticCalculators) { - try { - MutableContextSet ret = calculator.giveApplicableContext(accumulator); - //noinspection ConstantConditions - if (ret == null) { - throw new IllegalStateException(calculator.getClass() + " returned a null context set"); - } - accumulator = ret; - } catch (Exception e) { - AbstractContextManager.this.plugin.getLogger().warn("An exception was thrown by " + getCalculatorClass(calculator) + " whilst calculating static contexts"); - e.printStackTrace(); + for (StaticContextCalculator calculator : this.staticCalculators) { + try { + MutableContextSet ret = calculator.giveApplicableContext(accumulator); + //noinspection ConstantConditions + if (ret == null) { + throw new IllegalStateException(calculator.getClass() + " returned a null context set"); } + accumulator = ret; + } catch (Exception e) { + this.plugin.getLogger().warn("An exception was thrown by " + getCalculatorClass(calculator) + " whilst calculating static contexts"); + e.printStackTrace(); } + } - return formContexts(accumulator.makeImmutable()); + return formContexts(accumulator.makeImmutable()); + } + + private final class StaticLookupCache extends ExpiringCache { + StaticLookupCache() { + super(50L, TimeUnit.MILLISECONDS); + } + + @Nonnull + @Override + public Contexts supply() { + return calculateStatic(); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/contexts/ContextManager.java b/common/src/main/java/me/lucko/luckperms/common/contexts/ContextManager.java index a58a8e83d..80e039df7 100644 --- a/common/src/main/java/me/lucko/luckperms/common/contexts/ContextManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/contexts/ContextManager.java @@ -65,6 +65,14 @@ public interface ContextManager { */ Contexts getApplicableContexts(T subject); + /** + * Gets the cache instance for the given subject. + * + * @param subject the subject + * @return the cache + */ + ContextsCache getCacheFor(T subject); + /** * Gets the contexts from the static calculators in this manager. * diff --git a/common/src/main/java/me/lucko/luckperms/common/contexts/ContextsCache.java b/common/src/main/java/me/lucko/luckperms/common/contexts/ContextsCache.java new file mode 100644 index 000000000..99225eb1f --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/contexts/ContextsCache.java @@ -0,0 +1,67 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.contexts; + +import me.lucko.luckperms.api.Contexts; +import me.lucko.luckperms.api.context.ImmutableContextSet; +import me.lucko.luckperms.common.buffers.ExpiringCache; + +import java.util.concurrent.TimeUnit; + +import javax.annotation.Nonnull; + +/** + * Extension of {@link AbstractContextManager} that implements an expiring lookup cache + * per player. + * + * @param the player type + */ +public final class ContextsCache extends ExpiringCache { + private final T subject; + private final AbstractContextManager contextManager; + + public ContextsCache(T subject, AbstractContextManager contextManager) { + super(50L, TimeUnit.MILLISECONDS); // expire roughly every tick + this.subject = subject; + this.contextManager = contextManager; + } + + @Nonnull + @Override + protected Contexts supply() { + return this.contextManager.calculate(this.subject); + } + + public Contexts getContexts() { + return get(); + } + + public ImmutableContextSet getContextSet() { + // this is actually already immutable, but the Contexts method signature returns the interface. + // using the makeImmutable method is faster than casting + return get().getContexts().makeImmutable(); + } +} diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/model/permissible/LPPermissible.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/model/permissible/LPPermissible.java index cb9ad391a..13f705c7e 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/model/permissible/LPPermissible.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/model/permissible/LPPermissible.java @@ -25,9 +25,9 @@ package me.lucko.luckperms.nukkit.model.permissible; -import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.contexts.ContextsCache; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.verbose.CheckOrigin; import me.lucko.luckperms.nukkit.LPNukkitPlugin; @@ -73,6 +73,9 @@ public class LPPermissible extends PermissibleBase { // the luckperms plugin instance private final LPNukkitPlugin plugin; + // caches context lookups for the player + private final ContextsCache contextsCache; + // the players previous permissible. (the one they had before this one was injected) private PermissibleBase oldPermissible = null; @@ -88,6 +91,7 @@ public class LPPermissible extends PermissibleBase { this.user = Objects.requireNonNull(user, "user"); this.player = Objects.requireNonNull(player, "player"); this.plugin = Objects.requireNonNull(plugin, "plugin"); + this.contextsCache = plugin.getContextManager().getCacheFor(player); } @Override @@ -96,7 +100,7 @@ public class LPPermissible extends PermissibleBase { throw new NullPointerException("permission"); } - Tristate ts = this.user.getCachedData().getPermissionData(calculateContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK); + Tristate ts = this.user.getCachedData().getPermissionData(this.contextsCache.getContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK); return ts != Tristate.UNDEFINED || PermissionDefault.OP.getValue(isOp()); } @@ -106,7 +110,7 @@ public class LPPermissible extends PermissibleBase { throw new NullPointerException("permission"); } - Tristate ts = this.user.getCachedData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_LOOKUP_CHECK); + Tristate ts = this.user.getCachedData().getPermissionData(this.contextsCache.getContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_LOOKUP_CHECK); if (ts != Tristate.UNDEFINED) { return true; } @@ -125,7 +129,7 @@ public class LPPermissible extends PermissibleBase { throw new NullPointerException("permission"); } - Tristate ts = this.user.getCachedData().getPermissionData(calculateContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_PERMISSION_CHECK); + Tristate ts = this.user.getCachedData().getPermissionData(this.contextsCache.getContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_PERMISSION_CHECK); return ts != Tristate.UNDEFINED ? ts.asBoolean() : PermissionDefault.OP.getValue(isOp()); } @@ -135,7 +139,7 @@ public class LPPermissible extends PermissibleBase { throw new NullPointerException("permission"); } - Tristate ts = this.user.getCachedData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_PERMISSION_CHECK); + Tristate ts = this.user.getCachedData().getPermissionData(this.contextsCache.getContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_PERMISSION_CHECK); if (ts != Tristate.UNDEFINED) { return ts.asBoolean(); } @@ -159,16 +163,6 @@ public class LPPermissible extends PermissibleBase { } } - /** - * Obtains a {@link Contexts} instance for the player. - * Values are determined using the plugins ContextManager. - * - * @return the calculated contexts for the player. - */ - private Contexts calculateContexts() { - return this.plugin.getContextManager().getApplicableContexts(this.player); - } - @Override public void setOp(boolean value) { this.player.setOp(value); @@ -176,7 +170,7 @@ public class LPPermissible extends PermissibleBase { @Override public Map getEffectivePermissions() { - Set> permissions = this.user.getCachedData().getPermissionData(calculateContexts()).getImmutableBacking().entrySet(); + Set> permissions = this.user.getCachedData().getPermissionData(this.contextsCache.getContexts()).getImmutableBacking().entrySet(); Map ret = new HashMap<>(permissions.size()); for (Map.Entry entry : permissions) { diff --git a/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectProxy.java b/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectProxy.java index a03f0f512..97cf89043 100644 --- a/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectProxy.java +++ b/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectProxy.java @@ -26,6 +26,8 @@ package me.lucko.luckperms.sponge.service.proxy.api6; import me.lucko.luckperms.api.context.ImmutableContextSet; +import me.lucko.luckperms.common.contexts.ContextManager; +import me.lucko.luckperms.common.contexts.ContextsCache; import me.lucko.luckperms.common.utils.ImmutableCollectors; import me.lucko.luckperms.sponge.service.CompatibilityUtil; import me.lucko.luckperms.sponge.service.model.LPPermissionService; @@ -52,9 +54,14 @@ public final class SubjectProxy implements Subject, ProxiedSubject { private final LPPermissionService service; private final LPSubjectReference ref; + private final ContextsCache contextsCache; + public SubjectProxy(LPPermissionService service, LPSubjectReference ref) { this.service = service; this.ref = ref; + + ContextManager contextManager = (ContextManager) service.getPlugin().getContextManager(); + this.contextsCache = contextManager.getCacheFor(this); } private CompletableFuture handle() { @@ -157,7 +164,7 @@ public final class SubjectProxy implements Subject, ProxiedSubject { @Nonnull @Override public Set getActiveContexts() { - return CompatibilityUtil.convertContexts(this.service.getContextManager().getApplicableContext(this)); + return CompatibilityUtil.convertContexts(this.contextsCache.getContextSet()); } @Override diff --git a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectProxy.java b/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectProxy.java index c93b7a99a..81fe9a973 100644 --- a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectProxy.java +++ b/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectProxy.java @@ -26,6 +26,8 @@ package me.lucko.luckperms.sponge.service.proxy.api7; import me.lucko.luckperms.api.context.ImmutableContextSet; +import me.lucko.luckperms.common.contexts.ContextManager; +import me.lucko.luckperms.common.contexts.ContextsCache; import me.lucko.luckperms.sponge.service.CompatibilityUtil; import me.lucko.luckperms.sponge.service.model.LPPermissionService; import me.lucko.luckperms.sponge.service.model.LPSubject; @@ -52,9 +54,14 @@ public final class SubjectProxy implements Subject, ProxiedSubject { private final LPPermissionService service; private final LPSubjectReference ref; + private final ContextsCache contextsCache; + public SubjectProxy(LPPermissionService service, LPSubjectReference ref) { this.service = service; this.ref = ref; + + ContextManager contextManager = (ContextManager) service.getPlugin().getContextManager(); + this.contextsCache = contextManager.getCacheFor(this); } private CompletableFuture handle() { @@ -158,7 +165,7 @@ public final class SubjectProxy implements Subject, ProxiedSubject { @Nonnull @Override public Set getActiveContexts() { - return CompatibilityUtil.convertContexts(this.service.getContextManager().getApplicableContext(this)); + return CompatibilityUtil.convertContexts(this.contextsCache.getContextSet()); } @Override