Compare commits

...
Author SHA1 Message Date
Luck 3006e58209 Highest-n/lowest-n meta stack elements 2022-01-02 23:59:53 +00:00
Luck f61d9ff9f0 Fix NPE from permission holder initialisation order (#3263) 2022-01-02 16:58:15 +00:00
Luck a53c9fad00 Reuse the same InheritanceOrigin object where possible 2022-01-01 20:49:12 +00:00
Luck 4de7448394 Include whether a node is normal/transient in InheritanceOriginMetadata 2022-01-01 19:33:48 +00:00
Luck 6781c1fb51 Provide API access to cached data result info (#3243) 2022-01-01 14:38:46 +00:00
Luck acdc259771 Fix MetaValueSelector NPE 2021-12-31 17:50:42 +00:00
Luck d319d8dc52 Fix TristateResult NPE 2021-12-31 17:24:38 +00:00
Andrew 419d711fb6 Add full support for transient permissions through Vault API (#3258) 2021-12-31 16:16:06 +00:00
lucko 2d662cbab9 Retain cause node information for cached permission/meta data (#3250) 2021-12-31 16:03:00 +00:00
OmegaWeaponDev ae93c9768b Add default file name for export command (#3248) 2021-12-31 16:02:24 +00:00
Luck 664cad21a9 Clarify uuid type in user info command (#3233) 2021-12-10 23:47:06 +00:00
Luck 8cc126f711 Add config option to disable bulkupdate functionality 2021-12-09 21:32:02 +00:00
Luck 9c0874162b Fix cast to long in MongoStorage 2021-12-04 10:41:12 +00:00
Luck f35a9b6a09 Update dependencies for 1.18 2021-12-02 13:24:02 +00:00
Luck fa17404133 Fabric 1.18 2021-11-30 20:03:16 +00:00
Tadhg Boyle 64bd8b5eaf Add pagination to listgroups command (#3226) 2021-11-30 19:52:43 +00:00
Luck e227d96cbf Fix sql insert player query (#3225) 2021-11-28 10:46:48 +00:00
Luck 6e07d6ad36 Revert "Fix player insert duplicate key issue with PSQL (#3223)"
This reverts commit d6c810aa71.
2021-11-28 10:29:55 +00:00
Tadhg Boyle d6c810aa71 Fix player insert duplicate key issue with PSQL (#3223) 2021-11-27 22:48:22 +00:00
Luck 10f698b70f Add warning for common editor mistakes 2021-11-27 17:58:10 +00:00
Luck 594f50bee7 Add config option to disable context calculators 2021-11-27 09:49:01 +00:00
Luck 9204848ffb Fix username lookup returning "null" instead of null (#3220) 2021-11-27 09:43:47 +00:00
Luck 272d289d4a More context performance improvements (#3209) 2021-11-18 18:21:30 +00:00
Luck c9d4c7dac7 ContextSet performance improvements (#3209)
A bit experimental, should be stable enough though.
2021-11-17 23:18:11 +00:00
Eduard Wayland 848fc353d4 Added redis authentication with username (#3201) 2021-11-12 22:49:46 +00:00
Luck f896ed723d Edit issue templates (again) 2021-10-22 20:03:33 +01:00
Luck 00b62d7cd9 Edit issue templates 2021-10-22 20:01:46 +01:00
Larry f84f425817 Better issue templates (#3184) 2021-10-22 19:58:12 +01:00
Luck 9c5a43b487 Allow meta/prefix/suffix nodes to be negated to cancel inheritance (#3020) 2021-10-18 22:10:20 +01:00
Luck b2c76aca7d Improve translations handling (#3166) 2021-10-10 13:26:33 +01:00
RedstoneFuture 04bb035a83 Add warning nore about enable-ops to config files (#3170) 2021-10-03 18:55:38 +01:00
Luck 2cf77db8b6 TranslationManager fixes and improvements (#3165) 2021-09-18 17:30:17 +01:00
Luck d1cb644712 Fix empty prefix/suffix strings in YAML storage (#3139, #3160) 2021-09-10 19:14:47 +01:00
Luck 4e2807cff5 Refactor mod container retrieval in Fabric mod 2021-09-10 18:32:43 +01:00
Emily 5735e0ef44 Fix fabric/build.gradle placeholder replacement (#3153) 2021-09-04 22:15:13 +01:00
Luck 9cd33d26f2 Fix toLowerCase/toUpperCase locale sensitivity 2021-08-25 20:09:20 +01:00
Luck 062fca3f75 Fix Fabric version placeholder in metadata file (#3141) 2021-08-25 19:36:53 +01:00
Luck a182768cfa Change user housekeeper to be more lenient towards API usage (#3143) 2021-08-25 19:30:46 +01:00
Luck 93683c3a2f Update Fabric to 1.17, fix version placeholder filtering 2021-08-20 18:46:39 +01:00
Callum Seabrook ba522597a8 Update to Gradle 7 (#3051) 2021-08-20 18:16:39 +01:00
Luck b232cb94b4 ReflectionClassPathAppender on Java 16+ 2021-08-17 21:29:10 +01:00
Luck ee6aa47d99 Listen for ServerPostConnectEvent in Velocity context listener (#3133) 2021-08-14 09:08:14 +01:00
Luck b3ebf202fd Update adventure-platform for 1.17 (#3127) 2021-08-06 14:35:23 +01:00
Emily 0fa3095241 Save renamed group before syncing with storage (#3129) 2021-08-06 09:59:38 +01:00
Luck 433272518f Fix bad Continuation usage in Velocity listener 2021-07-31 08:29:32 +01:00
Luck 26de6d3a78 Update Velocity to 3.0.0, use a Continuation for permissions setup event 2021-07-30 13:07:54 +01:00
Luck 5ea2aee2f4 More helpful error message when user is null in API PlayerAdapter (#3104) 2021-07-18 20:38:06 +01:00
Luck 5a377e713e Fix NPE from null primary group in Vault hook (#3103) 2021-07-18 20:35:31 +01:00
Luck cfb298f69b Use different classifier for dependency files with Bukkit-Legacy remappings 2021-07-08 22:54:04 +01:00
Emily 563b5be9b2 Implement enable-ops and auto-op on Fabric (#3069) 2021-07-04 10:01:47 +01:00
Emily fb36bd0ced Bulk update triggers for group related commands (#3073) 2021-07-04 10:00:18 +01:00
Luck 979c80d9ee Clarify config comments for mongodb-connection-uri option (#3062) 2021-06-15 20:41:36 +01:00
Luck 4598435dd4 Fix extension loading causing whole plugin to error 2021-06-13 12:46:02 +01:00
Luck 51202924af Update commodore for MC 1.17 support 2021-06-12 17:57:43 +01:00
Jacob C 8ae70990ad Generify SqlStorage shutdown exception message (#3039) 2021-06-10 21:16:23 +01:00
Tjeu Kayim a63be102cf Implement command list updater for Fabric (#3060) 2021-06-10 21:15:33 +01:00
Tjeu Kayim 044deccd22 Fabric, fix GameMode.NOT_SET removed in 1.17 (#3053) 2021-06-10 21:12:03 +01:00
Luck ad49508b74 Some misc tidying up 2021-05-23 11:29:22 +01:00
Luck 05c9ca5951 Properly cleanup JarInJarClassLoader when plugin disables 2021-05-23 11:27:45 +01:00
Luck f5cdb98b01 Fix ExpiringSet not really working & causing memory leaks (#3027) 2021-05-19 12:31:48 +01:00
Luck 228687b741 Fix Nukkit PermissionAttachments always throwing NPEs 2021-05-10 17:39:08 +01:00
lucko 7ebbfcf0d2 Update banner in readme 2021-05-10 14:30:09 +01:00
Luck 49c17c4a18 Clarify message in API NotLoadedException 2021-05-06 13:38:49 +01:00
Luck 7c42b9aeca Revert "Add extra check to ensure RedisBungee is loaded (#2981)"
This reverts commit a68a5d91c4.
2021-05-06 13:25:23 +01:00
Luck 1511193e58 Add Bukkit child permissions to the PermissionRegistry 2021-05-06 12:23:09 +01:00
Luck 528d668bc6 API User#setPrimaryGroup should care about stored value only (#3008) 2021-05-03 23:10:57 +01:00
Luck 71896a75bf Warn users with multiple permissions plugins installed (#3006) 2021-05-02 16:14:23 +01:00
Luck 931d61e2ed Include more information about targets in verbose payloads 2021-05-02 15:50:38 +01:00
Luck b87b1c7123 Improve structure of LuckPermsSubscriptionMap 2021-05-02 15:26:44 +01:00
Luck fed5a0fd87 Fix potential race condition in LuckPermsProvider#get 2021-04-23 20:17:48 +01:00
Luck 9d4f278117 Remove redundant CommandResult enum 2021-04-22 21:01:57 +01:00
Luck 1d465aa800 Fix track editor command for empty tracks (£2997) 2021-04-22 20:32:43 +01:00
Luck 8741e8259f Fix commodore not working after dependency update 2021-04-18 18:42:57 +01:00
Luck 98adb046ee Update lots of dependencies 2021-04-18 18:20:54 +01:00
Luck a68a5d91c4 Add extra check to ensure RedisBungee is loaded (#2981) 2021-04-18 13:48:45 +01:00
Luck 0808392353 Update adventure 2021-04-18 13:33:22 +01:00
Luck 07f672ae1f Add 'skip-bulkupdate-confirmation' config option (#2980) 2021-04-18 11:55:06 +01:00
Federico López e219237e44 API Javadoc gen fixes (#2977) 2021-04-18 11:37:46 +01:00
Luck c533446ab0 Fabric fix compat with fake players
https://github.com/lucko/fabric-permissions-api/issues/3
2021-04-07 20:56:51 +01:00
Luck 82488140ee Parallelize user data load in web editor request (#2973) 2021-04-07 08:32:02 +01:00
Luck 327ccec02f Use the correct config file location on Fabric and Sponge (#2972) 2021-04-07 08:24:38 +01:00
Federico López 46efe0ef93 Give default group if needed on UserManager#modifyUser (#2971) 2021-04-07 08:11:19 +01:00
Luck c2231bcc2e Disallow empty permission node (#2966) 2021-04-05 18:30:12 +01:00
Luck d150bd39a5 Send messaging service ping for creategroup command (#2956) 2021-03-25 22:28:52 +00:00
Federico López f813122468 Fix 'meta settemp' ignoring temporary modifier (#2958) 2021-03-24 21:28:00 +00:00
Luck 2e62c98c45 Avoid blocking worker pool for command timeout check (#2951)
3rd time lucky...
2021-03-24 12:40:24 +00:00
Luck f0e1a8f092 Remove some BungeeCord/Velocity command aliases 2021-03-23 13:45:34 +00:00
Luck 2dc6902001 Fix deadlock when lots of commands are executed at the same time (#2951) 2021-03-23 11:10:18 +00:00
Luck 21f5c24847 Optimize Bukkit/Nukkit ChildProcessor refresh (#2952) 2021-03-20 18:17:17 +00:00
Luck 6b2c4bca58 Change command manager lock handling to avoid non-fair tryLock call 2021-03-20 15:28:45 +00:00
Luck 4564a97371 Update fabric-loop, explicitly set archivesBaseName to 'luckperms' (#2880) 2021-03-19 20:39:42 +00:00
Luck 1df66255e4 Fix RCON permission checking for LP commands on Bukkit (#2948) 2021-03-19 20:03:21 +00:00
Luck 1c7e2dfb8c Fix very lenient duration parsing (#2940) 2021-03-19 19:59:15 +00:00
Luck ad1ba3c126 Fix player locale retrieval on Bukkit 1.8 (#2930) 2021-03-19 19:33:18 +00:00
lucko 0f63b024e4 Update readme to clarify build output dirs 2021-03-15 12:17:02 +00:00
Daniil a17b441165 Don't rely on getPackage to determine generated class name (#2937) 2021-03-15 12:12:02 +00:00
Luck e3138eb94c Make console detection more resilient against spoofed UUIDs 2021-03-13 14:25:33 +00:00
Luck 53bb61f9b2 Allow server name to be specified by a system property 2021-03-13 14:16:02 +00:00
Luck 370e2aed0d Add some missing null annotations 2021-03-13 14:14:59 +00:00
411 changed files with 6706 additions and 3690 deletions

No files matched your search

-40
View File
@@ -1,40 +0,0 @@
---
name: 🐛 ​ Bug report
about: Report an issue or problem with the way LuckPerms is functioning.
title: ''
labels: 'type: issue'
assignees: ''
---
<!-- Before going any further, please check to see if your problem is fixed by updating to a newer version. The latest development builds can be obtained from https://luckperms.net/download -->
### Description
<!-- Please provide a short description of the issue in the space below. -->
### Reproduction steps
<!-- Please provide a clear list of steps we should follow in order to reproduce your issue in the space below. -->
1. Open the config.yml file and set example to true.
2. Restart the server
3. Run /lp user example permission set x from the console
4. See error
### Expected behaviour
<!-- Please provide a description of what you expected to happen. -->
### Environment details
<!-- Please provide details of your server environment in the space below. -->
* Server type/version: `ExampleSpigot` running version `1.12.2` build `???`
* LuckPerms version: `v???`
<!-- If you think it would be helpful, please also include a "pastebin" of any relevant config files or log output. You should use https://gist.github.com/ to upload, then include the link below. -->
### Any other relevant details
<!-- Please include any other relevant details in the space below. -->
+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
-16
View File
@@ -1,16 +0,0 @@
---
name: 💡 ​ Suggestion
about: Suggest an idea for an improvement or change to LuckPerms.
title: ''
labels: 'type: suggestion'
assignees: ''
---
### Description
<!-- Please provide a short description of the suggestion in the space below. -->
### Proposed behaviour
<!-- Please provide a short explanation of how the feature should work / be changed, and how this will effect the project. -->
+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
+2 -2
View File
@@ -1,4 +1,4 @@
![](https://i.imgur.com/ToguFkQ.png "Banner")
![](https://raw.githubusercontent.com/LuckPerms/branding/master/banner/banner.png "Banner")
# LuckPerms
[![Build Status](https://ci.lucko.me/job/LuckPerms/badge/icon)](https://ci.lucko.me/job/LuckPerms/)
[![javadoc](https://javadoc.io/badge2/net.luckperms/api/javadoc.svg)](https://javadoc.io/doc/net.luckperms/api)
@@ -34,7 +34,7 @@ cd LuckPerms/
./gradlew build
```
You can find the output jars in the `build/libs` directories.
You can find the output jars in the `loader/build/libs` or `build/libs` directories.
## Contributing
#### Pull Requests
+52 -39
View File
@@ -2,7 +2,7 @@ group = 'net.luckperms'
project.version = '5.3'
dependencies {
compileOnly 'org.checkerframework:checker-qual:3.8.0'
compileOnly 'org.checkerframework:checker-qual:3.12.0'
compileOnly 'org.jetbrains:annotations:20.1.0'
}
@@ -17,11 +17,17 @@ if (project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePass
options.encoding = 'UTF-8'
options.charSet = 'UTF-8'
options.links(
'https://docs.oracle.com/javase/8/docs/api/',
'https://checkerframework.org/api/',
'https://javadoc.io/static/org.jetbrains/annotations/20.1.0/'
)
options.addStringOption('Xdoclint:none', '-quiet')
if (JavaVersion.current() > JavaVersion.VERSION_1_8) {
options.addBooleanOption('-no-module-directories', true)
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) {
@@ -44,56 +50,63 @@ if (project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePass
sign configurations.archives
}
uploadArchives {
publishing {
repositories {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
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
repository(url: 'https://oss.sonatype.org/service/local/staging/deploy/maven2/') {
authentication(userName: sonatypeUsername, password: sonatypePassword)
credentials {
username sonatypeUsername
password sonatypePassword
}
}
}
snapshotRepository(url: 'https://oss.sonatype.org/content/repositories/snapshots/') {
authentication(userName: sonatypeUsername, password: sonatypePassword)
}
publications {
mavenJava(MavenPublication) {
artifactId = 'api'
from components.java
artifact sourcesJar
artifact javadocJar
pom {
project {
name 'LuckPerms API'
description 'A permissions plugin for Minecraft servers.'
url 'https://luckperms.net'
name 'LuckPerms API'
description 'A permissions plugin for Minecraft servers.'
url 'https://luckperms.net'
licenses {
license {
name 'MIT'
url 'https://opensource.org/licenses/MIT'
}
licenses {
license {
name 'MIT'
url 'https://opensource.org/licenses/MIT'
}
}
developers {
developer {
id 'lucko'
name 'Luck'
url 'https://lucko.me'
email 'git@lucko.me'
}
developers {
developer {
id 'lucko'
name 'Luck'
url 'https://lucko.me'
email 'git@lucko.me'
}
}
scm {
connection 'scm:git:https://github.com/lucko/LuckPerms.git'
developerConnection 'scm:git:git@github.com:lucko/LuckPerms.git'
url 'https://github.com/lucko/LuckPerms'
}
scm {
connection 'scm:git:https://github.com/lucko/LuckPerms.git'
developerConnection 'scm:git:git@github.com:lucko/LuckPerms.git'
url 'https://github.com/lucko/LuckPerms'
}
ciManagement {
system 'Jenkins'
url 'https://ci.lucko.me/job/LuckPerms'
}
ciManagement {
system 'Jenkins'
url 'https://ci.lucko.me/job/LuckPerms'
}
issueManagement {
system 'GitHub'
url 'https://github.com/lucko/LuckPerms/issues'
}
issueManagement {
system 'GitHub'
url 'https://github.com/lucko/LuckPerms/issues'
}
}
}
@@ -48,6 +48,7 @@ public final class LuckPermsProvider {
* @throws IllegalStateException if the API is not loaded yet
*/
public static @NonNull LuckPerms get() {
LuckPerms instance = LuckPermsProvider.instance;
if (instance == null) {
throw new NotLoadedException();
}
@@ -76,9 +77,9 @@ public final class LuckPermsProvider {
private static final String MESSAGE = "The LuckPerms API isn't loaded yet!\n" +
"This could be because:\n" +
" a) the LuckPerms plugin is not installed or it failed to enable\n" +
" b) your plugin does not declare a dependency on LuckPerms\n" +
" c) you are attempting to use the API before plugins reach the 'enable' phase\n" +
" (call the #get method in onEnable, not in your plugin constructor!)\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";
NotLoadedException() {
super(MESSAGE);
@@ -170,7 +170,7 @@ public interface Action extends Comparable<Action> {
* @param type the type
* @return the builder
*/
@NonNull Builder targetType(Action.Target.Type type);
@NonNull Builder targetType(Target.@NonNull Type type);
/**
* Sets the acted object for the entry.
@@ -31,6 +31,10 @@ import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Holds cached lookup data for a given set of query options.
*
* <p>All calls will account for inheritance, as well as any default data
* provided by the platform. These calls are heavily cached and are therefore
* fast.</p>
*/
public interface CachedData {
@@ -39,7 +39,7 @@ import java.util.concurrent.CompletableFuture;
* Holds cached permission and meta lookup data for a {@link PermissionHolder}.
*
* <p>All calls will account for inheritance, as well as any default data
* provided by the platform. This calls are heavily cached and are therefore
* provided by the platform. These calls are heavily cached and are therefore
* fast.</p>
*/
public interface CachedDataManager {
@@ -81,11 +81,10 @@ public interface CachedDataManager {
* <p>For {@link User}s, the most appropriate query options will be their
* {@link ContextManager#getQueryOptions(User) current active query options} if the
* corresponding player is online, and otherwise, will fallback to
* {@link ContextManager#getStaticQueryOptions() the current static query options}
* if they are offline.</p>
* {@link ContextManager#getStaticQueryOptions() the current static query options}.</p>
*
* <p>For {@link Group}s, the most appropriate query options will always be
* {@link ContextManager#getStaticQueryOptions()} the current static query options.</p>
* {@link ContextManager#getStaticQueryOptions() the current static query options}.</p>
*
* @return a permission data instance
* @since 5.1
@@ -99,11 +98,10 @@ public interface CachedDataManager {
* <p>For {@link User}s, the most appropriate query options will be their
* {@link ContextManager#getQueryOptions(User) current active query options} if the
* corresponding player is online, and otherwise, will fallback to
* {@link ContextManager#getStaticQueryOptions() the current static query options}
* if they are offline.</p>
* {@link ContextManager#getStaticQueryOptions() the current static query options}.</p>
*
* <p>For {@link Group}s, the most appropriate query options will always be
* {@link ContextManager#getStaticQueryOptions()} the current static query options.</p>
* {@link ContextManager#getStaticQueryOptions() the current static query options}.</p>
*
* @return a meta data instance
* @since 5.1
@@ -118,7 +116,7 @@ public interface CachedDataManager {
void invalidate();
/**
* Invalidates all of the underlying Permission calculators.
* Invalidates all underlying permission calculators.
*
* <p>Can be called to allow for an update in defaults.</p>
*/
@@ -26,6 +26,9 @@
package net.luckperms.api.cacheddata;
import net.luckperms.api.metastacking.MetaStackDefinition;
import net.luckperms.api.node.types.MetaNode;
import net.luckperms.api.node.types.PrefixNode;
import net.luckperms.api.node.types.SuffixNode;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -39,16 +42,40 @@ import java.util.function.Function;
/**
* Holds cached meta lookup data for a specific set of contexts.
*
* <p>Meta data refers to {@link PrefixNode prefixes}, {@link SuffixNode suffixes} and
* {@link MetaNode meta (options)} held by a permission holder.</p>
*
* <p>All calls will account for inheritance, as well as any default data
* provided by the platform. These calls are heavily cached and are therefore
* fast.</p>
*/
public interface CachedMetaData extends CachedData {
/**
* Query a meta value for the given {@code key}.
*
* <p>This method will always return a {@link Result}, but the
* {@link Result#result() inner result} {@link String} will be null if a value
* for the given key was not found.</p>
*
* @param key the key
* @return a result containing the value
* @since 5.4
*/
@NonNull Result<String, MetaNode> queryMetaValue(@NonNull String key);
/**
* Gets a value for the given meta key.
*
* <p>If no such meta value exists for the given key, {@code null} is returned.</p>
*
* @param key the key
* @return the value
*/
@Nullable String getMetaValue(@NonNull String key);
default @Nullable String getMetaValue(@NonNull String key) {
return queryMetaValue(key).result();
}
/**
* Gets a value for the given meta key, and runs it through the given {@code transformer}.
@@ -81,37 +108,97 @@ public interface CachedMetaData extends CachedData {
}
/**
* Gets the holder's highest priority prefix, or null if the holder has no prefixes
* Query for a prefix.
*
* <p>This method uses the rules defined by the {@link #getPrefixStackDefinition() prefix stack}
* to produce a {@link String} output.</p>
*
* <p>Assuming the default configuration is used, this will usually be the value of the
* holder's highest priority prefix node.</p>
*
* <p>This method will always return a {@link Result}, but the
* {@link Result#result() inner result} {@link String} will be null if
* a the resultant prefix stack contained no elements.</p>
*
* @return a result containing the prefix
* @since 5.4
*/
@NonNull Result<String, PrefixNode> queryPrefix();
/**
* Gets the prefix.
*
* <p>This method uses the rules defined by the {@link #getPrefixStackDefinition() prefix stack}
* to produce a {@link String} output.</p>
*
* <p>Assuming the default configuration is used, this will usually be the value of the
* holder's highest priority prefix node.</p>
*
* <p>If the resultant prefix stack contained no elements, {@code null} is returned.</p>
*
* @return a prefix string, or null
*/
@Nullable String getPrefix();
default @Nullable String getPrefix() {
return queryPrefix().result();
}
/**
* Gets the holder's highest priority suffix, or null if the holder has no suffixes
* Query for a suffix.
*
* <p>This method uses the rules defined by the {@link #getSuffixStackDefinition() suffix stack}
* to produce a {@link String} output.</p>
*
* <p>Assuming the default configuration is used, this will usually be the value of the
* holder's highest priority suffix node.</p>
*
* <p>This method will always return a {@link Result}, but the
* {@link Result#result() inner result} {@link String} will be null if
* a the resultant suffix stack contained no elements.</p>
*
* @return a result containing the suffix
* @since 5.4
*/
@NonNull Result<String, SuffixNode> querySuffix();
/**
* Gets the suffix.
*
* <p>This method uses the rules defined by the {@link #getSuffixStackDefinition() suffix stack}
* to produce a {@link String} output.</p>
*
* <p>Assuming the default configuration is used, this will usually be the value of the
* holder's highest priority suffix node.</p>
*
* <p>If the resultant suffix stack contained no elements, {@code null} is returned.</p>
*
* @return a suffix string, or null
*/
@Nullable String getSuffix();
default @Nullable String getSuffix() {
return querySuffix().result();
}
/**
* Gets an immutable copy of the meta this holder has.
* Gets a map of all accumulated {@link MetaNode meta}.
*
* @return an immutable map of meta
* <p>Prefer using the {@link #getMetaValue(String)} method for querying values.</p>
*
* @return a map of meta
*/
@NonNull @Unmodifiable Map<String, List<String>> getMeta();
/**
* Gets an immutable sorted map of all of the prefixes the holder has, whereby the first
* value is the highest priority prefix.
* Gets a sorted map of all accumulated {@link PrefixNode prefixes}.
*
* <p>Prefer using the {@link #getPrefix()} method for querying.</p>
*
* @return a sorted map of prefixes
*/
@NonNull @Unmodifiable SortedMap<Integer, String> getPrefixes();
/**
* Gets an immutable sorted map of all of the suffixes the holder has, whereby the first
* value is the highest priority suffix.
* Gets a sorted map of all accumulated {@link SuffixNode suffixes}.
*
* <p>Prefer using the {@link #getSuffix()} method for querying.</p>
*
* @return a sorted map of suffixes
*/
@@ -128,14 +215,14 @@ public interface CachedMetaData extends CachedData {
@Nullable String getPrimaryGroup();
/**
* Gets the definition used for the prefix stack
* Gets the definition used for the prefix stack.
*
* @return the definition used for the prefix stack
*/
@NonNull MetaStackDefinition getPrefixStackDefinition();
/**
* Gets the definition used for the suffix stack
* Gets the definition used for the suffix stack.
*
* @return the definition used for the suffix stack
*/
@@ -25,6 +25,7 @@
package net.luckperms.api.cacheddata;
import net.luckperms.api.node.Node;
import net.luckperms.api.util.Tristate;
import org.checkerframework.checker.nullness.qual.NonNull;
@@ -34,17 +35,39 @@ import java.util.Map;
/**
* Holds cached permission lookup data for a specific set of contexts.
*
* <p>All calls will account for inheritance, as well as any default data
* provided by the platform. These calls are heavily cached and are therefore
* fast.</p>
*/
public interface CachedPermissionData extends CachedData {
/**
* Gets a permission check result for the given permission node.
* Performs a permission check for the given {@code permission} node.
*
* <p>This check is equivalent to the "hasPermission" method call on most platforms.
* You can use {@link Tristate#asBoolean()} if you need a truthy result.</p>
*
* @param permission the permission node
* @return a result containing the tristate
* @throws NullPointerException if permission is null
* @since 5.4
*/
@NonNull Result<Tristate, Node> queryPermission(@NonNull String permission);
/**
* Performs a permission check for the given {@code permission} node.
*
* <p>This check is equivalent to the "hasPermission" method call on most platforms.
* You can use {@link Tristate#asBoolean()} if you need a truthy result.</p>
*
* @param permission the permission node
* @return a tristate result
* @throws NullPointerException if permission is null
*/
@NonNull Tristate checkPermission(@NonNull String permission);
default @NonNull Tristate checkPermission(@NonNull String permission) {
return queryPermission(permission).result();
}
/**
* Invalidates the underlying permission calculator cache.
@@ -0,0 +1,82 @@
/*
* 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.cacheddata;
import net.luckperms.api.node.Node;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Represents the result of a cached data lookup.
*
* <p>You can find "the holder that has the node that caused this result"
* using the following code:</p>
* <p></p>
* <blockquote>
* <pre>
* public static {@link net.luckperms.api.model.PermissionHolder.Identifier} holderThatHasTheNodeThatCausedTheResult(Result&lt;?, ?&gt; result) {
* {@link Node} node = result.node();
* if (node == null) {
* return null;
* }
* {@link net.luckperms.api.node.metadata.types.InheritanceOriginMetadata} origin = node.getMetadata(InheritanceOriginMetadata.KEY).orElse(null);
* if (origin == null) {
* return null;
* }
* return origin.getOrigin();
* }
* </pre>
*
* <p>Combined with the node itself, this is all the information needed to determine
* the root cause of the result.</p>
* </blockquote>
*
* <p>The nullability of {@link #result()} is purposely undefined to allow the
* flexibility for methods using {@link Result} to declare it. In general, if the {@code T} type
* has a nullable/undefined value, then the return of {@link #result()} will be non-null,
* and if not, it will be nullable.</p>
*
* @param <T> the result type
* @param <N> the node type
* @since 5.4
*/
public interface Result<T, N extends Node> {
/**
* Gets the underlying result.
*
* @return the underlying result
*/
T result();
/**
* Gets the node that caused the result.
*
* @return the causing node
*/
@Nullable N node();
}
@@ -24,7 +24,7 @@
*/
/**
* CachedData lookup API for {@link net.luckperms.api.model.user.User}s and
* Caches permission checks and meta lookups for {@link net.luckperms.api.model.user.User}s and
* {@link net.luckperms.api.model.group.Group}s.
*/
package net.luckperms.api.cacheddata;
@@ -103,7 +103,7 @@ public interface ContextCalculator<T> {
*
* @return a set of potential contexts
*/
default ContextSet estimatePotentialContexts() {
default @NonNull ContextSet estimatePotentialContexts() {
return ImmutableContextSet.empty();
}
@@ -119,7 +119,7 @@ public interface ImmutableContextSet extends ContextSet {
}
/**
* Adds of of the contexts in another {@link ContextSet} to the set.
* Adds all the contexts in another {@link ContextSet} to the set.
*
* @param contextSet the set to add from
* @return the builder
@@ -94,7 +94,7 @@ public interface MutableContextSet extends ContextSet {
}
/**
* Adds of of the contexts in another {@link ContextSet} to this set.
* Adds all the contexts in another {@link ContextSet} to this set.
*
* @param contextSet the set to add from
* @throws NullPointerException if the contextSet is null
@@ -56,6 +56,22 @@ public interface UniqueIdDetermineTypeEvent extends LuckPermsEvent, ResultEvent<
*/
String TYPE_UNAUTHENTICATED = "unauthenticated";
/**
* The players UUID most likely belongs to a NPC (non-player character).
*
* <p>Usually indicated by the UUID being {@link UUID#version() version} 2.</p>
*
* @since 5.4
*/
String TYPE_NPC = "npc";
/**
* Unknown UUID type.
*
* @since 5.4
*/
String TYPE_UNKNOWN = "unknown";
/**
* Gets the {@link UUID unique id} being queried.
*
@@ -40,7 +40,7 @@ public interface MessagingService {
*
* @return the name of this messaging service
*/
String getName();
@NonNull String getName();
/**
* Uses the messaging service to inform other servers about a general
@@ -31,6 +31,8 @@ import net.luckperms.api.node.types.SuffixNode;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Locale;
/**
* Represents a type of chat meta
*/
@@ -70,7 +72,7 @@ public enum ChatMetaType {
private final NodeType<? extends ChatMetaNode<?, ?>> nodeType;
ChatMetaType(NodeType<? extends ChatMetaNode<?, ?>> nodeType) {
this.name = nodeType.name().toLowerCase();
this.name = nodeType.name().toLowerCase(Locale.ROOT);
this.nodeType = nodeType;
}
@@ -201,11 +201,11 @@ public interface Node {
/**
* Gets the metadata corresponding to the given <code>key</code>, if present.
*
* @param key the key
* @param <T> the metadata type
* @param key the key
* @return the data, if present
*/
<T> Optional<T> getMetadata(NodeMetadataKey<T> key);
<T> Optional<T> getMetadata(@NonNull NodeMetadataKey<T> key);
/**
* Gets the metadata corresponding to the given <code>key</code>, throwing an exception
@@ -216,7 +216,7 @@ public interface Node {
* @return the data
* @throws IllegalStateException if data isn't present
*/
default <T> T metadata(NodeMetadataKey<T> key) throws IllegalStateException {
default <T> T metadata(@NonNull NodeMetadataKey<T> key) throws IllegalStateException {
return getMetadata(key).orElseThrow(() -> new IllegalStateException("Node '" + getKey() + "' does not have '" + key.name() + "' attached."));
}
@@ -109,7 +109,7 @@ public interface NodeBuilder<N extends ScopedNode<N, B>, B extends NodeBuilder<N
* @param unit the unit <code>duration</code> is measured in
* @return the builder
*/
default @NonNull B expiry(long duration, TimeUnit unit) {
default @NonNull B expiry(long duration, @NonNull TimeUnit unit) {
if (duration <= 0) {
throw new IllegalArgumentException("duration must be positive");
}
@@ -35,6 +35,8 @@ import net.luckperms.api.node.types.RegexPermissionNode;
import net.luckperms.api.node.types.SuffixNode;
import net.luckperms.api.node.types.WeightNode;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
@@ -48,59 +50,105 @@ public interface NodeType<T extends Node> {
/**
* Node type for {@link PermissionNode}.
*/
NodeType<PermissionNode> PERMISSION = new SimpleNodeType<>("PERMISSION", n -> n instanceof PermissionNode, n -> (PermissionNode) n);
NodeType<PermissionNode> PERMISSION = new SimpleNodeType<>(
"PERMISSION",
n -> n instanceof PermissionNode,
n -> (PermissionNode) n
);
/**
* Node type for {@link RegexPermissionNode}.
*/
NodeType<RegexPermissionNode> REGEX_PERMISSION = new SimpleNodeType<>("REGEX_PERMISSION", n -> n instanceof RegexPermissionNode, n -> (RegexPermissionNode) n);
NodeType<RegexPermissionNode> REGEX_PERMISSION = new SimpleNodeType<>(
"REGEX_PERMISSION",
n -> n instanceof RegexPermissionNode,
n -> (RegexPermissionNode) n
);
/**
* Node type for {@link InheritanceNode}.
*/
NodeType<InheritanceNode> INHERITANCE = new SimpleNodeType<>("INHERITANCE", n -> n instanceof InheritanceNode, n -> (InheritanceNode) n);
NodeType<InheritanceNode> INHERITANCE = new SimpleNodeType<>(
"INHERITANCE",
n -> n instanceof InheritanceNode,
n -> (InheritanceNode) n
);
/**
* Node type for {@link PrefixNode}.
*/
NodeType<PrefixNode> PREFIX = new SimpleNodeType<>("PREFIX", n -> n instanceof PrefixNode, n -> (PrefixNode) n);
NodeType<PrefixNode> PREFIX = new SimpleNodeType<>(
"PREFIX",
n -> n instanceof PrefixNode,
n -> (PrefixNode) n
);
/**
* Node type for {@link SuffixNode}.
*/
NodeType<SuffixNode> SUFFIX = new SimpleNodeType<>("SUFFIX", n -> n instanceof SuffixNode, n -> (SuffixNode) n);
NodeType<SuffixNode> SUFFIX = new SimpleNodeType<>(
"SUFFIX",
n -> n instanceof SuffixNode,
n -> (SuffixNode) n
);
/**
* Node type for {@link MetaNode}.
*/
NodeType<MetaNode> META = new SimpleNodeType<>("META", n -> n instanceof MetaNode, n -> (MetaNode) n);
NodeType<MetaNode> META = new SimpleNodeType<>(
"META",
n -> n instanceof MetaNode,
n -> (MetaNode) n
);
/**
* Node type for {@link WeightNode}.
*/
NodeType<WeightNode> WEIGHT = new SimpleNodeType<>("WEIGHT", n -> n instanceof WeightNode, n -> (WeightNode) n);
NodeType<WeightNode> WEIGHT = new SimpleNodeType<>(
"WEIGHT",
n -> n instanceof WeightNode,
n -> (WeightNode) n
);
/**
* Node type for {@link DisplayNameNode}.
*/
NodeType<DisplayNameNode> DISPLAY_NAME = new SimpleNodeType<>("DISPLAY_NAME", n -> n instanceof DisplayNameNode, n -> (DisplayNameNode) n);
NodeType<DisplayNameNode> DISPLAY_NAME = new SimpleNodeType<>(
"DISPLAY_NAME",
n -> n instanceof DisplayNameNode,
n -> (DisplayNameNode) n
);
/**
* Node type for {@link ChatMetaNode}.
*
* <p>This is an abstract type, and therefore will never
* be returned from {@link Node#getType()}.</p>
*/
NodeType<ChatMetaNode<?, ?>> CHAT_META = new SimpleNodeType<>("CHAT_META", n -> n instanceof ChatMetaNode<?, ?>, n -> (ChatMetaNode<?, ?>) n);
NodeType<ChatMetaNode<?, ?>> CHAT_META = new SimpleNodeType<>(
"CHAT_META",
n -> n instanceof ChatMetaNode<?, ?>,
n -> (ChatMetaNode<?, ?>) n
);
/**
* Node type for {@link ChatMetaNode} or {@link MetaNode}.
*
* <p>This is an abstract type, and therefore will never
* be returned from {@link Node#getType()}.</p>
*/
NodeType<Node> META_OR_CHAT_META = new SimpleNodeType<>("META_OR_CHAT_META", n -> META.matches(n) || CHAT_META.matches(n), Function.identity());
NodeType<Node> META_OR_CHAT_META = new SimpleNodeType<>(
"META_OR_CHAT_META",
n -> META.matches(n) || CHAT_META.matches(n),
Function.identity()
);
/**
* Gets a name for the node type.
*
* @return a name
*/
String name();
@NonNull String name();
/**
* Returns if the passed node matches the type
@@ -108,7 +156,7 @@ public interface NodeType<T extends Node> {
* @param node the node to test
* @return true if the node has the same type
*/
boolean matches(Node node);
boolean matches(@NonNull Node node);
/**
* Casts the given {@link Node} to the type defined by the {@link NodeType}.
@@ -120,7 +168,7 @@ public interface NodeType<T extends Node> {
* @return the casted node
* @throws IllegalArgumentException if the node to cast does not match the type
*/
T cast(Node node);
@NonNull T cast(@NonNull Node node);
/**
* Attempts to cast the given {@link Node} to the type defined by the
@@ -132,7 +180,7 @@ public interface NodeType<T extends Node> {
* @param node the node to cast
* @return an optional, possibly containing a casted node
*/
default Optional<T> tryCast(Node node) {
default @NonNull Optional<T> tryCast(@NonNull Node node) {
Objects.requireNonNull(node, "node");
if (!matches(node)) {
return Optional.empty();
@@ -147,7 +195,7 @@ public interface NodeType<T extends Node> {
*
* @return a predicate for the {@link #matches(Node)} method.
*/
default Predicate<Node> predicate() {
default @NonNull Predicate<Node> predicate() {
return this::matches;
}
@@ -159,7 +207,7 @@ public interface NodeType<T extends Node> {
* @param and a predicate to AND with the result of the type match check
* @return a matching predicate, ANDed with the given predicate parameter
*/
default Predicate<Node> predicate(Predicate<? super T> and) {
default @NonNull Predicate<Node> predicate(@NonNull Predicate<? super T> and) {
return node -> matches(node) && and.test(cast(node));
}
@@ -25,6 +25,9 @@
package net.luckperms.api.node;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -41,18 +44,18 @@ final class SimpleNodeType<T extends Node> implements NodeType<T> {
}
@Override
public String name() {
public @NotNull String name() {
return this.name;
}
@Override
public boolean matches(Node node) {
public boolean matches(@NonNull Node node) {
Objects.requireNonNull(node, "node");
return this.matches.test(node);
}
@Override
public T cast(Node node) {
public @NotNull T cast(@NonNull Node node) {
if (!matches(node)) {
throw new IllegalArgumentException("Node " + node.getClass() + " does not match " + this.name);
}
@@ -134,7 +134,7 @@ public interface NodeMatcher<T extends Node> extends Predicate<Node> {
* @param <T> the node type
* @return the matcher
*/
static <T extends Node> @NonNull NodeMatcher<T> type(NodeType<? extends T> type) {
static <T extends Node> @NonNull NodeMatcher<T> type(@NonNull NodeType<? extends T> type) {
return LuckPermsProvider.get().getNodeMatcherFactory().type(type);
}
@@ -117,6 +117,6 @@ public interface NodeMatcherFactory {
* @param <T> the node type
* @return the matcher
*/
<T extends Node> @NonNull NodeMatcher<T> type(NodeType<? extends T> type);
<T extends Node> @NonNull NodeMatcher<T> type(@NonNull NodeType<? extends T> type);
}
@@ -27,6 +27,7 @@ package net.luckperms.api.node.metadata;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Locale;
import java.util.Objects;
final class SimpleNodeMetadataKey<T> implements NodeMetadataKey<T> {
@@ -34,7 +35,7 @@ final class SimpleNodeMetadataKey<T> implements NodeMetadataKey<T> {
private final Class<T> type;
SimpleNodeMetadataKey(String name, Class<T> type) {
this.name = name.toLowerCase();
this.name = name.toLowerCase(Locale.ROOT);
this.type = type;
}
@@ -26,6 +26,7 @@
package net.luckperms.api.node.metadata.types;
import net.luckperms.api.model.PermissionHolder;
import net.luckperms.api.model.data.DataType;
import net.luckperms.api.node.Node;
import net.luckperms.api.node.metadata.NodeMetadataKey;
@@ -56,6 +57,15 @@ public interface InheritanceOriginMetadata {
*/
PermissionHolder.@NonNull Identifier getOrigin();
/**
* Gets the {@link DataType type} of the {@link net.luckperms.api.model.data.NodeMap}
* the node was inherited from.
*
* @return the type of the NodeMap the node was inherited from.
* @since 5.4
*/
@NonNull DataType getDataType();
/**
* Gets whether the associated node was inherited from another holder.
*
@@ -27,6 +27,7 @@ package net.luckperms.api.query;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Locale;
import java.util.Objects;
final class SimpleOptionKey<T> implements OptionKey<T> {
@@ -35,7 +36,7 @@ final class SimpleOptionKey<T> implements OptionKey<T> {
private final int hashCode;
SimpleOptionKey(String name, Class<T> type) {
this.name = name.toLowerCase();
this.name = name.toLowerCase(Locale.ROOT);
this.type = type;
this.hashCode = Objects.hash(this.name, this.type); // cache hashcode
}
@@ -93,7 +93,7 @@ public enum DataQueryOrder implements Comparator<DataType> {
* @param comparator the comparator
* @return the ordered data types
*/
public static List<DataType> order(@NonNull Comparator<? super DataType> comparator) {
public static @NonNull List<DataType> order(@NonNull Comparator<? super DataType> comparator) {
int compare = comparator.compare(DataType.TRANSIENT, DataType.NORMAL);
if (compare > 0) {
// transient first
@@ -32,6 +32,7 @@ import net.luckperms.api.query.OptionKey;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Comparator;
import java.util.Objects;
/**
* A function that generates a {@link DataQueryOrder} comparator for
@@ -52,7 +53,8 @@ public interface DataQueryOrderFunction {
* @return the data query order function
* @since 5.2
*/
static DataQueryOrderFunction always(Comparator<DataType> comparator) {
static @NonNull DataQueryOrderFunction always(@NonNull Comparator<DataType> comparator) {
Objects.requireNonNull(comparator, "comparator");
return id -> comparator;
}
@@ -31,6 +31,7 @@ import net.luckperms.api.query.OptionKey;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Objects;
import java.util.function.Predicate;
/**
@@ -53,7 +54,8 @@ public interface DataTypeFilterFunction {
* @param predicate the predicate
* @return the data type filter function
*/
static DataTypeFilterFunction always(Predicate<DataType> predicate) {
static @NonNull DataTypeFilterFunction always(@NonNull Predicate<DataType> predicate) {
Objects.requireNonNull(predicate, "predicate");
return id -> predicate;
}
@@ -25,6 +25,7 @@
package net.luckperms.api.query.meta;
import net.luckperms.api.cacheddata.Result;
import net.luckperms.api.node.types.MetaNode;
import net.luckperms.api.query.OptionKey;
@@ -54,6 +55,6 @@ public interface MetaValueSelector {
* @param values the values, in the order in which they were accumulated.
* @return the selected value
*/
@NonNull String selectValue(@NonNull String key, @NonNull List<String> values);
@NonNull Result<String, MetaNode> selectValue(@NonNull String key, @NonNull List<? extends Result<String, MetaNode>> values);
}
+4 -10
View File
@@ -1,19 +1,13 @@
buildscript {
repositories {
maven { url 'https://plugins.gradle.org/m2' }
}
dependencies {
classpath 'gradle.plugin.net.minecrell:licenser:0.4.1'
}
plugins {
id 'org.cadixdev.licenser' version '0.6.1' apply false
}
defaultTasks 'licenseFormat', 'build'
subprojects {
apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'net.minecrell.licenser'
apply plugin: 'maven-publish'
apply plugin: 'org.cadixdev.licenser'
group = 'me.lucko.luckperms'
version = '5.3-SNAPSHOT'
+6 -6
View File
@@ -1,15 +1,15 @@
plugins {
id 'com.github.johnrengelman.shadow' version '2.0.4'
id 'com.github.johnrengelman.shadow' version '7.0.0'
}
dependencies {
compile project(':bukkit')
compile 'com.google.code.gson:gson:2.7'
compile 'com.google.guava:guava:19.0'
implementation project(':bukkit')
implementation 'com.google.code.gson:gson:2.7'
implementation 'com.google.guava:guava:19.0'
}
shadowJar {
archiveName = 'luckperms-bukkitlegacy.jarinjar'
archiveFileName = 'luckperms-bukkitlegacy.jarinjar'
dependencies {
include(dependency('me.lucko.luckperms:.*'))
@@ -41,4 +41,4 @@ shadowJar {
artifacts {
archives shadowJar
}
}
+5 -6
View File
@@ -9,19 +9,18 @@ repositories {
dependencies {
compileOnly 'com.destroystokyo.paper:paper-api:1.15.2-R0.1-SNAPSHOT'
compile project(':api')
compile project(':common:loader-utils')
implementation project(':api')
implementation project(':common:loader-utils')
}
processResources {
from(sourceSets.main.resources.srcDirs) {
filesMatching('plugin.yml') {
expand 'pluginVersion': project.ext.fullVersion
include 'plugin.yml'
}
}
shadowJar {
archiveName = "LuckPerms-Bukkit-Legacy-${project.ext.fullVersion}.jar"
archiveFileName = "LuckPerms-Bukkit-Legacy-${project.ext.fullVersion}.jar"
from {
project(':bukkit-legacy').tasks.shadowJar.archiveFile
@@ -30,4 +29,4 @@ shadowJar {
artifacts {
archives shadowJar
}
}
+11 -7
View File
@@ -1,5 +1,5 @@
plugins {
id 'com.github.johnrengelman.shadow' version '2.0.4'
id 'com.github.johnrengelman.shadow' version '7.0.0'
}
repositories {
@@ -8,20 +8,24 @@ repositories {
}
dependencies {
compile project(':common')
implementation project(':common')
compileOnly project(':common:loader-utils')
compileOnly 'com.destroystokyo.paper:paper-api:1.15.2-R0.1-SNAPSHOT'
compileOnly 'me.lucko:adventure-platform-bukkit:4.0.0' // re: this artifact - see note in common/build.gradle
compileOnly 'me.lucko:commodore:1.7'
compileOnly('net.milkbowl.vault:VaultAPI:1.6') {
compileOnly('me.lucko:adventure-platform-bukkit:4.9.4') {
exclude(module: 'adventure-bom')
exclude(module: 'adventure-api')
exclude(module: 'adventure-nbt')
}
compileOnly 'me.lucko:commodore:1.11'
compileOnly('net.milkbowl.vault:VaultAPI:1.7') {
exclude(module: 'bukkit')
}
compileOnly 'lilypad.client.connect:api:0.0.1-SNAPSHOT'
}
shadowJar {
archiveName = 'luckperms-bukkit.jarinjar'
archiveFileName = 'luckperms-bukkit.jarinjar'
dependencies {
include(dependency('me.lucko.luckperms:.*'))
@@ -48,4 +52,4 @@ shadowJar {
artifacts {
archives shadowJar
}
}
+5 -6
View File
@@ -9,19 +9,18 @@ repositories {
dependencies {
compileOnly 'com.destroystokyo.paper:paper-api:1.15.2-R0.1-SNAPSHOT'
compile project(':api')
compile project(':common:loader-utils')
implementation project(':api')
implementation project(':common:loader-utils')
}
processResources {
from(sourceSets.main.resources.srcDirs) {
filesMatching('plugin.yml') {
expand 'pluginVersion': project.ext.fullVersion
include 'plugin.yml'
}
}
shadowJar {
archiveName = "LuckPerms-Bukkit-${project.ext.fullVersion}.jar"
archiveFileName = "LuckPerms-Bukkit-${project.ext.fullVersion}.jar"
from {
project(':bukkit').tasks.shadowJar.archiveFile
@@ -30,4 +29,4 @@ shadowJar {
artifacts {
archives shadowJar
}
}
@@ -94,6 +94,11 @@ public class BukkitSenderFactory extends SenderFactory<LPBukkitPlugin, CommandSe
getPlugin().getBootstrap().getServer().dispatchCommand(sender, command);
}
@Override
protected boolean isConsole(CommandSender sender) {
return sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender;
}
@Override
public void close() {
super.close();
@@ -60,8 +60,6 @@ import me.lucko.luckperms.common.model.manager.user.StandardUserManager;
import me.lucko.luckperms.common.plugin.AbstractLuckPermsPlugin;
import me.lucko.luckperms.common.plugin.util.AbstractConnectionListener;
import me.lucko.luckperms.common.sender.Sender;
import me.lucko.luckperms.common.tasks.CacheHousekeepingTask;
import me.lucko.luckperms.common.tasks.ExpireTemporaryTask;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.query.QueryOptions;
@@ -79,7 +77,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
/**
@@ -125,6 +122,7 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin {
dependencies.add(Dependency.ADVENTURE_PLATFORM_BUKKIT);
if (isBrigadierSupported()) {
dependencies.add(Dependency.COMMODORE);
dependencies.add(Dependency.COMMODORE_FILE);
}
return dependencies;
}
@@ -258,12 +256,6 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin {
this.bootstrap.getServer().getServicesManager().register(LuckPerms.class, api, this.bootstrap.getLoader(), ServicePriority.Normal);
}
@Override
protected void registerHousekeepingTasks() {
this.bootstrap.getScheduler().asyncRepeating(new ExpireTemporaryTask(this), 3, TimeUnit.SECONDS);
this.bootstrap.getScheduler().asyncRepeating(new CacheHousekeepingTask(this), 2, TimeUnit.MINUTES);
}
@Override
protected void performFinalSetup() {
// register permissions
@@ -304,7 +296,7 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin {
this.bootstrap.getScheduler().executeSync(() -> {
try {
LuckPermsPermissible lpPermissible = new LuckPermsPermissible(player, user, this);
PermissibleInjector.inject(player, lpPermissible);
PermissibleInjector.inject(player, lpPermissible, getLogger());
} catch (Throwable t) {
getLogger().severe("Exception thrown when setting up permissions for " +
player.getUniqueId() + " - " + player.getName(), t);
@@ -357,7 +349,7 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin {
try {
Class.forName(className);
return true;
} catch (ClassNotFoundException var1) {
} catch (ClassNotFoundException e) {
return false;
}
}
@@ -52,7 +52,7 @@ public class BukkitCalculatorFactory implements CalculatorFactory {
@Override
public PermissionCalculator build(QueryOptions queryOptions, CacheMetadata metadata) {
List<PermissionProcessor> processors = new ArrayList<>(7);
List<PermissionProcessor> processors = new ArrayList<>(8);
processors.add(new DirectProcessor());
@@ -75,7 +75,8 @@ public class BukkitCalculatorFactory implements CalculatorFactory {
boolean op = queryOptions.option(BukkitContextManager.OP_OPTION).orElse(false);
if (metadata.getHolderType() == HolderType.USER && this.plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_DEFAULT_PERMISSIONS)) {
boolean overrideWildcards = this.plugin.getConfiguration().get(ConfigKeys.APPLY_DEFAULT_NEGATIONS_BEFORE_WILDCARDS);
processors.add(new DefaultsProcessor(this.plugin, overrideWildcards, op));
processors.add(new DefaultPermissionMapProcessor(this.plugin, op));
processors.add(new PermissionMapProcessor(this.plugin, overrideWildcards, op));
}
if (op) {
@@ -26,23 +26,25 @@
package me.lucko.luckperms.bukkit.calculator;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.common.calculator.processor.AbstractPermissionProcessor;
import me.lucko.luckperms.common.cacheddata.result.TristateResult;
import me.lucko.luckperms.common.calculator.processor.AbstractSourceBasedProcessor;
import me.lucko.luckperms.common.calculator.processor.PermissionProcessor;
import me.lucko.luckperms.common.calculator.result.TristateResult;
import net.luckperms.api.util.Tristate;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Permission Processor for Bukkits "child" permission system.
*/
public class ChildProcessor extends AbstractPermissionProcessor implements PermissionProcessor {
public class ChildProcessor extends AbstractSourceBasedProcessor implements PermissionProcessor {
private static final TristateResult.Factory RESULT_FACTORY = new TristateResult.Factory(ChildProcessor.class);
private final LPBukkitPlugin plugin;
private final AtomicBoolean needsRefresh = new AtomicBoolean(false);
private Map<String, TristateResult> childPermissions = Collections.emptyMap();
public ChildProcessor(LPBukkitPlugin plugin) {
@@ -51,23 +53,27 @@ public class ChildProcessor extends AbstractPermissionProcessor implements Permi
@Override
public TristateResult hasPermission(String permission) {
if (this.needsRefresh.compareAndSet(true, false)) {
refresh();
}
return this.childPermissions.getOrDefault(permission, TristateResult.UNDEFINED);
}
@Override
public void refresh() {
Map<String, TristateResult> builder = new ConcurrentHashMap<>();
for (Map.Entry<String, Boolean> e : this.sourceMap.entrySet()) {
Map<String, Boolean> children = this.plugin.getPermissionMap().getChildPermissions(e.getKey(), e.getValue());
for (Map.Entry<String, Boolean> child : children.entrySet()) {
builder.put(child.getKey(), RESULT_FACTORY.result(Tristate.of(child.getValue()), "parent: " + e.getKey()));
}
}
this.childPermissions = builder;
Map<String, TristateResult> childPermissions = new HashMap<>();
this.sourceMap.forEach((key, node) -> {
Map<String, Boolean> children = this.plugin.getPermissionMap().getChildPermissions(key, node.getValue());
children.forEach((childKey, childValue) -> {
childPermissions.put(childKey, RESULT_FACTORY.resultWithOverride(node, Tristate.of(childValue)));
});
});
this.childPermissions = childPermissions;
this.needsRefresh.set(false);
}
@Override
public void invalidate() {
refresh();
this.needsRefresh.set(true);
}
}
@@ -0,0 +1,58 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.calculator;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.common.cacheddata.result.TristateResult;
import me.lucko.luckperms.common.calculator.processor.AbstractPermissionProcessor;
import me.lucko.luckperms.common.calculator.processor.PermissionProcessor;
import net.luckperms.api.util.Tristate;
/**
* Permission Processor for Bukkits "default" permission system.
*/
public class DefaultPermissionMapProcessor extends AbstractPermissionProcessor implements PermissionProcessor {
private static final TristateResult.Factory RESULT_FACTORY = new TristateResult.Factory(DefaultPermissionMapProcessor.class);
private final LPBukkitPlugin plugin;
private final boolean isOp;
public DefaultPermissionMapProcessor(LPBukkitPlugin plugin, boolean isOp) {
this.plugin = plugin;
this.isOp = isOp;
}
@Override
public TristateResult hasPermission(String permission) {
Tristate t = this.plugin.getDefaultPermissionMap().lookupDefaultPermission(permission, this.isOp);
if (t != Tristate.UNDEFINED) {
return RESULT_FACTORY.result(t);
}
return TristateResult.UNDEFINED;
}
}
@@ -1,90 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.calculator;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.common.calculator.processor.PermissionProcessor;
import me.lucko.luckperms.common.calculator.processor.SpongeWildcardProcessor;
import me.lucko.luckperms.common.calculator.processor.WildcardProcessor;
import me.lucko.luckperms.common.calculator.result.TristateResult;
import net.luckperms.api.util.Tristate;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
/**
* Permission Processor for Bukkits "default" permission system.
*/
public class DefaultsProcessor implements PermissionProcessor {
private static final TristateResult.Factory DEFAULT_PERMISSION_MAP_RESULT_FACTORY = new TristateResult.Factory(DefaultsProcessor.class, "default permission map");
private static final TristateResult.Factory PERMISSION_MAP_RESULT_FACTORY = new TristateResult.Factory(DefaultsProcessor.class, "permission map");
private final LPBukkitPlugin plugin;
private final boolean overrideWildcards;
private final boolean isOp;
public DefaultsProcessor(LPBukkitPlugin plugin, boolean overrideWildcards, boolean isOp) {
this.plugin = plugin;
this.overrideWildcards = overrideWildcards;
this.isOp = isOp;
}
private boolean canOverrideWildcard(TristateResult prev) {
return this.overrideWildcards &&
(prev.processorClass() == WildcardProcessor.class || prev.processorClass() == SpongeWildcardProcessor.class) &&
prev.result() == Tristate.TRUE;
}
@Override
public TristateResult hasPermission(TristateResult prev, String permission) {
if (prev != TristateResult.UNDEFINED) {
// Check to see if the result should be overridden
if (canOverrideWildcard(prev)) {
Permission defPerm = this.plugin.getPermissionMap().get(permission);
if (defPerm != null) {
PermissionDefault def = defPerm.getDefault();
if (def == PermissionDefault.FALSE || this.isOp && def == PermissionDefault.NOT_OP) {
return PERMISSION_MAP_RESULT_FACTORY.result(Tristate.FALSE, "permission map (overriding wildcard): " + prev.cause());
}
}
}
return prev;
}
Tristate t = this.plugin.getDefaultPermissionMap().lookupDefaultPermission(permission, this.isOp);
if (t != Tristate.UNDEFINED) {
return DEFAULT_PERMISSION_MAP_RESULT_FACTORY.result(t);
}
Permission defPerm = this.plugin.getPermissionMap().get(permission);
if (defPerm == null) {
return TristateResult.UNDEFINED;
}
return PERMISSION_MAP_RESULT_FACTORY.result(Tristate.of(defPerm.getDefault().getValue(this.isOp)));
}
}
@@ -25,8 +25,9 @@
package me.lucko.luckperms.bukkit.calculator;
import me.lucko.luckperms.common.cacheddata.result.TristateResult;
import me.lucko.luckperms.common.calculator.processor.AbstractPermissionProcessor;
import me.lucko.luckperms.common.calculator.processor.PermissionProcessor;
import me.lucko.luckperms.common.calculator.result.TristateResult;
import net.luckperms.api.util.Tristate;
@@ -34,7 +35,7 @@ import net.luckperms.api.util.Tristate;
* Permission Processor which is added for opped users, to simply return true if
* no other processors match.
*/
public final class OpProcessor implements PermissionProcessor {
public final class OpProcessor extends AbstractPermissionProcessor implements PermissionProcessor {
private static final TristateResult TRUE_RESULT = new TristateResult.Factory(OpProcessor.class).result(Tristate.TRUE);
public static final OpProcessor INSTANCE = new OpProcessor();
@@ -44,10 +45,7 @@ public final class OpProcessor implements PermissionProcessor {
}
@Override
public TristateResult hasPermission(TristateResult prev, String permission) {
if (prev != TristateResult.UNDEFINED) {
return prev;
}
public TristateResult hasPermission(String permission) {
return TRUE_RESULT;
}
}
@@ -0,0 +1,60 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.calculator;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.common.cacheddata.result.TristateResult;
import me.lucko.luckperms.common.calculator.processor.AbstractOverrideWildcardProcessor;
import me.lucko.luckperms.common.calculator.processor.PermissionProcessor;
import net.luckperms.api.util.Tristate;
import org.bukkit.permissions.Permission;
/**
* Permission Processor for Bukkits "default" permission system.
*/
public class PermissionMapProcessor extends AbstractOverrideWildcardProcessor implements PermissionProcessor {
private static final TristateResult.Factory RESULT_FACTORY = new TristateResult.Factory(PermissionMapProcessor.class);
private final LPBukkitPlugin plugin;
private final boolean isOp;
public PermissionMapProcessor(LPBukkitPlugin plugin, boolean overrideWildcards, boolean isOp) {
super(overrideWildcards);
this.plugin = plugin;
this.isOp = isOp;
}
@Override
public TristateResult hasPermission(String permission) {
Permission defPerm = this.plugin.getPermissionMap().get(permission);
if (defPerm == null) {
return TristateResult.UNDEFINED;
}
return RESULT_FACTORY.result(Tristate.of(defPerm.getDefault().getValue(this.isOp)));
}
}
@@ -30,8 +30,8 @@ import com.github.benmanes.caffeine.cache.LoadingCache;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.common.cache.LoadingMap;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.context.ContextManager;
import me.lucko.luckperms.common.context.QueryOptionsCache;
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;
@@ -29,7 +29,7 @@ import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl;
import me.lucko.luckperms.common.context.ImmutableContextSetImpl;
import me.lucko.luckperms.common.util.EnumNamer;
import net.luckperms.api.context.Context;
@@ -105,7 +105,7 @@ public class BukkitPlayerCalculator implements ContextCalculator<Player>, Listen
}
@Override
public ContextSet estimatePotentialContexts() {
public @NonNull ContextSet estimatePotentialContexts() {
ImmutableContextSet.Builder builder = new ImmutableContextSetImpl.BuilderImpl();
if (this.gamemode) {
for (GameMode mode : GameMode.values()) {
@@ -29,13 +29,13 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.bukkit.calculator.DefaultsProcessor;
import me.lucko.luckperms.bukkit.calculator.OpProcessor;
import me.lucko.luckperms.common.calculator.result.TristateResult;
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.QueryOptionsCache;
import me.lucko.luckperms.common.context.manager.QueryOptionsCache;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent;
import me.lucko.luckperms.common.verbose.event.CheckOrigin;
import net.luckperms.api.query.QueryOptions;
import net.luckperms.api.util.Tristate;
@@ -143,13 +143,13 @@ public class LuckPermsPermissible extends PermissibleBase {
}
QueryOptions queryOptions = this.queryOptionsSupplier.getQueryOptions();
TristateResult result = this.user.getCachedData().getPermissionData(queryOptions).checkPermission(permission, PermissionCheckEvent.Origin.PLATFORM_LOOKUP_CHECK);
TristateResult result = this.user.getCachedData().getPermissionData(queryOptions).checkPermission(permission, CheckOrigin.PLATFORM_API_HAS_PERMISSION_SET);
if (result.result() == Tristate.UNDEFINED) {
return false;
}
// ignore matches made from looking up in the permission map (replicate bukkit behaviour)
if (result.processorClass() == DefaultsProcessor.class && "permission map".equals(result.cause())) {
if (result.processorClass() == PermissionMapProcessor.class) {
return false;
}
@@ -173,7 +173,7 @@ public class LuckPermsPermissible extends PermissibleBase {
}
QueryOptions queryOptions = this.queryOptionsSupplier.getQueryOptions();
return this.user.getCachedData().getPermissionData(queryOptions).checkPermission(permission, PermissionCheckEvent.Origin.PLATFORM_PERMISSION_CHECK).result().asBoolean();
return this.user.getCachedData().getPermissionData(queryOptions).checkPermission(permission, CheckOrigin.PLATFORM_API_HAS_PERMISSION).result().asBoolean();
}
@Override
@@ -183,7 +183,7 @@ public class LuckPermsPermissible extends PermissibleBase {
}
QueryOptions queryOptions = this.queryOptionsSupplier.getQueryOptions();
TristateResult result = this.user.getCachedData().getPermissionData(queryOptions).checkPermission(permission.getName(), PermissionCheckEvent.Origin.PLATFORM_PERMISSION_CHECK);
TristateResult result = this.user.getCachedData().getPermissionData(queryOptions).checkPermission(permission.getName(), CheckOrigin.PLATFORM_API_HAS_PERMISSION);
// override default op handling using the Permission class we have
if (result.processorClass() == OpProcessor.class && this.plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_DEFAULT_PERMISSIONS)) {
@@ -46,6 +46,7 @@ import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -239,7 +240,7 @@ public class LuckPermsPermissionAttachment extends PermissionAttachment {
Objects.requireNonNull(name, "name is null");
Preconditions.checkArgument(!name.isEmpty(), "name is empty");
String permission = name.toLowerCase();
String permission = name.toLowerCase(Locale.ROOT);
Boolean previous = this.perms.put(permission, value);
if (previous != null && previous == value) {
@@ -264,7 +265,7 @@ public class LuckPermsPermissionAttachment extends PermissionAttachment {
Objects.requireNonNull(name, "name is null");
Preconditions.checkArgument(!name.isEmpty(), "name is empty");
String permission = name.toLowerCase();
String permission = name.toLowerCase(Locale.ROOT);
Boolean previous = this.perms.remove(permission);
if (previous == null) {
@@ -25,11 +25,12 @@
package me.lucko.luckperms.bukkit.inject.permissible;
import me.lucko.luckperms.common.calculator.result.TristateResult;
import me.lucko.luckperms.common.cacheddata.result.TristateResult;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.query.QueryOptionsImpl;
import me.lucko.luckperms.common.verbose.VerboseCheckTarget;
import me.lucko.luckperms.common.verbose.VerboseHandler;
import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent;
import me.lucko.luckperms.common.verbose.event.CheckOrigin;
import net.luckperms.api.util.Tristate;
@@ -51,7 +52,7 @@ import java.util.Set;
public class MonitoredPermissibleBase extends PermissibleBase {
final LuckPermsPlugin plugin;
private final PermissibleBase delegate;
private final String name;
private final VerboseCheckTarget verboseCheckTarget;
// remains false until the object has been constructed
// necessary to catch the superclass call to #recalculatePermissions on init
@@ -64,12 +65,12 @@ public class MonitoredPermissibleBase extends PermissibleBase {
this.plugin = plugin;
this.delegate = delegate;
this.name = name;
this.verboseCheckTarget = VerboseCheckTarget.internal(name);
this.initialised = true;
}
private void logCheck(PermissionCheckEvent.Origin origin, String permission, boolean result) {
this.plugin.getVerboseHandler().offerPermissionCheckEvent(origin, this.name, QueryOptionsImpl.DEFAULT_CONTEXTUAL, permission, TristateResult.of(Tristate.of(result)));
private void logCheck(CheckOrigin origin, String permission, boolean result) {
this.plugin.getVerboseHandler().offerPermissionCheckEvent(origin, this.verboseCheckTarget, QueryOptionsImpl.DEFAULT_CONTEXTUAL, permission, TristateResult.forMonitoredResult(Tristate.of(result)));
this.plugin.getPermissionRegistry().offer(permission);
}
@@ -84,7 +85,7 @@ public class MonitoredPermissibleBase extends PermissibleBase {
}
final boolean result = this.delegate.isPermissionSet(permission);
logCheck(PermissionCheckEvent.Origin.PLATFORM_LOOKUP_CHECK, permission, result);
logCheck(CheckOrigin.PLATFORM_API_HAS_PERMISSION_SET, permission, result);
return result;
}
@@ -95,7 +96,7 @@ public class MonitoredPermissibleBase extends PermissibleBase {
}
final boolean result = this.delegate.isPermissionSet(permission);
logCheck(PermissionCheckEvent.Origin.PLATFORM_LOOKUP_CHECK, permission.getName(), result);
logCheck(CheckOrigin.PLATFORM_API_HAS_PERMISSION_SET, permission.getName(), result);
return result;
}
@@ -106,7 +107,7 @@ public class MonitoredPermissibleBase extends PermissibleBase {
}
final boolean result = this.delegate.hasPermission(permission);
logCheck(PermissionCheckEvent.Origin.PLATFORM_PERMISSION_CHECK, permission, result);
logCheck(CheckOrigin.PLATFORM_API_HAS_PERMISSION, permission, result);
return result;
}
@@ -117,7 +118,7 @@ public class MonitoredPermissibleBase extends PermissibleBase {
}
final boolean result = this.delegate.hasPermission(permission);
logCheck(PermissionCheckEvent.Origin.PLATFORM_PERMISSION_CHECK, permission.getName(), result);
logCheck(CheckOrigin.PLATFORM_API_HAS_PERMISSION, permission.getName(), result);
return result;
}
@@ -26,6 +26,7 @@
package me.lucko.luckperms.bukkit.inject.permissible;
import me.lucko.luckperms.bukkit.util.CraftBukkitImplementation;
import me.lucko.luckperms.common.plugin.logging.PluginLogger;
import org.bukkit.entity.Player;
import org.bukkit.permissions.PermissibleBase;
@@ -84,9 +85,10 @@ public final class PermissibleInjector {
*
* @param player the player to inject into
* @param newPermissible the permissible to inject
* @param logger the plugin logger
* @throws Exception propagates any exceptions which were thrown during injection
*/
public static void inject(Player player, LuckPermsPermissible newPermissible) throws Exception {
public static void inject(Player player, LuckPermsPermissible newPermissible, PluginLogger logger) throws Exception {
// get the existing PermissibleBase held by the player
PermissibleBase oldPermissible = (PermissibleBase) HUMAN_ENTITY_PERMISSIBLE_FIELD.get(player);
@@ -96,6 +98,15 @@ public final class PermissibleInjector {
throw new IllegalStateException("LPPermissible already injected into player " + player.toString());
}
Class<? extends PermissibleBase> oldClass = oldPermissible.getClass();
if (!PermissibleBase.class.equals(oldClass)) {
logger.warn("Player " + player.getName() + " already has a custom permissible (" + oldClass.getName() + ")!\n" +
"This is probably because you have multiple permission plugins installed.\n" +
"Please make sure that LuckPerms is the only permission plugin installed on your server!\n" +
"(unless you're performing a migration, in which case, just remember to remove your old " +
"permission plugin once you're done!)");
}
// Move attachments over from the old permissible
//noinspection unchecked
@@ -151,4 +162,24 @@ public final class PermissibleInjector {
}
}
public static void checkInjected(Player player, PluginLogger logger) {
PermissibleBase permissibleBase;
try {
permissibleBase = (PermissibleBase) HUMAN_ENTITY_PERMISSIBLE_FIELD.get(player);
} catch (IllegalAccessException e) {
return; // ignore
}
if (permissibleBase instanceof LuckPermsPermissible) {
return; // all gucci
}
Class<? extends PermissibleBase> clazz = permissibleBase.getClass();
logger.warn("Player " + player.getName() + " has a non-LuckPerms permissible (" + clazz.getName() + ")!\n" +
"This is probably because you have multiple permission plugins installed.\n" +
"Please make sure that LuckPerms is the only permission plugin installed on your server!\n" +
"(unless you're performing a migration, in which case, just remember to remove your old " +
"permission plugin once you're done!)");
}
}
@@ -43,6 +43,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -140,7 +141,7 @@ public final class LuckPermsDefaultsMap implements Map<Boolean, Set<Permission>>
protected @NonNull Map<String, Boolean> supply() {
Map<String, Boolean> builder = new HashMap<>();
for (Permission perm : LuckPermsDefaultsMap.this.get(this.op)) {
String name = perm.getName().toLowerCase();
String name = perm.getName().toLowerCase(Locale.ROOT);
builder.put(name, true);
for (Map.Entry<String, Boolean> child : LuckPermsDefaultsMap.this.plugin.getPermissionMap().getChildPermissions(name, true).entrySet()) {
builder.putIfAbsent(child.getKey(), child.getValue());
@@ -40,6 +40,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
@@ -180,7 +181,7 @@ public final class LuckPermsPermissionMap extends ForwardingMap<String, Permissi
continue;
}
String key = e.getKey().toLowerCase();
String key = e.getKey().toLowerCase(Locale.ROOT);
if (accumulator.containsKey(key)) {
continue; // Prevent infinite loops
@@ -241,6 +242,10 @@ public final class LuckPermsPermissionMap extends ForwardingMap<String, Permissi
NotifyingChildrenMap(Map<String, Boolean> delegate) {
this.delegate = delegate;
for (String key : this.delegate.keySet()) {
LuckPermsPermissionMap.this.plugin.getPermissionRegistry().insert(key);
}
}
@Override
@@ -251,6 +256,7 @@ public final class LuckPermsPermissionMap extends ForwardingMap<String, Permissi
@Override
public Boolean put(@NonNull String key, @NonNull Boolean value) {
Boolean ret = super.put(key, value);
LuckPermsPermissionMap.this.plugin.getPermissionRegistry().insert(key);
LuckPermsPermissionMap.this.update();
return ret;
}
@@ -258,6 +264,9 @@ public final class LuckPermsPermissionMap extends ForwardingMap<String, Permissi
@Override
public void putAll(@NonNull Map<? extends String, ? extends Boolean> map) {
super.putAll(map);
for (String key : map.keySet()) {
LuckPermsPermissionMap.this.plugin.getPermissionRegistry().insert(key);
}
LuckPermsPermissionMap.this.update();
}
@@ -25,10 +25,7 @@
package me.lucko.luckperms.bukkit.inject.server;
import com.google.common.collect.Maps;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.common.util.ImmutableCollectors;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permissible;
@@ -40,10 +37,8 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.function.Function;
/**
* A replacement map for the 'permSubs' instance in Bukkit's SimplePluginManager.
@@ -67,15 +62,17 @@ import java.util.function.Function;
*
* Injected by {@link InjectorSubscriptionMap}.
*/
public final class LuckPermsSubscriptionMap extends HashMap<String, Map<Permissible, Boolean>> {
public final class LuckPermsSubscriptionMap implements Map<String, Map<Permissible, Boolean>> {
// the plugin instance
final LPBukkitPlugin plugin;
private final Map<Permissible, Set<String>> subscriptions = Collections.synchronizedMap(new WeakHashMap<>());
public LuckPermsSubscriptionMap(LPBukkitPlugin plugin, Map<String, Map<Permissible, Boolean>> existingData) {
this.plugin = plugin;
for (Entry<String, Map<Permissible, Boolean>> entry : existingData.entrySet()) {
super.put(entry.getKey(), new LPSubscriptionValueMap(entry.getKey(), entry.getValue()));
entry.getValue().keySet().forEach(permissible -> subscribe(permissible, entry.getKey()));
}
}
@@ -84,61 +81,55 @@ public final class LuckPermsSubscriptionMap extends HashMap<String, Map<Permissi
* we override it to always return a value - which means the null check in
* subscribeToDefaultPerms always fails - soo, we don't have to worry too much
* about implementing #put.
*
* we also ensure all returns are LPSubscriptionValueMaps. this extension
* will also delegate checks to online players - meaning we don't ever
* have to register their subscriptions with the plugin manager.
*/
@Override
public Map<Permissible, Boolean> get(Object key) {
if (key == null || !(key instanceof String)) {
return null;
return new ValueMap((String) key);
}
public void subscribe(Permissible permissible, String permission) {
// don't allow players to be put into this map
if (permissible instanceof Player) {
return;
}
String permission = (String) key;
Set<String> perms = this.subscriptions.computeIfAbsent(permissible, x -> Collections.synchronizedSet(new HashSet<>()));
perms.add(permission);
}
LPSubscriptionValueMap result = (LPSubscriptionValueMap) super.get(key);
if (result == null) {
// calculate a new map - always!
result = new LPSubscriptionValueMap(permission);
super.put(permission, result);
public boolean unsubscribe(Permissible permissible, String permission) {
if (permissible instanceof Player) {
return false; // ignore calls for players
}
return result;
}
Set<String> perms = this.subscriptions.get(permissible);
@Override
public Map<Permissible, Boolean> put(String key, Map<Permissible, Boolean> value) {
if (value == null) {
throw new NullPointerException("Map value cannot be null");
if (perms == null) {
return false;
}
// ensure values are LP subscription maps
if (!(value instanceof LPSubscriptionValueMap)) {
value = new LPSubscriptionValueMap(key, value);
return perms.remove(permission);
}
public @NonNull Set<Permissible> subscribers(String permission) {
Collection<? extends Player> onlinePlayers = this.plugin.getBootstrap().getServer().getOnlinePlayers();
Set<Permissible> set = new HashSet<>(onlinePlayers.size() + this.subscriptions.size());
// add permissibles from the subscriptions map
this.subscriptions.forEach((permissible, perms) -> {
if (perms.contains(permission)) {
set.add(permissible);
}
});
// add any online players who meet requirements
for (Player player : onlinePlayers) {
if (player.hasPermission(permission) || player.isPermissionSet(permission)) {
set.add(player);
}
}
return super.put(key, value);
}
@Override
public void putAll(Map<? extends String, ? extends Map<Permissible, Boolean>> m) {
m.forEach(this::put);
}
@Override
public Map<Permissible, Boolean> putIfAbsent(String key, Map<Permissible, Boolean> value) {
return get(key);
}
@Override
public Map<Permissible, Boolean> computeIfAbsent(String key, Function<? super String, ? extends Map<Permissible, Boolean>> mappingFunction) {
return get(key);
}
// if the key isn't null and is a string, #get will always return a value for it
@Override
public boolean containsKey(Object key) {
return key != null && key instanceof String;
return set;
}
/**
@@ -148,109 +139,53 @@ public final class LuckPermsSubscriptionMap extends HashMap<String, Map<Permissi
*/
public Map<String, Map<Permissible, Boolean>> detach() {
Map<String, Map<Permissible, Boolean>> map = new HashMap<>();
for (Map.Entry<String, Map<Permissible, Boolean>> ent : entrySet()) {
map.put(ent.getKey(), new WeakHashMap<>(((LPSubscriptionValueMap) ent.getValue()).backing));
}
this.subscriptions.forEach((permissible, perms) -> {
for (String perm : perms) {
map.computeIfAbsent(perm, x -> new WeakHashMap<>()).put(permissible, true);
}
});
return map;
}
@Override public Map<Permissible, Boolean> put(String key, Map<Permissible, Boolean> value) { throw new UnsupportedOperationException(); }
@Override public Map<Permissible, Boolean> remove(Object key) { throw new UnsupportedOperationException(); }
@Override public void putAll(Map<? extends String, ? extends Map<Permissible, Boolean>> m) { throw new UnsupportedOperationException(); }
@Override public void clear() { throw new UnsupportedOperationException(); }
@Override public Set<String> keySet() { throw new UnsupportedOperationException(); }
@Override public Collection<Map<Permissible, Boolean>> values() { throw new UnsupportedOperationException(); }
@Override public Set<Entry<String, Map<Permissible, Boolean>>> entrySet() { throw new UnsupportedOperationException(); }
@Override public int size() { return 0; }
@Override public boolean isEmpty() { throw new UnsupportedOperationException(); }
@Override public boolean containsKey(Object key) { throw new UnsupportedOperationException(); }
@Override public boolean containsValue(Object value) { throw new UnsupportedOperationException(); }
/**
* Value map extension which includes LP objects in Permissible related queries.
*/
public final class LPSubscriptionValueMap implements Map<Permissible, Boolean> {
public final class ValueMap implements Map<Permissible, Boolean> {
// the permission being mapped to this value map
private final String permission;
// the backing map
private final Map<Permissible, Boolean> backing;
private LPSubscriptionValueMap(String permission, Map<Permissible, Boolean> backing) {
public ValueMap(String permission) {
this.permission = permission;
this.backing = Collections.synchronizedMap(new WeakHashMap<>(backing));
// remove all players from the map
this.backing.keySet().removeIf(p -> p instanceof Player);
}
public LPSubscriptionValueMap(String permission) {
this.permission = permission;
this.backing = Collections.synchronizedMap(new WeakHashMap<>());
}
@Override
public Boolean get(Object key) {
boolean isPlayer = key instanceof Player;
// if the key is a player, check their LPPermissible first
if (isPlayer) {
Permissible p = (Permissible) key;
if (p.hasPermission(this.permission)) {
return true;
}
}
// then try the map
Boolean result = this.backing.get(key);
if (result != null) {
return result;
}
// then try the permissible, if we haven't already
if (!isPlayer && key instanceof Permissible) {
Permissible p = (Permissible) key;
if (p.hasPermission(this.permission)) {
return true;
}
}
// no result
return null;
}
@Override
public Boolean put(Permissible key, Boolean value) {
// don't allow players to be put into this map
if (key instanceof Player) {
return true;
}
return this.backing.put(key, value);
subscribe(key, this.permission);
return null;
}
@Override
public boolean containsKey(Object key) {
// delegate through the get method
return get(key) != null;
public Boolean remove(Object k) {
Permissible key = (Permissible) k;
return unsubscribe(key, this.permission) ? true : null;
}
@Override
public @NonNull Set<Permissible> keySet() {
// start with the backing set
Set<Permissible> set;
synchronized (this.backing) {
set = new HashSet<>(this.backing.keySet());
}
// add any online players who meet requirements
for (Player player : LuckPermsSubscriptionMap.this.plugin.getBootstrap().getServer().getOnlinePlayers()) {
if (player.hasPermission(this.permission) || player.isPermissionSet(this.permission)) {
set.add(player);
}
}
return set;
}
@Override
public @NonNull Set<Entry<Permissible, Boolean>> entrySet() {
return keySet().stream()
.map(key -> {
Boolean value = get(key);
return value != null ? Maps.immutableEntry(key, value) : null;
})
.filter(Objects::nonNull)
.collect(ImmutableCollectors.toSet());
return subscribers(this.permission);
}
@Override
@@ -262,34 +197,15 @@ public final class LuckPermsSubscriptionMap extends HashMap<String, Map<Permissi
@Override
public int size() {
return Math.max(1, this.backing.size());
return 1;
}
// just delegate to the backing map
@Override
public Boolean remove(Object key) {
return this.backing.remove(key);
}
@Override
public boolean containsValue(Object value) {
return this.backing.containsValue(value);
}
@Override
public void putAll(@NonNull Map<? extends Permissible, ? extends Boolean> m) {
this.backing.putAll(m);
}
@Override
public void clear() {
this.backing.clear();
}
@Override
public @NonNull Collection<Boolean> values() {
return this.backing.values();
}
@Override public void putAll(Map<? extends Permissible, ? extends Boolean> m) { throw new UnsupportedOperationException(); }
@Override public void clear() { throw new UnsupportedOperationException(); }
@Override public Collection<Boolean> values() { throw new UnsupportedOperationException(); }
@Override public Set<Entry<Permissible, Boolean>> entrySet() { throw new UnsupportedOperationException(); }
@Override public boolean containsKey(Object key) { throw new UnsupportedOperationException(); }
@Override public boolean containsValue(Object value) { throw new UnsupportedOperationException(); }
@Override public Boolean get(Object key) { throw new UnsupportedOperationException(); }
}
}
@@ -203,7 +203,7 @@ public class BukkitConnectionListener extends AbstractConnectionListener impleme
LuckPermsPermissible lpPermissible = new LuckPermsPermissible(player, user, this.plugin);
// Inject into the player
PermissibleInjector.inject(player, lpPermissible);
PermissibleInjector.inject(player, lpPermissible, this.plugin.getLogger());
} catch (Throwable t) {
this.plugin.getLogger().warn("Exception thrown when setting up permissions for " +
@@ -230,6 +230,8 @@ public class BukkitConnectionListener extends AbstractConnectionListener impleme
e.disallow(PlayerLoginEvent.Result.KICK_OTHER, "");
}
}
PermissibleInjector.checkInjected(e.getPlayer(), this.plugin.getLogger());
}
// Wait until the last priority to unload, so plugins can still perform permission checks on this event
@@ -38,6 +38,7 @@ import org.bukkit.event.server.PluginEnableEvent;
import org.bukkit.event.server.RemoteServerCommandEvent;
import org.bukkit.event.server.ServerCommandEvent;
import java.util.Locale;
import java.util.regex.Pattern;
public class BukkitPlatformListener implements Listener {
@@ -51,17 +52,17 @@ public class BukkitPlatformListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onPlayerCommand(PlayerCommandPreprocessEvent e) {
handleCommand(e.getPlayer(), e.getMessage().toLowerCase(), e);
handleCommand(e.getPlayer(), e.getMessage().toLowerCase(Locale.ROOT), e);
}
@EventHandler(ignoreCancelled = true)
public void onServerCommand(ServerCommandEvent e) {
handleCommand(e.getSender(), e.getCommand().toLowerCase(), e);
handleCommand(e.getSender(), e.getCommand().toLowerCase(Locale.ROOT), e);
}
@EventHandler(ignoreCancelled = true)
public void onRemoteServerCommand(RemoteServerCommandEvent e) {
handleCommand(e.getSender(), e.getCommand().toLowerCase(), e);
handleCommand(e.getSender(), e.getCommand().toLowerCase(Locale.ROOT), e);
}
private void handleCommand(CommandSender sender, String cmdLine, Cancellable event) {
@@ -47,7 +47,7 @@ public final class PlayerLocaleUtil {
Method legacyMethod = Player.Spigot.class.getMethod("getLocale");
function = player -> {
try {
return (String) legacyMethod.invoke(player);
return (String) legacyMethod.invoke(player.spigot());
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
@@ -79,6 +79,8 @@ public abstract class AbstractVaultPermission extends Permission {
public abstract boolean userHasPermission(String world, UUID uuid, String permission);
public abstract boolean userAddPermission(String world, UUID uuid, String permission);
public abstract boolean userRemovePermission(String world, UUID uuid, String permission);
public abstract boolean userAddTransient(String world, UUID uuid, String permission);
public abstract boolean userRemoveTransient(String world, UUID uuid, String permission);
public abstract boolean userInGroup(String world, UUID uuid, String group);
public abstract boolean userAddGroup(String world, UUID uuid, String group);
public abstract boolean userRemoveGroup(String world, UUID uuid, String group);
@@ -181,7 +183,7 @@ public abstract class AbstractVaultPermission extends Permission {
public final boolean playerAdd(Player player, String permission) {
Objects.requireNonNull(player, "player");
Objects.requireNonNull(permission, "permission");
return userAddPermission(convertWorld(player), ((OfflinePlayer) player).getUniqueId(), permission);
return userAddPermission(convertWorld(player), player.getUniqueId(), permission);
}
@Override
@@ -209,7 +211,63 @@ public abstract class AbstractVaultPermission extends Permission {
public final boolean playerRemove(Player player, String permission) {
Objects.requireNonNull(player, "player");
Objects.requireNonNull(permission, "permission");
return userRemovePermission(convertWorld(player), ((OfflinePlayer) player).getUniqueId(), permission);
return userRemovePermission(convertWorld(player), player.getUniqueId(), permission);
}
@Override
public boolean playerAddTransient(OfflinePlayer player, String permission) {
Objects.requireNonNull(player, "player");
Objects.requireNonNull(permission, "permission");
return userAddTransient(null, player.getUniqueId(), permission);
}
@Override
public final boolean playerAddTransient(Player player, String permission) {
Objects.requireNonNull(player, "player");
Objects.requireNonNull(permission, "permission");
return userAddTransient(convertWorld(player), player.getUniqueId(), permission);
}
@Override
public final boolean playerAddTransient(String world, OfflinePlayer player, String permission) {
Objects.requireNonNull(player, "player");
Objects.requireNonNull(permission, "permission");
return userAddTransient(convertWorld(world), player.getUniqueId(), permission);
}
@Override
public final boolean playerAddTransient(String world, Player player, String permission) {
Objects.requireNonNull(player, "player");
Objects.requireNonNull(permission, "permission");
return userAddTransient(convertWorld(world), player.getUniqueId(), permission);
}
@Override
public boolean playerRemoveTransient(OfflinePlayer player, String permission) {
Objects.requireNonNull(player, "player");
Objects.requireNonNull(permission, "permission");
return userRemoveTransient(null, player.getUniqueId(), permission);
}
@Override
public final boolean playerRemoveTransient(Player player, String permission) {
Objects.requireNonNull(player, "player");
Objects.requireNonNull(permission, "permission");
return userRemoveTransient(convertWorld(player), player.getUniqueId(), permission);
}
@Override
public final boolean playerRemoveTransient(String world, OfflinePlayer player, String permission) {
Objects.requireNonNull(player, "player");
Objects.requireNonNull(permission, "permission");
return userRemoveTransient(convertWorld(world), player.getUniqueId(), permission);
}
@Override
public final boolean playerRemoveTransient(String world, Player player, String permission) {
Objects.requireNonNull(player, "player");
Objects.requireNonNull(permission, "permission");
return userRemoveTransient(convertWorld(world), player.getUniqueId(), permission);
}
@Override
@@ -279,7 +337,7 @@ public abstract class AbstractVaultPermission extends Permission {
public final boolean playerInGroup(Player player, String group) {
Objects.requireNonNull(player, "player");
Objects.requireNonNull(group, "group");
return userInGroup(convertWorld(player), ((OfflinePlayer) player).getUniqueId(), group);
return userInGroup(convertWorld(player), player.getUniqueId(), group);
}
@Override
@@ -307,7 +365,7 @@ public abstract class AbstractVaultPermission extends Permission {
public final boolean playerAddGroup(Player player, String group) {
Objects.requireNonNull(player, "player");
Objects.requireNonNull(group, "group");
return userAddGroup(convertWorld(player), ((OfflinePlayer) player).getUniqueId(), group);
return userAddGroup(convertWorld(player), player.getUniqueId(), group);
}
@Override
@@ -334,7 +392,7 @@ public abstract class AbstractVaultPermission extends Permission {
@Override
public final boolean playerRemoveGroup(Player player, String group) {
Objects.requireNonNull(player, "player");
return userRemoveGroup(convertWorld(player), ((OfflinePlayer) player).getUniqueId(), group);
return userRemoveGroup(convertWorld(player), player.getUniqueId(), group);
}
@Override
@@ -358,7 +416,7 @@ public abstract class AbstractVaultPermission extends Permission {
@Override
public final String[] getPlayerGroups(Player player) {
Objects.requireNonNull(player, "player");
return userGetGroups(convertWorld(player), ((OfflinePlayer) player).getUniqueId());
return userGetGroups(convertWorld(player), player.getUniqueId());
}
@Override
@@ -382,7 +440,7 @@ public abstract class AbstractVaultPermission extends Permission {
@Override
public final String getPrimaryGroup(Player player) {
Objects.requireNonNull(player, "player");
return userGetPrimaryGroup(convertWorld(player), ((OfflinePlayer) player).getUniqueId());
return userGetPrimaryGroup(convertWorld(player), player.getUniqueId());
}
}
@@ -30,14 +30,15 @@ import com.google.common.base.Strings;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.common.cacheddata.type.MetaAccumulator;
import me.lucko.luckperms.common.cacheddata.type.MetaCache;
import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl;
import me.lucko.luckperms.common.cacheddata.type.MonitoredMetaCache;
import me.lucko.luckperms.common.context.ImmutableContextSetImpl;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.PermissionHolder;
import me.lucko.luckperms.common.node.types.Meta;
import me.lucko.luckperms.common.node.types.Prefix;
import me.lucko.luckperms.common.node.types.Suffix;
import me.lucko.luckperms.common.query.QueryOptionsImpl;
import me.lucko.luckperms.common.verbose.event.MetaCheckEvent;
import me.lucko.luckperms.common.verbose.event.CheckOrigin;
import net.luckperms.api.context.DefaultContextKeys;
import net.luckperms.api.context.ImmutableContextSet;
@@ -49,6 +50,7 @@ import net.luckperms.api.query.Flag;
import net.luckperms.api.query.QueryOptions;
import net.milkbowl.vault.chat.Chat;
import java.util.Locale;
import java.util.Objects;
import java.util.UUID;
@@ -96,7 +98,7 @@ public class LuckPermsVaultChat extends AbstractVaultChat {
PermissionHolder user = this.vaultPermission.lookupUser(uuid);
QueryOptions queryOptions = this.vaultPermission.getQueryOptions(uuid, world);
MetaCache metaData = user.getCachedData().getMetaData(queryOptions);
return Strings.nullToEmpty(metaData.getPrefix(MetaCheckEvent.Origin.THIRD_PARTY_API));
return Strings.nullToEmpty(metaData.getPrefix(CheckOrigin.THIRD_PARTY_API).result());
}
@Override
@@ -106,7 +108,7 @@ public class LuckPermsVaultChat extends AbstractVaultChat {
PermissionHolder user = this.vaultPermission.lookupUser(uuid);
QueryOptions queryOptions = this.vaultPermission.getQueryOptions(uuid, world);
MetaCache metaData = user.getCachedData().getMetaData(queryOptions);
return Strings.nullToEmpty(metaData.getSuffix(MetaCheckEvent.Origin.THIRD_PARTY_API));
return Strings.nullToEmpty(metaData.getSuffix(CheckOrigin.THIRD_PARTY_API).result());
}
@Override
@@ -138,8 +140,8 @@ public class LuckPermsVaultChat extends AbstractVaultChat {
PermissionHolder user = this.vaultPermission.lookupUser(uuid);
QueryOptions queryOptions = this.vaultPermission.getQueryOptions(uuid, world);
MetaCache metaData = user.getCachedData().getMetaData(queryOptions);
return metaData.getMetaValue(key, MetaCheckEvent.Origin.THIRD_PARTY_API);
MonitoredMetaCache metaData = user.getCachedData().getMetaData(queryOptions);
return metaData.getMetaValue(key, CheckOrigin.THIRD_PARTY_API).result();
}
@Override
@@ -157,21 +159,21 @@ public class LuckPermsVaultChat extends AbstractVaultChat {
@Override
public String getGroupChatPrefix(String world, String name) {
Objects.requireNonNull(name, "name");
MetaCache metaData = getGroupMetaCache(name, world);
MonitoredMetaCache metaData = getGroupMetaCache(name, world);
if (metaData == null) {
return null;
}
return Strings.nullToEmpty(metaData.getPrefix(MetaCheckEvent.Origin.THIRD_PARTY_API));
return Strings.nullToEmpty(metaData.getPrefix(CheckOrigin.THIRD_PARTY_API).result());
}
@Override
public String getGroupChatSuffix(String world, String name) {
Objects.requireNonNull(name, "name");
MetaCache metaData = getGroupMetaCache(name, world);
MonitoredMetaCache metaData = getGroupMetaCache(name, world);
if (metaData == null) {
return null;
}
return Strings.nullToEmpty(metaData.getSuffix(MetaCheckEvent.Origin.THIRD_PARTY_API));
return Strings.nullToEmpty(metaData.getSuffix(CheckOrigin.THIRD_PARTY_API).result());
}
@Override
@@ -198,11 +200,11 @@ public class LuckPermsVaultChat extends AbstractVaultChat {
public String getGroupMeta(String world, String name, String key) {
Objects.requireNonNull(name, "name");
Objects.requireNonNull(key, "key");
MetaCache metaData = getGroupMetaCache(name, world);
MonitoredMetaCache metaData = getGroupMetaCache(name, world);
if (metaData == null) {
return null;
}
return metaData.getMetaValue(key, MetaCheckEvent.Origin.THIRD_PARTY_API);
return metaData.getMetaValue(key, CheckOrigin.THIRD_PARTY_API).result();
}
@Override
@@ -222,7 +224,7 @@ public class LuckPermsVaultChat extends AbstractVaultChat {
return this.plugin.getGroupManager().getByDisplayName(name);
}
private MetaCache getGroupMetaCache(String name, String world) {
private MonitoredMetaCache getGroupMetaCache(String name, String world) {
Group group = getGroup(name);
if (group == null) {
return null;
@@ -254,7 +256,7 @@ public class LuckPermsVaultChat extends AbstractVaultChat {
private void setMeta(PermissionHolder holder, String key, Object value, String world) {
if (key.equalsIgnoreCase(Prefix.NODE_KEY) || key.equalsIgnoreCase(Suffix.NODE_KEY)) {
setChatMeta(holder, ChatMetaType.valueOf(key.toUpperCase()), value == null ? null : value.toString(), world);
setChatMeta(holder, ChatMetaType.valueOf(key.toUpperCase(Locale.ROOT)), value == null ? null : value.toString(), world);
return;
}
@@ -277,7 +279,7 @@ public class LuckPermsVaultChat extends AbstractVaultChat {
private QueryOptions createQueryOptionsForWorldSet(String world) {
ImmutableContextSet.Builder context = new ImmutableContextSetImpl.BuilderImpl();
if (world != null && !world.isEmpty() && !world.equalsIgnoreCase("global")) {
context.add(DefaultContextKeys.WORLD_KEY, world.toLowerCase());
context.add(DefaultContextKeys.WORLD_KEY, world.toLowerCase(Locale.ROOT));
}
context.add(DefaultContextKeys.SERVER_KEY, this.vaultPermission.getVaultServer());
@@ -29,10 +29,10 @@ import com.google.common.base.Preconditions;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.bukkit.context.BukkitContextManager;
import me.lucko.luckperms.common.cacheddata.type.MetaCache;
import me.lucko.luckperms.common.cacheddata.result.TristateResult;
import me.lucko.luckperms.common.cacheddata.type.MonitoredMetaCache;
import me.lucko.luckperms.common.cacheddata.type.PermissionCache;
import me.lucko.luckperms.common.calculator.processor.DirectProcessor;
import me.lucko.luckperms.common.calculator.result.TristateResult;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.HolderType;
@@ -44,9 +44,9 @@ import me.lucko.luckperms.common.node.types.Inheritance;
import me.lucko.luckperms.common.query.QueryOptionsImpl;
import me.lucko.luckperms.common.util.UniqueIdType;
import me.lucko.luckperms.common.util.Uuids;
import me.lucko.luckperms.common.verbose.event.MetaCheckEvent;
import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent;
import me.lucko.luckperms.common.verbose.event.CheckOrigin;
import net.luckperms.api.context.ContextSet;
import net.luckperms.api.context.DefaultContextKeys;
import net.luckperms.api.context.MutableContextSet;
import net.luckperms.api.model.data.DataType;
@@ -59,6 +59,7 @@ import net.milkbowl.vault.permission.Permission;
import org.bukkit.entity.Player;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
@@ -173,7 +174,7 @@ public class LuckPermsVaultPermission extends AbstractVaultPermission {
PermissionHolder user = lookupUser(uuid);
QueryOptions queryOptions = getQueryOptions(uuid, world);
PermissionCache permissionData = user.getCachedData().getPermissionData(queryOptions);
return permissionData.checkPermission(permission, PermissionCheckEvent.Origin.THIRD_PARTY_API).result().asBoolean();
return permissionData.checkPermission(permission, CheckOrigin.THIRD_PARTY_API).result().asBoolean();
}
@Override
@@ -185,7 +186,7 @@ public class LuckPermsVaultPermission extends AbstractVaultPermission {
if (user instanceof Group) {
throw new UnsupportedOperationException("Unable to modify the permissions of NPC players");
}
return holderAddPermission(user, permission, world);
return holderAddPermission(user, permission, world, DataType.NORMAL);
}
@Override
@@ -197,7 +198,31 @@ public class LuckPermsVaultPermission extends AbstractVaultPermission {
if (user instanceof Group) {
throw new UnsupportedOperationException("Unable to modify the permissions of NPC players");
}
return holderRemovePermission(user, permission, world);
return holderRemovePermission(user, permission, world, DataType.NORMAL);
}
@Override
public boolean userAddTransient(String world, UUID uuid, String permission) {
Objects.requireNonNull(uuid, "uuid");
Objects.requireNonNull(permission, "permission");
PermissionHolder user = lookupUser(uuid);
if (user instanceof Group) {
throw new UnsupportedOperationException("Unable to modify the permissions of NPC players");
}
return holderAddPermission(user, permission, world, DataType.TRANSIENT);
}
@Override
public boolean userRemoveTransient(String world, UUID uuid, String permission) {
Objects.requireNonNull(uuid, "uuid");
Objects.requireNonNull(permission, "permission");
PermissionHolder user = lookupUser(uuid);
if (user instanceof Group) {
throw new UnsupportedOperationException("Unable to modify the permissions of NPC players");
}
return holderRemovePermission(user, permission, world, DataType.TRANSIENT);
}
@Override
@@ -209,7 +234,7 @@ public class LuckPermsVaultPermission extends AbstractVaultPermission {
QueryOptions queryOptions = getQueryOptions(uuid, world);
PermissionCache permissionData = user.getCachedData().getPermissionData(queryOptions);
TristateResult result = permissionData.checkPermission(Inheritance.key(rewriteGroupName(group)), PermissionCheckEvent.Origin.THIRD_PARTY_API);
TristateResult result = permissionData.checkPermission(Inheritance.key(rewriteGroupName(group)), CheckOrigin.THIRD_PARTY_API);
return result.processorClass() == DirectProcessor.class && result.result().asBoolean();
}
@@ -252,8 +277,11 @@ public class LuckPermsVaultPermission extends AbstractVaultPermission {
}
QueryOptions queryOptions = getQueryOptions(uuid, world);
MetaCache metaData = user.getCachedData().getMetaData(queryOptions);
String value = metaData.getPrimaryGroup(MetaCheckEvent.Origin.THIRD_PARTY_API);
MonitoredMetaCache metaData = user.getCachedData().getMetaData(queryOptions);
String value = metaData.getPrimaryGroup(CheckOrigin.THIRD_PARTY_API);
if (value == null) {
return null;
}
Group group = getGroup(value);
return group != null ? groupName(group) : value;
@@ -271,7 +299,7 @@ public class LuckPermsVaultPermission extends AbstractVaultPermission {
QueryOptions queryOptions = getQueryOptions(null, world);
PermissionCache permissionData = group.getCachedData().getPermissionData(queryOptions);
return permissionData.checkPermission(permission, PermissionCheckEvent.Origin.THIRD_PARTY_API).result().asBoolean();
return permissionData.checkPermission(permission, CheckOrigin.THIRD_PARTY_API).result().asBoolean();
}
@Override
@@ -284,7 +312,7 @@ public class LuckPermsVaultPermission extends AbstractVaultPermission {
return false;
}
return holderAddPermission(group, permission, world);
return holderAddPermission(group, permission, world, DataType.NORMAL);
}
@Override
@@ -297,7 +325,7 @@ public class LuckPermsVaultPermission extends AbstractVaultPermission {
return false;
}
return holderRemovePermission(group, permission, world);
return holderRemovePermission(group, permission, world, DataType.NORMAL);
}
// utility methods for getting user and group instances
@@ -328,13 +356,13 @@ public class LuckPermsVaultPermission extends AbstractVaultPermission {
// utility method for getting a contexts instance for a given vault lookup.
QueryOptions getQueryOptions(@Nullable UUID uuid, @Nullable String world) {
MutableContextSet context;
ContextSet context;
Player player = Optional.ofNullable(uuid).flatMap(u -> this.plugin.getBootstrap().getPlayer(u)).orElse(null);
if (player != null) {
context = this.plugin.getContextManager().getContext(player).mutableCopy();
context = this.plugin.getContextManager().getContext(player);
} else {
context = this.plugin.getContextManager().getStaticContext().mutableCopy();
context = this.plugin.getContextManager().getStaticContext();
}
String playerWorld = player == null ? null : player.getWorld().getName();
@@ -342,20 +370,26 @@ public class LuckPermsVaultPermission extends AbstractVaultPermission {
// if world is null, we want to do a lookup in the players current context
// if world is not null, we want to do a lookup in that specific world
if (world != null && !world.isEmpty() && !world.equalsIgnoreCase(playerWorld)) {
MutableContextSet mutContext = context.mutableCopy();
context = mutContext;
// remove already accumulated worlds
context.removeAll(DefaultContextKeys.WORLD_KEY);
mutContext.removeAll(DefaultContextKeys.WORLD_KEY);
// add the vault world
context.add(DefaultContextKeys.WORLD_KEY, world.toLowerCase());
mutContext.add(DefaultContextKeys.WORLD_KEY, world.toLowerCase(Locale.ROOT));
}
// if we're using a special vault server
if (useVaultServer()) {
MutableContextSet mutContext = context instanceof MutableContextSet ? (MutableContextSet) context : context.mutableCopy();
context = mutContext;
// remove the normal server context from the set
context.remove(DefaultContextKeys.SERVER_KEY, getServer());
mutContext.remove(DefaultContextKeys.SERVER_KEY, getServer());
// add the vault specific server
if (!getVaultServer().equals("global")) {
context.add(DefaultContextKeys.SERVER_KEY, getVaultServer());
mutContext.add(DefaultContextKeys.SERVER_KEY, getVaultServer());
}
}
@@ -377,7 +411,7 @@ public class LuckPermsVaultPermission extends AbstractVaultPermission {
// utility methods for modifying the state of PermissionHolders
private boolean holderAddPermission(PermissionHolder holder, String permission, String world) {
private boolean holderAddPermission(PermissionHolder holder, String permission, String world, DataType type) {
Objects.requireNonNull(permission, "permission is null");
Preconditions.checkArgument(!permission.isEmpty(), "permission is an empty string");
@@ -386,13 +420,13 @@ public class LuckPermsVaultPermission extends AbstractVaultPermission {
.withContext(DefaultContextKeys.WORLD_KEY, world == null ? "global" : world)
.build();
if (holder.setNode(DataType.NORMAL, node, true).wasSuccessful()) {
if (holder.setNode(type, node, true).wasSuccessful()) {
return holderSave(holder);
}
return false;
}
private boolean holderRemovePermission(PermissionHolder holder, String permission, String world) {
private boolean holderRemovePermission(PermissionHolder holder, String permission, String world, DataType type) {
Objects.requireNonNull(permission, "permission is null");
Preconditions.checkArgument(!permission.isEmpty(), "permission is an empty string");
@@ -401,7 +435,7 @@ public class LuckPermsVaultPermission extends AbstractVaultPermission {
.withContext(DefaultContextKeys.WORLD_KEY, world == null ? "global" : world)
.build();
if (holder.unsetNode(DataType.NORMAL, node).wasSuccessful()) {
if (holder.unsetNode(type, node).wasSuccessful()) {
return holderSave(holder);
}
return false;
@@ -450,4 +484,5 @@ public class LuckPermsVaultPermission extends AbstractVaultPermission {
private boolean useVaultServer() {
return this.plugin.getConfiguration().get(ConfigKeys.USE_VAULT_SERVER);
}
}
+39 -5
View File
@@ -154,15 +154,25 @@ data:
#verifyServerCertificate: false
# The prefix for all LuckPerms SQL tables.
#
# - This only applies for remote SQL storage types (MySQL, MariaDB, etc).
# - Change this if you want to use different tables for different servers.
table-prefix: 'luckperms_'
# The prefix to use for all LuckPerms collections. Change this if you want to use different
# collections for different servers. The default is no prefix.
# The prefix to use for all LuckPerms MongoDB collections.
#
# - This only applies for the MongoDB storage type.
# - Change this if you want to use different collections for different servers. The default is no
# prefix.
mongodb-collection-prefix: ''
# MongoDB ClientConnectionURI for use with replica sets and custom connection options
# - See https://docs.mongodb.com/manual/reference/connection-string/
# The connection string URI to use to connect to the MongoDB instance.
#
# - When configured, this setting will override anything defined in the address, database,
# username or password fields above.
# - If you have a connection string that starts with 'mongodb://' or 'mongodb+srv://', enter it
# below.
# - For more information, please see https://docs.mongodb.com/manual/reference/connection-string/
mongodb-connection-uri: ''
# Define settings for a "split" storage setup.
@@ -231,6 +241,7 @@ watch-files: true
# below.
# => rabbitmq Uses RabbitMQ pub-sub to push changes. Your server connection info must be
# configured below.
# => custom Uses a messaging service provided using the LuckPerms API.
# => auto Attempts to automatically setup a messaging service using redis or sql.
messaging-service: auto
@@ -252,6 +263,7 @@ broadcast-received-log-entries: true
redis:
enabled: false
address: localhost
username: ''
password: ''
# Settings for RabbitMQ.
@@ -540,6 +552,13 @@ apply-bukkit-attachment-permissions: true
# | Extra settings | #
# +----------------------------------------------------------------------------------------------+ #
# A list of context calculators which will be skipped when calculating contexts.
#
# - You can disable context calculators by either:
# => specifying the Java class name used by the calculator (e.g. com.example.ExampleCalculator)
# => specifying a sub-section of the Java package used by the calculator (e.g. com.example)
disabled-context-calculators: []
# Allows you to set "aliases" for the worlds sent forward for context calculation.
#
# - These aliases are provided in addition to the real world name. Applied recursively.
@@ -575,7 +594,8 @@ group-weight:
# Controls whether server operators should exist at all.
#
# - When set to 'false', all players will be de-opped, and the /op and /deop commands will be
# disabled.
# disabled. Note that vanilla features like the spawn-protection require an operator on the
# server to work.
enable-ops: true
# Enables or disables a special permission based system in LuckPerms for controlling OP status.
@@ -660,6 +680,20 @@ debug-logins: false
# or less.
allow-invalid-usernames: false
# If LuckPerms should not require users to confirm bulkupdate operations.
#
# - When set to true, operations will be executed immediately.
# - This is not recommended, as bulkupdate has the potential to irreversibly delete large amounts of
# data, and is not designed to be executed automatically.
# - If automation is needed, users should prefer using the LuckPerms API.
skip-bulkupdate-confirmation: false
# If LuckPerms should prevent bulkupdate operations.
#
# - When set to true, bulkupdate operations (the /lp bulkupdate command) will not work.
# - When set to false, bulkupdate operations will be allowed via the console.
disable-bulkupdate: false
# If LuckPerms should allow a users primary group to be removed with the 'parent remove' command.
#
# - When this happens, the plugin will set their primary group back to default.
+12 -3
View File
@@ -54,6 +54,9 @@ luckperms {
translations {
install;
}
applyedits {
code brigadier:string single_word;
}
creategroup {
name brigadier:string single_word {
weight brigadier:integer {
@@ -62,9 +65,13 @@ luckperms {
}
}
deletegroup {
name brigadier:string single_word;
name brigadier:string single_word {
flags brigadier:string greedy_phrase;
}
}
listgroups {
page brigadier:integer;
}
listgroups;
createtrack {
name brigadier:string single_word;
}
@@ -546,7 +553,9 @@ luckperms {
context brigadier:string greedy_phrase;
}
rename {
newname brigadier:string single_word;
newname brigadier:string single_word {
flags brigadier:string greedy_phrase;
}
}
clone {
newname brigadier:string single_word;
+9 -5
View File
@@ -1,18 +1,22 @@
plugins {
id 'com.github.johnrengelman.shadow' version '2.0.4'
id 'com.github.johnrengelman.shadow' version '7.0.0'
}
dependencies {
compile project(':common')
implementation project(':common')
compileOnly project(':common:loader-utils')
compileOnly 'net.md-5:bungeecord-api:1.15-SNAPSHOT'
compileOnly 'me.lucko:adventure-platform-bungeecord:4.0.0' // re: this artifact - see note in common/build.gradle
compileOnly 'net.md-5:bungeecord-api:1.16-R0.4'
compileOnly('me.lucko:adventure-platform-bungeecord:4.9.4') {
exclude(module: 'adventure-bom')
exclude(module: 'adventure-api')
exclude(module: 'adventure-nbt')
}
compileOnly 'com.imaginarycode.minecraft:RedisBungee:0.4'
}
shadowJar {
archiveName = 'luckperms-bungee.jarinjar'
archiveFileName = 'luckperms-bungee.jarinjar'
dependencies {
include(dependency('me.lucko.luckperms:.*'))
+5 -6
View File
@@ -5,19 +5,18 @@ plugins {
dependencies {
compileOnly 'net.md-5:bungeecord-api:1.15-SNAPSHOT'
compile project(':api')
compile project(':common:loader-utils')
implementation project(':api')
implementation project(':common:loader-utils')
}
processResources {
from(sourceSets.main.resources.srcDirs) {
filesMatching('plugin.yml') {
expand 'pluginVersion': project.ext.fullVersion
include 'plugin.yml'
}
}
shadowJar {
archiveName = "LuckPerms-Bungee-${project.ext.fullVersion}.jar"
archiveFileName = "LuckPerms-Bungee-${project.ext.fullVersion}.jar"
from {
project(':bungee').tasks.shadowJar.archiveFile
@@ -26,4 +25,4 @@ shadowJar {
artifacts {
archives shadowJar
}
}
@@ -43,7 +43,7 @@ public class BungeeCommandExecutor extends Command implements TabExecutor {
private static final String NAME = "luckpermsbungee";
/** The command aliases */
private static final String[] ALIASES = {"lpb", "bperm", "bperms", "bpermission", "bpermissions"};
private static final String[] ALIASES = {"lpb"};
/** The main command name + aliases, prefixed with '/' */
private static final String[] SLASH_ALIASES = Stream.concat(
@@ -81,6 +81,11 @@ public class BungeeSenderFactory extends SenderFactory<LPBungeePlugin, CommandSe
getPlugin().getBootstrap().getProxy().getPluginManager().dispatchCommand(sender, command);
}
@Override
protected boolean isConsole(CommandSender sender) {
return !(sender instanceof ProxiedPlayer);
}
@Override
public void close() {
super.close();
@@ -47,8 +47,6 @@ import me.lucko.luckperms.common.model.manager.user.StandardUserManager;
import me.lucko.luckperms.common.plugin.AbstractLuckPermsPlugin;
import me.lucko.luckperms.common.plugin.util.AbstractConnectionListener;
import me.lucko.luckperms.common.sender.Sender;
import me.lucko.luckperms.common.tasks.CacheHousekeepingTask;
import me.lucko.luckperms.common.tasks.ExpireTemporaryTask;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.context.DefaultContextKeys;
@@ -57,7 +55,6 @@ import net.md_5.bungee.api.plugin.Plugin;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
/**
@@ -170,12 +167,6 @@ public class LPBungeePlugin extends AbstractLuckPermsPlugin {
// BungeeCord doesn't have a services manager
}
@Override
protected void registerHousekeepingTasks() {
this.bootstrap.getScheduler().asyncRepeating(new ExpireTemporaryTask(this), 3, TimeUnit.SECONDS);
this.bootstrap.getScheduler().asyncRepeating(new CacheHousekeepingTask(this), 2, TimeUnit.MINUTES);
}
@Override
protected void performFinalSetup() {
@@ -28,8 +28,8 @@ 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.ContextManager;
import me.lucko.luckperms.common.context.QueryOptionsSupplier;
import me.lucko.luckperms.common.context.manager.ContextManager;
import me.lucko.luckperms.common.context.manager.QueryOptionsSupplier;
import me.lucko.luckperms.common.util.CaffeineFactory;
import net.luckperms.api.context.ImmutableContextSet;
@@ -27,7 +27,7 @@ package me.lucko.luckperms.bungee.context;
import me.lucko.luckperms.bungee.LPBungeePlugin;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl;
import me.lucko.luckperms.common.context.ImmutableContextSetImpl;
import net.luckperms.api.context.ContextCalculator;
import net.luckperms.api.context.ContextConsumer;
@@ -60,7 +60,7 @@ public class BungeePlayerCalculator implements ContextCalculator<ProxiedPlayer>,
}
@Override
public ContextSet estimatePotentialContexts() {
public @NonNull ContextSet estimatePotentialContexts() {
ImmutableContextSet.Builder builder = new ImmutableContextSetImpl.BuilderImpl();
for (ServerInfo server : this.plugin.getBootstrap().getProxy().getServers().values()) {
builder.add(DefaultContextKeys.WORLD_KEY, server.getName());
@@ -28,7 +28,7 @@ package me.lucko.luckperms.bungee.context;
import com.imaginarycode.minecraft.redisbungee.RedisBungee;
import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl;
import me.lucko.luckperms.common.context.ImmutableContextSetImpl;
import net.luckperms.api.context.ContextConsumer;
import net.luckperms.api.context.ContextSet;
@@ -49,7 +49,7 @@ public class RedisBungeeCalculator implements StaticContextCalculator {
}
@Override
public ContextSet estimatePotentialContexts() {
public @NonNull ContextSet estimatePotentialContexts() {
RedisBungeeAPI redisBungee = RedisBungee.getApi();
if (redisBungee == null) {
return ImmutableContextSetImpl.EMPTY;
@@ -27,10 +27,12 @@ package me.lucko.luckperms.bungee.listeners;
import me.lucko.luckperms.bungee.LPBungeePlugin;
import me.lucko.luckperms.bungee.event.TristateCheckEvent;
import me.lucko.luckperms.common.calculator.result.TristateResult;
import me.lucko.luckperms.common.cacheddata.result.TristateResult;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.query.QueryOptionsImpl;
import me.lucko.luckperms.common.verbose.VerboseCheckTarget;
import me.lucko.luckperms.common.verbose.event.CheckOrigin;
import net.luckperms.api.query.QueryOptions;
import net.luckperms.api.util.Tristate;
@@ -70,7 +72,7 @@ public class BungeePermissionCheckListener implements Listener {
}
QueryOptions queryOptions = this.plugin.getContextManager().getQueryOptions(player);
Tristate result = user.getCachedData().getPermissionData(queryOptions).checkPermission(e.getPermission(), me.lucko.luckperms.common.verbose.event.PermissionCheckEvent.Origin.PLATFORM_PERMISSION_CHECK).result();
Tristate result = user.getCachedData().getPermissionData(queryOptions).checkPermission(e.getPermission(), CheckOrigin.PLATFORM_API_HAS_PERMISSION).result();
if (result == Tristate.UNDEFINED && this.plugin.getConfiguration().get(ConfigKeys.APPLY_BUNGEE_CONFIG_PERMISSIONS)) {
return; // just use the result provided by the proxy when the event was created
}
@@ -99,7 +101,7 @@ public class BungeePermissionCheckListener implements Listener {
}
QueryOptions queryOptions = this.plugin.getContextManager().getQueryOptions(player);
Tristate result = user.getCachedData().getPermissionData(queryOptions).checkPermission(e.getPermission(), me.lucko.luckperms.common.verbose.event.PermissionCheckEvent.Origin.PLATFORM_LOOKUP_CHECK).result();
Tristate result = user.getCachedData().getPermissionData(queryOptions).checkPermission(e.getPermission(), CheckOrigin.PLATFORM_API_HAS_PERMISSION_SET).result();
if (result == Tristate.UNDEFINED && this.plugin.getConfiguration().get(ConfigKeys.APPLY_BUNGEE_CONFIG_PERMISSIONS)) {
return; // just use the result provided by the proxy when the event was created
}
@@ -118,9 +120,9 @@ public class BungeePermissionCheckListener implements Listener {
String permission = e.getPermission();
Tristate result = Tristate.of(e.hasPermission());
String name = "internal/" + e.getSender().getName();
VerboseCheckTarget target = VerboseCheckTarget.internal(e.getSender().getName());
this.plugin.getVerboseHandler().offerPermissionCheckEvent(me.lucko.luckperms.common.verbose.event.PermissionCheckEvent.Origin.PLATFORM_PERMISSION_CHECK, name, QueryOptionsImpl.DEFAULT_CONTEXTUAL, permission, TristateResult.of(result));
this.plugin.getVerboseHandler().offerPermissionCheckEvent(CheckOrigin.PLATFORM_API_HAS_PERMISSION, target, QueryOptionsImpl.DEFAULT_CONTEXTUAL, permission, TristateResult.forMonitoredResult(result));
this.plugin.getPermissionRegistry().offer(permission);
}
@@ -135,9 +137,9 @@ public class BungeePermissionCheckListener implements Listener {
String permission = e.getPermission();
Tristate result = e.getResult();
String name = "internal/" + e.getSender().getName();
VerboseCheckTarget target = VerboseCheckTarget.internal(e.getSender().getName());
this.plugin.getVerboseHandler().offerPermissionCheckEvent(me.lucko.luckperms.common.verbose.event.PermissionCheckEvent.Origin.PLATFORM_LOOKUP_CHECK, name, QueryOptionsImpl.DEFAULT_CONTEXTUAL, permission, TristateResult.of(result));
this.plugin.getVerboseHandler().offerPermissionCheckEvent(CheckOrigin.PLATFORM_API_HAS_PERMISSION_SET, target, QueryOptionsImpl.DEFAULT_CONTEXTUAL, permission, TristateResult.forMonitoredResult(result));
this.plugin.getPermissionRegistry().offer(permission);
}
}
+37 -4
View File
@@ -151,15 +151,25 @@ data:
#verifyServerCertificate: false
# The prefix for all LuckPerms SQL tables.
#
# - This only applies for remote SQL storage types (MySQL, MariaDB, etc).
# - Change this if you want to use different tables for different servers.
table-prefix: 'luckperms_'
# The prefix to use for all LuckPerms collections. Change this if you want to use different
# collections for different servers. The default is no prefix.
# The prefix to use for all LuckPerms MongoDB collections.
#
# - This only applies for the MongoDB storage type.
# - Change this if you want to use different collections for different servers. The default is no
# prefix.
mongodb-collection-prefix: ''
# MongoDB ClientConnectionURI for use with replica sets and custom connection options
# - See https://docs.mongodb.com/manual/reference/connection-string/
# The connection string URI to use to connect to the MongoDB instance.
#
# - When configured, this setting will override anything defined in the address, database,
# username or password fields above.
# - If you have a connection string that starts with 'mongodb://' or 'mongodb+srv://', enter it
# below.
# - For more information, please see https://docs.mongodb.com/manual/reference/connection-string/
mongodb-connection-uri: ''
# Define settings for a "split" storage setup.
@@ -229,6 +239,7 @@ watch-files: true
# the RedisBungee plugin installed.
# => rabbitmq Uses RabbitMQ pub-sub to push changes. Your server connection info must be
# configured below.
# => custom Uses a messaging service provided using the LuckPerms API.
# => auto Attempts to automatically setup a messaging service using redis or sql.
messaging-service: auto
@@ -250,6 +261,7 @@ broadcast-received-log-entries: false
redis:
enabled: false
address: localhost
username: ''
password: ''
# Settings for RabbitMQ.
@@ -520,6 +532,13 @@ apply-bungee-config-permissions: false
# | Extra settings | #
# +----------------------------------------------------------------------------------------------+ #
# A list of context calculators which will be skipped when calculating contexts.
#
# - You can disable context calculators by either:
# => specifying the Java class name used by the calculator (e.g. com.example.ExampleCalculator)
# => specifying a sub-section of the Java package used by the calculator (e.g. com.example)
disabled-context-calculators: []
# Allows you to set "aliases" for the worlds sent forward for context calculation.
#
# - These aliases are provided in addition to the real world name. Applied recursively.
@@ -563,6 +582,20 @@ debug-logins: false
# or less.
allow-invalid-usernames: false
# If LuckPerms should not require users to confirm bulkupdate operations.
#
# - When set to true, operations will be executed immediately.
# - This is not recommended, as bulkupdate has the potential to irreversibly delete large amounts of
# data, and is not designed to be executed automatically.
# - If automation is needed, users should prefer using the LuckPerms API.
skip-bulkupdate-confirmation: false
# If LuckPerms should prevent bulkupdate operations.
#
# - When set to true, bulkupdate operations (the /lp bulkupdate command) will not work.
# - When set to false, bulkupdate operations will be allowed via the console.
disable-bulkupdate: false
# If LuckPerms should allow a users primary group to be removed with the 'parent remove' command.
#
# - When this happens, the plugin will set their primary group back to default.
+54 -36
View File
@@ -1,3 +1,7 @@
plugins {
id 'java-library'
}
test {
useJUnitPlatform {
excludeTags 'dependency_checksum'
@@ -5,55 +9,69 @@ test {
}
dependencies {
testCompile 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testCompile 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
compile project(':api')
compile 'org.checkerframework:checker-qual:3.8.0'
api project(':api')
api 'org.checkerframework:checker-qual:3.12.0'
compileOnly project(':common:loader-utils')
compileOnly 'org.slf4j:slf4j-api:1.7.30'
compileOnly 'org.apache.logging.log4j:log4j-api:2.14.0'
// This is a special re-packaged version of 'net.kyori:adventure-*' for our own use.
// Don't use it in other projects, you want the net.kyori version instead.
// See here for more info: https://github.com/KyoriPowered/adventure
compile('me.lucko:adventure-api:4.0.0') {
api('net.kyori:adventure-api:4.9.3') {
exclude(module: 'adventure-bom')
exclude(module: 'checker-qual')
exclude(module: 'annotations')
}
compile('net.kyori:event-api:3.0.0') {
exclude(module: 'checker-qual')
exclude(module: 'guava')
}
compile 'com.google.code.gson:gson:2.7'
compile 'com.google.guava:guava:19.0'
compile 'com.github.ben-manes.caffeine:caffeine:2.8.4'
compile 'com.squareup.okhttp3:okhttp:3.14.7'
compile 'com.squareup.okio:okio:1.17.5'
compile 'net.bytebuddy:byte-buddy:1.10.9'
compile('org.spongepowered:configurate-core:3.7') {
exclude(module: 'checker-qual')
exclude(module: 'guava')
}
compile('org.spongepowered:configurate-yaml:3.7') {
exclude(module: 'checker-qual')
exclude(module: 'snakeyaml')
}
compile('org.spongepowered:configurate-gson:3.7') {
exclude(module: 'checker-qual')
api('net.kyori:adventure-text-serializer-gson:4.9.3') {
exclude(module: 'adventure-bom')
exclude(module: 'adventure-api')
exclude(module: 'gson')
}
compile ('org.spongepowered:configurate-hocon:3.7') {
api('net.kyori:adventure-text-serializer-legacy:4.9.3') {
exclude(module: 'adventure-bom')
exclude(module: 'adventure-api')
}
api('net.kyori:adventure-text-serializer-plain:4.9.3') {
exclude(module: 'adventure-bom')
exclude(module: 'adventure-api')
}
api('net.kyori:event-api:3.0.0') {
exclude(module: 'checker-qual')
exclude(module: 'guava')
}
compile('me.lucko.configurate:configurate-toml:3.7') {
exclude(module: 'toml4j')
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
}
compile 'com.zaxxer:HikariCP:4.0.2'
compile 'redis.clients:jedis:3.3.0'
compile 'com.rabbitmq:amqp-client:5.10.0'
compile 'org.mongodb:mongo-java-driver:3.12.2'
compile 'org.yaml:snakeyaml:1.23'
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
}
api 'com.zaxxer:HikariCP:4.0.3'
api 'redis.clients:jedis:3.5.2'
api 'com.rabbitmq:amqp-client:5.12.0'
api 'org.mongodb:mongo-java-driver:3.12.8'
api 'org.yaml:snakeyaml:1.28'
}
@@ -33,6 +33,7 @@ import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
/**
@@ -64,6 +65,20 @@ public class JarInJarClassLoader extends URLClassLoader {
addURL(url);
}
public void deleteJarResource() {
URL[] urls = getURLs();
if (urls.length == 0) {
return;
}
try {
Path path = Paths.get(urls[0].toURI());
Files.deleteIfExists(path);
} catch (Exception e) {
// ignore
}
}
/**
* Creates a new plugin instance.
*
@@ -47,6 +47,7 @@ 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;
@@ -126,10 +127,10 @@ public class LoggedAction implements Action {
}
public boolean matchesSearch(String query) {
query = Objects.requireNonNull(query, "query").toLowerCase();
return this.source.name.toLowerCase().contains(query) ||
this.target.name.toLowerCase().contains(query) ||
this.description.toLowerCase().contains(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) {
@@ -269,7 +270,7 @@ public class LoggedAction implements Action {
}
@Override
public @NonNull Builder targetType(Action.Target.Type type) {
public @NonNull Builder targetType(Target.@NonNull Type type) {
this.targetType = Objects.requireNonNull(type, "type");
return this;
}
@@ -31,6 +31,7 @@ import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.storage.misc.DataConstraints;
import java.util.Locale;
import java.util.function.Predicate;
public final class ApiUtils {
@@ -55,7 +56,7 @@ public final class ApiUtils {
}
Preconditions.checkArgument(DataConstraints.GROUP_NAME_TEST.test(s), "Invalid name entry: " + s);
return s.toLowerCase();
return s.toLowerCase(Locale.ROOT);
}
}
@@ -25,7 +25,7 @@
package me.lucko.luckperms.common.api.implementation;
import me.lucko.luckperms.common.context.ContextManager;
import me.lucko.luckperms.common.context.manager.ContextManager;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.query.QueryOptionsBuilderImpl;
@@ -25,8 +25,8 @@
package me.lucko.luckperms.common.api.implementation;
import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl;
import me.lucko.luckperms.common.context.contextset.MutableContextSetImpl;
import me.lucko.luckperms.common.context.ImmutableContextSetImpl;
import me.lucko.luckperms.common.context.MutableContextSetImpl;
import net.luckperms.api.context.ContextSetFactory;
import net.luckperms.api.context.ImmutableContextSet;
@@ -42,7 +42,7 @@ public class ApiMessagingService implements MessagingService {
}
@Override
public String getName() {
public @NonNull String getName() {
return this.handle.getName();
}
@@ -25,7 +25,7 @@
package me.lucko.luckperms.common.api.implementation;
import me.lucko.luckperms.common.context.ContextManager;
import me.lucko.luckperms.common.context.manager.ContextManager;
import me.lucko.luckperms.common.model.manager.user.UserManager;
import net.luckperms.api.context.ImmutableContextSet;
@@ -57,7 +57,9 @@ public class ApiPlayerAdapter<S, P extends S> implements PlayerAdapter<P> {
public @NonNull User getUser(@NonNull P player) {
Objects.requireNonNull(player, "player");
me.lucko.luckperms.common.model.User user = this.userManager.getIfLoaded(this.contextManager.getUniqueId(checkType(player)));
Objects.requireNonNull(user, "user");
if (user == null) {
throw new IllegalStateException("Unable to get a user for " + player);
}
return user.getApiProxy();
}
@@ -30,7 +30,7 @@ import com.google.common.base.Preconditions;
import me.lucko.luckperms.common.cacheddata.UserCachedDataManager;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.node.types.Inheritance;
import me.lucko.luckperms.common.verbose.event.MetaCheckEvent;
import me.lucko.luckperms.common.verbose.event.CheckOrigin;
import net.luckperms.api.model.data.DataMutateResult;
import net.luckperms.api.model.data.DataType;
@@ -38,6 +38,7 @@ import net.luckperms.api.node.NodeEqualityPredicate;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Locale;
import java.util.Objects;
import java.util.UUID;
@@ -71,7 +72,7 @@ public class ApiUser extends ApiPermissionHolder implements net.luckperms.api.mo
@Override
public @NonNull String getPrimaryGroup() {
String value = this.handle.getCachedData().getMetaData(this.handle.getQueryOptions()).getPrimaryGroup(MetaCheckEvent.Origin.LUCKPERMS_API);
String value = this.handle.getCachedData().getMetaData(this.handle.getQueryOptions()).getPrimaryGroup(CheckOrigin.LUCKPERMS_API);
Objects.requireNonNull(value, "value"); // assert nonnull
return value;
}
@@ -79,15 +80,15 @@ public class ApiUser extends ApiPermissionHolder implements net.luckperms.api.mo
@Override
public @NonNull DataMutateResult setPrimaryGroup(@NonNull String group) {
Objects.requireNonNull(group, "group");
if (getPrimaryGroup().equalsIgnoreCase(group)) {
if (group.equalsIgnoreCase(this.handle.getPrimaryGroup().getStoredValue().orElse(null))) {
return DataMutateResult.FAIL_ALREADY_HAS;
}
if (!this.handle.hasNode(DataType.NORMAL, Inheritance.builder(group.toLowerCase()).build(), NodeEqualityPredicate.IGNORE_EXPIRY_TIME_AND_VALUE).asBoolean()) {
if (!this.handle.hasNode(DataType.NORMAL, Inheritance.builder(group.toLowerCase(Locale.ROOT)).build(), NodeEqualityPredicate.IGNORE_EXPIRY_TIME_AND_VALUE).asBoolean()) {
return DataMutateResult.FAIL;
}
this.handle.getPrimaryGroup().setStoredValue(group.toLowerCase());
this.handle.getPrimaryGroup().setStoredValue(group.toLowerCase(Locale.ROOT));
return DataMutateResult.SUCCESS;
}
@@ -63,17 +63,20 @@ public class ApiUserManager extends ApiAbstractManager<User, net.luckperms.api.m
return internal == null ? null : internal.getApiProxy();
}
private net.luckperms.api.model.user.User proxyAndRegisterUsage(User internal) {
if (internal != null) {
this.plugin.getUserManager().getHouseKeeper().registerApiUsage(internal.getUniqueId());
}
return proxy(internal);
}
@Override
public @NonNull CompletableFuture<net.luckperms.api.model.user.User> loadUser(@NonNull UUID uniqueId, @Nullable String username) {
Objects.requireNonNull(uniqueId, "uuid");
ApiUtils.checkUsername(username, this.plugin);
if (this.plugin.getUserManager().getIfLoaded(uniqueId) == null) {
this.plugin.getUserManager().getHouseKeeper().registerApiUsage(uniqueId);
}
return this.plugin.getStorage().loadUser(uniqueId, username)
.thenApply(this::proxy);
.thenApply(this::proxyAndRegisterUsage);
}
@Override
@@ -105,7 +108,10 @@ public class ApiUserManager extends ApiAbstractManager<User, net.luckperms.api.m
action.accept(user.getApiProxy());
return user;
}, this.plugin.getBootstrap().getScheduler().async())
.thenCompose(user -> this.plugin.getStorage().saveUser(user));
.thenCompose(user -> {
this.plugin.getUserManager().giveDefaultIfNeeded(user);
return this.plugin.getStorage().saveUser(user);
});
}
@Override
@@ -151,13 +157,13 @@ public class ApiUserManager extends ApiAbstractManager<User, net.luckperms.api.m
@Override
public net.luckperms.api.model.user.User getUser(@NonNull UUID uniqueId) {
Objects.requireNonNull(uniqueId, "uuid");
return proxy(this.handle.getIfLoaded(uniqueId));
return proxyAndRegisterUsage(this.handle.getIfLoaded(uniqueId));
}
@Override
public net.luckperms.api.model.user.User getUser(@NonNull String username) {
Objects.requireNonNull(username, "name");
return proxy(this.handle.getByUsername(username));
return proxyAndRegisterUsage(this.handle.getByUsername(username));
}
@Override
@@ -50,6 +50,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@@ -167,7 +168,7 @@ public class Importer implements Runnable {
for (JsonElement holderElement : holdersArray) {
JsonObject jsonData = holderElement.getAsJsonObject();
HolderType type = HolderType.valueOf(jsonData.get("type").getAsString().toUpperCase());
HolderType type = HolderType.valueOf(jsonData.get("type").getAsString().toUpperCase(Locale.ROOT));
String id = jsonData.get("id").getAsString();
if (type == HolderType.GROUP) {
@@ -25,6 +25,8 @@
package me.lucko.luckperms.common.bulkupdate.query;
import java.util.Locale;
/**
* Represents a field being used in an update
*/
@@ -38,7 +40,7 @@ public enum QueryField {
public static QueryField of(String s) {
try {
return valueOf(s.toUpperCase());
return valueOf(s.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException e) {
return null;
}
@@ -27,7 +27,7 @@ package me.lucko.luckperms.common.cacheddata;
import me.lucko.luckperms.common.cache.LoadingMap;
import me.lucko.luckperms.common.cacheddata.type.MetaAccumulator;
import me.lucko.luckperms.common.cacheddata.type.MetaCache;
import me.lucko.luckperms.common.cacheddata.type.MonitoredMetaCache;
import me.lucko.luckperms.common.cacheddata.type.PermissionCache;
import me.lucko.luckperms.common.calculator.CalculatorFactory;
import me.lucko.luckperms.common.calculator.PermissionCalculator;
@@ -40,6 +40,7 @@ import net.luckperms.api.cacheddata.CachedMetaData;
import net.luckperms.api.cacheddata.CachedPermissionData;
import net.luckperms.api.metastacking.MetaStackDefinition;
import net.luckperms.api.node.ChatMetaType;
import net.luckperms.api.node.Node;
import net.luckperms.api.query.QueryOptions;
import org.checkerframework.checker.nullness.qual.NonNull;
@@ -59,7 +60,7 @@ import java.util.function.IntFunction;
public abstract class AbstractCachedDataManager implements CachedDataManager {
private final LuckPermsPlugin plugin;
private final AbstractContainer<PermissionCache, CachedPermissionData> permission;
private final AbstractContainer<MetaCache, CachedMetaData> meta;
private final AbstractContainer<MonitoredMetaCache, CachedMetaData> meta;
protected AbstractCachedDataManager(LuckPermsPlugin plugin) {
this.plugin = plugin;
@@ -87,7 +88,7 @@ public abstract class AbstractCachedDataManager implements CachedDataManager {
}
@Override
public @NonNull MetaCache getMetaData(@NonNull QueryOptions queryOptions) {
public @NonNull MonitoredMetaCache getMetaData(@NonNull QueryOptions queryOptions) {
return this.meta.get(queryOptions);
}
@@ -97,7 +98,7 @@ public abstract class AbstractCachedDataManager implements CachedDataManager {
}
@Override
public @NonNull MetaCache getMetaData() {
public @NonNull MonitoredMetaCache getMetaData() {
return getMetaData(getQueryOptions());
}
@@ -139,7 +140,7 @@ public abstract class AbstractCachedDataManager implements CachedDataManager {
* @param <M> the map type
* @return the resolved permissions
*/
protected abstract <M extends Map<String, Boolean>> M resolvePermissions(IntFunction<M> mapFactory, QueryOptions queryOptions);
protected abstract <M extends Map<String, Node>> M resolvePermissions(IntFunction<M> mapFactory, QueryOptions queryOptions);
/**
* Resolves the owners meta data for the given {@link QueryOptions}.
@@ -153,18 +154,18 @@ public abstract class AbstractCachedDataManager implements CachedDataManager {
Objects.requireNonNull(queryOptions, "queryOptions");
CacheMetadata metadata = getMetadataForQueryOptions(queryOptions);
ConcurrentHashMap<String, Boolean> sourcePermissions = resolvePermissions(ConcurrentHashMap::new, queryOptions);
ConcurrentHashMap<String, Node> sourcePermissions = resolvePermissions(ConcurrentHashMap::new, queryOptions);
return new PermissionCache(queryOptions, metadata, getCalculatorFactory(), sourcePermissions);
}
private MetaCache calculateMeta(QueryOptions queryOptions) {
private MonitoredMetaCache calculateMeta(QueryOptions queryOptions) {
Objects.requireNonNull(queryOptions, "queryOptions");
CacheMetadata metadata = getMetadataForQueryOptions(queryOptions);
MetaAccumulator accumulator = newAccumulator(queryOptions);
resolveMeta(accumulator, queryOptions);
return new MetaCache(this.plugin, queryOptions, metadata, accumulator);
return new MonitoredMetaCache(this.plugin, queryOptions, metadata, accumulator);
}
@Override
@@ -254,24 +255,11 @@ public abstract class AbstractCachedDataManager implements CachedDataManager {
this.cache.clear();
}
}
private MetaStackDefinition getMetaStackDefinition(QueryOptions queryOptions, ChatMetaType type) {
MetaStackDefinition stack = queryOptions.option(type == ChatMetaType.PREFIX ?
MetaStackDefinition.PREFIX_STACK_KEY :
MetaStackDefinition.SUFFIX_STACK_KEY
).orElse(null);
if (stack == null) {
stack = getDefaultMetaStackDefinition(type);
}
return stack;
}
private MetaAccumulator newAccumulator(QueryOptions queryOptions) {
return new MetaAccumulator(
getMetaStackDefinition(queryOptions, ChatMetaType.PREFIX),
getMetaStackDefinition(queryOptions, ChatMetaType.SUFFIX)
queryOptions.option(MetaStackDefinition.PREFIX_STACK_KEY).orElseGet(() -> getDefaultMetaStackDefinition(ChatMetaType.PREFIX)),
queryOptions.option(MetaStackDefinition.SUFFIX_STACK_KEY).orElseGet(() -> getDefaultMetaStackDefinition(ChatMetaType.SUFFIX))
);
}
@@ -26,6 +26,7 @@
package me.lucko.luckperms.common.cacheddata;
import me.lucko.luckperms.common.model.HolderType;
import me.lucko.luckperms.common.verbose.VerboseCheckTarget;
import net.luckperms.api.cacheddata.CachedData;
import net.luckperms.api.query.QueryOptions;
@@ -41,18 +42,18 @@ public class CacheMetadata {
private final HolderType holderType;
/**
* The name of the object which owns the cache
* The information about the holder for verbose checks
*/
private final String objectName;
private final VerboseCheckTarget verboseCheckInfo;
/**
* The query options
*/
private final QueryOptions queryOptions;
public CacheMetadata(HolderType holderType, String objectName, QueryOptions queryOptions) {
public CacheMetadata(HolderType holderType, VerboseCheckTarget verboseCheckInfo, QueryOptions queryOptions) {
this.holderType = holderType;
this.objectName = objectName;
this.verboseCheckInfo = verboseCheckInfo;
this.queryOptions = queryOptions;
}
@@ -60,8 +61,8 @@ public class CacheMetadata {
return this.holderType;
}
public String getObjectName() {
return this.objectName;
public VerboseCheckTarget getVerboseCheckInfo() {
return this.verboseCheckInfo;
}
public QueryOptions getQueryOptions() {
@@ -27,6 +27,7 @@ package me.lucko.luckperms.common.cacheddata;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.HolderType;
import me.lucko.luckperms.common.verbose.VerboseCheckTarget;
import net.luckperms.api.cacheddata.CachedDataManager;
import net.luckperms.api.query.QueryOptions;
@@ -42,7 +43,7 @@ public class GroupCachedDataManager extends HolderCachedDataManager<Group> imple
@Override
protected CacheMetadata getMetadataForQueryOptions(QueryOptions queryOptions) {
return new CacheMetadata(HolderType.GROUP, this.holder.getPlainDisplayName(), queryOptions);
return new CacheMetadata(HolderType.GROUP, VerboseCheckTarget.group(this.holder), queryOptions);
}
}
@@ -32,6 +32,7 @@ import me.lucko.luckperms.common.model.PermissionHolder;
import net.luckperms.api.metastacking.MetaStackDefinition;
import net.luckperms.api.node.ChatMetaType;
import net.luckperms.api.node.Node;
import net.luckperms.api.query.QueryOptions;
import java.util.Map;
@@ -75,7 +76,7 @@ public abstract class HolderCachedDataManager<T extends PermissionHolder> extend
}
@Override
protected <M extends Map<String, Boolean>> M resolvePermissions(IntFunction<M> mapFactory, QueryOptions queryOptions) {
protected <M extends Map<String, Node>> M resolvePermissions(IntFunction<M> mapFactory, QueryOptions queryOptions) {
return this.holder.exportPermissions(mapFactory, queryOptions, true, getPlugin().getConfiguration().get(ConfigKeys.APPLYING_SHORTHAND));
}
@@ -27,6 +27,7 @@ package me.lucko.luckperms.common.cacheddata;
import me.lucko.luckperms.common.model.HolderType;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.verbose.VerboseCheckTarget;
import net.luckperms.api.cacheddata.CachedDataManager;
import net.luckperms.api.query.QueryOptions;
@@ -42,7 +43,7 @@ public class UserCachedDataManager extends HolderCachedDataManager<User> impleme
@Override
protected CacheMetadata getMetadataForQueryOptions(QueryOptions queryOptions) {
return new CacheMetadata(HolderType.USER, this.holder.getPlainDisplayName(), queryOptions);
return new CacheMetadata(HolderType.USER, VerboseCheckTarget.user(this.holder), queryOptions);
}
}
@@ -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.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;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Represents the result of a meta lookup
*
* @param <N> the node type
*/
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) {
this.result = result;
this.node = node;
this.overriddenResult = overriddenResult;
}
@Override
public @Nullable String result() {
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);
}
@Override
public String toString() {
return "StringResult(" +
"result=" + this.result + ", " +
"node=" + this.node + ", " +
"overriddenResult=" + this.overriddenResult + ')';
}
private static final StringResult<?> NULL_RESULT = new StringResult<>(null, null, null);
@SuppressWarnings("unchecked")
public static <N extends Node> StringResult<N> nullResult() {
return (StringResult<N>) NULL_RESULT;
}
public static <N extends Node> StringResult<N> of(String result) {
return new StringResult<>(result, null, null);
}
public static <N extends Node> StringResult<N> of(String result, N node) {
return new StringResult<>(result, node, null);
}
public static StringResult<MetaNode> of(MetaNode node) {
return new StringResult<>(node.getMetaValue(), node, null);
}
public static <N extends ChatMetaNode<?, ?>> StringResult<N> of(N node) {
return new StringResult<>(node.getMetaValue(), node, null);
}
}
@@ -0,0 +1,151 @@
/*
* 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 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;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Represents the result of a {@link PermissionCalculator} lookup.
*/
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) {
this.result = result;
this.node = node;
this.processorClass = processorClass;
}
@Override
public @NonNull Tristate result() {
return this.result;
}
@Override
public @Nullable Node node() {
return this.node;
}
public @Nullable Class<? extends PermissionProcessor> processorClass() {
return this.processorClass;
}
public @Nullable String processorClassFriendly() {
if (this.processorClass == null) {
return null;
} else if (this.processorClass.getName().startsWith("me.lucko.luckperms.")) {
String simpleName = this.processorClass.getSimpleName();
String platform = this.processorClass.getName().split("\\.")[3];
return platform + "." + simpleName;
} else {
return this.processorClass.getName();
}
}
public @Nullable TristateResult overriddenResult() {
return this.overriddenResult;
}
public void setOverriddenResult(TristateResult overriddenResult) {
this.overriddenResult = overriddenResult;
}
@Override
public String toString() {
return "TristateResult(" +
"result=" + this.result + ", " +
"node=" + this.node + ", " +
"processorClass=" + this.processorClass + ", " +
"overriddenResult=" + this.overriddenResult + ')';
}
private static final TristateResult TRUE = new TristateResult(Tristate.TRUE, null,null);
private static final TristateResult FALSE = new TristateResult(Tristate.FALSE, null,null);
public static final TristateResult UNDEFINED = new TristateResult(Tristate.UNDEFINED, null,null);
public static TristateResult forMonitoredResult(Tristate result) {
switch (result) {
case TRUE:
return TRUE;
case FALSE:
return FALSE;
case UNDEFINED:
return UNDEFINED;
default:
throw new AssertionError();
}
}
public static final class Factory {
private final Class<? extends PermissionProcessor> processorClass;
public Factory(Class<? extends PermissionProcessor> processorClass) {
this.processorClass = processorClass;
}
public TristateResult result(Tristate result) {
switch (result) {
case TRUE:
case FALSE:
return new TristateResult(result, null, this.processorClass);
case UNDEFINED:
return UNDEFINED;
default:
throw new AssertionError();
}
}
public TristateResult result(@Nullable Node node) {
if (node == null) {
return UNDEFINED;
}
return new TristateResult(Tristate.of(node.getValue()), node, this.processorClass);
}
public TristateResult resultWithOverride(@Nullable Node node, @NonNull Tristate result) {
if (result == Tristate.UNDEFINED) {
return UNDEFINED;
}
return new TristateResult(result, node, this.processorClass);
}
}
}
@@ -28,6 +28,7 @@ package me.lucko.luckperms.common.cacheddata.type;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import me.lucko.luckperms.common.cacheddata.result.StringResult;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.node.types.Weight;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
@@ -35,13 +36,16 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import net.luckperms.api.metastacking.MetaStackDefinition;
import net.luckperms.api.node.ChatMetaType;
import net.luckperms.api.node.Node;
import net.luckperms.api.node.types.ChatMetaNode;
import net.luckperms.api.node.types.MetaNode;
import net.luckperms.api.node.types.PrefixNode;
import net.luckperms.api.node.types.SuffixNode;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicReference;
@@ -73,16 +77,18 @@ public class MetaAccumulator {
private final AtomicReference<State> state = new AtomicReference<>(State.ACCUMULATING);
private final ListMultimap<String, String> meta;
private final SortedMap<Integer, String> prefixes;
private final SortedMap<Integer, String> suffixes;
private final ListMultimap<String, StringResult<MetaNode>> meta;
private final SortedMap<Integer, StringResult<PrefixNode>> prefixes;
private final SortedMap<Integer, StringResult<SuffixNode>> suffixes;
private int weight = 0;
private String primaryGroup;
private Set<String> seenNodeKeys = new HashSet<>();
private final MetaStackDefinition prefixDefinition;
private final MetaStackDefinition suffixDefinition;
private final MetaStackAccumulator prefixAccumulator;
private final MetaStackAccumulator suffixAccumulator;
private final MetaStackAccumulator<PrefixNode> prefixAccumulator;
private final MetaStackAccumulator<SuffixNode> suffixAccumulator;
public MetaAccumulator(MetaStackDefinition prefixDefinition, MetaStackDefinition suffixDefinition) {
Objects.requireNonNull(prefixDefinition, "prefixDefinition");
@@ -92,8 +98,8 @@ public class MetaAccumulator {
this.suffixes = new TreeMap<>(Comparator.reverseOrder());
this.prefixDefinition = prefixDefinition;
this.suffixDefinition = suffixDefinition;
this.prefixAccumulator = new MetaStackAccumulator(this.prefixDefinition, ChatMetaType.PREFIX);
this.suffixAccumulator = new MetaStackAccumulator(this.suffixDefinition, ChatMetaType.SUFFIX);
this.prefixAccumulator = new MetaStackAccumulator<>(this.prefixDefinition, ChatMetaType.PREFIX);
this.suffixAccumulator = new MetaStackAccumulator<>(this.suffixDefinition, ChatMetaType.SUFFIX);
}
private void ensureState(State state) {
@@ -115,11 +121,12 @@ public class MetaAccumulator {
// perform final changes
if (!this.meta.containsKey(Weight.NODE_KEY) && this.weight != 0) {
this.meta.put(Weight.NODE_KEY, String.valueOf(this.weight));
this.meta.put(Weight.NODE_KEY, StringResult.of(String.valueOf(this.weight)));
}
if (this.primaryGroup != null && !this.meta.containsKey("primarygroup")) {
this.meta.put("primarygroup", this.primaryGroup);
this.meta.put("primarygroup", StringResult.of(this.primaryGroup));
}
this.seenNodeKeys = null; // free up for GC
this.state.set(State.COMPLETE);
}
@@ -129,29 +136,34 @@ public class MetaAccumulator {
public void accumulateNode(Node n) {
ensureState(State.ACCUMULATING);
// only process distinct nodes once, allows inheritance to be
// "cancelled out" by assigning a false copy.
if (!this.seenNodeKeys.add(n.getKey())) {
return;
}
if (!n.getValue()) {
return;
}
if (n instanceof MetaNode) {
MetaNode mn = (MetaNode) n;
this.meta.put(mn.getMetaKey(), mn.getMetaValue());
this.meta.put(mn.getMetaKey(), StringResult.of(mn));
}
if (n instanceof PrefixNode) {
PrefixNode pn = (PrefixNode) n;
this.prefixes.putIfAbsent(pn.getPriority(), pn.getMetaValue());
this.prefixes.putIfAbsent(pn.getPriority(), StringResult.of(pn));
this.prefixAccumulator.offer(pn);
}
if (n instanceof SuffixNode) {
SuffixNode pn = (SuffixNode) n;
this.suffixes.putIfAbsent(pn.getPriority(), pn.getMetaValue());
this.suffixes.putIfAbsent(pn.getPriority(), StringResult.of(pn));
this.suffixAccumulator.offer(pn);
}
}
public void accumulateMeta(String key, String value) {
ensureState(State.ACCUMULATING);
this.meta.put(key, value);
}
public void accumulateWeight(int weight) {
ensureState(State.ACCUMULATING);
this.weight = Math.max(this.weight, weight);
@@ -164,22 +176,22 @@ public class MetaAccumulator {
// read methods
public ListMultimap<String, String> getMeta() {
public ListMultimap<String, StringResult<MetaNode>> getMeta() {
ensureState(State.COMPLETE);
return this.meta;
}
public Map<Integer, String> getChatMeta(ChatMetaType type) {
public Map<Integer, ? extends StringResult<? extends ChatMetaNode<?, ?>>> getChatMeta(ChatMetaType type) {
ensureState(State.COMPLETE);
return type == ChatMetaType.PREFIX ? this.prefixes : this.suffixes;
}
public SortedMap<Integer, String> getPrefixes() {
public SortedMap<Integer, StringResult<PrefixNode>> getPrefixes() {
ensureState(State.COMPLETE);
return this.prefixes;
}
public SortedMap<Integer, String> getSuffixes() {
public SortedMap<Integer, StringResult<SuffixNode>> getSuffixes() {
ensureState(State.COMPLETE);
return this.suffixes;
}
@@ -204,14 +216,14 @@ public class MetaAccumulator {
return this.suffixDefinition;
}
public String getPrefix() {
public StringResult<PrefixNode> getPrefix() {
ensureState(State.COMPLETE);
return this.prefixAccumulator.toFormattedString();
return this.prefixAccumulator.toResult();
}
public String getSuffix() {
public StringResult<SuffixNode> getSuffix() {
ensureState(State.COMPLETE);
return this.suffixAccumulator.toFormattedString();
return this.suffixAccumulator.toResult();
}
@Override
@@ -26,112 +26,213 @@
package me.lucko.luckperms.common.cacheddata.type;
import com.google.common.collect.ForwardingMap;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
import me.lucko.luckperms.common.cacheddata.CacheMetadata;
import me.lucko.luckperms.common.model.HolderType;
import me.lucko.luckperms.common.node.types.Prefix;
import me.lucko.luckperms.common.node.types.Suffix;
import me.lucko.luckperms.common.cacheddata.UsageTracked;
import me.lucko.luckperms.common.cacheddata.result.StringResult;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.verbose.event.MetaCheckEvent;
import me.lucko.luckperms.common.verbose.event.CheckOrigin;
import net.luckperms.api.cacheddata.CachedMetaData;
import net.luckperms.api.cacheddata.Result;
import net.luckperms.api.metastacking.MetaStackDefinition;
import net.luckperms.api.node.types.MetaNode;
import net.luckperms.api.node.types.PrefixNode;
import net.luckperms.api.node.types.SuffixNode;
import net.luckperms.api.query.QueryOptions;
import net.luckperms.api.query.meta.MetaValueSelector;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.SortedMap;
/**
* Holds cached meta for a given context
*/
public class MetaCache extends SimpleMetaCache implements CachedMetaData {
public class MetaCache extends UsageTracked implements CachedMetaData {
/** The plugin instance */
private final LuckPermsPlugin plugin;
/** The metadata for this cache */
private final CacheMetadata metadata;
/** The query options this container is holding data for */
private final QueryOptions queryOptions;
/** The object name passed to the verbose handler when checks are made */
private final String verboseCheckTarget;
/* The data */
private final Map<String, List<StringResult<MetaNode>>> meta;
private final Map<String, StringResult<MetaNode>> flattenedMeta;
private final SortedMap<Integer, StringResult<PrefixNode>> prefixes;
private final SortedMap<Integer, StringResult<SuffixNode>> suffixes;
private final int weight;
private final String primaryGroup;
private final MetaStackDefinition prefixDefinition;
private final MetaStackDefinition suffixDefinition;
private final StringResult<PrefixNode> prefix;
private final StringResult<SuffixNode> suffix;
public MetaCache(LuckPermsPlugin plugin, QueryOptions queryOptions, CacheMetadata metadata, MetaAccumulator sourceMeta) {
super(plugin, queryOptions, sourceMeta);
public MetaCache(LuckPermsPlugin plugin, QueryOptions queryOptions, MetaAccumulator sourceMeta) {
this.plugin = plugin;
this.metadata = metadata;
this.queryOptions = queryOptions;
if (this.metadata.getHolderType() == HolderType.GROUP) {
this.verboseCheckTarget = "group/" + this.metadata.getObjectName();
} else {
this.verboseCheckTarget = this.metadata.getObjectName();
Map<String, List<StringResult<MetaNode>>> meta = Multimaps.asMap(ImmutableListMultimap.copyOf(sourceMeta.getMeta()));
MetaValueSelector metaValueSelector = this.queryOptions.option(MetaValueSelector.KEY)
.orElseGet(() -> this.plugin.getConfiguration().get(ConfigKeys.META_VALUE_SELECTOR));
ImmutableMap.Builder<String, StringResult<MetaNode>> builder = ImmutableMap.builder();
for (Map.Entry<String, List<StringResult<MetaNode>>> e : meta.entrySet()) {
if (e.getValue().isEmpty()) {
continue;
}
Result<String, MetaNode> selected = metaValueSelector.selectValue(e.getKey(), e.getValue());
if (selected == null) {
throw new NullPointerException(metaValueSelector + " returned null");
}
builder.put(e.getKey(), (StringResult<MetaNode>) selected);
}
this.flattenedMeta = builder.build();
this.meta = new LowerCaseMetaMap(meta);
this.prefixes = ImmutableSortedMap.copyOfSorted(sourceMeta.getPrefixes());
this.suffixes = ImmutableSortedMap.copyOfSorted(sourceMeta.getSuffixes());
this.weight = sourceMeta.getWeight();
this.primaryGroup = sourceMeta.getPrimaryGroup();
this.prefixDefinition = sourceMeta.getPrefixDefinition();
this.suffixDefinition = sourceMeta.getSuffixDefinition();
this.prefix = sourceMeta.getPrefix();
this.suffix = sourceMeta.getSuffix();
}
public @NonNull StringResult<MetaNode> getMetaValue(String key, CheckOrigin origin) {
Objects.requireNonNull(key, "key");
return this.flattenedMeta.getOrDefault(key.toLowerCase(Locale.ROOT), StringResult.nullResult());
}
@Override
public String getMetaValue(String key, MetaCheckEvent.Origin origin) {
String value = super.getMetaValue(key, origin);
this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.verboseCheckTarget, this.metadata.getQueryOptions(), key, String.valueOf(value));
return value;
public final @NonNull Result<String, MetaNode> queryMetaValue(@NonNull String key) {
return getMetaValue(key, CheckOrigin.LUCKPERMS_API);
}
@Override
public String getPrefix(MetaCheckEvent.Origin origin) {
String value = super.getPrefix(origin);
this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.verboseCheckTarget, this.metadata.getQueryOptions(), Prefix.NODE_KEY, String.valueOf(value));
return value;
public final @Nullable String getMetaValue(@NonNull String key) {
return getMetaValue(key, CheckOrigin.LUCKPERMS_API).result();
}
public @NonNull StringResult<PrefixNode> getPrefix(CheckOrigin origin) {
return this.prefix;
}
@Override
public String getSuffix(MetaCheckEvent.Origin origin) {
String value = super.getSuffix(origin);
this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.verboseCheckTarget, this.metadata.getQueryOptions(), Suffix.NODE_KEY, String.valueOf(value));
return value;
public final @NonNull Result<String, PrefixNode> queryPrefix() {
return getPrefix(CheckOrigin.LUCKPERMS_API);
}
@Override
public Map<String, List<String>> getMeta(MetaCheckEvent.Origin origin) {
return new MonitoredMetaMap(super.getMeta(origin), origin);
public final @Nullable String getPrefix() {
return getPrefix(CheckOrigin.LUCKPERMS_API).result();
}
public @NonNull StringResult<SuffixNode> getSuffix(CheckOrigin origin) {
return this.suffix;
}
@Override
public int getWeight(MetaCheckEvent.Origin origin) {
int value = super.getWeight(origin);
this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.verboseCheckTarget, this.metadata.getQueryOptions(), "weight", String.valueOf(value));
return value;
public final @NonNull Result<String, SuffixNode> querySuffix() {
return getSuffix(CheckOrigin.LUCKPERMS_API);
}
@Override
public @Nullable String getPrimaryGroup(MetaCheckEvent.Origin origin) {
String value = super.getPrimaryGroup(origin);
this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.verboseCheckTarget, this.metadata.getQueryOptions(), "primarygroup", String.valueOf(value));
return value;
public final @Nullable String getSuffix() {
return getSuffix(CheckOrigin.LUCKPERMS_API).result();
}
private final class MonitoredMetaMap extends ForwardingMap<String, List<String>> {
private final Map<String, List<String>> delegate;
private final MetaCheckEvent.Origin origin;
protected Map<String, List<StringResult<MetaNode>>> getMetaResults(CheckOrigin origin) {
return this.meta;
}
private MonitoredMetaMap(Map<String, List<String>> delegate, MetaCheckEvent.Origin origin) {
public final Map<String, List<String>> getMeta(CheckOrigin origin) {
return Maps.transformValues(getMetaResults(origin), list -> Lists.transform(list, StringResult::result));
}
@Override
public final @NonNull Map<String, List<String>> getMeta() {
return getMeta(CheckOrigin.LUCKPERMS_API);
}
@Override
public @NonNull SortedMap<Integer, String> getPrefixes() {
return Maps.transformValues(this.prefixes, StringResult::result);
}
@Override
public @NonNull SortedMap<Integer, String> getSuffixes() {
return Maps.transformValues(this.suffixes, StringResult::result);
}
public int getWeight(CheckOrigin origin) {
return this.weight;
}
//@Override - not actually exposed in the API atm
public final int getWeight() {
return getWeight(CheckOrigin.LUCKPERMS_API);
}
public @Nullable String getPrimaryGroup(CheckOrigin origin) {
return this.primaryGroup;
}
@Override
public final @Nullable String getPrimaryGroup() {
return getPrimaryGroup(CheckOrigin.LUCKPERMS_API);
}
@Override
public @NonNull MetaStackDefinition getPrefixStackDefinition() {
return this.prefixDefinition;
}
@Override
public @NonNull MetaStackDefinition getSuffixStackDefinition() {
return this.suffixDefinition;
}
@Override
public @NonNull QueryOptions getQueryOptions() {
return this.queryOptions;
}
private static final class LowerCaseMetaMap extends ForwardingMap<String, List<StringResult<MetaNode>>> {
private final Map<String, List<StringResult<MetaNode>>> delegate;
private LowerCaseMetaMap(Map<String, List<StringResult<MetaNode>>> delegate) {
this.delegate = delegate;
this.origin = origin;
}
@Override
protected Map<String, List<String>> delegate() {
protected Map<String, List<StringResult<MetaNode>>> delegate() {
return this.delegate;
}
@Override
public List<String> get(Object k) {
public List<StringResult<MetaNode>> get(Object k) {
if (k == null) {
return null;
}
String key = (String) k;
List<String> values = super.get(key);
MetaCache.this.plugin.getVerboseHandler().offerMetaCheckEvent(this.origin, MetaCache.this.verboseCheckTarget, MetaCache.this.metadata.getQueryOptions(), key, String.valueOf(values));
return values;
return super.get(key.toLowerCase(Locale.ROOT));
}
}
@@ -25,21 +25,24 @@
package me.lucko.luckperms.common.cacheddata.type;
import me.lucko.luckperms.common.cacheddata.result.StringResult;
import me.lucko.luckperms.common.metastacking.ElementAccumulator;
import net.luckperms.api.metastacking.MetaStackDefinition;
import net.luckperms.api.metastacking.MetaStackElement;
import net.luckperms.api.node.ChatMetaType;
import net.luckperms.api.node.types.ChatMetaNode;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class MetaStackAccumulator {
public class MetaStackAccumulator<N extends ChatMetaNode<N, ?>> {
private final MetaStackDefinition definition;
private final List<Entry> entries;
private final List<ElementAccumulator<N>> entries;
public MetaStackAccumulator(MetaStackDefinition definition, ChatMetaType targetType) {
this.definition = definition;
@@ -47,20 +50,27 @@ public class MetaStackAccumulator {
List<MetaStackElement> elements = definition.getElements();
this.entries = new ArrayList<>(elements.size());
for (MetaStackElement element : elements) {
this.entries.add(new Entry(element, targetType));
this.entries.add(ElementAccumulator.create(element, targetType));
}
}
public void offer(ChatMetaNode<?, ?> node) {
for (Entry entry : this.entries) {
public void offer(N node) {
for (ElementAccumulator<N> entry : this.entries) {
entry.offer(node);
}
}
public List<N> getElements() {
return this.entries.stream()
.map(ElementAccumulator::result)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
public String toFormattedString() {
List<String> elements = new LinkedList<>();
for (Entry entry : this.entries) {
ChatMetaNode<?, ?> node = entry.getNode();
for (ElementAccumulator<N> entry : this.entries) {
N node = entry.result();
if (node != null) {
elements.add(node.getMetaValue());
}
@@ -94,27 +104,33 @@ public class MetaStackAccumulator {
return sb.toString();
}
private static final class Entry {
private final MetaStackElement element;
private final ChatMetaType type;
private @Nullable ChatMetaNode<?, ?> current = null;
Entry(MetaStackElement element, ChatMetaType type) {
this.element = element;
this.type = type;
public StringResult<N> toResult() {
String formatted = toFormattedString();
if (formatted == null) {
return StringResult.nullResult();
}
public ChatMetaNode<?, ?> getNode() {
return this.current;
}
List<N> elements = getElements();
public boolean offer(ChatMetaNode<?, ?> node) {
if (this.element.shouldAccumulate(this.type, node, this.current)) {
this.current = node;
return true;
switch (elements.size()) {
case 0:
throw new AssertionError();
case 1:
return StringResult.of(formatted, elements.get(0));
default: {
Iterator<N> it = elements.iterator();
StringResult<N> result = StringResult.of(formatted, it.next());
StringResult<N> root = result;
while (it.hasNext()) {
StringResult<N> nested = StringResult.of(it.next());
root.setOverriddenResult(nested);
root = nested;
}
return result;
}
return false;
}
}
}
@@ -0,0 +1,153 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.common.cacheddata.type;
import com.google.common.collect.ForwardingMap;
import me.lucko.luckperms.common.cacheddata.CacheMetadata;
import me.lucko.luckperms.common.cacheddata.result.StringResult;
import me.lucko.luckperms.common.node.types.Prefix;
import me.lucko.luckperms.common.node.types.Suffix;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.verbose.event.CheckOrigin;
import net.luckperms.api.cacheddata.CachedMetaData;
import net.luckperms.api.node.types.MetaNode;
import net.luckperms.api.node.types.PrefixNode;
import net.luckperms.api.node.types.SuffixNode;
import net.luckperms.api.query.QueryOptions;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Holds cached meta for a given context
*/
public class MonitoredMetaCache extends MetaCache implements CachedMetaData {
/** The plugin instance */
private final LuckPermsPlugin plugin;
/** The metadata for this cache */
private final CacheMetadata metadata;
public MonitoredMetaCache(LuckPermsPlugin plugin, QueryOptions queryOptions, CacheMetadata metadata, MetaAccumulator sourceMeta) {
super(plugin, queryOptions, sourceMeta);
this.plugin = plugin;
this.metadata = metadata;
}
@Override
@NonNull
public StringResult<MetaNode> getMetaValue(String key, CheckOrigin origin) {
StringResult<MetaNode> value = super.getMetaValue(key, origin);
this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), key, value);
return value;
}
@Override
@NonNull
public StringResult<PrefixNode> getPrefix(CheckOrigin origin) {
StringResult<PrefixNode> value = super.getPrefix(origin);
this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), Prefix.NODE_KEY, value);
return value;
}
@Override
@NonNull
public StringResult<SuffixNode> getSuffix(CheckOrigin origin) {
StringResult<SuffixNode> value = super.getSuffix(origin);
this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), Suffix.NODE_KEY, value);
return value;
}
@Override
protected Map<String, List<StringResult<MetaNode>>> getMetaResults(CheckOrigin origin) {
return new MonitoredMetaMap(super.getMetaResults(origin), origin);
}
@Override
public int getWeight(CheckOrigin origin) {
int value = super.getWeight(origin);
this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), "weight", StringResult.of(String.valueOf(value)));
return value;
}
@Override
public @Nullable String getPrimaryGroup(CheckOrigin origin) {
String value = super.getPrimaryGroup(origin);
this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), "primarygroup", StringResult.of(value));
return value;
}
private final class MonitoredMetaMap extends ForwardingMap<String, List<StringResult<MetaNode>>> {
private final Map<String, List<StringResult<MetaNode>>> delegate;
private final CheckOrigin origin;
private MonitoredMetaMap(Map<String, List<StringResult<MetaNode>>> delegate, CheckOrigin origin) {
this.delegate = delegate;
this.origin = origin;
}
@Override
protected Map<String, List<StringResult<MetaNode>>> delegate() {
return this.delegate;
}
@Override
public List<StringResult<MetaNode>> get(Object k) {
if (k == null) {
return null;
}
String key = (String) k;
List<StringResult<MetaNode>> values = super.get(key);
if (values == null || values.isEmpty()) {
MonitoredMetaCache.this.plugin.getVerboseHandler().offerMetaCheckEvent(this.origin, MonitoredMetaCache.this.metadata.getVerboseCheckInfo(), MonitoredMetaCache.this.metadata.getQueryOptions(), key, StringResult.nullResult());
} else {
Iterator<StringResult<MetaNode>> it = values.iterator();
StringResult<MetaNode> result = it.next().copy();
StringResult<MetaNode> root = result;
while (it.hasNext()) {
StringResult<MetaNode> nested = it.next().copy();
root.setOverriddenResult(nested);
root = nested;
}
MonitoredMetaCache.this.plugin.getVerboseHandler().offerMetaCheckEvent(this.origin, MonitoredMetaCache.this.metadata.getVerboseCheckInfo(), MonitoredMetaCache.this.metadata.getQueryOptions(), key, result);
}
return values;
}
}
}
@@ -25,14 +25,18 @@
package me.lucko.luckperms.common.cacheddata.type;
import com.google.common.collect.Maps;
import me.lucko.luckperms.common.cacheddata.CacheMetadata;
import me.lucko.luckperms.common.cacheddata.UsageTracked;
import me.lucko.luckperms.common.cacheddata.result.TristateResult;
import me.lucko.luckperms.common.calculator.CalculatorFactory;
import me.lucko.luckperms.common.calculator.PermissionCalculator;
import me.lucko.luckperms.common.calculator.result.TristateResult;
import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent;
import me.lucko.luckperms.common.verbose.event.CheckOrigin;
import net.luckperms.api.cacheddata.CachedPermissionData;
import net.luckperms.api.cacheddata.Result;
import net.luckperms.api.node.Node;
import net.luckperms.api.query.QueryOptions;
import net.luckperms.api.util.Tristate;
@@ -55,12 +59,12 @@ public class PermissionCache extends UsageTracked implements CachedPermissionDat
/**
* The raw set of permission strings.
*/
private final Map<String, Boolean> permissions;
private final Map<String, Node> permissions;
/**
* An immutable copy of {@link #permissions}
* A string->boolean view of {@link #permissions}
*/
private final Map<String, Boolean> permissionsUnmodifiable;
private final Map<String, Boolean> permissionsView;
/**
* The calculator instance responsible for resolving the raw permission strings in the permission map.
@@ -69,10 +73,10 @@ public class PermissionCache extends UsageTracked implements CachedPermissionDat
*/
private final PermissionCalculator calculator;
public PermissionCache(QueryOptions queryOptions, CacheMetadata metadata, CalculatorFactory calculatorFactory, ConcurrentHashMap<String, Boolean> sourcePermissions) {
public PermissionCache(QueryOptions queryOptions, CacheMetadata metadata, CalculatorFactory calculatorFactory, ConcurrentHashMap<String, Node> sourcePermissions) {
this.queryOptions = queryOptions;
this.permissions = sourcePermissions;
this.permissionsUnmodifiable = Collections.unmodifiableMap(this.permissions);
this.permissionsView = Collections.unmodifiableMap(Maps.transformValues(this.permissions, Node::getValue));
this.calculator = calculatorFactory.build(queryOptions, metadata);
this.calculator.setSourcePermissions(this.permissions);
@@ -89,19 +93,24 @@ public class PermissionCache extends UsageTracked implements CachedPermissionDat
@Override
public @NonNull Map<String, Boolean> getPermissionMap() {
return this.permissionsUnmodifiable;
return this.permissionsView;
}
public TristateResult checkPermission(String permission, PermissionCheckEvent.Origin origin) {
public TristateResult checkPermission(String permission, CheckOrigin origin) {
if (permission == null) {
throw new NullPointerException("permission");
}
return this.calculator.checkPermission(permission, origin);
}
@Override
public @NonNull Result<Tristate, Node> queryPermission(@NonNull String permission) {
return checkPermission(permission, CheckOrigin.LUCKPERMS_API);
}
@Override
public @NonNull Tristate checkPermission(@NonNull String permission) {
return checkPermission(permission, PermissionCheckEvent.Origin.LUCKPERMS_API).result();
return checkPermission(permission, CheckOrigin.LUCKPERMS_API).result();
}
@Override
@@ -1,212 +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.type;
import com.google.common.collect.ForwardingMap;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Multimaps;
import me.lucko.luckperms.common.cacheddata.UsageTracked;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.verbose.event.MetaCheckEvent;
import net.luckperms.api.cacheddata.CachedMetaData;
import net.luckperms.api.metastacking.MetaStackDefinition;
import net.luckperms.api.query.QueryOptions;
import net.luckperms.api.query.meta.MetaValueSelector;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.SortedMap;
/**
* Holds cached meta for a given context
*/
public class SimpleMetaCache extends UsageTracked implements CachedMetaData {
private final LuckPermsPlugin plugin;
/** The query options this container is holding data for */
private final QueryOptions queryOptions;
/* The data */
protected final Map<String, List<String>> meta;
protected final Map<String, String> flattenedMeta;
protected final SortedMap<Integer, String> prefixes;
protected final SortedMap<Integer, String> suffixes;
protected final int weight;
protected final String primaryGroup;
private final MetaStackDefinition prefixDefinition;
private final MetaStackDefinition suffixDefinition;
private final String prefix;
private final String suffix;
public SimpleMetaCache(LuckPermsPlugin plugin, QueryOptions queryOptions, MetaAccumulator sourceMeta) {
this.plugin = plugin;
this.queryOptions = queryOptions;
Map<String, List<String>> meta = Multimaps.asMap(ImmutableListMultimap.copyOf(sourceMeta.getMeta()));
MetaValueSelector metaValueSelector = this.queryOptions.option(MetaValueSelector.KEY)
.orElseGet(() -> this.plugin.getConfiguration().get(ConfigKeys.META_VALUE_SELECTOR));
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
for (Map.Entry<String, List<String>> e : meta.entrySet()) {
if (e.getValue().isEmpty()) {
continue;
}
String selected = metaValueSelector.selectValue(e.getKey(), e.getValue());
if (selected == null) {
throw new NullPointerException(metaValueSelector + " returned null");
}
builder.put(e.getKey(), selected);
}
this.flattenedMeta = builder.build();
this.meta = new LowerCaseMetaMap(meta);
this.prefixes = ImmutableSortedMap.copyOfSorted(sourceMeta.getPrefixes());
this.suffixes = ImmutableSortedMap.copyOfSorted(sourceMeta.getSuffixes());
this.weight = sourceMeta.getWeight();
this.primaryGroup = sourceMeta.getPrimaryGroup();
this.prefixDefinition = sourceMeta.getPrefixDefinition();
this.suffixDefinition = sourceMeta.getSuffixDefinition();
this.prefix = sourceMeta.getPrefix();
this.suffix = sourceMeta.getSuffix();
}
public String getMetaValue(String key, MetaCheckEvent.Origin origin) {
Objects.requireNonNull(key, "key");
return this.flattenedMeta.get(key.toLowerCase());
}
@Override
public final String getMetaValue(@NonNull String key) {
return getMetaValue(key, MetaCheckEvent.Origin.LUCKPERMS_API);
}
public String getPrefix(MetaCheckEvent.Origin origin) {
return this.prefix;
}
@Override
public final String getPrefix() {
return getPrefix(MetaCheckEvent.Origin.LUCKPERMS_API);
}
public String getSuffix(MetaCheckEvent.Origin origin) {
return this.suffix;
}
@Override
public final String getSuffix() {
return getSuffix(MetaCheckEvent.Origin.LUCKPERMS_API);
}
public Map<String, List<String>> getMeta(MetaCheckEvent.Origin origin) {
return this.meta;
}
@Override
public final @NonNull Map<String, List<String>> getMeta() {
return getMeta(MetaCheckEvent.Origin.LUCKPERMS_API);
}
@Override
public @NonNull SortedMap<Integer, String> getPrefixes() {
return this.prefixes;
}
@Override
public @NonNull SortedMap<Integer, String> getSuffixes() {
return this.suffixes;
}
public int getWeight(MetaCheckEvent.Origin origin) {
return this.weight;
}
//@Override - not actually exposed in the API atm
public final int getWeight() {
return getWeight(MetaCheckEvent.Origin.LUCKPERMS_API);
}
public @Nullable String getPrimaryGroup(MetaCheckEvent.Origin origin) {
return this.primaryGroup;
}
@Override
public final @Nullable String getPrimaryGroup() {
return getPrimaryGroup(MetaCheckEvent.Origin.LUCKPERMS_API);
}
@Override
public @NonNull MetaStackDefinition getPrefixStackDefinition() {
return this.prefixDefinition;
}
@Override
public @NonNull MetaStackDefinition getSuffixStackDefinition() {
return this.suffixDefinition;
}
@Override
public @NonNull QueryOptions getQueryOptions() {
return this.queryOptions;
}
private static final class LowerCaseMetaMap extends ForwardingMap<String, List<String>> {
private final Map<String, List<String>> delegate;
private LowerCaseMetaMap(Map<String, List<String>> delegate) {
this.delegate = delegate;
}
@Override
protected Map<String, List<String>> delegate() {
return this.delegate;
}
@Override
public List<String> get(Object k) {
if (k == null) {
return null;
}
String key = (String) k;
return super.get(key.toLowerCase());
}
}
}
@@ -25,11 +25,14 @@
package me.lucko.luckperms.common.cacheddata.type;
import net.luckperms.api.cacheddata.Result;
import net.luckperms.api.node.types.MetaNode;
import net.luckperms.api.query.meta.MetaValueSelector;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class SimpleMetaValueSelector implements MetaValueSelector {
@@ -42,7 +45,7 @@ public class SimpleMetaValueSelector implements MetaValueSelector {
}
@Override
public @NonNull String selectValue(@NonNull String key, @NonNull List<String> values) {
public @NonNull Result<String, MetaNode> selectValue(@NonNull String key, @NonNull List<? extends Result<String, MetaNode>> values) {
switch (values.size()) {
case 0:
throw new IllegalArgumentException("values is empty");
@@ -56,7 +59,7 @@ public class SimpleMetaValueSelector implements MetaValueSelector {
public enum Strategy {
INHERITANCE {
@Override
public String select(List<String> values) {
public Result<String, MetaNode> select(List<? extends Result<String, MetaNode>> values) {
return values.get(0);
}
},
@@ -64,7 +67,7 @@ public class SimpleMetaValueSelector implements MetaValueSelector {
private final DoubleSelectionPredicate selection = (value, current) -> value > current;
@Override
public String select(List<String> values) {
public Result<String, MetaNode> select(List<? extends Result<String, MetaNode>> values) {
return selectNumber(values, this.selection);
}
},
@@ -72,16 +75,16 @@ public class SimpleMetaValueSelector implements MetaValueSelector {
private final DoubleSelectionPredicate selection = (value, current) -> value < current;
@Override
public String select(List<String> values) {
public Result<String, MetaNode> select(List<? extends Result<String, MetaNode>> values) {
return selectNumber(values, this.selection);
}
};
public abstract String select(List<String> values);
public abstract Result<String, MetaNode> select(List<? extends Result<String, MetaNode>> values);
public static Strategy parse(String s) {
try {
return Strategy.valueOf(s.replace('-', '_').toUpperCase());
return Strategy.valueOf(s.replace('-', '_').toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException e) {
return null;
}
@@ -93,15 +96,15 @@ public class SimpleMetaValueSelector implements MetaValueSelector {
boolean shouldSelect(double value, double current);
}
private static String selectNumber(List<String> values, DoubleSelectionPredicate selection) {
private static Result<String, MetaNode> selectNumber(List<? extends Result<String, MetaNode>> values, DoubleSelectionPredicate selection) {
double current = 0;
String selected = null;
Result<String, MetaNode> selected = null;
for (String value : values) {
for (Result<String, MetaNode> result : values) {
try {
double parse = Double.parseDouble(value);
double parse = Double.parseDouble(result.result());
if (selected == null || selection.shouldSelect(parse, current)) {
selected = value;
selected = result;
current = parse;
}
} catch (NumberFormatException e) {
@@ -27,15 +27,17 @@ package me.lucko.luckperms.common.calculator;
import me.lucko.luckperms.common.cache.LoadingMap;
import me.lucko.luckperms.common.cacheddata.CacheMetadata;
import me.lucko.luckperms.common.cacheddata.result.TristateResult;
import me.lucko.luckperms.common.calculator.processor.PermissionProcessor;
import me.lucko.luckperms.common.calculator.result.TristateResult;
import me.lucko.luckperms.common.model.HolderType;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent;
import me.lucko.luckperms.common.verbose.event.CheckOrigin;
import net.luckperms.api.node.Node;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Collection;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;
@@ -56,19 +58,10 @@ public class PermissionCalculator implements Function<String, TristateResult> {
/** Loading cache for permission checks */
private final LoadingMap<String, TristateResult> lookupCache = LoadingMap.of(this);
/** The object name passed to the verbose handler when checks are made */
private final String verboseCheckTarget;
public PermissionCalculator(LuckPermsPlugin plugin, CacheMetadata metadata, Collection<PermissionProcessor> processors) {
this.plugin = plugin;
this.metadata = metadata;
this.processors = processors.toArray(new PermissionProcessor[0]);
if (this.metadata.getHolderType() == HolderType.GROUP) {
this.verboseCheckTarget = "group/" + this.metadata.getObjectName();
} else {
this.verboseCheckTarget = this.metadata.getObjectName();
}
}
/**
@@ -80,12 +73,12 @@ public class PermissionCalculator implements Function<String, TristateResult> {
* @param origin marks where this check originated from
* @return the result
*/
public TristateResult checkPermission(String permission, PermissionCheckEvent.Origin origin) {
public TristateResult checkPermission(String permission, CheckOrigin origin) {
// get the result
TristateResult result = this.lookupCache.get(permission);
// log this permission lookup to the verbose handler
this.plugin.getVerboseHandler().offerPermissionCheckEvent(origin, this.verboseCheckTarget, this.metadata.getQueryOptions(), permission, result);
this.plugin.getVerboseHandler().offerPermissionCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), permission, result);
// return the result
return result;
@@ -95,7 +88,7 @@ public class PermissionCalculator implements Function<String, TristateResult> {
public TristateResult apply(@NonNull String permission) {
// convert the permission to lowercase, as all values in the backing map are also lowercase.
// this allows fast case insensitive lookups
permission = permission.toLowerCase();
permission = permission.toLowerCase(Locale.ROOT);
// offer the permission to the permission vault
// we only need to do this once per permission, so it doesn't matter
@@ -115,7 +108,7 @@ public class PermissionCalculator implements Function<String, TristateResult> {
*
* @param sourceMap the source map
*/
public synchronized void setSourcePermissions(Map<String, Boolean> sourceMap) {
public synchronized void setSourcePermissions(Map<String, Node> sourceMap) {
for (PermissionProcessor processor : this.processors) {
processor.setSource(sourceMap);
processor.refresh();
Loaded 100 of 411 files, more files were not shown because too many files have changed in this diff. Show more