Refactor caching system (WIP) - still working towards #23
This commit is contained in:
@@ -22,9 +22,11 @@
|
||||
|
||||
package me.lucko.luckperms;
|
||||
|
||||
import me.lucko.luckperms.api.Contexts;
|
||||
import me.lucko.luckperms.api.Logger;
|
||||
import me.lucko.luckperms.api.PlatformType;
|
||||
import me.lucko.luckperms.api.implementation.ApiProvider;
|
||||
import me.lucko.luckperms.calculators.CalculatorFactory;
|
||||
import me.lucko.luckperms.commands.ConsecutiveExecutor;
|
||||
import me.lucko.luckperms.commands.Sender;
|
||||
import me.lucko.luckperms.config.LPConfiguration;
|
||||
@@ -64,6 +66,7 @@ public interface LuckPermsPlugin {
|
||||
ConsecutiveExecutor getConsecutiveExecutor();
|
||||
LocaleManager getLocaleManager();
|
||||
ContextManager getContextManager();
|
||||
CalculatorFactory getCalculatorFactory();
|
||||
|
||||
/**
|
||||
* @return the version of the plugin
|
||||
@@ -114,11 +117,8 @@ public interface LuckPermsPlugin {
|
||||
*/
|
||||
Sender getConsoleSender();
|
||||
|
||||
/**
|
||||
* Gets all possible permission nodes, used for resolving wildcards
|
||||
* @return a {@link List} of permission nodes
|
||||
*/
|
||||
List<String> getPossiblePermissions();
|
||||
// TODO javadoc
|
||||
Set<Contexts> getPreProcessContexts(boolean op);
|
||||
|
||||
/**
|
||||
* Gets a set of players ignoring logging output
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.caching;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.lucko.luckperms.api.Contexts;
|
||||
import me.lucko.luckperms.api.LocalizedNode;
|
||||
import me.lucko.luckperms.api.Node;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class MetaData {
|
||||
private final Contexts contexts;
|
||||
|
||||
@Getter
|
||||
private String prefix = null;
|
||||
|
||||
@Getter
|
||||
private String suffix = null;
|
||||
private Map<String, String> meta = new ConcurrentHashMap<>();
|
||||
|
||||
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");
|
||||
|
||||
int prefixPriority = Integer.MIN_VALUE;
|
||||
int suffixPriority = Integer.MIN_VALUE;
|
||||
|
||||
for (LocalizedNode ln : nodes) {
|
||||
Node n = ln.getNode();
|
||||
|
||||
if (!n.getValue()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!n.isMeta() || !n.isPrefix() || n.isSuffix()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!n.shouldApplyOnServer(server, this.contexts.isIncludeGlobal(), false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!n.shouldApplyOnWorld(world, this.contexts.isIncludeGlobalWorld(), false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!n.shouldApplyWithContext(contexts, false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n.isPrefix()) {
|
||||
Map.Entry<Integer, String> value = n.getPrefix();
|
||||
if (value.getKey() > prefixPriority) {
|
||||
this.prefix = value.getValue();
|
||||
prefixPriority = value.getKey();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n.isSuffix()) {
|
||||
Map.Entry<Integer, String> value = n.getSuffix();
|
||||
if (value.getKey() > suffixPriority) {
|
||||
this.suffix = value.getValue();
|
||||
suffixPriority = value.getKey();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n.isMeta()) {
|
||||
Map.Entry<String, String> meta = n.getMeta();
|
||||
this.meta.put(meta.getKey(), meta.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void invalidateCache() {
|
||||
meta.clear();
|
||||
prefix = null;
|
||||
suffix = null;
|
||||
}
|
||||
|
||||
public Map<String, String> getMeta() {
|
||||
return ImmutableMap.copyOf(meta);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.caching;
|
||||
|
||||
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.calculators.CalculatorFactory;
|
||||
import me.lucko.luckperms.calculators.PermissionCalculator;
|
||||
import me.lucko.luckperms.users.User;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class PermissionData {
|
||||
private final Contexts contexts;
|
||||
private final Map<String, Boolean> permissions;
|
||||
|
||||
private final PermissionCalculator calculator;
|
||||
|
||||
public PermissionData(Contexts contexts, User user, CalculatorFactory calculatorFactory) {
|
||||
this.contexts = contexts;
|
||||
permissions = new ConcurrentHashMap<>();
|
||||
calculator = calculatorFactory.build(contexts, user, permissions);
|
||||
}
|
||||
|
||||
public void invalidateCache() {
|
||||
calculator.invalidateCache();
|
||||
}
|
||||
|
||||
public void setPermissions(Map<String, Boolean> permissions) {
|
||||
this.permissions.clear();
|
||||
this.permissions.putAll(permissions);
|
||||
invalidateCache();
|
||||
}
|
||||
|
||||
public void comparePermissions(Map<String, Boolean> toApply) {
|
||||
boolean different = false;
|
||||
if (toApply.size() != permissions.size()) {
|
||||
different = true;
|
||||
} else {
|
||||
for (Map.Entry<String, Boolean> e : permissions.entrySet()) {
|
||||
if (toApply.containsKey(e.getKey()) && toApply.get(e.getKey()) == e.getValue()) {
|
||||
continue;
|
||||
}
|
||||
different = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (different) {
|
||||
setPermissions(toApply);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Boolean> getImmutableBacking() {
|
||||
return ImmutableMap.copyOf(permissions);
|
||||
}
|
||||
|
||||
public Tristate getPermissionValue(@NonNull String permission) {
|
||||
Tristate t = calculator.getPermissionValue(permission);
|
||||
if (t != Tristate.UNDEFINED) {
|
||||
return Tristate.fromBoolean(t.asBoolean());
|
||||
} else {
|
||||
return Tristate.UNDEFINED;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.caching;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.lucko.luckperms.api.Contexts;
|
||||
import me.lucko.luckperms.calculators.CalculatorFactory;
|
||||
import me.lucko.luckperms.users.User;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class UserData {
|
||||
private final User user;
|
||||
private final CalculatorFactory calculatorFactory;
|
||||
|
||||
private final Map<Contexts, PermissionData> permission = new ConcurrentHashMap<>();
|
||||
private final Map<Contexts, MetaData> meta = new ConcurrentHashMap<>();
|
||||
|
||||
public PermissionData getPermissionData(Contexts contexts) {
|
||||
return permission.computeIfAbsent(contexts, this::calculatePermissions);
|
||||
}
|
||||
|
||||
public MetaData getMetaData(Contexts contexts) {
|
||||
return meta.computeIfAbsent(contexts, this::calculateMeta);
|
||||
}
|
||||
|
||||
public PermissionData calculatePermissions(Contexts contexts) {
|
||||
PermissionData data = new PermissionData(contexts, user, calculatorFactory);
|
||||
data.setPermissions(user.exportNodes(contexts, Collections.emptyList(), true));
|
||||
return data;
|
||||
}
|
||||
|
||||
public void recalculatePermissions(Contexts contexts) {
|
||||
permission.compute(contexts, (c, data) -> {
|
||||
if (data == null) {
|
||||
data = new PermissionData(c, user, calculatorFactory);
|
||||
}
|
||||
|
||||
data.comparePermissions(user.exportNodes(c, Collections.emptyList(), true));
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
public void recalculatePermissions() {
|
||||
permission.keySet().forEach(this::recalculatePermissions);
|
||||
}
|
||||
|
||||
public MetaData calculateMeta(Contexts contexts) {
|
||||
MetaData data = new MetaData(contexts);
|
||||
data.loadMeta(user.getAllNodes(null, contexts));
|
||||
return data;
|
||||
}
|
||||
|
||||
public void recalculateMeta(Contexts contexts) {
|
||||
meta.compute(contexts, (c, data) -> {
|
||||
if (data == null) {
|
||||
data = new MetaData(c);
|
||||
}
|
||||
|
||||
data.loadMeta(user.getAllNodes(null, c));
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
public void recalculateMeta() {
|
||||
meta.keySet().forEach(this::recalculateMeta);
|
||||
}
|
||||
|
||||
public void preCalculate(Set<Contexts> contexts) {
|
||||
contexts.forEach(this::preCalculate);
|
||||
}
|
||||
|
||||
public void preCalculate(Contexts contexts) {
|
||||
getPermissionData(contexts);
|
||||
getMetaData(contexts);
|
||||
}
|
||||
|
||||
public void invalidateCache() {
|
||||
permission.clear();
|
||||
meta.clear();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.calculators;
|
||||
|
||||
import me.lucko.luckperms.api.Contexts;
|
||||
import me.lucko.luckperms.users.User;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Creates a calculator instance given a set of contexts
|
||||
*/
|
||||
public interface CalculatorFactory {
|
||||
|
||||
PermissionCalculator build(Contexts contexts, User user, Map<String, Boolean> map);
|
||||
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import me.lucko.luckperms.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.api.event.events.GroupAddEvent;
|
||||
import me.lucko.luckperms.api.implementation.internal.GroupLink;
|
||||
import me.lucko.luckperms.api.implementation.internal.PermissionHolderLink;
|
||||
import me.lucko.luckperms.caching.UserData;
|
||||
import me.lucko.luckperms.core.PermissionHolder;
|
||||
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
|
||||
import me.lucko.luckperms.exceptions.ObjectLacksException;
|
||||
@@ -40,7 +41,7 @@ import java.util.UUID;
|
||||
|
||||
@ToString(of = {"uuid"})
|
||||
@EqualsAndHashCode(of = {"uuid"}, callSuper = false)
|
||||
public abstract class User extends PermissionHolder implements Identifiable<UserIdentifier> {
|
||||
public class User extends PermissionHolder implements Identifiable<UserIdentifier> {
|
||||
|
||||
/**
|
||||
* The users Mojang UUID
|
||||
@@ -62,6 +63,9 @@ public abstract class User extends PermissionHolder implements Identifiable<User
|
||||
@Setter
|
||||
private String primaryGroup = null;
|
||||
|
||||
@Getter
|
||||
private UserData userData = null;
|
||||
|
||||
protected User(UUID uuid, LuckPermsPlugin plugin) {
|
||||
super(uuid.toString(), plugin);
|
||||
this.uuid = uuid;
|
||||
@@ -85,9 +89,43 @@ public abstract class User extends PermissionHolder implements Identifiable<User
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh and re-assign the users permissions
|
||||
* Sets up the UserData cache
|
||||
* Blocking call.
|
||||
*/
|
||||
public abstract void refreshPermissions();
|
||||
public void setupData(boolean op) {
|
||||
if (userData != null) {
|
||||
throw new IllegalStateException("Data already setup");
|
||||
}
|
||||
|
||||
userData = new UserData(this, null);
|
||||
userData.preCalculate(getPlugin().getPreProcessContexts(op));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the UserData cache from this user
|
||||
*/
|
||||
public void unregisterData() {
|
||||
if (userData != null) {
|
||||
userData.invalidateCache();
|
||||
userData = null;
|
||||
}
|
||||
// TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh and re-assign the users permissions
|
||||
* Blocking call.
|
||||
*/
|
||||
public synchronized void refreshPermissions() {
|
||||
if (userData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
UserData ud = userData;
|
||||
ud.recalculatePermissions();
|
||||
ud.recalculateMeta();
|
||||
// TODO api call?
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the user is a member of a group
|
||||
|
||||
@@ -33,7 +33,7 @@ import me.lucko.luckperms.utils.Identifiable;
|
||||
import java.util.UUID;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public abstract class UserManager extends AbstractManager<UserIdentifier, User> {
|
||||
public class UserManager extends AbstractManager<UserIdentifier, User> {
|
||||
private final LuckPermsPlugin plugin;
|
||||
|
||||
/**
|
||||
@@ -121,10 +121,21 @@ public abstract class UserManager extends AbstractManager<UserIdentifier, User>
|
||||
* Checks to see if the user is online, and if they are not, runs {@link #unload(Identifiable)}
|
||||
* @param user The user to be cleaned up
|
||||
*/
|
||||
public abstract void cleanup(User user);
|
||||
public void cleanup(User user) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the data of all online users
|
||||
*/
|
||||
public abstract void updateAllUsers();
|
||||
public void updateAllUsers() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public User apply(UserIdentifier id) {
|
||||
return id.getUsername() == null ?
|
||||
new User(id.getUuid(), plugin) :
|
||||
new User(id.getUuid(), id.getUsername(), plugin);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,13 @@ public class AbstractListener {
|
||||
}
|
||||
|
||||
plugin.getDatastore().loadUser(cache.getUUID(u), username);
|
||||
User user = plugin.getUserManager().get(cache.getUUID(u));
|
||||
if (user == null) {
|
||||
plugin.getLog().warn("Failed to load user: " + username);
|
||||
} else {
|
||||
user.setupData(false); // Pretty nasty calculation call. Sets up the caching system so data is ready when the user joins.
|
||||
}
|
||||
|
||||
final long time = System.currentTimeMillis() - startTime;
|
||||
if (time >= 1000) {
|
||||
plugin.getLog().warn("Processing login for " + username + " took " + time + "ms.");
|
||||
@@ -71,6 +78,7 @@ public class AbstractListener {
|
||||
|
||||
final User user = plugin.getUserManager().get(cache.getUUID(uuid));
|
||||
if (user != null) {
|
||||
user.unregisterData();
|
||||
plugin.getUserManager().unload(user);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user