Compare commits

..
Author SHA1 Message Date
Luck e9ea98e84e Integration tests against real server 2023-09-22 20:41:48 +01:00
339 changed files with 3418 additions and 13274 deletions

No files matched your search

-5
View File
@@ -1,5 +0,0 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# These are explicitly windows files and should use crlf
*.bat text eol=crlf
+17 -18
View File
@@ -19,24 +19,26 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Java
uses: actions/setup-java@v4
- name: validate gradle wrapper
uses: gradle/wrapper-validation-action@v1
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21'
java-version: '17'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
uses: gradle/gradle-build-action@v2
- name: Run build and tests with Gradle wrapper
run: ./gradlew test build -PdockerTests
- name: Publish test report
uses: mikepenz/action-junit-report@v5
uses: mikepenz/action-junit-report@v3
if: success() || failure()
with:
report_paths: '**/build/test-results/test/TEST-*.xml'
@@ -44,7 +46,7 @@ jobs:
detailed_summary: true
- name: Upload all artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: jars
path: |
@@ -59,7 +61,7 @@ jobs:
standalone/loader/build/libs/LuckPerms-Standalone-*.jar
- name: Upload standalone artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: standalone-binary
path: standalone/loader/build/libs/LuckPerms-Standalone-*.jar
@@ -74,27 +76,24 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Retrieve saved standalone jar artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v3
with:
name: standalone-binary
path: standalone/docker/
- name: Remove version number from jar artifact name
run: mv standalone/docker/LuckPerms-Standalone-*.jar standalone/docker/luckperms-standalone.jar
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v1
- name: Log in to the Container registry
uses: docker/login-action@v3
uses: docker/login-action@v1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
@@ -106,7 +105,7 @@ jobs:
- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v3
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
flavor: |
@@ -119,7 +118,7 @@ jobs:
type=raw,enable=${{ github.ref == 'refs/heads/master' }},value=${{ env.luckperms_version }}
- name: Build and push Docker image
uses: docker/build-push-action@v6
uses: docker/build-push-action@v2
with:
context: standalone/docker/
platforms: linux/amd64,linux/arm64
+1 -1
View File
@@ -24,7 +24,7 @@ For more information, see the wiki article on [Why LuckPerms?](https://luckperms
LuckPerms uses Gradle to handle dependencies & building.
#### Requirements
* Java 21 JDK or newer
* Java 17 JDK or newer
* Git
#### Compiling from source
+23 -10
View File
@@ -1,5 +1,5 @@
group = 'net.luckperms'
project.version = '5.5'
project.version = '5.4'
jar {
manifest {
@@ -8,8 +8,8 @@ jar {
}
dependencies {
compileOnly 'org.checkerframework:checker-qual:3.49.3'
compileOnly 'org.jetbrains:annotations:26.0.2'
compileOnly 'org.checkerframework:checker-qual:3.21.2'
compileOnly 'org.jetbrains:annotations:23.1.0'
}
// Only used occasionally for deployment - not needed for normal builds.
@@ -24,21 +24,31 @@ if (project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePass
options.charSet = 'UTF-8'
options.links(
'https://checkerframework.org/api/',
'https://javadoc.io/static/org.jetbrains/annotations/26.0.2/'
'https://javadoc.io/static/org.jetbrains/annotations/23.0.0/'
)
options.addStringOption('Xdoclint:none', '-quiet')
options.addStringOption('-since', '5.0,5.1,5.2,5.3,5.4,5.5')
options.addStringOption('-since', '5.0,5.1,5.2,5.3,5.4')
if (JavaVersion.current() > JavaVersion.VERSION_1_8) {
options.links.add('https://docs.oracle.com/en/java/javase/21/docs/api/')
options.links.add('https://docs.oracle.com/en/java/javase/11/docs/api/')
} else {
options.links.add('https://docs.oracle.com/javase/8/docs/api/')
}
}
java {
withJavadocJar()
withSourcesJar()
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier 'javadoc'
from javadoc.destinationDir
}
task sourcesJar(type: Jar) {
classifier 'sources'
from sourceSets.main.allSource
}
artifacts {
archives javadocJar
archives sourcesJar
}
publishing {
@@ -60,10 +70,12 @@ if (project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePass
artifactId = 'api'
from components.java
artifact sourcesJar
artifact javadocJar
pom {
name = 'LuckPerms API'
description = 'A permissions manager for Minecraft servers.'
description = 'A permissions plugin for Minecraft servers.'
url = 'https://luckperms.net'
licenses {
@@ -99,6 +111,7 @@ if (project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePass
signing {
useGpgCmd()
sign configurations.archives
sign publishing.publications.mavenJava
required = true
}
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<p>
LuckPerms is a permissions manager for Minecraft servers. It allows server admins to control what
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.
</p>
<h4>Useful Links</h4>
@@ -26,7 +26,6 @@
package net.luckperms.api;
import net.luckperms.api.actionlog.ActionLogger;
import net.luckperms.api.actionlog.filter.ActionFilterFactory;
import net.luckperms.api.context.ContextCalculator;
import net.luckperms.api.context.ContextManager;
import net.luckperms.api.event.EventBus;
@@ -41,7 +40,6 @@ import net.luckperms.api.model.user.User;
import net.luckperms.api.model.user.UserManager;
import net.luckperms.api.node.NodeBuilderRegistry;
import net.luckperms.api.node.matcher.NodeMatcherFactory;
import net.luckperms.api.platform.Health;
import net.luckperms.api.platform.Platform;
import net.luckperms.api.platform.PlayerAdapter;
import net.luckperms.api.platform.PluginMetadata;
@@ -221,17 +219,6 @@ public interface LuckPerms {
*/
@NonNull CompletableFuture<Void> runUpdateTask();
/**
* Executes a health check.
*
* <p>This task checks if the LuckPerms implementation is running and
* whether it has a connection to the database (if applicable).</p>
*
* @return the health status
* @since 5.5
*/
@NonNull Health runHealthCheck();
/**
* Registers a {@link MessengerProvider} for use by the platform.
*
@@ -268,14 +255,4 @@ public interface LuckPerms {
@Internal
@NonNull NodeMatcherFactory getNodeMatcherFactory();
/**
* Gets the {@link ActionFilterFactory}.
*
* @return the action filter factory
* @since 5.5
*/
@Internal
@NonNull
ActionFilterFactory getActionFilterFactory();
}
@@ -79,8 +79,7 @@ public final class LuckPermsProvider {
" a) the LuckPerms plugin is not installed or it failed to enable\n" +
" b) the plugin in the stacktrace does not declare a dependency on LuckPerms\n" +
" c) the plugin in the stacktrace is retrieving the API before the plugin 'enable' phase\n" +
" (call the #get method in onEnable, not the constructor!)\n" +
" d) the plugin in the stacktrace is incorrectly 'shading' the LuckPerms API into its jar\n";
" (call the #get method in onEnable, not the constructor!)\n";
NotLoadedException() {
super(MESSAGE);
@@ -28,7 +28,6 @@ package net.luckperms.api.actionlog;
import net.luckperms.api.LuckPermsProvider;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jetbrains.annotations.ApiStatus.NonExtendable;
import java.time.Instant;
import java.util.Optional;
@@ -36,10 +35,7 @@ import java.util.UUID;
/**
* Represents a logged action.
*
* <p>API users should not implement this interface directly.</p>
*/
@NonExtendable
public interface Action extends Comparable<Action> {
/**
@@ -85,7 +81,6 @@ public interface Action extends Comparable<Action> {
/**
* Represents the source of an action.
*/
@NonExtendable
interface Source {
/**
@@ -107,7 +102,6 @@ public interface Action extends Comparable<Action> {
/**
* Represents the target of an action.
*/
@NonExtendable
interface Target {
/**
@@ -132,7 +126,7 @@ public interface Action extends Comparable<Action> {
@NonNull Type getType();
/**
* Represents the type of {@link Target}.
* Represents the type of a {@link Target}.
*/
enum Type {
USER, GROUP, TRACK
@@ -25,7 +25,6 @@
package net.luckperms.api.actionlog;
import net.luckperms.api.actionlog.filter.ActionFilter;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.Unmodifiable;
@@ -41,11 +40,7 @@ import java.util.UUID;
* You can add to the log using the {@link ActionLogger}, and then request an updated copy.</p>
*
* <p>All methods are thread safe, and return immutable and thread safe collections.</p>
*
* @deprecated Use {@link ActionLogger#queryActions(ActionFilter)} or
* {@link ActionLogger#queryActions(ActionFilter, int, int)} instead.
*/
@Deprecated
public interface ActionLog {
/**
@@ -25,11 +25,9 @@
package net.luckperms.api.actionlog;
import net.luckperms.api.actionlog.filter.ActionFilter;
import net.luckperms.api.util.Page;
import net.luckperms.api.messaging.MessagingService;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
@@ -48,78 +46,43 @@ public interface ActionLogger {
* Gets a {@link ActionLog} instance from the plugin storage.
*
* @return a log instance
* @deprecated Use {@link #queryActions(ActionFilter)} or {@link #queryActions(ActionFilter, int, int)} instead. These methods
* are more efficient (they don't load the full action log into memory) and allow for pagination.
*/
@Deprecated
@NonNull CompletableFuture<ActionLog> getLog();
/**
* Gets all actions from the action log matching the given {@code filter}.
* Submits a log entry to the plugin to be handled.
*
* <p>If the filter is {@code null}, all actions will be returned.</p>
* <p>This method submits the log to the storage provider and broadcasts
* it.</p>
*
* <p>Unlike {@link #queryActions(ActionFilter, int, int)}, this method does not implement any pagination and will return
* all entries at once.</p>
* <p>It is therefore roughly equivalent to calling
* {@link #submitToStorage(Action)} and {@link #broadcastAction(Action)},
* however, using this method is preferred to making the calls individually.</p>
*
* @param filter the filter, optional
* @return the actions
* @since 5.5
*/
@NonNull CompletableFuture<List<Action>> queryActions(@NonNull ActionFilter filter);
/**
* Gets a page of actions from the action log matching the given {@code filter}.
*
* <p>If the filter is {@code null}, all actions will be returned.</p>
*
* @param filter the filter, optional
* @param pageSize the size of the page
* @param pageNumber the page number
* @return the page of actions
* @since 5.5
*/
@NonNull CompletableFuture<Page<Action>> queryActions(@NonNull ActionFilter filter, int pageSize, int pageNumber);
/**
* Submits a logged action to LuckPerms.
*
* <p>This method submits the action to the storage provider to be persisted in the action log.
* It also broadcasts it to administrator players on the current instance and to admins on other
* connected servers if a messaging service is configured.</p>
*
* <p>It is roughly equivalent to calling
* {@link #submitToStorage(Action)} followed by {@link #broadcastAction(Action)},
* however using this method is preferred to making the calls individually.</p>
*
* <p>If you want to submit an action log entry but don't know which method to pick,
* <p>If you want to submit a log entry but don't know which method to pick,
* use this one.</p>
*
* @param entry the entry to submit
* @return a future which will complete when the action is submitted
* @return a future which will complete when the action is done
*/
@NonNull CompletableFuture<Void> submit(@NonNull Action entry);
/**
* Submits a logged action to LuckPerms and persists it in the storage backend.
*
* <p>This method does not broadcast the action or send it through the messaging service.</p>
* Submits a log entry to the plugins storage handler.
*
* @param entry the entry to submit
* @return a future which will complete when the action is submitted
* @return a future which will complete when the action is done
*/
@NonNull CompletableFuture<Void> submitToStorage(@NonNull Action entry);
/**
* Submits a logged action to LuckPerms and broadcasts it to administrators.
* Submits a log entry to the plugins log broadcasting handler.
*
* <p>The broadcast is made to administrator players on the current instance
* and to admins on other connected servers if a messaging service is configured.</p>
*
* <p>This method does not save the action to the plugin storage backend.</p>
* <p>If enabled, this method will also dispatch the log entry via the
* plugins {@link MessagingService}.</p>
*
* @param entry the entry to submit
* @return a future which will complete when the action is broadcasted
* @return a future which will complete when the action is done
*/
@NonNull CompletableFuture<Void> broadcastAction(@NonNull Action entry);
@@ -1,114 +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 net.luckperms.api.actionlog.filter;
import net.luckperms.api.LuckPermsProvider;
import net.luckperms.api.actionlog.Action;
import org.jetbrains.annotations.ApiStatus.NonExtendable;
import java.util.UUID;
import java.util.function.Predicate;
/**
* A predicate filter which matches certain {@link Action}s.
*
* <p>API users should not implement this interface directly.</p>
*
* @since 5.5
*/
@NonExtendable
public interface ActionFilter extends Predicate<Action> {
/**
* Gets an {@link ActionFilter} which matches any action.
*
* @return the matcher
*/
static ActionFilter any() {
return LuckPermsProvider.get().getActionFilterFactory().any();
}
/**
* Gets an {@link ActionFilter} which matches actions with a specific source user.
*
* @param uniqueId the source user unique id
* @return the matcher
*/
static ActionFilter source(UUID uniqueId) {
return LuckPermsProvider.get().getActionFilterFactory().source(uniqueId);
}
/**
* Gets an {@link ActionFilter} which matches actions which target a specific user.
*
* @param uniqueId the target user unique id
* @return the matcher
*/
static ActionFilter user(UUID uniqueId) {
return LuckPermsProvider.get().getActionFilterFactory().user(uniqueId);
}
/**
* Gets an {@link ActionFilter} which matches actions which target a specific group.
*
* @param name the target group name
* @return the matcher
*/
static ActionFilter group(String name) {
return LuckPermsProvider.get().getActionFilterFactory().group(name);
}
/**
* Gets an {@link ActionFilter} which matches actions which target a specific track.
*
* @param name the target track name
* @return the matcher
*/
static ActionFilter track(String name) {
return LuckPermsProvider.get().getActionFilterFactory().track(name);
}
/**
* Gets an {@link ActionFilter} which matches actions which contain a specific search query in the source name,
* target name or description.
*
* @param query the search query
* @return the matcher
*/
static ActionFilter search(String query) {
return LuckPermsProvider.get().getActionFilterFactory().search(query);
}
/**
* Tests to see if the given {@link Action} matches the filter.
*
* @param action the action to test
* @return true if the action matched
*/
@Override
boolean test(Action action);
}
@@ -1,88 +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 net.luckperms.api.actionlog.filter;
import org.jetbrains.annotations.ApiStatus.Internal;
import java.util.UUID;
/**
* A factory which creates {@link ActionFilter}s.
*
* @since 5.5
*/
@Internal
public interface ActionFilterFactory {
/**
* Gets an {@link ActionFilter} which matches any action.
*
* @return the matcher
*/
ActionFilter any();
/**
* Gets an {@link ActionFilter} which matches actions with a specific source user.
*
* @param uniqueId the source user unique id
* @return the matcher
*/
ActionFilter source(UUID uniqueId);
/**
* Gets an {@link ActionFilter} which matches actions which target a specific user.
*
* @param uniqueId the target user unique id
* @return the matcher
*/
ActionFilter user(UUID uniqueId);
/**
* Gets an {@link ActionFilter} which matches actions which target a specific group.
*
* @param name the target group name
* @return the matcher
*/
ActionFilter group(String name);
/**
* Gets an {@link ActionFilter} which matches actions which target a specific track.
*
* @param name the target track name
* @return the matcher
*/
ActionFilter track(String name);
/**
* Gets an {@link ActionFilter} which matches actions which contain a specific search query in the source name,
* target name or description.
*
* @param query the search query
* @return the matcher
*/
ActionFilter search(String query);
}
@@ -1,29 +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.
*/
/**
* {@link net.luckperms.api.actionlog.Action} filters.
*/
package net.luckperms.api.actionlog.filter;
@@ -33,11 +33,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.UUID;
/**
* Called when a log entry is received via the MessagingService.
*
* <p>Note: listening to this event is the same as listening to the {@link LogBroadcastEvent}
* and filtering for {@link LogBroadcastEvent#getOrigin() origin} =
* {@link net.luckperms.api.event.log.LogBroadcastEvent.Origin#REMOTE REMOTE}.</p>
* Called when a log entry is received via the MessagingService
*/
public interface LogReceiveEvent extends LuckPermsEvent {
@@ -1,59 +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 net.luckperms.api.event.messaging;
import net.luckperms.api.event.LuckPermsEvent;
import net.luckperms.api.event.util.Param;
import net.luckperms.api.messaging.MessagingService;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Called when a custom payload message is received via the {@link MessagingService}.
*
* <p>This event is effectively the 'other end' of
* {@link MessagingService#sendCustomMessage(String, String)}.</p>
*
* @since 5.5
*/
public interface CustomMessageReceiveEvent extends LuckPermsEvent {
/**
* Gets the channel id.
*
* @return the channel id
*/
@Param(0)
@NonNull String getChannelId();
/**
* Gets the custom payload that was sent.
*
* @return the custom payload
*/
@Param(1)
@NonNull String getPayload();
}
@@ -1,29 +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.
*/
/**
* Events relating to the {@link net.luckperms.api.messaging.MessagingService}.
*/
package net.luckperms.api.event.messaging;
@@ -1,81 +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 net.luckperms.api.event.sync;
import net.luckperms.api.event.LuckPermsEvent;
import net.luckperms.api.event.util.Param;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.UUID;
/**
* Called after a network synchronisation task has been completed.
*
* <p>Note: the generic {@link PostSyncEvent} will also be called for {@link SyncType#FULL full syncs}.</p>
*
* @since 5.5
*/
public interface PostNetworkSyncEvent extends LuckPermsEvent {
/**
* Gets the ID of the sync request
*
* @return the id of the sync request
*/
@Param(0)
@NonNull UUID getSyncId();
/**
* Gets the sync type.
*
* @return the sync type
*/
@Param(1)
@NonNull SyncType getType();
/**
* Gets if a sync occurred.
*
* <p>For {@link SyncType} = {@link SyncType#FULL FULL}, this method always returns true.</p>
*
* <p>For {@link SyncType} = {@link SyncType#SPECIFIC_USER SPECIFIC_USER}, this method returns true if the
* user in question was online/loaded in memory at the time, and false otherwise.</p>
*
* @return if a sync occurred
*/
@Param(2)
boolean didSyncOccur();
/**
* Gets the unique id of the specific user that has been synced, if applicable.
*
* @return the unique id of the specific user
*/
@Param(3)
@Nullable UUID getSpecificUserUniqueId();
}
@@ -28,10 +28,7 @@ package net.luckperms.api.event.sync;
import net.luckperms.api.event.LuckPermsEvent;
/**
* Called after a full synchronisation task has been completed.
*
* <p>Note: this event is also called after synchronisations that were triggered over the network.
* In other words, this event will be called in addition to {@link PostNetworkSyncEvent}.</p>
* Called when an sync task has been completed
*/
public interface PostSyncEvent extends LuckPermsEvent {
@@ -29,15 +29,11 @@ import net.luckperms.api.event.LuckPermsEvent;
import net.luckperms.api.event.type.Cancellable;
import net.luckperms.api.event.util.Param;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.UUID;
/**
* Called after a request for synchronisation has been received via the messaging service,
* but before it has actually been completed.
*
* <p>Note: the generic {@link PreSyncEvent} will also be called for {@link SyncType#FULL full syncs}.</p>
* Called before a received network sync task runs
*/
public interface PreNetworkSyncEvent extends LuckPermsEvent, Cancellable {
@@ -49,22 +45,4 @@ public interface PreNetworkSyncEvent extends LuckPermsEvent, Cancellable {
@Param(0)
@NonNull UUID getSyncId();
/**
* Gets the sync type.
*
* @return the sync type
* @since 5.5
*/
@Param(1)
@NonNull SyncType getType();
/**
* Gets the unique id of the specific user that will be synced, if applicable.
*
* @return the unique id of the specific user
* @since 5.5
*/
@Param(2)
@Nullable UUID getSpecificUserUniqueId();
}
@@ -29,10 +29,7 @@ import net.luckperms.api.event.LuckPermsEvent;
import net.luckperms.api.event.type.Cancellable;
/**
* Called just before a full synchronisation task runs.
*
* <p>Note: this event is also called before synchronisations that were triggered over the network.
* In other words, this event will be called in addition to {@link PreNetworkSyncEvent}.</p>
* Called before a sync task runs
*/
public interface PreSyncEvent extends LuckPermsEvent, Cancellable {
@@ -1,45 +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 net.luckperms.api.event.sync;
/**
* Represents the type of synchronisation task.
*
* @since 5.5
*/
public enum SyncType {
/**
* A full sync will be performed - all groups, users and tracks
*/
FULL,
/**
* Only a specific user will be synced
*/
SPECIFIC_USER
}
@@ -26,12 +26,11 @@
package net.luckperms.api.messaging;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.event.messaging.CustomMessageReceiveEvent;
import net.luckperms.api.model.user.User;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* A means to send messages to other servers using the platforms networking
* A means to push changes to other servers using the platforms networking
*/
public interface MessagingService {
@@ -72,44 +71,4 @@ public interface MessagingService {
*/
void pushUserUpdate(@NonNull User user);
/**
* Uses the messaging service to send a message with a custom payload.
*
* <p>The intended use case of this functionality is to allow plugins/mods
* to send <b>lightweight</b> and <b>permissions-related</b> custom messages
* between instances, piggy-backing on top of the messenger abstraction
* already built into LuckPerms.</p>
*
* <p>It is <b>not</b> intended as a full message broker replacement/abstraction.
* Note that some of the messenger implementations in LuckPerms cannot handle
* a high volume of messages being sent (for example the SQL messenger).
* Additionally, some implementations do not give any guarantees that a message
* will be delivered on time or even at all (for example the plugin message
* messengers).</p>
*
* <p>With all of that in mind, please consider that if you are using this
* functionality to send messages that have nothing to do with LuckPerms or
* permissions, or that require guarantees around delivery reliability, you
* are most likely misusing the API and would be better off building your own
* integration with a message broker.</p>
*
* <p>Whilst there is (currently) no strict validation, it is recommended
* that the channel id should use the same format as Minecraft resource locations /
* namespaced keys. For example, a plugin called "SuperRanks" sending rank-up
* notifications using custom payload messages might use the channel id
* {@code "superranks:notifications"} for this purpose.</p>
*
* <p>The payload can be any valid UTF-8 string.</p>
*
* <p>The message will be delivered asynchronously.</p>
*
* <p>Other LuckPerms instances that receive the message will publish it to API
* consumers using the {@link CustomMessageReceiveEvent}.</p>
*
* @param channelId the channel id
* @param payload the message payload
* @since 5.5
*/
void sendCustomMessage(@NonNull String channelId, @NonNull String payload);
}
@@ -63,7 +63,6 @@ public interface DisplayNameNode extends ScopedNode<DisplayNameNode, DisplayName
*
* @param displayName the display name to set
* @return the builder
* @throws IllegalArgumentException if {@code displayName} is empty
*/
static @NonNull Builder builder(@NonNull String displayName) {
return builder().displayName(displayName);
@@ -67,7 +67,6 @@ public interface InheritanceNode extends ScopedNode<InheritanceNode, Inheritance
*
* @param group the group to set
* @return the builder
* @throws IllegalArgumentException if {@code group} is not a valid group name
*/
static @NonNull Builder builder(@NonNull String group) {
return builder().group(group);
@@ -71,7 +71,6 @@ public interface MetaNode extends ScopedNode<MetaNode, MetaNode.Builder> {
* @param key the meta key to set
* @param value the meta value to set
* @return the builder
* @throws IllegalArgumentException if {@code key} is empty
*/
static @NonNull Builder builder(@NonNull String key, @NonNull String value) {
return builder().key(key).value(value);
@@ -88,7 +88,6 @@ public interface PermissionNode extends ScopedNode<PermissionNode, PermissionNod
*
* @param permission the permission to set
* @return the builder
* @throws IllegalArgumentException if {@code permission} is empty
*/
static @NonNull Builder builder(@NonNull String permission) {
return builder().permission(permission);
@@ -75,7 +75,6 @@ public interface RegexPermissionNode extends ScopedNode<RegexPermissionNode, Reg
*
* @param pattern the pattern to set
* @return the builder
* @throws IllegalArgumentException if {@code pattern} is empty
*/
static @NonNull Builder builder(@NonNull String pattern) {
return builder().pattern(pattern);
@@ -1,51 +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 net.luckperms.api.platform;
import java.util.Map;
/**
* Represents the "health" status (healthcheck) of a LuckPerms implementation.
*
* @since 5.5
*/
public interface Health {
/**
* Gets if LuckPerms is healthy.
*
* @return if LuckPerms is healthy
*/
boolean isHealthy();
/**
* Gets extra metadata/details about the healthcheck result.
*
* @return details about the healthcheck status
*/
Map<String, Object> getDetails();
}
@@ -76,7 +76,6 @@ public interface Platform {
NUKKIT("Nukkit"),
VELOCITY("Velocity"),
FABRIC("Fabric"),
NEOFORGE("NeoForge"),
FORGE("Forge"),
STANDALONE("Standalone");
@@ -48,7 +48,7 @@ public interface DataTypeFilterFunction {
/**
* Creates a {@link DataTypeFilterFunction} that always returns the given
* {@code predicate} (commonly one of the values in {@link DataTypeFilter}).
* {@code predicate}.
*
* @param predicate the predicate
* @return the data type filter function
@@ -96,27 +96,6 @@ public interface TrackManager {
*/
@NonNull CompletableFuture<Void> deleteTrack(@NonNull Track track);
/**
* Loads (or creates) a track from the plugin's storage provider, applies the given {@code action},
* then saves the track's data back to storage.
*
* <p>This method effectively calls {@link #createAndLoadTrack(String)}, followed by the
* {@code action}, then {@link #saveTrack(Track)}, and returns an encapsulation of the whole
* process as a {@link CompletableFuture}. </p>
*
* @param name the name of the track
* @param action the action to apply to the track
* @return a future to encapsulate the operation
* @since 5.5
*/
default @NonNull CompletableFuture<Void> modifyTrack(@NonNull String name, @NonNull Consumer<? super Track> action) {
/* This default method is overridden in the implementation, and is just here
to demonstrate what this method does in the API sources. */
return createAndLoadTrack(name)
.thenApplyAsync(track -> { action.accept(track); return track; })
.thenCompose(this::saveTrack);
}
/**
* Loads all tracks into memory.
*
@@ -1,53 +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 net.luckperms.api.util;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.List;
/**
* Represents a page of entries.
*
* @since 5.5
*/
public interface Page<T> {
/**
* Gets the entries on this page.
*
* @return the entries
*/
@NonNull List<T> entries();
/**
* Gets the total/overall number of entries (not just the number of entries on this page).
*
* @return the total number of entries
*/
int overallSize();
}
+2 -12
View File
@@ -13,7 +13,7 @@ subprojects {
apply plugin: 'org.cadixdev.licenser'
group = 'me.lucko.luckperms'
version = '5.5-SNAPSHOT'
version = '5.4-SNAPSHOT'
sourceCompatibility = 1.8
targetCompatibility = 1.8
@@ -52,7 +52,7 @@ subprojects {
}
project.ext.majorVersion = '5'
project.ext.minorVersion = '5'
project.ext.minorVersion = '4'
project.ext.patchVersion = determinePatchVersion()
project.ext.apiVersion = project.ext.majorVersion + '.' + project.ext.minorVersion
project.ext.fullVersion = project.ext.apiVersion + '.' + project.ext.patchVersion
@@ -64,16 +64,6 @@ subprojects {
}
repositories {
// Fix issue with lwjgl-freetype not being found on macOS / ForgeGradle issue
//
// Could not resolve all files for configuration ':_compileJava_1'.
// Could not find lwjgl-freetype-3.3.3-natives-macos-patch.jar (org.lwjgl:lwjgl-freetype:3.3.3).
maven {
url "https://libraries.minecraft.net"
content {
includeModule("org.lwjgl", "lwjgl-freetype")
}
}
mavenCentral()
maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' }
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
+1 -1
View File
@@ -11,7 +11,7 @@ dependencies {
compileOnly project(':common:loader-utils')
compileOnly 'com.destroystokyo.paper:paper-api:1.15.2-R0.1-SNAPSHOT'
compileOnly('net.kyori:adventure-platform-bukkit:4.4.0') {
compileOnly('net.kyori:adventure-platform-bukkit:4.1.0') {
exclude(module: 'adventure-bom')
exclude(module: 'adventure-api')
exclude(module: 'adventure-nbt')
+14 -2
View File
@@ -2,6 +2,10 @@ plugins {
alias(libs.plugins.shadow)
}
configurations {
testJar
}
repositories {
maven { url 'https://repo.papermc.io/repository/maven-public/' }
}
@@ -25,12 +29,20 @@ shadowJar {
from {
project(':bukkit').tasks.shadowJar.archiveFile
}
}
manifest {
attributes(["paperweight-mappings-namespace": "mojang"])
tasks.register('copyTestJar', Copy) {
from tasks.shadowJar.archiveFile
into layout.buildDirectory.dir('testJars')
rename { String fileName ->
return 'luckperms-bukkit.testjar'
}
}
artifacts.add('testJar', layout.buildDirectory.dir('testJars')) {
builtBy('copyTestJar')
}
artifacts {
archives shadowJar
}
@@ -25,46 +25,85 @@
package me.lucko.luckperms.bukkit.context;
import com.github.benmanes.caffeine.cache.LoadingCache;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.bukkit.inject.permissible.LuckPermsPermissible;
import me.lucko.luckperms.bukkit.inject.permissible.PermissibleInjector;
import me.lucko.luckperms.common.context.manager.DetachedContextManager;
import me.lucko.luckperms.common.context.manager.QueryOptionsSupplier;
import me.lucko.luckperms.common.cache.LoadingMap;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.context.manager.ContextManager;
import me.lucko.luckperms.common.context.manager.QueryOptionsCache;
import me.lucko.luckperms.common.util.CaffeineFactory;
import net.luckperms.api.context.ImmutableContextSet;
import net.luckperms.api.query.OptionKey;
import net.luckperms.api.query.QueryOptions;
import org.bukkit.entity.Player;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class BukkitContextManager extends DetachedContextManager<Player, Player> {
public class BukkitContextManager extends ContextManager<Player, Player> {
public static final OptionKey<Boolean> OP_OPTION = OptionKey.of("op", Boolean.class);
// cache the creation of ContextsCache instances for online players with no expiry
private final LoadingMap<Player, QueryOptionsCache<Player>> onlineSubjectCaches = LoadingMap.of(key -> new QueryOptionsCache<>(key, this));
// cache the creation of ContextsCache instances for offline players with a 1m expiry
private final LoadingCache<Player, QueryOptionsCache<Player>> offlineSubjectCaches = CaffeineFactory.newBuilder()
.expireAfterAccess(1, TimeUnit.MINUTES)
.build(key -> {
QueryOptionsCache<Player> cache = this.onlineSubjectCaches.getIfPresent(key);
if (cache != null) {
return cache;
}
return new QueryOptionsCache<>(key, this);
});
public BukkitContextManager(LPBukkitPlugin plugin) {
super(plugin, Player.class, Player.class);
}
public void onPlayerQuit(Player player) {
this.onlineSubjectCaches.remove(player);
}
@Override
public UUID getUniqueId(Player player) {
return player.getUniqueId();
}
@Override
public @Nullable QueryOptionsSupplier getQueryOptionsSupplier(Player subject) {
Objects.requireNonNull(subject, "subject");
LuckPermsPermissible permissible = PermissibleInjector.get(subject);
if (permissible != null) {
return permissible.getQueryOptionsSupplier();
public QueryOptionsCache<Player> getCacheFor(Player subject) {
if (subject == null) {
throw new NullPointerException("subject");
}
if (subject.isOnline()) {
return this.onlineSubjectCaches.get(subject);
} else {
return this.offlineSubjectCaches.get(subject);
}
return null;
}
@Override
public void customizeQueryOptions(Player subject, QueryOptions.Builder builder) {
if (subject.isOp()) {
builder.option(OP_OPTION, true);
protected void invalidateCache(Player subject) {
QueryOptionsCache<Player> cache = this.onlineSubjectCaches.getIfPresent(subject);
if (cache != null) {
cache.invalidate();
}
cache = this.offlineSubjectCaches.getIfPresent(subject);
if (cache != null) {
cache.invalidate();
}
}
@Override
public QueryOptions formQueryOptions(Player subject, ImmutableContextSet contextSet) {
QueryOptions.Builder queryOptions = this.plugin.getConfiguration().get(ConfigKeys.GLOBAL_QUERY_OPTIONS).toBuilder();
if (subject.isOp()) {
queryOptions.option(OP_OPTION, true);
}
return queryOptions.context(contextSet).build();
}
}
@@ -32,7 +32,7 @@ import me.lucko.luckperms.bukkit.calculator.OpProcessor;
import me.lucko.luckperms.bukkit.calculator.PermissionMapProcessor;
import me.lucko.luckperms.common.cacheddata.result.TristateResult;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.context.manager.QueryOptionsSupplier;
import me.lucko.luckperms.common.context.manager.QueryOptionsCache;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.verbose.event.CheckOrigin;
import net.luckperms.api.query.QueryOptions;
@@ -68,7 +68,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* "Hot" method calls, (namely #hasPermission) are significantly faster than the base implementation.
*
* This class is **thread safe**. This means that when LuckPerms is installed on the server,
* it is safe to call Player#hasPermission asynchronously.
* is is safe to call Player#hasPermission asynchronously.
*/
public class LuckPermsPermissible extends PermissibleBase {
@@ -93,7 +93,7 @@ public class LuckPermsPermissible extends PermissibleBase {
private final LPBukkitPlugin plugin;
// caches context lookups for the player
private final QueryOptionsSupplier queryOptionsSupplier;
private final QueryOptionsCache<Player> queryOptionsSupplier;
// the players previous permissible. (the one they had before this one was injected)
private PermissibleBase oldPermissible = null;
@@ -110,7 +110,7 @@ public class LuckPermsPermissible extends PermissibleBase {
this.user = Objects.requireNonNull(user, "user");
this.player = Objects.requireNonNull(player, "player");
this.plugin = Objects.requireNonNull(plugin, "plugin");
this.queryOptionsSupplier = plugin.getContextManager().createQueryOptionsSupplier(player);
this.queryOptionsSupplier = plugin.getContextManager().getCacheFor(player);
injectFakeAttachmentsList();
}
@@ -293,7 +293,7 @@ public class LuckPermsPermissible extends PermissibleBase {
// the query options cache when op status changes.
// (#invalidate is a fast call)
if (this.queryOptionsSupplier != null) { // this method is called by the super class constructor, before this class has fully initialised
this.queryOptionsSupplier.invalidateCache();
this.queryOptionsSupplier.invalidate();
}
// but we don't need to do anything else in this method, unlike the CB impl.
@@ -316,10 +316,6 @@ public class LuckPermsPermissible extends PermissibleBase {
return this.plugin;
}
public QueryOptionsSupplier getQueryOptionsSupplier() {
return this.queryOptionsSupplier;
}
PermissibleBase getOldPermissible() {
return this.oldPermissible;
}
@@ -413,7 +409,9 @@ public class LuckPermsPermissible extends PermissibleBase {
@Override public PermissionAttachment remove(int index) { throw new UnsupportedOperationException(); }
@Override public int indexOf(Object o) { throw new UnsupportedOperationException(); }
@Override public int lastIndexOf(Object o) { throw new UnsupportedOperationException(); }
@Override public @NonNull ListIterator<PermissionAttachment> listIterator(int index) { throw new UnsupportedOperationException(); }
@Override public @NonNull List<PermissionAttachment> subList(int fromIndex, int toIndex) { throw new UnsupportedOperationException(); }
@Override
public @NonNull ListIterator<PermissionAttachment> listIterator(int index) { throw new UnsupportedOperationException(); }
@Override
public @NonNull List<PermissionAttachment> subList(int fromIndex, int toIndex) { throw new UnsupportedOperationException(); }
}
}
@@ -30,7 +30,6 @@ import me.lucko.luckperms.common.plugin.logging.PluginLogger;
import org.bukkit.entity.Player;
import org.bukkit.permissions.PermissibleBase;
import org.bukkit.permissions.PermissionAttachment;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.lang.reflect.Field;
import java.util.List;
@@ -162,19 +161,6 @@ public final class PermissibleInjector {
}
}
public static @Nullable LuckPermsPermissible get(Player player) {
PermissibleBase permissibleBase;
try {
permissibleBase = (PermissibleBase) HUMAN_ENTITY_PERMISSIBLE_FIELD.get(player);
} catch (IllegalAccessException e) {
return null;
}
if (permissibleBase instanceof LuckPermsPermissible) {
return (LuckPermsPermissible) permissibleBase;
}
return null;
}
public static void checkInjected(Player player, PluginLogger logger) {
PermissibleBase permissibleBase;
try {
@@ -253,6 +253,9 @@ public class BukkitConnectionListener extends AbstractConnectionListener impleme
if (this.plugin.getConfiguration().get(ConfigKeys.AUTO_OP)) {
player.setOp(false);
}
// remove their contexts cache
this.plugin.getContextManager().onPlayerQuit(player);
}, 1L);
}
@@ -36,25 +36,12 @@ 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 org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import java.util.Locale;
import java.util.regex.Pattern;
public class BukkitPlatformListener implements Listener {
private static final Pattern OP_COMMAND_PATTERN = Pattern.compile("^/?(\\w+:)?(deop|op)( .*)?$", Pattern.CASE_INSENSITIVE);
private static final boolean PLUGIN_DESCRIPTION_FILE_PROVIDES_SUPPORTED;
static {
boolean supported = false;
try {
PluginDescriptionFile.class.getMethod("getProvides");
supported = true;
} catch (NoSuchMethodException ignored) {
}
PLUGIN_DESCRIPTION_FILE_PROVIDES_SUPPORTED = supported;
}
private static final Pattern OP_COMMAND_PATTERN = Pattern.compile("^/?(\\w+:)?(deop|op)( .*)?$");
private final LPBukkitPlugin plugin;
@@ -64,17 +51,17 @@ public class BukkitPlatformListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onPlayerCommand(PlayerCommandPreprocessEvent e) {
handleCommand(e.getPlayer(), e.getMessage(), e);
handleCommand(e.getPlayer(), e.getMessage().toLowerCase(Locale.ROOT), e);
}
@EventHandler(ignoreCancelled = true)
public void onServerCommand(ServerCommandEvent e) {
handleCommand(e.getSender(), e.getCommand(), e);
handleCommand(e.getSender(), e.getCommand().toLowerCase(Locale.ROOT), e);
}
@EventHandler(ignoreCancelled = true)
public void onRemoteServerCommand(RemoteServerCommandEvent e) {
handleCommand(e.getSender(), e.getCommand(), e);
handleCommand(e.getSender(), e.getCommand().toLowerCase(Locale.ROOT), e);
}
private void handleCommand(CommandSender sender, String cmdLine, Cancellable event) {
@@ -94,11 +81,7 @@ public class BukkitPlatformListener implements Listener {
@EventHandler
public void onPluginEnable(PluginEnableEvent e) {
Plugin p = e.getPlugin();
boolean shouldHook = p.getName().equalsIgnoreCase("Vault") ||
(PLUGIN_DESCRIPTION_FILE_PROVIDES_SUPPORTED && p.getDescription().getProvides().contains("Vault"));
if (shouldHook) {
if (e.getPlugin().getName().equalsIgnoreCase("Vault")) {
this.plugin.tryVaultHook(true);
}
}
+1 -31
View File
@@ -717,35 +717,5 @@ update-client-command-list: true
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.wiki/w/Target_selectors
# See here for more info: https://minecraft.gamepedia.com/Commands#Target_selectors
resolve-command-selectors: false
# If the plugin should run in "read-only" mode for commands.
#
# In this mode, players or the console will only be able to execute commands that read or view
# data, and will not be able to execute any LP commands that modify data.
#
# If enabling read-only mode for just players, be aware that some other plugins may allow players
# to execute commands as the console, allowing them to run LP commands indirectly.
#
# Note: This does not affect interactions with LuckPerms via the API.
commands-read-only-mode:
players: false
console: false
# If LuckPerms commands should be disabled. When true, this will prevent all LP commands from being
# executed. In a sense this is a more extreme version of read-only mode (see above).
#
# LuckPerms will still act as the permission manager, but server administrators will be unable to
# make changes via commands.
#
# If disabling commands just for players, be aware that some other plugins may allow players to
# execute commands as the console, allowing them to run commands indirectly.
#
# If commands are disabled for both players and the console, LuckPerms will not attempt to register
# a command with the server at all & in a sense will be invisible to both players and admins.
#
# Note: This does not affect interactions with LuckPerms via the API.
disable-luckperms-commands:
players: false
console: false
+1 -1
View File
@@ -7,7 +7,7 @@ dependencies {
compileOnly project(':common:loader-utils')
compileOnly 'net.md-5:bungeecord-api:1.16-R0.4'
compileOnly('net.kyori:adventure-platform-bungeecord:4.4.0') {
compileOnly('net.kyori:adventure-platform-bungeecord:4.1.0') {
exclude(module: 'adventure-bom')
exclude(module: 'adventure-api')
exclude(module: 'adventure-nbt')
@@ -25,13 +25,25 @@
package me.lucko.luckperms.bungee.context;
import com.github.benmanes.caffeine.cache.LoadingCache;
import me.lucko.luckperms.bungee.LPBungeePlugin;
import me.lucko.luckperms.common.context.manager.SimpleContextManager;
import me.lucko.luckperms.common.context.manager.ContextManager;
import me.lucko.luckperms.common.context.manager.InlineQueryOptionsSupplier;
import me.lucko.luckperms.common.context.manager.QueryOptionsSupplier;
import me.lucko.luckperms.common.util.CaffeineFactory;
import net.luckperms.api.context.ImmutableContextSet;
import net.luckperms.api.query.QueryOptions;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class BungeeContextManager extends ContextManager<ProxiedPlayer, ProxiedPlayer> {
private final LoadingCache<ProxiedPlayer, QueryOptions> contextsCache = CaffeineFactory.newBuilder()
.expireAfterWrite(50, TimeUnit.MILLISECONDS)
.build(this::calculate);
public class BungeeContextManager extends SimpleContextManager<ProxiedPlayer, ProxiedPlayer> {
public BungeeContextManager(LPBungeePlugin plugin) {
super(plugin, ProxiedPlayer.class, ProxiedPlayer.class);
}
@@ -40,4 +52,35 @@ public class BungeeContextManager extends SimpleContextManager<ProxiedPlayer, Pr
public UUID getUniqueId(ProxiedPlayer player) {
return player.getUniqueId();
}
@Override
public QueryOptionsSupplier getCacheFor(ProxiedPlayer subject) {
if (subject == null) {
throw new NullPointerException("subject");
}
return new InlineQueryOptionsSupplier<>(subject, this.contextsCache);
}
// override getContext, getQueryOptions and invalidateCache to skip the QueryOptionsSupplier
@Override
public ImmutableContextSet getContext(ProxiedPlayer subject) {
return getQueryOptions(subject).context();
}
@Override
public QueryOptions getQueryOptions(ProxiedPlayer subject) {
return this.contextsCache.get(subject);
}
@Override
protected void invalidateCache(ProxiedPlayer subject) {
this.contextsCache.invalidate(subject);
}
@Override
public QueryOptions formQueryOptions(ProxiedPlayer subject, ImmutableContextSet contextSet) {
return formQueryOptions(contextSet);
}
}
-30
View File
@@ -611,33 +611,3 @@ disable-bulkupdate: false
#
# - When this happens, the plugin will set their primary group back to default.
prevent-primary-group-removal: false
# If the plugin should run in "read-only" mode for commands.
#
# In this mode, players or the console will only be able to execute commands that read or view
# data, and will not be able to execute any LP commands that modify data.
#
# If enabling read-only mode for just players, be aware that some other plugins may allow players
# to execute commands as the console, allowing them to run LP commands indirectly.
#
# Note: This does not affect interactions with LuckPerms via the API.
commands-read-only-mode:
players: false
console: false
# If LuckPerms commands should be disabled. When true, this will prevent all LP commands from being
# executed. In a sense this is a more extreme version of read-only mode (see above).
#
# LuckPerms will still act as the permission manager, but server administrators will be unable to
# make changes via commands.
#
# If disabling commands just for players, be aware that some other plugins may allow players to
# execute commands as the console, allowing them to run commands indirectly.
#
# If commands are disabled for both players and the console, LuckPerms will not attempt to register
# a command with the server at all & in a sense will be invisible to both players and admins.
#
# Note: This does not affect interactions with LuckPerms via the API.
disable-luckperms-commands:
players: false
console: false
+8 -20
View File
@@ -4,11 +4,7 @@ plugins {
}
test {
useJUnitPlatform {
if (!project.hasProperty('dockerTests')) {
excludeTags 'docker'
}
}
useJUnitPlatform {}
}
jacocoTestReport {
@@ -16,19 +12,12 @@ jacocoTestReport {
}
dependencies {
testImplementation 'org.slf4j:slf4j-simple:1.7.36'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.1'
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.1'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.1'
testImplementation 'org.testcontainers:junit-jupiter:1.20.4'
testImplementation 'org.mockito:mockito-core:5.11.0'
testImplementation 'org.mockito:mockito-junit-jupiter:5.11.0'
testImplementation 'org.mockito:mockito-core:4.11.0'
testImplementation 'org.mockito:mockito-junit-jupiter:4.11.0'
testImplementation 'com.h2database:h2:2.1.214'
testImplementation 'org.mongodb:mongodb-driver-legacy:4.5.0'
testImplementation 'org.spongepowered:configurate-yaml:3.7.2'
testImplementation 'org.spongepowered:configurate-hocon:3.7.2'
testImplementation 'me.lucko.configurate:configurate-toml:3.7'
testImplementation 'net.luckperms:rest-api-java-client:0.1-SNAPSHOT'
api project(':api')
api 'org.checkerframework:checker-qual:3.12.0'
@@ -39,29 +28,29 @@ dependencies {
compileOnly 'org.slf4j:slf4j-api:1.7.30'
compileOnly 'org.apache.logging.log4j:log4j-api:2.14.0'
api('net.kyori:adventure-api:4.21.0') {
api('net.kyori:adventure-api:4.11.0') {
exclude(module: 'adventure-bom')
exclude(module: 'checker-qual')
exclude(module: 'annotations')
}
api('net.kyori:adventure-text-serializer-gson:4.21.0') {
api('net.kyori:adventure-text-serializer-gson:4.11.0') {
exclude(module: 'adventure-bom')
exclude(module: 'adventure-api')
exclude(module: 'gson')
}
api('net.kyori:adventure-text-serializer-legacy:4.21.0') {
api('net.kyori:adventure-text-serializer-legacy:4.11.0') {
exclude(module: 'adventure-bom')
exclude(module: 'adventure-api')
}
api('net.kyori:adventure-text-serializer-plain:4.21.0') {
api('net.kyori:adventure-text-serializer-plain:4.11.0') {
exclude(module: 'adventure-bom')
exclude(module: 'adventure-api')
}
api("net.kyori:adventure-text-minimessage:4.21.0") {
api("net.kyori:adventure-text-minimessage:4.11.0") {
exclude(module: 'adventure-bom')
exclude(module: 'adventure-api')
}
@@ -101,5 +90,4 @@ dependencies {
compileOnly 'org.mongodb:mongodb-driver-legacy:4.5.0'
compileOnly 'org.postgresql:postgresql:42.6.0'
compileOnly 'org.yaml:snakeyaml:1.28'
compileOnly 'net.luckperms:rest-api-java-client:0.1-SNAPSHOT'
}
@@ -0,0 +1,108 @@
/*
* 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 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.ArrayList;
import java.util.List;
import java.util.SortedSet;
import java.util.UUID;
public class Log {
private static final Log EMPTY = new Log(ImmutableList.of());
public static Builder builder() {
return new Builder();
}
public static Log empty() {
return EMPTY;
}
private final SortedSet<LoggedAction> content;
Log(List<LoggedAction> content) {
this.content = ImmutableSortedSet.copyOf(content);
}
public SortedSet<LoggedAction> getContent() {
return this.content;
}
public SortedSet<LoggedAction> getContent(UUID actor) {
return this.content.stream()
.filter(e -> e.getSource().getUniqueId().equals(actor))
.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());
}
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());
}
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());
}
public SortedSet<LoggedAction> getSearch(String query) {
return this.content.stream()
.filter(e -> e.matchesSearch(query))
.collect(ImmutableCollectors.toSortedSet());
}
public static class Builder {
private final List<LoggedAction> content = new ArrayList<>();
public Builder add(LoggedAction e) {
this.content.add(e);
return this;
}
public Log build() {
if (this.content.isEmpty()) {
return EMPTY;
}
return new Log(this.content);
}
}
}
@@ -29,14 +29,12 @@ import me.lucko.luckperms.common.command.access.CommandPermission;
import me.lucko.luckperms.common.commands.log.LogNotify;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.messaging.InternalMessagingService;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
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.concurrent.CompletableFuture;
import java.util.regex.Pattern;
public class LogDispatcher {
@@ -66,12 +64,7 @@ public class LogDispatcher {
return !this.plugin.getEventDispatcher().dispatchLogBroadcast(cancelled, entry, origin);
}
// broadcast the entry to online players
private void broadcast(LoggedAction entry, LogBroadcastEvent.Origin broadcastOrigin, LogNotifyEvent.Origin origin, Sender sender) {
if (!shouldBroadcast(entry, broadcastOrigin)) {
return;
}
private void broadcast(LoggedAction entry, LogNotifyEvent.Origin origin, Sender sender) {
this.plugin.getOnlineSenders()
.filter(CommandPermission.LOG_NOTIFY::isAuthorized)
.filter(s -> {
@@ -81,46 +74,41 @@ public class LogDispatcher {
.forEach(s -> Message.LOG.send(s, entry));
}
// log the entry to storage
public CompletableFuture<Void> logToStorage(LoggedAction entry) {
public void dispatch(LoggedAction entry, Sender sender) {
if (!this.plugin.getEventDispatcher().dispatchLogPublish(false, entry)) {
return this.plugin.getStorage().logAction(entry);
} else {
return CompletableFuture.completedFuture(null);
this.plugin.getStorage().logAction(entry);
}
}
// log the entry to messaging
public CompletableFuture<Void> logToMessaging(LoggedAction entry) {
InternalMessagingService messagingService = this.plugin.getMessagingService().orElse(null);
if (messagingService != null) {
return messagingService.pushLog(entry);
} else {
return CompletableFuture.completedFuture(null);
this.plugin.getMessagingService().ifPresent(service -> service.pushLog(entry));
if (shouldBroadcast(entry, LogBroadcastEvent.Origin.LOCAL)) {
broadcast(entry, LogNotifyEvent.Origin.LOCAL, sender);
}
}
// log the entry to storage and messaging, and broadcast it to online players
private CompletableFuture<Void> dispatch(LoggedAction entry, Sender sender, LogBroadcastEvent.Origin broadcastOrigin, LogNotifyEvent.Origin origin) {
CompletableFuture<Void> storageFuture = logToStorage(entry);
CompletableFuture<Void> messagingFuture = logToMessaging(entry);
broadcast(entry, broadcastOrigin, origin, sender);
return CompletableFuture.allOf(storageFuture, messagingFuture);
}
public CompletableFuture<Void> dispatch(LoggedAction entry, Sender sender) {
return dispatch(entry, sender, LogBroadcastEvent.Origin.LOCAL, LogNotifyEvent.Origin.LOCAL);
}
public CompletableFuture<Void> dispatchFromApi(LoggedAction entry) {
return dispatch(entry, null, LogBroadcastEvent.Origin.LOCAL_API, LogNotifyEvent.Origin.LOCAL_API);
}
public void broadcastFromApi(LoggedAction entry) {
broadcast(entry, LogBroadcastEvent.Origin.LOCAL_API, LogNotifyEvent.Origin.LOCAL_API, null);
this.plugin.getMessagingService().ifPresent(extendedMessagingService -> extendedMessagingService.pushLog(entry));
if (shouldBroadcast(entry, LogBroadcastEvent.Origin.LOCAL_API)) {
broadcast(entry, LogNotifyEvent.Origin.LOCAL_API, null);
}
}
public void broadcastFromRemote(LoggedAction entry) {
broadcast(entry, LogBroadcastEvent.Origin.REMOTE, LogNotifyEvent.Origin.REMOTE, null);
public void dispatchFromApi(LoggedAction entry) {
if (!this.plugin.getEventDispatcher().dispatchLogPublish(false, entry)) {
try {
this.plugin.getStorage().logAction(entry).get();
} catch (Exception e) {
this.plugin.getLogger().warn("Error whilst storing action", e);
}
}
broadcastFromApi(entry);
}
public void dispatchFromRemote(LoggedAction entry) {
if (shouldBroadcast(entry, LogBroadcastEvent.Origin.REMOTE)) {
broadcast(entry, LogNotifyEvent.Origin.REMOTE, null);
}
}
}
@@ -26,7 +26,6 @@
package me.lucko.luckperms.common.actionlog;
import com.google.common.base.Strings;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.HolderType;
import me.lucko.luckperms.common.model.PermissionHolder;
@@ -45,10 +44,10 @@ import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
/**
* An implementation of {@link Action} and {@link Action.Builder},
@@ -124,11 +123,15 @@ public class LoggedAction implements Action {
return ActionComparator.INSTANCE.compare(this, other);
}
public boolean matchesSearch(String query) {
query = Objects.requireNonNull(query, "query").toLowerCase(Locale.ROOT);
return this.source.name.toLowerCase(Locale.ROOT).contains(query) ||
this.target.name.toLowerCase(Locale.ROOT).contains(query) ||
this.description.toLowerCase(Locale.ROOT).contains(query);
}
public void submit(LuckPermsPlugin plugin, Sender sender) {
CompletableFuture<Void> future = plugin.getLogDispatcher().dispatch(this, sender);
if (plugin.getConfiguration().get(ConfigKeys.LOG_SYNCHRONOUSLY_IN_COMMANDS)) {
future.join();
}
plugin.getLogDispatcher().dispatch(this, sender);
}
@Override
@@ -390,14 +393,14 @@ public class LoggedAction implements Action {
}
}
public static String getTypeString(Target.Type type) {
public static char getTypeCharacter(Target.Type type) {
switch (type) {
case USER:
return "U";
return 'U';
case GROUP:
return "G";
return 'G';
case TRACK:
return "T";
return 'T';
default:
throw new AssertionError();
}
@@ -1,60 +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.actionlog.filter;
import me.lucko.luckperms.common.filter.FilterField;
import net.luckperms.api.actionlog.Action;
import java.util.UUID;
public final class ActionFields {
public static final FilterField<Action, UUID> SOURCE_UNIQUE_ID = FilterField.named(
"SOURCE_UNIQUE_ID",
action -> action.getSource().getUniqueId()
);
public static final FilterField<Action, String> SOURCE_NAME = FilterField.named(
"SOURCE_NAME",
action -> action.getSource().getName()
);
public static final FilterField<Action, Action.Target.Type> TARGET_TYPE = FilterField.named(
"TARGET_TYPE",
action -> action.getTarget().getType()
);
public static final FilterField<Action, UUID> TARGET_UNIQUE_ID = FilterField.named(
"TARGET_UNIQUE_ID",
action -> action.getTarget().getUniqueId().orElse(null)
);
public static final FilterField<Action, String> TARGET_NAME = FilterField.named(
"TARGET_NAME",
action -> action.getTarget().getName()
);
public static final FilterField<Action, String> DESCRIPTION = FilterField.named(
"DESCRIPTION",
action -> action.getDescription()
);
}
@@ -1,70 +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.actionlog.filter;
import me.lucko.luckperms.common.filter.FilterField;
import me.lucko.luckperms.common.filter.mongo.FilterMongoBuilder;
import net.luckperms.api.actionlog.Action;
import java.util.UUID;
public final class ActionFilterMongoBuilder extends FilterMongoBuilder<Action> {
public static final ActionFilterMongoBuilder INSTANCE = new ActionFilterMongoBuilder();
private ActionFilterMongoBuilder() {
}
@Override
public String mapFieldName(FilterField<Action, ?> field) {
if (field == ActionFields.SOURCE_UNIQUE_ID) {
return "source.uniqueId";
} else if (field == ActionFields.SOURCE_NAME) {
return "source.name";
} else if (field == ActionFields.TARGET_TYPE) {
return "target.type";
} else if (field == ActionFields.TARGET_UNIQUE_ID) {
return "target.uniqueId";
} else if (field == ActionFields.TARGET_NAME) {
return "target.name";
} else if (field == ActionFields.DESCRIPTION) {
return "description";
}
throw new AssertionError(field);
}
@Override
public Object mapConstraintValue(Object value) {
if (value instanceof String | value instanceof UUID) {
return value;
} else if (value instanceof Action.Target.Type) {
return ((Action.Target.Type) value).name();
} else {
throw new IllegalArgumentException("Don't know how to map value with type: " + value.getClass().getName());
}
}
}
@@ -1,69 +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.actionlog.filter;
import me.lucko.luckperms.common.actionlog.LoggedAction;
import me.lucko.luckperms.common.filter.FilterField;
import me.lucko.luckperms.common.filter.sql.FilterSqlBuilder;
import net.luckperms.api.actionlog.Action;
import java.util.UUID;
public class ActionFilterSqlBuilder extends FilterSqlBuilder<Action> {
@Override
public void visitFieldName(FilterField<Action, ?> field) {
if (field == ActionFields.SOURCE_UNIQUE_ID) {
this.builder.append("actor_uuid");
} else if (field == ActionFields.SOURCE_NAME) {
this.builder.append("actor_name");
} else if (field == ActionFields.TARGET_TYPE) {
this.builder.append("type");
} else if (field == ActionFields.TARGET_UNIQUE_ID) {
this.builder.append("acted_uuid");
} else if (field == ActionFields.TARGET_NAME) {
this.builder.append("acted_name");
} else if (field == ActionFields.DESCRIPTION) {
this.builder.append("action");
} else {
throw new AssertionError(field);
}
}
@Override
public void visitConstraintValue(Object value) {
if (value instanceof String) {
this.builder.variable(((String) value));
} else if (value instanceof UUID) {
this.builder.variable(value.toString());
} else if (value instanceof Action.Target.Type) {
this.builder.variable(LoggedAction.getTypeString((Action.Target.Type) value));
} else {
throw new IllegalArgumentException("Don't know how to write value with type: " + value.getClass().getName());
}
}
}
@@ -1,110 +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.actionlog.filter;
import me.lucko.luckperms.common.filter.Comparison;
import me.lucko.luckperms.common.filter.ConstraintFactory;
import me.lucko.luckperms.common.filter.FilterList;
import net.luckperms.api.actionlog.Action;
import net.luckperms.api.actionlog.Action.Target;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.regex.Pattern;
public final class ActionFilters {
private ActionFilters() {}
// all actions
public static FilterList<Action> all() {
return FilterList.empty();
}
// all actions performed by a given source (actor)
public static FilterList<Action> source(UUID uniqueId) {
return FilterList.and(
ActionFields.SOURCE_UNIQUE_ID.isEqualTo(uniqueId, ConstraintFactory.UUIDS)
);
}
// all actions affecting a given user
public static FilterList<Action> user(UUID uniqueId) {
return FilterList.and(
ActionFields.TARGET_TYPE.isEqualTo(Target.Type.USER, TARGET_TYPE_CONSTRAINT_FACTORY),
ActionFields.TARGET_UNIQUE_ID.isEqualTo(uniqueId, ConstraintFactory.UUIDS)
);
}
// all actions affecting a given group
public static FilterList<Action> group(String name) {
return FilterList.and(
ActionFields.TARGET_TYPE.isEqualTo(Target.Type.GROUP, TARGET_TYPE_CONSTRAINT_FACTORY),
ActionFields.TARGET_NAME.isEqualTo(name, ConstraintFactory.STRINGS)
);
}
// all actions affecting a given track
public static FilterList<Action> track(String name) {
return FilterList.and(
ActionFields.TARGET_TYPE.isEqualTo(Target.Type.TRACK, TARGET_TYPE_CONSTRAINT_FACTORY),
ActionFields.TARGET_NAME.isEqualTo(name, ConstraintFactory.STRINGS)
);
}
// all actions matching the given search query
public static FilterList<Action> search(String query) {
return FilterList.or(
ActionFields.SOURCE_NAME.isSimilarTo("%" + query + "%", ConstraintFactory.STRINGS),
ActionFields.TARGET_NAME.isSimilarTo("%" + query + "%", ConstraintFactory.STRINGS),
ActionFields.DESCRIPTION.isSimilarTo("%" + query + "%", ConstraintFactory.STRINGS)
);
}
private static final ConstraintFactory<Target.Type> TARGET_TYPE_CONSTRAINT_FACTORY = new ConstraintFactory<Target.Type>() {
@Override
public Predicate<Target.Type> equal(Target.Type value) {
return value::equals;
}
@Override
public Predicate<Target.Type> notEqual(Target.Type value) {
return string -> !value.equals(string);
}
@Override
public Predicate<Target.Type> similar(Target.Type value) {
Pattern pattern = Comparison.compilePatternForLikeSyntax(value.toString());
return type -> pattern.matcher(type.toString()).matches();
}
@Override
public Predicate<Target.Type> notSimilar(Target.Type value) {
Pattern pattern = Comparison.compilePatternForLikeSyntax(value.toString());
return type -> !pattern.matcher(type.toString()).matches();
}
};
}
@@ -25,7 +25,6 @@
package me.lucko.luckperms.common.api;
import me.lucko.luckperms.common.api.implementation.ApiActionFilterFactory;
import me.lucko.luckperms.common.api.implementation.ApiActionLogger;
import me.lucko.luckperms.common.api.implementation.ApiContextManager;
import me.lucko.luckperms.common.api.implementation.ApiGroupManager;
@@ -48,7 +47,6 @@ import me.lucko.luckperms.common.plugin.logging.PluginLogger;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.LuckPermsProvider;
import net.luckperms.api.actionlog.ActionLogger;
import net.luckperms.api.actionlog.filter.ActionFilterFactory;
import net.luckperms.api.context.ContextManager;
import net.luckperms.api.messaging.MessagingService;
import net.luckperms.api.messenger.MessengerProvider;
@@ -57,7 +55,6 @@ import net.luckperms.api.model.group.GroupManager;
import net.luckperms.api.model.user.UserManager;
import net.luckperms.api.node.NodeBuilderRegistry;
import net.luckperms.api.node.matcher.NodeMatcherFactory;
import net.luckperms.api.platform.Health;
import net.luckperms.api.platform.Platform;
import net.luckperms.api.platform.PlayerAdapter;
import net.luckperms.api.platform.PluginMetadata;
@@ -175,11 +172,6 @@ public class LuckPermsApiProvider implements LuckPerms {
return this.plugin.getSyncTaskBuffer().request();
}
@Override
public @NonNull Health runHealthCheck() {
return this.plugin.runHealthCheck();
}
@Override
public @NonNull AbstractEventBus<?> getEventBus() {
return this.plugin.getEventDispatcher().getEventBus();
@@ -227,8 +219,4 @@ public class LuckPermsApiProvider implements LuckPerms {
return ApiNodeMatcherFactory.INSTANCE;
}
@Override
public @NonNull ActionFilterFactory getActionFilterFactory() {
return ApiActionFilterFactory.INSTANCE;
}
}
@@ -1,47 +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.api.implementation;
import me.lucko.luckperms.common.filter.FilterList;
import net.luckperms.api.actionlog.Action;
import net.luckperms.api.actionlog.filter.ActionFilter;
public class ApiActionFilter implements ActionFilter {
private final FilterList<Action> filter;
public ApiActionFilter(FilterList<Action> filter) {
this.filter = filter;
}
@Override
public boolean test(Action action) {
return this.filter.evaluate(action);
}
public FilterList<Action> getFilter() {
return this.filter;
}
}
@@ -1,77 +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.api.implementation;
import me.lucko.luckperms.common.actionlog.filter.ActionFilters;
import net.luckperms.api.actionlog.filter.ActionFilter;
import net.luckperms.api.actionlog.filter.ActionFilterFactory;
import java.util.Objects;
import java.util.UUID;
public final class ApiActionFilterFactory implements ActionFilterFactory {
public static final ApiActionFilterFactory INSTANCE = new ApiActionFilterFactory();
private ApiActionFilterFactory() {
}
@Override
public ActionFilter any() {
return new ApiActionFilter(ActionFilters.all());
}
@Override
public ActionFilter source(UUID uniqueId) {
Objects.requireNonNull(uniqueId, "uniqueId");
return new ApiActionFilter(ActionFilters.source(uniqueId));
}
@Override
public ActionFilter user(UUID uniqueId) {
Objects.requireNonNull(uniqueId, "uniqueId");
return new ApiActionFilter(ActionFilters.user(uniqueId));
}
@Override
public ActionFilter group(String name) {
Objects.requireNonNull(name, "name");
return new ApiActionFilter(ActionFilters.group(name));
}
@Override
public ActionFilter track(String name) {
Objects.requireNonNull(name, "name");
return new ApiActionFilter(ActionFilters.track(name));
}
@Override
public ActionFilter search(String query) {
Objects.requireNonNull(query, "query");
return new ApiActionFilter(ActionFilters.search(query));
}
}
@@ -25,63 +25,50 @@
package me.lucko.luckperms.common.api.implementation;
import com.google.common.collect.ImmutableSortedSet;
import me.lucko.luckperms.common.actionlog.LoggedAction;
import me.lucko.luckperms.common.util.ImmutableCollectors;
import me.lucko.luckperms.common.actionlog.Log;
import me.lucko.luckperms.common.api.ApiUtils;
import net.luckperms.api.actionlog.Action;
import net.luckperms.api.actionlog.ActionLog;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.List;
import java.util.Objects;
import java.util.SortedSet;
import java.util.UUID;
@Deprecated
@SuppressWarnings({"unchecked", "rawtypes"})
public class ApiActionLog implements ActionLog {
private final SortedSet<Action> content;
private final Log handle;
public ApiActionLog(List<LoggedAction> content) {
this.content = ImmutableSortedSet.copyOf(content);
public ApiActionLog(Log handle) {
this.handle = handle;
}
@Override
public @NonNull SortedSet<Action> getContent() {
return this.content;
return (SortedSet) this.handle.getContent();
}
@Override
public @NonNull SortedSet<Action> getContent(@NonNull UUID actor) {
Objects.requireNonNull(actor, "actor");
return this.content.stream()
.filter(e -> e.getSource().getUniqueId().equals(actor))
.collect(ImmutableCollectors.toSortedSet());
return (SortedSet) this.handle.getContent(actor);
}
@Override
public @NonNull SortedSet<Action> getUserHistory(@NonNull UUID uniqueId) {
Objects.requireNonNull(uniqueId, "uuid");
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());
return (SortedSet) this.handle.getUserHistory(uniqueId);
}
@Override
public @NonNull SortedSet<Action> getGroupHistory(@NonNull String name) {
Objects.requireNonNull(name, "name");
return this.content.stream()
.filter(e -> e.getTarget().getType() == Action.Target.Type.GROUP)
.filter(e -> e.getTarget().getName().equals(name))
.collect(ImmutableCollectors.toSortedSet());
return (SortedSet) this.handle.getGroupHistory(ApiUtils.checkName(name));
}
@Override
public @NonNull SortedSet<Action> getTrackHistory(@NonNull String name) {
Objects.requireNonNull(name, "name");
return this.content.stream()
.filter(e -> e.getTarget().getType() == Action.Target.Type.TRACK)
.filter(e -> e.getTarget().getName().equals(name))
.collect(ImmutableCollectors.toSortedSet());
return (SortedSet) this.handle.getTrackHistory(ApiUtils.checkName(name));
}
}
@@ -25,22 +25,13 @@
package me.lucko.luckperms.common.api.implementation;
import me.lucko.luckperms.common.actionlog.LogDispatcher;
import me.lucko.luckperms.common.actionlog.LogPage;
import me.lucko.luckperms.common.actionlog.LoggedAction;
import me.lucko.luckperms.common.actionlog.filter.ActionFilters;
import me.lucko.luckperms.common.filter.FilterList;
import me.lucko.luckperms.common.filter.PageParameters;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import net.luckperms.api.actionlog.Action;
import net.luckperms.api.actionlog.ActionLog;
import net.luckperms.api.actionlog.ActionLogger;
import net.luckperms.api.actionlog.filter.ActionFilter;
import net.luckperms.api.util.Page;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
public class ApiActionLogger implements ActionLogger {
@@ -56,66 +47,22 @@ public class ApiActionLogger implements ActionLogger {
}
@Override
@Deprecated
public @NonNull CompletableFuture<ActionLog> getLog() {
return this.plugin.getStorage().getLogPage(ActionFilters.all(), null)
.thenApply(result -> new ApiActionLog(result.getContent()));
}
@Override
public @NonNull CompletableFuture<List<Action>> queryActions(@NonNull ActionFilter filter) {
return this.plugin.getStorage().getLogPage(getFilterList(filter), null).thenApply(ActionPage::new).thenApply(Page::entries);
}
@Override
public @NonNull CompletableFuture<Page<Action>> queryActions(@NonNull ActionFilter filter, int pageSize, int pageNumber) {
return this.plugin.getStorage().getLogPage(getFilterList(filter), new PageParameters(pageSize, pageNumber)).thenApply(ActionPage::new);
return this.plugin.getStorage().getLog().thenApply(ApiActionLog::new);
}
@Override
public @NonNull CompletableFuture<Void> submit(@NonNull Action entry) {
return this.plugin.getLogDispatcher().dispatchFromApi((LoggedAction) entry);
return CompletableFuture.runAsync(() -> this.plugin.getLogDispatcher().dispatchFromApi((LoggedAction) entry), this.plugin.getBootstrap().getScheduler().async());
}
@Override
public @NonNull CompletableFuture<Void> submitToStorage(@NonNull Action entry) {
return this.plugin.getLogDispatcher().logToStorage((LoggedAction) entry);
return this.plugin.getStorage().logAction(entry);
}
@Override
public @NonNull CompletableFuture<Void> broadcastAction(@NonNull Action entry) {
LogDispatcher dispatcher = this.plugin.getLogDispatcher();
CompletableFuture<Void> messagingFuture = dispatcher.logToStorage(((LoggedAction) entry));
dispatcher.broadcastFromApi(((LoggedAction) entry));
return messagingFuture;
}
private static FilterList<Action> getFilterList(ActionFilter filter) {
Objects.requireNonNull(filter, "filter");
if (filter instanceof ApiActionFilter) {
return ((ApiActionFilter) filter).getFilter();
} else {
throw new IllegalArgumentException("Unknown filter type: " + filter.getClass());
}
}
private static final class ActionPage implements Page<Action> {
private final LogPage page;
private ActionPage(LogPage page) {
this.page = page;
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public @NonNull List<Action> entries() {
return (List) this.page.getContent();
}
@Override
public int overallSize() {
return this.page.getTotalEntries();
}
return CompletableFuture.runAsync(() -> this.plugin.getLogDispatcher().broadcastFromApi((LoggedAction) entry), this.plugin.getBootstrap().getScheduler().async());
}
}
@@ -54,11 +54,4 @@ public class ApiMessagingService implements MessagingService {
Objects.requireNonNull(user, "user");
this.handle.pushUserUpdate(ApiUser.cast(user));
}
@Override
public void sendCustomMessage(@NonNull String channelId, @NonNull String payload) {
Objects.requireNonNull(channelId, "channelId");
Objects.requireNonNull(payload, "payload");
this.handle.pushCustomPayload(channelId, payload);
}
}
@@ -26,8 +26,8 @@
package me.lucko.luckperms.common.api.implementation;
import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.common.cacheddata.metastack.SimpleMetaStackDefinition;
import me.lucko.luckperms.common.cacheddata.metastack.StandardStackElements;
import me.lucko.luckperms.common.metastacking.SimpleMetaStackDefinition;
import me.lucko.luckperms.common.metastacking.StandardStackElements;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import net.luckperms.api.metastacking.DuplicateRemovalFunction;
import net.luckperms.api.metastacking.MetaStackDefinition;
@@ -38,7 +38,6 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
public class ApiTrackManager extends ApiAbstractManager<Track, net.luckperms.api.track.Track, TrackManager<?>> implements net.luckperms.api.track.TrackManager {
public ApiTrackManager(LuckPermsPlugin plugin, TrackManager<?> handle) {
@@ -75,19 +74,6 @@ public class ApiTrackManager extends ApiAbstractManager<Track, net.luckperms.api
return this.plugin.getStorage().deleteTrack(ApiTrack.cast(track), DeletionCause.API);
}
@Override
public @NonNull CompletableFuture<Void> modifyTrack(@NonNull String name, @NonNull Consumer<? super net.luckperms.api.track.Track> action) {
Objects.requireNonNull(name, "name");
Objects.requireNonNull(action, "action");
return this.plugin.getStorage().createAndLoadTrack(name, CreationCause.API)
.thenApplyAsync(track -> {
action.accept(track.getApiProxy());
return track;
}, this.plugin.getBootstrap().getScheduler().async())
.thenCompose(track -> this.plugin.getStorage().saveTrack(track));
}
@Override
public @NonNull CompletableFuture<Void> loadAllTracks() {
return this.plugin.getStorage().loadAllTracks();
@@ -25,13 +25,14 @@
package me.lucko.luckperms.common.bulkupdate;
import me.lucko.luckperms.common.bulkupdate.action.BulkUpdateAction;
import me.lucko.luckperms.common.filter.FilterList;
import me.lucko.luckperms.common.bulkupdate.action.Action;
import me.lucko.luckperms.common.bulkupdate.query.Query;
import me.lucko.luckperms.common.model.HolderType;
import net.luckperms.api.node.Node;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -45,30 +46,35 @@ public final class BulkUpdate {
private final DataType dataType;
// the action to apply to the data which matches the constraints
private final BulkUpdateAction action;
private final Action action;
// a set of filters which data must match to be acted upon
private final FilterList<Node> filters;
// a set of constraints which data must match to be acted upon
private final List<Query> queries;
// update statistics of the operation (number of nodes, users and groups affected)
private final BulkUpdateStatistics statistics = new BulkUpdateStatistics();
private final boolean trackStatistics;
public BulkUpdate(DataType dataType, BulkUpdateAction action, FilterList<Node> filters, boolean trackStatistics) {
public BulkUpdate(DataType dataType, Action action, List<Query> queries, boolean trackStatistics) {
this.dataType = dataType;
this.action = action;
this.filters = filters;
this.queries = queries;
this.trackStatistics = trackStatistics;
}
/**
* Check to see if a Node instance satisfies the constraints of this query
* Check to see if a Node instance satisfies the constrints of this query
*
* @param node the node to check
* @return true if satisfied
*/
public boolean satisfiesFilters(Node node) {
return this.filters.evaluate(node);
public boolean satisfiesConstraints(Node node) {
for (Query query : this.queries) {
if (!query.isSatisfiedBy(node)) {
return false;
}
}
return true;
}
/**
@@ -78,7 +84,7 @@ public final class BulkUpdate {
* @return the transformed node, or null if the node should be deleted
*/
private Node apply(Node node) {
if (!satisfiesFilters(node)) {
if (!satisfiesConstraints(node)) {
return node; // make no change
}
@@ -123,16 +129,63 @@ public final class BulkUpdate {
return results;
}
/**
* Converts this {@link BulkUpdate} to SQL syntax
*
* @return this query in SQL form
*/
public PreparedStatementBuilder buildAsSql() {
// DELETE FROM {table} WHERE ...
// UPDATE {table} SET ... WHERE ...
PreparedStatementBuilder builder = new PreparedStatementBuilder();
// add the action
// (DELETE FROM or UPDATE)
this.action.appendSql(builder);
return appendConstraintsAsSql(builder);
}
/**
* Appends the constraints of this {@link BulkUpdate} to the provided statement builder in SQL syntax
*
* @param builder the statement builder to append the constraints to
* @return the same statement builder provided as input
*/
public PreparedStatementBuilder appendConstraintsAsSql(PreparedStatementBuilder builder) {
// if there are no constraints, just return without a WHERE clause
if (this.queries.isEmpty()) {
return builder;
}
// append constraints
builder.append(" WHERE");
for (int i = 0; i < this.queries.size(); i++) {
Query query = this.queries.get(i);
builder.append(" ");
if (i != 0) {
builder.append("AND ");
}
query.appendSql(builder);
}
return builder;
}
public DataType getDataType() {
return this.dataType;
}
public BulkUpdateAction getAction() {
public Action getAction() {
return this.action;
}
public FilterList<Node> getFilters() {
return this.filters;
public List<Query> getQueries() {
return this.queries;
}
public boolean isTrackingStatistics() {
@@ -151,12 +204,12 @@ public final class BulkUpdate {
return this.getDataType() == that.getDataType() &&
Objects.equals(this.getAction(), that.getAction()) &&
Objects.equals(this.getFilters(), that.getFilters());
Objects.equals(this.getQueries(), that.getQueries());
}
@Override
public int hashCode() {
return Objects.hash(getDataType(), getAction(), getFilters(), isTrackingStatistics());
return Objects.hash(getDataType(), getAction(), getQueries(), isTrackingStatistics());
}
@Override
@@ -164,7 +217,7 @@ public final class BulkUpdate {
return "BulkUpdate(" +
"dataType=" + this.getDataType() + ", " +
"action=" + this.getAction() + ", " +
"constraints=" + this.getFilters() + ", " +
"constraints=" + this.getQueries() + ", " +
"trackStatistics=" + this.isTrackingStatistics() + ")";
}
}
@@ -26,12 +26,8 @@
package me.lucko.luckperms.common.bulkupdate;
import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.common.bulkupdate.action.BulkUpdateAction;
import me.lucko.luckperms.common.filter.Comparison;
import me.lucko.luckperms.common.filter.ConstraintFactory;
import me.lucko.luckperms.common.filter.Filter;
import me.lucko.luckperms.common.filter.FilterList;
import net.luckperms.api.node.Node;
import me.lucko.luckperms.common.bulkupdate.action.Action;
import me.lucko.luckperms.common.bulkupdate.query.Query;
import java.util.LinkedHashSet;
import java.util.Set;
@@ -49,18 +45,18 @@ public class BulkUpdateBuilder {
private DataType dataType = DataType.ALL;
// the action to apply to the data which matches the constraints
private BulkUpdateAction action = null;
private Action action = null;
// should the operation count the number of affected nodes, users and groups
private boolean trackStatistics = false;
// a set of filters which data must match to be acted upon
private final Set<Filter<Node, String>> filters = new LinkedHashSet<>();
// a set of constraints which data must match to be acted upon
private final Set<Query> queries = new LinkedHashSet<>();
private BulkUpdateBuilder() {
}
public BulkUpdateBuilder action(BulkUpdateAction action) {
public BulkUpdateBuilder action(Action action) {
this.action = action;
return this;
}
@@ -75,8 +71,8 @@ public class BulkUpdateBuilder {
return this;
}
public BulkUpdateBuilder filter(BulkUpdateField field, Comparison comparison, String value) {
this.filters.add(new Filter<>(field, ConstraintFactory.STRINGS.build(comparison, value)));
public BulkUpdateBuilder query(Query query) {
this.queries.add(query);
return this;
}
@@ -85,8 +81,7 @@ public class BulkUpdateBuilder {
throw new IllegalStateException("no action specified");
}
FilterList<Node> filters = new FilterList<>(FilterList.LogicalOperator.AND, ImmutableList.copyOf(this.filters));
return new BulkUpdate(this.dataType, this.action, filters, this.trackStatistics);
return new BulkUpdate(this.dataType, this.action, ImmutableList.copyOf(this.queries), this.trackStatistics);
}
@Override
@@ -94,7 +89,7 @@ public class BulkUpdateBuilder {
return "BulkUpdateBuilder(" +
"dataType=" + this.dataType + ", " +
"action=" + this.action + ", " +
"constraints=" + this.filters + ", " +
"constraints=" + this.queries + ", " +
"trackStatistics=" + this.trackStatistics + ")";
}
}
@@ -1,76 +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.bulkupdate;
import me.lucko.luckperms.common.bulkupdate.action.BulkUpdateAction;
import me.lucko.luckperms.common.bulkupdate.action.DeleteAction;
import me.lucko.luckperms.common.bulkupdate.action.UpdateAction;
import me.lucko.luckperms.common.filter.FilterField;
import me.lucko.luckperms.common.filter.sql.FilterSqlBuilder;
import net.luckperms.api.node.Node;
public class BulkUpdateSqlBuilder extends FilterSqlBuilder<Node> {
public void visit(BulkUpdate update) {
visit(update.getAction());
visit(update.getFilters());
}
public void visit(BulkUpdateAction action) {
if (action instanceof UpdateAction) {
visit(((UpdateAction) action));
} else if (action instanceof DeleteAction) {
visit(((DeleteAction) action));
} else {
throw new UnsupportedOperationException(action.getClass().getName());
}
}
public void visit(UpdateAction action) {
this.builder.append("UPDATE {table} SET ");
visitFieldName(action.getField());
this.builder.append("=");
this.builder.variable(action.getNewValue());
}
public void visit(DeleteAction action) {
this.builder.append("DELETE FROM {table}");
}
@Override
public void visitFieldName(FilterField<Node, ?> field) {
if (field == BulkUpdateField.PERMISSION) {
this.builder.append("permission");
} else if (field == BulkUpdateField.SERVER) {
this.builder.append("server");
} else if (field == BulkUpdateField.WORLD) {
this.builder.append("world");
} else {
throw new AssertionError(field);
}
}
}
@@ -23,15 +23,14 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.storage.implementation.sql.builder;
import me.lucko.luckperms.common.storage.implementation.sql.StatementProcessor;
package me.lucko.luckperms.common.bulkupdate;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class PreparedStatementBuilder {
private final StringBuilder sb = new StringBuilder();
@@ -57,22 +56,13 @@ public class PreparedStatementBuilder {
return this;
}
public PreparedStatement build(Connection connection, StatementProcessor processor) throws SQLException {
PreparedStatement statement = null;
try {
statement = connection.prepareStatement(processor.process(this.sb.toString()));
for (int i = 0; i < this.variables.size(); i++) {
String var = this.variables.get(i);
statement.setString(i + 1, var);
}
return statement;
} catch (SQLException e) {
// if an exception is thrown, any try-with-resources block above this call won't be able to close the statement
if (statement != null) {
statement.close();
}
throw e;
public PreparedStatement build(Connection connection, Function<String, String> mapping) throws SQLException {
PreparedStatement statement = connection.prepareStatement(mapping.apply(this.sb.toString()));
for (int i = 0; i < this.variables.size(); i++) {
String var = this.variables.get(i);
statement.setString(i + 1, var);
}
return statement;
}
public String toReadableString() {
@@ -82,8 +72,4 @@ public class PreparedStatementBuilder {
}
return s;
}
public String toQueryString() {
return this.sb.toString();
}
}
@@ -25,12 +25,13 @@
package me.lucko.luckperms.common.bulkupdate.action;
import me.lucko.luckperms.common.bulkupdate.PreparedStatementBuilder;
import net.luckperms.api.node.Node;
/**
* Represents an action to be applied to a given node.
*/
public interface BulkUpdateAction {
public interface Action {
/**
* Gets the name of this action
@@ -43,8 +44,17 @@ public interface BulkUpdateAction {
* Applies this action to the given NodeModel, and returns the result.
*
* @param from the node to base changes from
* @return the new node instance, or null if the node should be deleted.
* @return the new nodemodel instance, or null if the node should be deleted.
*/
Node apply(Node from);
/**
* Gets this action in SQL form.
*
* Will include a placeholder for the table, as "{table}".
*
* @param builder the statement builder
*/
void appendSql(PreparedStatementBuilder builder);
}
@@ -25,9 +25,10 @@
package me.lucko.luckperms.common.bulkupdate.action;
import me.lucko.luckperms.common.bulkupdate.PreparedStatementBuilder;
import net.luckperms.api.node.Node;
public class DeleteAction implements BulkUpdateAction {
public class DeleteAction implements Action {
public static DeleteAction create() {
return new DeleteAction();
@@ -45,4 +46,9 @@ public class DeleteAction implements BulkUpdateAction {
public Node apply(Node from) {
return null; // this action just deletes nodes, so return null
}
@Override
public void appendSql(PreparedStatementBuilder builder) {
builder.append("DELETE FROM {table}");
}
}
@@ -25,27 +25,28 @@
package me.lucko.luckperms.common.bulkupdate.action;
import me.lucko.luckperms.common.bulkupdate.BulkUpdateField;
import me.lucko.luckperms.common.bulkupdate.PreparedStatementBuilder;
import me.lucko.luckperms.common.bulkupdate.query.QueryField;
import me.lucko.luckperms.common.node.factory.NodeBuilders;
import net.luckperms.api.context.DefaultContextKeys;
import net.luckperms.api.context.MutableContextSet;
import net.luckperms.api.node.Node;
public class UpdateAction implements BulkUpdateAction {
public class UpdateAction implements Action {
public static UpdateAction of(BulkUpdateField field, String value) {
public static UpdateAction of(QueryField field, String value) {
return new UpdateAction(field, value);
}
// the field we're updating
private final BulkUpdateField field;
private final QueryField field;
// the new value of the field
private final String newValue;
private final String value;
private UpdateAction(BulkUpdateField field, String newValue) {
private UpdateAction(QueryField field, String value) {
this.field = field;
this.newValue = newValue;
this.value = value;
}
@Override
@@ -53,19 +54,11 @@ public class UpdateAction implements BulkUpdateAction {
return "update";
}
public BulkUpdateField getField() {
return this.field;
}
public String getNewValue() {
return this.newValue;
}
@Override
public Node apply(Node from) {
switch (this.field) {
case PERMISSION:
return NodeBuilders.determineMostApplicable(this.newValue)
return NodeBuilders.determineMostApplicable(this.value)
.value(from.getValue())
.expiry(from.getExpiry())
.context(from.getContexts())
@@ -73,8 +66,8 @@ public class UpdateAction implements BulkUpdateAction {
case SERVER: {
MutableContextSet contexts = from.getContexts().mutableCopy();
contexts.removeAll(DefaultContextKeys.SERVER_KEY);
if (!this.newValue.equals("global")) {
contexts.add(DefaultContextKeys.SERVER_KEY, this.newValue);
if (!this.value.equals("global")) {
contexts.add(DefaultContextKeys.SERVER_KEY, this.value);
}
return from.toBuilder()
@@ -84,8 +77,8 @@ public class UpdateAction implements BulkUpdateAction {
case WORLD: {
MutableContextSet contexts = from.getContexts().mutableCopy();
contexts.removeAll(DefaultContextKeys.WORLD_KEY);
if (!this.newValue.equals("global")) {
contexts.add(DefaultContextKeys.WORLD_KEY, this.newValue);
if (!this.value.equals("global")) {
contexts.add(DefaultContextKeys.WORLD_KEY, this.value);
}
return from.toBuilder()
@@ -96,4 +89,10 @@ public class UpdateAction implements BulkUpdateAction {
throw new RuntimeException();
}
}
@Override
public void appendSql(PreparedStatementBuilder builder) {
builder.append("UPDATE {table} SET " + this.field.getSqlName() + "=");
builder.variable(this.value);
}
}
@@ -23,60 +23,48 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.filter;
package me.lucko.luckperms.common.bulkupdate.comparison;
import java.util.regex.Pattern;
import me.lucko.luckperms.common.bulkupdate.PreparedStatementBuilder;
/**
* A method of comparing two strings
*/
public enum Comparison {
EQUAL("=="),
NOT_EQUAL("!="),
SIMILAR("~~"),
NOT_SIMILAR("!~");
public static final String WILDCARD = "%";
public static final String WILDCARD_ONE = "_";
private final String symbol;
Comparison(String symbol) {
this.symbol = symbol;
}
public interface Comparison {
/**
* Gets the symbol which represents this comparison
*
* @return the comparison symbol
*/
public String getSymbol() {
return this.symbol;
}
String getSymbol();
@Override
public String toString() {
return this.symbol;
}
/**
* Creates a {@link CompiledExpression} for the given expression
*
* @param expression the expression
* @return the compiled expression
*/
CompiledExpression compile(String expression);
public static Comparison parse(String s) {
for (Comparison t : values()) {
if (t.getSymbol().equals(s)) {
return t;
}
}
return null;
}
/**
* Returns the comparison operator in SQL form
*/
void appendSql(PreparedStatementBuilder builder);
public static Pattern compilePatternForLikeSyntax(String expression) {
expression = expression.replace(".", "\\.");
/**
* An instance of {@link Comparison} which is bound to an expression.
*/
interface CompiledExpression {
// convert from SQL LIKE syntax to regex
expression = expression.replace(WILDCARD_ONE, ".");
expression = expression.replace(WILDCARD, ".*");
return Pattern.compile(expression, Pattern.CASE_INSENSITIVE);
/**
* Tests the expression against a given string, according to the
* rules of the parent {@link Comparison}.
*
* @param string the string
* @return if there was a match
*/
boolean test(String string);
}
}
@@ -23,28 +23,24 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.filter;
package me.lucko.luckperms.common.bulkupdate.comparison;
import java.util.function.Predicate;
import me.lucko.luckperms.common.bulkupdate.PreparedStatementBuilder;
public class Constraint<T> {
public class Constraint {
public static Constraint of(Comparison comparison, String expression) {
return new Constraint(comparison, expression);
}
private final Comparison comparison;
private final T value;
private final Predicate<T> predicate;
private final String expressionValue;
private final Comparison.CompiledExpression compiledExpression;
Constraint(Comparison comparison, T value, Predicate<T> predicate) {
private Constraint(Comparison comparison, String expression) {
this.comparison = comparison;
this.value = value;
this.predicate = predicate;
}
public Comparison comparison() {
return this.comparison;
}
public T value() {
return this.value;
this.expressionValue = expression;
this.compiledExpression = this.comparison.compile(this.expressionValue);
}
/**
@@ -53,12 +49,20 @@ public class Constraint<T> {
* @param value the value
* @return true if satisfied
*/
public boolean evaluate(T value) {
return this.predicate.test(value);
public boolean eval(String value) {
return this.compiledExpression.test(value);
}
public void appendSql(PreparedStatementBuilder builder, String field) {
// e.g. field LIKE ?
builder.append(field).append(' ');
this.comparison.appendSql(builder);
builder.append(' ');
builder.variable(this.expressionValue);
}
@Override
public String toString() {
return this.comparison + " " + this.value;
return this.comparison + " " + this.expressionValue;
}
}
@@ -0,0 +1,112 @@
/*
* 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.bulkupdate.comparison;
import me.lucko.luckperms.common.bulkupdate.PreparedStatementBuilder;
import java.util.regex.Pattern;
/**
* An enumeration of standard {@link Comparison}s.
*/
public enum StandardComparison implements Comparison {
EQUAL("==", "=") {
@Override
public CompiledExpression compile(String expression) {
return expression::equalsIgnoreCase;
}
},
NOT_EQUAL("!=", "!=") {
@Override
public CompiledExpression compile(String expression) {
return string -> !expression.equalsIgnoreCase(string);
}
},
SIMILAR("~~", "LIKE") {
@Override
public CompiledExpression compile(String expression) {
Pattern pattern = StandardComparison.compilePatternForLikeSyntax(expression);
return string -> pattern.matcher(string).matches();
}
},
NOT_SIMILAR("!~", "NOT LIKE") {
@Override
public CompiledExpression compile(String expression) {
Pattern pattern = StandardComparison.compilePatternForLikeSyntax(expression);
return string -> !pattern.matcher(string).matches();
}
};
public static final String WILDCARD = "%";
public static final String WILDCARD_ONE = "_";
private final String symbol;
private final String asSql;
StandardComparison(String symbol, String asSql) {
this.symbol = symbol;
this.asSql = asSql;
}
@Override
public String getSymbol() {
return this.symbol;
}
@Override
public void appendSql(PreparedStatementBuilder builder) {
builder.append(this.asSql);
}
@Override
public String toString() {
return this.symbol;
}
public static StandardComparison parseComparison(String s) {
for (StandardComparison t : values()) {
if (t.getSymbol().equals(s)) {
return t;
}
}
return null;
}
static Pattern compilePatternForLikeSyntax(String expression) {
expression = expression.replace(".", "\\.");
// convert from SQL LIKE syntax to regex
expression = expression.replace(WILDCARD_ONE, ".");
expression = expression.replace(WILDCARD, ".*");
return Pattern.compile(expression, Pattern.CASE_INSENSITIVE);
}
}
@@ -0,0 +1,83 @@
/*
* 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.bulkupdate.query;
import me.lucko.luckperms.common.bulkupdate.PreparedStatementBuilder;
import me.lucko.luckperms.common.bulkupdate.comparison.Constraint;
import net.luckperms.api.context.DefaultContextKeys;
import net.luckperms.api.node.Node;
/**
* Represents a query component
*/
public class Query {
public static Query of(QueryField field, Constraint constraint) {
return new Query(field, constraint);
}
// the field this query is comparing against
private final QueryField field;
// the constraint
private final Constraint constraint;
private Query(QueryField field, Constraint constraint) {
this.field = field;
this.constraint = constraint;
}
/**
* Returns if the given node satisfies this query
*
* @param node the node
* @return true if satisfied
*/
public boolean isSatisfiedBy(Node node) {
switch (this.field) {
case PERMISSION:
return this.constraint.eval(node.getKey());
case SERVER:
return this.constraint.eval(node.getContexts().getAnyValue(DefaultContextKeys.SERVER_KEY).orElse("global"));
case WORLD:
return this.constraint.eval(node.getContexts().getAnyValue(DefaultContextKeys.WORLD_KEY).orElse("global"));
default:
throw new RuntimeException();
}
}
public void appendSql(PreparedStatementBuilder builder) {
this.constraint.appendSql(builder, this.field.getSqlName());
}
public QueryField getField() {
return this.field;
}
public Constraint getConstraint() {
return this.constraint;
}
}
@@ -23,26 +23,34 @@
* SOFTWARE.
*/
package me.lucko.luckperms.standalone.app.integration;
import net.kyori.adventure.text.Component;
import net.luckperms.api.util.Tristate;
package me.lucko.luckperms.common.bulkupdate.query;
import java.util.Locale;
import java.util.UUID;
public interface StandaloneSender {
String getName();
/**
* Represents a field being used in an update
*/
public enum QueryField {
UUID getUniqueId();
PERMISSION("permission"),
SERVER("server"),
WORLD("world");
void sendMessage(Component component);
private final String sqlName;
Tristate getPermissionValue(String permission);
public static QueryField of(String s) {
try {
return valueOf(s.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException e) {
return null;
}
}
boolean hasPermission(String permission);
QueryField(String sqlName) {
this.sqlName = sqlName;
}
boolean isConsole();
Locale getLocale();
public String getSqlName() {
return this.sqlName;
}
}
@@ -193,7 +193,7 @@ public abstract class AbstractCachedDataManager implements CachedDataManager {
}
public void cleanup() {
this.cache.values().removeIf(value -> !((UsageTracked) value).usedInTheLast(2, TimeUnit.MINUTES));
this.cache.values().removeIf(value -> ((UsageTracked) value).usedSince(TimeUnit.MINUTES.toMillis(2)));
}
@Override
@@ -25,20 +25,14 @@
package me.lucko.luckperms.common.cacheddata;
import com.google.common.annotations.VisibleForTesting;
import java.util.concurrent.TimeUnit;
public abstract class UsageTracked {
@VisibleForTesting
protected long lastUsed = System.currentTimeMillis();
private long lastUsed = System.currentTimeMillis();
public void recordUsage() {
this.lastUsed = System.currentTimeMillis();
}
public boolean usedInTheLast(long duration, TimeUnit unit) {
return this.lastUsed > System.currentTimeMillis() - unit.toMillis(duration);
public boolean usedSince(long duration) {
return this.lastUsed > System.currentTimeMillis() - duration;
}
}
@@ -1,57 +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.cacheddata.result;
import net.luckperms.api.cacheddata.Result;
import net.luckperms.api.node.Node;
import org.checkerframework.checker.nullness.qual.Nullable;
public abstract class AbstractResult<T, N extends Node, S extends AbstractResult<T, N, S>> implements Result<T, N> {
/** The node that caused the result */
protected final N node;
/** A reference to another result that this one overrides */
protected S overriddenResult;
public AbstractResult(N node, S overriddenResult) {
this.node = node;
this.overriddenResult = overriddenResult;
}
@Override
public final @Nullable N node() {
return this.node;
}
public final @Nullable S overriddenResult() {
return this.overriddenResult;
}
public final void setOverriddenResult(S overriddenResult) {
this.overriddenResult = overriddenResult;
}
}
@@ -25,23 +25,30 @@
package me.lucko.luckperms.common.cacheddata.result;
import net.luckperms.api.cacheddata.Result;
import net.luckperms.api.node.Node;
import net.luckperms.api.node.types.WeightNode;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Represents the result of an integer meta lookup
*
* @param <N> the node type
*/
public final class IntegerResult<N extends Node> extends AbstractResult<Integer, N, IntegerResult<N>> {
public final class IntegerResult<N extends Node> implements Result<Integer, N> {
/** The result */
private final int result;
/** The node that caused the result */
private final N node;
/** A reference to another result that this one overrides */
private IntegerResult<N> overriddenResult;
public IntegerResult(int result, N node, IntegerResult<N> overriddenResult) {
super(node, overriddenResult);
this.result = result;
this.node = node;
this.overriddenResult = overriddenResult;
}
@Override
@@ -66,6 +73,19 @@ public final class IntegerResult<N extends Node> extends AbstractResult<Integer,
}
}
@Override
public @Nullable N node() {
return this.node;
}
public @Nullable IntegerResult<N> overriddenResult() {
return this.overriddenResult;
}
public void setOverriddenResult(IntegerResult<N> overriddenResult) {
this.overriddenResult = overriddenResult;
}
public boolean isNull() {
return this == NULL_RESULT;
}
@@ -25,6 +25,7 @@
package me.lucko.luckperms.common.cacheddata.result;
import net.luckperms.api.cacheddata.Result;
import net.luckperms.api.node.Node;
import net.luckperms.api.node.types.ChatMetaNode;
import net.luckperms.api.node.types.MetaNode;
@@ -35,14 +36,19 @@ import org.checkerframework.checker.nullness.qual.Nullable;
*
* @param <N> the node type
*/
public final class StringResult<N extends Node> extends AbstractResult<String, N, StringResult<N>> {
public final class StringResult<N extends Node> implements Result<String, N> {
/** The result, nullable */
private final String result;
/** The node that caused the result */
private final N node;
/** A reference to another result that this one overrides */
private StringResult<N> overriddenResult;
public StringResult(String result, N node, StringResult<N> overriddenResult) {
super(node, overriddenResult);
this.result = result;
this.node = node;
this.overriddenResult = overriddenResult;
}
@Override
@@ -50,6 +56,19 @@ public final class StringResult<N extends Node> extends AbstractResult<String, N
return this.result;
}
@Override
public @Nullable N node() {
return this.node;
}
public @Nullable StringResult<N> overriddenResult() {
return this.overriddenResult;
}
public void setOverriddenResult(StringResult<N> overriddenResult) {
this.overriddenResult = overriddenResult;
}
public StringResult<N> copy() {
return new StringResult<>(this.result, this.node, this.overriddenResult);
}
@@ -27,6 +27,7 @@ package me.lucko.luckperms.common.cacheddata.result;
import me.lucko.luckperms.common.calculator.PermissionCalculator;
import me.lucko.luckperms.common.calculator.processor.PermissionProcessor;
import net.luckperms.api.cacheddata.Result;
import net.luckperms.api.node.Node;
import net.luckperms.api.util.Tristate;
import org.checkerframework.checker.nullness.qual.NonNull;
@@ -35,16 +36,20 @@ import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Represents the result of a {@link PermissionCalculator} lookup.
*/
public final class TristateResult extends AbstractResult<Tristate, Node, TristateResult> {
public final class TristateResult implements Result<Tristate, Node> {
/** The result */
private final Tristate result;
/** The node that caused the result */
private final Node node;
/** The permission processor that provided the result */
private final Class<? extends PermissionProcessor> processorClass;
/** A reference to another result that this one overrides */
private TristateResult overriddenResult;
private TristateResult(Tristate result, Node node, Class<? extends PermissionProcessor> processorClass) {
super(node, null);
this.result = result;
this.node = node;
this.processorClass = processorClass;
}
@@ -53,6 +58,11 @@ public final class TristateResult extends AbstractResult<Tristate, Node, Tristat
return this.result;
}
@Override
public @Nullable Node node() {
return this.node;
}
public @Nullable Class<? extends PermissionProcessor> processorClass() {
return this.processorClass;
}
@@ -69,6 +79,14 @@ public final class TristateResult extends AbstractResult<Tristate, Node, Tristat
}
}
public @Nullable TristateResult overriddenResult() {
return this.overriddenResult;
}
public void setOverriddenResult(TristateResult overriddenResult) {
this.overriddenResult = overriddenResult;
}
@Override
public String toString() {
return "TristateResult(" +
@@ -79,7 +79,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
@@ -102,7 +101,7 @@ public class CommandManager {
.build()
);
private final AtomicBoolean executingCommand = new AtomicBoolean(false);
private final Set<UUID> playerRateLimit = ExpiringSet.newExpiringSet(500, TimeUnit.MILLISECONDS);
private final ExpiringSet<UUID> playerRateLimit = new ExpiringSet<>(500, TimeUnit.MILLISECONDS);
private final TabCompletions tabCompletions;
private final Map<String, Command<?>> mainCommands;
@@ -160,15 +159,6 @@ public class CommandManager {
return CompletableFuture.completedFuture(null);
}
boolean commandsDisabled = sender.isConsole()
? this.plugin.getConfiguration().get(ConfigKeys.DISABLE_LUCKPERMS_COMMANDS_CONSOLE)
: this.plugin.getConfiguration().get(ConfigKeys.DISABLE_LUCKPERMS_COMMANDS_PLAYERS);
if (commandsDisabled) {
Message.COMMANDS_DISABLED.send(sender);
return CompletableFuture.completedFuture(null);
}
SchedulerAdapter scheduler = this.plugin.getBootstrap().getScheduler();
List<String> argsCopy = new ArrayList<>(args);
@@ -295,14 +285,6 @@ public class CommandManager {
}
public List<String> tabCompleteCommand(Sender sender, List<String> arguments) {
boolean commandsDisabled = sender.isConsole()
? this.plugin.getConfiguration().get(ConfigKeys.DISABLE_LUCKPERMS_COMMANDS_CONSOLE)
: this.plugin.getConfiguration().get(ConfigKeys.DISABLE_LUCKPERMS_COMMANDS_PLAYERS);
if (commandsDisabled) {
return Collections.emptyList();
}
applyConvenienceAliases(arguments, false);
final List<Command<?>> mains = this.mainCommands.values().stream()
@@ -31,7 +31,6 @@ import me.lucko.luckperms.common.command.spec.CommandSpec;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.sender.Sender;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
@@ -67,7 +66,7 @@ public abstract class ChildCommand<T> extends Command<T> {
.collect(Collectors.toList());
builder.append(Component.text(" - ", NamedTextColor.DARK_AQUA))
.append(Component.join(JoinConfiguration.separator(Component.space()), argUsages))
.append(Component.join(Component.space(), argUsages))
.build();
}
@@ -35,7 +35,6 @@ import me.lucko.luckperms.common.model.PermissionHolder;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.sender.Sender;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
@@ -115,7 +114,7 @@ public abstract class GenericChildCommand {
.collect(Collectors.toList());
builder.append(Component.text(" - ", NamedTextColor.DARK_AQUA))
.append(Component.join(JoinConfiguration.separator(Component.space()), argUsages))
.append(Component.join(Component.space(), argUsages))
.build();
}
@@ -86,44 +86,39 @@ public abstract class ParentCommand<T, I> extends Command<Void> {
return;
}
if (this.type == Type.TARGETED) {
final String targetArgument = args.get(0);
I targetId = parseTarget(targetArgument, plugin, sender);
final String targetArgument = args.get(0);
I targetId = null;
if (this.type == Type.TAKES_ARGUMENT_FOR_TARGET) {
targetId = parseTarget(targetArgument, plugin, sender);
if (targetId == null) {
return;
}
}
ReentrantLock lock = getLockForTarget(targetId);
lock.lock();
try {
T target = getTarget(targetId, plugin, sender);
if (target == null) {
return;
}
try {
sub.execute(plugin, sender, target, args.subList(this.type.minArgs, args.size()), label);
} catch (CommandException e) {
e.handle(sender, label, sub);
}
cleanup(target, plugin);
} finally {
lock.unlock();
ReentrantLock lock = getLockForTarget(targetId);
lock.lock();
try {
T target = getTarget(targetId, plugin, sender);
if (target == null) {
return;
}
} else {
try {
sub.execute(plugin, sender, null, args.subList(this.type.minArgs, args.size()), label);
sub.execute(plugin, sender, target, args.subList(this.type.minArgs, args.size()), label);
} catch (CommandException e) {
e.handle(sender, label, sub);
}
cleanup(target, plugin);
} finally {
lock.unlock();
}
}
@Override
public List<String> tabComplete(LuckPermsPlugin plugin, Sender sender, ArgumentList args) {
switch (this.type) {
case TARGETED:
case TAKES_ARGUMENT_FOR_TARGET:
return TabCompleter.create()
.at(0, CompletionSupplier.startsWith(() -> getTargets(plugin).stream()))
.at(1, CompletionSupplier.startsWith(() -> getChildren().stream()
@@ -138,7 +133,7 @@ public abstract class ParentCommand<T, I> extends Command<Void> {
.orElse(Collections.emptyList())
)
.complete(args);
case NOT_TARGETED:
case NO_TARGET_ARGUMENT:
return TabCompleter.create()
.at(0, CompletionSupplier.startsWith(() -> getChildren().stream()
.filter(s -> s.isAuthorized(sender))
@@ -183,31 +178,21 @@ public abstract class ParentCommand<T, I> extends Command<Void> {
return getChildren().stream().anyMatch(sc -> sc.isAuthorized(sender));
}
protected List<String> getTargets(LuckPermsPlugin plugin) {
throw new UnsupportedOperationException();
}
protected abstract List<String> getTargets(LuckPermsPlugin plugin);
protected I parseTarget(String target, LuckPermsPlugin plugin, Sender sender) {
throw new UnsupportedOperationException();
}
protected abstract I parseTarget(String target, LuckPermsPlugin plugin, Sender sender);
protected ReentrantLock getLockForTarget(I target) {
throw new UnsupportedOperationException();
}
protected abstract ReentrantLock getLockForTarget(I target);
protected T getTarget(I target, LuckPermsPlugin plugin, Sender sender) {
throw new UnsupportedOperationException();
}
protected abstract T getTarget(I target, LuckPermsPlugin plugin, Sender sender);
protected void cleanup(T t, LuckPermsPlugin plugin) {
throw new UnsupportedOperationException();
}
protected abstract void cleanup(T t, LuckPermsPlugin plugin);
public enum Type {
// e.g. /lp log sub-command....
NOT_TARGETED(0),
NO_TARGET_ARGUMENT(0),
// e.g. /lp user <USER> sub-command....
TARGETED(1);
TAKES_ARGUMENT_FOR_TARGET(1);
private final int cmdIndex;
private final int minArgs;
@@ -33,7 +33,6 @@ import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.sender.Sender;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
@@ -71,7 +70,7 @@ public abstract class SingleCommand extends Command<Void> {
.collect(Collectors.toList());
builder.append(Component.text(" - ", NamedTextColor.DARK_AQUA))
.append(Component.join(JoinConfiguration.separator(Component.space()), argUsages))
.append(Component.join(Component.space(), argUsages))
.build();
}
@@ -32,143 +32,143 @@ import me.lucko.luckperms.common.sender.Sender;
*/
public enum CommandPermission {
SYNC("sync", Type.NONE, true),
INFO("info", Type.NONE, true),
EDITOR("editor", Type.NONE, true),
VERBOSE("verbose", Type.NONE, true),
VERBOSE_COMMAND_OTHERS("verbose.command.others", Type.NONE, false),
TREE("tree", Type.NONE, true),
SEARCH("search", Type.NONE, true),
IMPORT("import", Type.NONE, false),
EXPORT("export", Type.NONE, true),
RELOAD_CONFIG("reloadconfig", Type.NONE, true),
BULK_UPDATE("bulkupdate", Type.NONE, false),
APPLY_EDITS("applyedits", Type.NONE, false),
TRUST_EDITOR("trusteditor", Type.NONE, false),
TRANSLATIONS("translations", Type.NONE, true),
SYNC("sync", Type.NONE),
INFO("info", Type.NONE),
EDITOR("editor", Type.NONE),
VERBOSE("verbose", Type.NONE),
VERBOSE_COMMAND_OTHERS("verbose.command.others", Type.NONE),
TREE("tree", Type.NONE),
SEARCH("search", Type.NONE),
IMPORT("import", Type.NONE),
EXPORT("export", Type.NONE),
RELOAD_CONFIG("reloadconfig", Type.NONE),
BULK_UPDATE("bulkupdate", Type.NONE),
APPLY_EDITS("applyedits", Type.NONE),
TRUST_EDITOR("trusteditor", Type.NONE),
TRANSLATIONS("translations", Type.NONE),
CREATE_GROUP("creategroup", Type.NONE, false),
DELETE_GROUP("deletegroup", Type.NONE, false),
LIST_GROUPS("listgroups", Type.NONE, true),
CREATE_GROUP("creategroup", Type.NONE),
DELETE_GROUP("deletegroup", Type.NONE),
LIST_GROUPS("listgroups", Type.NONE),
CREATE_TRACK("createtrack", Type.NONE, false),
DELETE_TRACK("deletetrack", Type.NONE, false),
LIST_TRACKS("listtracks", Type.NONE, true),
CREATE_TRACK("createtrack", Type.NONE),
DELETE_TRACK("deletetrack", Type.NONE),
LIST_TRACKS("listtracks", Type.NONE),
USER_INFO("info", Type.USER, true),
USER_PERM_INFO("permission.info", Type.USER, true),
USER_PERM_SET("permission.set", Type.USER, false),
USER_PERM_UNSET("permission.unset", Type.USER, false),
USER_PERM_SET_TEMP("permission.settemp", Type.USER, false),
USER_PERM_UNSET_TEMP("permission.unsettemp", Type.USER, false),
USER_PERM_CHECK("permission.check", Type.USER, true),
USER_PERM_CLEAR("permission.clear", Type.USER, false),
USER_PARENT_INFO("parent.info", Type.USER, true),
USER_PARENT_SET("parent.set", Type.USER, false),
USER_PARENT_SET_TRACK("parent.settrack", Type.USER, false),
USER_PARENT_ADD("parent.add", Type.USER, false),
USER_PARENT_REMOVE("parent.remove", Type.USER, false),
USER_PARENT_ADD_TEMP("parent.addtemp", Type.USER, false),
USER_PARENT_REMOVE_TEMP("parent.removetemp", Type.USER, false),
USER_PARENT_CLEAR("parent.clear", Type.USER, false),
USER_PARENT_CLEAR_TRACK("parent.cleartrack", Type.USER, false),
USER_PARENT_SWITCHPRIMARYGROUP("parent.switchprimarygroup", Type.USER, false),
USER_META_INFO("meta.info", Type.USER, true),
USER_META_SET("meta.set", Type.USER, false),
USER_META_UNSET("meta.unset", Type.USER, false),
USER_META_SET_TEMP("meta.settemp", Type.USER, false),
USER_META_UNSET_TEMP("meta.unsettemp", Type.USER, false),
USER_META_ADD_PREFIX("meta.addprefix", Type.USER, false),
USER_META_ADD_SUFFIX("meta.addsuffix", Type.USER, false),
USER_META_SET_PREFIX("meta.setprefix", Type.USER, false),
USER_META_SET_SUFFIX("meta.setsuffix", Type.USER, false),
USER_META_REMOVE_PREFIX("meta.removeprefix", Type.USER, false),
USER_META_REMOVE_SUFFIX("meta.removesuffix", Type.USER, false),
USER_META_ADD_TEMP_PREFIX("meta.addtempprefix", Type.USER, false),
USER_META_ADD_TEMP_SUFFIX("meta.addtempsuffix", Type.USER, false),
USER_META_SET_TEMP_PREFIX("meta.settempprefix", Type.USER, false),
USER_META_SET_TEMP_SUFFIX("meta.settempsuffix", Type.USER, false),
USER_META_REMOVE_TEMP_PREFIX("meta.removetempprefix", Type.USER, false),
USER_META_REMOVE_TEMP_SUFFIX("meta.removetempsuffix", Type.USER, false),
USER_META_CLEAR("meta.clear", Type.USER, false),
USER_EDITOR("editor", Type.USER, true),
USER_SHOW_TRACKS("showtracks", Type.USER, true),
USER_PROMOTE("promote", Type.USER, false),
USER_DEMOTE("demote", Type.USER, false),
USER_CLEAR("clear", Type.USER, false),
USER_CLONE("clone", Type.USER, false),
USER_INFO("info", Type.USER),
USER_PERM_INFO("permission.info", Type.USER),
USER_PERM_SET("permission.set", Type.USER),
USER_PERM_UNSET("permission.unset", Type.USER),
USER_PERM_SET_TEMP("permission.settemp", Type.USER),
USER_PERM_UNSET_TEMP("permission.unsettemp", Type.USER),
USER_PERM_CHECK("permission.check", Type.USER),
USER_PERM_CLEAR("permission.clear", Type.USER),
USER_PARENT_INFO("parent.info", Type.USER),
USER_PARENT_SET("parent.set", Type.USER),
USER_PARENT_SET_TRACK("parent.settrack", Type.USER),
USER_PARENT_ADD("parent.add", Type.USER),
USER_PARENT_REMOVE("parent.remove", Type.USER),
USER_PARENT_ADD_TEMP("parent.addtemp", Type.USER),
USER_PARENT_REMOVE_TEMP("parent.removetemp", Type.USER),
USER_PARENT_CLEAR("parent.clear", Type.USER),
USER_PARENT_CLEAR_TRACK("parent.cleartrack", Type.USER),
USER_PARENT_SWITCHPRIMARYGROUP("parent.switchprimarygroup", Type.USER),
USER_META_INFO("meta.info", Type.USER),
USER_META_SET("meta.set", Type.USER),
USER_META_UNSET("meta.unset", Type.USER),
USER_META_SET_TEMP("meta.settemp", Type.USER),
USER_META_UNSET_TEMP("meta.unsettemp", Type.USER),
USER_META_ADD_PREFIX("meta.addprefix", Type.USER),
USER_META_ADD_SUFFIX("meta.addsuffix", Type.USER),
USER_META_SET_PREFIX("meta.setprefix", Type.USER),
USER_META_SET_SUFFIX("meta.setsuffix", Type.USER),
USER_META_REMOVE_PREFIX("meta.removeprefix", Type.USER),
USER_META_REMOVE_SUFFIX("meta.removesuffix", Type.USER),
USER_META_ADD_TEMP_PREFIX("meta.addtempprefix", Type.USER),
USER_META_ADD_TEMP_SUFFIX("meta.addtempsuffix", Type.USER),
USER_META_SET_TEMP_PREFIX("meta.settempprefix", Type.USER),
USER_META_SET_TEMP_SUFFIX("meta.settempsuffix", Type.USER),
USER_META_REMOVE_TEMP_PREFIX("meta.removetempprefix", Type.USER),
USER_META_REMOVE_TEMP_SUFFIX("meta.removetempsuffix", Type.USER),
USER_META_CLEAR("meta.clear", Type.USER),
USER_EDITOR("editor", Type.USER),
USER_SHOW_TRACKS("showtracks", Type.USER),
USER_PROMOTE("promote", Type.USER),
USER_DEMOTE("demote", Type.USER),
USER_CLEAR("clear", Type.USER),
USER_CLONE("clone", Type.USER),
GROUP_INFO("info", Type.GROUP, true),
GROUP_PERM_INFO("permission.info", Type.GROUP, true),
GROUP_PERM_SET("permission.set", Type.GROUP, false),
GROUP_PERM_UNSET("permission.unset", Type.GROUP, false),
GROUP_PERM_SET_TEMP("permission.settemp", Type.GROUP, false),
GROUP_PERM_UNSET_TEMP("permission.unsettemp", Type.GROUP, false),
GROUP_PERM_CHECK("permission.check", Type.GROUP, true),
GROUP_PERM_CLEAR("permission.clear", Type.GROUP, false),
GROUP_PARENT_INFO("parent.info", Type.GROUP, true),
GROUP_PARENT_SET("parent.set", Type.GROUP, false),
GROUP_PARENT_SET_TRACK("parent.settrack", Type.GROUP, false),
GROUP_PARENT_ADD("parent.add", Type.GROUP, false),
GROUP_PARENT_REMOVE("parent.remove", Type.GROUP, false),
GROUP_PARENT_ADD_TEMP("parent.addtemp", Type.GROUP, false),
GROUP_PARENT_REMOVE_TEMP("parent.removetemp", Type.GROUP, false),
GROUP_PARENT_CLEAR("parent.clear", Type.GROUP, false),
GROUP_PARENT_CLEAR_TRACK("parent.cleartrack", Type.GROUP, false),
GROUP_META_INFO("meta.info", Type.GROUP, true),
GROUP_META_SET("meta.set", Type.GROUP, false),
GROUP_META_UNSET("meta.unset", Type.GROUP, false),
GROUP_META_SET_TEMP("meta.settemp", Type.GROUP, false),
GROUP_META_UNSET_TEMP("meta.unsettemp", Type.GROUP, false),
GROUP_META_ADD_PREFIX("meta.addprefix", Type.GROUP, false),
GROUP_META_ADD_SUFFIX("meta.addsuffix", Type.GROUP, false),
GROUP_META_SET_PREFIX("meta.setprefix", Type.GROUP, false),
GROUP_META_SET_SUFFIX("meta.setsuffix", Type.GROUP, false),
GROUP_META_REMOVE_PREFIX("meta.removeprefix", Type.GROUP, false),
GROUP_META_REMOVE_SUFFIX("meta.removesuffix", Type.GROUP, false),
GROUP_META_ADD_TEMP_PREFIX("meta.addtempprefix", Type.GROUP, false),
GROUP_META_ADD_TEMP_SUFFIX("meta.addtempsuffix", Type.GROUP, false),
GROUP_META_SET_TEMP_PREFIX("meta.settempprefix", Type.GROUP, false),
GROUP_META_SET_TEMP_SUFFIX("meta.settempsuffix", Type.GROUP, false),
GROUP_META_REMOVE_TEMP_PREFIX("meta.removetempprefix", Type.GROUP, false),
GROUP_META_REMOVE_TEMP_SUFFIX("meta.removetempsuffix", Type.GROUP, false),
GROUP_META_CLEAR("meta.clear", Type.GROUP, false),
GROUP_EDITOR("editor", Type.GROUP, true),
GROUP_LIST_MEMBERS("listmembers", Type.GROUP, true),
GROUP_SHOW_TRACKS("showtracks", Type.GROUP, true),
GROUP_SET_WEIGHT("setweight", Type.GROUP, false),
GROUP_SET_DISPLAY_NAME("setdisplayname", Type.GROUP, false),
GROUP_CLEAR("clear", Type.GROUP, false),
GROUP_RENAME("rename", Type.GROUP, false),
GROUP_CLONE("clone", Type.GROUP, false),
GROUP_INFO("info", Type.GROUP),
GROUP_PERM_INFO("permission.info", Type.GROUP),
GROUP_PERM_SET("permission.set", Type.GROUP),
GROUP_PERM_UNSET("permission.unset", Type.GROUP),
GROUP_PERM_SET_TEMP("permission.settemp", Type.GROUP),
GROUP_PERM_UNSET_TEMP("permission.unsettemp", Type.GROUP),
GROUP_PERM_CHECK("permission.check", Type.GROUP),
GROUP_PERM_CLEAR("permission.clear", Type.GROUP),
GROUP_PARENT_INFO("parent.info", Type.GROUP),
GROUP_PARENT_SET("parent.set", Type.GROUP),
GROUP_PARENT_SET_TRACK("parent.settrack", Type.GROUP),
GROUP_PARENT_ADD("parent.add", Type.GROUP),
GROUP_PARENT_REMOVE("parent.remove", Type.GROUP),
GROUP_PARENT_ADD_TEMP("parent.addtemp", Type.GROUP),
GROUP_PARENT_REMOVE_TEMP("parent.removetemp", Type.GROUP),
GROUP_PARENT_CLEAR("parent.clear", Type.GROUP),
GROUP_PARENT_CLEAR_TRACK("parent.cleartrack", Type.GROUP),
GROUP_META_INFO("meta.info", Type.GROUP),
GROUP_META_SET("meta.set", Type.GROUP),
GROUP_META_UNSET("meta.unset", Type.GROUP),
GROUP_META_SET_TEMP("meta.settemp", Type.GROUP),
GROUP_META_UNSET_TEMP("meta.unsettemp", Type.GROUP),
GROUP_META_ADD_PREFIX("meta.addprefix", Type.GROUP),
GROUP_META_ADD_SUFFIX("meta.addsuffix", Type.GROUP),
GROUP_META_SET_PREFIX("meta.setprefix", Type.GROUP),
GROUP_META_SET_SUFFIX("meta.setsuffix", Type.GROUP),
GROUP_META_REMOVE_PREFIX("meta.removeprefix", Type.GROUP),
GROUP_META_REMOVE_SUFFIX("meta.removesuffix", Type.GROUP),
GROUP_META_ADD_TEMP_PREFIX("meta.addtempprefix", Type.GROUP),
GROUP_META_ADD_TEMP_SUFFIX("meta.addtempsuffix", Type.GROUP),
GROUP_META_SET_TEMP_PREFIX("meta.settempprefix", Type.GROUP),
GROUP_META_SET_TEMP_SUFFIX("meta.settempsuffix", Type.GROUP),
GROUP_META_REMOVE_TEMP_PREFIX("meta.removetempprefix", Type.GROUP),
GROUP_META_REMOVE_TEMP_SUFFIX("meta.removetempsuffix", Type.GROUP),
GROUP_META_CLEAR("meta.clear", Type.GROUP),
GROUP_EDITOR("editor", Type.GROUP),
GROUP_LIST_MEMBERS("listmembers", Type.GROUP),
GROUP_SHOW_TRACKS("showtracks", Type.GROUP),
GROUP_SET_WEIGHT("setweight", Type.GROUP),
GROUP_SET_DISPLAY_NAME("setdisplayname", Type.GROUP),
GROUP_CLEAR("clear", Type.GROUP),
GROUP_RENAME("rename", Type.GROUP),
GROUP_CLONE("clone", Type.GROUP),
TRACK_INFO("info", Type.TRACK, true),
TRACK_EDITOR("editor", Type.TRACK, true),
TRACK_APPEND("append", Type.TRACK, false),
TRACK_INSERT("insert", Type.TRACK, false),
TRACK_REMOVE("remove", Type.TRACK, false),
TRACK_CLEAR("clear", Type.TRACK, false),
TRACK_RENAME("rename", Type.TRACK, false),
TRACK_CLONE("clone", Type.TRACK, false),
TRACK_INFO("info", Type.TRACK),
TRACK_EDITOR("editor", Type.TRACK),
TRACK_APPEND("append", Type.TRACK),
TRACK_INSERT("insert", Type.TRACK),
TRACK_REMOVE("remove", Type.TRACK),
TRACK_CLEAR("clear", Type.TRACK),
TRACK_RENAME("rename", Type.TRACK),
TRACK_CLONE("clone", Type.TRACK),
LOG_RECENT("recent", Type.LOG, true),
LOG_USER_HISTORY("userhistory", Type.LOG, true),
LOG_GROUP_HISTORY("grouphistory", Type.LOG, true),
LOG_TRACK_HISTORY("trackhistory", Type.LOG, true),
LOG_SEARCH("search", Type.LOG, true),
LOG_NOTIFY("notify", Type.LOG, true),
LOG_RECENT("recent", Type.LOG),
LOG_USER_HISTORY("userhistory", Type.LOG),
LOG_GROUP_HISTORY("grouphistory", Type.LOG),
LOG_TRACK_HISTORY("trackhistory", Type.LOG),
LOG_SEARCH("search", Type.LOG),
LOG_NOTIFY("notify", Type.LOG),
SPONGE_PERMISSION_INFO("permission.info", Type.SPONGE, true),
SPONGE_PERMISSION_SET("permission.set", Type.SPONGE, false),
SPONGE_PERMISSION_CLEAR("permission.clear", Type.SPONGE, false),
SPONGE_PARENT_INFO("parent.info", Type.SPONGE, true),
SPONGE_PARENT_ADD("parent.add", Type.SPONGE, false),
SPONGE_PARENT_REMOVE("parent.remove", Type.SPONGE, false),
SPONGE_PARENT_CLEAR("parent.clear", Type.SPONGE, false),
SPONGE_OPTION_INFO("option.info", Type.SPONGE, true),
SPONGE_OPTION_SET("option.set", Type.SPONGE, false),
SPONGE_OPTION_UNSET("option.unset", Type.SPONGE, false),
SPONGE_OPTION_CLEAR("option.clear", Type.SPONGE, false);
SPONGE_PERMISSION_INFO("permission.info", Type.SPONGE),
SPONGE_PERMISSION_SET("permission.set", Type.SPONGE),
SPONGE_PERMISSION_CLEAR("permission.clear", Type.SPONGE),
SPONGE_PARENT_INFO("parent.info", Type.SPONGE),
SPONGE_PARENT_ADD("parent.add", Type.SPONGE),
SPONGE_PARENT_REMOVE("parent.remove", Type.SPONGE),
SPONGE_PARENT_CLEAR("parent.clear", Type.SPONGE),
SPONGE_OPTION_INFO("option.info", Type.SPONGE),
SPONGE_OPTION_SET("option.set", Type.SPONGE),
SPONGE_OPTION_UNSET("option.unset", Type.SPONGE),
SPONGE_OPTION_CLEAR("option.clear", Type.SPONGE);
public static final String ROOT = "luckperms.";
@@ -176,11 +176,9 @@ public enum CommandPermission {
private final String permission;
private final Type type;
private final boolean readOnly;
CommandPermission(String node, Type type, boolean readOnly) {
CommandPermission(String node, Type type) {
this.type = type;
this.readOnly = readOnly;
if (type == Type.NONE) {
this.node = node;
@@ -207,10 +205,6 @@ public enum CommandPermission {
return this.type;
}
public boolean isReadOnly() {
return this.readOnly;
}
public enum Type {
NONE(null),
@@ -28,9 +28,12 @@ package me.lucko.luckperms.common.commands.group;
import me.lucko.luckperms.common.actionlog.LoggedAction;
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
import me.lucko.luckperms.common.bulkupdate.BulkUpdateBuilder;
import me.lucko.luckperms.common.bulkupdate.BulkUpdateField;
import me.lucko.luckperms.common.bulkupdate.DataType;
import me.lucko.luckperms.common.bulkupdate.action.DeleteAction;
import me.lucko.luckperms.common.bulkupdate.comparison.Constraint;
import me.lucko.luckperms.common.bulkupdate.comparison.StandardComparison;
import me.lucko.luckperms.common.bulkupdate.query.Query;
import me.lucko.luckperms.common.bulkupdate.query.QueryField;
import me.lucko.luckperms.common.command.abstraction.SingleCommand;
import me.lucko.luckperms.common.command.access.ArgumentPermissions;
import me.lucko.luckperms.common.command.access.CommandPermission;
@@ -40,7 +43,6 @@ 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.config.ConfigKeys;
import me.lucko.luckperms.common.filter.Comparison;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.messaging.InternalMessagingService;
import me.lucko.luckperms.common.model.Group;
@@ -113,7 +115,7 @@ public class DeleteGroup extends SingleCommand {
.trackStatistics(false)
.dataType(DataType.ALL)
.action(DeleteAction.create())
.filter(BulkUpdateField.PERMISSION, Comparison.EQUAL, Inheritance.key(groupName))
.query(Query.of(QueryField.PERMISSION, Constraint.of(StandardComparison.EQUAL, Inheritance.key(groupName))))
.build();
plugin.getStorage().applyBulkUpdate(operation).whenCompleteAsync((v, ex) -> {
if (ex != null) {
@@ -60,7 +60,7 @@ public class GroupParentCommand extends ParentCommand<Group, String> {
.build(key -> new ReentrantLock());
public GroupParentCommand() {
super(CommandSpec.GROUP, "Group", Type.TARGETED, ImmutableList.<Command<Group>>builder()
super(CommandSpec.GROUP, "Group", Type.TAKES_ARGUMENT_FOR_TARGET, ImmutableList.<Command<Group>>builder()
.add(new GroupInfo())
.add(new CommandPermission<>(HolderType.GROUP))
.add(new CommandParent<>(HolderType.GROUP))
@@ -28,8 +28,11 @@ package me.lucko.luckperms.common.commands.group;
import me.lucko.luckperms.common.actionlog.LoggedAction;
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
import me.lucko.luckperms.common.bulkupdate.BulkUpdateBuilder;
import me.lucko.luckperms.common.bulkupdate.BulkUpdateField;
import me.lucko.luckperms.common.bulkupdate.action.UpdateAction;
import me.lucko.luckperms.common.bulkupdate.comparison.Constraint;
import me.lucko.luckperms.common.bulkupdate.comparison.StandardComparison;
import me.lucko.luckperms.common.bulkupdate.query.Query;
import me.lucko.luckperms.common.bulkupdate.query.QueryField;
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
import me.lucko.luckperms.common.command.access.ArgumentPermissions;
import me.lucko.luckperms.common.command.access.CommandPermission;
@@ -38,7 +41,6 @@ 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.command.utils.StorageAssistant;
import me.lucko.luckperms.common.filter.Comparison;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.node.types.Inheritance;
@@ -110,8 +112,8 @@ public class GroupRename extends ChildCommand<Group> {
BulkUpdate operation = BulkUpdateBuilder.create()
.trackStatistics(false)
.dataType(me.lucko.luckperms.common.bulkupdate.DataType.ALL)
.action(UpdateAction.of(BulkUpdateField.PERMISSION, Inheritance.key(newGroupName)))
.filter(BulkUpdateField.PERMISSION, Comparison.EQUAL, Inheritance.key(target.getName()))
.action(UpdateAction.of(QueryField.PERMISSION, Inheritance.key(newGroupName)))
.query(Query.of(QueryField.PERMISSION, Constraint.of(StandardComparison.EQUAL, Inheritance.key(target.getName()))))
.build();
return plugin.getStorage().applyBulkUpdate(operation);
} else {
@@ -25,26 +25,25 @@
package me.lucko.luckperms.common.commands.log;
import me.lucko.luckperms.common.actionlog.LogPage;
import me.lucko.luckperms.common.actionlog.Log;
import me.lucko.luckperms.common.actionlog.LoggedAction;
import me.lucko.luckperms.common.actionlog.filter.ActionFilters;
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
import me.lucko.luckperms.common.command.access.CommandPermission;
import me.lucko.luckperms.common.command.spec.CommandSpec;
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.filter.PageParameters;
import me.lucko.luckperms.common.locale.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.Paginated;
import me.lucko.luckperms.common.util.Predicates;
import java.util.List;
import java.util.Locale;
public class LogGroupHistory extends ChildCommand<Void> {
public class LogGroupHistory extends ChildCommand<Log> {
private static final int ENTRIES_PER_PAGE = 10;
public LogGroupHistory() {
@@ -52,34 +51,44 @@ public class LogGroupHistory extends ChildCommand<Void> {
}
@Override
public void execute(LuckPermsPlugin plugin, Sender sender, Void ignored, ArgumentList args, String label) {
public void execute(LuckPermsPlugin plugin, Sender sender, Log log, ArgumentList args, String label) {
String group = args.get(0).toLowerCase(Locale.ROOT);
if (!DataConstraints.GROUP_NAME_TEST.test(group)) {
Message.GROUP_INVALID_ENTRY.send(sender, group);
return;
}
PageParameters pageParams = new PageParameters(ENTRIES_PER_PAGE, args.getIntOrDefault(1, 1));
LogPage log = plugin.getStorage().getLogPage(ActionFilters.group(group), pageParams).join();
Paginated<LoggedAction> content = new Paginated<>(log.getGroupHistory(group));
int page = pageParams.pageNumber();
int maxPage = pageParams.getMaxPage(log.getTotalEntries());
int page = args.getIntOrDefault(1, Integer.MIN_VALUE);
if (page != Integer.MIN_VALUE) {
showLog(page, sender, content);
} else {
showLog(content.getMaxPages(ENTRIES_PER_PAGE), sender, content);
}
}
if (log.getTotalEntries() == 0) {
private static void showLog(int page, Sender sender, Paginated<LoggedAction> log) {
int maxPage = log.getMaxPages(ENTRIES_PER_PAGE);
if (maxPage == 0) {
Message.LOG_NO_ENTRIES.send(sender);
return;
}
if (page == Integer.MIN_VALUE) {
page = maxPage;
}
if (page < 1 || page > maxPage) {
Message.LOG_INVALID_PAGE_RANGE.send(sender, maxPage);
return;
}
List<LogPage.Entry<LoggedAction>> entries = log.getNumberedContent();
List<Paginated.Entry<LoggedAction>> entries = log.getPage(page, ENTRIES_PER_PAGE);
String name = entries.stream().findAny().get().value().getTarget().getName();
Message.LOG_HISTORY_GROUP_HEADER.send(sender, name, page, maxPage);
for (LogPage.Entry<LoggedAction> e : entries) {
for (Paginated.Entry<LoggedAction> e : entries) {
Message.LOG_ENTRY.send(sender, e.position(), e.value());
}
}
@@ -25,6 +25,7 @@
package me.lucko.luckperms.common.commands.log;
import me.lucko.luckperms.common.actionlog.Log;
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
import me.lucko.luckperms.common.command.access.CommandPermission;
import me.lucko.luckperms.common.command.spec.CommandSpec;
@@ -42,7 +43,7 @@ import net.luckperms.api.node.Node;
import java.util.Optional;
import java.util.UUID;
public class LogNotify extends ChildCommand<Void> {
public class LogNotify extends ChildCommand<Log> {
private static final String IGNORE_NODE = "luckperms.log.notify.ignoring";
public LogNotify() {
@@ -82,7 +83,7 @@ public class LogNotify extends ChildCommand<Void> {
}
@Override
public void execute(LuckPermsPlugin plugin, Sender sender, Void ignored, ArgumentList args, String label) {
public void execute(LuckPermsPlugin plugin, Sender sender, Log log, ArgumentList args, String label) {
if (sender.isConsole()) {
Message.LOG_NOTIFY_CONSOLE.send(sender);
return;
@@ -26,13 +26,22 @@
package me.lucko.luckperms.common.commands.log;
import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.common.actionlog.Log;
import me.lucko.luckperms.common.command.abstraction.Command;
import me.lucko.luckperms.common.command.abstraction.ParentCommand;
import me.lucko.luckperms.common.command.spec.CommandSpec;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.sender.Sender;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
public class LogParentCommand extends ParentCommand<Log, Void> {
private final ReentrantLock lock = new ReentrantLock();
public class LogParentCommand extends ParentCommand<Void, Void> {
public LogParentCommand() {
super(CommandSpec.LOG, "Log", Type.NOT_TARGETED, ImmutableList.<Command<Void>>builder()
super(CommandSpec.LOG, "Log", Type.NO_TARGET_ARGUMENT, ImmutableList.<Command<Log>>builder()
.add(new LogRecent())
.add(new LogSearch())
.add(new LogNotify())
@@ -42,4 +51,38 @@ public class LogParentCommand extends ParentCommand<Void, Void> {
.build()
);
}
@Override
protected ReentrantLock getLockForTarget(Void target) {
return this.lock; // all commands target the same log, so we share a lock between all "targets"
}
@Override
protected Log getTarget(Void target, LuckPermsPlugin plugin, Sender sender) {
Log log = plugin.getStorage().getLog().join();
if (log == null) {
Message.LOG_LOAD_ERROR.send(sender);
}
return log;
}
@Override
protected void cleanup(Log log, LuckPermsPlugin plugin) {
}
@Override
protected List<String> getTargets(LuckPermsPlugin plugin) {
// should never be called if we specify Type.NO_TARGET_ARGUMENT in the constructor
throw new UnsupportedOperationException();
}
@Override
protected Void parseTarget(String target, LuckPermsPlugin plugin, Sender sender) {
// should never be called if we specify Type.NO_TARGET_ARGUMENT in the constructor
throw new UnsupportedOperationException();
}
}
@@ -25,23 +25,22 @@
package me.lucko.luckperms.common.commands.log;
import me.lucko.luckperms.common.actionlog.LogPage;
import me.lucko.luckperms.common.actionlog.Log;
import me.lucko.luckperms.common.actionlog.LoggedAction;
import me.lucko.luckperms.common.actionlog.filter.ActionFilters;
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
import me.lucko.luckperms.common.command.access.CommandPermission;
import me.lucko.luckperms.common.command.spec.CommandSpec;
import me.lucko.luckperms.common.command.utils.ArgumentList;
import me.lucko.luckperms.common.filter.PageParameters;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.sender.Sender;
import me.lucko.luckperms.common.util.Paginated;
import me.lucko.luckperms.common.util.Predicates;
import java.util.List;
import java.util.UUID;
public class LogRecent extends ChildCommand<Void> {
public class LogRecent extends ChildCommand<Log> {
private static final int ENTRIES_PER_PAGE = 10;
public LogRecent() {
@@ -49,32 +48,39 @@ public class LogRecent extends ChildCommand<Void> {
}
@Override
public void execute(LuckPermsPlugin plugin, Sender sender, Void ignored, ArgumentList args, String label) {
int page = 1;
UUID uuid = null;
if (!args.isEmpty()) {
int pageNo = args.getIntOrDefault(0, Integer.MIN_VALUE);
if (pageNo != Integer.MIN_VALUE) {
page = pageNo;
} else {
uuid = args.getUserTarget(0, plugin, sender);
if (uuid == null) {
return;
}
pageNo = args.getIntOrDefault(1, Integer.MIN_VALUE);
if (pageNo != Integer.MIN_VALUE) {
page = pageNo;
}
}
public void execute(LuckPermsPlugin plugin, Sender sender, Log log, ArgumentList args, String label) {
if (args.isEmpty()) {
// No page or user
Paginated<LoggedAction> content = new Paginated<>(log.getContent());
showLog(content.getMaxPages(ENTRIES_PER_PAGE), false, sender, content);
return;
}
PageParameters pageParams = new PageParameters(ENTRIES_PER_PAGE, page);
LogPage log = plugin.getStorage().getLogPage(uuid == null ? ActionFilters.all() : ActionFilters.source(uuid), pageParams).join();
int page = args.getIntOrDefault(0, Integer.MIN_VALUE);
if (page != Integer.MIN_VALUE) {
Paginated<LoggedAction> content = new Paginated<>(log.getContent());
showLog(page, false, sender, content);
return;
}
int maxPage = pageParams.getMaxPage(log.getTotalEntries());
if (log.getTotalEntries() == 0) {
// User and possibly page
UUID uuid = args.getUserTarget(0, plugin, sender);
if (uuid == null) {
return;
}
Paginated<LoggedAction> content = new Paginated<>(log.getContent(uuid));
page = args.getIntOrDefault(1, Integer.MIN_VALUE);
if (page != Integer.MIN_VALUE) {
showLog(page, true, sender, content);
} else {
showLog(content.getMaxPages(ENTRIES_PER_PAGE), true, sender, content);
}
}
private static void showLog(int page, boolean specificUser, Sender sender, Paginated<LoggedAction> log) {
int maxPage = log.getMaxPages(ENTRIES_PER_PAGE);
if (maxPage == 0) {
Message.LOG_NO_ENTRIES.send(sender);
return;
}
@@ -84,8 +90,8 @@ public class LogRecent extends ChildCommand<Void> {
return;
}
List<LogPage.Entry<LoggedAction>> entries = log.getNumberedContent();
if (uuid != null) {
List<Paginated.Entry<LoggedAction>> entries = log.getPage(page, ENTRIES_PER_PAGE);
if (specificUser) {
String name = entries.stream().findAny().get().value().getSource().getName();
if (name.contains("@")) {
name = name.split("@")[0];
@@ -95,9 +101,8 @@ public class LogRecent extends ChildCommand<Void> {
Message.LOG_RECENT_HEADER.send(sender, page, maxPage);
}
for (LogPage.Entry<LoggedAction> e : entries) {
for (Paginated.Entry<LoggedAction> e : entries) {
Message.LOG_ENTRY.send(sender, e.position(), e.value());
}
}
}
@@ -25,22 +25,21 @@
package me.lucko.luckperms.common.commands.log;
import me.lucko.luckperms.common.actionlog.LogPage;
import me.lucko.luckperms.common.actionlog.Log;
import me.lucko.luckperms.common.actionlog.LoggedAction;
import me.lucko.luckperms.common.actionlog.filter.ActionFilters;
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
import me.lucko.luckperms.common.command.access.CommandPermission;
import me.lucko.luckperms.common.command.spec.CommandSpec;
import me.lucko.luckperms.common.command.utils.ArgumentList;
import me.lucko.luckperms.common.filter.PageParameters;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.sender.Sender;
import me.lucko.luckperms.common.util.Paginated;
import me.lucko.luckperms.common.util.Predicates;
import java.util.List;
public class LogSearch extends ChildCommand<Void> {
public class LogSearch extends ChildCommand<Log> {
private static final int ENTRIES_PER_PAGE = 10;
public LogSearch() {
@@ -48,8 +47,8 @@ public class LogSearch extends ChildCommand<Void> {
}
@Override
public void execute(LuckPermsPlugin plugin, Sender sender, Void ignored, ArgumentList args, String label) {
int page = 1;
public void execute(LuckPermsPlugin plugin, Sender sender, Log log, ArgumentList args, String label) {
int page = Integer.MIN_VALUE;
if (args.size() > 1) {
try {
page = Integer.parseInt(args.get(args.size() - 1));
@@ -60,26 +59,36 @@ public class LogSearch extends ChildCommand<Void> {
}
final String query = String.join(" ", args);
PageParameters pageParams = new PageParameters(ENTRIES_PER_PAGE, page);
LogPage log = plugin.getStorage().getLogPage(ActionFilters.search(query), pageParams).join();
Paginated<LoggedAction> content = new Paginated<>(log.getSearch(query));
int maxPage = pageParams.getMaxPage(log.getTotalEntries());
if (log.getTotalEntries() == 0) {
if (page != Integer.MIN_VALUE) {
showLog(page, query, sender, content);
} else {
showLog(content.getMaxPages(ENTRIES_PER_PAGE), query, sender, content);
}
}
private static void showLog(int page, String query, Sender sender, Paginated<LoggedAction> log) {
int maxPage = log.getMaxPages(ENTRIES_PER_PAGE);
if (maxPage == 0) {
Message.LOG_NO_ENTRIES.send(sender);
return;
}
if (page == Integer.MIN_VALUE) {
page = maxPage;
}
if (page < 1 || page > maxPage) {
Message.LOG_INVALID_PAGE_RANGE.send(sender, maxPage);
return;
}
List<LogPage.Entry<LoggedAction>> entries = log.getNumberedContent();
List<Paginated.Entry<LoggedAction>> entries = log.getPage(page, ENTRIES_PER_PAGE);
Message.LOG_SEARCH_HEADER.send(sender, query, page, maxPage);
for (LogPage.Entry<LoggedAction> e : entries) {
for (Paginated.Entry<LoggedAction> e : entries) {
Message.LOG_ENTRY.send(sender, e.position(), e.value());
}
}
}
@@ -25,26 +25,25 @@
package me.lucko.luckperms.common.commands.log;
import me.lucko.luckperms.common.actionlog.LogPage;
import me.lucko.luckperms.common.actionlog.Log;
import me.lucko.luckperms.common.actionlog.LoggedAction;
import me.lucko.luckperms.common.actionlog.filter.ActionFilters;
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
import me.lucko.luckperms.common.command.access.CommandPermission;
import me.lucko.luckperms.common.command.spec.CommandSpec;
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.filter.PageParameters;
import me.lucko.luckperms.common.locale.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.Paginated;
import me.lucko.luckperms.common.util.Predicates;
import java.util.List;
import java.util.Locale;
public class LogTrackHistory extends ChildCommand<Void> {
public class LogTrackHistory extends ChildCommand<Log> {
private static final int ENTRIES_PER_PAGE = 10;
public LogTrackHistory() {
@@ -52,33 +51,44 @@ public class LogTrackHistory extends ChildCommand<Void> {
}
@Override
public void execute(LuckPermsPlugin plugin, Sender sender, Void ignored, ArgumentList args, String label) {
public void execute(LuckPermsPlugin plugin, Sender sender, Log log, ArgumentList args, String label) {
String track = args.get(0).toLowerCase(Locale.ROOT);
if (!DataConstraints.TRACK_NAME_TEST.test(track)) {
Message.TRACK_INVALID_ENTRY.send(sender, track);
return;
}
PageParameters pageParams = new PageParameters(ENTRIES_PER_PAGE, args.getIntOrDefault(1, 1));
LogPage log = plugin.getStorage().getLogPage(ActionFilters.track(track), pageParams).join();
int page = pageParams.pageNumber();
int maxPage = pageParams.getMaxPage(log.getTotalEntries());
Paginated<LoggedAction> content = new Paginated<>(log.getTrackHistory(track));
if (log.getTotalEntries() == 0) {
int page = args.getIntOrDefault(1, Integer.MIN_VALUE);
if (page != Integer.MIN_VALUE) {
showLog(page, sender, content);
} else {
showLog(content.getMaxPages(ENTRIES_PER_PAGE), sender, content);
}
}
private static void showLog(int page, Sender sender, Paginated<LoggedAction> log) {
int maxPage = log.getMaxPages(ENTRIES_PER_PAGE);
if (maxPage == 0) {
Message.LOG_NO_ENTRIES.send(sender);
return;
}
if (page == Integer.MIN_VALUE) {
page = maxPage;
}
if (page < 1 || page > maxPage) {
Message.LOG_INVALID_PAGE_RANGE.send(sender, maxPage);
return;
}
List<LogPage.Entry<LoggedAction>> entries = log.getNumberedContent();
List<Paginated.Entry<LoggedAction>> entries = log.getPage(page, ENTRIES_PER_PAGE);
String name = entries.stream().findAny().get().value().getTarget().getName();
Message.LOG_HISTORY_TRACK_HEADER.send(sender, name, page, maxPage);
for (LogPage.Entry<LoggedAction> e : entries) {
for (Paginated.Entry<LoggedAction> e : entries) {
Message.LOG_ENTRY.send(sender, e.position(), e.value());
}
}
@@ -25,23 +25,22 @@
package me.lucko.luckperms.common.commands.log;
import me.lucko.luckperms.common.actionlog.LogPage;
import me.lucko.luckperms.common.actionlog.Log;
import me.lucko.luckperms.common.actionlog.LoggedAction;
import me.lucko.luckperms.common.actionlog.filter.ActionFilters;
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
import me.lucko.luckperms.common.command.access.CommandPermission;
import me.lucko.luckperms.common.command.spec.CommandSpec;
import me.lucko.luckperms.common.command.utils.ArgumentList;
import me.lucko.luckperms.common.filter.PageParameters;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.sender.Sender;
import me.lucko.luckperms.common.util.Paginated;
import me.lucko.luckperms.common.util.Predicates;
import java.util.List;
import java.util.UUID;
public class LogUserHistory extends ChildCommand<Void> {
public class LogUserHistory extends ChildCommand<Log> {
private static final int ENTRIES_PER_PAGE = 10;
public LogUserHistory() {
@@ -49,19 +48,25 @@ public class LogUserHistory extends ChildCommand<Void> {
}
@Override
public void execute(LuckPermsPlugin plugin, Sender sender, Void ignored, ArgumentList args, String label) {
public void execute(LuckPermsPlugin plugin, Sender sender, Log log, ArgumentList args, String label) {
UUID uuid = args.getUserTarget(0, plugin, sender);
if (uuid == null) {
return;
}
PageParameters pageParams = new PageParameters(ENTRIES_PER_PAGE, args.getIntOrDefault(1, 1));
LogPage log = plugin.getStorage().getLogPage(ActionFilters.user(uuid), pageParams).join();
Paginated<LoggedAction> content = new Paginated<>(log.getUserHistory(uuid));
int page = pageParams.pageNumber();
int maxPage = pageParams.getMaxPage(log.getTotalEntries());
int page = args.getIntOrDefault(1, Integer.MIN_VALUE);
if (page != Integer.MIN_VALUE) {
showLog(page, sender, content);
} else {
showLog(content.getMaxPages(ENTRIES_PER_PAGE), sender, content);
}
}
if (log.getTotalEntries() == 0) {
private static void showLog(int page, Sender sender, Paginated<LoggedAction> log) {
int maxPage = log.getMaxPages(ENTRIES_PER_PAGE);
if (maxPage == 0) {
Message.LOG_NO_ENTRIES.send(sender);
return;
}
@@ -71,11 +76,11 @@ public class LogUserHistory extends ChildCommand<Void> {
return;
}
List<LogPage.Entry<LoggedAction>> entries = log.getNumberedContent();
List<Paginated.Entry<LoggedAction>> entries = log.getPage(page, ENTRIES_PER_PAGE);
String name = entries.stream().findAny().get().value().getTarget().getName();
Message.LOG_HISTORY_USER_HEADER.send(sender, name, page, maxPage);
for (LogPage.Entry<LoggedAction> e : entries) {
for (Paginated.Entry<LoggedAction> e : entries) {
Message.LOG_ENTRY.send(sender, e.position(), e.value());
}
}
@@ -28,12 +28,15 @@ package me.lucko.luckperms.common.commands.misc;
import com.github.benmanes.caffeine.cache.Cache;
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
import me.lucko.luckperms.common.bulkupdate.BulkUpdateBuilder;
import me.lucko.luckperms.common.bulkupdate.BulkUpdateField;
import me.lucko.luckperms.common.bulkupdate.BulkUpdateSqlBuilder;
import me.lucko.luckperms.common.bulkupdate.BulkUpdateStatistics;
import me.lucko.luckperms.common.bulkupdate.DataType;
import me.lucko.luckperms.common.bulkupdate.action.DeleteAction;
import me.lucko.luckperms.common.bulkupdate.action.UpdateAction;
import me.lucko.luckperms.common.bulkupdate.comparison.Comparison;
import me.lucko.luckperms.common.bulkupdate.comparison.Constraint;
import me.lucko.luckperms.common.bulkupdate.comparison.StandardComparison;
import me.lucko.luckperms.common.bulkupdate.query.Query;
import me.lucko.luckperms.common.bulkupdate.query.QueryField;
import me.lucko.luckperms.common.command.abstraction.CommandException;
import me.lucko.luckperms.common.command.abstraction.SingleCommand;
import me.lucko.luckperms.common.command.access.CommandPermission;
@@ -41,7 +44,6 @@ import me.lucko.luckperms.common.command.spec.CommandSpec;
import me.lucko.luckperms.common.command.utils.ArgumentException;
import me.lucko.luckperms.common.command.utils.ArgumentList;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.filter.Comparison;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.sender.Sender;
@@ -110,7 +112,7 @@ public class BulkUpdateCommand extends SingleCommand {
}
String field = args.remove(0);
BulkUpdateField queryField = BulkUpdateField.of(field);
QueryField queryField = QueryField.of(field);
if (queryField == null) {
throw new ArgumentException.DetailedUsage();
}
@@ -129,20 +131,20 @@ public class BulkUpdateCommand extends SingleCommand {
return;
}
BulkUpdateField field = BulkUpdateField.of(parts[0]);
QueryField field = QueryField.of(parts[0]);
if (field == null) {
Message.BULK_UPDATE_INVALID_CONSTRAINT.send(sender, constraint);
return;
}
Comparison comparison = Comparison.parse(parts[1]);
Comparison comparison = StandardComparison.parseComparison(parts[1]);
if (comparison == null) {
Message.BULK_UPDATE_INVALID_COMPARISON.send(sender, parts[1]);
return;
}
String expr = parts[2];
bulkUpdateBuilder.filter(field, comparison, expr);
bulkUpdateBuilder.query(Query.of(field, Constraint.of(comparison, expr)));
}
BulkUpdate bulkUpdate = bulkUpdateBuilder.build();
@@ -153,11 +155,7 @@ public class BulkUpdateCommand extends SingleCommand {
String id = String.format("%04d", ThreadLocalRandom.current().nextInt(10000));
this.pendingOperations.put(id, bulkUpdate);
BulkUpdateSqlBuilder sqlBuilder = new BulkUpdateSqlBuilder();
sqlBuilder.visit(bulkUpdate);
String readableSql = sqlBuilder.builder().toReadableString().replace("{table}", bulkUpdate.getDataType().getName());
Message.BULK_UPDATE_QUEUED.send(sender, readableSql);
Message.BULK_UPDATE_QUEUED.send(sender, bulkUpdate.buildAsSql().toReadableString().replace("{table}", bulkUpdate.getDataType().getName()));
Message.BULK_UPDATE_CONFIRM.send(sender, label, id);
}
}
@@ -33,6 +33,9 @@ import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.sender.Sender;
import me.lucko.luckperms.common.util.Predicates;
import net.kyori.adventure.text.Component;
import java.util.Map;
public class InfoCommand extends SingleCommand {
public InfoCommand() {
@@ -41,7 +44,8 @@ public class InfoCommand extends SingleCommand {
@Override
public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, String label) {
Message.INFO.send(sender, plugin, plugin.getStorage().getMeta());
Map<Component, Component> storageMeta = plugin.getStorage().getMeta();
Message.INFO.send(sender, plugin, storageMeta);
}
}
@@ -26,6 +26,9 @@
package me.lucko.luckperms.common.commands.misc;
import com.google.common.collect.Maps;
import me.lucko.luckperms.common.bulkupdate.comparison.Comparison;
import me.lucko.luckperms.common.bulkupdate.comparison.Constraint;
import me.lucko.luckperms.common.bulkupdate.comparison.StandardComparison;
import me.lucko.luckperms.common.cache.LoadingMap;
import me.lucko.luckperms.common.command.abstraction.SingleCommand;
import me.lucko.luckperms.common.command.access.CommandPermission;
@@ -33,7 +36,6 @@ import me.lucko.luckperms.common.command.spec.CommandSpec;
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.filter.Comparison;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.model.HolderType;
import me.lucko.luckperms.common.node.comparator.NodeEntryComparator;
@@ -60,13 +62,13 @@ public class SearchCommand extends SingleCommand {
@Override
public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, String label) {
Comparison comparison = Comparison.parse(args.get(0));
Comparison comparison = StandardComparison.parseComparison(args.get(0));
if (comparison == null) {
comparison = Comparison.EQUAL;
comparison = StandardComparison.EQUAL;
args.add(0, "==");
}
ConstraintNodeMatcher<Node> matcher = StandardNodeMatchers.key(args.get(1), comparison);
ConstraintNodeMatcher<Node> matcher = StandardNodeMatchers.of(Constraint.of(comparison, args.get(1)));
int page = args.getIntOrDefault(2, 1);
Message.SEARCH_SEARCHING.send(sender, matcher.toString());
@@ -118,7 +120,7 @@ public class SearchCommand extends SingleCommand {
headerMessage.send(sender, page, pages.size(), results.size());
for (Map.Entry<String, NodeEntry<T, Node>> ent : mappedContent) {
Message.SEARCH_NODE_ENTRY.send(sender, comparison != Comparison.EQUAL, ent.getValue().getNode(), ent.getKey(), holderType, label, sender.getPlugin());
Message.SEARCH_NODE_ENTRY.send(sender, comparison != StandardComparison.EQUAL, ent.getValue().getNode(), ent.getKey(), holderType, label, sender.getPlugin());
}
}
}
@@ -53,7 +53,7 @@ public class TrackParentCommand extends ParentCommand<Track, String> {
.build(key -> new ReentrantLock());
public TrackParentCommand() {
super(CommandSpec.TRACK, "Track", Type.TARGETED, ImmutableList.<Command<Track>>builder()
super(CommandSpec.TRACK, "Track", Type.TAKES_ARGUMENT_FOR_TARGET, ImmutableList.<Command<Track>>builder()
.add(new TrackInfo())
.add(new TrackEditor())
.add(new TrackAppend())
@@ -62,7 +62,7 @@ public class UserParentCommand extends ParentCommand<User, UserIdentifier> {
.build(key -> new ReentrantLock());
public UserParentCommand() {
super(CommandSpec.USER, "User", Type.TARGETED, ImmutableList.<Command<User>>builder()
super(CommandSpec.USER, "User", Type.TAKES_ARGUMENT_FOR_TARGET, ImmutableList.<Command<User>>builder()
.add(new UserInfo())
.add(new CommandPermission<>(HolderType.USER))
.add(new CommandParent<>(HolderType.USER))
@@ -28,14 +28,14 @@ package me.lucko.luckperms.common.config;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import me.lucko.luckperms.common.cacheddata.metastack.SimpleMetaStackDefinition;
import me.lucko.luckperms.common.cacheddata.metastack.StandardStackElements;
import me.lucko.luckperms.common.cacheddata.type.SimpleMetaValueSelector;
import me.lucko.luckperms.common.config.generic.KeyedConfiguration;
import me.lucko.luckperms.common.config.generic.key.ConfigKey;
import me.lucko.luckperms.common.config.generic.key.SimpleConfigKey;
import me.lucko.luckperms.common.context.calculator.WorldNameRewriter;
import me.lucko.luckperms.common.graph.TraversalAlgorithm;
import me.lucko.luckperms.common.metastacking.SimpleMetaStackDefinition;
import me.lucko.luckperms.common.metastacking.StandardStackElements;
import me.lucko.luckperms.common.model.PrimaryGroupHolder;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.query.QueryOptionsBuilderImpl;
@@ -246,26 +246,6 @@ public final class ConfigKeys {
*/
public static final ConfigKey<Boolean> REQUIRE_SENDER_GROUP_MEMBERSHIP_TO_MODIFY = booleanKey("require-sender-group-membership-to-modify", false);
/**
* If the plugin is in read-only mode for players
*/
public static final ConfigKey<Boolean> READ_ONLY_MODE_PLAYERS = booleanKey("commands-read-only-mode.players", false);
/**
* If the plugin is in read-only mode for the server console
*/
public static final ConfigKey<Boolean> READ_ONLY_MODE_CONSOLE = booleanKey("commands-read-only-mode.console", false);
/**
* If LuckPerms commands are disabled for players
*/
public static final ConfigKey<Boolean> DISABLE_LUCKPERMS_COMMANDS_PLAYERS = booleanKey("disable-luckperms-commands.players", false);
/**
* If LuckPerms commands are disabled for the server console
*/
public static final ConfigKey<Boolean> DISABLE_LUCKPERMS_COMMANDS_CONSOLE = booleanKey("disable-luckperms-commands.console", false);
/**
* If wildcards are being applied
*/
@@ -390,16 +370,16 @@ public final class ConfigKeys {
/**
* Creates a new prefix MetaStack element based upon the configured values.
*/
public static final ConfigKey<MetaStackDefinition> PREFIX_FORMATTING_OPTIONS = key(c -> {
List<String> format = c.getStringList("meta-formatting.prefix.format", new ArrayList<>());
public static final ConfigKey<MetaStackDefinition> PREFIX_FORMATTING_OPTIONS = key(l -> {
List<String> format = l.getStringList("meta-formatting.prefix.format", new ArrayList<>());
if (format.isEmpty()) {
format.add("highest");
}
String startSpacer = c.getString("meta-formatting.prefix.start-spacer", "");
String middleSpacer = c.getString("meta-formatting.prefix.middle-spacer", " ");
String endSpacer = c.getString("meta-formatting.prefix.end-spacer", "");
String startSpacer = l.getString("meta-formatting.prefix.start-spacer", "");
String middleSpacer = l.getString("meta-formatting.prefix.middle-spacer", " ");
String endSpacer = l.getString("meta-formatting.prefix.end-spacer", "");
DuplicateRemovalFunction duplicateRemovalFunction;
switch (c.getString("meta-formatting.prefix.duplicates", "").toLowerCase(Locale.ROOT)) {
switch (l.getString("meta-formatting.prefix.duplicates", "").toLowerCase(Locale.ROOT)) {
case "first-only":
duplicateRemovalFunction = DuplicateRemovalFunction.FIRST_ONLY;
break;
@@ -411,22 +391,22 @@ public final class ConfigKeys {
break;
}
return new SimpleMetaStackDefinition(StandardStackElements.parseList(c.getPlugin(), format), duplicateRemovalFunction, startSpacer, middleSpacer, endSpacer);
return new SimpleMetaStackDefinition(StandardStackElements.parseList(l.getPlugin(), format), duplicateRemovalFunction, startSpacer, middleSpacer, endSpacer);
});
/**
* Creates a new suffix MetaStack element based upon the configured values.
*/
public static final ConfigKey<MetaStackDefinition> SUFFIX_FORMATTING_OPTIONS = key(c -> {
List<String> format = c.getStringList("meta-formatting.suffix.format", new ArrayList<>());
public static final ConfigKey<MetaStackDefinition> SUFFIX_FORMATTING_OPTIONS = key(l -> {
List<String> format = l.getStringList("meta-formatting.suffix.format", new ArrayList<>());
if (format.isEmpty()) {
format.add("highest");
}
String startSpacer = c.getString("meta-formatting.suffix.start-spacer", "");
String middleSpacer = c.getString("meta-formatting.suffix.middle-spacer", " ");
String endSpacer = c.getString("meta-formatting.suffix.end-spacer", "");
String startSpacer = l.getString("meta-formatting.suffix.start-spacer", "");
String middleSpacer = l.getString("meta-formatting.suffix.middle-spacer", " ");
String endSpacer = l.getString("meta-formatting.suffix.end-spacer", "");
DuplicateRemovalFunction duplicateRemovalFunction;
switch (c.getString("meta-formatting.suffix.duplicates", "").toLowerCase(Locale.ROOT)) {
switch (l.getString("meta-formatting.suffix.duplicates", "").toLowerCase(Locale.ROOT)) {
case "first-only":
duplicateRemovalFunction = DuplicateRemovalFunction.FIRST_ONLY;
break;
@@ -438,7 +418,7 @@ public final class ConfigKeys {
break;
}
return new SimpleMetaStackDefinition(StandardStackElements.parseList(c.getPlugin(), format), duplicateRemovalFunction, startSpacer, middleSpacer, endSpacer);
return new SimpleMetaStackDefinition(StandardStackElements.parseList(l.getPlugin(), format), duplicateRemovalFunction, startSpacer, middleSpacer, endSpacer);
});
/**
@@ -463,11 +443,6 @@ public final class ConfigKeys {
.collect(ImmutableCollectors.toList());
});
/**
* If log should be posted synchronously to storage/messaging in commands
*/
public static final ConfigKey<Boolean> LOG_SYNCHRONOUSLY_IN_COMMANDS = booleanKey("log-synchronously-in-commands", false);
/**
* If LuckPerms should automatically install translation bundles and periodically update them.
*/
@@ -605,16 +580,6 @@ public final class ConfigKeys {
return c.getString("data.mongodb-connection-uri", c.getString("data.mongodb_connection_URI", ""));
}));
/**
* The REST storage URL
*/
public static final ConfigKey<String> REST_STORAGE_URL = notReloadable(stringKey("data.rest-url", "http://localhost:8080/"));
/**
* The REST storage auth key
*/
public static final ConfigKey<String> REST_STORAGE_AUTH_KEY = notReloadable(stringKey("data.rest-auth-key", ""));
/**
* The name of the storage method being used
*/
@@ -790,15 +755,4 @@ public final class ConfigKeys {
return KEYS;
}
/**
* Check if the value at the given path should be censored in console/log output
*
* @param path the path
* @return true if the value should be censored
*/
public static boolean shouldCensorValue(final String path) {
final String lower = path.toLowerCase(Locale.ROOT);
return lower.contains("password") || lower.contains("uri");
}
}
Loaded 100 of 339 files, more files were not shown because too many files have changed in this diff. Show more