Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f54e57d43 | ||
|
|
d150bd39a5 | ||
|
|
f813122468 | ||
|
|
2e62c98c45 | ||
|
|
f0e1a8f092 | ||
|
|
2dc6902001 | ||
|
|
21f5c24847 | ||
|
|
6b2c4bca58 | ||
|
|
4564a97371 | ||
|
|
1df66255e4 | ||
|
|
1c7e2dfb8c | ||
|
|
ad1ba3c126 | ||
|
|
0f63b024e4 | ||
|
|
a17b441165 | ||
|
|
e3138eb94c | ||
|
|
53bb61f9b2 | ||
|
|
370e2aed0d |
No files matched your search
@@ -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;
|
||||
}
|
||||
|
||||
+1
-1
@@ -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())
|
||||
|
||||
+15
-14
@@ -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.
|
||||
|
||||
+1
-1
@@ -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) {
|
||||
|
||||
+5
@@ -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);
|
||||
|
||||
+9
-9
@@ -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
@@ -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) {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"mixins.luckperms.json"
|
||||
"luckperms.mixins.json"
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=0.9.0",
|
||||
|
||||
File renamed without changes.
@@ -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;
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -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());
|
||||
|
||||
Reference in new issue
Block a user