Compare commits

...
17 Commits
Author SHA1 Message Date
Luck 6f54e57d43 Implement luckiness calculator 2021-04-01 06:45:02 +01:00
Luck d150bd39a5 Send messaging service ping for creategroup command (#2956) 2021-03-25 22:28:52 +00:00
Federico López f813122468 Fix 'meta settemp' ignoring temporary modifier (#2958) 2021-03-24 21:28:00 +00:00
Luck 2e62c98c45 Avoid blocking worker pool for command timeout check (#2951)
3rd time lucky...
2021-03-24 12:40:24 +00:00
Luck f0e1a8f092 Remove some BungeeCord/Velocity command aliases 2021-03-23 13:45:34 +00:00
Luck 2dc6902001 Fix deadlock when lots of commands are executed at the same time (#2951) 2021-03-23 11:10:18 +00:00
Luck 21f5c24847 Optimize Bukkit/Nukkit ChildProcessor refresh (#2952) 2021-03-20 18:17:17 +00:00
Luck 6b2c4bca58 Change command manager lock handling to avoid non-fair tryLock call 2021-03-20 15:28:45 +00:00
Luck 4564a97371 Update fabric-loop, explicitly set archivesBaseName to 'luckperms' (#2880) 2021-03-19 20:39:42 +00:00
Luck 1df66255e4 Fix RCON permission checking for LP commands on Bukkit (#2948) 2021-03-19 20:03:21 +00:00
Luck 1c7e2dfb8c Fix very lenient duration parsing (#2940) 2021-03-19 19:59:15 +00:00
Luck ad1ba3c126 Fix player locale retrieval on Bukkit 1.8 (#2930) 2021-03-19 19:33:18 +00:00
lucko 0f63b024e4 Update readme to clarify build output dirs 2021-03-15 12:17:02 +00:00
Daniil a17b441165 Don't rely on getPackage to determine generated class name (#2937) 2021-03-15 12:12:02 +00:00
Luck e3138eb94c Make console detection more resilient against spoofed UUIDs 2021-03-13 14:25:33 +00:00
Luck 53bb61f9b2 Allow server name to be specified by a system property 2021-03-13 14:16:02 +00:00
Luck 370e2aed0d Add some missing null annotations 2021-03-13 14:14:59 +00:00
56 changed files with 476 additions and 175 deletions

No files matched your search

+1 -1
View File
@@ -34,7 +34,7 @@ cd LuckPerms/
./gradlew build
```
You can find the output jars in the `build/libs` directories.
You can find the output jars in the `loader/build/libs` or `build/libs` directories.
## Contributing
#### Pull Requests
@@ -170,7 +170,7 @@ public interface Action extends Comparable<Action> {
* @param type the type
* @return the builder
*/
@NonNull Builder targetType(Action.Target.Type type);
@NonNull Builder targetType(Target.@NonNull Type type);
/**
* Sets the acted object for the entry.
@@ -103,7 +103,7 @@ public interface ContextCalculator<T> {
*
* @return a set of potential contexts
*/
default ContextSet estimatePotentialContexts() {
default @NonNull ContextSet estimatePotentialContexts() {
return ImmutableContextSet.empty();
}
@@ -40,7 +40,7 @@ public interface MessagingService {
*
* @return the name of this messaging service
*/
String getName();
@NonNull String getName();
/**
* Uses the messaging service to inform other servers about a general
@@ -201,11 +201,11 @@ public interface Node {
/**
* Gets the metadata corresponding to the given <code>key</code>, if present.
*
* @param key the key
* @param <T> the metadata type
* @param key the key
* @return the data, if present
*/
<T> Optional<T> getMetadata(NodeMetadataKey<T> key);
<T> Optional<T> getMetadata(@NonNull NodeMetadataKey<T> key);
/**
* Gets the metadata corresponding to the given <code>key</code>, throwing an exception
@@ -216,7 +216,7 @@ public interface Node {
* @return the data
* @throws IllegalStateException if data isn't present
*/
default <T> T metadata(NodeMetadataKey<T> key) throws IllegalStateException {
default <T> T metadata(@NonNull NodeMetadataKey<T> key) throws IllegalStateException {
return getMetadata(key).orElseThrow(() -> new IllegalStateException("Node '" + getKey() + "' does not have '" + key.name() + "' attached."));
}
@@ -109,7 +109,7 @@ public interface NodeBuilder<N extends ScopedNode<N, B>, B extends NodeBuilder<N
* @param unit the unit <code>duration</code> is measured in
* @return the builder
*/
default @NonNull B expiry(long duration, TimeUnit unit) {
default @NonNull B expiry(long duration, @NonNull TimeUnit unit) {
if (duration <= 0) {
throw new IllegalArgumentException("duration must be positive");
}
@@ -35,6 +35,8 @@ import net.luckperms.api.node.types.RegexPermissionNode;
import net.luckperms.api.node.types.SuffixNode;
import net.luckperms.api.node.types.WeightNode;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
@@ -48,59 +50,105 @@ public interface NodeType<T extends Node> {
/**
* Node type for {@link PermissionNode}.
*/
NodeType<PermissionNode> PERMISSION = new SimpleNodeType<>("PERMISSION", n -> n instanceof PermissionNode, n -> (PermissionNode) n);
NodeType<PermissionNode> PERMISSION = new SimpleNodeType<>(
"PERMISSION",
n -> n instanceof PermissionNode,
n -> (PermissionNode) n
);
/**
* Node type for {@link RegexPermissionNode}.
*/
NodeType<RegexPermissionNode> REGEX_PERMISSION = new SimpleNodeType<>("REGEX_PERMISSION", n -> n instanceof RegexPermissionNode, n -> (RegexPermissionNode) n);
NodeType<RegexPermissionNode> REGEX_PERMISSION = new SimpleNodeType<>(
"REGEX_PERMISSION",
n -> n instanceof RegexPermissionNode,
n -> (RegexPermissionNode) n
);
/**
* Node type for {@link InheritanceNode}.
*/
NodeType<InheritanceNode> INHERITANCE = new SimpleNodeType<>("INHERITANCE", n -> n instanceof InheritanceNode, n -> (InheritanceNode) n);
NodeType<InheritanceNode> INHERITANCE = new SimpleNodeType<>(
"INHERITANCE",
n -> n instanceof InheritanceNode,
n -> (InheritanceNode) n
);
/**
* Node type for {@link PrefixNode}.
*/
NodeType<PrefixNode> PREFIX = new SimpleNodeType<>("PREFIX", n -> n instanceof PrefixNode, n -> (PrefixNode) n);
NodeType<PrefixNode> PREFIX = new SimpleNodeType<>(
"PREFIX",
n -> n instanceof PrefixNode,
n -> (PrefixNode) n
);
/**
* Node type for {@link SuffixNode}.
*/
NodeType<SuffixNode> SUFFIX = new SimpleNodeType<>("SUFFIX", n -> n instanceof SuffixNode, n -> (SuffixNode) n);
NodeType<SuffixNode> SUFFIX = new SimpleNodeType<>(
"SUFFIX",
n -> n instanceof SuffixNode,
n -> (SuffixNode) n
);
/**
* Node type for {@link MetaNode}.
*/
NodeType<MetaNode> META = new SimpleNodeType<>("META", n -> n instanceof MetaNode, n -> (MetaNode) n);
NodeType<MetaNode> META = new SimpleNodeType<>(
"META",
n -> n instanceof MetaNode,
n -> (MetaNode) n
);
/**
* Node type for {@link WeightNode}.
*/
NodeType<WeightNode> WEIGHT = new SimpleNodeType<>("WEIGHT", n -> n instanceof WeightNode, n -> (WeightNode) n);
NodeType<WeightNode> WEIGHT = new SimpleNodeType<>(
"WEIGHT",
n -> n instanceof WeightNode,
n -> (WeightNode) n
);
/**
* Node type for {@link DisplayNameNode}.
*/
NodeType<DisplayNameNode> DISPLAY_NAME = new SimpleNodeType<>("DISPLAY_NAME", n -> n instanceof DisplayNameNode, n -> (DisplayNameNode) n);
NodeType<DisplayNameNode> DISPLAY_NAME = new SimpleNodeType<>(
"DISPLAY_NAME",
n -> n instanceof DisplayNameNode,
n -> (DisplayNameNode) n
);
/**
* Node type for {@link ChatMetaNode}.
*
* <p>This is an abstract type, and therefore will never
* be returned from {@link Node#getType()}.</p>
*/
NodeType<ChatMetaNode<?, ?>> CHAT_META = new SimpleNodeType<>("CHAT_META", n -> n instanceof ChatMetaNode<?, ?>, n -> (ChatMetaNode<?, ?>) n);
NodeType<ChatMetaNode<?, ?>> CHAT_META = new SimpleNodeType<>(
"CHAT_META",
n -> n instanceof ChatMetaNode<?, ?>,
n -> (ChatMetaNode<?, ?>) n
);
/**
* Node type for {@link ChatMetaNode} or {@link MetaNode}.
*
* <p>This is an abstract type, and therefore will never
* be returned from {@link Node#getType()}.</p>
*/
NodeType<Node> META_OR_CHAT_META = new SimpleNodeType<>("META_OR_CHAT_META", n -> META.matches(n) || CHAT_META.matches(n), Function.identity());
NodeType<Node> META_OR_CHAT_META = new SimpleNodeType<>(
"META_OR_CHAT_META",
n -> META.matches(n) || CHAT_META.matches(n),
Function.identity()
);
/**
* Gets a name for the node type.
*
* @return a name
*/
String name();
@NonNull String name();
/**
* Returns if the passed node matches the type
@@ -108,7 +156,7 @@ public interface NodeType<T extends Node> {
* @param node the node to test
* @return true if the node has the same type
*/
boolean matches(Node node);
boolean matches(@NonNull Node node);
/**
* Casts the given {@link Node} to the type defined by the {@link NodeType}.
@@ -120,7 +168,7 @@ public interface NodeType<T extends Node> {
* @return the casted node
* @throws IllegalArgumentException if the node to cast does not match the type
*/
T cast(Node node);
@NonNull T cast(@NonNull Node node);
/**
* Attempts to cast the given {@link Node} to the type defined by the
@@ -132,7 +180,7 @@ public interface NodeType<T extends Node> {
* @param node the node to cast
* @return an optional, possibly containing a casted node
*/
default Optional<T> tryCast(Node node) {
default @NonNull Optional<T> tryCast(@NonNull Node node) {
Objects.requireNonNull(node, "node");
if (!matches(node)) {
return Optional.empty();
@@ -147,7 +195,7 @@ public interface NodeType<T extends Node> {
*
* @return a predicate for the {@link #matches(Node)} method.
*/
default Predicate<Node> predicate() {
default @NonNull Predicate<Node> predicate() {
return this::matches;
}
@@ -159,7 +207,7 @@ public interface NodeType<T extends Node> {
* @param and a predicate to AND with the result of the type match check
* @return a matching predicate, ANDed with the given predicate parameter
*/
default Predicate<Node> predicate(Predicate<? super T> and) {
default @NonNull Predicate<Node> predicate(@NonNull Predicate<? super T> and) {
return node -> matches(node) && and.test(cast(node));
}
@@ -25,6 +25,9 @@
package net.luckperms.api.node;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -41,18 +44,18 @@ final class SimpleNodeType<T extends Node> implements NodeType<T> {
}
@Override
public String name() {
public @NotNull String name() {
return this.name;
}
@Override
public boolean matches(Node node) {
public boolean matches(@NonNull Node node) {
Objects.requireNonNull(node, "node");
return this.matches.test(node);
}
@Override
public T cast(Node node) {
public @NotNull T cast(@NonNull Node node) {
if (!matches(node)) {
throw new IllegalArgumentException("Node " + node.getClass() + " does not match " + this.name);
}
@@ -134,7 +134,7 @@ public interface NodeMatcher<T extends Node> extends Predicate<Node> {
* @param <T> the node type
* @return the matcher
*/
static <T extends Node> @NonNull NodeMatcher<T> type(NodeType<? extends T> type) {
static <T extends Node> @NonNull NodeMatcher<T> type(@NonNull NodeType<? extends T> type) {
return LuckPermsProvider.get().getNodeMatcherFactory().type(type);
}
@@ -117,6 +117,6 @@ public interface NodeMatcherFactory {
* @param <T> the node type
* @return the matcher
*/
<T extends Node> @NonNull NodeMatcher<T> type(NodeType<? extends T> type);
<T extends Node> @NonNull NodeMatcher<T> type(@NonNull NodeType<? extends T> type);
}
@@ -93,7 +93,7 @@ public enum DataQueryOrder implements Comparator<DataType> {
* @param comparator the comparator
* @return the ordered data types
*/
public static List<DataType> order(@NonNull Comparator<? super DataType> comparator) {
public static @NonNull List<DataType> order(@NonNull Comparator<? super DataType> comparator) {
int compare = comparator.compare(DataType.TRANSIENT, DataType.NORMAL);
if (compare > 0) {
// transient first
@@ -32,6 +32,7 @@ import net.luckperms.api.query.OptionKey;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Comparator;
import java.util.Objects;
/**
* A function that generates a {@link DataQueryOrder} comparator for
@@ -52,7 +53,8 @@ public interface DataQueryOrderFunction {
* @return the data query order function
* @since 5.2
*/
static DataQueryOrderFunction always(Comparator<DataType> comparator) {
static @NonNull DataQueryOrderFunction always(@NonNull Comparator<DataType> comparator) {
Objects.requireNonNull(comparator, "comparator");
return id -> comparator;
}
@@ -31,6 +31,7 @@ import net.luckperms.api.query.OptionKey;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Objects;
import java.util.function.Predicate;
/**
@@ -53,7 +54,8 @@ public interface DataTypeFilterFunction {
* @param predicate the predicate
* @return the data type filter function
*/
static DataTypeFilterFunction always(Predicate<DataType> predicate) {
static @NonNull DataTypeFilterFunction always(@NonNull Predicate<DataType> predicate) {
Objects.requireNonNull(predicate, "predicate");
return id -> predicate;
}
@@ -94,6 +94,11 @@ public class BukkitSenderFactory extends SenderFactory<LPBukkitPlugin, CommandSe
getPlugin().getBootstrap().getServer().dispatchCommand(sender, command);
}
@Override
protected boolean isConsole(CommandSender sender) {
return sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender;
}
@Override
public void close() {
super.close();
@@ -33,8 +33,9 @@ import me.lucko.luckperms.common.calculator.result.TristateResult;
import net.luckperms.api.util.Tristate;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Permission Processor for Bukkits "child" permission system.
@@ -43,6 +44,7 @@ public class ChildProcessor extends AbstractPermissionProcessor implements Permi
private static final TristateResult.Factory RESULT_FACTORY = new TristateResult.Factory(ChildProcessor.class);
private final LPBukkitPlugin plugin;
private final AtomicBoolean needsRefresh = new AtomicBoolean(false);
private Map<String, TristateResult> childPermissions = Collections.emptyMap();
public ChildProcessor(LPBukkitPlugin plugin) {
@@ -51,23 +53,27 @@ public class ChildProcessor extends AbstractPermissionProcessor implements Permi
@Override
public TristateResult hasPermission(String permission) {
if (this.needsRefresh.compareAndSet(true, false)) {
refresh();
}
return this.childPermissions.getOrDefault(permission, TristateResult.UNDEFINED);
}
@Override
public void refresh() {
Map<String, TristateResult> builder = new ConcurrentHashMap<>();
for (Map.Entry<String, Boolean> e : this.sourceMap.entrySet()) {
Map<String, Boolean> children = this.plugin.getPermissionMap().getChildPermissions(e.getKey(), e.getValue());
for (Map.Entry<String, Boolean> child : children.entrySet()) {
builder.put(child.getKey(), RESULT_FACTORY.result(Tristate.of(child.getValue()), "parent: " + e.getKey()));
}
}
this.childPermissions = builder;
Map<String, TristateResult> childPermissions = new HashMap<>();
this.sourceMap.forEach((key, value) -> {
Map<String, Boolean> children = this.plugin.getPermissionMap().getChildPermissions(key, value);
children.forEach((childKey, childValue) -> {
childPermissions.put(childKey, RESULT_FACTORY.result(Tristate.of(childValue), "parent: " + key));
});
});
this.childPermissions = childPermissions;
this.needsRefresh.set(false);
}
@Override
public void invalidate() {
refresh();
this.needsRefresh.set(true);
}
}
@@ -105,7 +105,7 @@ public class BukkitPlayerCalculator implements ContextCalculator<Player>, Listen
}
@Override
public ContextSet estimatePotentialContexts() {
public @NonNull ContextSet estimatePotentialContexts() {
ImmutableContextSet.Builder builder = new ImmutableContextSetImpl.BuilderImpl();
if (this.gamemode) {
for (GameMode mode : GameMode.values()) {
@@ -47,7 +47,7 @@ public final class PlayerLocaleUtil {
Method legacyMethod = Player.Spigot.class.getMethod("getLocale");
function = player -> {
try {
return (String) legacyMethod.invoke(player);
return (String) legacyMethod.invoke(player.spigot());
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
@@ -43,7 +43,7 @@ public class BungeeCommandExecutor extends Command implements TabExecutor {
private static final String NAME = "luckpermsbungee";
/** The command aliases */
private static final String[] ALIASES = {"lpb", "bperm", "bperms", "bpermission", "bpermissions"};
private static final String[] ALIASES = {"lpb"};
/** The main command name + aliases, prefixed with '/' */
private static final String[] SLASH_ALIASES = Stream.concat(
@@ -81,6 +81,11 @@ public class BungeeSenderFactory extends SenderFactory<LPBungeePlugin, CommandSe
getPlugin().getBootstrap().getProxy().getPluginManager().dispatchCommand(sender, command);
}
@Override
protected boolean isConsole(CommandSender sender) {
return !(sender instanceof ProxiedPlayer);
}
@Override
public void close() {
super.close();
@@ -60,7 +60,7 @@ public class BungeePlayerCalculator implements ContextCalculator<ProxiedPlayer>,
}
@Override
public ContextSet estimatePotentialContexts() {
public @NonNull ContextSet estimatePotentialContexts() {
ImmutableContextSet.Builder builder = new ImmutableContextSetImpl.BuilderImpl();
for (ServerInfo server : this.plugin.getBootstrap().getProxy().getServers().values()) {
builder.add(DefaultContextKeys.WORLD_KEY, server.getName());
@@ -49,7 +49,7 @@ public class RedisBungeeCalculator implements StaticContextCalculator {
}
@Override
public ContextSet estimatePotentialContexts() {
public @NonNull ContextSet estimatePotentialContexts() {
RedisBungeeAPI redisBungee = RedisBungee.getApi();
if (redisBungee == null) {
return ImmutableContextSetImpl.EMPTY;
@@ -269,7 +269,7 @@ public class LoggedAction implements Action {
}
@Override
public @NonNull Builder targetType(Action.Target.Type type) {
public @NonNull Builder targetType(Target.@NonNull Type type) {
this.targetType = Objects.requireNonNull(type, "type");
return this;
}
@@ -42,7 +42,7 @@ public class ApiMessagingService implements MessagingService {
}
@Override
public String getName() {
public @NonNull String getName() {
return this.handle.getName();
}
@@ -0,0 +1,145 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.calculator;
import net.luckperms.api.util.Tristate;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
/**
* The LuckPerms Luckiness Calculator (tm)
*/
public final class LuckyPerms {
/** A sequence of the first 1000 lucky numbers. */
private static final Set<Integer> LUCKY_NUMBERS = generateLuckyNumbers(1000);
/** Is it an unlucky day? */
private static final boolean UNLUCKY_DAY;
/** The unlucky number. */
private static final int UNLUCKY_NUMBER = 666;
/** The lucky number */
private static final int LUCKY_NUMBER;
static {
LocalDate date = LocalDate.now();
UNLUCKY_DAY = date.getDayOfWeek() == DayOfWeek.FRIDAY && date.getDayOfMonth() == 13;
if (Locale.getDefault().getLanguage().equals(Locale.CHINESE.getLanguage())) {
// https://en.wikipedia.org/wiki/Chinese_numerology#Eight
LUCKY_NUMBER = 8;
} else {
// https://en.wikipedia.org/wiki/7
LUCKY_NUMBER = 7;
}
}
/** A ~magical~ counter used when determining luckiness. */
private final AtomicInteger counter;
public LuckyPerms() {
this.counter = new AtomicInteger();
}
/**
* Uses highly advanced, patented LuckPerms(tm) calculations to determine
* the current luckiness measure.
*
* <p>Returns {@link Tristate#TRUE} if lucky, {@link Tristate#FALSE} if unlucky,
* and {@link Tristate#UNDEFINED} if unable to determine.</p>
*
* @return the calculated luckiness measure
*/
public Tristate calculateLuckiness() {
int i = this.counter.incrementAndGet();
// is the counter divisible by the unlucky number? oh no.. that's not a good sign!
if (i % UNLUCKY_NUMBER == 0) {
return Tristate.FALSE;
}
// got lucky on a dice roll & the counter is in the lucky numbers set? must be lucky!
if (isEven(rollDice()) && LUCKY_NUMBERS.contains(i % 1000)) {
return Tristate.TRUE;
}
// is it Friday 13th? I sure hope not.
if (UNLUCKY_DAY) {
return Tristate.FALSE;
}
// is the current system time divisible by the lucky number? sounds promising?!
if ((System.currentTimeMillis() / 1000) % LUCKY_NUMBER == 0) {
return Tristate.TRUE;
}
// well, this is embarrassing.
return Tristate.UNDEFINED;
}
// Roll a 6-sided fair dice.
private static int rollDice() {
return ThreadLocalRandom.current().nextInt(6) + 1;
}
// Is the number even?
private static boolean isEven(int i) {
return i % 2 == 0;
}
// Generates 'lucky numbers'
// see: https://en.wikipedia.org/wiki/Lucky_number
private static Set<Integer> generateLuckyNumbers(int n) {
// generate a sequence 1..n
List<Integer> sequence = new ArrayList<>(n);
for (int i = 1; i <= n; i++) {
sequence.add(i);
}
int j = 1; // the index of the value being considered as the next step
int step = 2; // the step to use when deleting
// delete sequence[j]th elements in the list
while (step < sequence.size()) {
int idx = step - 1;
while (idx < sequence.size()) {
sequence.remove(idx);
idx += (step - 1);
}
step = sequence.get(j++);
}
return new HashSet<>(sequence);
}
}
@@ -33,6 +33,8 @@ import me.lucko.luckperms.common.model.HolderType;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent;
import net.luckperms.api.util.Tristate;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Collection;
@@ -53,6 +55,9 @@ public class PermissionCalculator implements Function<String, TristateResult> {
/** The processors which back this calculator */
private final PermissionProcessor[] processors;
/** The LuckPerms Luckiness Calculator */
private final LuckyPerms feelingLucky = new LuckyPerms();
/** Loading cache for permission checks */
private final LoadingMap<String, TristateResult> lookupCache = LoadingMap.of(this);
@@ -81,6 +86,13 @@ public class PermissionCalculator implements Function<String, TristateResult> {
* @return the result
*/
public TristateResult checkPermission(String permission, PermissionCheckEvent.Origin origin) {
// check the luckiness calculator before anything else.
// if unable to determine luckiness, fallback to checking permissions data *sigh*
Tristate luckiness = this.feelingLucky.calculateLuckiness();
if (luckiness != Tristate.UNDEFINED) {
return TristateResult.of(luckiness);
}
// get the result
TristateResult result = this.lookupCache.get(permission);
@@ -26,6 +26,7 @@
package me.lucko.luckperms.common.command;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import me.lucko.luckperms.common.command.abstraction.Command;
import me.lucko.luckperms.common.command.abstraction.CommandException;
@@ -61,6 +62,7 @@ import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.plugin.AbstractLuckPermsPlugin;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter;
import me.lucko.luckperms.common.plugin.scheduler.SchedulerTask;
import me.lucko.luckperms.common.sender.Sender;
import me.lucko.luckperms.common.util.ImmutableCollectors;
@@ -75,9 +77,11 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -87,13 +91,17 @@ import java.util.stream.Collectors;
public class CommandManager {
private final LuckPermsPlugin plugin;
private final ReentrantLock lock;
private final ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder()
.setDaemon(true)
.setNameFormat("luckperms-command-executor")
.build()
);
private final AtomicBoolean executingCommand = new AtomicBoolean(false);
private final TabCompletions tabCompletions;
private final Map<String, Command<?>> mainCommands;
public CommandManager(LuckPermsPlugin plugin) {
this.plugin = plugin;
this.lock = new ReentrantLock(true); // enable fairness
this.tabCompletions = new TabCompletions(plugin);
this.mainCommands = ImmutableList.<Command<?>>builder()
.add(new UserParentCommand())
@@ -137,28 +145,48 @@ public class CommandManager {
SchedulerAdapter scheduler = this.plugin.getBootstrap().getScheduler();
List<String> argsCopy = new ArrayList<>(args);
// schedule a future to execute the command
AtomicReference<Thread> thread = new AtomicReference<>();
CompletableFuture<CommandResult> future = CompletableFuture.supplyAsync(() -> {
thread.set(Thread.currentThread());
if (!this.lock.tryLock()) {
Message.ALREADY_EXECUTING_COMMAND.send(sender);
this.lock.lock();
}
// if the executingCommand flag is set, there is another command executing at the moment
if (this.executingCommand.get()) {
Message.ALREADY_EXECUTING_COMMAND.send(sender);
}
// a reference to the thread being used to execute the command
AtomicReference<Thread> executorThread = new AtomicReference<>();
// a reference to the timeout task scheduled to catch if this command takes too long to execute
AtomicReference<SchedulerTask> timeoutTask = new AtomicReference<>();
// schedule the actual execution of the command using the command executor service
CompletableFuture<CommandResult> future = CompletableFuture.supplyAsync(() -> {
// set flags
executorThread.set(Thread.currentThread());
this.executingCommand.set(true);
// actually try to execute the command
try {
return execute(sender, label, args);
} catch (Throwable e) {
// catch any exception
this.plugin.getLogger().severe("Exception whilst executing command: " + args, e);
return null;
} finally {
this.lock.unlock();
thread.set(null);
}
}, scheduler.async());
// unset flags
this.executingCommand.set(false);
executorThread.set(null);
// catch if the command doesn't complete within a given time
scheduler.awaitTimeout(future, 10, TimeUnit.SECONDS, () -> handleCommandTimeout(thread, argsCopy));
// cancel the timeout task
SchedulerTask timeout;
if ((timeout = timeoutTask.get()) != null) {
timeout.cancel();
}
}
}, this.executor);
// schedule another task to catch if the command doesn't complete after 10 seconds
timeoutTask.set(scheduler.asyncLater(() -> {
if (!future.isDone()) {
handleCommandTimeout(executorThread, argsCopy);
}
}, 10, TimeUnit.SECONDS));
return future;
}
@@ -166,7 +194,7 @@ public class CommandManager {
private void handleCommandTimeout(AtomicReference<Thread> thread, List<String> args) {
Thread executorThread = thread.get();
if (executorThread == null) {
this.plugin.getLogger().warn("Command execution " + args + " has not completed - but executor thread is null!");
this.plugin.getLogger().warn("Command execution " + args + " has not completed - is another command execution blocking it?");
} else {
String stackTrace = Arrays.stream(executorThread.getStackTrace())
.map(el -> " " + el.toString())
@@ -45,10 +45,10 @@ import me.lucko.luckperms.common.sender.Sender;
import me.lucko.luckperms.common.util.Predicates;
import net.luckperms.api.context.MutableContextSet;
import net.luckperms.api.model.data.DataMutateResult;
import net.luckperms.api.model.data.DataType;
import net.luckperms.api.model.data.TemporaryNodeMergeStrategy;
import net.luckperms.api.node.Node;
import net.luckperms.api.node.NodeEqualityPredicate;
import net.luckperms.api.node.NodeType;
import java.time.Duration;
@@ -80,23 +80,24 @@ public class MetaSetTemp extends GenericChildCommand {
}
Node node = Meta.builder(key, value).withContext(context).expiry(duration).build();
// remove temp meta nodes that have the same key and /different/ value (don't want to remove it if we are accumulating/replacing)
target.removeIf(DataType.NORMAL, context, NodeType.META.predicate(n -> n.hasExpiry() && n.getMetaKey().equalsIgnoreCase(key) && !n.getMetaValue().equals(value)), false);
DataMutateResult.WithMergedNode result = target.setNode(DataType.NORMAL, node, modifier);
if (target.hasNode(DataType.NORMAL, node, NodeEqualityPredicate.IGNORE_EXPIRY_TIME_AND_VALUE).asBoolean()) {
if (result.getResult().wasSuccessful()) {
duration = result.getMergedNode().getExpiryDuration();
Message.SET_META_TEMP_SUCCESS.send(sender, key, value, target, duration, context);
LoggedAction.build().source(sender).target(target)
.description("meta", "settemp", key, value, duration, context)
.build().submit(plugin, sender);
StorageAssistant.save(target, sender, plugin);
return CommandResult.SUCCESS;
} else {
Message.ALREADY_HAS_TEMP_META.send(sender, target, key, value, context);
return CommandResult.STATE_ERROR;
}
target.removeIf(DataType.NORMAL, context, NodeType.META.predicate(n -> n.hasExpiry() && n.getMetaKey().equalsIgnoreCase(key)), false);
duration = target.setNode(DataType.NORMAL, node, modifier).getMergedNode().getExpiryDuration();
Message.SET_META_TEMP_SUCCESS.send(sender, key, value, target, duration, context);
LoggedAction.build().source(sender).target(target)
.description("meta", "settemp", key, value, duration, context)
.build().submit(plugin, sender);
StorageAssistant.save(target, sender, plugin);
return CommandResult.SUCCESS;
}
@Override
@@ -32,6 +32,7 @@ import me.lucko.luckperms.common.command.access.CommandPermission;
import me.lucko.luckperms.common.command.spec.CommandSpec;
import me.lucko.luckperms.common.command.utils.ArgumentException;
import me.lucko.luckperms.common.command.utils.ArgumentList;
import me.lucko.luckperms.common.command.utils.StorageAssistant;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.node.types.DisplayName;
@@ -94,7 +95,7 @@ public class CreateGroup extends SingleCommand {
group.setNode(DataType.NORMAL, DisplayName.builder(displayName).build(), false);
}
plugin.getStorage().saveGroup(group);
StorageAssistant.save(group, sender, plugin);
} catch (Exception e) {
plugin.getLogger().warn("Error whilst creating group", e);
Message.CREATE_ERROR.send(sender, Component.text(groupName));
@@ -85,7 +85,13 @@ public final class ConfigKeys {
/**
* The name of the server
*/
public static final ConfigKey<String> SERVER = lowercaseStringKey("server", "global");
public static final ConfigKey<String> SERVER = key(c -> {
String server = c.getString("server", "global").toLowerCase();
if (server.equals("load-from-system-property")) {
server = System.getProperty("luckperms.server", "global").toLowerCase();
}
return server;
});
/**
* How many minutes to wait between syncs. A value <= 0 will disable syncing.
@@ -54,7 +54,7 @@ public class ConfigurationContextCalculator implements StaticContextCalculator {
}
@Override
public ContextSet estimatePotentialContexts() {
public @NonNull ContextSet estimatePotentialContexts() {
ImmutableContextSet.Builder builder = new ImmutableContextSetImpl.BuilderImpl();
calculate(builder::add);
return builder.build();
@@ -53,8 +53,14 @@ public class ContextSetComparator implements Comparator<ImmutableContextSet> {
return 0;
}
// compare presence of any context (non-empty)
int result = Boolean.compare(!o1.isEmpty(), !o2.isEmpty());
if (result != 0) {
return result;
}
// compare presence of a server context
int result = Boolean.compare(o1.containsKey(DefaultContextKeys.SERVER_KEY), o2.containsKey(DefaultContextKeys.SERVER_KEY));
result = Boolean.compare(o1.containsKey(DefaultContextKeys.SERVER_KEY), o2.containsKey(DefaultContextKeys.SERVER_KEY));
if (result != 0) {
return result;
}
@@ -75,13 +81,10 @@ public class ContextSetComparator implements Comparator<ImmutableContextSet> {
// However, we *have* to maintain transitivity in this comparator (despite how
// expensive/complex it may be) as it is used in the PermissionHolder nodes treemap.
// in order to have consistent ordering, we have to compare the content of the context sets
// by sorting the contents and then comparing which set is greater.
Context[] o1Array = o1 instanceof ImmutableContextSetImpl ? ((ImmutableContextSetImpl) o1).toArray() : o1.toSet().toArray(new Context[0]);
Context[] o2Array = o2 instanceof ImmutableContextSetImpl ? ((ImmutableContextSetImpl) o2).toArray() : o2.toSet().toArray(new Context[0]);
Arrays.sort(o1Array, CONTEXT_COMPARATOR);
Arrays.sort(o2Array, CONTEXT_COMPARATOR);
// in order to have consistent ordering, we have to compare the content of the context sets.
// to do this, obtain sorted array representations of each set, then compare which is greater
Context[] o1Array = o1 instanceof ImmutableContextSetImpl ? ((ImmutableContextSetImpl) o1).toArray() : toArray(o1);
Context[] o2Array = o2 instanceof ImmutableContextSetImpl ? ((ImmutableContextSetImpl) o2).toArray() : toArray(o2);
for (int i = 0; i < o1Array.length; i++) {
Context ent1 = o1Array[i];
@@ -96,7 +99,13 @@ public class ContextSetComparator implements Comparator<ImmutableContextSet> {
throw new AssertionError("sets are equal? " + o1 + " - " + o2);
}
private static final Comparator<Context> CONTEXT_COMPARATOR = ContextSetComparator::compareContexts;
public static final Comparator<Context> CONTEXT_COMPARATOR = ContextSetComparator::compareContexts;
private static Context[] toArray(ImmutableContextSet set) {
Context[] array = set.toSet().toArray(new Context[0]);
Arrays.sort(array, CONTEXT_COMPARATOR);
return array;
}
private static int compareContexts(Context o1, Context o2) {
if (o1 == o2) {
@@ -32,6 +32,8 @@ import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
import me.lucko.luckperms.common.context.ContextSetComparator;
import net.luckperms.api.context.Context;
import net.luckperms.api.context.ContextSatisfyMode;
import net.luckperms.api.context.ContextSet;
@@ -40,6 +42,7 @@ import net.luckperms.api.context.MutableContextSet;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
@@ -74,6 +77,8 @@ public final class ImmutableContextSetImpl extends AbstractContextSet implements
for (Map.Entry<String, String> e : entries) {
this.array[i++] = new ContextImpl(e.getKey(), e.getValue());
}
// sort the array at construction so the comparator doesn't need to
Arrays.sort(this.array, ContextSetComparator.CONTEXT_COMPARATOR);
}
@Override
@@ -106,7 +106,8 @@ public class GeneratedEventClass {
// determine a generated class name of the event
String eventClassSuffix = eventClass.getName().substring(LuckPermsEvent.class.getPackage().getName().length());
String generatedClassName = GeneratedEventClass.class.getPackage().getName() + eventClassSuffix;
String packageWithName = GeneratedEventClass.class.getName();
String generatedClassName = packageWithName.substring(0, packageWithName.lastIndexOf('.')) + eventClassSuffix;
DynamicType.Builder<AbstractEvent> builder = new ByteBuddy(ClassFileVersion.JAVA_V8)
// create a subclass of AbstractEvent
@@ -97,7 +97,7 @@ public abstract class AbstractNode<N extends ScopedNode<N, B>, B extends NodeBui
}
@Override
public <T> Optional<T> getMetadata(NodeMetadataKey<T> key) {
public <T> Optional<T> getMetadata(@NonNull NodeMetadataKey<T> key) {
//noinspection unchecked
T value = (T) this.metadata.get(key);
return Optional.ofNullable(value);
@@ -25,11 +25,8 @@
package me.lucko.luckperms.common.plugin.scheduler;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* A scheduler for running tasks using the systems provided by the platform
@@ -88,27 +85,6 @@ public interface SchedulerAdapter {
*/
SchedulerTask asyncRepeating(Runnable task, long interval, TimeUnit unit);
/**
* Waits for the given time for {@code future} to complete. If the future isn't completed,
* {@code onTimeout} is executed.
*
* @param future the future to wait for
* @param timeout the time to wait
* @param unit the unit of timeout
* @param onTimeout the function to execute when the timeout expires
*/
default void awaitTimeout(CompletableFuture<?> future, long timeout, TimeUnit unit, Runnable onTimeout) {
executeAsync(() -> {
try {
future.get(timeout, unit);
} catch (InterruptedException | ExecutionException e) {
// ignore
} catch (TimeoutException e) {
onTimeout.run();
}
});
}
/**
* Shuts down the scheduler instance.
*
@@ -52,6 +52,7 @@ public final class AbstractSender<T> implements Sender {
private final UUID uniqueId;
private final String name;
private final boolean isConsole;
AbstractSender(LuckPermsPlugin plugin, SenderFactory<?, T> factory, T sender) {
this.plugin = plugin;
@@ -59,6 +60,7 @@ public final class AbstractSender<T> implements Sender {
this.sender = sender;
this.uniqueId = factory.getUniqueId(this.sender);
this.name = factory.getName(this.sender);
this.isConsole = this.factory.isConsole(this.sender);
}
@Override
@@ -102,6 +104,11 @@ public final class AbstractSender<T> implements Sender {
this.factory.performCommand(this.sender, commandLine);
}
@Override
public boolean isConsole() {
return this.isConsole;
}
@Override
public boolean isValid() {
return isConsole() || this.plugin.getBootstrap().isPlayerOnline(this.uniqueId);
@@ -31,16 +31,11 @@ import net.luckperms.api.util.Tristate;
import java.util.UUID;
public abstract class DummySender implements Sender {
public abstract class DummyConsoleSender implements Sender {
private final LuckPermsPlugin platform;
private final UUID uniqueId;
private final String name;
public DummySender(LuckPermsPlugin plugin, UUID uniqueId, String name) {
public DummyConsoleSender(LuckPermsPlugin plugin) {
this.platform = plugin;
this.uniqueId = uniqueId;
this.name = name;
}
@Override
@@ -58,6 +53,11 @@ public abstract class DummySender implements Sender {
}
@Override
public boolean isConsole() {
return true;
}
@Override
public LuckPermsPlugin getPlugin() {
return this.platform;
@@ -65,11 +65,11 @@ public abstract class DummySender implements Sender {
@Override
public UUID getUniqueId() {
return this.uniqueId;
return Sender.CONSOLE_UUID;
}
@Override
public String getName() {
return this.name;
return Sender.CONSOLE_NAME;
}
}
@@ -149,9 +149,7 @@ public interface Sender {
*
* @return if the sender is the console
*/
default boolean isConsole() {
return CONSOLE_UUID.equals(getUniqueId());
}
boolean isConsole();
/**
* Gets whether this sender is still valid & receiving messages.
@@ -62,6 +62,8 @@ public abstract class SenderFactory<P extends LuckPermsPlugin, T> implements Aut
protected abstract void performCommand(T sender, String command);
protected abstract boolean isConsole(T sender);
public final Sender wrap(T sender) {
Objects.requireNonNull(sender, "sender");
return new AbstractSender<>(this.plugin, this, sender);
@@ -47,42 +47,37 @@ public final class DurationParser {
.put(ChronoUnit.DAYS, "d(?:ay)?s?")
.put(ChronoUnit.HOURS, "h(?:our|r)?s?")
.put(ChronoUnit.MINUTES, "m(?:inute|in)?s?")
.put(ChronoUnit.SECONDS, "(?:s(?:econd|ec)?s?)?")
.put(ChronoUnit.SECONDS, "s(?:econd|ec)?s?")
.build();
private static final ChronoUnit[] UNITS = UNITS_PATTERNS.keySet().toArray(new ChronoUnit[0]);
private static final String PATTERN_STRING = UNITS_PATTERNS.values().stream()
.map(pattern -> "(?:(\\d+)\\s*" + pattern + "[,\\s]*)?")
.collect(Collectors.joining());
.collect(Collectors.joining("", "^\\s*", "$"));
private static final Pattern PATTERN = Pattern.compile(PATTERN_STRING, Pattern.CASE_INSENSITIVE);
public static Duration parseDuration(String input) throws IllegalArgumentException {
Matcher matcher = PATTERN.matcher(input);
while (matcher.find()) {
if (matcher.group() == null || matcher.group().isEmpty()) {
continue;
}
Duration duration = Duration.ZERO;
for (int i = 0; i < UNITS.length; i++) {
ChronoUnit unit = UNITS[i];
int g = i + 1;
if (matcher.group(g) != null && !matcher.group(g).isEmpty()) {
int n = Integer.parseInt(matcher.group(g));
if (n > 0) {
duration = duration.plus(unit.getDuration().multipliedBy(n));
}
}
}
return duration;
if (!matcher.matches()) {
throw new IllegalArgumentException("unable to parse duration: " + input);
}
throw new IllegalArgumentException("unable to parse duration: " + input);
Duration duration = Duration.ZERO;
for (int i = 0; i < UNITS.length; i++) {
ChronoUnit unit = UNITS[i];
int g = i + 1;
if (matcher.group(g) != null && !matcher.group(g).isEmpty()) {
int n = Integer.parseInt(matcher.group(g));
if (n > 0) {
duration = duration.plus(unit.getDuration().multipliedBy(n));
}
}
}
return duration;
}
}
@@ -31,6 +31,7 @@ import java.time.Duration;
import java.time.temporal.ChronoUnit;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class DurationParserTest {
@@ -111,6 +112,17 @@ public class DurationParserTest {
test(expected, "5y 4mo 3w 2d 1h 6m 7s");
test(expected, "5y4mo3w2d1h6m7s");
test(expected, "5 years 4 months 3 weeks 2 days 1 hour 6 minutes 7 seconds");
test(expected, "5y, 4mo, 3w, 2d, 1h, 6m, 7s");
test(expected, "5y,4mo,3w,2d,1h,6m,7s");
test(expected, "5 years, 4 months, 3 weeks, 2 days, 1 hour, 6 minutes, 7 seconds");
}
@Test
void testFail() {
assertThrows(IllegalArgumentException.class, () -> DurationParser.parseDuration("definitely not a duration"));
assertThrows(IllegalArgumentException.class, () -> DurationParser.parseDuration("still 1 not a duration"));
assertThrows(IllegalArgumentException.class, () -> DurationParser.parseDuration("still 1s not a duration"));
}
}
+3 -1
View File
@@ -2,9 +2,11 @@ import net.fabricmc.loom.task.RemapJarTask
plugins {
id 'com.github.johnrengelman.shadow' version '4.0.1'
id 'fabric-loom' version '0.5-SNAPSHOT'
id 'fabric-loom' version '0.6-SNAPSHOT'
}
archivesBaseName = 'luckperms'
repositories {
maven { url 'https://maven.fabricmc.net/' }
mavenLocal()
@@ -104,6 +104,11 @@ public class FabricSenderFactory extends SenderFactory<LPFabricPlugin, ServerCom
sender.getMinecraftServer().getCommandManager().execute(sender, command);
}
@Override
protected boolean isConsole(ServerCommandSource sender) {
return sender.getEntity() == null;
}
public static Text toNativeText(Component component) {
return Text.Serializer.fromJson(GsonComponentSerializer.gson().serialize(component));
}
@@ -38,7 +38,7 @@ import me.lucko.luckperms.common.model.manager.group.StandardGroupManager;
import me.lucko.luckperms.common.model.manager.track.StandardTrackManager;
import me.lucko.luckperms.common.model.manager.user.StandardUserManager;
import me.lucko.luckperms.common.plugin.AbstractLuckPermsPlugin;
import me.lucko.luckperms.common.sender.DummySender;
import me.lucko.luckperms.common.sender.DummyConsoleSender;
import me.lucko.luckperms.common.sender.Sender;
import me.lucko.luckperms.common.tasks.CacheHousekeepingTask;
import me.lucko.luckperms.common.tasks.ExpireTemporaryTask;
@@ -221,7 +221,7 @@ public class LPFabricPlugin extends AbstractLuckPermsPlugin {
public Sender getConsoleSender() {
return this.bootstrap.getServer()
.map(s -> this.senderFactory.wrap(s.getCommandSource()))
.orElseGet(() -> new DummySender(this, Sender.CONSOLE_UUID, Sender.CONSOLE_NAME) {
.orElseGet(() -> new DummyConsoleSender(this) {
@Override
public void sendMessage(Component message) {
LPFabricPlugin.this.bootstrap.getPluginLogger().info(PlainComponentSerializer.plain().serialize(TranslationManager.render(message)));
@@ -44,6 +44,7 @@ import net.minecraft.util.Identifier;
import net.minecraft.world.GameMode;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
import java.util.Set;
@@ -86,7 +87,7 @@ public class FabricPlayerCalculator implements ContextCalculator<ServerPlayerEnt
}
@Override
public ContextSet estimatePotentialContexts() {
public @NotNull @NonNull ContextSet estimatePotentialContexts() {
ImmutableContextSet.Builder builder = new ImmutableContextSetImpl.BuilderImpl();
if (this.gamemode) {
+1 -1
View File
@@ -27,7 +27,7 @@
]
},
"mixins": [
"mixins.luckperms.json"
"luckperms.mixins.json"
],
"depends": {
"fabricloader": ">=0.9.0",
@@ -35,6 +35,7 @@ import net.luckperms.api.util.Tristate;
import cn.nukkit.Player;
import cn.nukkit.command.CommandSender;
import cn.nukkit.command.ConsoleCommandSender;
import java.util.Locale;
import java.util.UUID;
@@ -92,4 +93,8 @@ public class NukkitSenderFactory extends SenderFactory<LPNukkitPlugin, CommandSe
getPlugin().getBootstrap().getServer().dispatchCommand(sender, command);
}
@Override
protected boolean isConsole(CommandSender sender) {
return sender instanceof ConsoleCommandSender;
}
}
@@ -33,8 +33,9 @@ import me.lucko.luckperms.nukkit.LPNukkitPlugin;
import net.luckperms.api.util.Tristate;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Permission Processor for Nukkits "child" permission system.
@@ -43,6 +44,7 @@ public class ChildProcessor extends AbstractPermissionProcessor implements Permi
private static final TristateResult.Factory RESULT_FACTORY = new TristateResult.Factory(ChildProcessor.class);
private final LPNukkitPlugin plugin;
private final AtomicBoolean needsRefresh = new AtomicBoolean(false);
private Map<String, TristateResult> childPermissions = Collections.emptyMap();
public ChildProcessor(LPNukkitPlugin plugin) {
@@ -51,23 +53,27 @@ public class ChildProcessor extends AbstractPermissionProcessor implements Permi
@Override
public TristateResult hasPermission(String permission) {
if (this.needsRefresh.compareAndSet(true, false)) {
refresh();
}
return this.childPermissions.getOrDefault(permission, TristateResult.UNDEFINED);
}
@Override
public void refresh() {
Map<String, TristateResult> builder = new ConcurrentHashMap<>();
for (Map.Entry<String, Boolean> e : this.sourceMap.entrySet()) {
Map<String, Boolean> children = this.plugin.getPermissionMap().getChildPermissions(e.getKey(), e.getValue());
for (Map.Entry<String, Boolean> child : children.entrySet()) {
builder.put(child.getKey(), RESULT_FACTORY.result(Tristate.of(child.getValue()), "parent: " + e.getKey()));
}
}
this.childPermissions = builder;
Map<String, TristateResult> childPermissions = new HashMap<>();
this.sourceMap.forEach((key, value) -> {
Map<String, Boolean> children = this.plugin.getPermissionMap().getChildPermissions(key, value);
children.forEach((childKey, childValue) -> {
childPermissions.put(childKey, RESULT_FACTORY.result(Tristate.of(childValue), "parent: " + key));
});
});
this.childPermissions = childPermissions;
this.needsRefresh.set(false);
}
@Override
public void invalidate() {
refresh();
this.needsRefresh.set(true);
}
}
@@ -82,7 +82,7 @@ public class NukkitPlayerCalculator implements ContextCalculator<Player>, Listen
}
@Override
public ContextSet estimatePotentialContexts() {
public @NonNull ContextSet estimatePotentialContexts() {
ImmutableContextSet.Builder builder = new ImmutableContextSetImpl.BuilderImpl();
if (this.gamemode) {
for (int mode : KNOWN_GAMEMODES) {
@@ -38,7 +38,7 @@ import me.lucko.luckperms.common.messaging.MessagingFactory;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.model.manager.track.StandardTrackManager;
import me.lucko.luckperms.common.plugin.AbstractLuckPermsPlugin;
import me.lucko.luckperms.common.sender.DummySender;
import me.lucko.luckperms.common.sender.DummyConsoleSender;
import me.lucko.luckperms.common.sender.Sender;
import me.lucko.luckperms.common.tasks.CacheHousekeepingTask;
import me.lucko.luckperms.common.tasks.ExpireTemporaryTask;
@@ -250,7 +250,7 @@ public class LPSpongePlugin extends AbstractLuckPermsPlugin {
if (this.bootstrap.getGame().isServerAvailable()) {
return this.senderFactory.wrap(this.bootstrap.getGame().getServer().getConsole());
} else {
return new DummySender(this, Sender.CONSOLE_UUID, Sender.CONSOLE_NAME) {
return new DummyConsoleSender(this) {
@Override
public void sendMessage(Component message) {
LPSpongePlugin.this.getLogger().info(LegacyComponentSerializer.legacySection().serialize(TranslationManager.render(message)));
@@ -35,6 +35,7 @@ import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.luckperms.api.util.Tristate;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.command.source.ConsoleSource;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.serializer.TextSerializers;
@@ -89,6 +90,11 @@ public class SpongeSenderFactory extends SenderFactory<LPSpongePlugin, CommandSo
getPlugin().getBootstrap().getGame().getCommandManager().process(source, command);
}
@Override
protected boolean isConsole(CommandSource sender) {
return sender instanceof ConsoleSource;
}
public static Text toNativeText(Component component) {
return TextSerializers.JSON.deserialize(GsonComponentSerializer.gson().serialize(component));
}
@@ -95,7 +95,7 @@ public class SpongePlayerCalculator implements ContextCalculator<Subject> {
}
@Override
public ContextSet estimatePotentialContexts() {
public @NonNull ContextSet estimatePotentialContexts() {
ImmutableContextSet.Builder builder = new ImmutableContextSetImpl.BuilderImpl();
Game game = this.plugin.getBootstrap().getGame();
@@ -39,7 +39,7 @@ import java.util.List;
public class VelocityCommandExecutor extends CommandManager implements RawCommand {
/* The command aliases */
private static final String PRIMARY_ALIAS = "luckpermsvelocity";
private static final String[] ALIASES = {"lpv", "vperm", "vperms", "vpermission", "vpermissions"};
private static final String[] ALIASES = {"lpv"};
/* The command aliases, prefixed with '/' */
private static final String SLASH_PRIMARY_ALIAS = "/luckpermsvelocity";
@@ -26,6 +26,7 @@
package me.lucko.luckperms.velocity;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.ConsoleCommandSource;
import com.velocitypowered.api.proxy.Player;
import me.lucko.luckperms.common.locale.TranslationManager;
@@ -85,4 +86,9 @@ public class VelocitySenderFactory extends SenderFactory<LPVelocityPlugin, Comma
protected void performCommand(CommandSource source, String command) {
getPlugin().getBootstrap().getProxy().getCommandManager().executeAsync(source, command).join();
}
@Override
protected boolean isConsole(CommandSource sender) {
return sender instanceof ConsoleCommandSource;
}
}
@@ -43,6 +43,7 @@ import net.luckperms.api.context.DefaultContextKeys;
import net.luckperms.api.context.ImmutableContextSet;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.NotNull;
public class VelocityPlayerCalculator implements ContextCalculator<Player> {
private final LPVelocityPlugin plugin;
@@ -61,7 +62,7 @@ public class VelocityPlayerCalculator implements ContextCalculator<Player> {
}
@Override
public ContextSet estimatePotentialContexts() {
public @NotNull @NonNull ContextSet estimatePotentialContexts() {
ImmutableContextSet.Builder builder = new ImmutableContextSetImpl.BuilderImpl();
for (RegisteredServer server : this.plugin.getBootstrap().getProxy().getAllServers()) {
builder.add(DefaultContextKeys.WORLD_KEY, server.getServerInfo().getName());