implement cached state for all permission holders. towards #69
This commit is contained in:
parent
3771a50f30
commit
257f426ab9
@ -39,6 +39,7 @@ import me.lucko.luckperms.bukkit.model.DefaultsProvider;
|
|||||||
import me.lucko.luckperms.bukkit.vault.VaultHook;
|
import me.lucko.luckperms.bukkit.vault.VaultHook;
|
||||||
import me.lucko.luckperms.common.LuckPermsPlugin;
|
import me.lucko.luckperms.common.LuckPermsPlugin;
|
||||||
import me.lucko.luckperms.common.api.ApiProvider;
|
import me.lucko.luckperms.common.api.ApiProvider;
|
||||||
|
import me.lucko.luckperms.common.caching.handlers.CachedStateManager;
|
||||||
import me.lucko.luckperms.common.calculators.CalculatorFactory;
|
import me.lucko.luckperms.common.calculators.CalculatorFactory;
|
||||||
import me.lucko.luckperms.common.commands.ConsecutiveExecutor;
|
import me.lucko.luckperms.common.commands.ConsecutiveExecutor;
|
||||||
import me.lucko.luckperms.common.commands.sender.Sender;
|
import me.lucko.luckperms.common.commands.sender.Sender;
|
||||||
@ -107,6 +108,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
|||||||
private DefaultsProvider defaultsProvider;
|
private DefaultsProvider defaultsProvider;
|
||||||
private ChildPermissionProvider childPermissionProvider;
|
private ChildPermissionProvider childPermissionProvider;
|
||||||
private LocaleManager localeManager;
|
private LocaleManager localeManager;
|
||||||
|
private CachedStateManager cachedStateManager;
|
||||||
private ContextManager<Player> contextManager;
|
private ContextManager<Player> contextManager;
|
||||||
private WorldCalculator worldCalculator;
|
private WorldCalculator worldCalculator;
|
||||||
private CalculatorFactory calculatorFactory;
|
private CalculatorFactory calculatorFactory;
|
||||||
@ -218,6 +220,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
|||||||
importer = new Importer(commandManager);
|
importer = new Importer(commandManager);
|
||||||
consecutiveExecutor = new ConsecutiveExecutor(commandManager);
|
consecutiveExecutor = new ConsecutiveExecutor(commandManager);
|
||||||
calculatorFactory = new BukkitCalculatorFactory(this);
|
calculatorFactory = new BukkitCalculatorFactory(this);
|
||||||
|
cachedStateManager = new CachedStateManager(this);
|
||||||
|
|
||||||
contextManager = new ContextManager<>();
|
contextManager = new ContextManager<>();
|
||||||
worldCalculator = new WorldCalculator(this);
|
worldCalculator = new WorldCalculator(this);
|
||||||
|
@ -32,6 +32,7 @@ import me.lucko.luckperms.api.context.ContextSet;
|
|||||||
import me.lucko.luckperms.api.context.MutableContextSet;
|
import me.lucko.luckperms.api.context.MutableContextSet;
|
||||||
import me.lucko.luckperms.common.LuckPermsPlugin;
|
import me.lucko.luckperms.common.LuckPermsPlugin;
|
||||||
import me.lucko.luckperms.common.api.ApiProvider;
|
import me.lucko.luckperms.common.api.ApiProvider;
|
||||||
|
import me.lucko.luckperms.common.caching.handlers.CachedStateManager;
|
||||||
import me.lucko.luckperms.common.calculators.CalculatorFactory;
|
import me.lucko.luckperms.common.calculators.CalculatorFactory;
|
||||||
import me.lucko.luckperms.common.commands.CommandManager;
|
import me.lucko.luckperms.common.commands.CommandManager;
|
||||||
import me.lucko.luckperms.common.commands.ConsecutiveExecutor;
|
import me.lucko.luckperms.common.commands.ConsecutiveExecutor;
|
||||||
@ -89,6 +90,7 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
|
|||||||
private Importer importer;
|
private Importer importer;
|
||||||
private ConsecutiveExecutor consecutiveExecutor;
|
private ConsecutiveExecutor consecutiveExecutor;
|
||||||
private LocaleManager localeManager;
|
private LocaleManager localeManager;
|
||||||
|
private CachedStateManager cachedStateManager;
|
||||||
private ContextManager<ProxiedPlayer> contextManager;
|
private ContextManager<ProxiedPlayer> contextManager;
|
||||||
private CalculatorFactory calculatorFactory;
|
private CalculatorFactory calculatorFactory;
|
||||||
private BufferedRequest<Void> updateTaskBuffer;
|
private BufferedRequest<Void> updateTaskBuffer;
|
||||||
@ -164,6 +166,7 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
|
|||||||
importer = new Importer(commandManager);
|
importer = new Importer(commandManager);
|
||||||
consecutiveExecutor = new ConsecutiveExecutor(commandManager);
|
consecutiveExecutor = new ConsecutiveExecutor(commandManager);
|
||||||
calculatorFactory = new BungeeCalculatorFactory(this);
|
calculatorFactory = new BungeeCalculatorFactory(this);
|
||||||
|
cachedStateManager = new CachedStateManager(this);
|
||||||
|
|
||||||
contextManager = new ContextManager<>();
|
contextManager = new ContextManager<>();
|
||||||
BackendServerCalculator serverCalculator = new BackendServerCalculator();
|
BackendServerCalculator serverCalculator = new BackendServerCalculator();
|
||||||
|
@ -26,6 +26,7 @@ import me.lucko.luckperms.api.Contexts;
|
|||||||
import me.lucko.luckperms.api.Logger;
|
import me.lucko.luckperms.api.Logger;
|
||||||
import me.lucko.luckperms.api.PlatformType;
|
import me.lucko.luckperms.api.PlatformType;
|
||||||
import me.lucko.luckperms.common.api.ApiProvider;
|
import me.lucko.luckperms.common.api.ApiProvider;
|
||||||
|
import me.lucko.luckperms.common.caching.handlers.CachedStateManager;
|
||||||
import me.lucko.luckperms.common.calculators.CalculatorFactory;
|
import me.lucko.luckperms.common.calculators.CalculatorFactory;
|
||||||
import me.lucko.luckperms.common.commands.BaseCommand;
|
import me.lucko.luckperms.common.commands.BaseCommand;
|
||||||
import me.lucko.luckperms.common.commands.ConsecutiveExecutor;
|
import me.lucko.luckperms.common.commands.ConsecutiveExecutor;
|
||||||
@ -153,6 +154,13 @@ public interface LuckPermsPlugin {
|
|||||||
*/
|
*/
|
||||||
ContextManager getContextManager();
|
ContextManager getContextManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the cached state manager for the platform.
|
||||||
|
*
|
||||||
|
* @return the cached state manager
|
||||||
|
*/
|
||||||
|
CachedStateManager getCachedStateManager();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the class responsible for constructing PermissionCalculators on this platform.
|
* Gets the class responsible for constructing PermissionCalculators on this platform.
|
||||||
*
|
*
|
||||||
|
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* 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.common.caching.handlers;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import com.google.common.collect.HashMultimap;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
|
||||||
|
import me.lucko.luckperms.common.LuckPermsPlugin;
|
||||||
|
import me.lucko.luckperms.common.core.model.PermissionHolder;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages the cached state of all permission holders
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CachedStateManager {
|
||||||
|
private static final Consumer<PermissionHolder> INVALIDATE_CONSUMER = PermissionHolder::invalidateInheritanceCaches;
|
||||||
|
|
||||||
|
private final LuckPermsPlugin plugin;
|
||||||
|
|
||||||
|
// Group --> Groups that inherit from that group. (reverse relationship)
|
||||||
|
private final Multimap<HolderReference, HolderReference> map = HashMultimap.create();
|
||||||
|
private final ReentrantLock lock = new ReentrantLock();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a set of holder names that inherit permissions (either directly or via other groups)
|
||||||
|
* from the given holder name
|
||||||
|
*
|
||||||
|
* @param holder the holder name to query for
|
||||||
|
* @return a set of inherited groups
|
||||||
|
*/
|
||||||
|
public Set<HolderReference> getInheritances(HolderReference holder) {
|
||||||
|
Set<HolderReference> set = new HashSet<>();
|
||||||
|
set.add(holder);
|
||||||
|
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
Set<HolderReference> clone = new HashSet<>(set);
|
||||||
|
|
||||||
|
boolean work = false;
|
||||||
|
|
||||||
|
for (HolderReference s : clone) {
|
||||||
|
if (set.addAll(map.get(s))) {
|
||||||
|
work = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!work) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
set.remove(holder);
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a holder and the groups they inherit from within this map.
|
||||||
|
*
|
||||||
|
* @param holder the holder to add
|
||||||
|
* @param inheritedGroups a list of groups the holder inherits from
|
||||||
|
*/
|
||||||
|
public void putAll(HolderReference holder, Set<HolderReference> inheritedGroups) {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
map.entries().removeIf(entry -> entry.getValue().equals(holder));
|
||||||
|
|
||||||
|
for (HolderReference child : inheritedGroups) {
|
||||||
|
map.put(child, holder);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears defined inheritances for the given holder name.
|
||||||
|
*
|
||||||
|
* @param holder the holder name to clear
|
||||||
|
*/
|
||||||
|
public void clear(HolderReference holder) {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
map.entries().removeIf(entry -> entry.getValue().equals(holder));
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invalidateInheritances(HolderReference holder) {
|
||||||
|
Set<HolderReference> toInvalidate = getInheritances(holder);
|
||||||
|
toInvalidate.forEach(hr -> hr.apply(plugin, INVALIDATE_CONSUMER));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* 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.common.caching.handlers;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import me.lucko.luckperms.common.LuckPermsPlugin;
|
||||||
|
import me.lucko.luckperms.common.core.model.Group;
|
||||||
|
import me.lucko.luckperms.common.core.model.PermissionHolder;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor(staticName = "of")
|
||||||
|
public class GroupReference implements HolderReference<String> {
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HolderType getType() {
|
||||||
|
return HolderType.GROUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(LuckPermsPlugin plugin, Consumer<PermissionHolder> consumer) {
|
||||||
|
Group group = plugin.getGroupManager().getIfLoaded(id);
|
||||||
|
if (group == null) return;
|
||||||
|
|
||||||
|
consumer.accept(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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.common.caching.handlers;
|
||||||
|
|
||||||
|
import me.lucko.luckperms.common.LuckPermsPlugin;
|
||||||
|
import me.lucko.luckperms.common.core.model.PermissionHolder;
|
||||||
|
import me.lucko.luckperms.common.utils.Identifiable;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public interface HolderReference<T> extends Identifiable<T> {
|
||||||
|
|
||||||
|
HolderType getType();
|
||||||
|
void apply(LuckPermsPlugin plugin, Consumer<PermissionHolder> consumer);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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.common.caching.handlers;
|
||||||
|
|
||||||
|
public enum HolderType {
|
||||||
|
USER, GROUP
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* 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.common.caching.handlers;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import me.lucko.luckperms.common.LuckPermsPlugin;
|
||||||
|
import me.lucko.luckperms.common.core.UserIdentifier;
|
||||||
|
import me.lucko.luckperms.common.core.model.PermissionHolder;
|
||||||
|
import me.lucko.luckperms.common.core.model.User;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor(staticName = "of")
|
||||||
|
public class UserReference implements HolderReference<UserIdentifier> {
|
||||||
|
private final UserIdentifier id;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HolderType getType() {
|
||||||
|
return HolderType.USER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(LuckPermsPlugin plugin, Consumer<PermissionHolder> consumer) {
|
||||||
|
User user = plugin.getUserManager().getIfLoaded(id);
|
||||||
|
if (user == null) return;
|
||||||
|
|
||||||
|
consumer.accept(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* 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.common.caching.holder;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import me.lucko.luckperms.api.Contexts;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ToString
|
||||||
|
@EqualsAndHashCode
|
||||||
|
@AllArgsConstructor(staticName = "of")
|
||||||
|
public class ExportNodesHolder {
|
||||||
|
|
||||||
|
private final Contexts contexts;
|
||||||
|
private final Boolean lowerCase;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* 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.common.caching.holder;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
|
import me.lucko.luckperms.common.utils.ExtractedContexts;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ToString
|
||||||
|
@EqualsAndHashCode
|
||||||
|
@AllArgsConstructor(staticName = "of")
|
||||||
|
public class GetAllNodesHolder {
|
||||||
|
|
||||||
|
private final ImmutableList<String> excludedGroups;
|
||||||
|
private final ExtractedContexts contexts;
|
||||||
|
|
||||||
|
}
|
@ -27,6 +27,8 @@ import lombok.Getter;
|
|||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
|
||||||
import me.lucko.luckperms.common.LuckPermsPlugin;
|
import me.lucko.luckperms.common.LuckPermsPlugin;
|
||||||
|
import me.lucko.luckperms.common.caching.handlers.GroupReference;
|
||||||
|
import me.lucko.luckperms.common.caching.handlers.HolderReference;
|
||||||
import me.lucko.luckperms.common.utils.Identifiable;
|
import me.lucko.luckperms.common.utils.Identifiable;
|
||||||
|
|
||||||
@ToString(of = {"name"})
|
@ToString(of = {"name"})
|
||||||
@ -62,4 +64,9 @@ public class Group extends PermissionHolder implements Identifiable<String> {
|
|||||||
public String getFriendlyName() {
|
public String getFriendlyName() {
|
||||||
return getDisplayName();
|
return getDisplayName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HolderReference<String> toReference() {
|
||||||
|
return GroupReference.of(getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,10 @@ import lombok.AccessLevel;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.cache.CacheLoader;
|
||||||
|
import com.google.common.cache.LoadingCache;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.ImmutableSortedSet;
|
import com.google.common.collect.ImmutableSortedSet;
|
||||||
@ -44,6 +48,9 @@ import me.lucko.luckperms.common.LuckPermsPlugin;
|
|||||||
import me.lucko.luckperms.common.api.internal.GroupLink;
|
import me.lucko.luckperms.common.api.internal.GroupLink;
|
||||||
import me.lucko.luckperms.common.api.internal.PermissionHolderLink;
|
import me.lucko.luckperms.common.api.internal.PermissionHolderLink;
|
||||||
import me.lucko.luckperms.common.caching.MetaHolder;
|
import me.lucko.luckperms.common.caching.MetaHolder;
|
||||||
|
import me.lucko.luckperms.common.caching.handlers.HolderReference;
|
||||||
|
import me.lucko.luckperms.common.caching.holder.ExportNodesHolder;
|
||||||
|
import me.lucko.luckperms.common.caching.holder.GetAllNodesHolder;
|
||||||
import me.lucko.luckperms.common.commands.utils.Util;
|
import me.lucko.luckperms.common.commands.utils.Util;
|
||||||
import me.lucko.luckperms.common.core.InheritanceInfo;
|
import me.lucko.luckperms.common.core.InheritanceInfo;
|
||||||
import me.lucko.luckperms.common.core.NodeBuilder;
|
import me.lucko.luckperms.common.core.NodeBuilder;
|
||||||
@ -77,26 +84,6 @@ import java.util.stream.Collectors;
|
|||||||
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
|
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
|
||||||
public abstract class PermissionHolder {
|
public abstract class PermissionHolder {
|
||||||
|
|
||||||
public static Map<String, Boolean> exportToLegacy(Set<Node> nodes) {
|
|
||||||
Map<String, Boolean> m = new HashMap<>();
|
|
||||||
for (Node node : nodes) {
|
|
||||||
m.put(node.toSerializedNode(), node.getValue());
|
|
||||||
}
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Node.Builder buildNode(String permission) {
|
|
||||||
return new NodeBuilder(permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ImmutableLocalizedNode makeLocal(Node node, String location) {
|
|
||||||
return ImmutableLocalizedNode.of(node, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Node makeNode(String s, Boolean b) {
|
|
||||||
return NodeFactory.fromSerialisedNode(s, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The UUID of the user / name of the group.
|
* The UUID of the user / name of the group.
|
||||||
* Used to prevent circular inheritance issues
|
* Used to prevent circular inheritance issues
|
||||||
@ -110,25 +97,83 @@ public abstract class PermissionHolder {
|
|||||||
@Getter(AccessLevel.PROTECTED)
|
@Getter(AccessLevel.PROTECTED)
|
||||||
private final LuckPermsPlugin plugin;
|
private final LuckPermsPlugin plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The holders persistent nodes
|
||||||
|
*/
|
||||||
private final Set<Node> nodes = new HashSet<>();
|
private final Set<Node> nodes = new HashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The holders transient nodes
|
||||||
|
*/
|
||||||
private final Set<Node> transientNodes = new HashSet<>();
|
private final Set<Node> transientNodes = new HashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lock used by Storage implementations to prevent concurrent read/writes
|
||||||
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
private final Lock ioLock = new ReentrantLock();
|
private final Lock ioLock = new ReentrantLock();
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CACHES - cache the result of a number of methods in this class, until they are invalidated.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Internal Caches - only depend on the state of this instance. */
|
||||||
|
|
||||||
private Cache<ImmutableSet<Node>> enduringCache = new Cache<>(() -> {
|
private Cache<ImmutableSet<Node>> enduringCache = new Cache<>(() -> {
|
||||||
synchronized (nodes) {
|
synchronized (nodes) {
|
||||||
return ImmutableSet.copyOf(nodes);
|
return ImmutableSet.copyOf(nodes);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
private Cache<ImmutableSet<Node>> transientCache = new Cache<>(() -> {
|
private Cache<ImmutableSet<Node>> transientCache = new Cache<>(() -> {
|
||||||
synchronized (transientNodes) {
|
synchronized (transientNodes) {
|
||||||
return ImmutableSet.copyOf(transientNodes);
|
return ImmutableSet.copyOf(transientNodes);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
private Cache<ImmutableSortedSet<LocalizedNode>> cache = new Cache<>(this::cacheApply);
|
||||||
|
private Cache<ImmutableSortedSet<LocalizedNode>> mergedCache = new Cache<>(this::mergedCacheApply);
|
||||||
|
|
||||||
private Cache<ImmutableSortedSet<LocalizedNode>> cache = new Cache<>(() -> {
|
/* External Caches - may depend on the state of other instances. */
|
||||||
|
|
||||||
|
private LoadingCache<GetAllNodesHolder, SortedSet<LocalizedNode>> getAllNodesCache = CacheBuilder.newBuilder()
|
||||||
|
.build(new CacheLoader<GetAllNodesHolder, SortedSet<LocalizedNode>>() {
|
||||||
|
@Override
|
||||||
|
public SortedSet<LocalizedNode> load(GetAllNodesHolder getAllNodesHolder) {
|
||||||
|
return getAllNodesCacheApply(getAllNodesHolder);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
private LoadingCache<ExtractedContexts, Set<LocalizedNode>> getAllNodesFilteredCache = CacheBuilder.newBuilder()
|
||||||
|
.build(new CacheLoader<ExtractedContexts, Set<LocalizedNode>>() {
|
||||||
|
@Override
|
||||||
|
public Set<LocalizedNode> load(ExtractedContexts extractedContexts) throws Exception {
|
||||||
|
return getAllNodesFilteredApply(extractedContexts);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
private LoadingCache<ExportNodesHolder, Map<String, Boolean>> exportNodesCache = CacheBuilder.newBuilder()
|
||||||
|
.build(new CacheLoader<ExportNodesHolder, Map<String, Boolean>>() {
|
||||||
|
@Override
|
||||||
|
public Map<String, Boolean> load(ExportNodesHolder exportNodesHolder) throws Exception {
|
||||||
|
return exportNodesApply(exportNodesHolder);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Caching apply methods. Are just called by the caching instances to gather data about the instance. */
|
||||||
|
|
||||||
|
private void invalidateCache(boolean enduring) {
|
||||||
|
if (enduring) {
|
||||||
|
enduringCache.invalidate();
|
||||||
|
} else {
|
||||||
|
transientCache.invalidate();
|
||||||
|
}
|
||||||
|
cache.invalidate();
|
||||||
|
mergedCache.invalidate();
|
||||||
|
invalidateInheritanceCaches();
|
||||||
|
plugin.getCachedStateManager().invalidateInheritances(toReference());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImmutableSortedSet<LocalizedNode> cacheApply() {
|
||||||
TreeSet<LocalizedNode> combined = new TreeSet<>(PriorityComparator.reverse());
|
TreeSet<LocalizedNode> combined = new TreeSet<>(PriorityComparator.reverse());
|
||||||
Set<Node> enduring = getNodes();
|
Set<Node> enduring = getNodes();
|
||||||
if (!enduring.isEmpty()) {
|
if (!enduring.isEmpty()) {
|
||||||
@ -160,9 +205,9 @@ public abstract class PermissionHolder {
|
|||||||
higherPriority.add(entry);
|
higherPriority.add(entry);
|
||||||
}
|
}
|
||||||
return ImmutableSortedSet.copyOfSorted(combined);
|
return ImmutableSortedSet.copyOfSorted(combined);
|
||||||
});
|
}
|
||||||
|
|
||||||
private Cache<ImmutableSortedSet<LocalizedNode>> mergedCache = new Cache<>(() -> {
|
private ImmutableSortedSet<LocalizedNode> mergedCacheApply() {
|
||||||
TreeSet<LocalizedNode> combined = new TreeSet<>(PriorityComparator.reverse());
|
TreeSet<LocalizedNode> combined = new TreeSet<>(PriorityComparator.reverse());
|
||||||
Set<Node> enduring = getNodes();
|
Set<Node> enduring = getNodes();
|
||||||
if (!enduring.isEmpty()) {
|
if (!enduring.isEmpty()) {
|
||||||
@ -194,9 +239,137 @@ public abstract class PermissionHolder {
|
|||||||
higherPriority.add(entry);
|
higherPriority.add(entry);
|
||||||
}
|
}
|
||||||
return ImmutableSortedSet.copyOfSorted(combined);
|
return ImmutableSortedSet.copyOfSorted(combined);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
public void invalidateInheritanceCaches() {
|
||||||
|
getAllNodesCache.invalidateAll();
|
||||||
|
getAllNodesFilteredCache.invalidateAll();
|
||||||
|
exportNodesCache.invalidateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
private SortedSet<LocalizedNode> getAllNodesCacheApply(GetAllNodesHolder getAllNodesHolder) {
|
||||||
|
List<String> excludedGroups = new ArrayList<>(getAllNodesHolder.getExcludedGroups());
|
||||||
|
ExtractedContexts contexts = getAllNodesHolder.getContexts();
|
||||||
|
|
||||||
|
SortedSet<LocalizedNode> all = new TreeSet<>((SortedSet<LocalizedNode>) getPermissions(true));
|
||||||
|
|
||||||
|
excludedGroups.add(getObjectName().toLowerCase());
|
||||||
|
|
||||||
|
Set<Node> parents = all.stream()
|
||||||
|
.map(LocalizedNode::getNode)
|
||||||
|
.filter(Node::getValue)
|
||||||
|
.filter(Node::isGroupNode)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
Contexts context = contexts.getContexts();
|
||||||
|
String server = contexts.getServer();
|
||||||
|
String world = contexts.getWorld();
|
||||||
|
|
||||||
|
parents.removeIf(node ->
|
||||||
|
!node.shouldApplyOnServer(server, context.isApplyGlobalGroups(), plugin.getConfiguration().isApplyingRegex()) ||
|
||||||
|
!node.shouldApplyOnWorld(world, context.isApplyGlobalWorldGroups(), plugin.getConfiguration().isApplyingRegex()) ||
|
||||||
|
!node.shouldApplyWithContext(contexts.getContextSet(), false)
|
||||||
|
);
|
||||||
|
|
||||||
|
TreeSet<Map.Entry<Integer, Node>> sortedParents = new TreeSet<>(Util.META_COMPARATOR.reversed());
|
||||||
|
Map<String, Integer> weights = plugin.getConfiguration().getGroupWeights();
|
||||||
|
for (Node node : parents) {
|
||||||
|
if (weights.containsKey(node.getGroupName().toLowerCase())) {
|
||||||
|
sortedParents.add(Maps.immutableEntry(weights.get(node.getGroupName().toLowerCase()), node));
|
||||||
|
} else {
|
||||||
|
sortedParents.add(Maps.immutableEntry(0, node));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<Integer, Node> e : sortedParents) {
|
||||||
|
Node parent = e.getValue();
|
||||||
|
Group group = plugin.getGroupManager().getIfLoaded(parent.getGroupName());
|
||||||
|
if (group == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (excludedGroups.contains(group.getObjectName().toLowerCase())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
inherited:
|
||||||
|
for (LocalizedNode inherited : group.getAllNodes(excludedGroups, contexts)) {
|
||||||
|
for (LocalizedNode existing : all) {
|
||||||
|
if (existing.getNode().almostEquals(inherited.getNode())) {
|
||||||
|
continue inherited;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
all.add(inherited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<LocalizedNode> getAllNodesFilteredApply(ExtractedContexts contexts) {
|
||||||
|
SortedSet<LocalizedNode> allNodes;
|
||||||
|
|
||||||
|
Contexts context = contexts.getContexts();
|
||||||
|
String server = contexts.getServer();
|
||||||
|
String world = contexts.getWorld();
|
||||||
|
|
||||||
|
if (context.isApplyGroups()) {
|
||||||
|
allNodes = getAllNodes(null, contexts);
|
||||||
|
} else {
|
||||||
|
allNodes = new TreeSet<>((SortedSet<LocalizedNode>) getPermissions(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
allNodes.removeIf(node ->
|
||||||
|
!node.shouldApplyOnServer(server, context.isIncludeGlobal(), plugin.getConfiguration().isApplyingRegex()) ||
|
||||||
|
!node.shouldApplyOnWorld(world, context.isIncludeGlobalWorld(), plugin.getConfiguration().isApplyingRegex()) ||
|
||||||
|
!node.shouldApplyWithContext(contexts.getContextSet(), false)
|
||||||
|
);
|
||||||
|
|
||||||
|
Set<LocalizedNode> perms = ConcurrentHashMap.newKeySet();
|
||||||
|
|
||||||
|
all:
|
||||||
|
for (LocalizedNode ln : allNodes) {
|
||||||
|
// Force higher priority nodes to override
|
||||||
|
for (LocalizedNode alreadyIn : perms) {
|
||||||
|
if (ln.getNode().getPermission().equals(alreadyIn.getNode().getPermission())) {
|
||||||
|
continue all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
perms.add(ln);
|
||||||
|
}
|
||||||
|
|
||||||
|
return perms;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Boolean> exportNodesApply(ExportNodesHolder exportNodesHolder) {
|
||||||
|
Contexts context = exportNodesHolder.getContexts();
|
||||||
|
Boolean lowerCase = exportNodesHolder.getLowerCase();
|
||||||
|
|
||||||
|
Map<String, Boolean> perms = new HashMap<>();
|
||||||
|
|
||||||
|
for (LocalizedNode ln : getAllNodesFiltered(ExtractedContexts.generate(context))) {
|
||||||
|
Node node = ln.getNode();
|
||||||
|
|
||||||
|
perms.put(lowerCase ? node.getPermission().toLowerCase() : node.getPermission(), node.getValue());
|
||||||
|
|
||||||
|
if (plugin.getConfiguration().isApplyingShorthand()) {
|
||||||
|
List<String> sh = node.resolveShorthand();
|
||||||
|
if (!sh.isEmpty()) {
|
||||||
|
sh.stream().map(s -> lowerCase ? s.toLowerCase() : s)
|
||||||
|
.filter(s -> !perms.containsKey(s))
|
||||||
|
.forEach(s -> perms.put(s, node.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ImmutableMap.copyOf(perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public abstract String getFriendlyName();
|
public abstract String getFriendlyName();
|
||||||
|
public abstract HolderReference<?> toReference();
|
||||||
|
|
||||||
public Set<Node> getNodes() {
|
public Set<Node> getNodes() {
|
||||||
return enduringCache.get();
|
return enduringCache.get();
|
||||||
@ -238,16 +411,6 @@ public abstract class PermissionHolder {
|
|||||||
invalidateCache(false);
|
invalidateCache(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void invalidateCache(boolean enduring) {
|
|
||||||
if (enduring) {
|
|
||||||
enduringCache.invalidate();
|
|
||||||
} else {
|
|
||||||
transientCache.invalidate();
|
|
||||||
}
|
|
||||||
cache.invalidate();
|
|
||||||
mergedCache.invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Combines and returns this holders nodes in a priority order.
|
* Combines and returns this holders nodes in a priority order.
|
||||||
*
|
*
|
||||||
@ -257,6 +420,104 @@ public abstract class PermissionHolder {
|
|||||||
return mergeTemp ? mergedCache.get() : cache.get();
|
return mergeTemp ? mergedCache.get() : cache.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves inherited nodes and returns them
|
||||||
|
*
|
||||||
|
* @param excludedGroups a list of groups to exclude
|
||||||
|
* @param contexts context to decide if groups should be applied
|
||||||
|
* @return a set of nodes
|
||||||
|
*/
|
||||||
|
public SortedSet<LocalizedNode> getAllNodes(List<String> excludedGroups, ExtractedContexts contexts) {
|
||||||
|
return getAllNodesCache.getUnchecked(GetAllNodesHolder.of(excludedGroups == null ? ImmutableList.of() : ImmutableList.copyOf(excludedGroups), contexts));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all of the nodes that this holder has (and inherits), given the context
|
||||||
|
*
|
||||||
|
* @param contexts the context for this request
|
||||||
|
* @return a map of permissions
|
||||||
|
*/
|
||||||
|
public Set<LocalizedNode> getAllNodesFiltered(ExtractedContexts contexts) {
|
||||||
|
return getAllNodesFilteredCache.getUnchecked(contexts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the output of {@link #getAllNodesFiltered(ExtractedContexts)}, and expands shorthand perms
|
||||||
|
*
|
||||||
|
* @param context the context for this request
|
||||||
|
* @return a map of permissions
|
||||||
|
*/
|
||||||
|
public Map<String, Boolean> exportNodes(Contexts context, boolean lowerCase) {
|
||||||
|
return exportNodesCache.getUnchecked(ExportNodesHolder.of(context, lowerCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
public MetaHolder accumulateMeta(MetaHolder holder, List<String> excludedGroups, ExtractedContexts contexts) {
|
||||||
|
if (holder == null) {
|
||||||
|
holder = new MetaHolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (excludedGroups == null) {
|
||||||
|
excludedGroups = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
excludedGroups.add(getObjectName().toLowerCase());
|
||||||
|
|
||||||
|
Contexts context = contexts.getContexts();
|
||||||
|
String server = contexts.getServer();
|
||||||
|
String world = contexts.getWorld();
|
||||||
|
|
||||||
|
SortedSet<LocalizedNode> all = new TreeSet<>((SortedSet<LocalizedNode>) getPermissions(true));
|
||||||
|
for (LocalizedNode ln : all) {
|
||||||
|
Node n = ln.getNode();
|
||||||
|
|
||||||
|
if (!n.getValue()) continue;
|
||||||
|
if (!n.isMeta() && !n.isPrefix() && !n.isSuffix()) continue;
|
||||||
|
if (!n.shouldApplyOnServer(server, context.isIncludeGlobal(), false)) continue;
|
||||||
|
if (!n.shouldApplyOnWorld(world, context.isIncludeGlobalWorld(), false)) continue;
|
||||||
|
if (!n.shouldApplyWithContext(contexts.getContextSet(), false)) continue;
|
||||||
|
|
||||||
|
holder.accumulateNode(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Node> parents = all.stream()
|
||||||
|
.map(LocalizedNode::getNode)
|
||||||
|
.filter(Node::getValue)
|
||||||
|
.filter(Node::isGroupNode)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
parents.removeIf(node ->
|
||||||
|
!node.shouldApplyOnServer(server, context.isApplyGlobalGroups(), plugin.getConfiguration().isApplyingRegex()) ||
|
||||||
|
!node.shouldApplyOnWorld(world, context.isApplyGlobalWorldGroups(), plugin.getConfiguration().isApplyingRegex()) ||
|
||||||
|
!node.shouldApplyWithContext(contexts.getContextSet(), false)
|
||||||
|
);
|
||||||
|
|
||||||
|
TreeSet<Map.Entry<Integer, Node>> sortedParents = new TreeSet<>(Util.META_COMPARATOR.reversed());
|
||||||
|
Map<String, Integer> weights = plugin.getConfiguration().getGroupWeights();
|
||||||
|
for (Node node : parents) {
|
||||||
|
if (weights.containsKey(node.getGroupName().toLowerCase())) {
|
||||||
|
sortedParents.add(Maps.immutableEntry(weights.get(node.getGroupName().toLowerCase()), node));
|
||||||
|
} else {
|
||||||
|
sortedParents.add(Maps.immutableEntry(0, node));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<Integer, Node> e : sortedParents) {
|
||||||
|
Node parent = e.getValue();
|
||||||
|
Group group = plugin.getGroupManager().getIfLoaded(parent.getGroupName());
|
||||||
|
if (group == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (excludedGroups.contains(group.getObjectName().toLowerCase())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
group.accumulateMeta(holder, excludedGroups, contexts);
|
||||||
|
}
|
||||||
|
|
||||||
|
return holder;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes temporary permissions that have expired
|
* Removes temporary permissions that have expired
|
||||||
*
|
*
|
||||||
@ -311,210 +572,6 @@ public abstract class PermissionHolder {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves inherited nodes and returns them
|
|
||||||
*
|
|
||||||
* @param excludedGroups a list of groups to exclude
|
|
||||||
* @param contexts context to decide if groups should be applied
|
|
||||||
* @return a set of nodes
|
|
||||||
*/
|
|
||||||
public SortedSet<LocalizedNode> getAllNodes(List<String> excludedGroups, ExtractedContexts contexts) {
|
|
||||||
SortedSet<LocalizedNode> all = new TreeSet<>((SortedSet<LocalizedNode>) getPermissions(true));
|
|
||||||
|
|
||||||
if (excludedGroups == null) {
|
|
||||||
excludedGroups = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
excludedGroups.add(getObjectName().toLowerCase());
|
|
||||||
|
|
||||||
Set<Node> parents = all.stream()
|
|
||||||
.map(LocalizedNode::getNode)
|
|
||||||
.filter(Node::getValue)
|
|
||||||
.filter(Node::isGroupNode)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
|
|
||||||
Contexts context = contexts.getContexts();
|
|
||||||
String server = contexts.getServer();
|
|
||||||
String world = contexts.getWorld();
|
|
||||||
|
|
||||||
parents.removeIf(node ->
|
|
||||||
!node.shouldApplyOnServer(server, context.isApplyGlobalGroups(), plugin.getConfiguration().isApplyingRegex()) ||
|
|
||||||
!node.shouldApplyOnWorld(world, context.isApplyGlobalWorldGroups(), plugin.getConfiguration().isApplyingRegex()) ||
|
|
||||||
!node.shouldApplyWithContext(contexts.getContextSet(), false)
|
|
||||||
);
|
|
||||||
|
|
||||||
TreeSet<Map.Entry<Integer, Node>> sortedParents = new TreeSet<>(Util.META_COMPARATOR.reversed());
|
|
||||||
Map<String, Integer> weights = plugin.getConfiguration().getGroupWeights();
|
|
||||||
for (Node node : parents) {
|
|
||||||
if (weights.containsKey(node.getGroupName().toLowerCase())) {
|
|
||||||
sortedParents.add(Maps.immutableEntry(weights.get(node.getGroupName().toLowerCase()), node));
|
|
||||||
} else {
|
|
||||||
sortedParents.add(Maps.immutableEntry(0, node));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Map.Entry<Integer, Node> e : sortedParents) {
|
|
||||||
Node parent = e.getValue();
|
|
||||||
Group group = plugin.getGroupManager().getIfLoaded(parent.getGroupName());
|
|
||||||
if (group == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (excludedGroups.contains(group.getObjectName().toLowerCase())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
inherited:
|
|
||||||
for (LocalizedNode inherited : group.getAllNodes(excludedGroups, contexts)) {
|
|
||||||
for (LocalizedNode existing : all) {
|
|
||||||
if (existing.getNode().almostEquals(inherited.getNode())) {
|
|
||||||
continue inherited;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
all.add(inherited);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return all;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MetaHolder accumulateMeta(MetaHolder holder, List<String> excludedGroups, ExtractedContexts contexts) {
|
|
||||||
if (holder == null) {
|
|
||||||
holder = new MetaHolder();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (excludedGroups == null) {
|
|
||||||
excludedGroups = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
excludedGroups.add(getObjectName().toLowerCase());
|
|
||||||
|
|
||||||
Contexts context = contexts.getContexts();
|
|
||||||
String server = contexts.getServer();
|
|
||||||
String world = contexts.getWorld();
|
|
||||||
|
|
||||||
SortedSet<LocalizedNode> all = new TreeSet<>((SortedSet<LocalizedNode>) getPermissions(true));
|
|
||||||
for (LocalizedNode ln : all) {
|
|
||||||
Node n = ln.getNode();
|
|
||||||
|
|
||||||
if (!n.getValue()) continue;
|
|
||||||
if (!n.isMeta() && !n.isPrefix() && !n.isSuffix()) continue;
|
|
||||||
if (!n.shouldApplyOnServer(server, context.isIncludeGlobal(), false)) continue;
|
|
||||||
if (!n.shouldApplyOnWorld(world, context.isIncludeGlobalWorld(), false)) continue;
|
|
||||||
if (!n.shouldApplyWithContext(contexts.getContextSet(), false)) continue;
|
|
||||||
|
|
||||||
holder.accumulateNode(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<Node> parents = all.stream()
|
|
||||||
.map(LocalizedNode::getNode)
|
|
||||||
.filter(Node::getValue)
|
|
||||||
.filter(Node::isGroupNode)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
|
|
||||||
parents.removeIf(node ->
|
|
||||||
!node.shouldApplyOnServer(server, context.isApplyGlobalGroups(), plugin.getConfiguration().isApplyingRegex()) ||
|
|
||||||
!node.shouldApplyOnWorld(world, context.isApplyGlobalWorldGroups(), plugin.getConfiguration().isApplyingRegex()) ||
|
|
||||||
!node.shouldApplyWithContext(contexts.getContextSet(), false)
|
|
||||||
);
|
|
||||||
|
|
||||||
TreeSet<Map.Entry<Integer, Node>> sortedParents = new TreeSet<>(Util.META_COMPARATOR.reversed());
|
|
||||||
Map<String, Integer> weights = plugin.getConfiguration().getGroupWeights();
|
|
||||||
for (Node node : parents) {
|
|
||||||
if (weights.containsKey(node.getGroupName().toLowerCase())) {
|
|
||||||
sortedParents.add(Maps.immutableEntry(weights.get(node.getGroupName().toLowerCase()), node));
|
|
||||||
} else {
|
|
||||||
sortedParents.add(Maps.immutableEntry(0, node));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Map.Entry<Integer, Node> e : sortedParents) {
|
|
||||||
Node parent = e.getValue();
|
|
||||||
Group group = plugin.getGroupManager().getIfLoaded(parent.getGroupName());
|
|
||||||
if (group == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (excludedGroups.contains(group.getObjectName().toLowerCase())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
group.accumulateMeta(holder, excludedGroups, contexts);
|
|
||||||
}
|
|
||||||
|
|
||||||
return holder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all of the nodes that this holder has (and inherits), given the context
|
|
||||||
*
|
|
||||||
* @param contexts the context for this request
|
|
||||||
* @return a map of permissions
|
|
||||||
*/
|
|
||||||
public Set<LocalizedNode> getAllNodesFiltered(ExtractedContexts contexts) {
|
|
||||||
SortedSet<LocalizedNode> allNodes;
|
|
||||||
|
|
||||||
Contexts context = contexts.getContexts();
|
|
||||||
String server = contexts.getServer();
|
|
||||||
String world = contexts.getWorld();
|
|
||||||
|
|
||||||
if (context.isApplyGroups()) {
|
|
||||||
allNodes = getAllNodes(null, contexts);
|
|
||||||
} else {
|
|
||||||
allNodes = new TreeSet<>((SortedSet<LocalizedNode>) getPermissions(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
allNodes.removeIf(node ->
|
|
||||||
!node.shouldApplyOnServer(server, context.isIncludeGlobal(), plugin.getConfiguration().isApplyingRegex()) ||
|
|
||||||
!node.shouldApplyOnWorld(world, context.isIncludeGlobalWorld(), plugin.getConfiguration().isApplyingRegex()) ||
|
|
||||||
!node.shouldApplyWithContext(contexts.getContextSet(), false)
|
|
||||||
);
|
|
||||||
|
|
||||||
Set<LocalizedNode> perms = ConcurrentHashMap.newKeySet();
|
|
||||||
|
|
||||||
all:
|
|
||||||
for (LocalizedNode ln : allNodes) {
|
|
||||||
// Force higher priority nodes to override
|
|
||||||
for (LocalizedNode alreadyIn : perms) {
|
|
||||||
if (ln.getNode().getPermission().equals(alreadyIn.getNode().getPermission())) {
|
|
||||||
continue all;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
perms.add(ln);
|
|
||||||
}
|
|
||||||
|
|
||||||
return perms;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the output of {@link #getAllNodesFiltered(ExtractedContexts)}, and expands shorthand perms
|
|
||||||
*
|
|
||||||
* @param context the context for this request
|
|
||||||
* @return a map of permissions
|
|
||||||
*/
|
|
||||||
public Map<String, Boolean> exportNodes(Contexts context, boolean lowerCase) {
|
|
||||||
Map<String, Boolean> perms = new HashMap<>();
|
|
||||||
|
|
||||||
for (LocalizedNode ln : getAllNodesFiltered(ExtractedContexts.generate(context))) {
|
|
||||||
Node node = ln.getNode();
|
|
||||||
|
|
||||||
perms.put(lowerCase ? node.getPermission().toLowerCase() : node.getPermission(), node.getValue());
|
|
||||||
|
|
||||||
if (plugin.getConfiguration().isApplyingShorthand()) {
|
|
||||||
List<String> sh = node.resolveShorthand();
|
|
||||||
if (!sh.isEmpty()) {
|
|
||||||
sh.stream().map(s -> lowerCase ? s.toLowerCase() : s)
|
|
||||||
.filter(s -> !perms.containsKey(s))
|
|
||||||
.forEach(s -> perms.put(s, node.getValue()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ImmutableMap.copyOf(perms);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the holder has a permission node
|
* Check if the holder has a permission node
|
||||||
*
|
*
|
||||||
@ -1029,4 +1086,24 @@ public abstract class PermissionHolder {
|
|||||||
.map(Node::getGroupName)
|
.map(Node::getGroupName)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Map<String, Boolean> exportToLegacy(Set<Node> nodes) {
|
||||||
|
Map<String, Boolean> m = new HashMap<>();
|
||||||
|
for (Node node : nodes) {
|
||||||
|
m.put(node.toSerializedNode(), node.getValue());
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Node.Builder buildNode(String permission) {
|
||||||
|
return new NodeBuilder(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ImmutableLocalizedNode makeLocal(Node node, String location) {
|
||||||
|
return ImmutableLocalizedNode.of(node, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Node makeNode(String s, Boolean b) {
|
||||||
|
return NodeFactory.fromSerialisedNode(s, b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,8 @@ import me.lucko.luckperms.api.event.events.UserPermissionRefreshEvent;
|
|||||||
import me.lucko.luckperms.common.LuckPermsPlugin;
|
import me.lucko.luckperms.common.LuckPermsPlugin;
|
||||||
import me.lucko.luckperms.common.api.internal.UserLink;
|
import me.lucko.luckperms.common.api.internal.UserLink;
|
||||||
import me.lucko.luckperms.common.caching.UserCache;
|
import me.lucko.luckperms.common.caching.UserCache;
|
||||||
|
import me.lucko.luckperms.common.caching.handlers.HolderReference;
|
||||||
|
import me.lucko.luckperms.common.caching.handlers.UserReference;
|
||||||
import me.lucko.luckperms.common.core.UserIdentifier;
|
import me.lucko.luckperms.common.core.UserIdentifier;
|
||||||
import me.lucko.luckperms.common.utils.BufferedRequest;
|
import me.lucko.luckperms.common.utils.BufferedRequest;
|
||||||
import me.lucko.luckperms.common.utils.Identifiable;
|
import me.lucko.luckperms.common.utils.Identifiable;
|
||||||
@ -99,6 +101,11 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HolderReference<UserIdentifier> toReference() {
|
||||||
|
return UserReference.of(getId());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up the UserData cache
|
* Sets up the UserData cache
|
||||||
* Blocking call.
|
* Blocking call.
|
||||||
|
@ -32,6 +32,7 @@ import me.lucko.luckperms.api.LuckPermsApi;
|
|||||||
import me.lucko.luckperms.api.PlatformType;
|
import me.lucko.luckperms.api.PlatformType;
|
||||||
import me.lucko.luckperms.common.LuckPermsPlugin;
|
import me.lucko.luckperms.common.LuckPermsPlugin;
|
||||||
import me.lucko.luckperms.common.api.ApiProvider;
|
import me.lucko.luckperms.common.api.ApiProvider;
|
||||||
|
import me.lucko.luckperms.common.caching.handlers.CachedStateManager;
|
||||||
import me.lucko.luckperms.common.calculators.CalculatorFactory;
|
import me.lucko.luckperms.common.calculators.CalculatorFactory;
|
||||||
import me.lucko.luckperms.common.commands.BaseCommand;
|
import me.lucko.luckperms.common.commands.BaseCommand;
|
||||||
import me.lucko.luckperms.common.commands.ConsecutiveExecutor;
|
import me.lucko.luckperms.common.commands.ConsecutiveExecutor;
|
||||||
@ -141,6 +142,7 @@ public class LPSpongePlugin implements LuckPermsPlugin {
|
|||||||
private ConsecutiveExecutor consecutiveExecutor;
|
private ConsecutiveExecutor consecutiveExecutor;
|
||||||
private LuckPermsService service;
|
private LuckPermsService service;
|
||||||
private LocaleManager localeManager;
|
private LocaleManager localeManager;
|
||||||
|
private CachedStateManager cachedStateManager;
|
||||||
private ContextManager<Subject> contextManager;
|
private ContextManager<Subject> contextManager;
|
||||||
private CalculatorFactory calculatorFactory;
|
private CalculatorFactory calculatorFactory;
|
||||||
private BufferedRequest<Void> updateTaskBuffer;
|
private BufferedRequest<Void> updateTaskBuffer;
|
||||||
@ -215,6 +217,7 @@ public class LPSpongePlugin implements LuckPermsPlugin {
|
|||||||
importer = new Importer(commandManager);
|
importer = new Importer(commandManager);
|
||||||
consecutiveExecutor = new ConsecutiveExecutor(commandManager);
|
consecutiveExecutor = new ConsecutiveExecutor(commandManager);
|
||||||
calculatorFactory = new SpongeCalculatorFactory(this);
|
calculatorFactory = new SpongeCalculatorFactory(this);
|
||||||
|
cachedStateManager = new CachedStateManager(this);
|
||||||
|
|
||||||
contextManager = new ContextManager<>();
|
contextManager = new ContextManager<>();
|
||||||
contextManager.registerCalculator(new ServerCalculator<>(getConfiguration().getServer()));
|
contextManager.registerCalculator(new ServerCalculator<>(getConfiguration().getServer()));
|
||||||
|
Loading…
Reference in New Issue
Block a user