Compare commits

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

No files matched your search

-6
View File
@@ -1,6 +0,0 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# These are explicitly windows files and should use crlf
*.bat text eol=crlf
.gitea/CODEOWNERS linguist-detectable=false
-3
View File
@@ -1,3 +0,0 @@
# Pull requests request review from the eligible staff team(s).
# One approval from either an Admin or Developer satisfies branch protection.
* @trueog/Admins @trueog/Developers
+2
View File
@@ -0,0 +1,2 @@
github: lucko
patreon: luckdev
+63
View File
@@ -0,0 +1,63 @@
name: 🐛 ​ Bug report
description: Report an issue or problem with the way LuckPerms is functioning.
labels: ["type: issue"]
body:
- type: markdown
attributes:
value: "Before going any further, please check to see if your problem is fixed by updating to a newer version. The latest development builds can be obtained from https://luckperms.net/download"
- type: textarea
id: description
attributes:
label: Description
description: Please provide a short description of the issue in the space below.
placeholder: Description goes here!
validations:
required: true
- type: textarea
id: reproduction-steps
attributes:
label: Reproduction Steps
description: Please provide a clear list of steps we should follow in order to reproduce your issue in the space below.
placeholder: List reproduction steps here, please!
validations:
required: true
- type: textarea
id: expected-behaviour
attributes:
label: Expected Behaviour
description: Please provide a description of what you expected to happen.
placeholder: Describe what you expected to happen here.
validations:
required: true
- type: input
id: server-info
attributes:
label: Server Details
description: Please provide a description of the server details, including type and specific version number.
placeholder: 'git-Paper-124 (MC: 1.17.1)'
validations:
required: true
- type: input
id: luckperms-version
attributes:
label: LuckPerms Version
description: Please provide the specific, precise version number of LuckPerms you are using to reproduce this bug.
placeholder: v5.3.70
validations:
required: true
- type: textarea
id: logs
attributes:
label: Logs and Configs
description: If you think it would be helpful, please also include a pastebin of any relevant config files or log output. You should use https://gist.github.com/ to upload, then include the link below.
placeholder: https://gist.github.com/HelpfulBugReporter/ThisIsAVeryUsefulLogOutput
validations:
required: false
- type: textarea
id: extra-info
attributes:
label: Extra Details
description: Please include any other relevant details in the space below.
placeholder: I was able to reproduce this only at the end of a rainbow.
validations:
required: false
+5
View File
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: 💬 ​ Ask a question
url: https://discord.gg/luckperms
about: We use Discord to provide support for the project. If you have a question, need help, or aren't sure if you've found a bug or not, please come and talk to us there!
+31
View File
@@ -0,0 +1,31 @@
name: 💡 ​ Suggestion
description: Suggest an idea for an improvement or change to LuckPerms.
labels: ["type: suggestion"]
body:
- type: markdown
attributes:
value: "Before going any further, please check to see if your suggestion has already been added by checking the [wiki](https://luckperms.net/wiki/Home). The latest development builds can be obtained from https://luckperms.net/download"
- type: textarea
id: description
attributes:
label: Description
description: Please provide a short description of your suggestion in the space below.
placeholder: Hello, perhaps you should...
validations:
required: true
- type: textarea
id: proposed-behaviour
attributes:
label: Proposed Behaviour
description: Please provide a short explanation of how the feature should work / be changed, and how this will affect the project.
placeholder: It would...
validations:
required: true
- type: textarea
id: extra-info
attributes:
label: Extra Details
description: Please include any other relevant details in the space below.
placeholder: I think this should only function at the end of a rainbow.
validations:
required: false
+127
View File
@@ -0,0 +1,127 @@
name: Build Gradle and Publish Docker image
on:
push:
branches:
- 'master'
tags:
- 'v*'
pull_request:
branches:
- 'master'
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-gradle:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: validate gradle wrapper
uses: gradle/wrapper-validation-action@v1
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- name: Setup Gradle
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@v3
if: success() || failure()
with:
report_paths: '**/build/test-results/test/TEST-*.xml'
annotate_notice: true
detailed_summary: true
- name: Upload all artifacts
uses: actions/upload-artifact@v3
with:
name: jars
path: |
bukkit/loader/build/libs/LuckPerms-Bukkit-*.jar
bukkit-legacy/loader/build/libs/LuckPerms-Bukkit-Legacy-*.jar
bungee/loader/build/libs/LuckPerms-Bungee-*.jar
sponge/loader/build/libs/LuckPerms-Sponge-*.jar
nukkit/loader/build/libs/LuckPerms-Nukkit-*.jar
velocity/build/libs/LuckPerms-Velocity-*.jar
fabric/build/libs/LuckPerms-Fabric-*.jar
forge/loader/build/libs/LuckPerms-Forge-*.jar
standalone/loader/build/libs/LuckPerms-Standalone-*.jar
- name: Upload standalone artifact
uses: actions/upload-artifact@v3
with:
name: standalone-binary
path: standalone/loader/build/libs/LuckPerms-Standalone-*.jar
build-docker:
needs: build-gradle
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Retrieve saved standalone jar artifact
uses: actions/download-artifact@v3
with:
name: standalone-binary
path: standalone/docker/
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Log in to the Container registry
uses: docker/login-action@v1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Determine the LuckPerms version
run: |
echo "luckperms_version=$(git describe --tags | awk -F "-" '{print $1 "." $2}')" >> "$GITHUB_ENV"
- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v3
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
flavor: |
latest=${{ github.ref == 'refs/heads/master' }}
tags: |
type=schedule
type=ref,event=branch
type=ref,event=tag
type=ref,event=pr
type=raw,enable=${{ github.ref == 'refs/heads/master' }},value=${{ env.luckperms_version }}
- name: Build and push Docker image
uses: docker/build-push-action@v2
with:
context: standalone/docker/
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+26
View File
@@ -0,0 +1,26 @@
### :speech_balloon: Looking for support?
Details about support for the project can be found [here, on the Wiki Homepage](https://luckperms.net/wiki/Home#support).
### :bug: Reporting bugs?
Before reporting a bug or issue, please make sure that the issue is actually being caused by or related to LuckPerms. We get a lot of reports which are caused by other software - please double check!
If you're unsure, feel free to ask using the above resources BEFORE making a report.
Bugs or issues should be reported using the [GitHub Issues tab](https://github.com/LuckPerms/LuckPerms/issues).
### :pencil: Want to contribute code?
#### Pull Requests
If you make any changes or improvements to the plugin which you think would be beneficial to others, please consider making a pull request to merge your changes back into the upstream project. (especially if your changes are bug fixes!)
LuckPerms loosely follows the [Google Java Style Guide](https://google.github.io/styleguide/javaguide.html). Generally, try to copy the style of code found in the class you're editing.
If you're considering submitting a substantial pull request, please open an issue so we can discuss the change before starting work on the contribution. Most pull requests are happily accepted, but larger changes may have an impact on the maintainability of the project, and require more consideration.
#### Project Layout
The project is split up into a few separate modules.
* **API** - The public, semantically versioned API used by other plugins wishing to integrate with and retrieve data from LuckPerms. This module (for the most part) does not contain any implementation itself, and is provided by the plugin.
* **Common** - The common module contains most of the code which implements the respective LuckPerms plugins. This abstract module reduces duplicated code throughout the project.
* **Bukkit, BungeeCord, Fabric, Forge, Nukkit, Sponge & Velocity** - Each use the common module to implement plugins on the respective server platforms.
+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
+119
View File
@@ -0,0 +1,119 @@
group = 'net.luckperms'
project.version = '5.4'
jar {
manifest {
attributes('Automatic-Module-Name': 'net.luckperms.api')
}
}
dependencies {
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.
/*
if (project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePassword')) {
apply plugin: 'signing'
javadoc {
title = 'LuckPerms API (v' + project.ext.apiVersion + ')'
options.overview = 'javadoc/overview.html'
options.encoding = 'UTF-8'
options.charSet = 'UTF-8'
options.links(
'https://checkerframework.org/api/',
'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')
if (JavaVersion.current() > JavaVersion.VERSION_1_8) {
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/')
}
}
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 {
repositories {
maven {
def releasesRepoUrl = 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
def snapshotsRepoUrl = 'https://oss.sonatype.org/content/repositories/snapshots/'
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
credentials {
username sonatypeUsername
password sonatypePassword
}
}
}
publications {
mavenJava(MavenPublication) {
artifactId = 'api'
from components.java
artifact sourcesJar
artifact javadocJar
pom {
name = 'LuckPerms API'
description = 'A permissions plugin for Minecraft servers.'
url = 'https://luckperms.net'
licenses {
license {
name = 'MIT'
url = 'https://opensource.org/licenses/MIT'
}
}
developers {
developer {
id = 'lucko'
name = 'Luck'
url = 'https://lucko.me'
email = 'git@lucko.me'
}
}
scm {
connection = 'scm:git:https://github.com/LuckPerms/LuckPerms.git'
developerConnection = 'scm:git:git@github.com:LuckPerms/LuckPerms.git'
url = 'https://github.com/LuckPerms/LuckPerms'
}
issueManagement {
system = 'GitHub'
url = 'https://github.com/LuckPerms/LuckPerms/issues'
}
}
}
}
}
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);
}
@@ -1,56 +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.messenger.message.type;
import net.luckperms.api.messenger.message.Message;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Represents a "custom payload" message.
*
* <p>Used by API consumers to send custom messages between servers.</p>
*
* @see net.luckperms.api.messaging.MessagingService#sendCustomMessage(String, String)
* @see net.luckperms.api.event.messaging.CustomMessageReceiveEvent
* @since 5.5
*/
public interface CustomMessage extends Message {
/**
* Gets the channel identifier.
*
* @return the namespace
*/
@NonNull String getChannelId();
/**
* Gets the payload.
*
* @return the payload
*/
@NonNull String getPayload();
}
@@ -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);
@@ -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();
}
+73
View File
@@ -0,0 +1,73 @@
plugins {
alias(libs.plugins.licenser) apply false
}
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
defaultTasks 'licenseFormat', 'build'
subprojects {
apply plugin: 'java'
apply plugin: 'maven-publish'
apply plugin: 'org.cadixdev.licenser'
group = 'me.lucko.luckperms'
version = '5.4-SNAPSHOT'
sourceCompatibility = 1.8
targetCompatibility = 1.8
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8'
}
tasks.withType(Test).configureEach {
testLogging {
events = [TestLogEvent.PASSED, TestLogEvent.FAILED, TestLogEvent.SKIPPED]
exceptionFormat = TestExceptionFormat.FULL
showExceptions = true
showCauses = true
showStackTraces = true
}
}
jar {
from '../LICENSE.txt'
}
def determinePatchVersion = {
// get the name of the last tag
def tagInfo = new ByteArrayOutputStream()
exec {
commandLine 'git', 'describe', '--tags'
standardOutput = tagInfo
}
tagInfo = tagInfo.toString()
if (!tagInfo.contains('-')) {
return 0
}
return tagInfo.split("-")[1]
}
project.ext.majorVersion = '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
license {
header = rootProject.file('HEADER.txt')
include '**/*.java'
newLine = true
}
repositories {
mavenCentral()
maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' }
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
maven { url 'https://repo.lucko.me/' }
maven { url 'https://libraries.minecraft.net/' }
}
}
-348
View File
@@ -1,348 +0,0 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.gradle.kotlin.dsl.provideDelegate
/* ------------------------------ Plugins ------------------------------ */
plugins {
id("java") // Import Java plugin.
id("java-library") // Import Java Library plugin.
id("com.diffplug.spotless") version "7.0.4" // Import Spotless plugin.
id("com.gradleup.shadow") version "8.3.8" // Import Shadow plugin.
id("checkstyle") // Import Checkstyle plugin.
eclipse // Import Eclipse plugin.
kotlin("jvm") version "2.1.21" // Import Kotlin JVM plugin.
}
extra["kotlinAttribute"] = Attribute.of("kotlin-tag", Boolean::class.javaObjectType)
val kotlinAttribute: Attribute<Boolean> by rootProject.extra
/* --------------------------- JDK / Kotlin ---------------------------- */
java {
sourceCompatibility = JavaVersion.VERSION_17 // Compile with JDK 17 compatibility.
toolchain { // Select Java toolchain.
languageVersion.set(JavaLanguageVersion.of(17)) // Use JDK 17.
vendor.set(JvmVendorSpec.GRAAL_VM) // Use GraalVM CE.
}
}
kotlin { jvmToolchain(17) }
/* ----------------------------- Metadata ------------------------------ */
group = "me.lucko.luckperms" // Declare bundle identifier.
version = "5.5.0-SNAPSHOT" // Declare plugin version (will be in .jar).
val apiVersion = "1.19" // Declare minecraft server target version.
/* ----------------------------- Resources ----------------------------- */
tasks.named<ProcessResources>("processResources") {
val props = mapOf("version" to version, "apiVersion" to apiVersion)
inputs.properties(props) // Indicates to rerun if version changes.
filesMatching("plugin.yml") { expand(props) }
from("LICENSE") { into("/") } // Bundle licenses into jarfiles.
}
/* ---------------------------- Repos ---------------------------------- */
repositories {
// Fix issue with lwjgl-freetype not being found on macOS / ForgeGradle issue
maven("https://libraries.minecraft.net") { content { includeModule("org.lwjgl", "lwjgl-freetype") } }
mavenCentral()
maven { url = uri("https://repo.lucko.me/") }
maven { url = uri("https://libraries.minecraft.net/") }
maven { url = uri("https://repo.papermc.io/repository/maven-public/") }
maven { url = uri("file://${System.getProperty("user.home")}/.m2/repository") }
System.getProperty("SELF_MAVEN_LOCAL_REPO")?.let {
val dir = file(it)
if (dir.isDirectory) {
println("Using SELF_MAVEN_LOCAL_REPO at: $it")
maven { url = uri("file://${dir.absolutePath}") }
} else {
mavenLocal()
}
}
}
/* ---------------------- Java project deps ---------------------------- */
dependencies {
// Root project carries no direct compile deps; subprojects declare their own.
}
/* ---------------------- Reproducible jars ---------------------------- */
tasks.withType<AbstractArchiveTask>().configureEach { // Ensure reproducible .jars
isPreserveFileTimestamps = false
isReproducibleFileOrder = true
}
/* ----------------------------- Shadow -------------------------------- */
tasks.shadowJar {
archiveClassifier.set("") // Use empty string instead of null.
minimize()
}
tasks.jar { archiveClassifier.set("part") } // Applies to root jarfile only.
tasks.build { dependsOn(tasks.spotlessApply) } // Build depends on spotless and shadow.
/* --------------------------- Javac opts ------------------------------- */
tasks.withType<JavaCompile>().configureEach {
options.compilerArgs.add("-parameters") // Enable reflection for java code.
options.isFork = true // Run javac in its own process.
options.compilerArgs.add("-Xlint:deprecation") // Trigger deprecation warning messages.
options.encoding = "UTF-8" // Use UTF-8 file encoding.
}
/* ----------------------------- Auto Formatting ------------------------ */
spotless {
java {
eclipse().configFile("config/formatter/eclipse-java-formatter.xml") // Eclipse java formatting.
leadingTabsToSpaces() // Convert leftover leading tabs to spaces.
removeUnusedImports() // Remove imports that aren't being called.
}
kotlinGradle {
ktfmt().kotlinlangStyle().configure { it.setMaxWidth(120) } // JetBrains Kotlin formatting.
target("build.gradle.kts", "settings.gradle.kts") // Gradle files to format.
}
}
checkstyle {
toolVersion = "10.18.1" // Declare checkstyle version to use.
configFile = file("config/checkstyle/checkstyle.xml") // Point checkstyle to config file.
isIgnoreFailures = true // Don't fail the build if checkstyle does not pass.
isShowViolations = true // Show the violations in any IDE with the checkstyle plugin.
}
tasks.named("compileJava") {
dependsOn("spotlessApply") // Run spotless before compiling with the JDK.
}
tasks.named("spotlessCheck") {
dependsOn("spotlessApply") // Run spotless before checking if spotless ran.
}
/* --------------------------- LuckPerms modules ------------------------ */
subprojects {
apply(plugin = "java-library")
apply(plugin = "eclipse")
group = "me.lucko.luckperms"
version = rootProject.version
repositories {
maven("https://libraries.minecraft.net") { content { includeModule("org.lwjgl", "lwjgl-freetype") } }
mavenCentral()
maven { url = uri("https://repo.lucko.me/") }
maven { url = uri("https://libraries.minecraft.net/") }
maven { url = uri("https://repo.papermc.io/repository/maven-public/") }
maven { url = uri("file://${System.getProperty("user.home")}/.m2/repository") }
System.getProperty("SELF_MAVEN_LOCAL_REPO")?.let {
val dir = file(it)
if (dir.isDirectory) {
println("Using SELF_MAVEN_LOCAL_REPO at: $it")
maven { url = uri("file://${dir.absolutePath}") }
} else {
mavenLocal()
}
}
}
tasks.withType<Test>().configureEach {
testLogging {
events("PASSED", "FAILED", "SKIPPED")
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
showExceptions = true
showCauses = true
showStackTraces = true
}
useJUnitPlatform()
}
tasks.withType<JavaCompile>().configureEach { options.encoding = "UTF-8" }
tasks.withType<Jar>().configureEach { from(rootProject.file("LICENSE.txt")) }
}
/* :api */
project(":api") {
dependencies {
compileOnly("org.checkerframework:checker-qual:3.49.3")
compileOnly("org.jetbrains:annotations:26.0.2")
}
tasks.jar { manifest { attributes(mapOf("Automatic-Module-Name" to "net.luckperms.api")) } }
}
/* :common */
project(":common") {
apply(plugin = "jacoco")
val patchedDir = layout.buildDirectory.dir("patchedSrc/commonMain/java")
val patchCommonForJ17 =
tasks.register<Copy>("patchCommonForJ17") {
from("src/main/java")
into(patchedDir)
filteringCharset = "UTF-8"
filesMatching("**/*.java") {
filter { line ->
if (
line.contains(".reversed()") &&
!line.contains("Comparator") &&
!line.contains("comparing") &&
!line.contains("thenComparing") &&
!line.contains("INSTANCE") &&
!line.contains("normal()")
) {
line.replace(
".reversed()",
".stream().collect(java.util.stream.Collectors.collectingAndThen(" +
"java.util.stream.Collectors.toList(), l -> { java.util.Collections.reverse(l); return l; }))",
)
} else line
}
}
}
sourceSets.named("main") {
java.setSrcDirs(emptyList<Any>())
java.srcDir(patchedDir)
}
tasks.named<JavaCompile>("compileJava") { dependsOn(patchCommonForJ17) }
dependencies {
testImplementation("org.slf4j:slf4j-simple:1.7.36")
testImplementation("org.junit.jupiter:junit-jupiter:5.13.0")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testImplementation("org.testcontainers:junit-jupiter:1.21.1")
testImplementation("org.mockito:mockito-core:5.18.0")
testImplementation("org.mockito:mockito-junit-jupiter:5.18.0")
testImplementation("com.h2database:h2:2.1.214")
testImplementation("org.mongodb:mongodb-driver-legacy:5.5.0")
testImplementation("org.spongepowered:configurate-yaml:3.7.3")
testImplementation("org.spongepowered:configurate-hocon:3.7.3")
testImplementation("me.lucko.configurate:configurate-toml:3.7")
testImplementation("net.luckperms:rest-api-java-client:0.1")
api(project(":api"))
api("org.checkerframework:checker-qual:3.12.0")
compileOnly(project(":common:loader-utils"))
compileOnly("com.mojang:brigadier:1.0.18")
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") {
exclude(module = "adventure-bom")
exclude(module = "checker-qual")
exclude(module = "annotations")
}
api("net.kyori:adventure-text-serializer-gson:4.21.0") {
exclude(module = "adventure-bom")
exclude(module = "adventure-api")
exclude(module = "gson")
}
api("net.kyori:adventure-text-serializer-legacy:4.21.0") {
exclude(module = "adventure-bom")
exclude(module = "adventure-api")
}
api("net.kyori:adventure-text-serializer-plain:4.21.0") {
exclude(module = "adventure-bom")
exclude(module = "adventure-api")
}
api("net.kyori:adventure-text-minimessage:4.21.0") {
exclude(module = "adventure-bom")
exclude(module = "adventure-api")
}
api("net.kyori:event-api:3.0.0") {
exclude(module = "checker-qual")
exclude(module = "guava")
}
api("com.google.code.gson:gson:2.7")
api("com.google.guava:guava:19.0")
api("com.github.ben-manes.caffeine:caffeine:3.2.0")
api("com.squareup.okhttp3:okhttp:3.14.9")
api("com.squareup.okio:okio:1.17.6")
api("net.bytebuddy:byte-buddy:1.15.11")
api("org.spongepowered:configurate-core:3.7.3") { isTransitive = false }
api("org.spongepowered:configurate-yaml:3.7.3") { isTransitive = false }
api("org.spongepowered:configurate-gson:3.7.3") { isTransitive = false }
api("org.spongepowered:configurate-hocon:3.7.3") { isTransitive = false }
api("me.lucko.configurate:configurate-toml:3.7") { isTransitive = false }
compileOnly("com.zaxxer:HikariCP:6.3.0")
compileOnly("redis.clients:jedis:5.2.0")
compileOnly("io.nats:jnats:2.21.1")
compileOnly("com.rabbitmq:amqp-client:5.25.0")
compileOnly("org.mongodb:mongodb-driver-legacy:5.5.0")
compileOnly("org.postgresql:postgresql:42.7.6")
compileOnly("org.yaml:snakeyaml:1.33")
compileOnly("net.luckperms:rest-api-java-client:0.1")
}
}
/* :common:loader-utils */
project(":common:loader-utils") {
// nothing special
}
/* :bukkit */
project(":bukkit") {
apply(plugin = "com.gradleup.shadow")
dependencies {
implementation(project(":common"))
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") {
exclude(module = "adventure-bom")
exclude(module = "adventure-api")
exclude(module = "adventure-nbt")
}
compileOnly("me.lucko:commodore:2.0")
compileOnly("net.milkbowl.vault:VaultAPI:1.7") { exclude(module = "bukkit") }
compileOnly("lilypad.client.connect:api:0.0.1-SNAPSHOT")
}
tasks.named<ShadowJar>("shadowJar") {
archiveFileName.set("luckperms-bukkit.jarinjar")
dependencies { include(dependency("me.lucko.luckperms:.*")) }
relocate("net.kyori.adventure", "me.lucko.luckperms.lib.adventure")
relocate("net.kyori.event", "me.lucko.luckperms.lib.eventbus")
relocate("com.github.benmanes.caffeine", "me.lucko.luckperms.lib.caffeine")
relocate("okio", "me.lucko.luckperms.lib.okio")
relocate("okhttp3", "me.lucko.luckperms.lib.okhttp3")
relocate("net.bytebuddy", "me.lucko.luckperms.lib.bytebuddy")
relocate("me.lucko.commodore", "me.lucko.luckperms.lib.commodore")
relocate("org.mariadb.jdbc", "me.lucko.luckperms.lib.mariadb")
relocate("com.mysql", "me.lucko.luckperms.lib.mysql")
relocate("org.postgresql", "me.lucko.luckperms.lib.postgresql")
relocate("com.zaxxer.hikari", "me.lucko.luckperms.lib.hikari")
relocate("com.mongodb", "me.lucko.luckperms.lib.mongodb")
relocate("org.bson", "me.lucko.luckperms.lib.bson")
relocate("redis.clients.jedis", "me.lucko.luckperms.lib.jedis")
relocate("io.nats.client", "me.lucko.luckperms.lib.nats")
relocate("com.rabbitmq", "me.lucko.luckperms.lib.rabbitmq")
relocate("org.apache.commons.pool2", "me.lucko.luckperms.lib.commonspool2")
relocate("ninja.leaping.configurate", "me.lucko.luckperms.lib.configurate")
}
}
/* :bukkit:loader */
project(":bukkit:loader") {
apply(plugin = "com.gradleup.shadow")
dependencies {
compileOnly("com.destroystokyo.paper:paper-api:1.15.2-R0.1-SNAPSHOT")
implementation(project(":api"))
implementation(project(":common:loader-utils"))
}
tasks.processResources {
filesMatching("plugin.yml") { expand(mapOf("pluginVersion" to rootProject.version.toString())) }
}
tasks.named<ShadowJar>("shadowJar") {
archiveFileName.set("LuckPerms-${rootProject.version}.jar")
from(project(":bukkit").tasks.named<ShadowJar>("shadowJar").flatMap { it.archiveFile })
manifest { attributes(mapOf("paperweight-mappings-namespace" to "mojang")) }
destinationDirectory.set(rootProject.layout.buildDirectory.dir("libs"))
}
}
/* Build root produces the server jar */
tasks.named("build") { dependsOn(project(":bukkit:loader").tasks.named("shadowJar")) }
+45
View File
@@ -0,0 +1,45 @@
plugins {
alias(libs.plugins.shadow)
}
dependencies {
implementation project(':bukkit')
implementation 'com.google.code.gson:gson:2.7'
implementation 'com.google.guava:guava:19.0'
}
shadowJar {
archiveFileName = 'luckperms-bukkitlegacy.jarinjar'
dependencies {
include(dependency('me.lucko.luckperms:.*'))
include(dependency('com.google.guava:guava:.*'))
include(dependency('com.google.code.gson:gson:.*'))
}
relocate 'com.google.common', 'me.lucko.luckperms.lib.guava'
relocate 'com.google.gson', 'me.lucko.luckperms.lib.gson'
relocate 'net.kyori.adventure', 'me.lucko.luckperms.lib.adventure'
relocate 'net.kyori.event', 'me.lucko.luckperms.lib.eventbus'
relocate 'com.github.benmanes.caffeine', 'me.lucko.luckperms.lib.caffeine'
relocate 'okio', 'me.lucko.luckperms.lib.okio'
relocate 'okhttp3', 'me.lucko.luckperms.lib.okhttp3'
relocate 'net.bytebuddy', 'me.lucko.luckperms.lib.bytebuddy'
relocate 'me.lucko.commodore', 'me.lucko.luckperms.lib.commodore'
relocate 'org.mariadb.jdbc', 'me.lucko.luckperms.lib.mariadb'
relocate 'com.mysql', 'me.lucko.luckperms.lib.mysql'
relocate 'org.postgresql', 'me.lucko.luckperms.lib.postgresql'
relocate 'com.zaxxer.hikari', 'me.lucko.luckperms.lib.hikari'
relocate 'com.mongodb', 'me.lucko.luckperms.lib.mongodb'
relocate 'org.bson', 'me.lucko.luckperms.lib.bson'
relocate 'redis.clients.jedis', 'me.lucko.luckperms.lib.jedis'
relocate 'io.nats.client', 'me.lucko.luckperms.lib.nats'
relocate 'com.rabbitmq', 'me.lucko.luckperms.lib.rabbitmq'
relocate 'org.apache.commons.pool2', 'me.lucko.luckperms.lib.commonspool2'
relocate 'ninja.leaping.configurate', 'me.lucko.luckperms.lib.configurate'
}
artifacts {
archives shadowJar
}
+32
View File
@@ -0,0 +1,32 @@
plugins {
alias(libs.plugins.shadow)
}
repositories {
maven { url 'https://repo.papermc.io/repository/maven-public/' }
}
dependencies {
compileOnly 'com.destroystokyo.paper:paper-api:1.15.2-R0.1-SNAPSHOT'
implementation project(':api')
implementation project(':common:loader-utils')
}
processResources {
filesMatching('plugin.yml') {
expand 'pluginVersion': project.ext.fullVersion
}
}
shadowJar {
archiveFileName = "LuckPerms-Bukkit-Legacy-${project.ext.fullVersion}.jar"
from {
project(':bukkit-legacy').tasks.shadowJar.archiveFile
}
}
artifacts {
archives shadowJar
}
+55
View File
@@ -0,0 +1,55 @@
plugins {
alias(libs.plugins.shadow)
}
repositories {
maven { url 'https://repo.papermc.io/repository/maven-public/' }
}
dependencies {
implementation project(':common')
compileOnly project(':common:loader-utils')
compileOnly 'com.destroystokyo.paper:paper-api:1.15.2-R0.1-SNAPSHOT'
compileOnly('net.kyori:adventure-platform-bukkit:4.1.0') {
exclude(module: 'adventure-bom')
exclude(module: 'adventure-api')
exclude(module: 'adventure-nbt')
}
compileOnly 'me.lucko:commodore:2.0'
compileOnly('net.milkbowl.vault:VaultAPI:1.7') {
exclude(module: 'bukkit')
}
compileOnly 'lilypad.client.connect:api:0.0.1-SNAPSHOT'
}
shadowJar {
archiveFileName = 'luckperms-bukkit.jarinjar'
dependencies {
include(dependency('me.lucko.luckperms:.*'))
}
relocate 'net.kyori.adventure', 'me.lucko.luckperms.lib.adventure'
relocate 'net.kyori.event', 'me.lucko.luckperms.lib.eventbus'
relocate 'com.github.benmanes.caffeine', 'me.lucko.luckperms.lib.caffeine'
relocate 'okio', 'me.lucko.luckperms.lib.okio'
relocate 'okhttp3', 'me.lucko.luckperms.lib.okhttp3'
relocate 'net.bytebuddy', 'me.lucko.luckperms.lib.bytebuddy'
relocate 'me.lucko.commodore', 'me.lucko.luckperms.lib.commodore'
relocate 'org.mariadb.jdbc', 'me.lucko.luckperms.lib.mariadb'
relocate 'com.mysql', 'me.lucko.luckperms.lib.mysql'
relocate 'org.postgresql', 'me.lucko.luckperms.lib.postgresql'
relocate 'com.zaxxer.hikari', 'me.lucko.luckperms.lib.hikari'
relocate 'com.mongodb', 'me.lucko.luckperms.lib.mongodb'
relocate 'org.bson', 'me.lucko.luckperms.lib.bson'
relocate 'redis.clients.jedis', 'me.lucko.luckperms.lib.jedis'
relocate 'io.nats.client', 'me.lucko.luckperms.lib.nats'
relocate 'com.rabbitmq', 'me.lucko.luckperms.lib.rabbitmq'
relocate 'org.apache.commons.pool2', 'me.lucko.luckperms.lib.commonspool2'
relocate 'ninja.leaping.configurate', 'me.lucko.luckperms.lib.configurate'
}
artifacts {
archives shadowJar
}
+48
View File
@@ -0,0 +1,48 @@
plugins {
alias(libs.plugins.shadow)
}
configurations {
testJar
}
repositories {
maven { url 'https://repo.papermc.io/repository/maven-public/' }
}
dependencies {
compileOnly 'com.destroystokyo.paper:paper-api:1.15.2-R0.1-SNAPSHOT'
implementation project(':api')
implementation project(':common:loader-utils')
}
processResources {
filesMatching('plugin.yml') {
expand 'pluginVersion': project.ext.fullVersion
}
}
shadowJar {
archiveFileName = "LuckPerms-Bukkit-${project.ext.fullVersion}.jar"
from {
project(':bukkit').tasks.shadowJar.archiveFile
}
}
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
}
@@ -307,7 +307,7 @@ public class LPBukkitBootstrap implements LuckPermsBootstrap, LoaderBootstrap, B
try {
Class.forName("com.google.gson.JsonElement");
return false;
} catch (Exception e) {
} catch (ClassNotFoundException e) {
return true;
}
}
@@ -221,12 +221,10 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin {
* - https://hub.spigotmc.org/jira/browse/SPIGOT-5546
* - https://github.com/PaperMC/Paper/pull/3509
*/
if (getConfiguration().get(ConfigKeys.VAULT_ENABLED)) {
PluginManagerUtil.injectDependency(this.bootstrap.getServer().getPluginManager(), this.bootstrap.getLoader().getName(), "Vault");
PluginManagerUtil.injectDependency(this.bootstrap.getServer().getPluginManager(), this.bootstrap.getLoader().getName(), "Vault");
// Provide vault support
tryVaultHook(false);
}
// Provide vault support
tryVaultHook(false);
}
@Override
@@ -239,10 +237,6 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin {
return; // already hooked
}
if (!getConfiguration().get(ConfigKeys.VAULT_ENABLED)) {
return; // disabled in config
}
try {
if (force || this.bootstrap.getServer().getPluginManager().isPluginEnabled("Vault")) {
this.vaultHookManager = new VaultHookManager(this);
@@ -353,7 +347,7 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin {
try {
Class.forName(className);
return true;
} catch (Exception e) {
} catch (ClassNotFoundException e) {
return false;
}
}
@@ -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,15 +81,7 @@ public class BukkitPlatformListener implements Listener {
@EventHandler
public void onPluginEnable(PluginEnableEvent e) {
if (!this.plugin.getConfiguration().get(ConfigKeys.VAULT_ENABLED)) {
return;
}
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);
}
}
+19 -60
View File
@@ -41,7 +41,7 @@ server: global
# - To get around this, you can use a player's uuid directly in the command, or enable this option.
# - When this is set to 'true', the server facility is used. This may use a number of methods,
# including checking the servers local cache, or making a request to the Mojang API.
use-server-uuid-cache: true
use-server-uuid-cache: false
@@ -83,7 +83,7 @@ use-server-uuid-cache: true
#
# - A H2 database is the default option.
# - If you want to edit data manually in "traditional" storage files, we suggest using YAML.
storage-method: postgresql
storage-method: h2
# The following block defines the settings for remote database storage methods.
#
@@ -99,11 +99,11 @@ data:
# The name of the database to store LuckPerms data in.
# - This must be created already. Don't worry about this setting if you're using MongoDB.
database: luckperms
database: minecraft
# Credentials for the database.
username: '^{POSTGRES_USER}'
password: '^{POSTGRES_PASSWORD}'
username: root
password: ''
# These settings apply to the MySQL connection pool.
# - The default values will be suitable for the majority of users.
@@ -132,11 +132,11 @@ data:
# from being timed out by the database or network infrastructure, measured in milliseconds.
# - The value should be less than maximum-lifetime and greater than 30000 (30 seconds).
# - Setting the value to zero will disable the keepalive functionality.
keepalive-time: 120000
keepalive-time: 0
# This setting controls the maximum number of milliseconds that the plugin will wait for a
# connection from the pool, before timing out.
connection-timeout: 10000 # 10 seconds
connection-timeout: 5000 # 5 seconds
# This setting allows you to define extra properties for connections.
#
@@ -216,7 +216,7 @@ sync-minutes: -1
# - When using a file-based storage type, LuckPerms can monitor the data files for changes, and
# automatically update when changes are detected.
# - If you don't want this feature to be active, set this option to false.
watch-files: false
watch-files: true
# Define which messaging service should be used by the plugin.
#
@@ -258,16 +258,16 @@ push-log-entries: true
# - If you have LuckPerms installed on your backend servers as well as a BungeeCord proxy, you
# should set this option to false on either your backends or your proxies, to avoid players being
# messaged twice about log entries.
broadcast-received-log-entries: false
broadcast-received-log-entries: true
# Settings for Keydb.
# Settings for Redis.
# Port 6379 is used by default; set address to "host:port" if differs
# Multiple Keydb nodes can be specified in the same format as a string list under the name "addresses".
# Multiple Redis nodes can be specified in the same format as a string list under the name "addresses".
redis:
enabled: true
enabled: false
address: localhost
username: 'default'
password: 'bookchin'
username: ''
password: ''
# Settings for Nats.
# Port 4222 is used by default; set address to "host:port" if differs
@@ -335,7 +335,7 @@ argument-based-command-permissions: false
# If the plugin should check whether senders are a member of a given group before they're able to
# edit the groups data or add/remove other users to/from it.
# Note: these limitations do not apply to the web editor!
require-sender-group-membership-to-modify: true
require-sender-group-membership-to-modify: false
# If the plugin should send log notifications to users whenever permissions are modified.
#
@@ -408,14 +408,14 @@ meta-formatting:
duplicates: first-only
start-spacer: ""
middle-spacer: " "
end-spacer: " "
end-spacer: ""
suffix:
format:
- "highest"
duplicates: first-only
start-spacer: " "
start-spacer: ""
middle-spacer: " "
end-spacer: " "
end-spacer: ""
@@ -627,19 +627,8 @@ commands-allow-op: true
# +----------------------------------------------------------------------------------------------+ #
# | Vault integration settings | #
# | | #
# | Disabled by default (vault-enabled: false). The remaining vault-* settings below only take | #
# | effect when vault-enabled is set to true. | #
# +----------------------------------------------------------------------------------------------+ #
# If the Vault API bridge should be registered.
#
# - When false (default), LuckPerms will not hook into Vault at all. Vault-dependent plugins will
# fall back to SuperPerms and will not see LuckPerms groups, prefixes, or meta.
# - When true, LuckPerms will register Vault permission and chat providers if the Vault plugin is
# installed. Required for any plugin that reads groups/prefix/suffix via the Vault API.
vault-enabled: false
# If Vault lookups for offline players on the main server thread should be enabled.
#
# LuckPerms has a "catch" for plugins attempting to perform unsafe offline player data lookups
@@ -728,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: true
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: true
console: false
+47
View File
@@ -0,0 +1,47 @@
plugins {
alias(libs.plugins.shadow)
}
dependencies {
implementation project(':common')
compileOnly project(':common:loader-utils')
compileOnly 'net.md-5:bungeecord-api:1.16-R0.4'
compileOnly('net.kyori:adventure-platform-bungeecord:4.1.0') {
exclude(module: 'adventure-bom')
exclude(module: 'adventure-api')
exclude(module: 'adventure-nbt')
}
compileOnly 'com.imaginarycode.minecraft:RedisBungee:0.4'
}
shadowJar {
archiveFileName = 'luckperms-bungee.jarinjar'
dependencies {
include(dependency('me.lucko.luckperms:.*'))
}
relocate 'net.kyori.adventure', 'me.lucko.luckperms.lib.adventure'
relocate 'net.kyori.event', 'me.lucko.luckperms.lib.eventbus'
relocate 'com.github.benmanes.caffeine', 'me.lucko.luckperms.lib.caffeine'
relocate 'okio', 'me.lucko.luckperms.lib.okio'
relocate 'okhttp3', 'me.lucko.luckperms.lib.okhttp3'
relocate 'net.bytebuddy', 'me.lucko.luckperms.lib.bytebuddy'
relocate 'me.lucko.commodore', 'me.lucko.luckperms.lib.commodore'
relocate 'org.mariadb.jdbc', 'me.lucko.luckperms.lib.mariadb'
relocate 'com.mysql', 'me.lucko.luckperms.lib.mysql'
relocate 'org.postgresql', 'me.lucko.luckperms.lib.postgresql'
relocate 'com.zaxxer.hikari', 'me.lucko.luckperms.lib.hikari'
relocate 'com.mongodb', 'me.lucko.luckperms.lib.mongodb'
relocate 'org.bson', 'me.lucko.luckperms.lib.bson'
relocate 'redis.clients.jedis', 'me.lucko.luckperms.lib.jedis'
relocate 'io.nats.client', 'me.lucko.luckperms.lib.nats'
relocate 'com.rabbitmq', 'me.lucko.luckperms.lib.rabbitmq'
relocate 'org.apache.commons.pool2', 'me.lucko.luckperms.lib.commonspool2'
relocate 'ninja.leaping.configurate', 'me.lucko.luckperms.lib.configurate'
}
artifacts {
archives shadowJar
}
+28
View File
@@ -0,0 +1,28 @@
plugins {
alias(libs.plugins.shadow)
}
dependencies {
compileOnly 'net.md-5:bungeecord-api:1.15-SNAPSHOT'
implementation project(':api')
implementation project(':common:loader-utils')
}
processResources {
filesMatching('plugin.yml') {
expand 'pluginVersion': project.ext.fullVersion
}
}
shadowJar {
archiveFileName = "LuckPerms-Bungee-${project.ext.fullVersion}.jar"
from {
project(':bungee').tasks.shadowJar.archiveFile
}
}
artifacts {
archives shadowJar
}
@@ -302,7 +302,7 @@ public class LPBungeeBootstrap implements LuckPermsBootstrap, LoaderBootstrap, B
try {
Class.forName("com.google.gson.internal.bind.TreeTypeAdapter");
return false;
} catch (Exception e) {
} catch (ClassNotFoundException e) {
return true;
}
}
@@ -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
+93
View File
@@ -0,0 +1,93 @@
plugins {
id("java-library")
id("jacoco")
}
test {
useJUnitPlatform {}
}
jacocoTestReport {
dependsOn test
}
dependencies {
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.mockito:mockito-core:4.11.0'
testImplementation 'org.mockito:mockito-junit-jupiter:4.11.0'
testImplementation 'com.h2database:h2:2.1.214'
api project(':api')
api 'org.checkerframework:checker-qual:3.12.0'
compileOnly project(':common:loader-utils')
compileOnly 'com.mojang:brigadier:1.0.18'
compileOnly 'org.slf4j:slf4j-api:1.7.30'
compileOnly 'org.apache.logging.log4j:log4j-api:2.14.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.11.0') {
exclude(module: 'adventure-bom')
exclude(module: 'adventure-api')
exclude(module: 'gson')
}
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.11.0') {
exclude(module: 'adventure-bom')
exclude(module: 'adventure-api')
}
api("net.kyori:adventure-text-minimessage:4.11.0") {
exclude(module: 'adventure-bom')
exclude(module: 'adventure-api')
}
api('net.kyori:event-api:3.0.0') {
exclude(module: 'checker-qual')
exclude(module: 'guava')
}
api 'com.google.code.gson:gson:2.7'
api 'com.google.guava:guava:19.0'
api 'com.github.ben-manes.caffeine:caffeine:2.9.0'
api 'com.squareup.okhttp3:okhttp:3.14.9'
api 'com.squareup.okio:okio:1.17.5'
api 'net.bytebuddy:byte-buddy:1.10.22'
api('org.spongepowered:configurate-core:3.7.2') {
transitive = false
}
api('org.spongepowered:configurate-yaml:3.7.2') {
transitive = false
}
api('org.spongepowered:configurate-gson:3.7.2') {
transitive = false
}
api('org.spongepowered:configurate-hocon:3.7.2') {
transitive = false
}
api('me.lucko.configurate:configurate-toml:3.7') {
transitive = false
}
compileOnly 'com.zaxxer:HikariCP:4.0.3'
compileOnly 'redis.clients:jedis:4.4.3'
compileOnly 'io.nats:jnats:2.16.4'
compileOnly 'com.rabbitmq:amqp-client:5.12.0'
compileOnly 'org.mongodb:mongodb-driver-legacy:4.5.0'
compileOnly 'org.postgresql:postgresql:42.6.0'
compileOnly 'org.yaml:snakeyaml:1.28'
}
+1
View File
@@ -0,0 +1 @@
// nothing special to do here (yet)!
@@ -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;
@@ -155,20 +154,11 @@ public class CommandManager {
public CompletableFuture<Void> executeCommand(Sender sender, String label, List<String> args) {
UUID uniqueId = sender.getUniqueId();
if (this.plugin.getConfiguration().get(ConfigKeys.COMMANDS_RATE_LIMIT) && !sender.isConsole() && !Sender.CONSOLE_UUID.equals(uniqueId) && !this.playerRateLimit.add(uniqueId)) {
if (this.plugin.getConfiguration().get(ConfigKeys.COMMANDS_RATE_LIMIT) && !sender.isConsole() && !this.playerRateLimit.add(uniqueId)) {
this.plugin.getLogger().warn("Player '" + uniqueId + "' is spamming LuckPerms commands. Ignoring further inputs.");
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))
Loaded 100 of 365 files, more files were not shown because too many files have changed in this diff. Show more