Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63878166db | ||
|
|
2ea43d4b2e | ||
|
|
442c7d46f9 | ||
|
|
971bb67b51 | ||
|
|
020aff66aa | ||
|
|
198b86d7c3 | ||
|
|
84e7fd4d09 | ||
|
|
4027129052 | ||
|
|
63f67ca72c | ||
|
|
2adaea2dac | ||
|
|
5e48224c78 | ||
|
|
cc1d8984da | ||
|
|
8d9b4182f4 | ||
|
|
f972113d11 | ||
|
|
4d5a24f2c4 | ||
|
|
e2b575dd24 | ||
|
|
7fae141e33 | ||
|
|
6f90e545fc | ||
|
|
ee3bb22c2c | ||
|
|
361a4d66bc | ||
|
|
e1bac438aa | ||
|
|
ac9706b83f | ||
|
|
fea757887c | ||
|
|
1eab127f50 | ||
|
|
92e008ff0a | ||
|
|
9659333d0e | ||
|
|
d8020e77fe | ||
|
|
232d17436c | ||
|
|
1461651e22 | ||
|
|
ef556f7cf7 | ||
|
|
47ca6b162b | ||
|
|
f72a5cb6b5 | ||
|
|
cd0fd80b33 | ||
|
|
0fd7f643a7 | ||
|
|
6c7c1b67b8 | ||
|
|
4514a17eaf | ||
|
|
adbd2fc81f | ||
|
|
8b506b6a95 | ||
|
|
426c8565fd | ||
|
|
bd6d3ab7c0 | ||
|
|
d12d13d6ea | ||
|
|
cc08390abe | ||
|
|
b460e282b1 | ||
|
|
005d19f2a3 | ||
|
|
8817e17d2c | ||
|
|
0f23b7a460 | ||
|
|
21ae1dbac0 | ||
|
|
6cb0f5e810 | ||
|
|
2ac7d3dfe6 | ||
|
|
bfaa543ecd | ||
|
|
f893b1c632 | ||
|
|
cb5a28f043 | ||
|
|
bd9718c5af | ||
|
|
97a0dbebcb | ||
|
|
df324de9ec | ||
|
|
6fa690a49d | ||
|
|
2bb1aa7511 | ||
|
|
0bef0902c0 | ||
|
|
f10f60e27f | ||
|
|
91337b49ea | ||
|
|
f5e41f7e7e | ||
|
|
fe0e82f285 | ||
|
|
8996cf02af | ||
|
|
e37c77a1be | ||
|
|
11998b2dd6 | ||
|
|
8754123535 | ||
|
|
91d75424f4 | ||
|
|
c5c99abd16 | ||
|
|
6c88f7629e | ||
|
|
98c71739f8 | ||
|
|
25551641fc | ||
|
|
00b701f129 | ||
|
|
ab082aa311 | ||
|
|
dc32c84bb3 | ||
|
|
0bb4161397 | ||
|
|
4a049ca732 | ||
|
|
d5ac45d230 | ||
|
|
6b3e4bdc71 | ||
|
|
5c0d82f306 | ||
|
|
5a6176def5 | ||
|
|
13ebc87b29 | ||
|
|
1ca1bcf1d1 | ||
|
|
ea5a05d770 | ||
|
|
ed85ab1bfd | ||
|
|
c09e4a1aa0 | ||
|
|
ee36fa9d81 | ||
|
|
bfa5fc43cd | ||
|
|
5d6389249a | ||
|
|
637cb0d836 | ||
|
|
bfebe6a5d0 | ||
|
|
4cbbbd390f | ||
|
|
c120fa6a4d | ||
|
|
918ea7503a | ||
|
|
dbe07827bd | ||
|
|
98eb756951 | ||
|
|
86eeeb5b94 | ||
|
|
a3e6628e11 | ||
|
|
84bc4217de | ||
|
|
b88819b9bb | ||
|
|
c7a0e59919 | ||
|
|
9984d4be42 | ||
|
|
a1c91b9007 | ||
|
|
6a256d59f5 | ||
|
|
fca2f1e6de | ||
|
|
ec7994a561 | ||
|
|
2839c36ea2 | ||
|
|
9f22bf2fbf | ||
|
|
5f0df1b167 |
No files matched your search
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: 🐛 Bug report
|
||||
about: Report an issue or problem with the way LuckPerms is functioning.
|
||||
title: ''
|
||||
labels: 'type: issue'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Before going any further, please check to see if your problem is fixed by updating to a newer version. The latest development builds can be obtained from https://luckperms.net/download -->
|
||||
|
||||
### Description
|
||||
<!-- Please provide a short description of the issue in the space below. -->
|
||||
|
||||
|
||||
### Reproduction steps
|
||||
<!-- Please provide a clear list of steps we should follow in order to reproduce your issue in the space below. -->
|
||||
|
||||
1. Open the config.yml file and set example to true.
|
||||
2. Restart the server
|
||||
3. Run /lp user example permission set x from the console
|
||||
4. See error
|
||||
|
||||
|
||||
### Expected behaviour
|
||||
<!-- Please provide a description of what you expected to happen. -->
|
||||
|
||||
|
||||
### Environment details
|
||||
<!-- Please provide details of your server environment in the space below. -->
|
||||
|
||||
* Server type/version: `ExampleSpigot` running version `1.12.2` build `???`
|
||||
* LuckPerms version: `v???`
|
||||
|
||||
<!-- If you think it would be helpful, please also include a "pastebin" of any relevant config files or log output. You should use https://gist.github.com/ to upload, then include the link below. -->
|
||||
|
||||
|
||||
### Any other relevant details
|
||||
<!-- Please include any other relevant details in the space below. -->
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 💬 Ask a question
|
||||
url: https://discord.gg/luckperms
|
||||
about: We use Discord to provide support for the project. If you have a question, need help, or aren't sure if you've found a bug or not, please come and talk to us there!
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: 💡 Suggestion
|
||||
about: Suggest an idea for an improvement or change to LuckPerms.
|
||||
title: ''
|
||||
labels: 'type: suggestion'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### Description
|
||||
<!-- Please provide a short description of the suggestion in the space below. -->
|
||||
|
||||
|
||||
### Proposed behaviour
|
||||
<!-- Please provide a short explanation of how the feature should work / be changed, and how this will effect the project. -->
|
||||
|
||||
@@ -5,17 +5,21 @@
|
||||
[](https://search.maven.org/artifact/net.luckperms/api)
|
||||
[](https://discord.gg/luckperms)
|
||||
|
||||
LuckPerms is an advanced permissions implementation aiming to be a fast, reliable and flexible alternative to existing permission plugins. The project's main goals are centered around high performance and a wide feature set, filling the gaps of functionality and building upon existing features found in other plugins.
|
||||
LuckPerms is a permissions plugin for Minecraft servers. It allows server admins to control what features players can use by creating groups and assigning permissions.
|
||||
|
||||
LuckPerms also includes an extensive API for developers, and support for a variety of Minecraft server software & data storage options.
|
||||
It is:
|
||||
|
||||
Still not convinced? More information can be found on the wiki, under: [Why LuckPerms?](https://github.com/lucko/LuckPerms/wiki/Why-LuckPerms)
|
||||
* **fast** - written with performance and scalability in mind.
|
||||
* **reliable** - trusted by thousands of server admins, and the largest of server networks.
|
||||
* **easy to use** - setup permissions using commands, directly in config files, or using the web editor.
|
||||
* **flexible** - supports a variety of data storage options, and works on lots of different server types.
|
||||
* **extensive** - a plethora of customization options and settings which can be changed to suit your server.
|
||||
* **free** - available for download and usage at no cost, and permissively licensed so it can remain free forever.
|
||||
|
||||
For more information, see the wiki article on [Why LuckPerms?](https://github.com/lucko/LuckPerms/wiki/Why-LuckPerms)
|
||||
|
||||
## Useful Links
|
||||
|
||||
The latest **downloads** can be found on the [project's homepage](https://luckperms.net/).
|
||||
|
||||
More information about the project & how to use the plugin can be found in the [wiki](https://github.com/lucko/LuckPerms/wiki).
|
||||
The latest downloads & other useful links can be found on the project homepage at [luckperms.net](https://luckperms.net/).
|
||||
|
||||
## Building
|
||||
LuckPerms uses Gradle to handle dependencies & building.
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
group = 'net.luckperms'
|
||||
project.version = '5.1'
|
||||
project.version = '5.2'
|
||||
|
||||
dependencies {
|
||||
compileOnly 'org.checkerframework:checker-qual:2.5.5'
|
||||
@@ -56,7 +56,7 @@ if (project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePass
|
||||
pom {
|
||||
project {
|
||||
name 'LuckPerms API'
|
||||
description 'An advanced permissions plugin for Bukkit/Spigot, BungeeCord, Sponge, Nukkit and Velocity.'
|
||||
description 'A permissions plugin for Minecraft servers.'
|
||||
url 'https://luckperms.net'
|
||||
|
||||
licenses {
|
||||
|
||||
@@ -34,6 +34,8 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
* <p>Context keys and values may not be null or empty. A key/value will be
|
||||
* deemed empty if it's length is zero, or if it consists of only space
|
||||
* characters.</p>
|
||||
*
|
||||
* @see ContextSet
|
||||
*/
|
||||
public interface Context {
|
||||
|
||||
|
||||
@@ -138,6 +138,18 @@ public interface ContextManager {
|
||||
*/
|
||||
void unregisterCalculator(@NonNull ContextCalculator<?> calculator);
|
||||
|
||||
/**
|
||||
* Signal to the {@link ContextManager} that a {@code subject}s
|
||||
* current contexts have changed.
|
||||
*
|
||||
* <p>It is not strictly necessary to make a call to this method every time a context
|
||||
* changes.</p>
|
||||
*
|
||||
* @param subject the subject
|
||||
* @since 5.2
|
||||
*/
|
||||
void signalContextUpdate(@NonNull Object subject);
|
||||
|
||||
/**
|
||||
* Gets the {@link ContextSetFactory}, responsible for creating
|
||||
* {@link ContextSet} instances.
|
||||
@@ -150,7 +162,11 @@ public interface ContextManager {
|
||||
* Invalidates the lookup cache for a given subject
|
||||
*
|
||||
* @param subject the subject
|
||||
* @deprecated use {@link #signalContextUpdate(Object)}
|
||||
*/
|
||||
void invalidateCache(@NonNull Object subject);
|
||||
@Deprecated
|
||||
default void invalidateCache(@NonNull Object subject) {
|
||||
signalContextUpdate(subject);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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 net.luckperms.api.context;
|
||||
|
||||
import net.luckperms.api.query.OptionKey;
|
||||
|
||||
/**
|
||||
* Mode for determining whether a {@link ContextSet} satisfies another.
|
||||
*
|
||||
* @since 5.2
|
||||
* @see ContextSet#isSatisfiedBy(ContextSet, ContextSatisfyMode)
|
||||
*/
|
||||
public enum ContextSatisfyMode {
|
||||
|
||||
/**
|
||||
* Mode where a {@link ContextSet} A will be satisfied by another set B, if at least one of the
|
||||
* key-value entries per key in A are also in B.
|
||||
*
|
||||
* <p>For example, given <code>A = {server=survival, world=overworld, world=nether}</code>,
|
||||
* another set {@code X} will satisfy {@code A} if {@code X} contains
|
||||
* <code>server=survival AND (world=overworld OR world=nether)</code>.</p>
|
||||
*/
|
||||
AT_LEAST_ONE_VALUE_PER_KEY,
|
||||
|
||||
/**
|
||||
* Mode where a {@link ContextSet} A will be satisfied by another set B, if all key-value
|
||||
* entries in A are also in B.
|
||||
*
|
||||
* <p>For example, given <code>A = {server=survival, world=overworld, world=nether}</code>,
|
||||
* another set {@code X} will satisfy {@code A} if {@code X} contains
|
||||
* <code>server=survival AND world=overworld AND world=nether</code>.</p>
|
||||
*/
|
||||
ALL_VALUES_PER_KEY;
|
||||
|
||||
/**
|
||||
* The {@link OptionKey} for {@link ContextSatisfyMode}.
|
||||
*/
|
||||
public static final OptionKey<ContextSatisfyMode> KEY = OptionKey.of("contextsatisfymode", ContextSatisfyMode.class);
|
||||
|
||||
}
|
||||
@@ -40,19 +40,33 @@ import java.util.Set;
|
||||
* something will apply.</p>
|
||||
*
|
||||
* <p>A single "context" consists of a key and a value, both strings. The key
|
||||
* represents the type of context, and the value represents the setting of the
|
||||
* context key.</p>
|
||||
* describes the type of the context, and the value represents what the key is
|
||||
* set to.</p>
|
||||
*
|
||||
* <p>For example, a context with {@code key=world} and {@code value=world_nether}
|
||||
* describes that a subject is in the "world_nether" world.</p>
|
||||
*
|
||||
* <p>Contexts are exposed to end users and manipulated when managing permissions.
|
||||
* For this reason, context keys should strike a balance between being descriptive
|
||||
* and succinct.</p>
|
||||
*
|
||||
* <p>Context keys and values are case-insensitive, and will be automatically
|
||||
* converted to {@link String#toLowerCase() lowercase} when added to a
|
||||
* context set. Keys and values cannot be null or empty (len=0 or consisting of
|
||||
* only whitespace).</p>
|
||||
*
|
||||
* <p>If a context key is formed of more than one word, the parts should be
|
||||
* separated by the '{@code -}' character. (e.g. "{@code server-type}")</p>
|
||||
*
|
||||
* <p>If a context key is likely to conflict with another plugin, it should be
|
||||
* appropriately namespaced using the name of the plugin providing the context.
|
||||
* The namespace should be at the start of the context key, and separated using
|
||||
* the '{@code :}' character. (e.g. "{@code worldguard:region}" for WorldGuard
|
||||
* regions)</p>
|
||||
*
|
||||
* <p>Contexts can be combined with each other to form so called
|
||||
* "context sets" - simply a collection of context pairs.</p>
|
||||
*
|
||||
* <p>Context keys and values are case-insensitive, and will be converted to
|
||||
* {@link String#toLowerCase() lowercase} by all implementations.</p>
|
||||
*
|
||||
* <p>Context keys and values may not be null or empty. A key/value will be
|
||||
* deemed empty if it's length is zero, or if it consists of only space
|
||||
* characters.</p>
|
||||
*
|
||||
* <p>Two default ContextSet implementations are provided.
|
||||
* {@link MutableContextSet} allows the addition and removal of context keys
|
||||
* after construction, and {@link ImmutableContextSet} does not.</p>
|
||||
@@ -197,17 +211,47 @@ public interface ContextSet extends Iterable<Context> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if this {@link ContextSet} is fully "satisfied" by another set.
|
||||
* Returns if the {@link ContextSet} contains any of the given context pairings.
|
||||
*
|
||||
* <p>For a context set to "satisfy" another, it must itself contain all of
|
||||
* the context pairings in the other set.</p>
|
||||
*
|
||||
* <p>Mathematically, this method returns true if this set is a <b>subset</b> of the other.</p>
|
||||
*
|
||||
* @param other the other set to check
|
||||
* @return true if all entries in this set are also in the other set
|
||||
* @param key the key to look for
|
||||
* @param values the values to look for
|
||||
* @return true if the set contains any of the pairs
|
||||
* @since 5.2
|
||||
*/
|
||||
boolean isSatisfiedBy(@NonNull ContextSet other);
|
||||
default boolean containsAny(@NonNull String key, @NonNull Iterable<String> values) {
|
||||
Objects.requireNonNull(key, "key");
|
||||
Objects.requireNonNull(values, "values");
|
||||
|
||||
for (String value : values) {
|
||||
if (contains(key, value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if this {@link ContextSet} is "satisfied" by another set.
|
||||
*
|
||||
* <p>{@link ContextSatisfyMode#AT_LEAST_ONE_VALUE_PER_KEY} is the mode used by this method.</p>
|
||||
*
|
||||
* @param other the other set
|
||||
* @return true if this context set is satisfied by the other
|
||||
*/
|
||||
default boolean isSatisfiedBy(@NonNull ContextSet other) {
|
||||
return isSatisfiedBy(other, ContextSatisfyMode.AT_LEAST_ONE_VALUE_PER_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if this {@link ContextSet} is "satisfied" by another set, according to the given
|
||||
* {@code mode}.
|
||||
*
|
||||
* @param other the other set
|
||||
* @param mode the mode to use
|
||||
* @return true if this context set is satisfied by the other
|
||||
* @since 5.2
|
||||
*/
|
||||
boolean isSatisfiedBy(@NonNull ContextSet other, @NonNull ContextSatisfyMode mode);
|
||||
|
||||
/**
|
||||
* Returns if the {@link ContextSet} is empty.
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 net.luckperms.api.event.context;
|
||||
|
||||
import net.luckperms.api.context.ContextManager;
|
||||
import net.luckperms.api.event.LuckPermsEvent;
|
||||
import net.luckperms.api.event.util.Param;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Called when a subject's current/active contexts are updated.
|
||||
*
|
||||
* <p>There are no guarantees that this event will be called for every update. It is merely to be
|
||||
* used as a "hint" for plugins which depend on the current active contexts for a player.</p>
|
||||
*
|
||||
* <p>It will always be fired following a call to
|
||||
* {@link ContextManager#signalContextUpdate(Object)}.</p>
|
||||
*
|
||||
* <p>The {@link #getSubject() subject} is always an instance of the platform's subject type. See
|
||||
* {@link ContextManager} for details.</p>
|
||||
*
|
||||
* <p>Unlike most other LuckPerms events, this event is not fired asynchronously. Care should be
|
||||
* taken to ensure listeners are lightweight. Additionally, listeners should ensure they do not
|
||||
* cause further updates to player context, thus possibly causing a stack overflow.</p>
|
||||
*
|
||||
* @since 5.2
|
||||
*/
|
||||
public interface ContextUpdateEvent extends LuckPermsEvent {
|
||||
|
||||
/**
|
||||
* Gets the subject whose contexts were updated.
|
||||
*
|
||||
* @return the subject
|
||||
*/
|
||||
@Param(0)
|
||||
@NonNull Object getSubject();
|
||||
|
||||
/**
|
||||
* Gets the subject whose contexts were updated, casted to a given type.
|
||||
*
|
||||
* @param subjectClass the type to cast to
|
||||
* @param <T> the subject type
|
||||
* @return the casted subject
|
||||
*/
|
||||
default <T> @NonNull Optional<T> getSubject(@NonNull Class<T> subjectClass) {
|
||||
Object subject = getSubject();
|
||||
return subjectClass.isInstance(subject) ? Optional.of(subjectClass.cast(subject)) : Optional.empty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import net.luckperms.api.model.group.Group;
|
||||
import net.luckperms.api.model.user.User;
|
||||
import net.luckperms.api.node.Node;
|
||||
import net.luckperms.api.node.NodeType;
|
||||
import net.luckperms.api.query.Flag;
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
@@ -277,10 +278,14 @@ public interface PermissionHolder {
|
||||
@NonNull SortedSet<Node> resolveDistinctInheritedNodes(@NonNull QueryOptions queryOptions);
|
||||
|
||||
/**
|
||||
* Gets a collection of the {@link Group}s this holder inherits nodes from, both directly
|
||||
* and indirectly (through directly inherited groups).
|
||||
* Gets a collection of the {@link Group}s this holder inherits nodes from.
|
||||
*
|
||||
* <p>It effectively resolves the whole "inheritance tree".</p>
|
||||
* <p>If {@link Flag#RESOLVE_INHERITANCE} is set, this will include holders inherited from both
|
||||
* directly and indirectly (through directly inherited groups). It will effectively resolve the
|
||||
* whole "inheritance tree".</p>
|
||||
*
|
||||
* <p>If {@link Flag#RESOLVE_INHERITANCE} is not set, then the traversal will only go one
|
||||
* level up the inheritance tree, and return only directly inherited groups.</p>
|
||||
*
|
||||
* <p>The collection will be ordered according to the platforms inheritance rules. The groups
|
||||
* which are inherited from first will appear earlier in the list.</p>
|
||||
|
||||
@@ -147,6 +147,17 @@ public interface UserManager {
|
||||
*/
|
||||
@NonNull CompletableFuture<PlayerSaveResult> savePlayerData(@NonNull UUID uniqueId, @NonNull String username);
|
||||
|
||||
/**
|
||||
* Deletes any data about a given player from the uuid caching system.
|
||||
*
|
||||
* <p>Note that this method does not affect any saved user/permissions data.</p>
|
||||
*
|
||||
* @param uniqueId the users mojang unique id
|
||||
* @return a future encapsulating the result of the operation
|
||||
* @since 5.2
|
||||
*/
|
||||
@NonNull CompletableFuture<Void> deletePlayerData(@NonNull UUID uniqueId);
|
||||
|
||||
/**
|
||||
* Gets a set all "unique" user UUIDs.
|
||||
*
|
||||
|
||||
@@ -100,6 +100,7 @@ public interface PlayerAdapter<T> {
|
||||
default @NonNull CachedPermissionData getPermissionData(@NonNull T player) {
|
||||
return getUser(player).getCachedData().getPermissionData(getQueryOptions(player));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current {@link CachedMetaData} for the {@code player},
|
||||
* using their {@link #getQueryOptions(Object) active query options}.
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
package net.luckperms.api.query;
|
||||
|
||||
import net.luckperms.api.LuckPermsProvider;
|
||||
import net.luckperms.api.context.ContextSatisfyMode;
|
||||
import net.luckperms.api.context.ContextSet;
|
||||
import net.luckperms.api.context.ImmutableContextSet;
|
||||
|
||||
@@ -166,10 +167,29 @@ public interface QueryOptions {
|
||||
* Gets whether this {@link QueryOptions} satisfies the given required
|
||||
* {@link ContextSet context}.
|
||||
*
|
||||
* <p>{@link ContextSatisfyMode#AT_LEAST_ONE_VALUE_PER_KEY} is used if this {@link QueryOptions}
|
||||
* instance doesn't have the {@link ContextSatisfyMode#KEY option key} set.</p>
|
||||
*
|
||||
* @param contextSet the contexts
|
||||
* @return the result
|
||||
*/
|
||||
boolean satisfies(@NonNull ContextSet contextSet);
|
||||
default boolean satisfies(@NonNull ContextSet contextSet) {
|
||||
return satisfies(contextSet, ContextSatisfyMode.AT_LEAST_ONE_VALUE_PER_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this {@link QueryOptions} satisfies the given required
|
||||
* {@link ContextSet context}.
|
||||
*
|
||||
* <p>The {@code defaultContextSatisfyMode} parameter is used if this {@link QueryOptions}
|
||||
* instance doesn't have the {@link ContextSatisfyMode#KEY option key} set.</p>
|
||||
*
|
||||
* @param contextSet the contexts
|
||||
* @param defaultContextSatisfyMode the default context satisfy mode to use
|
||||
* @return the result
|
||||
* @since 5.2
|
||||
*/
|
||||
boolean satisfies(@NonNull ContextSet contextSet, @NonNull ContextSatisfyMode defaultContextSatisfyMode);
|
||||
|
||||
/**
|
||||
* Converts this {@link QueryOptions} to a mutable builder.
|
||||
|
||||
@@ -38,7 +38,7 @@ public interface QueryOptionsRegistry {
|
||||
* Gets the default {@link QueryMode#CONTEXTUAL contextual}
|
||||
* query options.
|
||||
*
|
||||
* <p>Prefer using the {@link QueryOptions#defaultContextualOptions()} accessor.</p>>
|
||||
* <p>Prefer using the {@link QueryOptions#defaultContextualOptions()} accessor.</p>
|
||||
*
|
||||
* @return the default contextual query options
|
||||
* @see QueryOptions#defaultContextualOptions()
|
||||
@@ -49,7 +49,7 @@ public interface QueryOptionsRegistry {
|
||||
* Gets the default {@link QueryMode#NON_CONTEXTUAL non contextual}
|
||||
* query options.
|
||||
*
|
||||
* <p>Prefer using the {@link QueryOptions#nonContextual()} accessor.</p>>
|
||||
* <p>Prefer using the {@link QueryOptions#nonContextual()} accessor.</p>
|
||||
*
|
||||
* @return the default non contextual query options
|
||||
* @see QueryOptions#nonContextual()
|
||||
|
||||
@@ -48,6 +48,8 @@ import java.util.function.Consumer;
|
||||
* it is possible that more {@link DataType}s may be added in the future.
|
||||
* Ideally the {@link Comparator} implementations should be able to handle these
|
||||
* smoothly.</p>
|
||||
*
|
||||
* @see DataQueryOrderFunction
|
||||
*/
|
||||
public enum DataQueryOrder implements Comparator<DataType> {
|
||||
|
||||
@@ -60,10 +62,7 @@ public enum DataQueryOrder implements Comparator<DataType> {
|
||||
if (o1 == o2) {
|
||||
return 0;
|
||||
}
|
||||
return Boolean.compare(
|
||||
o1 == DataType.TRANSIENT,
|
||||
o2 == DataType.TRANSIENT
|
||||
);
|
||||
return o1 == DataType.TRANSIENT ? 1 : -1;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -76,10 +75,7 @@ public enum DataQueryOrder implements Comparator<DataType> {
|
||||
if (o1 == o2) {
|
||||
return 0;
|
||||
}
|
||||
return -Boolean.compare(
|
||||
o1 == DataType.TRANSIENT,
|
||||
o2 == DataType.TRANSIENT
|
||||
);
|
||||
return o1 == DataType.TRANSIENT ? -1 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -44,6 +44,18 @@ public interface DataQueryOrderFunction {
|
||||
*/
|
||||
OptionKey<DataQueryOrderFunction> KEY = OptionKey.of("dataqueryorderfunction", DataQueryOrderFunction.class);
|
||||
|
||||
/**
|
||||
* Creates a {@link DataQueryOrderFunction} that always returns the given
|
||||
* {@code comparator}.
|
||||
*
|
||||
* @param comparator the comparator
|
||||
* @return the data query order function
|
||||
* @since 5.2
|
||||
*/
|
||||
static DataQueryOrderFunction always(Comparator<DataType> comparator) {
|
||||
return id -> comparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link DataQueryOrder} comparator for the given
|
||||
* {@link PermissionHolder.Identifier holder identifier}.
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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 net.luckperms.api.query.dataorder;
|
||||
|
||||
import net.luckperms.api.model.data.DataType;
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Represents which different {@link DataType}s are used for a query.
|
||||
*
|
||||
* <p>The {@link DataTypeFilter} enum simply represents some default
|
||||
* implementations of the {@link Predicate} required by {@link QueryOptions}
|
||||
* and the {@link #values(Predicate)} method.</p>
|
||||
*
|
||||
* <p>Users are free to implement their own predicate. However, be aware that
|
||||
* it is possible that more {@link DataType}s may be added in the future.
|
||||
* Ideally the {@link Predicate} implementations should be able to handle these
|
||||
* smoothly.</p>
|
||||
*
|
||||
* @see DataTypeFilterFunction
|
||||
* @since 5.2
|
||||
*/
|
||||
public enum DataTypeFilter implements Predicate<DataType> {
|
||||
|
||||
/**
|
||||
* A data type filter indicating that all {@link DataType}s should be used.
|
||||
*/
|
||||
ALL {
|
||||
@Override
|
||||
public boolean test(DataType dataType) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* A data type filter indicating that no {@link DataType}s should be used.
|
||||
*/
|
||||
NONE {
|
||||
@Override
|
||||
public boolean test(DataType dataType) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* A data type filter indicating that only {@link DataType#NORMAL} should be used.
|
||||
*/
|
||||
NORMAL_ONLY {
|
||||
@Override
|
||||
public boolean test(DataType dataType) {
|
||||
return dataType == DataType.NORMAL;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* A data type filter indicating that only {@link DataType#TRANSIENT} should be used.
|
||||
*/
|
||||
TRANSIENT_ONLY {
|
||||
@Override
|
||||
public boolean test(DataType dataType) {
|
||||
return dataType == DataType.TRANSIENT;
|
||||
}
|
||||
};
|
||||
|
||||
private static final List<DataType> ALL_LIST = Collections.unmodifiableList(Arrays.asList(DataType.NORMAL, DataType.TRANSIENT));
|
||||
private static final List<DataType> NORMAL_ONLY_LIST = Collections.unmodifiableList(Collections.singletonList(DataType.NORMAL));
|
||||
private static final List<DataType> TRANSIENT_ONLY_LIST = Collections.unmodifiableList(Collections.singletonList(DataType.TRANSIENT));
|
||||
|
||||
/**
|
||||
* Gets a {@link List} of all {@link DataType}s, filtered by the {@code predicate}.
|
||||
*
|
||||
* @param predicate the predicate to filter with
|
||||
* @return the list of data types
|
||||
*/
|
||||
public static @NonNull List<DataType> values(@NonNull Predicate<? super DataType> predicate) {
|
||||
boolean normal = predicate.test(DataType.NORMAL);
|
||||
boolean trans = predicate.test(DataType.TRANSIENT);
|
||||
|
||||
if (normal && trans) {
|
||||
return ALL_LIST;
|
||||
} else if (normal) {
|
||||
return NORMAL_ONLY_LIST;
|
||||
} else if (trans) {
|
||||
return TRANSIENT_ONLY_LIST;
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 net.luckperms.api.query.dataorder;
|
||||
|
||||
import net.luckperms.api.model.PermissionHolder;
|
||||
import net.luckperms.api.model.data.DataType;
|
||||
import net.luckperms.api.query.OptionKey;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* A function that generates a {@link DataTypeFilter} predicate for
|
||||
* {@link PermissionHolder}s as required during inheritance.
|
||||
*
|
||||
* @since 5.2
|
||||
*/
|
||||
public interface DataTypeFilterFunction {
|
||||
|
||||
/**
|
||||
* The {@link OptionKey} for {@link DataTypeFilterFunction}.
|
||||
*/
|
||||
OptionKey<DataTypeFilterFunction> KEY = OptionKey.of("datatypefilterfunction", DataTypeFilterFunction.class);
|
||||
|
||||
/**
|
||||
* Creates a {@link DataTypeFilterFunction} that always returns the given
|
||||
* {@code predicate}.
|
||||
*
|
||||
* @param predicate the predicate
|
||||
* @return the data type filter function
|
||||
*/
|
||||
static DataTypeFilterFunction always(Predicate<DataType> predicate) {
|
||||
return id -> predicate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link DataTypeFilter} predicate for the given
|
||||
* {@link PermissionHolder.Identifier holder identifier}.
|
||||
*
|
||||
* @param holderIdentifier the holder identifier
|
||||
* @return the predicate to use
|
||||
*/
|
||||
@NonNull Predicate<DataType> getTypeFilter(PermissionHolder.@NonNull Identifier holderIdentifier);
|
||||
|
||||
}
|
||||
+2
-2
@@ -16,7 +16,7 @@ subprojects {
|
||||
apply plugin: 'net.minecrell.licenser'
|
||||
|
||||
group = 'me.lucko.luckperms'
|
||||
version = '5.1-SNAPSHOT'
|
||||
version = '5.2-SNAPSHOT'
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
@@ -45,7 +45,7 @@ subprojects {
|
||||
}
|
||||
|
||||
project.ext.majorVersion = '5'
|
||||
project.ext.minorVersion = '1'
|
||||
project.ext.minorVersion = '2'
|
||||
project.ext.patchVersion = determinePatchVersion()
|
||||
project.ext.apiVersion = project.ext.majorVersion + '.' + project.ext.minorVersion
|
||||
project.ext.fullVersion = project.ext.apiVersion + '.' + project.ext.patchVersion
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@ repositories {
|
||||
dependencies {
|
||||
compile project(':common')
|
||||
|
||||
compileOnly 'com.destroystokyo.paper:paper-api:1.15.1-R0.1-SNAPSHOT'
|
||||
compileOnly 'net.kyori:text-adapter-bukkit:3.0.3'
|
||||
compileOnly 'com.destroystokyo.paper:paper-api:1.15.2-R0.1-SNAPSHOT'
|
||||
compileOnly 'net.kyori:text-adapter-bukkit:3.0.5'
|
||||
compileOnly 'me.lucko:commodore:1.7'
|
||||
compileOnly('net.milkbowl.vault:VaultAPI:1.6') {
|
||||
exclude(module: 'bukkit')
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
package me.lucko.luckperms.bukkit;
|
||||
|
||||
import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
|
||||
import me.lucko.luckperms.common.config.generic.adapter.ConfigurationAdapter;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
package me.lucko.luckperms.bukkit;
|
||||
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import me.lucko.luckperms.common.sender.SenderFactory;
|
||||
import me.lucko.luckperms.common.util.TextUtils;
|
||||
@@ -41,9 +40,8 @@ import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class BukkitSenderFactory extends SenderFactory<CommandSender> {
|
||||
|
||||
public BukkitSenderFactory(LuckPermsPlugin plugin) {
|
||||
public class BukkitSenderFactory extends SenderFactory<LPBukkitPlugin, CommandSender> {
|
||||
public BukkitSenderFactory(LPBukkitPlugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@@ -102,6 +100,11 @@ public class BukkitSenderFactory extends SenderFactory<CommandSender> {
|
||||
return sender.hasPermission(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performCommand(CommandSender sender, String command) {
|
||||
getPlugin().getBootstrap().getServer().dispatchCommand(sender, command);
|
||||
}
|
||||
|
||||
private static final class SyncMessengerAgent implements Runnable {
|
||||
private final CommandSender sender;
|
||||
private final String message;
|
||||
|
||||
@@ -39,14 +39,18 @@ import org.bukkit.Server;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Bootstrap plugin for LuckPerms running on Bukkit.
|
||||
@@ -253,13 +257,23 @@ public class LPBukkitBootstrap extends JavaPlugin implements LuckPermsBootstrap
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> getPlayerList() {
|
||||
return getServer().getOnlinePlayers().stream().map(Player::getName);
|
||||
public Collection<String> getPlayerList() {
|
||||
Collection<? extends Player> players = getServer().getOnlinePlayers();
|
||||
List<String> list = new ArrayList<>(players.size());
|
||||
for (Player player : players) {
|
||||
list.add(player.getName());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<UUID> getOnlinePlayers() {
|
||||
return getServer().getOnlinePlayers().stream().map(Player::getUniqueId);
|
||||
public Collection<UUID> getOnlinePlayers() {
|
||||
Collection<? extends Player> players = getServer().getOnlinePlayers();
|
||||
List<UUID> list = new ArrayList<>(players.size());
|
||||
for (Player player : players) {
|
||||
list.add(player.getUniqueId());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -268,6 +282,19 @@ public class LPBukkitBootstrap extends JavaPlugin implements LuckPermsBootstrap
|
||||
return player != null && player.isOnline();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String identifyClassLoader(ClassLoader classLoader) throws ReflectiveOperationException {
|
||||
Class<?> pluginClassLoaderClass = Class.forName("org.bukkit.plugin.java.PluginClassLoader");
|
||||
if (pluginClassLoaderClass.isInstance(classLoader)) {
|
||||
Method getPluginMethod = pluginClassLoaderClass.getDeclaredMethod("getPlugin");
|
||||
getPluginMethod.setAccessible(true);
|
||||
|
||||
JavaPlugin plugin = (JavaPlugin) getPluginMethod.invoke(classLoader);
|
||||
return plugin.getName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean checkIncompatibleVersion() {
|
||||
try {
|
||||
Class.forName("com.google.gson.JsonElement");
|
||||
|
||||
@@ -38,16 +38,18 @@ import me.lucko.luckperms.bukkit.inject.server.InjectorSubscriptionMap;
|
||||
import me.lucko.luckperms.bukkit.inject.server.LuckPermsDefaultsMap;
|
||||
import me.lucko.luckperms.bukkit.inject.server.LuckPermsPermissionMap;
|
||||
import me.lucko.luckperms.bukkit.inject.server.LuckPermsSubscriptionMap;
|
||||
import me.lucko.luckperms.bukkit.listeners.BukkitAutoOpListener;
|
||||
import me.lucko.luckperms.bukkit.listeners.BukkitCommandListUpdater;
|
||||
import me.lucko.luckperms.bukkit.listeners.BukkitConnectionListener;
|
||||
import me.lucko.luckperms.bukkit.listeners.BukkitPlatformListener;
|
||||
import me.lucko.luckperms.bukkit.messaging.BukkitMessagingFactory;
|
||||
import me.lucko.luckperms.bukkit.util.PluginManagerUtil;
|
||||
import me.lucko.luckperms.bukkit.vault.VaultHookManager;
|
||||
import me.lucko.luckperms.common.api.LuckPermsApiProvider;
|
||||
import me.lucko.luckperms.common.api.implementation.ApiUser;
|
||||
import me.lucko.luckperms.common.calculator.CalculatorFactory;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
|
||||
import me.lucko.luckperms.common.config.generic.adapter.ConfigurationAdapter;
|
||||
import me.lucko.luckperms.common.dependencies.Dependency;
|
||||
import me.lucko.luckperms.common.event.AbstractEventBus;
|
||||
import me.lucko.luckperms.common.messaging.MessagingFactory;
|
||||
@@ -62,7 +64,6 @@ import me.lucko.luckperms.common.tasks.CacheHousekeepingTask;
|
||||
import me.lucko.luckperms.common.tasks.ExpireTemporaryTask;
|
||||
|
||||
import net.luckperms.api.LuckPerms;
|
||||
import net.luckperms.api.event.user.UserDataRecalculateEvent;
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
|
||||
import org.bukkit.OfflinePlayer;
|
||||
@@ -74,7 +75,8 @@ import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -156,7 +158,7 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin {
|
||||
this.commandManager.register();
|
||||
|
||||
// setup brigadier
|
||||
if (isBrigadierSupported()) {
|
||||
if (isBrigadierSupported() && getConfiguration().get(ConfigKeys.REGISTER_COMMAND_LIST_DATA)) {
|
||||
try {
|
||||
LuckPermsBrigadier.register(this, command);
|
||||
} catch (Exception e) {
|
||||
@@ -182,7 +184,10 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin {
|
||||
@Override
|
||||
protected void setupContextManager() {
|
||||
this.contextManager = new BukkitContextManager(this);
|
||||
this.contextManager.registerCalculator(new WorldCalculator(this));
|
||||
|
||||
WorldCalculator worldCalculator = new WorldCalculator(this);
|
||||
this.bootstrap.getServer().getPluginManager().registerEvents(worldCalculator, this.bootstrap);
|
||||
this.contextManager.registerCalculator(worldCalculator);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -203,6 +208,20 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin {
|
||||
this.bootstrap.getServer().getScheduler().runTaskLaterAsynchronously(this.bootstrap, injector, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is an unfortunate solution to a problem which shouldn't even exist. As of Spigot 1.15,
|
||||
* the way LP establishes it's load order relative to Vault triggers a dependency warning.
|
||||
* This is a workaround to prevent that from showing, since at the moment, there is nothing I
|
||||
* can reasonably do to improve this handling in LP without breaking plugins which use/obtain
|
||||
* Vault in their onEnable without depending on us.
|
||||
*
|
||||
* Noteworthy discussion here:
|
||||
* - https://github.com/lucko/LuckPerms/issues/1959
|
||||
* - https://hub.spigotmc.org/jira/browse/SPIGOT-5546
|
||||
* - https://github.com/PaperMC/Paper/pull/3509
|
||||
*/
|
||||
PluginManagerUtil.injectDependency(this.bootstrap.getServer().getPluginManager(), this.bootstrap.getName(), "Vault");
|
||||
|
||||
// Provide vault support
|
||||
tryVaultHook(false);
|
||||
}
|
||||
@@ -266,11 +285,12 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin {
|
||||
|
||||
// register autoop listener
|
||||
if (getConfiguration().get(ConfigKeys.AUTO_OP)) {
|
||||
getApiProvider().getEventBus().subscribe(UserDataRecalculateEvent.class, event -> {
|
||||
User user = ApiUser.cast(event.getUser());
|
||||
Optional<Player> player = getBootstrap().getPlayer(user.getUniqueId());
|
||||
player.ifPresent(p -> refreshAutoOp(p, false));
|
||||
});
|
||||
getApiProvider().getEventBus().subscribe(new BukkitAutoOpListener(this));
|
||||
}
|
||||
|
||||
// register bukkit command list updater
|
||||
if (getConfiguration().get(ConfigKeys.UPDATE_CLIENT_COMMAND_LIST) && BukkitCommandListUpdater.isSupported()) {
|
||||
getApiProvider().getEventBus().subscribe(new BukkitCommandListUpdater(this));
|
||||
}
|
||||
|
||||
// Load any online users (in the case of a reload)
|
||||
@@ -328,32 +348,6 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshAutoOp(Player player, boolean callerIsSync) {
|
||||
if (!getConfiguration().get(ConfigKeys.AUTO_OP)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!callerIsSync && this.bootstrap.isServerStopping()) {
|
||||
return;
|
||||
}
|
||||
|
||||
User user = getUserManager().getIfLoaded(player.getUniqueId());
|
||||
boolean value;
|
||||
|
||||
if (user != null) {
|
||||
Map<String, Boolean> permData = user.getCachedData().getPermissionData(this.contextManager.getQueryOptions(player)).getPermissionMap();
|
||||
value = permData.getOrDefault("luckperms.autoop", false);
|
||||
} else {
|
||||
value = false;
|
||||
}
|
||||
|
||||
if (callerIsSync) {
|
||||
player.setOp(value);
|
||||
} else {
|
||||
this.bootstrap.getScheduler().executeSync(() -> player.setOp(value));
|
||||
}
|
||||
}
|
||||
|
||||
private File resolveConfig() {
|
||||
File configFile = new File(this.bootstrap.getDataFolder(), "config.yml");
|
||||
if (!configFile.exists()) {
|
||||
@@ -387,9 +381,10 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin {
|
||||
|
||||
@Override
|
||||
public Stream<Sender> getOnlineSenders() {
|
||||
List<Player> players = new ArrayList<>(this.bootstrap.getServer().getOnlinePlayers());
|
||||
return Stream.concat(
|
||||
Stream.of(getConsoleSender()),
|
||||
this.bootstrap.getServer().getOnlinePlayers().stream().map(p -> getSenderFactory().wrap(p))
|
||||
players.stream().map(p -> getSenderFactory().wrap(p))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -88,11 +88,7 @@ public class BukkitContextManager extends ContextManager<Player, Player> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCache(Player subject) {
|
||||
if (subject == null) {
|
||||
throw new NullPointerException("subject");
|
||||
}
|
||||
|
||||
protected void invalidateCache(Player subject) {
|
||||
QueryOptionsCache<Player> cache = this.onlineSubjectCaches.getIfPresent(subject);
|
||||
if (cache != null) {
|
||||
cache.invalidate();
|
||||
|
||||
@@ -38,13 +38,17 @@ import net.luckperms.api.context.ImmutableContextSet;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class WorldCalculator implements ContextCalculator<Player> {
|
||||
public class WorldCalculator implements ContextCalculator<Player>, Listener {
|
||||
private final LPBukkitPlugin plugin;
|
||||
|
||||
public WorldCalculator(LPBukkitPlugin plugin) {
|
||||
@@ -75,4 +79,9 @@ public class WorldCalculator implements ContextCalculator<Player> {
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onWorldChange(PlayerChangedWorldEvent e) {
|
||||
this.plugin.getContextManager().signalContextUpdate(e.getPlayer());
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* 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.bukkit.inject.dummy;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginDescriptionFile;
|
||||
import org.bukkit.plugin.PluginLoader;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Dummy plugin instance
|
||||
*/
|
||||
public class DummyPlugin implements Plugin {
|
||||
public static final DummyPlugin INSTANCE = new DummyPlugin();
|
||||
|
||||
private DummyPlugin() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public @NonNull File getDataFolder() { return null; }
|
||||
@Override public @NonNull PluginDescriptionFile getDescription() { return null; }
|
||||
@Override public @NonNull FileConfiguration getConfig() { return null; }
|
||||
@Override public InputStream getResource(@NonNull String s) { return null; }
|
||||
@Override public void saveConfig() {}
|
||||
@Override public void saveDefaultConfig() {}
|
||||
@Override public void saveResource(@NonNull String s, boolean b) {}
|
||||
@Override public void reloadConfig() {}
|
||||
@Override public @NonNull PluginLoader getPluginLoader() { return null; }
|
||||
@Override public @NonNull Server getServer() { return null; }
|
||||
@Override public void onDisable() {}
|
||||
@Override public void onLoad() {}
|
||||
@Override public void onEnable() {}
|
||||
@Override public boolean isNaggable() { return false; }
|
||||
@Override public void setNaggable(boolean b) {}
|
||||
@Override public ChunkGenerator getDefaultWorldGenerator(@NonNull String s, String s1) { return null; }
|
||||
@Override public @NonNull Logger getLogger() { return null; }
|
||||
@Override public @NonNull String getName() { return null; }
|
||||
@Override public boolean onCommand(@NonNull CommandSender commandSender, @NonNull Command command, @NonNull String s, @NonNull String[] strings) { return false; }
|
||||
@Override public List<String> onTabComplete(@NonNull CommandSender commandSender, @NonNull Command command, @NonNull String s, @NonNull String[] strings) { return null; }
|
||||
|
||||
}
|
||||
+8
-34
@@ -23,7 +23,9 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.bukkit.inject.dummy;
|
||||
package me.lucko.luckperms.bukkit.inject.permissible;
|
||||
|
||||
import me.lucko.luckperms.common.util.EmptyCollections;
|
||||
|
||||
import org.bukkit.permissions.PermissibleBase;
|
||||
import org.bukkit.permissions.Permission;
|
||||
@@ -33,14 +35,8 @@ import org.bukkit.plugin.Plugin;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class DummyPermissibleBase extends PermissibleBase {
|
||||
private static final Field ATTACHMENTS_FIELD;
|
||||
@@ -61,13 +57,9 @@ public class DummyPermissibleBase extends PermissibleBase {
|
||||
public static void copyFields(PermissibleBase from, PermissibleBase to) {
|
||||
try {
|
||||
ATTACHMENTS_FIELD.set(to, ATTACHMENTS_FIELD.get(from));
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
PERMISSIONS_FIELD.set(to, PERMISSIONS_FIELD.get(from));
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,29 +68,11 @@ public class DummyPermissibleBase extends PermissibleBase {
|
||||
private DummyPermissibleBase() {
|
||||
super(null);
|
||||
|
||||
// we want the singleton dummy attachment to be stateless
|
||||
// the behaviour of this class is to fail silently, so we can't use Collections.emptyX
|
||||
try {
|
||||
ATTACHMENTS_FIELD.set(this, new ArrayList<PermissionAttachment>(){
|
||||
@Override public boolean add(PermissionAttachment permissionAttachment) { return true; }
|
||||
@Override public void add(int index, PermissionAttachment element) { }
|
||||
@Override public boolean addAll(Collection<? extends PermissionAttachment> c) { return true; }
|
||||
@Override public boolean addAll(int index, Collection<? extends PermissionAttachment> c) { return true; }
|
||||
});
|
||||
ATTACHMENTS_FIELD.set(this, EmptyCollections.list());
|
||||
PERMISSIONS_FIELD.set(this, EmptyCollections.map());
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
PERMISSIONS_FIELD.set(this, new HashMap<String, PermissionAttachmentInfo>() {
|
||||
@Override public PermissionAttachmentInfo put(String key, PermissionAttachmentInfo value) { return null; }
|
||||
@Override public void putAll(Map<? extends String, ? extends PermissionAttachmentInfo> m) { }
|
||||
@Override public PermissionAttachmentInfo putIfAbsent(String key, PermissionAttachmentInfo value) { return null; }
|
||||
@Override public PermissionAttachmentInfo compute(String key, BiFunction<? super String, ? super PermissionAttachmentInfo, ? extends PermissionAttachmentInfo> remappingFunction) { return null; }
|
||||
@Override public PermissionAttachmentInfo computeIfPresent(String key, BiFunction<? super String, ? super PermissionAttachmentInfo, ? extends PermissionAttachmentInfo> remappingFunction) { return null; }
|
||||
@Override public PermissionAttachmentInfo computeIfAbsent(String key, Function<? super String, ? extends PermissionAttachmentInfo> mappingFunction) { return null; }
|
||||
});
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
+7
-21
@@ -221,9 +221,7 @@ public class LuckPermsPermissible extends PermissibleBase {
|
||||
|
||||
@Override
|
||||
public @NonNull LuckPermsPermissionAttachment addAttachment(@NonNull Plugin plugin) {
|
||||
if (plugin == null) {
|
||||
throw new NullPointerException("plugin");
|
||||
}
|
||||
Objects.requireNonNull(plugin, "plugin");
|
||||
|
||||
LuckPermsPermissionAttachment attachment = new LuckPermsPermissionAttachment(this, plugin);
|
||||
attachment.hook();
|
||||
@@ -232,12 +230,8 @@ public class LuckPermsPermissible extends PermissibleBase {
|
||||
|
||||
@Override
|
||||
public @NonNull PermissionAttachment addAttachment(@NonNull Plugin plugin, @NonNull String permission, boolean value) {
|
||||
if (plugin == null) {
|
||||
throw new NullPointerException("plugin");
|
||||
}
|
||||
if (permission == null) {
|
||||
throw new NullPointerException("permission");
|
||||
}
|
||||
Objects.requireNonNull(plugin, "plugin");
|
||||
Objects.requireNonNull(permission, "permission");
|
||||
|
||||
PermissionAttachment attachment = addAttachment(plugin);
|
||||
attachment.setPermission(permission, value);
|
||||
@@ -246,9 +240,7 @@ public class LuckPermsPermissible extends PermissibleBase {
|
||||
|
||||
@Override
|
||||
public LuckPermsPermissionAttachment addAttachment(@NonNull Plugin plugin, int ticks) {
|
||||
if (plugin == null) {
|
||||
throw new NullPointerException("plugin");
|
||||
}
|
||||
Objects.requireNonNull(plugin, "plugin");
|
||||
|
||||
if (!plugin.isEnabled()) {
|
||||
throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled");
|
||||
@@ -264,12 +256,8 @@ public class LuckPermsPermissible extends PermissibleBase {
|
||||
|
||||
@Override
|
||||
public LuckPermsPermissionAttachment addAttachment(@NonNull Plugin plugin, @NonNull String permission, boolean value, int ticks) {
|
||||
if (plugin == null) {
|
||||
throw new NullPointerException("plugin");
|
||||
}
|
||||
if (permission == null) {
|
||||
throw new NullPointerException("permission");
|
||||
}
|
||||
Objects.requireNonNull(plugin, "plugin");
|
||||
Objects.requireNonNull(permission, "permission");
|
||||
|
||||
LuckPermsPermissionAttachment attachment = addAttachment(plugin, ticks);
|
||||
attachment.setPermission(permission, value);
|
||||
@@ -278,9 +266,7 @@ public class LuckPermsPermissible extends PermissibleBase {
|
||||
|
||||
@Override
|
||||
public void removeAttachment(@NonNull PermissionAttachment attachment) {
|
||||
if (attachment == null) {
|
||||
throw new NullPointerException("attachment");
|
||||
}
|
||||
Objects.requireNonNull(attachment, "attachment");
|
||||
|
||||
LuckPermsPermissionAttachment luckPermsAttachment;
|
||||
|
||||
|
||||
+2
-3
@@ -27,7 +27,6 @@ package me.lucko.luckperms.bukkit.inject.permissible;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import me.lucko.luckperms.bukkit.inject.dummy.DummyPlugin;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.node.factory.NodeBuilders;
|
||||
@@ -106,7 +105,7 @@ public class LuckPermsPermissionAttachment extends PermissionAttachment {
|
||||
private PermissionAttachment source;
|
||||
|
||||
public LuckPermsPermissionAttachment(LuckPermsPermissible permissible, Plugin owner) {
|
||||
super(DummyPlugin.INSTANCE, null);
|
||||
super(owner, null);
|
||||
this.permissible = permissible;
|
||||
this.owner = owner;
|
||||
|
||||
@@ -114,7 +113,7 @@ public class LuckPermsPermissionAttachment extends PermissionAttachment {
|
||||
}
|
||||
|
||||
public LuckPermsPermissionAttachment(LuckPermsPermissible permissible, PermissionAttachment source) {
|
||||
super(DummyPlugin.INSTANCE, null);
|
||||
super(source.getPlugin(), null);
|
||||
this.permissible = permissible;
|
||||
this.owner = source.getPlugin();
|
||||
|
||||
|
||||
-1
@@ -25,7 +25,6 @@
|
||||
|
||||
package me.lucko.luckperms.bukkit.inject.permissible;
|
||||
|
||||
import me.lucko.luckperms.bukkit.inject.dummy.DummyPermissibleBase;
|
||||
import me.lucko.luckperms.common.calculator.result.TristateResult;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.query.QueryOptionsImpl;
|
||||
|
||||
-1
@@ -25,7 +25,6 @@
|
||||
|
||||
package me.lucko.luckperms.bukkit.inject.permissible;
|
||||
|
||||
import me.lucko.luckperms.bukkit.inject.dummy.DummyPermissibleBase;
|
||||
import me.lucko.luckperms.bukkit.util.CraftBukkitImplementation;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
+28
-12
@@ -25,9 +25,7 @@
|
||||
|
||||
package me.lucko.luckperms.bukkit.inject.server;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
|
||||
import me.lucko.luckperms.common.util.ImmutableCollectors;
|
||||
@@ -40,11 +38,12 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* A replacement map for the 'permSubs' instance in Bukkit's SimplePluginManager.
|
||||
@@ -121,6 +120,21 @@ public final class LuckPermsSubscriptionMap extends HashMap<String, Map<Permissi
|
||||
return super.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends Map<Permissible, Boolean>> m) {
|
||||
m.forEach(this::put);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Permissible, Boolean> putIfAbsent(String key, Map<Permissible, Boolean> value) {
|
||||
return get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Permissible, Boolean> computeIfAbsent(String key, Function<? super String, ? extends Map<Permissible, Boolean>> mappingFunction) {
|
||||
return get(key);
|
||||
}
|
||||
|
||||
// if the key isn't null and is a string, #get will always return a value for it
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
@@ -212,18 +226,20 @@ public final class LuckPermsSubscriptionMap extends HashMap<String, Map<Permissi
|
||||
|
||||
@Override
|
||||
public @NonNull Set<Permissible> keySet() {
|
||||
// gather players (LPPermissibles)
|
||||
Set<Permissible> players = LuckPermsSubscriptionMap.this.plugin.getBootstrap().getServer().getOnlinePlayers().stream()
|
||||
.filter(player -> player.hasPermission(this.permission) || player.isPermissionSet(this.permission))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
ImmutableSet<Permissible> backing;
|
||||
// start with the backing set
|
||||
Set<Permissible> set;
|
||||
synchronized (this.backing) {
|
||||
backing = ImmutableSet.copyOf(this.backing.keySet());
|
||||
set = new HashSet<>(this.backing.keySet());
|
||||
}
|
||||
|
||||
// then combine the players with the backing map
|
||||
return Sets.union(players, backing);
|
||||
// add any online players who meet requirements
|
||||
for (Player player : LuckPermsSubscriptionMap.this.plugin.getBootstrap().getServer().getOnlinePlayers()) {
|
||||
if (player.hasPermission(this.permission) || player.isPermissionSet(this.permission)) {
|
||||
set.add(player);
|
||||
}
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.bukkit.listeners;
|
||||
|
||||
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
|
||||
import me.lucko.luckperms.common.api.implementation.ApiUser;
|
||||
import me.lucko.luckperms.common.event.LuckPermsEventListener;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
|
||||
import net.luckperms.api.event.EventBus;
|
||||
import net.luckperms.api.event.context.ContextUpdateEvent;
|
||||
import net.luckperms.api.event.user.UserDataRecalculateEvent;
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Implements the LuckPerms auto op feature.
|
||||
*/
|
||||
public class BukkitAutoOpListener implements LuckPermsEventListener {
|
||||
private static final String NODE = "luckperms.autoop";
|
||||
|
||||
private final LPBukkitPlugin plugin;
|
||||
|
||||
public BukkitAutoOpListener(LPBukkitPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(EventBus bus) {
|
||||
bus.subscribe(UserDataRecalculateEvent.class, this::onUserDataRecalculate);
|
||||
bus.subscribe(ContextUpdateEvent.class, this::onContextUpdate);
|
||||
}
|
||||
|
||||
private void onUserDataRecalculate(UserDataRecalculateEvent e) {
|
||||
User user = ApiUser.cast(e.getUser());
|
||||
this.plugin.getBootstrap().getPlayer(user.getUniqueId()).ifPresent(p -> refreshAutoOp(p, false));
|
||||
}
|
||||
|
||||
private void onContextUpdate(ContextUpdateEvent e) {
|
||||
e.getSubject(Player.class).ifPresent(p -> refreshAutoOp(p, true));
|
||||
}
|
||||
|
||||
private void refreshAutoOp(Player player, boolean callerIsSync) {
|
||||
if (!callerIsSync && this.plugin.getBootstrap().isServerStopping()) {
|
||||
return;
|
||||
}
|
||||
|
||||
User user = this.plugin.getUserManager().getIfLoaded(player.getUniqueId());
|
||||
|
||||
boolean value;
|
||||
if (user != null) {
|
||||
QueryOptions queryOptions = this.plugin.getContextManager().getQueryOptions(player);
|
||||
Map<String, Boolean> permData = user.getCachedData().getPermissionData(queryOptions).getPermissionMap();
|
||||
value = permData.getOrDefault(NODE, false);
|
||||
} else {
|
||||
value = false;
|
||||
}
|
||||
|
||||
if (callerIsSync) {
|
||||
player.setOp(value);
|
||||
} else {
|
||||
this.plugin.getBootstrap().getScheduler().executeSync(() -> player.setOp(value));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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.bukkit.listeners;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
|
||||
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
|
||||
import me.lucko.luckperms.common.cache.BufferedRequest;
|
||||
import me.lucko.luckperms.common.event.LuckPermsEventListener;
|
||||
import me.lucko.luckperms.common.util.CaffeineFactory;
|
||||
|
||||
import net.luckperms.api.event.EventBus;
|
||||
import net.luckperms.api.event.context.ContextUpdateEvent;
|
||||
import net.luckperms.api.event.user.UserDataRecalculateEvent;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Calls {@link Player#updateCommands()} when a players permissions change.
|
||||
*/
|
||||
public class BukkitCommandListUpdater implements LuckPermsEventListener {
|
||||
|
||||
public static boolean isSupported() {
|
||||
try {
|
||||
Player.class.getMethod("updateCommands");
|
||||
return true;
|
||||
} catch (NoSuchMethodException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private final LPBukkitPlugin plugin;
|
||||
private final LoadingCache<UUID, SendBuffer> sendingBuffers = CaffeineFactory.newBuilder()
|
||||
.expireAfterAccess(10, TimeUnit.SECONDS)
|
||||
.build(SendBuffer::new);
|
||||
|
||||
public BukkitCommandListUpdater(LPBukkitPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(EventBus bus) {
|
||||
bus.subscribe(UserDataRecalculateEvent.class, this::onUserDataRecalculate);
|
||||
bus.subscribe(ContextUpdateEvent.class, this::onContextUpdate);
|
||||
}
|
||||
|
||||
private void onUserDataRecalculate(UserDataRecalculateEvent e) {
|
||||
requestUpdate(e.getUser().getUniqueId());
|
||||
}
|
||||
|
||||
private void onContextUpdate(ContextUpdateEvent e) {
|
||||
e.getSubject(Player.class).ifPresent(p -> requestUpdate(p.getUniqueId()));
|
||||
}
|
||||
|
||||
private void requestUpdate(UUID uniqueId) {
|
||||
if (this.plugin.getBootstrap().isServerStopping()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.plugin.getBootstrap().isPlayerOnline(uniqueId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Buffer the request to send a commands update.
|
||||
this.sendingBuffers.get(uniqueId).request();
|
||||
}
|
||||
|
||||
// Called when the buffer times out.
|
||||
private void sendUpdate(UUID uniqueId) {
|
||||
if (this.plugin.getBootstrap().isServerStopping()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.plugin.getBootstrap().getScheduler().sync()
|
||||
.execute(() -> this.plugin.getBootstrap().getPlayer(uniqueId).ifPresent(Player::updateCommands));
|
||||
}
|
||||
|
||||
private final class SendBuffer extends BufferedRequest<Void> {
|
||||
private final UUID uniqueId;
|
||||
|
||||
SendBuffer(UUID uniqueId) {
|
||||
super(500, TimeUnit.MILLISECONDS, BukkitCommandListUpdater.this.plugin.getBootstrap().getScheduler());
|
||||
this.uniqueId = uniqueId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void perform() {
|
||||
sendUpdate(this.uniqueId);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -201,7 +201,7 @@ public class BukkitConnectionListener extends AbstractConnectionListener impleme
|
||||
return;
|
||||
}
|
||||
|
||||
this.plugin.refreshAutoOp(player, true);
|
||||
this.plugin.getContextManager().signalContextUpdate(player);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
|
||||
+10
-22
@@ -32,38 +32,40 @@ import me.lucko.luckperms.common.locale.message.Message;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
import org.bukkit.event.server.PluginEnableEvent;
|
||||
import org.bukkit.event.server.RemoteServerCommandEvent;
|
||||
import org.bukkit.event.server.ServerCommandEvent;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class BukkitPlatformListener implements Listener {
|
||||
private static final Pattern OP_COMMAND_PATTERN = Pattern.compile("^/?(\\w+:)?(deop|op)( .*)?$");
|
||||
|
||||
private final LPBukkitPlugin plugin;
|
||||
|
||||
public BukkitPlatformListener(LPBukkitPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onPlayerCommand(PlayerCommandPreprocessEvent e) {
|
||||
handleCommand(e.getPlayer(), e.getMessage().toLowerCase(), e);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onServerCommand(ServerCommandEvent e) {
|
||||
handleCommand(e.getSender(), e.getCommand().toLowerCase(), e);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onRemoteServerCommand(RemoteServerCommandEvent e) {
|
||||
handleCommand(e.getSender(), e.getCommand().toLowerCase(), e);
|
||||
}
|
||||
|
||||
private void handleCommand(CommandSender sender, String s, Cancellable event) {
|
||||
if (s.isEmpty()) {
|
||||
private void handleCommand(CommandSender sender, String cmdLine, Cancellable event) {
|
||||
if (cmdLine.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -71,15 +73,7 @@ public class BukkitPlatformListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
if (s.charAt(0) == '/') {
|
||||
s = s.substring(1);
|
||||
}
|
||||
|
||||
if (s.contains(":")) {
|
||||
s = s.substring(s.indexOf(':') + 1);
|
||||
}
|
||||
|
||||
if (s.equals("op") || s.startsWith("op ") || s.equals("deop") || s.startsWith("deop ")) {
|
||||
if (OP_COMMAND_PATTERN.matcher(cmdLine).matches()) {
|
||||
event.setCancelled(true);
|
||||
sender.sendMessage(Message.OP_DISABLED.asString(this.plugin.getLocaleManager()));
|
||||
}
|
||||
@@ -92,10 +86,4 @@ public class BukkitPlatformListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onWorldChange(PlayerChangedWorldEvent e) {
|
||||
this.plugin.getContextManager().invalidateCache(e.getPlayer());
|
||||
this.plugin.refreshAutoOp(e.getPlayer(), true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import de.bananaco.bpermissions.api.WorldManager;
|
||||
import me.lucko.luckperms.common.command.CommandResult;
|
||||
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.commands.migration.MigrationUtils;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.locale.command.CommandSpec;
|
||||
@@ -61,7 +62,6 @@ import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
@@ -83,7 +83,7 @@ public class MigrationBPermissions extends ChildCommand<Object> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) {
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object ignored, ArgumentList args, String label) {
|
||||
ProgressLogger log = new ProgressLogger(Message.MIGRATION_LOG, Message.MIGRATION_LOG_PROGRESS, "bPermissions");
|
||||
log.addListener(plugin.getConsoleSender());
|
||||
log.addListener(sender);
|
||||
|
||||
@@ -28,6 +28,7 @@ package me.lucko.luckperms.bukkit.migration;
|
||||
import me.lucko.luckperms.common.command.CommandResult;
|
||||
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.commands.migration.MigrationUtils;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.locale.command.CommandSpec;
|
||||
@@ -74,7 +75,7 @@ public class MigrationGroupManager extends ChildCommand<Object> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) {
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object ignored, ArgumentList args, String label) {
|
||||
ProgressLogger log = new ProgressLogger(Message.MIGRATION_LOG, Message.MIGRATION_LOG_PROGRESS, "GroupManager");
|
||||
log.addListener(plugin.getConsoleSender());
|
||||
log.addListener(sender);
|
||||
|
||||
+2
-1
@@ -30,6 +30,7 @@ import com.platymuus.bukkit.permissions.PermissionsPlugin;
|
||||
import me.lucko.luckperms.common.command.CommandResult;
|
||||
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.commands.migration.MigrationUtils;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.locale.command.CommandSpec;
|
||||
@@ -62,7 +63,7 @@ public class MigrationPermissionsBukkit extends ChildCommand<Object> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) {
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object ignored, ArgumentList args, String label) {
|
||||
ProgressLogger log = new ProgressLogger(Message.MIGRATION_LOG, Message.MIGRATION_LOG_PROGRESS, "PermissionsBukkit");
|
||||
log.addListener(plugin.getConsoleSender());
|
||||
log.addListener(sender);
|
||||
|
||||
+2
-1
@@ -30,6 +30,7 @@ import com.google.common.base.Strings;
|
||||
import me.lucko.luckperms.common.command.CommandResult;
|
||||
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.commands.migration.MigrationUtils;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.locale.command.CommandSpec;
|
||||
@@ -107,7 +108,7 @@ public class MigrationPermissionsEx extends ChildCommand<Object> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) {
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object ignored, ArgumentList args, String label) {
|
||||
ProgressLogger log = new ProgressLogger(Message.MIGRATION_LOG, Message.MIGRATION_LOG_PROGRESS, "PermissionsEx");
|
||||
log.addListener(plugin.getConsoleSender());
|
||||
log.addListener(sender);
|
||||
|
||||
+2
-1
@@ -36,6 +36,7 @@ import com.zaxxer.hikari.HikariDataSource;
|
||||
import me.lucko.luckperms.common.command.CommandResult;
|
||||
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.commands.migration.MigrationUtils;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
@@ -84,7 +85,7 @@ public class MigrationPowerfulPerms extends ChildCommand<Object> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) {
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object ignored, ArgumentList args, String label) {
|
||||
ProgressLogger log = new ProgressLogger(Message.MIGRATION_LOG, Message.MIGRATION_LOG_PROGRESS, "PowerfulPerms");
|
||||
log.addListener(plugin.getConsoleSender());
|
||||
log.addListener(sender);
|
||||
|
||||
@@ -28,6 +28,7 @@ package me.lucko.luckperms.bukkit.migration;
|
||||
import me.lucko.luckperms.common.command.CommandResult;
|
||||
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.commands.migration.MigrationUtils;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.locale.command.CommandSpec;
|
||||
@@ -63,7 +64,6 @@ import org.tyrannyofheaven.bukkit.zPermissions.model.PermissionEntity;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
@@ -75,7 +75,7 @@ public class MigrationZPermissions extends ChildCommand<Object> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) {
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object ignored, ArgumentList args, String label) {
|
||||
ProgressLogger log = new ProgressLogger(Message.MIGRATION_LOG, Message.MIGRATION_LOG_PROGRESS, "zPermissions");
|
||||
log.addListener(plugin.getConsoleSender());
|
||||
log.addListener(sender);
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.bukkit.util;
|
||||
|
||||
import com.google.common.graph.MutableGraph;
|
||||
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.SimplePluginManager;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public final class PluginManagerUtil {
|
||||
private PluginManagerUtil() {}
|
||||
|
||||
private static final Field DEPENDENCY_GRAPH_FIELD;
|
||||
|
||||
static {
|
||||
Field dependencyGraphField = null;
|
||||
try {
|
||||
dependencyGraphField = SimplePluginManager.class.getDeclaredField("dependencyGraph");
|
||||
dependencyGraphField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
DEPENDENCY_GRAPH_FIELD = dependencyGraphField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects a dependency relationship into the plugin manager.
|
||||
*
|
||||
* @param plugin the plugin
|
||||
* @param depend the plugin being depended on
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void injectDependency(PluginManager pluginManager, String plugin, String depend) {
|
||||
if (DEPENDENCY_GRAPH_FIELD == null || !(pluginManager instanceof SimplePluginManager)) {
|
||||
return; // fail silently
|
||||
}
|
||||
|
||||
try {
|
||||
MutableGraph<String> graph = (MutableGraph<String>) DEPENDENCY_GRAPH_FIELD.get(pluginManager);
|
||||
graph.putEdge(plugin, depend);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,11 +46,11 @@ import me.lucko.luckperms.common.util.Uuids;
|
||||
import me.lucko.luckperms.common.verbose.event.MetaCheckEvent;
|
||||
import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent;
|
||||
|
||||
import net.luckperms.api.context.ContextSet;
|
||||
import net.luckperms.api.context.DefaultContextKeys;
|
||||
import net.luckperms.api.context.MutableContextSet;
|
||||
import net.luckperms.api.model.data.DataType;
|
||||
import net.luckperms.api.node.Node;
|
||||
import net.luckperms.api.node.NodeType;
|
||||
import net.luckperms.api.query.Flag;
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
import net.milkbowl.vault.permission.Permission;
|
||||
@@ -251,10 +251,9 @@ public class LuckPermsVaultPermission extends AbstractVaultPermission {
|
||||
Objects.requireNonNull(uuid, "uuid");
|
||||
|
||||
PermissionHolder user = lookupUser(uuid);
|
||||
ContextSet contexts = getQueryOptions(uuid, world).context();
|
||||
QueryOptions queryOptions = getQueryOptions(uuid, world);
|
||||
|
||||
return user.normalData().immutableInheritance().values().stream()
|
||||
.filter(n -> n.getContexts().isSatisfiedBy(contexts))
|
||||
return user.getOwnNodes(NodeType.INHERITANCE, queryOptions).stream()
|
||||
.map(n -> {
|
||||
Group group = this.plugin.getGroupManager().getIfLoaded(n.getGroupName());
|
||||
if (group != null) {
|
||||
|
||||
@@ -148,7 +148,7 @@ data:
|
||||
#verifyServerCertificate: false
|
||||
|
||||
# The prefix for all LuckPerms SQL tables.
|
||||
# - Change this is you want to use different tables for different servers.
|
||||
# - Change this if you want to use different tables for different servers.
|
||||
table-prefix: 'luckperms_'
|
||||
|
||||
# The prefix to use for all LuckPerms collections. Change this if you want to use different
|
||||
@@ -304,6 +304,12 @@ require-sender-group-membership-to-modify: false
|
||||
# '/lp log notify <on|off>'
|
||||
log-notify: true
|
||||
|
||||
# Defines a list of log entries which should not be sent as notifications to users.
|
||||
#
|
||||
# - Each entry in the list is a RegEx expression which is matched against the log entry description.
|
||||
log-notify-filtered-descriptions:
|
||||
# - "parent add example"
|
||||
|
||||
# Defines the options for prefix and suffix stacking.
|
||||
#
|
||||
# - The feature allows you to display multiple prefixes or suffixes alongside a players username in
|
||||
@@ -400,6 +406,15 @@ inheritance-traversal-algorithm: depth-first-pre-order
|
||||
# and when this setting is 'false':, the rules are just applied during each step of the traversal.
|
||||
post-traversal-inheritance-sort: false
|
||||
|
||||
# Defines the mode used to determine whether a set of contexts are satisfied.
|
||||
#
|
||||
# - Possible options:
|
||||
# => at-least-one-value-per-key Set A will be satisfied by another set B, if at least one of the
|
||||
# key-value entries per key in A are also in B.
|
||||
# => all-values-per-key Set A will be satisfied by another set B, if all key-value
|
||||
# entries in A are also in B.
|
||||
context-satisfy-mode: at-least-one-value-per-key
|
||||
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
# | Permission resolution settings | #
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
@@ -608,6 +623,12 @@ allow-invalid-usernames: false
|
||||
# - When this happens, the plugin will set their primary group back to default.
|
||||
prevent-primary-group-removal: false
|
||||
|
||||
# If LuckPerms should update the list of commands sent to the client when permissions are changed.
|
||||
update-client-command-list: true
|
||||
|
||||
# If LuckPerms should attempt to register "Brigadier" command list data for its commands.
|
||||
register-command-list-data: true
|
||||
|
||||
# If LuckPerms should attempt to resolve Vanilla command target selectors for LP commands.
|
||||
# See here for more info: https://minecraft.gamepedia.com/Commands#Target_selectors
|
||||
resolve-command-selectors: false
|
||||
@@ -16,6 +16,11 @@ luckperms {
|
||||
record {
|
||||
filter brigadier:string greedy_phrase;
|
||||
}
|
||||
command {
|
||||
player brigadier:string single_word {
|
||||
command brigadier:string greedy_phrase;
|
||||
}
|
||||
}
|
||||
}
|
||||
tree {
|
||||
scope brigadier:string single_word {
|
||||
@@ -100,6 +105,9 @@ luckperms {
|
||||
unsettemp {
|
||||
node brigadier:string quotable_phrase {
|
||||
context brigadier:string greedy_phrase;
|
||||
duration brigadier:string single_word {
|
||||
context brigadier:string greedy_phrase;
|
||||
}
|
||||
}
|
||||
}
|
||||
check {
|
||||
@@ -151,6 +159,9 @@ luckperms {
|
||||
removetemp {
|
||||
group brigadier:string single_word {
|
||||
context brigadier:string greedy_phrase;
|
||||
duration brigadier:string single_word {
|
||||
context brigadier:string greedy_phrase;
|
||||
}
|
||||
}
|
||||
}
|
||||
clear {
|
||||
@@ -342,6 +353,9 @@ luckperms {
|
||||
unsettemp {
|
||||
node brigadier:string quotable_phrase {
|
||||
context brigadier:string greedy_phrase;
|
||||
duration brigadier:string single_word {
|
||||
context brigadier:string greedy_phrase;
|
||||
}
|
||||
}
|
||||
}
|
||||
check {
|
||||
@@ -393,6 +407,9 @@ luckperms {
|
||||
removetemp {
|
||||
group brigadier:string single_word {
|
||||
context brigadier:string greedy_phrase;
|
||||
duration brigadier:string single_word {
|
||||
context brigadier:string greedy_phrase;
|
||||
}
|
||||
}
|
||||
}
|
||||
clear {
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ dependencies {
|
||||
compile project(':common')
|
||||
|
||||
compileOnly 'net.md-5:bungeecord-api:1.15-SNAPSHOT'
|
||||
compileOnly 'net.kyori:text-adapter-bungeecord:3.0.3'
|
||||
compileOnly 'net.kyori:text-adapter-bungeecord:3.0.4'
|
||||
compileOnly 'com.imaginarycode.minecraft:RedisBungee:0.4'
|
||||
|
||||
// migration plugins
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
package me.lucko.luckperms.bungee;
|
||||
|
||||
import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
|
||||
import me.lucko.luckperms.common.config.generic.adapter.ConfigurationAdapter;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
package me.lucko.luckperms.bungee;
|
||||
|
||||
import me.lucko.luckperms.bungee.event.TristateCheckEvent;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import me.lucko.luckperms.common.sender.SenderFactory;
|
||||
import me.lucko.luckperms.common.util.TextUtils;
|
||||
@@ -39,8 +38,8 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class BungeeSenderFactory extends SenderFactory<CommandSender> {
|
||||
public BungeeSenderFactory(LuckPermsPlugin plugin) {
|
||||
public class BungeeSenderFactory extends SenderFactory<LPBungeePlugin, CommandSender> {
|
||||
public BungeeSenderFactory(LPBungeePlugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@@ -79,4 +78,9 @@ public class BungeeSenderFactory extends SenderFactory<CommandSender> {
|
||||
protected boolean hasPermission(CommandSender sender, String node) {
|
||||
return sender.hasPermission(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performCommand(CommandSender sender, String command) {
|
||||
getPlugin().getBootstrap().getProxy().getPluginManager().dispatchCommand(sender, command);
|
||||
}
|
||||
}
|
||||
@@ -36,14 +36,19 @@ import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter;
|
||||
import net.luckperms.api.platform.Platform;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.api.plugin.PluginDescription;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Bootstrap plugin for LuckPerms running on BungeeCord.
|
||||
@@ -246,13 +251,23 @@ public class LPBungeeBootstrap extends Plugin implements LuckPermsBootstrap {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> getPlayerList() {
|
||||
return getProxy().getPlayers().stream().map(ProxiedPlayer::getName);
|
||||
public Collection<String> getPlayerList() {
|
||||
Collection<ProxiedPlayer> players = getProxy().getPlayers();
|
||||
List<String> list = new ArrayList<>(players.size());
|
||||
for (ProxiedPlayer player : players) {
|
||||
list.add(player.getName());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<UUID> getOnlinePlayers() {
|
||||
return getProxy().getPlayers().stream().map(ProxiedPlayer::getUniqueId);
|
||||
public Collection<UUID> getOnlinePlayers() {
|
||||
Collection<ProxiedPlayer> players = getProxy().getPlayers();
|
||||
List<UUID> list = new ArrayList<>(players.size());
|
||||
for (ProxiedPlayer player : players) {
|
||||
list.add(player.getUniqueId());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -260,9 +275,19 @@ public class LPBungeeBootstrap extends Plugin implements LuckPermsBootstrap {
|
||||
return getProxy().getPlayer(uniqueId) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String identifyClassLoader(ClassLoader classLoader) throws Exception {
|
||||
Class<?> pluginClassLoader = Class.forName("net.md_5.bungee.api.plugin.PluginClassloader");
|
||||
if (pluginClassLoader.isInstance(classLoader)) {
|
||||
PluginDescription desc = (PluginDescription) pluginClassLoader.getDeclaredField("desc").get(classLoader);
|
||||
return desc.getName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean checkIncompatibleVersion() {
|
||||
try {
|
||||
Class<?> aClass = Class.forName("com.google.gson.internal.bind.TreeTypeAdapter");
|
||||
Class.forName("com.google.gson.internal.bind.TreeTypeAdapter");
|
||||
return false;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return true;
|
||||
|
||||
@@ -35,7 +35,7 @@ import me.lucko.luckperms.bungee.messaging.BungeeMessagingFactory;
|
||||
import me.lucko.luckperms.common.api.LuckPermsApiProvider;
|
||||
import me.lucko.luckperms.common.calculator.CalculatorFactory;
|
||||
import me.lucko.luckperms.common.command.CommandManager;
|
||||
import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
|
||||
import me.lucko.luckperms.common.config.generic.adapter.ConfigurationAdapter;
|
||||
import me.lucko.luckperms.common.dependencies.Dependency;
|
||||
import me.lucko.luckperms.common.event.AbstractEventBus;
|
||||
import me.lucko.luckperms.common.messaging.MessagingFactory;
|
||||
@@ -138,7 +138,10 @@ public class LPBungeePlugin extends AbstractLuckPermsPlugin {
|
||||
@Override
|
||||
protected void setupContextManager() {
|
||||
this.contextManager = new BungeeContextManager(this);
|
||||
this.contextManager.registerCalculator(new BackendServerCalculator(this));
|
||||
|
||||
BackendServerCalculator backendServerCalculator = new BackendServerCalculator(this);
|
||||
this.bootstrap.getProxy().getPluginManager().registerListener(this.bootstrap, backendServerCalculator);
|
||||
this.contextManager.registerCalculator(backendServerCalculator);
|
||||
|
||||
if (this.bootstrap.getProxy().getPluginManager().getPlugin("RedisBungee") != null) {
|
||||
this.contextManager.registerCalculator(new RedisBungeeCalculator());
|
||||
|
||||
@@ -36,6 +36,9 @@ import net.luckperms.api.context.DefaultContextKeys;
|
||||
import net.luckperms.api.context.ImmutableContextSet;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.ServerSwitchEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
@@ -43,7 +46,7 @@ import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class BackendServerCalculator implements ContextCalculator<ProxiedPlayer> {
|
||||
public class BackendServerCalculator implements ContextCalculator<ProxiedPlayer>, Listener {
|
||||
|
||||
private static String getServer(ProxiedPlayer player) {
|
||||
return player.getServer() == null ? null : (player.getServer().getInfo() == null ? null : player.getServer().getInfo().getName().toLowerCase());
|
||||
@@ -74,4 +77,9 @@ public class BackendServerCalculator implements ContextCalculator<ProxiedPlayer>
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onServerSwitch(ServerSwitchEvent e) {
|
||||
this.plugin.getContextManager().signalContextUpdate(e.getPlayer());
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ public class BungeeContextManager extends ContextManager<ProxiedPlayer, ProxiedP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCache(ProxiedPlayer subject) {
|
||||
protected void invalidateCache(ProxiedPlayer subject) {
|
||||
this.contextsCache.invalidate(subject);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ package me.lucko.luckperms.bungee.migration;
|
||||
import me.lucko.luckperms.common.command.CommandResult;
|
||||
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.commands.migration.MigrationUtils;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.locale.command.CommandSpec;
|
||||
@@ -61,7 +62,7 @@ public class MigrationBungeePerms extends ChildCommand<Object> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) {
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object ignored, ArgumentList args, String label) {
|
||||
ProgressLogger log = new ProgressLogger(Message.MIGRATION_LOG, Message.MIGRATION_LOG_PROGRESS, "BungeePerms");
|
||||
log.addListener(plugin.getConsoleSender());
|
||||
log.addListener(sender);
|
||||
|
||||
@@ -145,7 +145,7 @@ data:
|
||||
#verifyServerCertificate: false
|
||||
|
||||
# The prefix for all LuckPerms SQL tables.
|
||||
# - Change this is you want to use different tables for different servers.
|
||||
# - Change this if you want to use different tables for different servers.
|
||||
table-prefix: 'luckperms_'
|
||||
|
||||
# The prefix to use for all LuckPerms collections. Change this if you want to use different
|
||||
@@ -312,6 +312,12 @@ require-sender-group-membership-to-modify: false
|
||||
# '/lp log notify <on|off>'
|
||||
log-notify: true
|
||||
|
||||
# Defines a list of log entries which should not be sent as notifications to users.
|
||||
#
|
||||
# - Each entry in the list is a RegEx expression which is matched against the log entry description.
|
||||
log-notify-filtered-descriptions:
|
||||
# - "parent add example"
|
||||
|
||||
# Defines the options for prefix and suffix stacking.
|
||||
#
|
||||
# - The feature allows you to display multiple prefixes or suffixes alongside a players username in
|
||||
@@ -408,6 +414,15 @@ inheritance-traversal-algorithm: depth-first-pre-order
|
||||
# and when this setting is 'false':, the rules are just applied during each step of the traversal.
|
||||
post-traversal-inheritance-sort: false
|
||||
|
||||
# Defines the mode used to determine whether a set of contexts are satisfied.
|
||||
#
|
||||
# - Possible options:
|
||||
# => at-least-one-value-per-key Set A will be satisfied by another set B, if at least one of the
|
||||
# key-value entries per key in A are also in B.
|
||||
# => all-values-per-key Set A will be satisfied by another set B, if all key-value
|
||||
# entries in A are also in B.
|
||||
context-satisfy-mode: at-least-one-value-per-key
|
||||
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
# | Permission resolution settings | #
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
|
||||
+15
-10
@@ -2,13 +2,13 @@ dependencies {
|
||||
compile project(':api')
|
||||
|
||||
compile 'org.checkerframework:checker-qual:2.5.5'
|
||||
compile('net.kyori:text-api:3.0.3') {
|
||||
compile('net.kyori:text-api:3.0.4') {
|
||||
exclude(module: 'checker-qual')
|
||||
}
|
||||
compile('net.kyori:text-serializer-legacy:3.0.3') {
|
||||
compile('net.kyori:text-serializer-legacy:3.0.4') {
|
||||
exclude(module: 'text-api')
|
||||
}
|
||||
compile('net.kyori:text-serializer-gson:3.0.3') {
|
||||
compile('net.kyori:text-serializer-gson:3.0.4') {
|
||||
exclude(module: 'text-api')
|
||||
exclude(module: 'gson')
|
||||
}
|
||||
@@ -18,24 +18,29 @@ dependencies {
|
||||
}
|
||||
compile 'com.google.code.gson:gson:2.7'
|
||||
compile 'com.google.guava:guava:19.0'
|
||||
compile 'com.github.ben-manes.caffeine:caffeine:2.8.1'
|
||||
compile 'com.github.ben-manes.caffeine:caffeine:2.8.4'
|
||||
compile 'com.squareup.okhttp3:okhttp:3.14.7'
|
||||
compile 'com.squareup.okio:okio:1.17.5'
|
||||
compile 'net.bytebuddy:byte-buddy:1.10.9'
|
||||
compile('me.lucko.configurate:configurate-core:3.5') {
|
||||
compile('org.spongepowered:configurate-core:3.7') {
|
||||
exclude(module: 'checker-qual')
|
||||
exclude(module: 'guava')
|
||||
}
|
||||
compile('me.lucko.configurate:configurate-yaml:3.5') {
|
||||
compile('org.spongepowered:configurate-yaml:3.7') {
|
||||
exclude(module: 'checker-qual')
|
||||
exclude(module: 'snakeyaml')
|
||||
}
|
||||
compile('me.lucko.configurate:configurate-gson:3.5') {
|
||||
compile('org.spongepowered:configurate-gson:3.7') {
|
||||
exclude(module: 'checker-qual')
|
||||
exclude(module: 'gson')
|
||||
}
|
||||
compile 'me.lucko.configurate:configurate-hocon:3.5'
|
||||
compile('me.lucko.configurate:configurate-toml:3.5') {
|
||||
compile ('org.spongepowered:configurate-hocon:3.7') {
|
||||
exclude(module: 'checker-qual')
|
||||
}
|
||||
compile('me.lucko.configurate:configurate-toml:3.7') {
|
||||
exclude(module: 'toml4j')
|
||||
}
|
||||
compile 'com.zaxxer:HikariCP:3.4.2'
|
||||
compile 'com.zaxxer:HikariCP:3.4.5'
|
||||
compile 'redis.clients:jedis:3.3.0'
|
||||
compile 'org.mongodb:mongo-java-driver:3.12.2'
|
||||
compile 'org.yaml:snakeyaml:1.23'
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.actionlog;
|
||||
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
|
||||
final class ActionComparator implements Comparator<Action> {
|
||||
public static final Comparator<Action> INSTANCE = new ActionComparator();
|
||||
|
||||
private ActionComparator() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(Action o1, Action o2) {
|
||||
int cmp = o1.getTimestamp().compareTo(o2.getTimestamp());
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
|
||||
Action.Source o1Source = o1.getSource();
|
||||
Action.Source o2Source = o2.getSource();
|
||||
|
||||
cmp = o1Source.getUniqueId().compareTo(o2Source.getUniqueId());
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
|
||||
cmp = o1Source.getName().compareTo(o2Source.getName());
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
|
||||
Action.Target o1Target = o1.getTarget();
|
||||
Action.Target o2Target = o2.getTarget();
|
||||
|
||||
cmp = o1Target.getType().compareTo(o2Target.getType());
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
|
||||
cmp = o1Target.getType().compareTo(o2Target.getType());
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
|
||||
cmp = compareOptionals(o1Target.getUniqueId(), o2Target.getUniqueId());
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
|
||||
cmp = o1Source.getName().compareTo(o2Source.getName());
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
|
||||
return o1.getDescription().compareTo(o2.getDescription());
|
||||
}
|
||||
|
||||
@SuppressWarnings({"OptionalUsedAsFieldOrParameterType", "OptionalIsPresent"})
|
||||
private static <T extends Comparable<T>> int compareOptionals(Optional<T> a, Optional<T> b) {
|
||||
if (!a.isPresent()) {
|
||||
return b.isPresent() ? -1 : 0;
|
||||
} else if (!b.isPresent()) {
|
||||
return 1;
|
||||
} else {
|
||||
return a.get().compareTo(b.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,36 +25,33 @@
|
||||
|
||||
package me.lucko.luckperms.common.actionlog;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
|
||||
import me.lucko.luckperms.common.util.ImmutableCollectors;
|
||||
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Log {
|
||||
private static Log empty = null;
|
||||
private static final Log EMPTY = new Log(ImmutableList.of());
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static synchronized Log empty() {
|
||||
if (empty == null) {
|
||||
empty = builder().build();
|
||||
}
|
||||
return empty;
|
||||
public static Log empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
private final SortedSet<LoggedAction> content;
|
||||
|
||||
public Log(SortedSet<LoggedAction> content) {
|
||||
this.content = ImmutableSortedSet.copyOfSorted(content);
|
||||
Log(List<LoggedAction> content) {
|
||||
this.content = ImmutableSortedSet.copyOf(content);
|
||||
}
|
||||
|
||||
public SortedSet<LoggedAction> getContent() {
|
||||
@@ -64,38 +61,38 @@ public class Log {
|
||||
public SortedSet<LoggedAction> getContent(UUID actor) {
|
||||
return this.content.stream()
|
||||
.filter(e -> e.getSource().getUniqueId().equals(actor))
|
||||
.collect(Collectors.toCollection(TreeSet::new));
|
||||
.collect(ImmutableCollectors.toSortedSet());
|
||||
}
|
||||
|
||||
public SortedSet<LoggedAction> getUserHistory(UUID uniqueId) {
|
||||
return this.content.stream()
|
||||
.filter(e -> e.getTarget().getType() == Action.Target.Type.USER)
|
||||
.filter(e -> e.getTarget().getUniqueId().isPresent() && e.getTarget().getUniqueId().get().equals(uniqueId))
|
||||
.collect(ImmutableCollectors.toSortedSet(Comparator.naturalOrder()));
|
||||
.collect(ImmutableCollectors.toSortedSet());
|
||||
}
|
||||
|
||||
public SortedSet<LoggedAction> getGroupHistory(String name) {
|
||||
return this.content.stream()
|
||||
.filter(e -> e.getTarget().getType() == Action.Target.Type.GROUP)
|
||||
.filter(e -> e.getTarget().getName().equals(name))
|
||||
.collect(ImmutableCollectors.toSortedSet(Comparator.naturalOrder()));
|
||||
.collect(ImmutableCollectors.toSortedSet());
|
||||
}
|
||||
|
||||
public SortedSet<LoggedAction> getTrackHistory(String name) {
|
||||
return this.content.stream()
|
||||
.filter(e -> e.getTarget().getType() == Action.Target.Type.TRACK)
|
||||
.filter(e -> e.getTarget().getName().equals(name))
|
||||
.collect(ImmutableCollectors.toSortedSet(Comparator.naturalOrder()));
|
||||
.collect(ImmutableCollectors.toSortedSet());
|
||||
}
|
||||
|
||||
public SortedSet<LoggedAction> getSearch(String query) {
|
||||
return this.content.stream()
|
||||
.filter(e -> e.matchesSearch(query))
|
||||
.collect(ImmutableCollectors.toSortedSet(Comparator.naturalOrder()));
|
||||
.collect(ImmutableCollectors.toSortedSet());
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private final SortedSet<LoggedAction> content = new TreeSet<>();
|
||||
private final List<LoggedAction> content = new ArrayList<>();
|
||||
|
||||
public Builder add(LoggedAction e) {
|
||||
this.content.add(e);
|
||||
@@ -103,6 +100,9 @@ public class Log {
|
||||
}
|
||||
|
||||
public Log build() {
|
||||
if (this.content.isEmpty()) {
|
||||
return EMPTY;
|
||||
}
|
||||
return new Log(this.content);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ import me.lucko.luckperms.common.sender.Sender;
|
||||
import net.luckperms.api.event.log.LogBroadcastEvent;
|
||||
import net.luckperms.api.event.log.LogNotifyEvent;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class LogDispatcher {
|
||||
private final LuckPermsPlugin plugin;
|
||||
|
||||
@@ -42,6 +45,26 @@ public class LogDispatcher {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
private boolean shouldBroadcast(LoggedAction entry, LogBroadcastEvent.Origin origin) {
|
||||
boolean cancelled = false;
|
||||
|
||||
if (!this.plugin.getConfiguration().get(ConfigKeys.LOG_NOTIFY)) {
|
||||
cancelled = true;
|
||||
} else if (origin == LogBroadcastEvent.Origin.REMOTE && !this.plugin.getConfiguration().get(ConfigKeys.BROADCAST_RECEIVED_LOG_ENTRIES)) {
|
||||
cancelled = true;
|
||||
} else {
|
||||
Collection<Pattern> filters = this.plugin.getConfiguration().get(ConfigKeys.LOG_NOTIFY_FILTERED_DESCRIPTIONS);
|
||||
for (Pattern filter : filters) {
|
||||
if (filter.matcher(entry.getDescription()).matches()) {
|
||||
cancelled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !this.plugin.getEventDispatcher().dispatchLogBroadcast(cancelled, entry, origin);
|
||||
}
|
||||
|
||||
private void broadcast(LoggedAction entry, LogNotifyEvent.Origin origin, Sender sender) {
|
||||
this.plugin.getOnlineSenders()
|
||||
.filter(CommandPermission.LOG_NOTIFY::isAuthorized)
|
||||
@@ -64,12 +87,19 @@ public class LogDispatcher {
|
||||
|
||||
this.plugin.getMessagingService().ifPresent(service -> service.pushLog(entry));
|
||||
|
||||
boolean shouldCancel = !this.plugin.getConfiguration().get(ConfigKeys.LOG_NOTIFY);
|
||||
if (!this.plugin.getEventDispatcher().dispatchLogBroadcast(shouldCancel, entry, LogBroadcastEvent.Origin.LOCAL)) {
|
||||
if (shouldBroadcast(entry, LogBroadcastEvent.Origin.LOCAL)) {
|
||||
broadcast(entry, LogNotifyEvent.Origin.LOCAL, sender);
|
||||
}
|
||||
}
|
||||
|
||||
public void broadcastFromApi(LoggedAction entry) {
|
||||
this.plugin.getMessagingService().ifPresent(extendedMessagingService -> extendedMessagingService.pushLog(entry));
|
||||
|
||||
if (shouldBroadcast(entry, LogBroadcastEvent.Origin.LOCAL_API)) {
|
||||
broadcast(entry, LogNotifyEvent.Origin.LOCAL_API, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void dispatchFromApi(LoggedAction entry) {
|
||||
if (!this.plugin.getEventDispatcher().dispatchLogPublish(false, entry)) {
|
||||
try {
|
||||
@@ -82,18 +112,8 @@ public class LogDispatcher {
|
||||
broadcastFromApi(entry);
|
||||
}
|
||||
|
||||
public void broadcastFromApi(LoggedAction entry) {
|
||||
this.plugin.getMessagingService().ifPresent(extendedMessagingService -> extendedMessagingService.pushLog(entry));
|
||||
|
||||
boolean shouldCancel = !this.plugin.getConfiguration().get(ConfigKeys.LOG_NOTIFY);
|
||||
if (!this.plugin.getEventDispatcher().dispatchLogBroadcast(shouldCancel, entry, LogBroadcastEvent.Origin.LOCAL_API)) {
|
||||
broadcast(entry, LogNotifyEvent.Origin.LOCAL_API, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void dispatchFromRemote(LoggedAction entry) {
|
||||
boolean shouldCancel = !this.plugin.getConfiguration().get(ConfigKeys.BROADCAST_RECEIVED_LOG_ENTRIES) || !this.plugin.getConfiguration().get(ConfigKeys.LOG_NOTIFY);
|
||||
if (!this.plugin.getEventDispatcher().dispatchLogBroadcast(shouldCancel, entry, LogBroadcastEvent.Origin.REMOTE)) {
|
||||
if (shouldBroadcast(entry, LogBroadcastEvent.Origin.REMOTE)) {
|
||||
broadcast(entry, LogNotifyEvent.Origin.REMOTE, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import me.lucko.luckperms.common.model.Track;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import me.lucko.luckperms.common.util.DurationFormatter;
|
||||
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
import net.luckperms.api.context.Context;
|
||||
@@ -45,7 +46,6 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
@@ -58,15 +58,6 @@ import java.util.UUID;
|
||||
*/
|
||||
public class LoggedAction implements Action {
|
||||
|
||||
private static final Comparator<Action> COMPARATOR = Comparator
|
||||
.<Action>comparingLong(a -> a.getTimestamp().getEpochSecond())
|
||||
.thenComparing(a -> a.getSource().getUniqueId())
|
||||
.thenComparing(a -> a.getSource().getName(), String.CASE_INSENSITIVE_ORDER)
|
||||
.thenComparing(a -> a.getTarget().getType())
|
||||
.thenComparing(e -> e.getTarget().getUniqueId().map(UUID::toString).orElse(""))
|
||||
.thenComparing(a -> a.getTarget().getName(), String.CASE_INSENSITIVE_ORDER)
|
||||
.thenComparing(Action::getDescription);
|
||||
|
||||
/**
|
||||
* Creates a new log entry builder
|
||||
*
|
||||
@@ -131,7 +122,7 @@ public class LoggedAction implements Action {
|
||||
@Override
|
||||
public int compareTo(@NonNull Action other) {
|
||||
Objects.requireNonNull(other, "other");
|
||||
return COMPARATOR.compare(this, other);
|
||||
return ActionComparator.INSTANCE.compare(this, other);
|
||||
}
|
||||
|
||||
public boolean matchesSearch(String query) {
|
||||
@@ -171,13 +162,7 @@ public class LoggedAction implements Action {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int PRIME = 59;
|
||||
int result = 1;
|
||||
result = result * PRIME + getTimestamp().hashCode();
|
||||
result = result * PRIME + getSource().hashCode();
|
||||
result = result * PRIME + getTarget().hashCode();
|
||||
result = result * PRIME + getDescription().hashCode();
|
||||
return result;
|
||||
return Objects.hash(getTimestamp(), getSource(), getTarget(), getDescription());
|
||||
}
|
||||
|
||||
private static final class SourceImpl implements Source {
|
||||
@@ -265,36 +250,43 @@ public class LoggedAction implements Action {
|
||||
private Target.Type targetType = null;
|
||||
private String description = null;
|
||||
|
||||
@Override
|
||||
public @NonNull Builder timestamp(@NonNull Instant timestamp) {
|
||||
this.timestamp = timestamp.getEpochSecond();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Builder source(@NonNull UUID source) {
|
||||
this.sourceUniqueId = Objects.requireNonNull(source, "source");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Builder sourceName(@NonNull String sourceName) {
|
||||
this.sourceName = Objects.requireNonNull(sourceName, "sourceName");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Builder targetType(Action.Target.Type type) {
|
||||
this.targetType = Objects.requireNonNull(type, "type");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Builder target(UUID target) {
|
||||
this.targetUniqueId = target; // nullable
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Builder targetName(@NonNull String targetName) {
|
||||
this.targetName = Objects.requireNonNull(targetName, "targetName");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Builder description(@NonNull String description) {
|
||||
this.description = Objects.requireNonNull(description, "description");
|
||||
return this;
|
||||
@@ -346,6 +338,8 @@ public class LoggedAction implements Action {
|
||||
}
|
||||
parts.add(context.getKey() + "=" + context.getValue());
|
||||
}
|
||||
} else if (o instanceof Duration) {
|
||||
parts.add(DurationFormatter.CONCISE.format((Duration) o));
|
||||
} else {
|
||||
parts.add(String.valueOf(o));
|
||||
}
|
||||
|
||||
@@ -38,13 +38,13 @@ import me.lucko.luckperms.common.api.implementation.ApiQueryOptionsRegistry;
|
||||
import me.lucko.luckperms.common.api.implementation.ApiTrackManager;
|
||||
import me.lucko.luckperms.common.api.implementation.ApiUserManager;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.event.AbstractEventBus;
|
||||
import me.lucko.luckperms.common.messaging.LuckPermsMessagingService;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
|
||||
import net.luckperms.api.LuckPerms;
|
||||
import net.luckperms.api.actionlog.ActionLogger;
|
||||
import net.luckperms.api.context.ContextManager;
|
||||
import net.luckperms.api.event.EventBus;
|
||||
import net.luckperms.api.messaging.MessagingService;
|
||||
import net.luckperms.api.messenger.MessengerProvider;
|
||||
import net.luckperms.api.metastacking.MetaStackFactory;
|
||||
@@ -140,7 +140,7 @@ public class LuckPermsApiProvider implements LuckPerms {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull EventBus getEventBus() {
|
||||
public @NonNull AbstractEventBus<?> getEventBus() {
|
||||
return this.plugin.getEventDispatcher().getEventBus();
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -116,8 +116,8 @@ public class ApiContextManager implements net.luckperms.api.context.ContextManag
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCache(@NonNull Object subject) {
|
||||
public void signalContextUpdate(@NonNull Object subject) {
|
||||
Objects.requireNonNull(subject, "subject");
|
||||
this.handle.invalidateCache(checkType(subject));
|
||||
this.handle.signalContextUpdate(checkType(subject));
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -115,7 +115,7 @@ public class ApiGroupManager extends ApiAbstractManager<Group, net.luckperms.api
|
||||
@Deprecated
|
||||
public @NonNull CompletableFuture<List<HeldNode<String>>> getWithPermission(@NonNull String permission) {
|
||||
Objects.requireNonNull(permission, "permission");
|
||||
return (CompletableFuture) this.plugin.getStorage().getUsersWithPermission(StandardNodeMatchers.key(permission));
|
||||
return (CompletableFuture) this.plugin.getStorage().searchGroupNodes(StandardNodeMatchers.key(permission));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -123,7 +123,7 @@ public class ApiGroupManager extends ApiAbstractManager<Group, net.luckperms.api
|
||||
public @NonNull <T extends Node> CompletableFuture<Map<String, Collection<T>>> searchAll(@NonNull NodeMatcher<? extends T> matcher) {
|
||||
Objects.requireNonNull(matcher, "matcher");
|
||||
ConstraintNodeMatcher<? extends T> constraint = (ConstraintNodeMatcher<? extends T>) matcher;
|
||||
return this.plugin.getStorage().getGroupsWithPermission(constraint).thenApply(list -> {
|
||||
return this.plugin.getStorage().searchGroupNodes(constraint).thenApply(list -> {
|
||||
ImmutableListMultimap.Builder<String, T> builder = ImmutableListMultimap.builder();
|
||||
for (NodeEntry<String, ? extends T> row : list) {
|
||||
builder.put(row.getHolder(), row.getNode());
|
||||
|
||||
+1
-1
@@ -174,7 +174,7 @@ public class ApiPermissionHolder implements net.luckperms.api.model.PermissionHo
|
||||
|
||||
@Override
|
||||
public @NonNull Map<ImmutableContextSet, Collection<Node>> toMap() {
|
||||
return ApiPermissionHolder.this.handle.getData(this.dataType).immutable().asMap();
|
||||
return ApiPermissionHolder.this.handle.getData(this.dataType).asMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -52,7 +52,7 @@ public class ApiPlatform implements Platform, PluginMetadata {
|
||||
|
||||
@Override
|
||||
public @NonNull String getApiVersion() {
|
||||
return "5.0";
|
||||
return "5.2";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -35,7 +35,6 @@ import me.lucko.luckperms.common.verbose.event.MetaCheckEvent;
|
||||
import net.luckperms.api.model.data.DataMutateResult;
|
||||
import net.luckperms.api.model.data.DataType;
|
||||
import net.luckperms.api.node.NodeEqualityPredicate;
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
@@ -72,10 +71,7 @@ public class ApiUser extends ApiPermissionHolder implements net.luckperms.api.mo
|
||||
|
||||
@Override
|
||||
public @NonNull String getPrimaryGroup() {
|
||||
QueryOptions queryOptions = this.handle.getPlugin().getQueryOptionsForUser(this.handle)
|
||||
.orElseGet(() -> this.handle.getPlugin().getContextManager().getStaticQueryOptions());
|
||||
|
||||
String value = this.handle.getCachedData().getMetaData(queryOptions).getPrimaryGroup(MetaCheckEvent.Origin.LUCKPERMS_API);
|
||||
String value = this.handle.getCachedData().getMetaData(this.handle.getQueryOptions()).getPrimaryGroup(MetaCheckEvent.Origin.LUCKPERMS_API);
|
||||
Objects.requireNonNull(value, "value"); // assert nonnull
|
||||
return value;
|
||||
}
|
||||
|
||||
+8
-2
@@ -114,6 +114,12 @@ public class ApiUserManager extends ApiAbstractManager<User, net.luckperms.api.m
|
||||
return this.plugin.getStorage().savePlayerData(uniqueId, username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CompletableFuture<Void> deletePlayerData(@NonNull UUID uniqueId) {
|
||||
Objects.requireNonNull(uniqueId, "uniqueId");
|
||||
return this.plugin.getStorage().deletePlayerData(uniqueId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CompletableFuture<Set<UUID>> getUniqueUsers() {
|
||||
return this.plugin.getStorage().getUniqueUsers();
|
||||
@@ -124,7 +130,7 @@ public class ApiUserManager extends ApiAbstractManager<User, net.luckperms.api.m
|
||||
@Deprecated
|
||||
public @NonNull CompletableFuture<List<HeldNode<UUID>>> getWithPermission(@NonNull String permission) {
|
||||
Objects.requireNonNull(permission, "permission");
|
||||
return (CompletableFuture) this.plugin.getStorage().getUsersWithPermission(StandardNodeMatchers.key(permission));
|
||||
return (CompletableFuture) this.plugin.getStorage().searchUserNodes(StandardNodeMatchers.key(permission));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -132,7 +138,7 @@ public class ApiUserManager extends ApiAbstractManager<User, net.luckperms.api.m
|
||||
public <T extends Node> @NonNull CompletableFuture<Map<UUID, Collection<T>>> searchAll(@NonNull NodeMatcher<? extends T> matcher) {
|
||||
Objects.requireNonNull(matcher, "matcher");
|
||||
ConstraintNodeMatcher<? extends T> constraint = (ConstraintNodeMatcher<? extends T>) matcher;
|
||||
return this.plugin.getStorage().getUsersWithPermission(constraint).thenApply(list -> {
|
||||
return this.plugin.getStorage().searchUserNodes(constraint).thenApply(list -> {
|
||||
ImmutableListMultimap.Builder<UUID, T> builder = ImmutableListMultimap.builder();
|
||||
for (NodeEntry<UUID, ? extends T> row : list) {
|
||||
builder.put(row.getHolder(), row.getNode());
|
||||
|
||||
@@ -27,6 +27,8 @@ package me.lucko.luckperms.common.backup;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import me.lucko.luckperms.common.http.AbstractHttpClient;
|
||||
import me.lucko.luckperms.common.http.UnsuccessfulRequestException;
|
||||
import me.lucko.luckperms.common.locale.message.Message;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.Track;
|
||||
@@ -42,8 +44,10 @@ import me.lucko.luckperms.common.util.gson.JArray;
|
||||
import me.lucko.luckperms.common.util.gson.JObject;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -71,20 +75,20 @@ import java.util.zip.GZIPOutputStream;
|
||||
/**
|
||||
* Handles export operations
|
||||
*/
|
||||
public class Exporter implements Runnable {
|
||||
public abstract class Exporter implements Runnable {
|
||||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
|
||||
|
||||
private final LuckPermsPlugin plugin;
|
||||
protected final LuckPermsPlugin plugin;
|
||||
private final Sender executor;
|
||||
private final Path filePath;
|
||||
private final boolean includeUsers;
|
||||
private final ProgressLogger log;
|
||||
private final boolean includeGroups;
|
||||
protected final ProgressLogger log;
|
||||
|
||||
public Exporter(LuckPermsPlugin plugin, Sender executor, Path filePath, boolean includeUsers) {
|
||||
protected Exporter(LuckPermsPlugin plugin, Sender executor, boolean includeUsers, boolean includeGroups) {
|
||||
this.plugin = plugin;
|
||||
this.executor = executor;
|
||||
this.filePath = filePath;
|
||||
this.includeUsers = includeUsers;
|
||||
this.includeGroups = includeGroups;
|
||||
|
||||
this.log = new ProgressLogger(Message.EXPORT_LOG, Message.EXPORT_LOG_PROGRESS, null);
|
||||
this.log.addListener(plugin.getConsoleSender());
|
||||
@@ -93,34 +97,30 @@ public class Exporter implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
JsonObject file = new JsonObject();
|
||||
file.add("metadata", new JObject()
|
||||
JsonObject json = new JsonObject();
|
||||
json.add("metadata", new JObject()
|
||||
.add("generatedBy", this.executor.getNameWithLocation())
|
||||
.add("generatedAt", DATE_FORMAT.format(new Date(System.currentTimeMillis())))
|
||||
.toJson());
|
||||
|
||||
this.log.log("Gathering group data...");
|
||||
file.add("groups", exportGroups());
|
||||
if (this.includeGroups) {
|
||||
this.log.log("Gathering group data...");
|
||||
json.add("groups", exportGroups());
|
||||
|
||||
this.log.log("Gathering track data...");
|
||||
file.add("tracks", exportTracks());
|
||||
this.log.log("Gathering track data...");
|
||||
json.add("tracks", exportTracks());
|
||||
}
|
||||
|
||||
if (this.includeUsers) {
|
||||
this.log.log("Gathering user data...");
|
||||
file.add("users", exportUsers());
|
||||
json.add("users", exportUsers());
|
||||
}
|
||||
|
||||
this.log.log("Finished gathering data, writing file...");
|
||||
|
||||
try (BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(this.filePath)), StandardCharsets.UTF_8))) {
|
||||
GsonProvider.prettyPrinting().toJson(file, out);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
this.log.getListeners().forEach(l -> Message.LOG_EXPORT_SUCCESS.send(l, this.filePath.toFile().getAbsolutePath()));
|
||||
processOutput(json);
|
||||
}
|
||||
|
||||
protected abstract void processOutput(JsonObject json);
|
||||
|
||||
private JsonObject exportGroups() {
|
||||
JsonObject out = new JsonObject();
|
||||
List<Group> groups = this.plugin.getGroupManager().getAll().values().stream()
|
||||
@@ -219,4 +219,57 @@ public class Exporter implements Runnable {
|
||||
}
|
||||
return outJson;
|
||||
}
|
||||
|
||||
public static final class SaveFile extends Exporter {
|
||||
private final Path filePath;
|
||||
|
||||
public SaveFile(LuckPermsPlugin plugin, Sender executor, Path filePath, boolean includeUsers, boolean includeGroups) {
|
||||
super(plugin, executor, includeUsers, includeGroups);
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processOutput(JsonObject json) {
|
||||
this.log.log("Finished gathering data, writing file...");
|
||||
|
||||
try (BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(this.filePath)), StandardCharsets.UTF_8))) {
|
||||
GsonProvider.prettyPrinting().toJson(json, out);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
this.log.getListeners().forEach(l -> Message.LOG_EXPORT_SUCCESS.send(l, this.filePath.toFile().getAbsolutePath()));
|
||||
}
|
||||
}
|
||||
|
||||
public static final class WebUpload extends Exporter {
|
||||
private final String label;
|
||||
|
||||
public WebUpload(LuckPermsPlugin plugin, Sender executor, boolean includeUsers, boolean includeGroups, String label) {
|
||||
super(plugin, executor, includeUsers, includeGroups);
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processOutput(JsonObject json) {
|
||||
this.log.log("Finished gathering data, uploading data...");
|
||||
|
||||
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
|
||||
try (Writer writer = new OutputStreamWriter(new GZIPOutputStream(bytesOut), StandardCharsets.UTF_8)) {
|
||||
GsonProvider.prettyPrinting().toJson(json, writer);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
String pasteId = this.plugin.getBytebin().postContent(bytesOut.toByteArray(), AbstractHttpClient.JSON_TYPE, false).key();
|
||||
this.log.getListeners().forEach(l -> Message.EXPORT_CODE.send(l, pasteId, this.label, pasteId));
|
||||
} catch (UnsuccessfulRequestException e) {
|
||||
this.log.getListeners().forEach(l -> Message.EXPORT_HTTP_REQUEST_FAILURE.send(l, e.getResponse().code(), e.getResponse().message()));
|
||||
} catch (IOException e) {
|
||||
new RuntimeException("Error uploading data to bytebin", e).printStackTrace();
|
||||
this.log.getListeners().forEach(Message.EXPORT_HTTP_UNKNOWN_FAILURE::send);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,7 @@ import net.luckperms.api.model.data.DataType;
|
||||
import net.luckperms.api.node.Node;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -66,8 +67,9 @@ public class Importer implements Runnable {
|
||||
private final LuckPermsPlugin plugin;
|
||||
private final Set<Sender> notify;
|
||||
private final JsonObject data;
|
||||
private final boolean merge;
|
||||
|
||||
public Importer(LuckPermsPlugin plugin, Sender executor, JsonObject data) {
|
||||
public Importer(LuckPermsPlugin plugin, Sender executor, JsonObject data, boolean merge) {
|
||||
this.plugin = plugin;
|
||||
|
||||
if (executor.isConsole()) {
|
||||
@@ -76,6 +78,7 @@ public class Importer implements Runnable {
|
||||
this.notify = ImmutableSet.of(executor, plugin.getConsoleSender());
|
||||
}
|
||||
this.data = data;
|
||||
this.merge = merge;
|
||||
}
|
||||
|
||||
private static final class UserData {
|
||||
@@ -92,7 +95,11 @@ public class Importer implements Runnable {
|
||||
|
||||
private void processGroup(String groupName, Set<Node> nodes) {
|
||||
Group group = this.plugin.getStorage().createAndLoadGroup(groupName, CreationCause.INTERNAL).join();
|
||||
group.setNodes(DataType.NORMAL, nodes);
|
||||
if (this.merge) {
|
||||
group.mergeNodes(DataType.NORMAL, nodes);
|
||||
} else {
|
||||
group.setNodes(DataType.NORMAL, nodes);
|
||||
}
|
||||
this.plugin.getStorage().saveGroup(group);
|
||||
}
|
||||
|
||||
@@ -107,11 +114,23 @@ public class Importer implements Runnable {
|
||||
if (userData.primaryGroup != null) {
|
||||
user.getPrimaryGroup().setStoredValue(userData.primaryGroup);
|
||||
}
|
||||
user.setNodes(DataType.NORMAL, userData.nodes);
|
||||
if (this.merge) {
|
||||
user.mergeNodes(DataType.NORMAL, userData.nodes);
|
||||
} else {
|
||||
user.setNodes(DataType.NORMAL, userData.nodes);
|
||||
}
|
||||
this.plugin.getStorage().saveUser(user).join();
|
||||
this.plugin.getUserManager().getHouseKeeper().cleanup(user.getUniqueId());
|
||||
}
|
||||
|
||||
private Set<Map.Entry<String, JsonElement>> getDataSection(String id) {
|
||||
if (this.data.has(id)) {
|
||||
return this.data.get(id).getAsJsonObject().entrySet();
|
||||
} else {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
long startTime = System.currentTimeMillis();
|
||||
@@ -126,16 +145,16 @@ public class Importer implements Runnable {
|
||||
Map<String, List<String>> tracks = new HashMap<>();
|
||||
Map<UUID, UserData> users = new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, JsonElement> group : this.data.get("groups").getAsJsonObject().entrySet()) {
|
||||
for (Map.Entry<String, JsonElement> group : getDataSection("groups")) {
|
||||
groups.put(group.getKey(), NodeJsonSerializer.deserializeNodes(group.getValue().getAsJsonObject().get("nodes").getAsJsonArray()));
|
||||
}
|
||||
for (Map.Entry<String, JsonElement> track : this.data.get("tracks").getAsJsonObject().entrySet()) {
|
||||
for (Map.Entry<String, JsonElement> track : getDataSection("tracks")) {
|
||||
JsonArray trackGroups = track.getValue().getAsJsonObject().get("groups").getAsJsonArray();
|
||||
List<String> trackGroupsList = new ArrayList<>();
|
||||
trackGroups.forEach(g -> trackGroupsList.add(g.getAsString()));
|
||||
tracks.put(track.getKey(), trackGroupsList);
|
||||
}
|
||||
for (Map.Entry<String, JsonElement> user : this.data.get("users").getAsJsonObject().entrySet()) {
|
||||
for (Map.Entry<String, JsonElement> user : getDataSection("users")) {
|
||||
JsonObject jsonData = user.getValue().getAsJsonObject();
|
||||
|
||||
UUID uuid = UUID.fromString(user.getKey());
|
||||
|
||||
+6
@@ -45,7 +45,13 @@ public class PreparedStatementBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public PreparedStatementBuilder append(char c) {
|
||||
this.sb.append(c);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PreparedStatementBuilder variable(String variable) {
|
||||
this.sb.append('?');
|
||||
this.variables.add(variable);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public class UpdateAction implements Action {
|
||||
|
||||
@Override
|
||||
public void appendSql(PreparedStatementBuilder builder) {
|
||||
builder.append("UPDATE {table} SET " + this.field.getSqlName() + "=?");
|
||||
builder.append("UPDATE {table} SET " + this.field.getSqlName() + "=");
|
||||
builder.variable(this.value);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -55,9 +55,9 @@ public class Constraint {
|
||||
|
||||
public void appendSql(PreparedStatementBuilder builder, String field) {
|
||||
// e.g. field LIKE ?
|
||||
builder.append(field + " ");
|
||||
builder.append(field).append(' ');
|
||||
this.comparison.appendSql(builder);
|
||||
builder.append(" ?");
|
||||
builder.append(' ');
|
||||
builder.variable(this.expressionValue);
|
||||
}
|
||||
|
||||
|
||||
+24
-17
@@ -29,6 +29,7 @@ import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter;
|
||||
import me.lucko.luckperms.common.plugin.scheduler.SchedulerTask;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -75,7 +76,7 @@ public abstract class BufferedRequest<T> {
|
||||
if (this.processor != null) {
|
||||
try {
|
||||
return this.processor.extendAndGetFuture();
|
||||
} catch (IllegalStateException e) {
|
||||
} catch (ProcessorAlreadyRanException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
@@ -101,8 +102,8 @@ public abstract class BufferedRequest<T> {
|
||||
*/
|
||||
protected abstract T perform();
|
||||
|
||||
private static class Processor<R> {
|
||||
private Supplier<R> supplier;
|
||||
private static final class Processor<R> {
|
||||
private final Supplier<R> supplier;
|
||||
|
||||
private final long delay;
|
||||
private final TimeUnit unit;
|
||||
@@ -110,11 +111,11 @@ public abstract class BufferedRequest<T> {
|
||||
private final SchedulerAdapter schedulerAdapter;
|
||||
|
||||
private final Object[] mutex = new Object[0];
|
||||
private CompletableFuture<R> future = new CompletableFuture<>();
|
||||
private final CompletableFuture<R> future = new CompletableFuture<>();
|
||||
private boolean usable = true;
|
||||
|
||||
private SchedulerTask scheduledTask;
|
||||
private BoundTask boundTask = null;
|
||||
private CompletionTask boundTask = null;
|
||||
|
||||
Processor(Supplier<R> supplier, long delay, TimeUnit unit, SchedulerAdapter schedulerAdapter) {
|
||||
this.supplier = supplier;
|
||||
@@ -122,19 +123,28 @@ public abstract class BufferedRequest<T> {
|
||||
this.unit = unit;
|
||||
this.schedulerAdapter = schedulerAdapter;
|
||||
|
||||
rescheduleTask();
|
||||
scheduleTask();
|
||||
}
|
||||
|
||||
private void rescheduleTask() {
|
||||
private void rescheduleTask() throws ProcessorAlreadyRanException {
|
||||
synchronized (this.mutex) {
|
||||
if (!this.usable) {
|
||||
throw new IllegalStateException("Processor not usable");
|
||||
throw new ProcessorAlreadyRanException();
|
||||
}
|
||||
if (this.scheduledTask != null) {
|
||||
this.scheduledTask.cancel();
|
||||
}
|
||||
this.boundTask = new BoundTask();
|
||||
scheduleTask();
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleTask() {
|
||||
this.boundTask = new CompletionTask();
|
||||
try {
|
||||
this.scheduledTask = this.schedulerAdapter.asyncLater(this.boundTask, this.delay, this.unit);
|
||||
} catch (RejectedExecutionException e) {
|
||||
// If we can't schedule the completion in the future, just do it now.
|
||||
this.boundTask.run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,12 +152,12 @@ public abstract class BufferedRequest<T> {
|
||||
return this.future;
|
||||
}
|
||||
|
||||
CompletableFuture<R> extendAndGetFuture() {
|
||||
CompletableFuture<R> extendAndGetFuture() throws ProcessorAlreadyRanException {
|
||||
rescheduleTask();
|
||||
return this.future;
|
||||
}
|
||||
|
||||
private final class BoundTask implements Runnable {
|
||||
private final class CompletionTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (Processor.this.mutex) {
|
||||
@@ -172,14 +182,11 @@ public abstract class BufferedRequest<T> {
|
||||
new RuntimeException("Processor " + Processor.this.supplier + " threw an exception whilst computing a result", e).printStackTrace();
|
||||
Processor.this.future.completeExceptionally(e);
|
||||
}
|
||||
|
||||
// allow supplier and future to be GCed
|
||||
Processor.this.supplier = null;
|
||||
Processor.this.future = null;
|
||||
Processor.this.scheduledTask = null;
|
||||
Processor.this.boundTask = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ProcessorAlreadyRanException extends Exception {
|
||||
|
||||
}
|
||||
|
||||
|
||||
+64
-222
@@ -25,19 +25,16 @@
|
||||
|
||||
package me.lucko.luckperms.common.cacheddata;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
|
||||
import me.lucko.luckperms.common.cache.MRUCache;
|
||||
import me.lucko.luckperms.common.cache.LoadingMap;
|
||||
import me.lucko.luckperms.common.cacheddata.type.MetaAccumulator;
|
||||
import me.lucko.luckperms.common.cacheddata.type.MetaCache;
|
||||
import me.lucko.luckperms.common.cacheddata.type.PermissionCache;
|
||||
import me.lucko.luckperms.common.calculator.CalculatorFactory;
|
||||
import me.lucko.luckperms.common.calculator.PermissionCalculator;
|
||||
import me.lucko.luckperms.common.metastacking.SimpleMetaStack;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.util.CaffeineFactory;
|
||||
|
||||
import net.luckperms.api.cacheddata.CachedData;
|
||||
import net.luckperms.api.cacheddata.CachedDataManager;
|
||||
import net.luckperms.api.cacheddata.CachedMetaData;
|
||||
import net.luckperms.api.cacheddata.CachedPermissionData;
|
||||
@@ -46,31 +43,28 @@ import net.luckperms.api.node.ChatMetaType;
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
/**
|
||||
* Abstract implementation of {@link CachedDataManager}.
|
||||
*/
|
||||
public abstract class AbstractCachedDataManager implements CachedDataManager {
|
||||
|
||||
/**
|
||||
* The plugin instance
|
||||
*/
|
||||
private final LuckPermsPlugin plugin;
|
||||
|
||||
private final Permission permissionDataManager;
|
||||
private final Meta metaDataManager;
|
||||
private final AbstractContainer<PermissionCache, CachedPermissionData> permission;
|
||||
private final AbstractContainer<MetaCache, CachedMetaData> meta;
|
||||
|
||||
protected AbstractCachedDataManager(LuckPermsPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.permissionDataManager = new Permission();
|
||||
this.metaDataManager = new Meta();
|
||||
this.permission = new AbstractContainer<>(this::calculatePermissions);
|
||||
this.meta = new AbstractContainer<>(this::calculateMeta);
|
||||
}
|
||||
|
||||
public LuckPermsPlugin getPlugin() {
|
||||
@@ -79,22 +73,22 @@ public abstract class AbstractCachedDataManager implements CachedDataManager {
|
||||
|
||||
@Override
|
||||
public @NonNull Container<CachedPermissionData> permissionData() {
|
||||
return this.permissionDataManager;
|
||||
return this.permission;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Container<CachedMetaData> metaData() {
|
||||
return this.metaDataManager;
|
||||
return this.meta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull PermissionCache getPermissionData(@NonNull QueryOptions queryOptions) {
|
||||
return this.permissionDataManager.get(queryOptions);
|
||||
return this.permission.get(queryOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull MetaCache getMetaData(@NonNull QueryOptions queryOptions) {
|
||||
return this.metaDataManager.get(queryOptions);
|
||||
return this.meta.get(queryOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -139,11 +133,13 @@ public abstract class AbstractCachedDataManager implements CachedDataManager {
|
||||
|
||||
/**
|
||||
* Resolves the owners permissions data for the given {@link QueryOptions}.
|
||||
*
|
||||
*
|
||||
* @param mapFactory a function to create a map instance to return the results in
|
||||
* @param queryOptions the query options
|
||||
* @return a map of permissions to back the {@link PermissionCache}
|
||||
* @param <M> the map type
|
||||
* @return the resolved permissions
|
||||
*/
|
||||
protected abstract Map<String, Boolean> resolvePermissions(QueryOptions queryOptions);
|
||||
protected abstract <M extends Map<String, Boolean>> M resolvePermissions(IntFunction<M> mapFactory, QueryOptions queryOptions);
|
||||
|
||||
/**
|
||||
* Resolves the owners meta data for the given {@link QueryOptions}.
|
||||
@@ -153,245 +149,109 @@ public abstract class AbstractCachedDataManager implements CachedDataManager {
|
||||
*/
|
||||
protected abstract void resolveMeta(MetaAccumulator accumulator, QueryOptions queryOptions);
|
||||
|
||||
private PermissionCache calculatePermissions(QueryOptions queryOptions, PermissionCache data) {
|
||||
private PermissionCache calculatePermissions(QueryOptions queryOptions) {
|
||||
Objects.requireNonNull(queryOptions, "queryOptions");
|
||||
CacheMetadata metadata = getMetadataForQueryOptions(queryOptions);
|
||||
|
||||
if (data == null) {
|
||||
CacheMetadata metadata = getMetadataForQueryOptions(queryOptions);
|
||||
data = new PermissionCache(queryOptions, metadata, getCalculatorFactory());
|
||||
}
|
||||
|
||||
data.setPermissions(resolvePermissions(queryOptions));
|
||||
return data;
|
||||
ConcurrentHashMap<String, Boolean> sourcePermissions = resolvePermissions(ConcurrentHashMap::new, queryOptions);
|
||||
return new PermissionCache(queryOptions, metadata, getCalculatorFactory(), sourcePermissions);
|
||||
}
|
||||
|
||||
private MetaCache calculateMeta(QueryOptions queryOptions, MetaCache data) {
|
||||
private MetaCache calculateMeta(QueryOptions queryOptions) {
|
||||
Objects.requireNonNull(queryOptions, "queryOptions");
|
||||
|
||||
if (data == null) {
|
||||
CacheMetadata metadata = getMetadataForQueryOptions(queryOptions);
|
||||
data = new MetaCache(this.plugin, queryOptions, metadata);
|
||||
}
|
||||
CacheMetadata metadata = getMetadataForQueryOptions(queryOptions);
|
||||
|
||||
MetaAccumulator accumulator = newAccumulator(queryOptions);
|
||||
resolveMeta(accumulator, queryOptions);
|
||||
data.loadMeta(accumulator);
|
||||
|
||||
return data;
|
||||
return new MetaCache(this.plugin, queryOptions, metadata, accumulator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void invalidate() {
|
||||
this.permissionDataManager.invalidate();
|
||||
this.metaDataManager.invalidate();
|
||||
this.permission.invalidate();
|
||||
this.meta.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void invalidatePermissionCalculators() {
|
||||
this.permissionDataManager.cache.synchronous().asMap().values().forEach(PermissionCache::invalidateCache);
|
||||
this.permission.cache.values().forEach(PermissionCache::invalidateCache);
|
||||
}
|
||||
|
||||
public final void performCacheCleanup() {
|
||||
this.permissionDataManager.cache.synchronous().cleanUp();
|
||||
this.metaDataManager.cache.synchronous().cleanUp();
|
||||
this.permission.cleanup();
|
||||
this.meta.cleanup();
|
||||
}
|
||||
|
||||
private final class Permission extends MRUCache<RecentPermissionData> implements Container<CachedPermissionData> {
|
||||
private final AsyncLoadingCache<QueryOptions, PermissionCache> cache = CaffeineFactory.newBuilder()
|
||||
.expireAfterAccess(2, TimeUnit.MINUTES)
|
||||
.buildAsync(new PermissionCacheLoader());
|
||||
private static final class AbstractContainer<C extends I, I extends CachedData> implements Container<I> {
|
||||
private final Function<QueryOptions, C> cacheLoader;
|
||||
private final LoadingMap<QueryOptions, C> cache;
|
||||
|
||||
public AbstractContainer(Function<QueryOptions, C> cacheLoader) {
|
||||
this.cacheLoader = cacheLoader;
|
||||
this.cache = LoadingMap.of(this.cacheLoader);
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
this.cache.values().removeIf(value -> ((UsageTracked) value).usedSince(TimeUnit.MINUTES.toMillis(2)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull PermissionCache get(@NonNull QueryOptions queryOptions) {
|
||||
public @NonNull C get(@NonNull QueryOptions queryOptions) {
|
||||
Objects.requireNonNull(queryOptions, "queryOptions");
|
||||
|
||||
RecentPermissionData recent = getRecent();
|
||||
if (recent != null && queryOptions.equals(recent.queryOptions)) {
|
||||
return recent.permissionData;
|
||||
}
|
||||
|
||||
int modCount = modCount();
|
||||
PermissionCache data = this.cache.synchronous().get(queryOptions);
|
||||
offerRecent(modCount, new RecentPermissionData(queryOptions, data));
|
||||
|
||||
//noinspection ConstantConditions
|
||||
C data = this.cache.get(queryOptions);
|
||||
((UsageTracked) data).recordUsage();
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull PermissionCache calculate(@NonNull QueryOptions queryOptions) {
|
||||
public @NonNull C calculate(@NonNull QueryOptions queryOptions) {
|
||||
Objects.requireNonNull(queryOptions, "queryOptions");
|
||||
return calculatePermissions(queryOptions, null);
|
||||
return this.cacheLoader.apply(queryOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recalculate(@NonNull QueryOptions queryOptions) {
|
||||
Objects.requireNonNull(queryOptions, "queryOptions");
|
||||
this.cache.synchronous().refresh(queryOptions);
|
||||
clearRecent();
|
||||
CompletableFuture.runAsync(() -> {
|
||||
final C value = this.cacheLoader.apply(queryOptions);
|
||||
this.cache.put(queryOptions, value);
|
||||
}, CaffeineFactory.executor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CompletableFuture<? extends PermissionCache> reload(@NonNull QueryOptions queryOptions) {
|
||||
public @NonNull CompletableFuture<? extends C> reload(@NonNull QueryOptions queryOptions) {
|
||||
Objects.requireNonNull(queryOptions, "queryOptions");
|
||||
|
||||
// get the previous value - to use when recalculating
|
||||
CompletableFuture<PermissionCache> previous = this.cache.getIfPresent(queryOptions);
|
||||
// invalidate the previous value until we're done recalculating
|
||||
this.cache.remove(queryOptions);
|
||||
|
||||
// invalidate any previous setting
|
||||
this.cache.synchronous().invalidate(queryOptions);
|
||||
clearRecent();
|
||||
|
||||
// if the previous value is already calculated, use it when recalculating.
|
||||
PermissionCache value = getIfReady(previous);
|
||||
if (value != null) {
|
||||
return this.cache.get(queryOptions, c -> calculatePermissions(c, value));
|
||||
}
|
||||
|
||||
// otherwise, just calculate a new value
|
||||
return this.cache.get(queryOptions);
|
||||
// request recalculation from the cache
|
||||
return CompletableFuture.supplyAsync(() -> this.cache.get(queryOptions), CaffeineFactory.executor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recalculate() {
|
||||
Set<QueryOptions> keys = this.cache.synchronous().asMap().keySet();
|
||||
Set<QueryOptions> keys = this.cache.keySet();
|
||||
keys.forEach(this::recalculate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CompletableFuture<Void> reload() {
|
||||
Set<QueryOptions> keys = this.cache.synchronous().asMap().keySet();
|
||||
Set<QueryOptions> keys = this.cache.keySet();
|
||||
return CompletableFuture.allOf(keys.stream().map(this::reload).toArray(CompletableFuture[]::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate(@NonNull QueryOptions queryOptions) {
|
||||
Objects.requireNonNull(queryOptions, "queryOptions");
|
||||
this.cache.synchronous().invalidate(queryOptions);
|
||||
clearRecent();
|
||||
this.cache.remove(queryOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
this.cache.synchronous().invalidateAll();
|
||||
clearRecent();
|
||||
}
|
||||
}
|
||||
|
||||
private final class Meta extends MRUCache<RecentMetaData> implements Container<CachedMetaData> {
|
||||
private final AsyncLoadingCache<QueryOptions, MetaCache> cache = CaffeineFactory.newBuilder()
|
||||
.expireAfterAccess(2, TimeUnit.MINUTES)
|
||||
.buildAsync(new MetaCacheLoader());
|
||||
|
||||
@Override
|
||||
public @NonNull MetaCache get(@NonNull QueryOptions queryOptions) {
|
||||
Objects.requireNonNull(queryOptions, "queryOptions");
|
||||
|
||||
RecentMetaData recent = getRecent();
|
||||
if (recent != null && queryOptions.equals(recent.queryOptions)) {
|
||||
return recent.metaData;
|
||||
}
|
||||
|
||||
int modCount = modCount();
|
||||
MetaCache data = this.cache.synchronous().get(queryOptions);
|
||||
offerRecent(modCount, new RecentMetaData(queryOptions, data));
|
||||
|
||||
//noinspection ConstantConditions
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull MetaCache calculate(@NonNull QueryOptions queryOptions) {
|
||||
Objects.requireNonNull(queryOptions, "queryOptions");
|
||||
return calculateMeta(queryOptions, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recalculate(@NonNull QueryOptions queryOptions) {
|
||||
Objects.requireNonNull(queryOptions, "queryOptions");
|
||||
this.cache.synchronous().refresh(queryOptions);
|
||||
clearRecent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CompletableFuture<? extends MetaCache> reload(@NonNull QueryOptions queryOptions) {
|
||||
Objects.requireNonNull(queryOptions, "queryOptions");
|
||||
|
||||
// get the previous value - to use when recalculating
|
||||
CompletableFuture<MetaCache> previous = this.cache.getIfPresent(queryOptions);
|
||||
|
||||
// invalidate any previous setting
|
||||
this.cache.synchronous().invalidate(queryOptions);
|
||||
clearRecent();
|
||||
|
||||
// if the previous value is already calculated, use it when recalculating.
|
||||
MetaCache value = getIfReady(previous);
|
||||
if (value != null) {
|
||||
return this.cache.get(queryOptions, c -> calculateMeta(c, value));
|
||||
}
|
||||
|
||||
// otherwise, just calculate a new value
|
||||
return this.cache.get(queryOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recalculate() {
|
||||
Set<QueryOptions> keys = this.cache.synchronous().asMap().keySet();
|
||||
keys.forEach(this::recalculate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CompletableFuture<Void> reload() {
|
||||
Set<QueryOptions> keys = this.cache.synchronous().asMap().keySet();
|
||||
return CompletableFuture.allOf(keys.stream().map(this::reload).toArray(CompletableFuture[]::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate(@NonNull QueryOptions queryOptions) {
|
||||
Objects.requireNonNull(queryOptions, "queryOptions");
|
||||
this.cache.synchronous().invalidate(queryOptions);
|
||||
clearRecent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
this.cache.synchronous().invalidateAll();
|
||||
clearRecent();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isReady(@Nullable CompletableFuture<?> future) {
|
||||
return (future != null) && future.isDone()
|
||||
&& !future.isCompletedExceptionally()
|
||||
&& (future.join() != null);
|
||||
}
|
||||
|
||||
/** Returns the current value or null if either not done or failed. */
|
||||
private static <V> V getIfReady(@Nullable CompletableFuture<V> future) {
|
||||
return isReady(future) ? future.join() : null;
|
||||
}
|
||||
|
||||
private final class PermissionCacheLoader implements CacheLoader<QueryOptions, PermissionCache> {
|
||||
@Override
|
||||
public PermissionCache load(@NonNull QueryOptions queryOptions) {
|
||||
return calculatePermissions(queryOptions, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionCache reload(@NonNull QueryOptions queryOptions, @NonNull PermissionCache oldData) {
|
||||
return calculatePermissions(queryOptions, oldData);
|
||||
}
|
||||
}
|
||||
|
||||
private final class MetaCacheLoader implements CacheLoader<QueryOptions, MetaCache> {
|
||||
@Override
|
||||
public MetaCache load(@NonNull QueryOptions queryOptions) {
|
||||
return calculateMeta(queryOptions, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaCache reload(@NonNull QueryOptions queryOptions, @NonNull MetaCache oldData) {
|
||||
return calculateMeta(queryOptions, oldData);
|
||||
this.cache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,37 +260,19 @@ public abstract class AbstractCachedDataManager implements CachedDataManager {
|
||||
MetaStackDefinition.PREFIX_STACK_KEY :
|
||||
MetaStackDefinition.SUFFIX_STACK_KEY
|
||||
).orElse(null);
|
||||
|
||||
if (stack == null) {
|
||||
stack = getDefaultMetaStackDefinition(type);
|
||||
}
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
private MetaAccumulator newAccumulator(QueryOptions queryOptions) {
|
||||
return new MetaAccumulator(
|
||||
new SimpleMetaStack(getMetaStackDefinition(queryOptions, ChatMetaType.PREFIX), ChatMetaType.PREFIX),
|
||||
new SimpleMetaStack(getMetaStackDefinition(queryOptions, ChatMetaType.SUFFIX), ChatMetaType.SUFFIX)
|
||||
getMetaStackDefinition(queryOptions, ChatMetaType.PREFIX),
|
||||
getMetaStackDefinition(queryOptions, ChatMetaType.SUFFIX)
|
||||
);
|
||||
}
|
||||
|
||||
private static final class RecentPermissionData {
|
||||
final QueryOptions queryOptions;
|
||||
final PermissionCache permissionData;
|
||||
|
||||
RecentPermissionData(QueryOptions queryOptions, PermissionCache permissionData) {
|
||||
this.queryOptions = queryOptions;
|
||||
this.permissionData = permissionData;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RecentMetaData {
|
||||
final QueryOptions queryOptions;
|
||||
final MetaCache metaData;
|
||||
|
||||
RecentMetaData(QueryOptions queryOptions, MetaCache metaData) {
|
||||
this.queryOptions = queryOptions;
|
||||
this.metaData = metaData;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+3
-2
@@ -35,6 +35,7 @@ import net.luckperms.api.node.ChatMetaType;
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
/**
|
||||
* Holds an easily accessible cache of a holders data in a number of contexts
|
||||
@@ -74,8 +75,8 @@ public abstract class HolderCachedDataManager<T extends PermissionHolder> extend
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Boolean> resolvePermissions(QueryOptions queryOptions) {
|
||||
return this.holder.exportPermissions(queryOptions, true, getPlugin().getConfiguration().get(ConfigKeys.APPLYING_SHORTHAND));
|
||||
protected <M extends Map<String, Boolean>> M resolvePermissions(IntFunction<M> mapFactory, QueryOptions queryOptions) {
|
||||
return this.holder.exportPermissions(mapFactory, queryOptions, true, getPlugin().getConfiguration().get(ConfigKeys.APPLYING_SHORTHAND));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+7
-27
@@ -23,36 +23,16 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.cache;
|
||||
package me.lucko.luckperms.common.cacheddata;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
public abstract class UsageTracked {
|
||||
private long lastUsed = System.currentTimeMillis();
|
||||
|
||||
/**
|
||||
* Implementation of a most-recently-used cache with a mod counter, to prevent race conditions
|
||||
* occurring when the cache is cleared whilst a calculation is taking place.
|
||||
*
|
||||
* @param <T> the cached type
|
||||
*/
|
||||
public abstract class MRUCache<T> {
|
||||
private volatile T recent;
|
||||
private final AtomicInteger modCount = new AtomicInteger();
|
||||
|
||||
protected int modCount() {
|
||||
return this.modCount.get();
|
||||
public void recordUsage() {
|
||||
this.lastUsed = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
protected T getRecent() {
|
||||
return this.recent;
|
||||
}
|
||||
|
||||
protected void offerRecent(int validAt, T offer) {
|
||||
if (validAt == this.modCount.get()) {
|
||||
this.recent = offer;
|
||||
}
|
||||
}
|
||||
|
||||
protected void clearRecent() {
|
||||
this.recent = null;
|
||||
this.modCount.incrementAndGet();
|
||||
public boolean usedSince(long duration) {
|
||||
return this.lastUsed > (System.currentTimeMillis() - duration);
|
||||
}
|
||||
}
|
||||
+34
-21
@@ -29,11 +29,10 @@ import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.metastacking.MetaStack;
|
||||
import me.lucko.luckperms.common.metastacking.SimpleMetaStack;
|
||||
import me.lucko.luckperms.common.node.types.Weight;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
|
||||
import net.luckperms.api.metastacking.MetaStackDefinition;
|
||||
import net.luckperms.api.node.ChatMetaType;
|
||||
import net.luckperms.api.node.Node;
|
||||
import net.luckperms.api.node.types.MetaNode;
|
||||
@@ -55,8 +54,8 @@ public class MetaAccumulator {
|
||||
|
||||
public static MetaAccumulator makeFromConfig(LuckPermsPlugin plugin) {
|
||||
return new MetaAccumulator(
|
||||
new SimpleMetaStack(plugin.getConfiguration().get(ConfigKeys.PREFIX_FORMATTING_OPTIONS), ChatMetaType.PREFIX),
|
||||
new SimpleMetaStack(plugin.getConfiguration().get(ConfigKeys.SUFFIX_FORMATTING_OPTIONS), ChatMetaType.SUFFIX)
|
||||
plugin.getConfiguration().get(ConfigKeys.PREFIX_FORMATTING_OPTIONS),
|
||||
plugin.getConfiguration().get(ConfigKeys.SUFFIX_FORMATTING_OPTIONS)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -80,17 +79,21 @@ public class MetaAccumulator {
|
||||
private int weight = 0;
|
||||
private String primaryGroup;
|
||||
|
||||
private final MetaStack prefixStack;
|
||||
private final MetaStack suffixStack;
|
||||
private final MetaStackDefinition prefixDefinition;
|
||||
private final MetaStackDefinition suffixDefinition;
|
||||
private final MetaStackAccumulator prefixAccumulator;
|
||||
private final MetaStackAccumulator suffixAccumulator;
|
||||
|
||||
public MetaAccumulator(MetaStack prefixStack, MetaStack suffixStack) {
|
||||
Objects.requireNonNull(prefixStack, "prefixStack");
|
||||
Objects.requireNonNull(suffixStack, "suffixStack");
|
||||
public MetaAccumulator(MetaStackDefinition prefixDefinition, MetaStackDefinition suffixDefinition) {
|
||||
Objects.requireNonNull(prefixDefinition, "prefixDefinition");
|
||||
Objects.requireNonNull(suffixDefinition, "suffixDefinition");
|
||||
this.meta = ArrayListMultimap.create();
|
||||
this.prefixes = new TreeMap<>(Comparator.reverseOrder());
|
||||
this.suffixes = new TreeMap<>(Comparator.reverseOrder());
|
||||
this.prefixStack = prefixStack;
|
||||
this.suffixStack = suffixStack;
|
||||
this.prefixDefinition = prefixDefinition;
|
||||
this.suffixDefinition = suffixDefinition;
|
||||
this.prefixAccumulator = new MetaStackAccumulator(this.prefixDefinition, ChatMetaType.PREFIX);
|
||||
this.suffixAccumulator = new MetaStackAccumulator(this.suffixDefinition, ChatMetaType.SUFFIX);
|
||||
}
|
||||
|
||||
private void ensureState(State state) {
|
||||
@@ -125,7 +128,7 @@ public class MetaAccumulator {
|
||||
|
||||
public void accumulateNode(Node n) {
|
||||
ensureState(State.ACCUMULATING);
|
||||
|
||||
|
||||
if (n instanceof MetaNode) {
|
||||
MetaNode mn = (MetaNode) n;
|
||||
this.meta.put(mn.getMetaKey(), mn.getMetaValue());
|
||||
@@ -134,13 +137,13 @@ public class MetaAccumulator {
|
||||
if (n instanceof PrefixNode) {
|
||||
PrefixNode pn = (PrefixNode) n;
|
||||
this.prefixes.putIfAbsent(pn.getPriority(), pn.getMetaValue());
|
||||
this.prefixStack.accumulateToAll(pn);
|
||||
this.prefixAccumulator.offer(pn);
|
||||
}
|
||||
|
||||
|
||||
if (n instanceof SuffixNode) {
|
||||
SuffixNode pn = (SuffixNode) n;
|
||||
this.suffixes.putIfAbsent(pn.getPriority(), pn.getMetaValue());
|
||||
this.suffixStack.accumulateToAll(pn);
|
||||
this.suffixAccumulator.offer(pn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,14 +194,24 @@ public class MetaAccumulator {
|
||||
return this.primaryGroup;
|
||||
}
|
||||
|
||||
public MetaStack getPrefixStack() {
|
||||
public MetaStackDefinition getPrefixDefinition() {
|
||||
ensureState(State.COMPLETE);
|
||||
return this.prefixStack;
|
||||
return this.prefixDefinition;
|
||||
}
|
||||
|
||||
public MetaStack getSuffixStack() {
|
||||
public MetaStackDefinition getSuffixDefinition() {
|
||||
ensureState(State.COMPLETE);
|
||||
return this.suffixStack;
|
||||
return this.suffixDefinition;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
ensureState(State.COMPLETE);
|
||||
return this.prefixAccumulator.toFormattedString();
|
||||
}
|
||||
|
||||
public String getSuffix() {
|
||||
ensureState(State.COMPLETE);
|
||||
return this.suffixAccumulator.toFormattedString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -209,7 +222,7 @@ public class MetaAccumulator {
|
||||
"suffixes=" + this.suffixes + ", " +
|
||||
"weight=" + this.weight + ", " +
|
||||
"primaryGroup=" + this.primaryGroup + ", " +
|
||||
"prefixStack=" + this.prefixStack + ", " +
|
||||
"suffixStack=" + this.suffixStack + ")";
|
||||
"prefixStack=" + this.prefixAccumulator + ", " +
|
||||
"suffixStack=" + this.suffixAccumulator + ")";
|
||||
}
|
||||
}
|
||||
@@ -56,8 +56,8 @@ public class MetaCache extends SimpleMetaCache implements CachedMetaData {
|
||||
/** The object name passed to the verbose handler when checks are made */
|
||||
private final String verboseCheckTarget;
|
||||
|
||||
public MetaCache(LuckPermsPlugin plugin, QueryOptions queryOptions, CacheMetadata metadata) {
|
||||
super(plugin, queryOptions);
|
||||
public MetaCache(LuckPermsPlugin plugin, QueryOptions queryOptions, CacheMetadata metadata, MetaAccumulator sourceMeta) {
|
||||
super(plugin, queryOptions, sourceMeta);
|
||||
this.plugin = plugin;
|
||||
this.metadata = metadata;
|
||||
|
||||
@@ -68,28 +68,40 @@ public class MetaCache extends SimpleMetaCache implements CachedMetaData {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMetaValue(String key, MetaCheckEvent.Origin origin) {
|
||||
String value = super.getMetaValue(key, origin);
|
||||
this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.verboseCheckTarget, this.metadata.getQueryOptions(), key, String.valueOf(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefix(MetaCheckEvent.Origin origin) {
|
||||
String value = super.getPrefix(origin);
|
||||
this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.verboseCheckTarget, this.metadata.getQueryOptions(), Prefix.NODE_KEY, String.valueOf(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSuffix(MetaCheckEvent.Origin origin) {
|
||||
String value = super.getSuffix(origin);
|
||||
this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.verboseCheckTarget, this.metadata.getQueryOptions(), Suffix.NODE_KEY, String.valueOf(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> getMeta(MetaCheckEvent.Origin origin) {
|
||||
return new MonitoredMetaMap(super.getMeta(origin), origin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeight(MetaCheckEvent.Origin origin) {
|
||||
int value = super.getWeight(origin);
|
||||
this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.verboseCheckTarget, this.metadata.getQueryOptions(), "weight", String.valueOf(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getPrimaryGroup(MetaCheckEvent.Origin origin) {
|
||||
String value = super.getPrimaryGroup(origin);
|
||||
this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.verboseCheckTarget, this.metadata.getQueryOptions(), "primarygroup", String.valueOf(value));
|
||||
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.cacheddata.type;
|
||||
|
||||
import net.luckperms.api.metastacking.MetaStackDefinition;
|
||||
import net.luckperms.api.metastacking.MetaStackElement;
|
||||
import net.luckperms.api.node.ChatMetaType;
|
||||
import net.luckperms.api.node.types.ChatMetaNode;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class MetaStackAccumulator {
|
||||
private final MetaStackDefinition definition;
|
||||
private final List<Entry> entries;
|
||||
|
||||
public MetaStackAccumulator(MetaStackDefinition definition, ChatMetaType targetType) {
|
||||
this.definition = definition;
|
||||
|
||||
List<MetaStackElement> elements = definition.getElements();
|
||||
this.entries = new ArrayList<>(elements.size());
|
||||
for (MetaStackElement element : elements) {
|
||||
this.entries.add(new Entry(element, targetType));
|
||||
}
|
||||
}
|
||||
|
||||
public void offer(ChatMetaNode<?, ?> node) {
|
||||
for (Entry entry : this.entries) {
|
||||
entry.offer(node);
|
||||
}
|
||||
}
|
||||
|
||||
public String toFormattedString() {
|
||||
List<String> elements = new LinkedList<>();
|
||||
for (Entry entry : this.entries) {
|
||||
ChatMetaNode<?, ?> node = entry.getNode();
|
||||
if (node != null) {
|
||||
elements.add(node.getMetaValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (elements.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.definition.getDuplicateRemovalFunction().processDuplicates(elements);
|
||||
|
||||
Iterator<String> it = elements.iterator();
|
||||
if (!it.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(this.definition.getStartSpacer());
|
||||
|
||||
// append first - we've checked hasNext already
|
||||
sb.append(it.next());
|
||||
|
||||
// append rest
|
||||
while (it.hasNext()){
|
||||
sb.append(this.definition.getMiddleSpacer());
|
||||
sb.append(it.next());
|
||||
}
|
||||
|
||||
sb.append(this.definition.getEndSpacer());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static final class Entry {
|
||||
private final MetaStackElement element;
|
||||
private final ChatMetaType type;
|
||||
|
||||
private @Nullable ChatMetaNode<?, ?> current = null;
|
||||
|
||||
Entry(MetaStackElement element, ChatMetaType type) {
|
||||
this.element = element;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public ChatMetaNode<?, ?> getNode() {
|
||||
return this.current;
|
||||
}
|
||||
|
||||
public boolean offer(ChatMetaNode<?, ?> node) {
|
||||
if (this.element.shouldAccumulate(this.type, node, this.current)) {
|
||||
this.current = node;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-17
@@ -26,6 +26,7 @@
|
||||
package me.lucko.luckperms.common.cacheddata.type;
|
||||
|
||||
import me.lucko.luckperms.common.cacheddata.CacheMetadata;
|
||||
import me.lucko.luckperms.common.cacheddata.UsageTracked;
|
||||
import me.lucko.luckperms.common.calculator.CalculatorFactory;
|
||||
import me.lucko.luckperms.common.calculator.PermissionCalculator;
|
||||
import me.lucko.luckperms.common.calculator.result.TristateResult;
|
||||
@@ -44,7 +45,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
/**
|
||||
* Holds cached permissions data for a given context
|
||||
*/
|
||||
public class PermissionCache implements CachedPermissionData {
|
||||
public class PermissionCache extends UsageTracked implements CachedPermissionData {
|
||||
|
||||
/**
|
||||
* The query options this container is holding data for
|
||||
@@ -68,13 +69,13 @@ public class PermissionCache implements CachedPermissionData {
|
||||
*/
|
||||
private final PermissionCalculator calculator;
|
||||
|
||||
public PermissionCache(QueryOptions queryOptions, CacheMetadata metadata, CalculatorFactory calculatorFactory) {
|
||||
public PermissionCache(QueryOptions queryOptions, CacheMetadata metadata, CalculatorFactory calculatorFactory, ConcurrentHashMap<String, Boolean> sourcePermissions) {
|
||||
this.queryOptions = queryOptions;
|
||||
this.permissions = new ConcurrentHashMap<>();
|
||||
this.permissions = sourcePermissions;
|
||||
this.permissionsUnmodifiable = Collections.unmodifiableMap(this.permissions);
|
||||
|
||||
this.calculator = calculatorFactory.build(queryOptions, metadata);
|
||||
this.calculator.setSourcePermissions(this.permissions); // Initial setup.
|
||||
this.calculator.setSourcePermissions(this.permissions);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -82,19 +83,6 @@ public class PermissionCache implements CachedPermissionData {
|
||||
this.calculator.invalidateCache();
|
||||
}
|
||||
|
||||
private void setPermissionsInternal(Map<String, Boolean> permissions) {
|
||||
this.permissions.clear();
|
||||
this.permissions.putAll(permissions);
|
||||
this.calculator.setSourcePermissions(this.permissions);
|
||||
invalidateCache();
|
||||
}
|
||||
|
||||
public void setPermissions(Map<String, Boolean> toApply) {
|
||||
if (!this.permissions.equals(toApply)) {
|
||||
setPermissionsInternal(toApply);
|
||||
}
|
||||
}
|
||||
|
||||
public PermissionCalculator getCalculator() {
|
||||
return this.calculator;
|
||||
}
|
||||
|
||||
+36
-25
@@ -30,8 +30,8 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Multimaps;
|
||||
|
||||
import me.lucko.luckperms.common.cacheddata.UsageTracked;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.metastacking.MetaStack;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.verbose.event.MetaCheckEvent;
|
||||
|
||||
@@ -51,7 +51,7 @@ import java.util.SortedMap;
|
||||
/**
|
||||
* Holds cached meta for a given context
|
||||
*/
|
||||
public class SimpleMetaCache implements CachedMetaData {
|
||||
public class SimpleMetaCache extends UsageTracked implements CachedMetaData {
|
||||
|
||||
private final LuckPermsPlugin plugin;
|
||||
|
||||
@@ -59,21 +59,22 @@ public class SimpleMetaCache implements CachedMetaData {
|
||||
private final QueryOptions queryOptions;
|
||||
|
||||
/* The data */
|
||||
protected Map<String, List<String>> meta = ImmutableMap.of();
|
||||
protected Map<String, String> flattenedMeta = ImmutableMap.of();
|
||||
protected SortedMap<Integer, String> prefixes = ImmutableSortedMap.of();
|
||||
protected SortedMap<Integer, String> suffixes = ImmutableSortedMap.of();
|
||||
protected String primaryGroup = null;
|
||||
protected MetaStack prefixStack = null;
|
||||
protected MetaStack suffixStack = null;
|
||||
protected final Map<String, List<String>> meta;
|
||||
protected final Map<String, String> flattenedMeta;
|
||||
protected final SortedMap<Integer, String> prefixes;
|
||||
protected final SortedMap<Integer, String> suffixes;
|
||||
protected final int weight;
|
||||
protected final String primaryGroup;
|
||||
private final MetaStackDefinition prefixDefinition;
|
||||
private final MetaStackDefinition suffixDefinition;
|
||||
private final String prefix;
|
||||
private final String suffix;
|
||||
|
||||
public SimpleMetaCache(LuckPermsPlugin plugin, QueryOptions queryOptions) {
|
||||
public SimpleMetaCache(LuckPermsPlugin plugin, QueryOptions queryOptions, MetaAccumulator sourceMeta) {
|
||||
this.plugin = plugin;
|
||||
this.queryOptions = queryOptions;
|
||||
}
|
||||
|
||||
public void loadMeta(MetaAccumulator meta) {
|
||||
this.meta = Multimaps.asMap(ImmutableListMultimap.copyOf(meta.getMeta()));
|
||||
this.meta = Multimaps.asMap(ImmutableListMultimap.copyOf(sourceMeta.getMeta()));
|
||||
|
||||
MetaValueSelector metaValueSelector = this.queryOptions.option(MetaValueSelector.KEY)
|
||||
.orElseGet(() -> this.plugin.getConfiguration().get(ConfigKeys.META_VALUE_SELECTOR));
|
||||
@@ -93,11 +94,14 @@ public class SimpleMetaCache implements CachedMetaData {
|
||||
}
|
||||
this.flattenedMeta = builder.build();
|
||||
|
||||
this.prefixes = ImmutableSortedMap.copyOfSorted(meta.getPrefixes());
|
||||
this.suffixes = ImmutableSortedMap.copyOfSorted(meta.getSuffixes());
|
||||
this.primaryGroup = meta.getPrimaryGroup();
|
||||
this.prefixStack = meta.getPrefixStack();
|
||||
this.suffixStack = meta.getSuffixStack();
|
||||
this.prefixes = ImmutableSortedMap.copyOfSorted(sourceMeta.getPrefixes());
|
||||
this.suffixes = ImmutableSortedMap.copyOfSorted(sourceMeta.getSuffixes());
|
||||
this.weight = sourceMeta.getWeight();
|
||||
this.primaryGroup = sourceMeta.getPrimaryGroup();
|
||||
this.prefixDefinition = sourceMeta.getPrefixDefinition();
|
||||
this.suffixDefinition = sourceMeta.getSuffixDefinition();
|
||||
this.prefix = sourceMeta.getPrefix();
|
||||
this.suffix = sourceMeta.getSuffix();
|
||||
}
|
||||
|
||||
public String getMetaValue(String key, MetaCheckEvent.Origin origin) {
|
||||
@@ -111,8 +115,7 @@ public class SimpleMetaCache implements CachedMetaData {
|
||||
}
|
||||
|
||||
public String getPrefix(MetaCheckEvent.Origin origin) {
|
||||
MetaStack prefixStack = this.prefixStack;
|
||||
return prefixStack == null ? null : prefixStack.toFormattedString();
|
||||
return this.prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,8 +124,7 @@ public class SimpleMetaCache implements CachedMetaData {
|
||||
}
|
||||
|
||||
public String getSuffix(MetaCheckEvent.Origin origin) {
|
||||
MetaStack suffixStack = this.suffixStack;
|
||||
return suffixStack == null ? null : suffixStack.toFormattedString();
|
||||
return this.suffix;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -149,23 +151,32 @@ public class SimpleMetaCache implements CachedMetaData {
|
||||
return this.suffixes;
|
||||
}
|
||||
|
||||
public int getWeight(MetaCheckEvent.Origin origin) {
|
||||
return this.weight;
|
||||
}
|
||||
|
||||
//@Override - not actually exposed in the API atm
|
||||
public final int getWeight() {
|
||||
return getWeight(MetaCheckEvent.Origin.LUCKPERMS_API);
|
||||
}
|
||||
|
||||
public @Nullable String getPrimaryGroup(MetaCheckEvent.Origin origin) {
|
||||
return this.primaryGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final @Nullable String getPrimaryGroup() {
|
||||
return this.primaryGroup;
|
||||
return getPrimaryGroup(MetaCheckEvent.Origin.LUCKPERMS_API);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull MetaStackDefinition getPrefixStackDefinition() {
|
||||
return this.prefixStack.getDefinition();
|
||||
return this.prefixDefinition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull MetaStackDefinition getSuffixStackDefinition() {
|
||||
return this.suffixStack.getDefinition();
|
||||
return this.suffixDefinition;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,6 +33,7 @@ import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.command.tabcomplete.CompletionSupplier;
|
||||
import me.lucko.luckperms.common.command.tabcomplete.TabCompleter;
|
||||
import me.lucko.luckperms.common.command.tabcomplete.TabCompletions;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.commands.group.CreateGroup;
|
||||
import me.lucko.luckperms.common.commands.group.DeleteGroup;
|
||||
import me.lucko.luckperms.common.commands.group.GroupParentCommand;
|
||||
@@ -60,6 +61,7 @@ import me.lucko.luckperms.common.commands.user.UserParentCommand;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.locale.message.Message;
|
||||
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.sender.Sender;
|
||||
import me.lucko.luckperms.common.util.ImmutableCollectors;
|
||||
@@ -158,14 +160,14 @@ public class CommandManager {
|
||||
|
||||
// Handle no arguments
|
||||
if (arguments.isEmpty() || (arguments.size() == 1 && arguments.get(0).trim().isEmpty())) {
|
||||
Message.BLANK.send(sender, "&2Running &bLuckPerms v" + this.plugin.getBootstrap().getVersion() + "&2.");
|
||||
Message.BLANK.send(sender, "&2Running &b" + AbstractLuckPermsPlugin.getPluginName() + " v" + this.plugin.getBootstrap().getVersion() + "&2.");
|
||||
if (hasPermissionForAny(sender)) {
|
||||
Message.VIEW_AVAILABLE_COMMANDS_PROMPT.send(sender, label);
|
||||
return CommandResult.SUCCESS;
|
||||
}
|
||||
|
||||
Collection<? extends Group> groups = this.plugin.getGroupManager().getAll().values();
|
||||
if (groups.size() <= 1 && groups.stream().allMatch(g -> g.normalData().immutable().isEmpty())) {
|
||||
if (groups.size() <= 1 && groups.stream().allMatch(g -> g.normalData().isEmpty())) {
|
||||
Message.FIRST_TIME_SETUP.send(sender, label, sender.getName());
|
||||
} else {
|
||||
Message.NO_PERMISSION_FOR_SUBCOMMANDS.send(sender);
|
||||
@@ -199,7 +201,7 @@ public class CommandManager {
|
||||
// Try to execute the command.
|
||||
CommandResult result;
|
||||
try {
|
||||
result = main.execute(this.plugin, sender, null, arguments, label);
|
||||
result = main.execute(this.plugin, sender, null, new ArgumentList(arguments), label);
|
||||
} catch (CommandException e) {
|
||||
result = e.handle(sender, label, main);
|
||||
} catch (Throwable e) {
|
||||
@@ -223,14 +225,14 @@ public class CommandManager {
|
||||
.from(1, partial -> mains.stream()
|
||||
.filter(m -> m.getName().equalsIgnoreCase(arguments.get(0)))
|
||||
.findFirst()
|
||||
.map(cmd -> cmd.tabComplete(this.plugin, sender, arguments.subList(1, arguments.size())))
|
||||
.map(cmd -> cmd.tabComplete(this.plugin, sender, new ArgumentList(arguments.subList(1, arguments.size()))))
|
||||
.orElse(Collections.emptyList())
|
||||
)
|
||||
.complete(arguments);
|
||||
}
|
||||
|
||||
private void sendCommandUsage(Sender sender, String label) {
|
||||
Message.BLANK.send(sender, "&2Running &bLuckPerms v" + this.plugin.getBootstrap().getVersion() + "&2.");
|
||||
Message.BLANK.send(sender, "&2Running &b" + AbstractLuckPermsPlugin.getPluginName() + " v" + this.plugin.getBootstrap().getVersion() + "&2.");
|
||||
this.mainCommands.values().stream()
|
||||
.filter(Command::shouldDisplay)
|
||||
.filter(c -> c.isAuthorized(sender))
|
||||
@@ -312,7 +314,7 @@ public class CommandManager {
|
||||
String rewrite = rewrites.apply(arg);
|
||||
if (rewrite != null) {
|
||||
args.remove(i);
|
||||
args.add(0, rewrite);
|
||||
args.add(i, rewrite);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ package me.lucko.luckperms.common.command.abstraction;
|
||||
|
||||
import me.lucko.luckperms.common.command.CommandResult;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.locale.command.Argument;
|
||||
import me.lucko.luckperms.common.locale.command.LocalizedCommandSpec;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
@@ -145,10 +146,10 @@ public abstract class Command<T> {
|
||||
}
|
||||
|
||||
// Main execution method for the command.
|
||||
public abstract CommandResult execute(LuckPermsPlugin plugin, Sender sender, T t, List<String> args, String label) throws CommandException;
|
||||
public abstract CommandResult execute(LuckPermsPlugin plugin, Sender sender, T target, ArgumentList args, String label) throws CommandException;
|
||||
|
||||
// Tab completion method - default implementation is provided as some commands do not provide tab completions.
|
||||
public List<String> tabComplete(LuckPermsPlugin plugin, Sender sender, List<String> args) {
|
||||
public List<String> tabComplete(LuckPermsPlugin plugin, Sender sender, ArgumentList args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ import me.lucko.luckperms.common.sender.Sender;
|
||||
*/
|
||||
public abstract class CommandException extends Exception {
|
||||
|
||||
public abstract CommandResult handle(Sender sender);
|
||||
protected abstract CommandResult handle(Sender sender);
|
||||
|
||||
public CommandResult handle(Sender sender, String label, Command<?> command) {
|
||||
return handle(sender);
|
||||
|
||||
+3
-2
@@ -27,6 +27,7 @@ package me.lucko.luckperms.common.command.abstraction;
|
||||
|
||||
import me.lucko.luckperms.common.command.CommandResult;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.locale.command.Argument;
|
||||
import me.lucko.luckperms.common.locale.command.LocalizedCommandSpec;
|
||||
import me.lucko.luckperms.common.locale.message.Message;
|
||||
@@ -71,9 +72,9 @@ public abstract class GenericChildCommand {
|
||||
this.argumentCheck = argumentCheck;
|
||||
}
|
||||
|
||||
public abstract CommandResult execute(LuckPermsPlugin plugin, Sender sender, PermissionHolder holder, List<String> args, String label, CommandPermission permission) throws CommandException;
|
||||
public abstract CommandResult execute(LuckPermsPlugin plugin, Sender sender, PermissionHolder target, ArgumentList args, String label, CommandPermission permission) throws CommandException;
|
||||
|
||||
public List<String> tabComplete(LuckPermsPlugin plugin, Sender sender, List<String> args) {
|
||||
public List<String> tabComplete(LuckPermsPlugin plugin, Sender sender, ArgumentList args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -28,6 +28,7 @@ package me.lucko.luckperms.common.command.abstraction;
|
||||
import me.lucko.luckperms.common.command.CommandResult;
|
||||
import me.lucko.luckperms.common.command.tabcomplete.CompletionSupplier;
|
||||
import me.lucko.luckperms.common.command.tabcomplete.TabCompleter;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.locale.command.LocalizedCommandSpec;
|
||||
import me.lucko.luckperms.common.locale.message.Message;
|
||||
import me.lucko.luckperms.common.model.HolderType;
|
||||
@@ -58,7 +59,7 @@ public class GenericParentCommand<T extends PermissionHolder> extends ChildComma
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, T holder, List<String> args, String label) {
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, T holder, ArgumentList args, String label) {
|
||||
if (args.isEmpty()) {
|
||||
sendUsageDetailed(sender, label);
|
||||
return CommandResult.INVALID_ARGS;
|
||||
@@ -94,7 +95,7 @@ public class GenericParentCommand<T extends PermissionHolder> extends ChildComma
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(LuckPermsPlugin plugin, Sender sender, List<String> args) {
|
||||
public List<String> tabComplete(LuckPermsPlugin plugin, Sender sender, ArgumentList args) {
|
||||
return TabCompleter.create()
|
||||
.at(0, CompletionSupplier.startsWith(() -> this.children.stream()
|
||||
.filter(s -> s.isAuthorized(sender, this.type))
|
||||
|
||||
+3
-2
@@ -28,6 +28,7 @@ package me.lucko.luckperms.common.command.abstraction;
|
||||
import me.lucko.luckperms.common.command.CommandResult;
|
||||
import me.lucko.luckperms.common.command.tabcomplete.CompletionSupplier;
|
||||
import me.lucko.luckperms.common.command.tabcomplete.TabCompleter;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.locale.command.LocalizedCommandSpec;
|
||||
import me.lucko.luckperms.common.locale.message.Message;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
@@ -59,7 +60,7 @@ public abstract class ParentCommand<T, I> extends Command<Void> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Void ignored, List<String> args, String label) {
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Void ignored, ArgumentList args, String label) {
|
||||
// check if required argument and/or subcommand is missing
|
||||
if (args.size() < this.type.minArgs) {
|
||||
sendUsage(sender, label);
|
||||
@@ -118,7 +119,7 @@ public abstract class ParentCommand<T, I> extends Command<Void> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(LuckPermsPlugin plugin, Sender sender, List<String> args) {
|
||||
public List<String> tabComplete(LuckPermsPlugin plugin, Sender sender, ArgumentList args) {
|
||||
switch (this.type) {
|
||||
case TAKES_ARGUMENT_FOR_TARGET:
|
||||
return TabCompleter.create()
|
||||
|
||||
+3
-3
@@ -27,13 +27,13 @@ package me.lucko.luckperms.common.command.abstraction;
|
||||
|
||||
import me.lucko.luckperms.common.command.CommandResult;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.locale.command.Argument;
|
||||
import me.lucko.luckperms.common.locale.command.LocalizedCommandSpec;
|
||||
import me.lucko.luckperms.common.locale.message.Message;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
@@ -46,11 +46,11 @@ public abstract class SingleCommand extends Command<Void> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Void v, List<String> args, String label) throws CommandException {
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Void ignored, ArgumentList args, String label) throws CommandException {
|
||||
return execute(plugin, sender, args, label);
|
||||
}
|
||||
|
||||
public abstract CommandResult execute(LuckPermsPlugin plugin, Sender sender, List<String> args, String label) throws CommandException;
|
||||
public abstract CommandResult execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, String label) throws CommandException;
|
||||
|
||||
@Override
|
||||
public void sendUsage(Sender sender, String label) {
|
||||
|
||||
@@ -37,6 +37,7 @@ public enum CommandPermission {
|
||||
EDITOR("editor", Type.NONE),
|
||||
DEBUG("debug", Type.NONE),
|
||||
VERBOSE("verbose", Type.NONE),
|
||||
VERBOSE_COMMAND_OTHERS("verbose.command.others", Type.NONE),
|
||||
TREE("tree", Type.NONE),
|
||||
SEARCH("search", Type.NONE),
|
||||
CHECK("check", Type.NONE),
|
||||
|
||||
+12
@@ -49,6 +49,18 @@ public interface CompletionSupplier {
|
||||
return partial -> stringsSupplier.get().filter(TabCompleter.startsWithIgnoreCase(partial)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
static CompletionSupplier contains(String... strings) {
|
||||
return contains(() -> Arrays.stream(strings));
|
||||
}
|
||||
|
||||
static CompletionSupplier contains(Collection<String> strings) {
|
||||
return contains(strings::stream);
|
||||
}
|
||||
|
||||
static CompletionSupplier contains(Supplier<Stream<String>> stringsSupplier) {
|
||||
return partial -> stringsSupplier.get().filter(TabCompleter.containsIgnoreCase(partial)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
List<String> supplyCompletions(String partial);
|
||||
|
||||
}
|
||||
@@ -107,4 +107,13 @@ public class TabCompleter {
|
||||
};
|
||||
}
|
||||
|
||||
static Predicate<String> containsIgnoreCase(String substring) {
|
||||
return string -> {
|
||||
if (string.length() < substring.length()) {
|
||||
return false;
|
||||
}
|
||||
return string.toLowerCase().contains(substring.toLowerCase());
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -99,7 +99,7 @@ public final class TabCompletions {
|
||||
int index = partial.indexOf('=');
|
||||
if (index == -1) {
|
||||
// cursor is specifying the key
|
||||
return CompletionSupplier.startsWith(potentialContexts.toMap().keySet()).supplyCompletions(partial);
|
||||
return CompletionSupplier.contains(potentialContexts.toMap().keySet()).supplyCompletions(partial);
|
||||
}
|
||||
|
||||
// cursor is specifying the value
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.command.utils;
|
||||
|
||||
import me.lucko.luckperms.common.command.CommandResult;
|
||||
import me.lucko.luckperms.common.command.abstraction.Command;
|
||||
import me.lucko.luckperms.common.command.abstraction.CommandException;
|
||||
import me.lucko.luckperms.common.command.abstraction.GenericChildCommand;
|
||||
import me.lucko.luckperms.common.locale.message.Message;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
|
||||
public abstract class ArgumentException extends CommandException {
|
||||
|
||||
public static class DetailedUsage extends ArgumentException {
|
||||
@Override
|
||||
protected CommandResult handle(Sender sender) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult handle(Sender sender, String label, Command<?> command) {
|
||||
command.sendDetailedUsage(sender, label);
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult handle(Sender sender, GenericChildCommand command) {
|
||||
command.sendDetailedUsage(sender);
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PastDate extends ArgumentException {
|
||||
@Override
|
||||
protected CommandResult handle(Sender sender) {
|
||||
Message.PAST_DATE_ERROR.send(sender);
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
}
|
||||
|
||||
public static class InvalidDate extends ArgumentException {
|
||||
private final String invalidDate;
|
||||
|
||||
public InvalidDate(String invalidDate) {
|
||||
this.invalidDate = invalidDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CommandResult handle(Sender sender) {
|
||||
Message.ILLEGAL_DATE_ERROR.send(sender, this.invalidDate);
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
}
|
||||
|
||||
public static class InvalidPriority extends ArgumentException {
|
||||
private final String invalidPriority;
|
||||
|
||||
public InvalidPriority(String invalidPriority) {
|
||||
this.invalidPriority = invalidPriority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult handle(Sender sender) {
|
||||
Message.META_INVALID_PRIORITY.send(sender, this.invalidPriority);
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* 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.command.utils;
|
||||
|
||||
import com.google.common.collect.ForwardingList;
|
||||
|
||||
import me.lucko.luckperms.common.command.abstraction.CommandException;
|
||||
import me.lucko.luckperms.common.commands.user.UserParentCommand;
|
||||
import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl;
|
||||
import me.lucko.luckperms.common.context.contextset.MutableContextSetImpl;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import me.lucko.luckperms.common.util.DurationParser;
|
||||
|
||||
import net.luckperms.api.context.Context;
|
||||
import net.luckperms.api.context.DefaultContextKeys;
|
||||
import net.luckperms.api.context.ImmutableContextSet;
|
||||
import net.luckperms.api.context.MutableContextSet;
|
||||
import net.luckperms.api.model.data.TemporaryNodeMergeStrategy;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* A list of {@link String} arguments, with extra methods to help
|
||||
* with parsing.
|
||||
*/
|
||||
public class ArgumentList extends ForwardingList<String> {
|
||||
private final List<String> backingList;
|
||||
|
||||
public ArgumentList(List<String> backingList) {
|
||||
this.backingList = backingList;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> delegate() {
|
||||
return this.backingList;
|
||||
}
|
||||
|
||||
public boolean indexOutOfBounds(int index) {
|
||||
return index < 0 || index >= size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(int index) throws IndexOutOfBoundsException {
|
||||
return super.get(index).replace("{SPACE}", " ");
|
||||
}
|
||||
|
||||
public String getOrDefault(int index, String defaultValue) {
|
||||
if (indexOutOfBounds(index)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull ArgumentList subList(int fromIndex, int toIndex) {
|
||||
return new ArgumentList(super.subList(fromIndex, toIndex));
|
||||
}
|
||||
|
||||
public int getIntOrDefault(int index, int defaultValue) {
|
||||
if (indexOutOfBounds(index)) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
try {
|
||||
return Integer.parseInt(get(index));
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public String getLowercase(int index, Predicate<? super String> test) throws ArgumentException.DetailedUsage {
|
||||
String arg = get(index).toLowerCase();
|
||||
if (!test.test(arg)) {
|
||||
throw new ArgumentException.DetailedUsage();
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
public boolean getBooleanOrInsert(int index, boolean defaultValue) {
|
||||
if (!indexOutOfBounds(index)) {
|
||||
String arg = get(index);
|
||||
if (arg.equalsIgnoreCase("true") || arg.equalsIgnoreCase("false")) {
|
||||
return Boolean.parseBoolean(arg);
|
||||
}
|
||||
}
|
||||
|
||||
add(index, Boolean.toString(defaultValue));
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public int getPriority(int index) throws ArgumentException {
|
||||
try {
|
||||
return Integer.parseInt(get(index));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ArgumentException.InvalidPriority(get(index));
|
||||
}
|
||||
}
|
||||
|
||||
public UUID getUserTarget(int index, LuckPermsPlugin plugin, Sender sender) {
|
||||
String arg = get(index);
|
||||
return UserParentCommand.parseTargetUniqueId(arg, plugin, sender);
|
||||
}
|
||||
|
||||
public Duration getDuration(int index) throws ArgumentException {
|
||||
String arg = get(index);
|
||||
return parseDuration(arg).orElseThrow(() -> new ArgumentException.InvalidDate(arg));
|
||||
}
|
||||
|
||||
public Duration getDurationOrDefault(int index, Duration defaultValue) throws ArgumentException {
|
||||
if (indexOutOfBounds(index)) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return parseDuration(get(index)).orElse(defaultValue);
|
||||
}
|
||||
|
||||
private static Optional<Duration> parseDuration(String input) throws ArgumentException.PastDate {
|
||||
try {
|
||||
long number = Long.parseLong(input);
|
||||
Instant now = Instant.now().truncatedTo(ChronoUnit.SECONDS);
|
||||
Duration duration = checkPastDate(Duration.between(now, Instant.ofEpochSecond(number)));
|
||||
return Optional.of(duration);
|
||||
} catch (NumberFormatException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
Duration duration = checkPastDate(DurationParser.parseDuration(input));
|
||||
return Optional.of(duration);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static Duration checkPastDate(Duration duration) throws ArgumentException.PastDate {
|
||||
if (duration.isNegative()) {
|
||||
throw new ArgumentException.PastDate();
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
public Optional<TemporaryNodeMergeStrategy> getTemporaryModifierAndRemove(int index) {
|
||||
if (indexOutOfBounds(index)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
TemporaryNodeMergeStrategy strategy = parseTemporaryModifier(get(index));
|
||||
if (strategy == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
remove(index);
|
||||
return Optional.of(strategy);
|
||||
}
|
||||
|
||||
private static TemporaryNodeMergeStrategy parseTemporaryModifier(String s) {
|
||||
switch (s.toLowerCase()) {
|
||||
case "accumulate":
|
||||
return TemporaryNodeMergeStrategy.ADD_NEW_DURATION_TO_EXISTING;
|
||||
case "replace":
|
||||
return TemporaryNodeMergeStrategy.REPLACE_EXISTING_IF_DURATION_LONGER;
|
||||
case "deny":
|
||||
case "none":
|
||||
return TemporaryNodeMergeStrategy.NONE;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public MutableContextSet getContextOrDefault(int fromIndex, LuckPermsPlugin plugin) throws CommandException {
|
||||
if (size() <= fromIndex) {
|
||||
return plugin.getConfiguration().getContextsFile().getDefaultContexts().mutableCopy();
|
||||
}
|
||||
return parseContext(fromIndex);
|
||||
}
|
||||
|
||||
public ImmutableContextSet getContextOrEmpty(int fromIndex) {
|
||||
if (size() <= fromIndex) {
|
||||
return ImmutableContextSetImpl.EMPTY;
|
||||
}
|
||||
return parseContext(fromIndex).immutableCopy();
|
||||
}
|
||||
|
||||
private MutableContextSet parseContext(int fromIndex) {
|
||||
MutableContextSet contextSet = new MutableContextSetImpl();
|
||||
List<String> entries = subList(fromIndex, size());
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
String entry = entries.get(i);
|
||||
int sep = entry.indexOf('=');
|
||||
|
||||
String key;
|
||||
String value;
|
||||
|
||||
if (sep != -1) {
|
||||
key = entry.substring(0, sep);
|
||||
value = entry.substring(sep + 1);
|
||||
} else {
|
||||
key = i == 1 ? DefaultContextKeys.WORLD_KEY : DefaultContextKeys.SERVER_KEY;
|
||||
value = entry;
|
||||
}
|
||||
|
||||
if (!Context.isValidKey(key) || !Context.isValidValue(value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
contextSet.add(key, value);
|
||||
}
|
||||
|
||||
return contextSet;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,279 +0,0 @@
|
||||
/*
|
||||
* 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.command.utils;
|
||||
|
||||
import me.lucko.luckperms.common.command.CommandResult;
|
||||
import me.lucko.luckperms.common.command.abstraction.Command;
|
||||
import me.lucko.luckperms.common.command.abstraction.CommandException;
|
||||
import me.lucko.luckperms.common.command.abstraction.GenericChildCommand;
|
||||
import me.lucko.luckperms.common.commands.user.UserParentCommand;
|
||||
import me.lucko.luckperms.common.context.contextset.AbstractContextSet;
|
||||
import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl;
|
||||
import me.lucko.luckperms.common.context.contextset.MutableContextSetImpl;
|
||||
import me.lucko.luckperms.common.locale.message.Message;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import me.lucko.luckperms.common.storage.misc.DataConstraints;
|
||||
import me.lucko.luckperms.common.util.DurationParser;
|
||||
|
||||
import net.luckperms.api.context.Context;
|
||||
import net.luckperms.api.context.DefaultContextKeys;
|
||||
import net.luckperms.api.context.ImmutableContextSet;
|
||||
import net.luckperms.api.context.MutableContextSet;
|
||||
import net.luckperms.api.model.data.TemporaryNodeMergeStrategy;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Utility class to help process arguments, and throw checked exceptions if the arguments are invalid.
|
||||
*/
|
||||
public class ArgumentParser {
|
||||
|
||||
public static String parseString(int index, List<String> args) {
|
||||
return args.get(index).replace("{SPACE}", " ");
|
||||
}
|
||||
|
||||
public static String parseStringOrElse(int index, List<String> args, String other) {
|
||||
if (index < 0 || index >= args.size()) {
|
||||
return other;
|
||||
}
|
||||
|
||||
return args.get(index).replace("{SPACE}", " ");
|
||||
}
|
||||
|
||||
public static int parseIntOrElse(int index, List<String> args, int other) {
|
||||
if (index < 0 || index >= args.size()) {
|
||||
return other;
|
||||
}
|
||||
|
||||
try {
|
||||
return Integer.parseInt(args.get(index));
|
||||
} catch (NumberFormatException e) {
|
||||
return other;
|
||||
}
|
||||
}
|
||||
|
||||
public static String parseName(int index, List<String> args) throws ArgumentException {
|
||||
String groupName = args.get(index).toLowerCase();
|
||||
if (!DataConstraints.GROUP_NAME_TEST.test(groupName)) {
|
||||
throw new DetailedUsageException();
|
||||
}
|
||||
return groupName;
|
||||
}
|
||||
|
||||
public static String parseNameWithSpace(int index, List<String> args) throws ArgumentException {
|
||||
String groupName = args.get(index).toLowerCase();
|
||||
if (!DataConstraints.GROUP_NAME_TEST_ALLOW_SPACE.test(groupName)) {
|
||||
throw new DetailedUsageException();
|
||||
}
|
||||
return groupName;
|
||||
}
|
||||
|
||||
public static boolean parseBoolean(int index, List<String> args) {
|
||||
if (index < args.size()) {
|
||||
String bool = args.get(index);
|
||||
if (bool.equalsIgnoreCase("true") || bool.equalsIgnoreCase("false")) {
|
||||
return Boolean.parseBoolean(bool);
|
||||
}
|
||||
}
|
||||
|
||||
args.add(index, "true");
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Duration parseDuration(int index, List<String> args) throws ArgumentException {
|
||||
String input = args.get(index);
|
||||
Duration duration;
|
||||
|
||||
try {
|
||||
long number = Long.parseLong(input);
|
||||
Instant now = Instant.now().truncatedTo(ChronoUnit.SECONDS);
|
||||
duration = Duration.between(now, Instant.ofEpochSecond(number));
|
||||
} catch (NumberFormatException e) {
|
||||
try {
|
||||
duration = DurationParser.parseDuration(input);
|
||||
} catch (IllegalArgumentException e1) {
|
||||
throw new InvalidDateException(input);
|
||||
}
|
||||
}
|
||||
|
||||
if (duration.isNegative()) {
|
||||
throw new PastDateException();
|
||||
}
|
||||
|
||||
return duration;
|
||||
}
|
||||
|
||||
public static TemporaryNodeMergeStrategy parseTemporaryModifier(String s) {
|
||||
switch (s.toLowerCase()) {
|
||||
case "accumulate":
|
||||
return TemporaryNodeMergeStrategy.ADD_NEW_DURATION_TO_EXISTING;
|
||||
case "replace":
|
||||
return TemporaryNodeMergeStrategy.REPLACE_EXISTING_IF_DURATION_LONGER;
|
||||
case "deny":
|
||||
case "none":
|
||||
return TemporaryNodeMergeStrategy.NONE;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown value: " + s);
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<TemporaryNodeMergeStrategy> parseTemporaryModifier(int index, List<String> args) {
|
||||
if (index < 0 || index >= args.size()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
String s = args.get(index);
|
||||
try {
|
||||
Optional<TemporaryNodeMergeStrategy> strategy = Optional.of(parseTemporaryModifier(s));
|
||||
args.remove(index);
|
||||
return strategy;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public static MutableContextSet parseContext(int fromIndex, List<String> args, LuckPermsPlugin plugin) throws CommandException {
|
||||
if (args.size() <= fromIndex) {
|
||||
return plugin.getConfiguration().getContextsFile().getDefaultContexts().mutableCopy();
|
||||
}
|
||||
return parseContext(fromIndex, args);
|
||||
}
|
||||
|
||||
public static ImmutableContextSet parseContextSponge(int fromIndex, List<String> args) {
|
||||
if (args.size() <= fromIndex) {
|
||||
return ImmutableContextSetImpl.EMPTY;
|
||||
}
|
||||
return parseContext(fromIndex, args).immutableCopy();
|
||||
}
|
||||
|
||||
private static MutableContextSet parseContext(int fromIndex, List<String> args) {
|
||||
MutableContextSet contextSet = new MutableContextSetImpl();
|
||||
List<String> entries = args.subList(fromIndex, args.size());
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
String entry = entries.get(i);
|
||||
int sep = entry.indexOf('=');
|
||||
|
||||
String key;
|
||||
String value;
|
||||
|
||||
if (sep != -1) {
|
||||
key = entry.substring(0, sep);
|
||||
value = entry.substring(sep + 1);
|
||||
} else {
|
||||
key = i == 1 ? DefaultContextKeys.WORLD_KEY : DefaultContextKeys.SERVER_KEY;
|
||||
value = entry;
|
||||
}
|
||||
|
||||
// TODO reconsider a better place to insert / avoid this special case
|
||||
if (!Context.isValidKey(key) || !Context.isValidValue(value) || AbstractContextSet.isGlobalServerWorldEntry(key, value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
contextSet.add(key, value);
|
||||
}
|
||||
|
||||
return contextSet;
|
||||
}
|
||||
|
||||
public static int parsePriority(int index, List<String> args) throws ArgumentException {
|
||||
try {
|
||||
return Integer.parseInt(args.get(index));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new InvalidPriorityException(args.get(index));
|
||||
}
|
||||
}
|
||||
|
||||
public static UUID parseUserTarget(int index, List<String> args, LuckPermsPlugin plugin, Sender sender) {
|
||||
final String target = args.get(index);
|
||||
return UserParentCommand.parseTargetUniqueId(target, plugin, sender);
|
||||
}
|
||||
|
||||
public abstract static class ArgumentException extends CommandException {
|
||||
|
||||
}
|
||||
|
||||
public static class DetailedUsageException extends ArgumentException {
|
||||
@Override
|
||||
public CommandResult handle(Sender sender) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult handle(Sender sender, String label, Command<?> command) {
|
||||
command.sendDetailedUsage(sender, label);
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult handle(Sender sender, GenericChildCommand command) {
|
||||
command.sendDetailedUsage(sender);
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PastDateException extends ArgumentException {
|
||||
@Override
|
||||
public CommandResult handle(Sender sender) {
|
||||
Message.PAST_DATE_ERROR.send(sender);
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
}
|
||||
|
||||
public static class InvalidDateException extends ArgumentException {
|
||||
private final String invalidDate;
|
||||
|
||||
public InvalidDateException(String invalidDate) {
|
||||
this.invalidDate = invalidDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult handle(Sender sender) {
|
||||
Message.ILLEGAL_DATE_ERROR.send(sender, this.invalidDate);
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
}
|
||||
|
||||
public static class InvalidPriorityException extends ArgumentException {
|
||||
private final String invalidPriority;
|
||||
|
||||
public InvalidPriorityException(String invalidPriority) {
|
||||
this.invalidPriority = invalidPriority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult handle(Sender sender) {
|
||||
Message.META_INVALID_PRIORITY.send(sender, this.invalidPriority);
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,11 +25,9 @@
|
||||
|
||||
package me.lucko.luckperms.common.command.utils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SortMode {
|
||||
|
||||
public static SortMode determine(List<String> args) {
|
||||
public static SortMode determine(ArgumentList args) {
|
||||
SortType type = SortType.PRIORITY;
|
||||
boolean ascending = true;
|
||||
for (String arg : args) {
|
||||
|
||||
+12
-14
@@ -34,8 +34,6 @@ import me.lucko.luckperms.common.locale.command.CommandSpec;
|
||||
import me.lucko.luckperms.common.model.HolderType;
|
||||
import me.lucko.luckperms.common.model.PermissionHolder;
|
||||
|
||||
import net.luckperms.api.node.ChatMetaType;
|
||||
|
||||
public class CommandMeta<T extends PermissionHolder> extends GenericParentCommand<T> {
|
||||
public CommandMeta(LocaleManager locale, HolderType type) {
|
||||
super(CommandSpec.META.localize(locale), "Meta", type, ImmutableList.<GenericChildCommand>builder()
|
||||
@@ -44,18 +42,18 @@ public class CommandMeta<T extends PermissionHolder> extends GenericParentComman
|
||||
.add(new MetaUnset(locale))
|
||||
.add(new MetaSetTemp(locale))
|
||||
.add(new MetaUnsetTemp(locale))
|
||||
.add(new MetaAddChatMeta(locale, ChatMetaType.PREFIX))
|
||||
.add(new MetaAddChatMeta(locale, ChatMetaType.SUFFIX))
|
||||
.add(new MetaSetChatMeta(locale, ChatMetaType.PREFIX))
|
||||
.add(new MetaSetChatMeta(locale, ChatMetaType.SUFFIX))
|
||||
.add(new MetaRemoveChatMeta(locale, ChatMetaType.PREFIX))
|
||||
.add(new MetaRemoveChatMeta(locale, ChatMetaType.SUFFIX))
|
||||
.add(new MetaAddTempChatMeta(locale, ChatMetaType.PREFIX))
|
||||
.add(new MetaAddTempChatMeta(locale, ChatMetaType.SUFFIX))
|
||||
.add(new MetaSetTempChatMeta(locale, ChatMetaType.PREFIX))
|
||||
.add(new MetaSetTempChatMeta(locale, ChatMetaType.SUFFIX))
|
||||
.add(new MetaRemoveTempChatMeta(locale, ChatMetaType.PREFIX))
|
||||
.add(new MetaRemoveTempChatMeta(locale, ChatMetaType.SUFFIX))
|
||||
.add(MetaAddChatMeta.forPrefix(locale))
|
||||
.add(MetaAddChatMeta.forSuffix(locale))
|
||||
.add(MetaSetChatMeta.forPrefix(locale))
|
||||
.add(MetaSetChatMeta.forSuffix(locale))
|
||||
.add(MetaRemoveChatMeta.forPrefix(locale))
|
||||
.add(MetaRemoveChatMeta.forSuffix(locale))
|
||||
.add(MetaAddTempChatMeta.forPrefix(locale))
|
||||
.add(MetaAddTempChatMeta.forSuffix(locale))
|
||||
.add(MetaSetTempChatMeta.forPrefix(locale))
|
||||
.add(MetaSetTempChatMeta.forSuffix(locale))
|
||||
.add(MetaRemoveTempChatMeta.forPrefix(locale))
|
||||
.add(MetaRemoveTempChatMeta.forSuffix(locale))
|
||||
.add(new MetaClear(locale))
|
||||
.build());
|
||||
}
|
||||
|
||||
+39
-23
@@ -33,11 +33,12 @@ import me.lucko.luckperms.common.command.access.ArgumentPermissions;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.command.tabcomplete.TabCompleter;
|
||||
import me.lucko.luckperms.common.command.tabcomplete.TabCompletions;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentParser;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.command.utils.MessageUtils;
|
||||
import me.lucko.luckperms.common.command.utils.StorageAssistant;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.locale.command.CommandSpec;
|
||||
import me.lucko.luckperms.common.locale.command.LocalizedCommandSpec;
|
||||
import me.lucko.luckperms.common.locale.message.Message;
|
||||
import me.lucko.luckperms.common.model.PermissionHolder;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
@@ -55,60 +56,75 @@ import net.luckperms.api.node.ChatMetaType;
|
||||
import java.util.List;
|
||||
|
||||
public class MetaAddChatMeta extends GenericChildCommand {
|
||||
|
||||
public static MetaAddChatMeta forPrefix(LocaleManager locale) {
|
||||
return new MetaAddChatMeta(
|
||||
ChatMetaType.PREFIX,
|
||||
CommandSpec.META_ADDPREFIX.localize(locale),
|
||||
"addprefix",
|
||||
CommandPermission.USER_META_ADD_PREFIX,
|
||||
CommandPermission.GROUP_META_ADD_PREFIX
|
||||
);
|
||||
}
|
||||
|
||||
public static MetaAddChatMeta forSuffix(LocaleManager locale) {
|
||||
return new MetaAddChatMeta(
|
||||
ChatMetaType.SUFFIX,
|
||||
CommandSpec.META_ADDSUFFIX.localize(locale),
|
||||
"addsuffix",
|
||||
CommandPermission.USER_META_ADD_SUFFIX,
|
||||
CommandPermission.GROUP_META_ADD_SUFFIX
|
||||
);
|
||||
}
|
||||
|
||||
private final ChatMetaType type;
|
||||
|
||||
public MetaAddChatMeta(LocaleManager locale, ChatMetaType type) {
|
||||
super(
|
||||
type == ChatMetaType.PREFIX ? CommandSpec.META_ADDPREFIX.localize(locale) : CommandSpec.META_ADDSUFFIX.localize(locale),
|
||||
"add" + type.name().toLowerCase(),
|
||||
type == ChatMetaType.PREFIX ? CommandPermission.USER_META_ADD_PREFIX : CommandPermission.USER_META_ADD_SUFFIX,
|
||||
type == ChatMetaType.PREFIX ? CommandPermission.GROUP_META_ADD_PREFIX : CommandPermission.GROUP_META_ADD_SUFFIX,
|
||||
Predicates.inRange(0, 1)
|
||||
);
|
||||
private MetaAddChatMeta(ChatMetaType type, LocalizedCommandSpec spec, String name, CommandPermission userPermission, CommandPermission groupPermission) {
|
||||
super(spec, name, userPermission, groupPermission, Predicates.inRange(0, 1));
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, PermissionHolder holder, List<String> args, String label, CommandPermission permission) throws CommandException {
|
||||
if (ArgumentPermissions.checkModifyPerms(plugin, sender, permission, holder)) {
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, PermissionHolder target, ArgumentList args, String label, CommandPermission permission) throws CommandException {
|
||||
if (ArgumentPermissions.checkModifyPerms(plugin, sender, permission, target)) {
|
||||
Message.COMMAND_NO_PERMISSION.send(sender);
|
||||
return CommandResult.NO_PERMISSION;
|
||||
}
|
||||
|
||||
int priority = ArgumentParser.parsePriority(0, args);
|
||||
String meta = ArgumentParser.parseString(1, args);
|
||||
MutableContextSet context = ArgumentParser.parseContext(2, args, plugin);
|
||||
int priority = args.getPriority(0);
|
||||
String meta = args.get(1);
|
||||
MutableContextSet context = args.getContextOrDefault(2, plugin);
|
||||
|
||||
if (ArgumentPermissions.checkContext(plugin, sender, permission, context) ||
|
||||
ArgumentPermissions.checkGroup(plugin, sender, holder, context)) {
|
||||
ArgumentPermissions.checkGroup(plugin, sender, target, context)) {
|
||||
Message.COMMAND_NO_PERMISSION.send(sender);
|
||||
return CommandResult.NO_PERMISSION;
|
||||
}
|
||||
|
||||
DataMutateResult result = holder.setNode(DataType.NORMAL, this.type.builder(meta, priority).withContext(context).build(), true);
|
||||
DataMutateResult result = target.setNode(DataType.NORMAL, this.type.builder(meta, priority).withContext(context).build(), true);
|
||||
if (result.wasSuccessful()) {
|
||||
TextComponent.Builder builder = Message.ADD_CHATMETA_SUCCESS.asComponent(plugin.getLocaleManager(), holder.getFormattedDisplayName(), this.type.name().toLowerCase(), meta, priority, MessageUtils.contextSetToString(plugin.getLocaleManager(), context)).toBuilder();
|
||||
TextComponent.Builder builder = Message.ADD_CHATMETA_SUCCESS.asComponent(plugin.getLocaleManager(), target.getFormattedDisplayName(), this.type.name().toLowerCase(), meta, priority, MessageUtils.contextSetToString(plugin.getLocaleManager(), context)).toBuilder();
|
||||
HoverEvent event = HoverEvent.showText(TextUtils.fromLegacy(
|
||||
"¥3Raw " + this.type.name().toLowerCase() + ": ¥r" + meta,
|
||||
'¥'
|
||||
"§3Raw " + this.type.name().toLowerCase() + ": §r" + meta,
|
||||
'§'
|
||||
));
|
||||
builder.applyDeep(c -> c.hoverEvent(event));
|
||||
sender.sendMessage(builder.build());
|
||||
|
||||
LoggedAction.build().source(sender).target(holder)
|
||||
LoggedAction.build().source(sender).target(target)
|
||||
.description("meta" , "add" + this.type.name().toLowerCase(), priority, meta, context)
|
||||
.build().submit(plugin, sender);
|
||||
|
||||
StorageAssistant.save(holder, sender, plugin);
|
||||
StorageAssistant.save(target, sender, plugin);
|
||||
return CommandResult.SUCCESS;
|
||||
} else {
|
||||
Message.ALREADY_HAS_CHAT_META.send(sender, holder.getFormattedDisplayName(), this.type.name().toLowerCase(), meta, priority, MessageUtils.contextSetToString(plugin.getLocaleManager(), context));
|
||||
Message.ALREADY_HAS_CHAT_META.send(sender, target.getFormattedDisplayName(), this.type.name().toLowerCase(), meta, priority, MessageUtils.contextSetToString(plugin.getLocaleManager(), context));
|
||||
return CommandResult.STATE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(LuckPermsPlugin plugin, Sender sender, List<String> args) {
|
||||
public List<String> tabComplete(LuckPermsPlugin plugin, Sender sender, ArgumentList args) {
|
||||
return TabCompleter.create()
|
||||
.from(2, TabCompletions.contexts(plugin))
|
||||
.complete(args);
|
||||
|
||||
Loaded 100 of 314 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user