diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b84bce2f7..16bf9b016 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,9 @@ jobs: with: fetch-depth: 0 + - name: validate gradle wrapper + uses: gradle/wrapper-validation-action@v1 + - uses: actions/setup-java@v3 with: distribution: 'temurin' @@ -74,6 +77,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 + with: + fetch-depth: 0 - name: Retrieve saved standalone jar artifact uses: actions/download-artifact@v3 @@ -81,10 +86,6 @@ jobs: name: standalone-binary path: standalone/docker/ - - name: Display structure of downloaded files - run: ls -R - working-directory: standalone/docker/ - - name: Set up QEMU uses: docker/setup-qemu-action@v1 @@ -98,6 +99,10 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Determine the LuckPerms version + run: | + echo "luckperms_version=$(git describe --tags | awk -F "-" '{print $1 "." $2}')" >> "$GITHUB_ENV" + - name: Extract metadata for Docker id: meta uses: docker/metadata-action@v3 @@ -105,6 +110,12 @@ jobs: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} flavor: | latest=${{ github.ref == 'refs/heads/master' }} + tags: | + type=schedule + type=ref,event=branch + type=ref,event=tag + type=ref,event=pr + type=raw,enable=${{ github.ref == 'refs/heads/master' }},value=${{ env.luckperms_version }} - name: Build and push Docker image uses: docker/build-push-action@v2 diff --git a/.gitignore b/.gitignore index 1cb4ee2b9..f74dbcc7f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +### LuckPerms Standalone ### +standalone/loader/data/ + ### Gradle & IntelliJ ### .gradle/ /.idea/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f55363882..278998629 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ Before reporting a bug or issue, please make sure that the issue is actually bei If you're unsure, feel free to ask using the above resources BEFORE making a report. -Bugs or issues should be reported using the [GitHub Issues tab](https://github.com/lucko/LuckPerms/issues). +Bugs or issues should be reported using the [GitHub Issues tab](https://github.com/LuckPerms/LuckPerms/issues). ### :pencil: Want to contribute code? #### Pull Requests diff --git a/README.md b/README.md index 2dae793b8..83e8f0c82 100644 --- a/README.md +++ b/README.md @@ -24,12 +24,12 @@ For more information, see the wiki article on [Why LuckPerms?](https://luckperms LuckPerms uses Gradle to handle dependencies & building. #### Requirements -* Java 8 JDK or newer +* Java 17 JDK or newer * Git #### Compiling from source ```sh -git clone https://github.com/lucko/LuckPerms.git +git clone https://github.com/LuckPerms/LuckPerms.git cd LuckPerms/ ./gradlew build ``` @@ -56,4 +56,4 @@ The project is split up into a few separate modules. * **Bukkit, BungeeCord, Fabric, Forge, Nukkit, Sponge & Velocity** - Each use the common module to implement plugins on the respective server platforms. ## License -LuckPerms is licensed under the permissive MIT license. Please see [`LICENSE.txt`](https://github.com/lucko/LuckPerms/blob/master/LICENSE.txt) for more info. +LuckPerms is licensed under the permissive MIT license. Please see [`LICENSE.txt`](https://github.com/LuckPerms/LuckPerms/blob/master/LICENSE.txt) for more info. diff --git a/build.gradle b/build.gradle index d8ac4cb96..c8431de6a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,10 @@ plugins { - id 'org.cadixdev.licenser' version '0.6.1' apply false + alias(libs.plugins.licenser) apply false } +import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import org.gradle.api.tasks.testing.logging.TestLogEvent + defaultTasks 'licenseFormat', 'build' subprojects { @@ -15,10 +18,20 @@ subprojects { sourceCompatibility = 1.8 targetCompatibility = 1.8 - tasks.withType(JavaCompile) { + tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' } + tasks.withType(Test).configureEach { + testLogging { + events = [TestLogEvent.PASSED, TestLogEvent.FAILED, TestLogEvent.SKIPPED] + exceptionFormat = TestExceptionFormat.FULL + showExceptions = true + showCauses = true + showStackTraces = true + } + } + jar { from '../LICENSE.txt' } diff --git a/bukkit-legacy/build.gradle b/bukkit-legacy/build.gradle index 772630d6d..2cbdccf4a 100644 --- a/bukkit-legacy/build.gradle +++ b/bukkit-legacy/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.github.johnrengelman.shadow' version '7.1.2' + alias(libs.plugins.shadow) } dependencies { @@ -29,7 +29,7 @@ shadowJar { relocate 'me.lucko.commodore', 'me.lucko.luckperms.lib.commodore' relocate 'org.mariadb.jdbc', 'me.lucko.luckperms.lib.mariadb' relocate 'com.mysql', 'me.lucko.luckperms.lib.mysql' - relocate 'com.impossibl', 'me.lucko.luckperms.lib.postgresql' + relocate 'org.postgresql', 'me.lucko.luckperms.lib.postgresql' relocate 'com.zaxxer.hikari', 'me.lucko.luckperms.lib.hikari' relocate 'com.mongodb', 'me.lucko.luckperms.lib.mongodb' relocate 'org.bson', 'me.lucko.luckperms.lib.bson' diff --git a/bukkit-legacy/loader/build.gradle b/bukkit-legacy/loader/build.gradle index 65bab3fdd..92b9a4e1d 100644 --- a/bukkit-legacy/loader/build.gradle +++ b/bukkit-legacy/loader/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.github.johnrengelman.shadow' + alias(libs.plugins.shadow) } repositories { diff --git a/bukkit/build.gradle b/bukkit/build.gradle index f7cae794f..db57631ab 100644 --- a/bukkit/build.gradle +++ b/bukkit/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.github.johnrengelman.shadow' version '7.1.2' + alias(libs.plugins.shadow) } repositories { @@ -39,7 +39,7 @@ shadowJar { relocate 'me.lucko.commodore', 'me.lucko.luckperms.lib.commodore' relocate 'org.mariadb.jdbc', 'me.lucko.luckperms.lib.mariadb' relocate 'com.mysql', 'me.lucko.luckperms.lib.mysql' - relocate 'com.impossibl', 'me.lucko.luckperms.lib.postgresql' + relocate 'org.postgresql', 'me.lucko.luckperms.lib.postgresql' relocate 'com.zaxxer.hikari', 'me.lucko.luckperms.lib.hikari' relocate 'com.mongodb', 'me.lucko.luckperms.lib.mongodb' relocate 'org.bson', 'me.lucko.luckperms.lib.bson' diff --git a/bukkit/loader/build.gradle b/bukkit/loader/build.gradle index 31b83217c..c41f2a81c 100644 --- a/bukkit/loader/build.gradle +++ b/bukkit/loader/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.github.johnrengelman.shadow' + alias(libs.plugins.shadow) } repositories { diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java index 4d46e15e1..bcba22774 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java @@ -217,7 +217,7 @@ protected void setupPlatformHooks() { * Vault in their onEnable without depending on us. * * Noteworthy discussion here: - * - https://github.com/lucko/LuckPerms/issues/1959 + * - https://github.com/LuckPerms/LuckPerms/issues/1959 * - https://hub.spigotmc.org/jira/browse/SPIGOT-5546 * - https://github.com/PaperMC/Paper/pull/3509 */ diff --git a/bukkit/src/main/resources/config.yml b/bukkit/src/main/resources/config.yml index 155b310a7..d65b1a5b9 100644 --- a/bukkit/src/main/resources/config.yml +++ b/bukkit/src/main/resources/config.yml @@ -8,7 +8,7 @@ # | | # # | WIKI: https://luckperms.net/wiki | # # | DISCORD: https://discord.gg/luckperms | # -# | BUG REPORTS: https://github.com/lucko/LuckPerms/issues | # +# | BUG REPORTS: https://github.com/LuckPerms/LuckPerms/issues | # # | | # # | Each option in this file is documented and explained here: | # # | ==> https://luckperms.net/wiki/Configuration | # @@ -262,6 +262,7 @@ broadcast-received-log-entries: true # Settings for Redis. # Port 6379 is used by default; set address to "host:port" if differs +# Multiple Redis nodes can be specified in the same format as a string list under the name "addresses". redis: enabled: false address: localhost diff --git a/bungee/build.gradle b/bungee/build.gradle index 00ffa4171..f46056304 100644 --- a/bungee/build.gradle +++ b/bungee/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.github.johnrengelman.shadow' version '7.1.2' + alias(libs.plugins.shadow) } dependencies { @@ -31,7 +31,7 @@ shadowJar { relocate 'me.lucko.commodore', 'me.lucko.luckperms.lib.commodore' relocate 'org.mariadb.jdbc', 'me.lucko.luckperms.lib.mariadb' relocate 'com.mysql', 'me.lucko.luckperms.lib.mysql' - relocate 'com.impossibl', 'me.lucko.luckperms.lib.postgresql' + relocate 'org.postgresql', 'me.lucko.luckperms.lib.postgresql' relocate 'com.zaxxer.hikari', 'me.lucko.luckperms.lib.hikari' relocate 'com.mongodb', 'me.lucko.luckperms.lib.mongodb' relocate 'org.bson', 'me.lucko.luckperms.lib.bson' diff --git a/bungee/loader/build.gradle b/bungee/loader/build.gradle index 10de5e9e5..de0e40b1f 100644 --- a/bungee/loader/build.gradle +++ b/bungee/loader/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.github.johnrengelman.shadow' + alias(libs.plugins.shadow) } dependencies { diff --git a/bungee/src/main/resources/config.yml b/bungee/src/main/resources/config.yml index 3e0633fff..be3313f0c 100644 --- a/bungee/src/main/resources/config.yml +++ b/bungee/src/main/resources/config.yml @@ -8,7 +8,7 @@ # | | # # | WIKI: https://luckperms.net/wiki | # # | DISCORD: https://discord.gg/luckperms | # -# | BUG REPORTS: https://github.com/lucko/LuckPerms/issues | # +# | BUG REPORTS: https://github.com/LuckPerms/LuckPerms/issues | # # | | # # | Each option in this file is documented and explained here: | # # | ==> https://luckperms.net/wiki/Configuration | # @@ -260,6 +260,7 @@ broadcast-received-log-entries: false # Settings for Redis. # Port 6379 is used by default; set address to "host:port" if differs +# Multiple Redis nodes can be specified in the same format as a string list under the name "addresses". redis: enabled: false address: localhost diff --git a/common/build.gradle b/common/build.gradle index a22461cef..c2b3f51f7 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -1,11 +1,16 @@ plugins { - id 'java-library' + id("java-library") + id("jacoco") } test { useJUnitPlatform {} } +jacocoTestReport { + dependsOn test +} + dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.1' testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.1' @@ -45,6 +50,11 @@ dependencies { exclude(module: 'adventure-api') } + api("net.kyori:adventure-text-minimessage:4.11.0") { + exclude(module: 'adventure-bom') + exclude(module: 'adventure-api') + } + api('net.kyori:event-api:3.0.0') { exclude(module: 'checker-qual') exclude(module: 'guava') @@ -74,10 +84,10 @@ dependencies { transitive = false } compileOnly 'com.zaxxer:HikariCP:4.0.3' - compileOnly 'redis.clients:jedis:3.5.2' + compileOnly 'redis.clients:jedis:4.4.3' compileOnly 'io.nats:jnats:2.16.4' compileOnly 'com.rabbitmq:amqp-client:5.12.0' compileOnly 'org.mongodb:mongodb-driver-legacy:4.5.0' - compileOnly 'com.impossibl.pgjdbc-ng:pgjdbc-ng:0.8.9' + compileOnly 'org.postgresql:postgresql:42.6.0' compileOnly 'org.yaml:snakeyaml:1.28' } diff --git a/common/src/main/java/me/lucko/luckperms/common/cache/BufferedRequest.java b/common/src/main/java/me/lucko/luckperms/common/cache/BufferedRequest.java index 93d938843..6189c03de 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cache/BufferedRequest.java +++ b/common/src/main/java/me/lucko/luckperms/common/cache/BufferedRequest.java @@ -86,6 +86,17 @@ public CompletableFuture request() { } } + /** + * Gets if the request buffer has been enqueued + * + * @return if the buffer is enqueued + */ + public boolean isEnqueued() { + synchronized (this.mutex) { + return this.processor != null; + } + } + /** * Requests the value, bypassing the buffer * diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/other/HolderEditor.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/other/HolderEditor.java index 25044ae6b..656f2d019 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/other/HolderEditor.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/other/HolderEditor.java @@ -84,7 +84,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, T target, ArgumentLis Message.EDITOR_START.send(sender); - WebEditorSession.createAndOpen(holders, Collections.emptyList(), sender, label, plugin); + WebEditorSession.create(holders, Collections.emptyList(), sender, label, plugin).open(); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java index 46b182dfb..5f55c03ce 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java @@ -107,7 +107,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, St Message.EDITOR_START.send(sender); - WebEditorSession.createAndOpen(holders, tracks, sender, label, plugin); + WebEditorSession.create(holders, tracks, sender, label, plugin).open(); } private enum Type { diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackEditor.java b/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackEditor.java index cc4efff64..61856d766 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackEditor.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackEditor.java @@ -97,7 +97,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, Track target, Argumen Message.EDITOR_START.send(sender); - WebEditorSession.createAndOpen(holders, Collections.singletonList(target), sender, label, plugin); + WebEditorSession.create(holders, Collections.singletonList(target), sender, label, plugin).open(); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java index 221e515dd..216915694 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java @@ -73,6 +73,7 @@ import static me.lucko.luckperms.common.config.generic.key.ConfigKeyFactory.mapKey; import static me.lucko.luckperms.common.config.generic.key.ConfigKeyFactory.notReloadable; import static me.lucko.luckperms.common.config.generic.key.ConfigKeyFactory.stringKey; +import static me.lucko.luckperms.common.config.generic.key.ConfigKeyFactory.stringListKey; /** * All of the {@link ConfigKey}s used by LuckPerms. @@ -639,6 +640,11 @@ private ConfigKeys() {} */ public static final ConfigKey REDIS_ADDRESS = notReloadable(stringKey("redis.address", null)); + /** + * The addresses of the redis servers (only for redis clusters) + */ + public static final ConfigKey> REDIS_ADDRESSES = notReloadable(stringListKey("redis.addresses", ImmutableList.of())); + /** * The username to connect with, or an empty string if it should use default */ @@ -719,6 +725,11 @@ private ConfigKeys() {} */ public static final ConfigKey BYTESOCKS_HOST = stringKey("bytesocks-host", "usersockets.luckperms.net"); + /** + * If TLS (https/wss) should be used when connecting to bytesocks + */ + public static final ConfigKey BYTESOCKS_USE_TLS = booleanKey("bytesocks-use-tls", true); + /** * The URL of the web editor */ diff --git a/common/src/main/java/me/lucko/luckperms/common/config/generic/key/ConfigKeyFactory.java b/common/src/main/java/me/lucko/luckperms/common/config/generic/key/ConfigKeyFactory.java index cd7da5a0c..add003435 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/generic/key/ConfigKeyFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/generic/key/ConfigKeyFactory.java @@ -28,6 +28,7 @@ import com.google.common.collect.ImmutableMap; import me.lucko.luckperms.common.config.generic.adapter.ConfigurationAdapter; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.function.Function; @@ -36,6 +37,7 @@ public interface ConfigKeyFactory { ConfigKeyFactory BOOLEAN = ConfigurationAdapter::getBoolean; ConfigKeyFactory STRING = ConfigurationAdapter::getString; + ConfigKeyFactory> STRING_LIST = ConfigurationAdapter::getStringList; ConfigKeyFactory LOWERCASE_STRING = (adapter, path, def) -> adapter.getString(path, def).toLowerCase(Locale.ROOT); ConfigKeyFactory> STRING_MAP = (config, path, def) -> ImmutableMap.copyOf(config.getStringMap(path, ImmutableMap.of())); @@ -56,6 +58,10 @@ static SimpleConfigKey stringKey(String path, String def) { return key(new Bound<>(STRING, path, def)); } + static SimpleConfigKey> stringListKey(String path, List def) { + return key(new Bound<>(STRING_LIST, path, def)); + } + static SimpleConfigKey lowercaseStringKey(String path, String def) { return key(new Bound<>(LOWERCASE_STRING, path, def)); } diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java index c66dcbb6a..99407c306 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java @@ -56,36 +56,35 @@ public enum Dependency { JAR_RELOCATOR( "me.lucko", "jar-relocator", - "1.4", - "1RsiF3BiVztjlfTA+svDCuoDSGFuSpTZYHvUK8yBx8I=" + "1.7", + "b30RhOF6kHiHl+O5suNLh/+eAr1iOFEFLXhwkHHDu4I=" ), - ADVENTURE( "me{}lucko", "adventure-api", - "4.11.0", - "7xrFaBbsTiQPZKBDzvDnTd8XIgOsHTy9qQICm3342GU=", + "4.13.0", + "gcmYlY1KDrzbnE9nB8rA44oBZKPSMvv9kNRefoEuud0=", Relocation.of("adventure", "net{}kyori{}adventure") ), ADVENTURE_PLATFORM( "me{}lucko", "adventure-platform-api", - "4.11.2", - "zCnxNgosme++TsheFaL+YHdtRIrp+oJhUiI8awsKgfQ=", + "4.13.0", + "Kk8IkEMVa9ITBfC3yocpcXQiZ9CwN9VxeWjKUD8I0n0=", Relocation.of("adventure", "net{}kyori{}adventure") ), ADVENTURE_PLATFORM_BUKKIT( "me{}lucko", "adventure-platform-bukkit", - "4.11.2", - "mrtYZvt00GH4oejuBJ2QEbFkmz1F+PGmthPwroSxCgo=", + "4.13.0", + "Rif/+xdGfRrlhxXYb2+jMFBnwnLQ2pnjAJ/BxWApins=", Relocation.of("adventure", "net{}kyori{}adventure") ), ADVENTURE_PLATFORM_BUNGEECORD( "me{}lucko", "adventure-platform-bungeecord", - "4.11.2", - "+WUdRdZ6qkacw3ha/R3ayLx46soMywGe70Zmnw4yha8=", + "4.13.0", + "MLk/qAgWC9YT2ImLq/sVo114V5Rk1jQr4jc57WAoO74=", Relocation.of("adventure", "net{}kyori{}adventure") ), EVENT( @@ -153,18 +152,11 @@ public enum Dependency { Relocation.of("mysql", "com{}mysql") ), POSTGRESQL_DRIVER( - "com{}impossibl{}pgjdbc-ng", - "pgjdbc-ng", - "0.8.9", - "WEYMezl02Rot2n2ATs7NABcvL9ceQ/oLA/XPduIEaWA=", - Relocation.of("postgresql", "com{}impossibl") - ), - POSTGRESQL_DRIVER_SPY( - "com{}impossibl{}pgjdbc-ng", - "spy", - "0.8.9", - "72ZuhpMy/4EYJZuSjBjGI5NGgdWmOpwjDHW9ISnqso8=", - Relocation.of("postgresql", "com{}impossibl") + "org{}postgresql", + "postgresql", + "42.6.0", + "uBfGekDJQkn9WdTmhuMyftDT0/rkJrINoPHnVlLPxGE=", + Relocation.of("postgresql", "org{}postgresql") ), H2_DRIVER_LEGACY( "com.h2database", @@ -246,8 +238,8 @@ public enum Dependency { JEDIS( "redis.clients", "jedis", - "3.5.2", - "jX3340YaYjHFQN2sA+GCo33LB4FuIYKgQUPUv2MK/Xo=", + "4.4.3", + "wwwoCDPCywcfoNwpvwP95kXYusXSTtXhuVrB31sxE0k=", Relocation.of("jedis", "redis{}clients{}jedis"), Relocation.of("commonspool2", "org{}apache{}commons{}pool2") ), diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java index 45b4e52a5..73b3b08e3 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java @@ -55,7 +55,7 @@ public class DependencyRegistry { .putAll(StorageType.MONGODB, Dependency.MONGODB_DRIVER_CORE, Dependency.MONGODB_DRIVER_LEGACY, Dependency.MONGODB_DRIVER_SYNC, Dependency.MONGODB_DRIVER_BSON) .putAll(StorageType.MARIADB, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, Dependency.HIKARI, Dependency.MARIADB_DRIVER) .putAll(StorageType.MYSQL, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, Dependency.HIKARI, Dependency.MYSQL_DRIVER) - .putAll(StorageType.POSTGRESQL, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, Dependency.HIKARI, Dependency.POSTGRESQL_DRIVER, Dependency.POSTGRESQL_DRIVER_SPY) + .putAll(StorageType.POSTGRESQL, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, Dependency.HIKARI, Dependency.POSTGRESQL_DRIVER) .putAll(StorageType.SQLITE, Dependency.SQLITE_DRIVER) .putAll(StorageType.H2, Dependency.H2_DRIVER) .build(); diff --git a/common/src/main/java/me/lucko/luckperms/common/http/BytesocksClient.java b/common/src/main/java/me/lucko/luckperms/common/http/BytesocksClient.java index 61affe512..b8cb50958 100644 --- a/common/src/main/java/me/lucko/luckperms/common/http/BytesocksClient.java +++ b/common/src/main/java/me/lucko/luckperms/common/http/BytesocksClient.java @@ -49,11 +49,11 @@ public class BytesocksClient extends AbstractHttpClient { * @param host the bytesocks host * @param userAgent the client user agent string */ - public BytesocksClient(OkHttpClient okHttpClient, String host, String userAgent) { + public BytesocksClient(OkHttpClient okHttpClient, String host, boolean tls, String userAgent) { super(okHttpClient); - this.httpUrl = "https://" + host + "/"; - this.wsUrl = "wss://" + host + "/"; + this.httpUrl = (tls ? "https://" : "http://") + host + "/"; + this.wsUrl = (tls ? "wss://" : "ws://") + host + "/"; this.userAgent = userAgent; } diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java index d192d9cbf..710d86bf7 100644 --- a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java +++ b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java @@ -39,9 +39,11 @@ import me.lucko.luckperms.common.util.DurationFormatter; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ComponentLike; +import net.kyori.adventure.text.JoinConfiguration; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.luckperms.api.context.Context; import net.luckperms.api.context.ContextSet; @@ -96,7 +98,7 @@ public interface Message { TextComponent OPEN_BRACKET = Component.text('('); TextComponent CLOSE_BRACKET = Component.text(')'); TextComponent FULL_STOP = Component.text('.'); - + Component PREFIX_COMPONENT = text() .color(GRAY) .append(text('[')) @@ -135,7 +137,7 @@ static TextComponent prefixed(ComponentLike component) { // " | |__) " // " |___ | " - return join(newline(), + return joinNewline( text() .append(text(" ", AQUA)) .append(text(" __ ", DARK_AQUA)) @@ -175,7 +177,7 @@ static TextComponent prefixed(ComponentLike component) { .color(GRAY) ); - Args2 FIRST_TIME_SETUP = (label, username) -> join(newline(), + Args2 FIRST_TIME_SETUP = (label, username) -> joinNewline( // "&3It seems that no permissions have been setup yet!" // "&3Before you can use any of the LuckPerms commands in-game, you need to use the console to give yourself access." // "&3Open your console and run:" @@ -278,7 +280,7 @@ static TextComponent prefixed(ComponentLike component) { .append(FULL_STOP) ); - Args1 LOG = action -> join(newline(), + Args1 LOG = action -> joinNewline( // "&3LOG &3&l> &8(&e{}&8) [&a{}&8] (&b{}&8)" // "&3LOG &3&l> &f{}" prefixed(text() @@ -489,7 +491,7 @@ static TextComponent prefixed(ComponentLike component) { .append(CLOSE_BRACKET) )); - Args2 COMMAND_USAGE_DETAILED_HEADER = (name, usage) -> join(newline(), + Args2 COMMAND_USAGE_DETAILED_HEADER = (name, usage) -> joinNewline( // "&3&lCommand Usage &3- &b{}" // "&b> &7{}" prefixed(text() @@ -666,7 +668,7 @@ static TextComponent prefixed(ComponentLike component) { .append(FULL_STOP) ); - Args0 VERBOSE_OFF_COMMAND_NO_CHECKS = () -> join(newline(), + Args0 VERBOSE_OFF_COMMAND_NO_CHECKS = () -> joinNewline( // &bThe command execution completed, but no permission checks were made. // &7This might be because the plugin runs commands in the background (async). You can still use verbose manually to detect checks made like this. prefixed(translatable() @@ -705,7 +707,7 @@ static TextComponent prefixed(ComponentLike component) { .args(translatable("luckperms.command.verbose.disabled-term", RED)) ); - Args1 VERBOSE_RESULTS_URL = url -> join(newline(), + Args1 VERBOSE_RESULTS_URL = url -> joinNewline( // "&aVerbose results URL:" // prefixed(translatable() @@ -731,7 +733,7 @@ static TextComponent prefixed(ComponentLike component) { .append(FULL_STOP) ); - Args1 TREE_URL = url -> join(newline(), + Args1 TREE_URL = url -> joinNewline( // "&aPermission tree URL:" // prefixed(translatable() @@ -886,7 +888,7 @@ static TextComponent prefixed(ComponentLike component) { .apply(builder -> { boolean explicitGlobalContext = !plugin.getConfiguration().getContextsFile().getDefaultContexts().isEmpty(); - Component hover = join(newline(), + Component hover = joinNewline( text() .append(text('>', DARK_AQUA)) .append(space()) @@ -930,7 +932,7 @@ static TextComponent prefixed(ComponentLike component) { .apply(builder -> { boolean explicitGlobalContext = !plugin.getConfiguration().getContextsFile().getDefaultContexts().isEmpty(); - Component hover = join(newline(), + Component hover = joinNewline( text() .append(text('>', DARK_AQUA)) .append(space()) @@ -949,7 +951,7 @@ static TextComponent prefixed(ComponentLike component) { }) .build(); - Args2 APPLY_EDITS_SESSION_UNKNOWN = (code, label) -> join(newline(), + Args2 APPLY_EDITS_SESSION_UNKNOWN = (code, label) -> joinNewline( // "&4The changes received from the web editor were not made in a session started on this server!" // "&cAre you sure you're running the /lp applyedits command in the right place?" // "&cTo ignore this warning and apply the changes anyway, run: &4/lp applyedits --force" @@ -967,7 +969,7 @@ static TextComponent prefixed(ComponentLike component) { .append(text("/" + label + " applyedits " + code + " --force", DARK_RED))) ); - Args2 APPLY_EDITS_SESSION_APPLIED_ALREADY = (code, label) -> join(newline(), + Args2 APPLY_EDITS_SESSION_APPLIED_ALREADY = (code, label) -> joinNewline( // "&4The changes received from the web editor are based on an initial session which has already been applied!" // "&cTo avoid conflicts, you should never re-use the same editor session after the changes from it have been applied once already." // "&cTo ignore this warning and apply the changes anyway, run: /lp applyedits --force" @@ -1149,7 +1151,7 @@ static TextComponent prefixed(ComponentLike component) { .key("luckperms.command.editor.start") ); - Args1 EDITOR_URL = url -> join(newline(), + Args1 EDITOR_URL = url -> joinNewline( // "&aClick the link below to open the editor:" // prefixed(translatable() @@ -1183,7 +1185,7 @@ static TextComponent prefixed(ComponentLike component) { .append(FULL_STOP) ); - Args4 EDITOR_SOCKET_UNTRUSTED = (nonce, browser, cmdLabel, console) -> join(newline(), + Args4 EDITOR_SOCKET_UNTRUSTED = (nonce, browser, cmdLabel, console) -> joinNewline( // "&bAn editor window has connected, but it is not yet trusted." // "&8(&7session id = &faaaaa&7, browser = &fChrome on Windows 10&8)" // "&7If it was you, &aclick here&7 to trust the session!" @@ -1229,7 +1231,7 @@ static TextComponent prefixed(ComponentLike component) { })) ); - Args0 EDITOR_SOCKET_TRUST_SUCCESS = () -> join(newline(), + Args0 EDITOR_SOCKET_TRUST_SUCCESS = () -> joinNewline( // "&aThe editor session has been marked as trusted." // "&7In the future, connections from the same browser will be trusted automatically." // "&7The plugin will now attempt to establish a connection with the editor..." @@ -1533,7 +1535,7 @@ static TextComponent prefixed(ComponentLike component) { ) ); - Args2> INFO = (plugin, storageMeta) -> join(newline(), + Args2> INFO = (plugin, storageMeta) -> joinNewline( // "&2Running &bLuckPerms v{}&2 by &bLuck&2." // "&f- &3Platform: &f{}" // "&f- &3Server Brand: &f{}" @@ -1768,7 +1770,7 @@ static TextComponent prefixed(ComponentLike component) { String holderName = holder.getType() == HolderType.GROUP ? holder.getIdentifier().getName() : holder.getPlainDisplayName(); boolean explicitGlobalContext = !holder.getPlugin().getConfiguration().getContextsFile().getDefaultContexts().isEmpty(); - Component hover = join(newline(), + Component hover = joinNewline( text() .append(text('>', DARK_AQUA)) .append(space()) @@ -1787,7 +1789,7 @@ static TextComponent prefixed(ComponentLike component) { }) .build(); - Args3 PERMISSION_INFO_TEMPORARY_NODE_ENTRY = (node, holder, label) -> join(newline(), + Args3 PERMISSION_INFO_TEMPORARY_NODE_ENTRY = (node, holder, label) -> joinNewline( text() .append(text('>', DARK_AQUA)) .append(space()) @@ -1798,7 +1800,7 @@ static TextComponent prefixed(ComponentLike component) { String holderName = holder.getType() == HolderType.GROUP ? holder.getIdentifier().getName() : holder.getPlainDisplayName(); boolean explicitGlobalContext = !holder.getPlugin().getConfiguration().getContextsFile().getDefaultContexts().isEmpty(); - Component hover = join(newline(), + Component hover = joinNewline( text() .append(text('>', DARK_AQUA)) .append(space()) @@ -1868,7 +1870,7 @@ static TextComponent prefixed(ComponentLike component) { String holderName = holder.getType() == HolderType.GROUP ? holder.getIdentifier().getName() : holder.getPlainDisplayName(); boolean explicitGlobalContext = !holder.getPlugin().getConfiguration().getContextsFile().getDefaultContexts().isEmpty(); - Component hover = join(newline(), + Component hover = joinNewline( text() .append(text('>', DARK_AQUA)) .append(space()) @@ -1890,7 +1892,7 @@ static TextComponent prefixed(ComponentLike component) { .append(formatContextSetBracketed(node.getContexts(), empty())) .build(); - Args3 PARENT_INFO_TEMPORARY_NODE_ENTRY = (node, holder, label) -> join(newline(), + Args3 PARENT_INFO_TEMPORARY_NODE_ENTRY = (node, holder, label) -> joinNewline( text() .append(text('>', DARK_AQUA)) .append(space()) @@ -1901,7 +1903,7 @@ static TextComponent prefixed(ComponentLike component) { String holderName = holder.getType() == HolderType.GROUP ? holder.getIdentifier().getName() : holder.getPlainDisplayName(); boolean explicitGlobalContext = !holder.getPlugin().getConfiguration().getContextsFile().getDefaultContexts().isEmpty(); - Component hover = join(newline(), + Component hover = joinNewline( text() .append(text('>', DARK_AQUA)) .append(text(node.getGroupName(), WHITE)), @@ -1938,7 +1940,7 @@ static TextComponent prefixed(ComponentLike component) { .append(text(':')) ); - Args3 LIST_TRACKS_ENTRY = (name, contextSet, path) -> join(newline(), + Args3 LIST_TRACKS_ENTRY = (name, contextSet, path) -> joinNewline( // "&3> &a{}: {}" // "&7 ({}&7)" text() @@ -2040,7 +2042,7 @@ static TextComponent prefixed(ComponentLike component) { ) ); - Args5 PERMISSION_CHECK_RESULT = (permission, result, processor, causeNode, context) -> join(newline(), + Args5 PERMISSION_CHECK_RESULT = (permission, result, processor, causeNode, context) -> joinNewline( // &aPermission check for &b{}&a: // &3Result: {} // &3Processor: &f{} @@ -2516,7 +2518,7 @@ static TextComponent prefixed(ComponentLike component) { HolderType originType = HolderType.valueOf(origin.getOrigin().getType().toUpperCase(Locale.ROOT)); boolean explicitGlobalContext = !holder.getPlugin().getConfiguration().getContextsFile().getDefaultContexts().isEmpty(); - Component hover = join(newline(), + Component hover = joinNewline( text() .append(text('>', DARK_AQUA)) .append(space()) @@ -2591,7 +2593,7 @@ static TextComponent prefixed(ComponentLike component) { HolderType originType = HolderType.valueOf(origin.getOrigin().getType().toUpperCase(Locale.ROOT)); boolean explicitGlobalContext = !holder.getPlugin().getConfiguration().getContextsFile().getDefaultContexts().isEmpty(); - Component hover = join(newline(), + Component hover = joinNewline( text() .append(text('>', DARK_AQUA)) .append(space()) @@ -2987,7 +2989,7 @@ static TextComponent prefixed(ComponentLike component) { .append(FULL_STOP) ); - Args3 BULK_UPDATE_STATISTICS = (nodes, users, groups) -> join(newline(), + Args3 BULK_UPDATE_STATISTICS = (nodes, users, groups) -> joinNewline( // "&bTotal affected nodes: &a{}" // "&bTotal affected users: &a{}" // "&bTotal affected groups: &a{}" @@ -3069,7 +3071,7 @@ static TextComponent prefixed(ComponentLike component) { }) ); - Args1 TRANSLATIONS_DOWNLOAD_PROMPT = label -> join(newline(), + Args1 TRANSLATIONS_DOWNLOAD_PROMPT = label -> joinNewline( // "Use /lp translations install to download and install up-to-date versions of these translations provided by the community." // "Please note that this will override any changes you've made for these languages." prefixed(translatable() @@ -3113,7 +3115,7 @@ static TextComponent prefixed(ComponentLike component) { .append(FULL_STOP) ); - Args4 USER_INFO_GENERAL = (username, uuid, uuidType, online) -> join(newline(), + Args4 USER_INFO_GENERAL = (username, uuid, uuidType, online) -> joinNewline( // "&b&l> &bUser Info: &f{}" // "&f- &3UUID: &f{}" // "&f &7(type: {}&7)" @@ -3147,7 +3149,7 @@ static TextComponent prefixed(ComponentLike component) { .append(online ? translatable("luckperms.command.user.info.status.online", GREEN) : translatable("luckperms.command.user.info.status.offline", RED))) ); - Args6>> USER_INFO_CONTEXTUAL_DATA = (active, contexts, prefix, suffix, primaryGroup, meta) -> join(newline(), + Args6>> USER_INFO_CONTEXTUAL_DATA = (active, contexts, prefix, suffix, primaryGroup, meta) -> joinNewline( // "&f- &aContextual Data: &7(mode: {}&7)" // " &3Contexts: {}" // " &3Prefix: {}" @@ -3234,7 +3236,7 @@ static TextComponent prefixed(ComponentLike component) { .build() ) .collect(Collectors.toList()); - builder.append(join(space(), entries)); + builder.append(join(JoinConfiguration.separator(space()), entries)); } })) ); @@ -3261,7 +3263,7 @@ static TextComponent prefixed(ComponentLike component) { .append(formatContextSetBracketed(node.getContexts(), empty())) ); - Args1 INFO_PARENT_TEMPORARY_NODE_ENTRY = node -> join(newline(), + Args1 INFO_PARENT_TEMPORARY_NODE_ENTRY = node -> joinNewline( prefixed(text() .append(text(" > ", DARK_AQUA)) .append(text(node.getGroupName(), WHITE)) @@ -3390,7 +3392,7 @@ static TextComponent prefixed(ComponentLike component) { .append(FULL_STOP) ); - Args1 USER_PROMOTE_ERROR_MALFORMED = name -> join(newline(), + Args1 USER_PROMOTE_ERROR_MALFORMED = name -> joinNewline( // "&aThe next group on the track, &b{}&a, no longer exists. Unable to promote user." // "&aEither create the group, or remove it from the track and try again." prefixed(translatable() @@ -3444,7 +3446,7 @@ static TextComponent prefixed(ComponentLike component) { .append(FULL_STOP) ); - Args1 USER_DEMOTE_ERROR_MALFORMED = name -> join(newline(), + Args1 USER_DEMOTE_ERROR_MALFORMED = name -> joinNewline( // "&aThe previous group on the track, &b{}&a, no longer exists. Unable to demote user." // "&aEither create the group, or remove it from the track and try again." prefixed(translatable() @@ -3461,7 +3463,7 @@ static TextComponent prefixed(ComponentLike component) { .append(FULL_STOP)) ); - Args3 GROUP_INFO_GENERAL = (name, displayName, weight) -> join(newline(), + Args3 GROUP_INFO_GENERAL = (name, displayName, weight) -> joinNewline( // "&b&l> &bGroup Info: &f{}" // "&f- &3Display Name: &f{}" // "&f- &3Weight: &f{}" @@ -3486,7 +3488,7 @@ static TextComponent prefixed(ComponentLike component) { .append(weight.isPresent() ? text(weight.getAsInt(), WHITE) : translatable("luckperms.command.generic.contextual-data.null-result", WHITE))) ); - Args3>> GROUP_INFO_CONTEXTUAL_DATA = (prefix, suffix, meta) -> join(newline(), + Args3>> GROUP_INFO_CONTEXTUAL_DATA = (prefix, suffix, meta) -> joinNewline( // "&f- &aContextual Data: &7(mode: &8server&7)" // " &3Prefix: {}" // " &3Suffix: {}" @@ -3559,7 +3561,7 @@ static TextComponent prefixed(ComponentLike component) { .build() ) .collect(Collectors.toList()); - builder.append(join(space(), entries)); + builder.append(join(JoinConfiguration.separator(space()), entries)); } })) ); @@ -3615,7 +3617,7 @@ static TextComponent prefixed(ComponentLike component) { .append(FULL_STOP) ); - Args2 TRACK_INFO = (name, path) -> join(newline(), + Args2 TRACK_INFO = (name, path) -> joinNewline( // "&b&l> &bShowing Track: &f{}" + "\n" + // "&f- &7Path: &f{}", prefixed(text() @@ -3721,7 +3723,7 @@ static TextComponent prefixed(ComponentLike component) { .append(FULL_STOP) ); - Args2 LOG_ENTRY = (pos, action) -> join(newline(), + Args2 LOG_ENTRY = (pos, action) -> joinNewline( // "&b#{} &8(&7{} ago&8) &8(&e{}&8) [&a{}&8] (&b{}&8)" // "&7> &f{}" prefixed(text() @@ -3982,7 +3984,7 @@ static TextComponent prefixed(ComponentLike component) { .append(FULL_STOP) ); - Args2 EXPORT_WEB_SUCCESS = (pasteId, label) -> join(newline(), + Args2 EXPORT_WEB_SUCCESS = (pasteId, label) -> joinNewline( // "&aExport code: &7{}" // "&7Use the following command to import:" // "&a/{} import {} --upload" @@ -3995,7 +3997,8 @@ static TextComponent prefixed(ComponentLike component) { .key("luckperms.command.export.web.import-command-description") .color(GRAY) .append(text(":")), - text("/" + label + " import " + pasteId + " --upload", GREEN)); + text("/" + label + " import " + pasteId + " --upload", GREEN) + ); Args1 IMPORT_FILE_DOESNT_EXIST = file -> prefixed(text() // "&cError: File &4{}&c does not exist." @@ -4150,9 +4153,18 @@ static TextComponent prefixed(ComponentLike component) { ); static Component formatColoredValue(String value) { - return LegacyComponentSerializer.legacyAmpersand().deserialize(value).toBuilder() - .hoverEvent(HoverEvent.showText(text(value, WHITE))) - .build(); + boolean containsLegacyFormattingCharacter = value.indexOf(LegacyComponentSerializer.AMPERSAND_CHAR) != -1 + || value.indexOf(LegacyComponentSerializer.SECTION_CHAR) != -1; + + HoverEvent hover = HoverEvent.showText(text(value, WHITE)); + + if (containsLegacyFormattingCharacter) { + return LegacyComponentSerializer.legacyAmpersand().deserialize(value).toBuilder() + .hoverEvent(hover) + .build(); + } else { + return MiniMessage.miniMessage().deserialize(value).hoverEvent(hover); + } } static Component formatContextBracketed(String key, String value) { @@ -4320,6 +4332,10 @@ static Component formatTristate(Tristate tristate) { } } + static Component joinNewline(final ComponentLike... components) { + return join(JoinConfiguration.newlines(), components); + } + interface Args0 { Component build(); diff --git a/common/src/main/java/me/lucko/luckperms/common/messaging/MessagingFactory.java b/common/src/main/java/me/lucko/luckperms/common/messaging/MessagingFactory.java index c7a6271e5..7a2800779 100644 --- a/common/src/main/java/me/lucko/luckperms/common/messaging/MessagingFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/messaging/MessagingFactory.java @@ -43,6 +43,8 @@ import net.luckperms.api.messenger.MessengerProvider; import org.checkerframework.checker.nullness.qual.NonNull; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; public class MessagingFactory

{ @@ -194,6 +196,7 @@ private class RedisMessengerProvider implements MessengerProvider { LuckPermsConfiguration config = getPlugin().getConfiguration(); String address = config.get(ConfigKeys.REDIS_ADDRESS); + List addresses = config.get(ConfigKeys.REDIS_ADDRESSES); String username = config.get(ConfigKeys.REDIS_USERNAME); String password = config.get(ConfigKeys.REDIS_PASSWORD); if (password.isEmpty()) { @@ -204,7 +207,18 @@ private class RedisMessengerProvider implements MessengerProvider { } boolean ssl = config.get(ConfigKeys.REDIS_SSL); - redis.init(address, username, password, ssl); + if (!addresses.isEmpty()) { + // redis cluster + addresses = new ArrayList<>(addresses); + if (address != null) { + addresses.add(address); + } + redis.init(addresses, username, password, ssl); + } else { + // redis pool + redis.init(address, username, password, ssl); + } + return redis; } } diff --git a/common/src/main/java/me/lucko/luckperms/common/messaging/postgres/PostgresMessenger.java b/common/src/main/java/me/lucko/luckperms/common/messaging/postgres/PostgresMessenger.java index 1f73fdf54..a79a9b63f 100644 --- a/common/src/main/java/me/lucko/luckperms/common/messaging/postgres/PostgresMessenger.java +++ b/common/src/main/java/me/lucko/luckperms/common/messaging/postgres/PostgresMessenger.java @@ -25,8 +25,6 @@ package me.lucko.luckperms.common.messaging.postgres; -import com.impossibl.postgres.api.jdbc.PGConnection; -import com.impossibl.postgres.api.jdbc.PGNotificationListener; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.plugin.scheduler.SchedulerTask; import me.lucko.luckperms.common.storage.implementation.sql.SqlStorage; @@ -34,12 +32,17 @@ import net.luckperms.api.messenger.Messenger; import net.luckperms.api.messenger.message.OutgoingMessage; import org.checkerframework.checker.nullness.qual.NonNull; +import org.postgresql.PGConnection; +import org.postgresql.PGNotification; +import org.postgresql.util.PSQLException; +import java.net.SocketException; +import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.Statement; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; /** * An implementation of {@link Messenger} using Postgres. @@ -68,7 +71,7 @@ public void init() { @Override public void sendOutgoingMessage(@NonNull OutgoingMessage outgoingMessage) { - try (PGConnection connection = this.sqlStorage.getConnectionFactory().getConnection().unwrap(PGConnection.class)) { + try (Connection connection = this.sqlStorage.getConnectionFactory().getConnection()) { try (PreparedStatement ps = connection.prepareStatement("SELECT pg_notify(?, ?)")) { ps.setString(1, CHANNEL); ps.setString(2, outgoingMessage.asEncodedString()); @@ -122,47 +125,60 @@ private boolean checkAndReopenConnection(boolean firstStartup) { } } - private class NotificationListener implements PGNotificationListener, AutoCloseable { - private final CountDownLatch latch = new CountDownLatch(1); - private final AtomicBoolean listening = new AtomicBoolean(false); + private class NotificationListener implements AutoCloseable { + private static final int RECEIVE_TIMEOUT_MILLIS = 1000; - public void listenAndBind() { - try (PGConnection connection = PostgresMessenger.this.sqlStorage.getConnectionFactory().getConnection().unwrap(PGConnection.class)) { - connection.addNotificationListener(CHANNEL, this); + private final AtomicBoolean open = new AtomicBoolean(true); + private final AtomicReference listeningThread = new AtomicReference<>(); + public void listenAndBind() { + try (Connection connection = PostgresMessenger.this.sqlStorage.getConnectionFactory().getConnection()) { try (Statement s = connection.createStatement()) { s.execute("LISTEN \"" + CHANNEL + "\""); } - this.listening.set(true); - this.latch.await(); + PGConnection pgConnection = connection.unwrap(PGConnection.class); + this.listeningThread.set(Thread.currentThread()); + + while (this.open.get()) { + PGNotification[] notifications = pgConnection.getNotifications(RECEIVE_TIMEOUT_MILLIS); + if (notifications != null) { + for (PGNotification notification : notifications) { + handleNotification(notification); + } + } + } + + } catch (PSQLException e) { + if (!(e.getCause() instanceof SocketException && e.getCause().getMessage().equals("Socket closed"))) { + e.printStackTrace(); + } } catch (Exception e) { e.printStackTrace(); } finally { - this.listening.set(false); + this.listeningThread.set(null); } } public boolean isListening() { - return this.listening.get(); + return this.listeningThread.get() != null; } - @Override - public void notification(int processId, String channelName, String payload) { - if (!CHANNEL.equals(channelName)) { + public void handleNotification(PGNotification notification) { + if (!CHANNEL.equals(notification.getName())) { return; } - PostgresMessenger.this.consumer.consumeIncomingMessageAsString(payload); - } - - @Override - public void closed() { - this.latch.countDown(); + PostgresMessenger.this.consumer.consumeIncomingMessageAsString(notification.getParameter()); } @Override public void close() { - this.latch.countDown(); + if (this.open.compareAndSet(true, false)) { + Thread thread = this.listeningThread.get(); + if (thread != null) { + thread.interrupt(); + } + } } } diff --git a/common/src/main/java/me/lucko/luckperms/common/messaging/redis/RedisMessenger.java b/common/src/main/java/me/lucko/luckperms/common/messaging/redis/RedisMessenger.java index 8fec0f7d7..cfe9ac5a5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/messaging/redis/RedisMessenger.java +++ b/common/src/main/java/me/lucko/luckperms/common/messaging/redis/RedisMessenger.java @@ -30,11 +30,18 @@ import net.luckperms.api.messenger.Messenger; import net.luckperms.api.messenger.message.OutgoingMessage; import org.checkerframework.checker.nullness.qual.NonNull; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; -import redis.clients.jedis.JedisPoolConfig; +import redis.clients.jedis.DefaultJedisClientConfig; +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.JedisClientConfig; +import redis.clients.jedis.JedisCluster; +import redis.clients.jedis.JedisPooled; import redis.clients.jedis.JedisPubSub; import redis.clients.jedis.Protocol; +import redis.clients.jedis.UnifiedJedis; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; /** * An implementation of {@link Messenger} using Redis. @@ -45,7 +52,7 @@ public class RedisMessenger implements Messenger { private final LuckPermsPlugin plugin; private final IncomingMessageConsumer consumer; - private /* final */ JedisPool jedisPool; + private /* final */ UnifiedJedis jedis; private /* final */ Subscription sub; private boolean closing = false; @@ -54,57 +61,74 @@ public RedisMessenger(LuckPermsPlugin plugin, IncomingMessageConsumer consumer) this.consumer = consumer; } + public void init(List addresses, String username, String password, boolean ssl) { + Set hosts = addresses.stream().map(RedisMessenger::parseAddress).collect(Collectors.toSet()); + this.init(new JedisCluster(hosts, jedisConfig(username, password, ssl))); + } + public void init(String address, String username, String password, boolean ssl) { + this.init(new JedisPooled(parseAddress(address), jedisConfig(username, password, ssl))); + } + + private void init(UnifiedJedis jedis) { + this.jedis = jedis; + this.sub = new Subscription(this); + this.plugin.getBootstrap().getScheduler().executeAsync(this.sub); + } + + private static JedisClientConfig jedisConfig(String username, String password, boolean ssl) { + return DefaultJedisClientConfig.builder() + .user(username) + .password(password) + .ssl(ssl) + .timeoutMillis(Protocol.DEFAULT_TIMEOUT) + .build(); + } + + private static HostAndPort parseAddress(String address) { String[] addressSplit = address.split(":"); String host = addressSplit[0]; int port = addressSplit.length > 1 ? Integer.parseInt(addressSplit[1]) : Protocol.DEFAULT_PORT; - - if (username == null) { - this.jedisPool = new JedisPool(new JedisPoolConfig(), host, port, Protocol.DEFAULT_TIMEOUT, password, ssl); - } else { - this.jedisPool = new JedisPool(new JedisPoolConfig(), host, port, Protocol.DEFAULT_TIMEOUT, username, password, ssl); - } - - this.sub = new Subscription(); - this.plugin.getBootstrap().getScheduler().executeAsync(this.sub); + return new HostAndPort(host, port); } @Override public void sendOutgoingMessage(@NonNull OutgoingMessage outgoingMessage) { - try (Jedis jedis = this.jedisPool.getResource()) { - jedis.publish(CHANNEL, outgoingMessage.asEncodedString()); - } catch (Exception e) { - e.printStackTrace(); - } + this.jedis.publish(CHANNEL, outgoingMessage.asEncodedString()); } @Override public void close() { this.closing = true; this.sub.unsubscribe(); - this.jedisPool.destroy(); + this.jedis.close(); } - private class Subscription extends JedisPubSub implements Runnable { + private static class Subscription extends JedisPubSub implements Runnable { + private final RedisMessenger messenger; + + private Subscription(RedisMessenger messenger) { + this.messenger = messenger; + } @Override public void run() { boolean first = true; - while (!RedisMessenger.this.closing && !Thread.interrupted() && !RedisMessenger.this.jedisPool.isClosed()) { - try (Jedis jedis = RedisMessenger.this.jedisPool.getResource()) { + while (!this.messenger.closing && !Thread.interrupted() && this.isRedisAlive()) { + try { if (first) { first = false; } else { - RedisMessenger.this.plugin.getLogger().info("Redis pubsub connection re-established"); + this.messenger.plugin.getLogger().info("Redis pubsub connection re-established"); } - jedis.subscribe(this, CHANNEL); // blocking call + this.messenger.jedis.subscribe(this, CHANNEL); // blocking call } catch (Exception e) { - if (RedisMessenger.this.closing) { + if (this.messenger.closing) { return; } - RedisMessenger.this.plugin.getLogger().warn("Redis pubsub connection dropped, trying to re-open the connection", e); + this.messenger.plugin.getLogger().warn("Redis pubsub connection dropped, trying to re-open the connection", e); try { unsubscribe(); } catch (Exception ignored) { @@ -126,8 +150,19 @@ public void onMessage(String channel, String msg) { if (!channel.equals(CHANNEL)) { return; } - RedisMessenger.this.consumer.consumeIncomingMessageAsString(msg); + this.messenger.consumer.consumeIncomingMessageAsString(msg); } - } + private boolean isRedisAlive() { + UnifiedJedis jedis = this.messenger.jedis; + + if (jedis instanceof JedisPooled) { + return !((JedisPooled) jedis).getPool().isClosed(); + } else if (jedis instanceof JedisCluster) { + return !((JedisCluster) jedis).getClusterNodes().isEmpty(); + } else { + throw new RuntimeException("Unknown jedis type: " + jedis.getClass().getName()); + } + } + } } diff --git a/common/src/main/java/me/lucko/luckperms/common/model/nodemap/RecordedNodeMap.java b/common/src/main/java/me/lucko/luckperms/common/model/nodemap/RecordedNodeMap.java index e655c2ccf..40a12aaf4 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/nodemap/RecordedNodeMap.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/nodemap/RecordedNodeMap.java @@ -27,6 +27,8 @@ import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableSet; +import me.lucko.luckperms.common.model.manager.group.GroupManager; +import me.lucko.luckperms.common.node.types.Inheritance; import me.lucko.luckperms.common.util.Difference; import net.luckperms.api.context.ContextSet; import net.luckperms.api.context.ImmutableContextSet; @@ -86,6 +88,12 @@ public Difference exportChanges(Predicate> onlyIf) { } } + public Difference addDefaultNodeToChangeSet() { + Difference diff = new Difference<>(); + diff.recordChange(Difference.ChangeType.ADD, Inheritance.builder(GroupManager.DEFAULT_GROUP_NAME).build()); + return record(diff); + } + private Difference record(Difference result) { this.lock.lock(); try { diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java index 2f4b14cf7..fc662324e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java @@ -148,8 +148,17 @@ public final void enable() { .callTimeout(15, TimeUnit.SECONDS) .build(); - this.bytebin = new BytebinClient(this.httpClient, getConfiguration().get(ConfigKeys.BYTEBIN_URL), "luckperms"); - this.bytesocks = new BytesocksClient(this.httpClient, getConfiguration().get(ConfigKeys.BYTESOCKS_HOST), "luckperms/editor"); + this.bytebin = new BytebinClient( + this.httpClient, + getConfiguration().get(ConfigKeys.BYTEBIN_URL), + "luckperms" + ); + this.bytesocks = new BytesocksClient( + this.httpClient, + getConfiguration().get(ConfigKeys.BYTESOCKS_HOST), + getConfiguration().get(ConfigKeys.BYTESOCKS_USE_TLS), + "luckperms/editor" + ); this.webEditorStore = new WebEditorStore(this); // init translation repo and update bundle files @@ -175,7 +184,7 @@ public final void enable() { this.fileWatcher = new FileWatcher(this, getBootstrap().getDataDirectory()); } catch (Throwable e) { // catch throwable here, seems some JVMs throw UnsatisfiedLinkError when trying - // to create a watch service. see: https://github.com/lucko/LuckPerms/issues/2066 + // to create a watch service. see: https://github.com/LuckPerms/LuckPerms/issues/2066 getLogger().warn("Error occurred whilst trying to create a file watcher:", e); } } @@ -451,6 +460,10 @@ public LuckPermsConfiguration getConfiguration() { return this.configuration; } + public OkHttpClient getHttpClient() { + return this.httpClient; + } + @Override public BytebinClient getBytebin() { return this.bytebin; diff --git a/common/src/main/java/me/lucko/luckperms/common/sender/AbstractSender.java b/common/src/main/java/me/lucko/luckperms/common/sender/AbstractSender.java index 0a72cd950..9d12ca53d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/sender/AbstractSender.java +++ b/common/src/main/java/me/lucko/luckperms/common/sender/AbstractSender.java @@ -31,7 +31,12 @@ import net.kyori.adventure.text.TextComponent; import net.luckperms.api.util.Tristate; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.UUID; /** * Simple implementation of {@link Sender} using a {@link SenderFactory} diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/FileActionLogger.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/FileActionLogger.java index 12a15d375..263034507 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/FileActionLogger.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/FileActionLogger.java @@ -131,6 +131,11 @@ public void flush() { } public Log getLog() throws IOException { + // if there is log content waiting to be written, flush immediately before trying to read + if (this.saveBuffer.isEnqueued()) { + this.saveBuffer.requestDirectly(); + } + if (!Files.exists(this.contentFile)) { return Log.empty(); } diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java index 81409aa9c..ee68fb342 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java @@ -399,6 +399,13 @@ public void saveUser(User user) throws SQLException { return true; }); + // if the user only has the default group, delete their data + boolean isDefaultUser = !this.plugin.getUserManager().isNonDefaultUser(user); + if (changes != null && isDefaultUser) { + user.normalData().addDefaultNodeToChangeSet(); + changes = null; + } + if (changes == null) { try (Connection c = this.connectionFactory.getConnection()) { deleteUser(c, user.getUniqueId()); diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/file/H2ConnectionFactory.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/file/H2ConnectionFactory.java index b0c7808b2..cc36994ed 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/file/H2ConnectionFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/file/H2ConnectionFactory.java @@ -28,6 +28,7 @@ import me.lucko.luckperms.common.dependencies.Dependency; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import java.io.IOException; import java.lang.reflect.Constructor; import java.nio.file.Files; import java.nio.file.Path; @@ -123,6 +124,12 @@ public void run(H2ConnectionFactory newFactory) throws Exception { this.plugin.getLogger().warn("[DB Upgrade] Found an old (v1) H2 database file. LuckPerms will now attempt to upgrade it to v2 (this is a one time operation)."); + try { + Files.deleteIfExists(tempMigrationFile); + } catch (IOException e) { + this.plugin.getLogger().warn("[DB Upgrade] Unable to delete temporary data from a previous migration attempt", e); + } + this.plugin.getLogger().info("[DB Upgrade] Stage 1: Exporting the old database to an intermediary file..."); Constructor constructor = getConnectionConstructor(); try (Connection c = getConnection(constructor, oldDatabase)) { diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/hikari/DriverBasedHikariConnectionFactory.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/hikari/DriverBasedHikariConnectionFactory.java new file mode 100644 index 000000000..9d649eddf --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/hikari/DriverBasedHikariConnectionFactory.java @@ -0,0 +1,80 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * 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.storage.implementation.sql.connection.hikari; + +import com.zaxxer.hikari.HikariConfig; +import me.lucko.luckperms.common.storage.misc.StorageCredentials; + +import java.sql.Driver; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Enumeration; + +/** + * Extension of {@link HikariConnectionFactory} that uses the driver class name to configure Hikari. + */ +public abstract class DriverBasedHikariConnectionFactory extends HikariConnectionFactory { + protected DriverBasedHikariConnectionFactory(StorageCredentials configuration) { + super(configuration); + } + + protected abstract String driverClassName(); + + protected abstract String driverJdbcIdentifier(); + + @Override + protected void configureDatabase(HikariConfig config, String address, String port, String databaseName, String username, String password) { + config.setDriverClassName(driverClassName()); + config.setJdbcUrl(String.format("jdbc:%s://%s:%s/%s", driverJdbcIdentifier(), address, port, databaseName)); + config.setUsername(username); + config.setPassword(password); + } + + @Override + protected void postInitialize() { + super.postInitialize(); + + // Calling Class.forName("") is enough to call the static initializer + // which makes our driver available in DriverManager. We don't want that, so unregister it after + // the pool has been setup. + deregisterDriver(driverClassName()); + } + + private static void deregisterDriver(String driverClassName) { + Enumeration drivers = DriverManager.getDrivers(); + while (drivers.hasMoreElements()) { + Driver driver = drivers.nextElement(); + if (driver.getClass().getName().equals(driverClassName)) { + try { + DriverManager.deregisterDriver(driver); + } catch (SQLException e) { + // ignore + } + } + } + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/hikari/HikariConnectionFactory.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/hikari/HikariConnectionFactory.java index f433ebd67..f147efac8 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/hikari/HikariConnectionFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/hikari/HikariConnectionFactory.java @@ -37,11 +37,8 @@ import net.kyori.adventure.text.format.NamedTextColor; import java.sql.Connection; -import java.sql.Driver; -import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; -import java.util.Enumeration; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -245,18 +242,4 @@ private static void handleClassloadingError(Throwable throwable, LuckPermsPlugin logger.warn("Class " + className + " has been loaded by: " + loaderName); } } - - protected static void deregisterDriver(String driverClassName) { - Enumeration drivers = DriverManager.getDrivers(); - while (drivers.hasMoreElements()) { - Driver driver = drivers.nextElement(); - if (driver.getClass().getName().equals(driverClassName)) { - try { - DriverManager.deregisterDriver(driver); - } catch (SQLException e) { - // ignore - } - } - } - } } diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/hikari/MariaDbConnectionFactory.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/hikari/MariaDbConnectionFactory.java index b41c203a6..abd395637 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/hikari/MariaDbConnectionFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/hikari/MariaDbConnectionFactory.java @@ -25,14 +25,11 @@ package me.lucko.luckperms.common.storage.implementation.sql.connection.hikari; -import com.zaxxer.hikari.HikariConfig; import me.lucko.luckperms.common.storage.misc.StorageCredentials; -import java.util.Map; import java.util.function.Function; -import java.util.stream.Collectors; -public class MariaDbConnectionFactory extends HikariConnectionFactory { +public class MariaDbConnectionFactory extends DriverBasedHikariConnectionFactory { public MariaDbConnectionFactory(StorageCredentials configuration) { super(configuration); } @@ -48,21 +45,13 @@ protected String defaultPort() { } @Override - protected void configureDatabase(HikariConfig config, String address, String port, String databaseName, String username, String password) { - config.setDriverClassName("org.mariadb.jdbc.Driver"); - config.setJdbcUrl("jdbc:mariadb://" + address + ":" + port + "/" + databaseName); - config.setUsername(username); - config.setPassword(password); + protected String driverClassName() { + return "org.mariadb.jdbc.Driver"; } @Override - protected void postInitialize() { - super.postInitialize(); - - // Calling Class.forName("org.mariadb.jdbc.Driver") is enough to call the static initializer - // which makes our driver available in DriverManager. We don't want that, so unregister it after - // the pool has been setup. - deregisterDriver("org.mariadb.jdbc.Driver"); + protected String driverJdbcIdentifier() { + return "mariadb"; } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/hikari/MySqlConnectionFactory.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/hikari/MySqlConnectionFactory.java index ce96742c5..926069021 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/hikari/MySqlConnectionFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/hikari/MySqlConnectionFactory.java @@ -25,17 +25,12 @@ package me.lucko.luckperms.common.storage.implementation.sql.connection.hikari; -import com.zaxxer.hikari.HikariConfig; import me.lucko.luckperms.common.storage.misc.StorageCredentials; -import java.sql.Driver; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.util.Enumeration; import java.util.Map; import java.util.function.Function; -public class MySqlConnectionFactory extends HikariConnectionFactory { +public class MySqlConnectionFactory extends DriverBasedHikariConnectionFactory { public MySqlConnectionFactory(StorageCredentials configuration) { super(configuration); } @@ -51,21 +46,13 @@ protected String defaultPort() { } @Override - protected void configureDatabase(HikariConfig config, String address, String port, String databaseName, String username, String password) { - config.setDriverClassName("com.mysql.cj.jdbc.Driver"); - config.setJdbcUrl("jdbc:mysql://" + address + ":" + port + "/" + databaseName); - config.setUsername(username); - config.setPassword(password); + protected String driverClassName() { + return "com.mysql.cj.jdbc.Driver"; } @Override - protected void postInitialize() { - super.postInitialize(); - - // Calling Class.forName("com.mysql.cj.jdbc.Driver") is enough to call the static initializer - // which makes our driver available in DriverManager. We don't want that, so unregister it after - // the pool has been setup. - deregisterDriver("com.mysql.cj.jdbc.Driver"); + protected String driverJdbcIdentifier() { + return "mysql"; } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/hikari/PostgresConnectionFactory.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/hikari/PostgresConnectionFactory.java index 6fcfb8eae..581be1246 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/hikari/PostgresConnectionFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/hikari/PostgresConnectionFactory.java @@ -25,13 +25,12 @@ package me.lucko.luckperms.common.storage.implementation.sql.connection.hikari; -import com.zaxxer.hikari.HikariConfig; import me.lucko.luckperms.common.storage.misc.StorageCredentials; import java.util.Map; import java.util.function.Function; -public class PostgresConnectionFactory extends HikariConnectionFactory { +public class PostgresConnectionFactory extends DriverBasedHikariConnectionFactory { public PostgresConnectionFactory(StorageCredentials configuration) { super(configuration); } @@ -47,13 +46,13 @@ protected String defaultPort() { } @Override - protected void configureDatabase(HikariConfig config, String address, String port, String databaseName, String username, String password) { - config.setDataSourceClassName("com.impossibl.postgres.jdbc.PGDataSource"); - config.addDataSourceProperty("serverName", address); - config.addDataSourceProperty("portNumber", Integer.parseInt(port)); - config.addDataSourceProperty("databaseName", databaseName); - config.addDataSourceProperty("user", username); - config.addDataSourceProperty("password", password); + protected String driverClassName() { + return "org.postgresql.Driver"; + } + + @Override + protected String driverJdbcIdentifier() { + return "postgresql"; } @Override @@ -63,12 +62,6 @@ protected void overrideProperties(Map properties) { // remove the default config properties which don't exist for PostgreSQL properties.remove("useUnicode"); properties.remove("characterEncoding"); - - // socketTimeout -> networkTimeout - Object socketTimeout = properties.remove("socketTimeout"); - if (socketTimeout != null) { - properties.putIfAbsent("networkTimeout", Integer.parseInt(socketTimeout.toString())); - } } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/misc/NodeEntry.java b/common/src/main/java/me/lucko/luckperms/common/storage/misc/NodeEntry.java index 6c51df421..6b8ad0b4e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/misc/NodeEntry.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/misc/NodeEntry.java @@ -54,6 +54,11 @@ private NodeEntry(H holder, N node) { return this.node; } + @Override + public String toString() { + return "NodeEntry(holder=" + this.holder + ", node=" + this.node + ')'; + } + @Override public boolean equals(Object o) { if (o == this) return true; diff --git a/common/src/main/java/me/lucko/luckperms/common/util/UniqueIdType.java b/common/src/main/java/me/lucko/luckperms/common/util/UniqueIdType.java index e31ccd814..a01ae5a29 100644 --- a/common/src/main/java/me/lucko/luckperms/common/util/UniqueIdType.java +++ b/common/src/main/java/me/lucko/luckperms/common/util/UniqueIdType.java @@ -79,8 +79,8 @@ public static UniqueIdType determineType(UUID uniqueId, LuckPermsPlugin plugin) break; case 2: // if the uuid is version 2, assume it is an NPC - // see: https://github.com/lucko/LuckPerms/issues/1470 - // and https://github.com/lucko/LuckPerms/issues/1470#issuecomment-475403162 + // see: https://github.com/LuckPerms/LuckPerms/issues/1470 + // and https://github.com/LuckPerms/LuckPerms/issues/1470#issuecomment-475403162 type = UniqueIdDetermineTypeEvent.TYPE_NPC; break; default: diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSession.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSession.java index 137558518..67d5c8f39 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSession.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSession.java @@ -56,10 +56,9 @@ */ public class WebEditorSession { - public static void createAndOpen(List holders, List tracks, Sender sender, String cmdLabel, LuckPermsPlugin plugin) { + public static WebEditorSession create(List holders, List tracks, Sender sender, String cmdLabel, LuckPermsPlugin plugin) { WebEditorRequest initialRequest = WebEditorRequest.generate(holders, tracks, sender, cmdLabel, plugin); - WebEditorSession session = new WebEditorSession(initialRequest, plugin, sender, cmdLabel); - session.open(); + return new WebEditorSession(initialRequest, plugin, sender, cmdLabel); } private WebEditorRequest initialRequest; @@ -83,9 +82,9 @@ public WebEditorSession(WebEditorRequest initialRequest, LuckPermsPlugin plugin, this.tracks = new LinkedHashSet<>(initialRequest.getTracks().keySet()); } - public void open() { + public String open() { createSocket(); - createInitialSession(); + return createInitialSession(); } private void createSocket() { @@ -117,7 +116,7 @@ private static boolean ignoreSocketConnectError(Exception e) { return false; } - private void createInitialSession() { + private String createInitialSession() { Objects.requireNonNull(this.initialRequest); WebEditorRequest request = this.initialRequest; @@ -129,7 +128,7 @@ private void createInitialSession() { String id = uploadRequestData(request); if (id == null) { - return; + return null; } // form a url for the editor @@ -140,6 +139,12 @@ private void createInitialSession() { if (this.socket != null) { this.socket.scheduleCleanupIfUnused(); } + + return id; + } + + public WebEditorSocket getSocket() { + return this.socket; } public void includeCreatedGroup(Group group) { diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorStore.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorStore.java index e92c51b0e..7e806d8ac 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorStore.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorStore.java @@ -76,9 +76,6 @@ public WebEditorKeystore keystore() { } public KeyPair keyPair() { - if (!this.keyPair.get().isDone()) { - throw new IllegalStateException("Web editor keypair has not been generated yet! Has the server just started?"); - } return this.keyPair.get().join(); } diff --git a/common/src/test/java/me/lucko/luckperms/common/command/utils/ArgumentListTest.java b/common/src/test/java/me/lucko/luckperms/common/command/utils/ArgumentListTest.java new file mode 100644 index 000000000..1b0c85810 --- /dev/null +++ b/common/src/test/java/me/lucko/luckperms/common/command/utils/ArgumentListTest.java @@ -0,0 +1,105 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.command.utils; + +import com.google.common.collect.ImmutableList; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; +import net.luckperms.api.context.ContextSet; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class ArgumentListTest { + + @Test + public void testGetString() { + ArgumentList list = new ArgumentList(ImmutableList.of("hello", "world{SPACE}")); + + assertEquals("hello", list.getOrDefault(0, "def")); + assertEquals("world ", list.getOrDefault(1, "def")); + assertEquals("def", list.getOrDefault(2, "def")); + assertEquals("def", list.getOrDefault(-1, "def")); + assertNull(list.getOrDefault(2, null)); + assertNull(list.getOrDefault(-1, null)); + } + + @Test + public void testGetInt() { + ArgumentList list = new ArgumentList(ImmutableList.of("5", "-50")); + + assertEquals(5, list.getIntOrDefault(0, -1)); + assertEquals(-50, list.getIntOrDefault(1, -1)); + assertEquals(-1, list.getIntOrDefault(2, -1)); + assertEquals(-1, list.getIntOrDefault(-1, -1)); + } + + private static Stream testParseContext() { + return Stream.of( + Arguments.of(new String[]{}, ImmutableContextSetImpl.EMPTY), + Arguments.of(new String[]{"test"}, ImmutableContextSetImpl.of("server", "test")), + Arguments.of( + new String[]{"a", "b", "c"}, + new ImmutableContextSetImpl.BuilderImpl() + .add("server", "a") + .add("world", "b") + .add("server", "c") + .build() + ), + Arguments.of( + new String[]{"a", "thing=b", "c"}, + new ImmutableContextSetImpl.BuilderImpl() + .add("server", "a") + .add("thing", "b") + .add("server", "c") + .build() + ), + Arguments.of( + new String[]{"thing=a", "thing=b", "c"}, + new ImmutableContextSetImpl.BuilderImpl() + .add("thing", "a") + .add("thing", "b") + .add("server", "c") + .build() + ), + Arguments.of(new String[]{"="}, ImmutableContextSetImpl.EMPTY) + ); + } + + @ParameterizedTest + @MethodSource + public void testParseContext(String[] arguments, ContextSet expected) { + ArgumentList list = new ArgumentList(ImmutableList.copyOf(arguments)); + assertEquals(expected, list.getContextOrEmpty(0)); + } + + +} diff --git a/common/src/test/java/me/lucko/luckperms/common/command/utils/ArgumentTokenizerTest.java b/common/src/test/java/me/lucko/luckperms/common/command/utils/ArgumentTokenizerTest.java new file mode 100644 index 000000000..2d91281ac --- /dev/null +++ b/common/src/test/java/me/lucko/luckperms/common/command/utils/ArgumentTokenizerTest.java @@ -0,0 +1,92 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.command.utils; + +import com.google.common.collect.ImmutableList; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ArgumentTokenizerTest { + + private static Stream testBasicTokenize() { + return Stream.of( + Arguments.of("", new String[]{}), + Arguments.of("hello world", new String[]{"hello", "world"}), + Arguments.of("hello world", new String[]{"hello", "", "world"}), + Arguments.of("hello world", new String[]{"hello", "", "", "world"}), + Arguments.of("\"hello world\"", new String[]{"hello world"}), + Arguments.of("\"hello world\"", new String[]{"hello world"}), + Arguments.of("\" hello world\"", new String[]{" hello world"}), + Arguments.of("\"hello world \"", new String[]{"hello world "}), + Arguments.of("\"hello\"\"world\"", new String[]{"hello", "world"}), + Arguments.of("\"hello\" \"world\"", new String[]{"hello", "world"}) + ); + } + + @ParameterizedTest + @MethodSource + public void testBasicTokenize(String input, String[] expectedTokens) { + for (ArgumentTokenizer tokenizer : ArgumentTokenizer.values()) { + List tokens = tokenizer.tokenizeInput(input); + assertEquals(ImmutableList.copyOf(expectedTokens), ImmutableList.copyOf(tokens), "tokenizer " + tokenizer + " produced tokens " + tokens); + } + } + + private static Stream testExecuteTokenize() { + return Stream.of( + Arguments.of("hello world ", new String[]{"hello", "world"}), + Arguments.of("hello world ", new String[]{"hello", "world", ""}) + ); + } + + @ParameterizedTest + @MethodSource + public void testExecuteTokenize(String input, String[] expectedTokens) { + List tokens = ArgumentTokenizer.EXECUTE.tokenizeInput(input); + assertEquals(ImmutableList.copyOf(expectedTokens), ImmutableList.copyOf(tokens)); + } + + private static Stream testTabCompleteTokenize() { + return Stream.of( + Arguments.of("hello world ", new String[]{"hello", "world", ""}), + Arguments.of("hello world ", new String[]{"hello", "world", "", ""}) + ); + } + + @ParameterizedTest + @MethodSource + public void testTabCompleteTokenize(String input, String[] expectedTokens) { + List tokens = ArgumentTokenizer.TAB_COMPLETE.tokenizeInput(input); + assertEquals(ImmutableList.copyOf(expectedTokens), ImmutableList.copyOf(tokens)); + } + +} diff --git a/common/src/test/java/me/lucko/luckperms/common/storage/SqlStorageTest.java b/common/src/test/java/me/lucko/luckperms/common/storage/SqlStorageTest.java index bb9836882..bcd45e79e 100644 --- a/common/src/test/java/me/lucko/luckperms/common/storage/SqlStorageTest.java +++ b/common/src/test/java/me/lucko/luckperms/common/storage/SqlStorageTest.java @@ -28,20 +28,31 @@ import com.google.common.collect.ImmutableSet; import me.lucko.luckperms.common.actionlog.Log; import me.lucko.luckperms.common.actionlog.LoggedAction; +import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.config.LuckPermsConfiguration; import me.lucko.luckperms.common.event.EventDispatcher; import me.lucko.luckperms.common.model.Group; +import me.lucko.luckperms.common.model.PrimaryGroupHolder; +import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.manager.group.GroupManager; import me.lucko.luckperms.common.model.manager.group.StandardGroupManager; +import me.lucko.luckperms.common.model.manager.user.StandardUserManager; +import me.lucko.luckperms.common.model.manager.user.UserManager; +import me.lucko.luckperms.common.node.types.Inheritance; import me.lucko.luckperms.common.node.types.Permission; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap; +import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter; import me.lucko.luckperms.common.storage.implementation.sql.SqlStorage; import me.lucko.luckperms.common.storage.implementation.sql.connection.ConnectionFactory; import me.lucko.luckperms.common.storage.implementation.sql.connection.file.NonClosableConnection; import net.luckperms.api.actionlog.Action; import net.luckperms.api.model.PlayerSaveResult; import net.luckperms.api.model.PlayerSaveResult.Outcome; +import net.luckperms.api.model.data.DataType; import net.luckperms.api.node.Node; +import net.luckperms.api.node.types.InheritanceNode; +import net.luckperms.api.node.types.PermissionNode; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -58,6 +69,7 @@ import java.util.function.Function; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; @@ -66,18 +78,25 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class SqlStorageTest { @Mock private LuckPermsPlugin plugin; @Mock private LuckPermsBootstrap bootstrap; + @Mock private LuckPermsConfiguration configuration; private SqlStorage storage; @BeforeEach public void setupMocksAndDatabase() throws Exception { lenient().when(this.plugin.getBootstrap()).thenReturn(this.bootstrap); + lenient().when(this.plugin.getConfiguration()).thenReturn(this.configuration); + lenient().when(this.plugin.getEventDispatcher()).thenReturn(mock(EventDispatcher.class)); + lenient().when(this.bootstrap.getScheduler()).thenReturn(mock(SchedulerAdapter.class)); + lenient().when(this.configuration.get(ConfigKeys.PRIMARY_GROUP_CALCULATION)).thenReturn(PrimaryGroupHolder.AllParentsByWeight::new); + lenient().when(this.configuration.get(ConfigKeys.PRIMARY_GROUP_CALCULATION_METHOD)).thenReturn("parents-by-weight"); lenient().when(this.bootstrap.getResourceStream(anyString())) .then(answer((String path) -> SqlStorageTest.class.getClassLoader().getResourceAsStream(path))); lenient().when(this.plugin.getEventDispatcher()).thenReturn(mock(EventDispatcher.class)); @@ -197,6 +216,48 @@ public void testSaveAndLoadGroup() throws Exception { assertEquals(nodes, loaded.normalData().asSet()); } + @Test + public void testSaveAndDeleteUser() throws SQLException { + StandardUserManager userManager = new StandardUserManager(this.plugin); + + //noinspection unchecked,rawtypes + when(this.plugin.getUserManager()).thenReturn((UserManager) userManager); + + UUID exampleUniqueId = UUID.fromString("069a79f4-44e9-4726-a5be-fca90e38aaf5"); + String exampleUsername = "Notch"; + PermissionNode examplePermission = Permission.builder() + .permission("test.1") + .withContext("server", "test") + .build(); + InheritanceNode defaultGroupNode = Inheritance.builder(GroupManager.DEFAULT_GROUP_NAME).build(); + + // create a default user, assert that is doesn't appear in unique users list + this.storage.savePlayerData(exampleUniqueId, exampleUsername); + assertFalse(this.storage.getUniqueUsers().contains(exampleUniqueId)); + + // give the user a node, assert that it does appear in unique users list + User user = this.storage.loadUser(exampleUniqueId, exampleUsername); + user.setNode(DataType.NORMAL, examplePermission, true); + this.storage.saveUser(user); + assertTrue(this.storage.getUniqueUsers().contains(exampleUniqueId)); + + // clear all nodes (reset to default) and assert that it does not appear in unique users list + user.clearNodes(DataType.NORMAL, null, true); + this.storage.saveUser(user); + assertFalse(this.storage.getUniqueUsers().contains(exampleUniqueId)); + assertEquals(ImmutableSet.of(defaultGroupNode), user.normalData().asSet()); + + // give it a node again, assert that it shows as a unique user + user.setNode(DataType.NORMAL, examplePermission, true); + this.storage.saveUser(user); + assertTrue(this.storage.getUniqueUsers().contains(exampleUniqueId)); + assertEquals(ImmutableSet.of(defaultGroupNode, examplePermission), user.normalData().asSet()); + + // reload user data from the db and assert that it is unchanged + user = this.storage.loadUser(exampleUniqueId, exampleUsername); + assertEquals(ImmutableSet.of(defaultGroupNode, examplePermission), user.normalData().asSet()); + } + private static class TestH2ConnectionFactory implements ConnectionFactory { private final NonClosableConnection connection; diff --git a/fabric/build.gradle b/fabric/build.gradle index 58a298b2c..36e7c5f60 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -1,22 +1,21 @@ import net.fabricmc.loom.task.RemapJarTask plugins { - id 'com.github.johnrengelman.shadow' version '7.1.2' - id 'fabric-loom' version '1.1-SNAPSHOT' + alias(libs.plugins.shadow) + alias(libs.plugins.loom) } archivesBaseName = 'luckperms' repositories { maven { url 'https://maven.fabricmc.net/' } - mavenLocal() } dependencies { // https://modmuss50.me/fabric.html - minecraft 'com.mojang:minecraft:1.19.4' - mappings 'net.fabricmc:yarn:1.19.4+build.1:v2' - modImplementation 'net.fabricmc:fabric-loader:0.14.17' + minecraft 'com.mojang:minecraft:1.20.1' + mappings 'net.fabricmc:yarn:1.20.1+build.2:v2' + modImplementation 'net.fabricmc:fabric-loader:0.14.21' Set apiModules = [ 'fabric-api-base', @@ -26,7 +25,7 @@ dependencies { ] apiModules.forEach { - modImplementation(fabricApi.module(it, '0.76.0+1.19.4')) + modImplementation(fabricApi.module(it, '0.83.1+1.20.1')) } include(modImplementation('me.lucko:fabric-permissions-api:0.2-SNAPSHOT')) @@ -61,7 +60,7 @@ shadowJar { relocate 'me.lucko.commodore', 'me.lucko.luckperms.lib.commodore' relocate 'org.mariadb.jdbc', 'me.lucko.luckperms.lib.mariadb' relocate 'com.mysql', 'me.lucko.luckperms.lib.mysql' - relocate 'com.impossibl', 'me.lucko.luckperms.lib.postgresql' + relocate 'org.postgresql', 'me.lucko.luckperms.lib.postgresql' relocate 'com.zaxxer.hikari', 'me.lucko.luckperms.lib.hikari' relocate 'com.mongodb', 'me.lucko.luckperms.lib.mongodb' relocate 'org.bson', 'me.lucko.luckperms.lib.bson' @@ -75,8 +74,8 @@ shadowJar { task remappedShadowJar(type: RemapJarTask) { dependsOn tasks.shadowJar - input.set(tasks.shadowJar.archiveFile) - addNestedDependencies.set(true) + input = tasks.shadowJar.archiveFile + addNestedDependencies = true archiveFileName = "LuckPerms-Fabric-${project.ext.fullVersion}.jar" } diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/FabricSenderFactory.java b/fabric/src/main/java/me/lucko/luckperms/fabric/FabricSenderFactory.java index d6a0713a9..57538da83 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/FabricSenderFactory.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/FabricSenderFactory.java @@ -29,12 +29,15 @@ import me.lucko.luckperms.common.locale.TranslationManager; import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.sender.SenderFactory; +import me.lucko.luckperms.fabric.mixin.ServerCommandSourceAccessor; import me.lucko.luckperms.fabric.model.MixinUser; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.luckperms.api.util.Tristate; +import net.minecraft.server.command.CommandOutput; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.rcon.RconCommandOutput; import net.minecraft.text.Text; import java.util.Locale; @@ -72,11 +75,13 @@ protected String getName(ServerCommandSource commandSource) { @Override protected void sendMessage(ServerCommandSource sender, Component message) { - Locale locale = null; + final Locale locale; if (sender.getEntity() instanceof ServerPlayerEntity) { locale = ((MixinUser) sender.getEntity()).getCachedLocale(); + } else { + locale = null; } - sender.sendFeedback(toNativeText(TranslationManager.render(message, locale)), false); + sender.sendFeedback(() -> toNativeText(TranslationManager.render(message, locale)), false); } @Override @@ -105,7 +110,10 @@ protected void performCommand(ServerCommandSource sender, String command) { @Override protected boolean isConsole(ServerCommandSource sender) { - return sender.getEntity() == null; + CommandOutput output = ((ServerCommandSourceAccessor) sender).getOutput(); + return output == sender.getServer() || // Console + output.getClass() == RconCommandOutput.class || // Rcon + (output == CommandOutput.DUMMY && sender.getName().equals("")); // Functions } public static Text toNativeText(Component component) { diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java b/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java index 87cfb368f..4313f42f1 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java @@ -79,7 +79,7 @@ public void calculate(@NonNull ServerPlayerEntity target, @NonNull ContextConsum } // TODO: figure out dimension type context too - ServerWorld world = target.getWorld(); + ServerWorld world = target.getServerWorld(); if (this.world) { this.plugin.getConfiguration().get(ConfigKeys.WORLD_REWRITES).rewriteAndSubmit(getContextKey(world.getRegistryKey().getValue()), consumer); } diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerCommandSourceAccessor.java b/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerCommandSourceAccessor.java new file mode 100644 index 000000000..3418b859c --- /dev/null +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerCommandSourceAccessor.java @@ -0,0 +1,42 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * 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.fabric.mixin; + +import net.minecraft.server.command.CommandOutput; +import net.minecraft.server.command.ServerCommandSource; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +/** + * Accessor mixin to provide access to the underlying {@link CommandOutput} + */ +@Mixin(ServerCommandSource.class) +public interface ServerCommandSourceAccessor { + + @Accessor("output") + CommandOutput getOutput(); + +} diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java b/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java index fc99d810e..c7545660a 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java @@ -69,7 +69,7 @@ public abstract class ServerPlayerEntityMixin implements MixinUser { private Locale luckperms$locale; // Used by PlayerChangeWorldCallback hook below. - @Shadow public abstract ServerWorld getWorld(); + @Shadow public abstract ServerWorld getServerWorld(); @Override public User getLuckPermsUser() { @@ -183,6 +183,6 @@ private void luckperms_setClientSettings(ClientSettingsC2SPacket information, Ca @Inject(at = @At("TAIL"), method = "worldChanged") private void luckperms_onChangeDimension(ServerWorld targetWorld, CallbackInfo ci) { - PlayerChangeWorldCallback.EVENT.invoker().onChangeWorld(this.getWorld(), targetWorld, (ServerPlayerEntity) (Object) this); + PlayerChangeWorldCallback.EVENT.invoker().onChangeWorld(this.getServerWorld(), targetWorld, (ServerPlayerEntity) (Object) this); } } diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 0121aa4e1..e820b2a6b 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -17,8 +17,8 @@ "license": "MIT", "contact": { "homepage": "https://luckperms.net", - "source": "https://github.com/lucko/LuckPerms", - "issues": "https://github.com/lucko/LuckPerms/issues" + "source": "https://github.com/LuckPerms/LuckPerms", + "issues": "https://github.com/LuckPerms/LuckPerms/issues" }, "environment": "server", "entrypoints": { diff --git a/fabric/src/main/resources/luckperms.conf b/fabric/src/main/resources/luckperms.conf index 6045f1ebc..29517a215 100644 --- a/fabric/src/main/resources/luckperms.conf +++ b/fabric/src/main/resources/luckperms.conf @@ -8,7 +8,7 @@ # | | # # | WIKI: https://luckperms.net/wiki | # # | DISCORD: https://discord.gg/luckperms | # -# | BUG REPORTS: https://github.com/lucko/LuckPerms/issues | # +# | BUG REPORTS: https://github.com/LuckPerms/LuckPerms/issues | # # | | # # | Each option in this file is documented and explained here: | # # | ==> https://luckperms.net/wiki/Configuration | # @@ -265,6 +265,7 @@ broadcast-received-log-entries = true # Settings for Redis. # Port 6379 is used by default; set address to "host:port" if differs +# Multiple Redis nodes can be specified in the same format as a string list under the name "addresses". redis { enabled = false address = "localhost" diff --git a/fabric/src/main/resources/luckperms.mixins.json b/fabric/src/main/resources/luckperms.mixins.json index 24634c967..bc4c58c9b 100644 --- a/fabric/src/main/resources/luckperms.mixins.json +++ b/fabric/src/main/resources/luckperms.mixins.json @@ -4,6 +4,7 @@ "compatibilityLevel": "JAVA_8", "mixins": [ "CommandManagerMixin", + "ServerCommandSourceAccessor", "ServerLoginNetworkHandlerAccessor", "ServerPlayerEntityMixin" ], diff --git a/forge/build.gradle b/forge/build.gradle index b7e674267..b5d9f80e0 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -1,20 +1,9 @@ -buildscript { - repositories { - maven { url 'https://plugins.gradle.org/m2' } - maven { url 'https://maven.minecraftforge.net/' } - } - - dependencies { - classpath 'gradle.plugin.com.github.johnrengelman:shadow:7.1.2' - classpath 'net.kyori:blossom:1.3.0' - classpath 'net.minecraftforge.gradle:ForgeGradle:5.1.+' - } +plugins { + alias(libs.plugins.blossom) + alias(libs.plugins.shadow) + alias(libs.plugins.forgegradle) } -apply plugin: 'com.github.johnrengelman.shadow' -apply plugin: 'net.kyori.blossom' -apply plugin: 'net.minecraftforge.gradle' - sourceCompatibility = 1.8 targetCompatibility = 17 @@ -27,12 +16,6 @@ minecraft { mappings channel: 'official', version: minecraftVersion } -configurations { -} - -repositories { -} - dependencies { minecraft "net.minecraftforge:forge:${minecraftVersion}-${forgeVersion}" implementation project(':common') @@ -60,7 +43,7 @@ shadowJar { relocate 'me.lucko.commodore', 'me.lucko.luckperms.lib.commodore' relocate 'org.mariadb.jdbc', 'me.lucko.luckperms.lib.mariadb' relocate 'com.mysql', 'me.lucko.luckperms.lib.mysql' - relocate 'com.impossibl', 'me.lucko.luckperms.lib.postgresql' + relocate 'org.postgresql', 'me.lucko.luckperms.lib.postgresql' relocate 'com.zaxxer.hikari', 'me.lucko.luckperms.lib.hikari' relocate 'com.mongodb', 'me.lucko.luckperms.lib.mongodb' relocate 'org.bson', 'me.lucko.luckperms.lib.bson' diff --git a/forge/forge-api/build.gradle b/forge/forge-api/build.gradle index 8edccffbe..cf210c2ea 100644 --- a/forge/forge-api/build.gradle +++ b/forge/forge-api/build.gradle @@ -1,15 +1,7 @@ -buildscript { - repositories { - maven { url 'https://maven.minecraftforge.net/' } - } - - dependencies { - classpath 'net.minecraftforge.gradle:ForgeGradle:5.1.+' - } +plugins { + alias(libs.plugins.forgegradle) } -apply plugin: 'net.minecraftforge.gradle' - sourceCompatibility = 1.8 targetCompatibility = 17 @@ -17,12 +9,6 @@ minecraft { mappings channel: 'official', version: minecraftVersion } -configurations { -} - -repositories { -} - dependencies { minecraft "net.minecraftforge:forge:${minecraftVersion}-${forgeVersion}" implementation project(':api') diff --git a/forge/gradle.properties b/forge/gradle.properties index f081a1dcb..96afd9f7c 100644 --- a/forge/gradle.properties +++ b/forge/gradle.properties @@ -1,2 +1,2 @@ -minecraftVersion=1.19.4 -forgeVersion=45.0.1 \ No newline at end of file +minecraftVersion=1.20.1 +forgeVersion=47.0.1 \ No newline at end of file diff --git a/forge/loader/build.gradle b/forge/loader/build.gradle index 3eca8f201..1b6f0040d 100644 --- a/forge/loader/build.gradle +++ b/forge/loader/build.gradle @@ -1,19 +1,9 @@ -buildscript { - repositories { - maven { url 'https://plugins.gradle.org/m2' } - maven { url 'https://maven.minecraftforge.net/' } - } - - dependencies { - classpath 'gradle.plugin.com.github.johnrengelman:shadow:7.1.2' - classpath 'net.minecraftforge.gradle:ForgeGradle:5.1.+' - } +plugins { + alias(libs.plugins.shadow) + alias(libs.plugins.forgegradle) + id("java-library") } -apply plugin: 'com.github.johnrengelman.shadow' -apply plugin: 'java-library' -apply plugin: 'net.minecraftforge.gradle' - sourceCompatibility = 1.8 targetCompatibility = 17 @@ -21,9 +11,6 @@ minecraft { mappings channel: 'official', version: minecraftVersion } -repositories { -} - dependencies { minecraft "net.minecraftforge:forge:${minecraftVersion}-${forgeVersion}" implementation project(':api') diff --git a/forge/src/main/java/me/lucko/luckperms/forge/ForgeSenderFactory.java b/forge/src/main/java/me/lucko/luckperms/forge/ForgeSenderFactory.java index 8a8f63a2e..83c6fdc19 100644 --- a/forge/src/main/java/me/lucko/luckperms/forge/ForgeSenderFactory.java +++ b/forge/src/main/java/me/lucko/luckperms/forge/ForgeSenderFactory.java @@ -38,8 +38,10 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.luckperms.api.util.Tristate; +import net.minecraft.commands.CommandSource; import net.minecraft.commands.CommandSourceStack; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.rcon.RconConsoleSource; import net.minecraft.world.entity.player.Player; import java.util.Locale; @@ -77,7 +79,7 @@ protected void sendMessage(CommandSourceStack sender, Component message) { locale = null; } - sender.sendSuccess(toNativeText(TranslationManager.render(message, locale)), false); + sender.sendSuccess(() -> toNativeText(TranslationManager.render(message, locale)), false); } @Override @@ -107,7 +109,10 @@ protected void performCommand(CommandSourceStack sender, String command) { @Override protected boolean isConsole(CommandSourceStack sender) { - return !(sender.getEntity() instanceof Player); + CommandSource output = sender.source; + return output == sender.getServer() || // Console + output.getClass() == RconConsoleSource.class || // Rcon + (output == CommandSource.NULL && sender.getTextName().equals("")); // Functions } public static net.minecraft.network.chat.Component toNativeText(Component component) { diff --git a/forge/src/main/java/me/lucko/luckperms/forge/context/ForgePlayerCalculator.java b/forge/src/main/java/me/lucko/luckperms/forge/context/ForgePlayerCalculator.java index 6f37ec65d..bb5716018 100644 --- a/forge/src/main/java/me/lucko/luckperms/forge/context/ForgePlayerCalculator.java +++ b/forge/src/main/java/me/lucko/luckperms/forge/context/ForgePlayerCalculator.java @@ -68,7 +68,7 @@ public ForgePlayerCalculator(LPForgePlugin plugin, Set disabled) { @Override public void calculate(@NonNull ServerPlayer target, @NonNull ContextConsumer consumer) { - ServerLevel level = target.getLevel(); + ServerLevel level = target.serverLevel(); if (this.dimensionType) { consumer.accept(DefaultContextKeys.DIMENSION_TYPE_KEY, getContextKey(level.dimension().location())); } diff --git a/forge/src/main/resources/luckperms.conf b/forge/src/main/resources/luckperms.conf index 0093326c8..06e3c0d97 100644 --- a/forge/src/main/resources/luckperms.conf +++ b/forge/src/main/resources/luckperms.conf @@ -8,7 +8,7 @@ # | | # # | WIKI: https://luckperms.net/wiki | # # | DISCORD: https://discord.gg/luckperms | # -# | BUG REPORTS: https://github.com/lucko/LuckPerms/issues | # +# | BUG REPORTS: https://github.com/LuckPerms/LuckPerms/issues | # # | | # # | Each option in this file is documented and explained here: | # # | ==> https://luckperms.net/wiki/Configuration | # @@ -263,6 +263,7 @@ broadcast-received-log-entries = true # Settings for Redis. # Port 6379 is used by default; set address to "host:port" if differs +# Multiple Redis nodes can be specified in the same format as a string list under the name "addresses". redis { enabled = false address = "localhost" diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 000000000..097413772 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,13 @@ +[versions] +shadow = "8.1.1" +blossom = "1.3.1" +forgegradle = "[6.0,6.2)" +loom = "1.2-SNAPSHOT" +licenser = "0.6.1" + +[plugins] +blossom = { id = "net.kyori.blossom", version.ref = "blossom" } +shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" } +forgegradle = { id = "net.minecraftforge.gradle", version.ref = "forgegradle" } +loom = { id = "fabric-loom", version.ref = "loom" } +licenser = { id = "org.cadixdev.licenser", version.ref = "licenser" } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2..c1962a79e 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ae04661ee..37aef8d3f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c78733..aeb74cbb4 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +80,10 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -143,12 +140,16 @@ fi if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,6 +194,10 @@ if "$cygwin" || "$msys" ; then done fi + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in @@ -205,6 +210,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index 107acd32c..93e3f59f1 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/nukkit/build.gradle b/nukkit/build.gradle index b97fa9106..a30745eb1 100644 --- a/nukkit/build.gradle +++ b/nukkit/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.github.johnrengelman.shadow' version '7.1.2' + alias(libs.plugins.shadow) } repositories { @@ -29,7 +29,7 @@ shadowJar { relocate 'me.lucko.commodore', 'me.lucko.luckperms.lib.commodore' relocate 'org.mariadb.jdbc', 'me.lucko.luckperms.lib.mariadb' relocate 'com.mysql', 'me.lucko.luckperms.lib.mysql' - relocate 'com.impossibl', 'me.lucko.luckperms.lib.postgresql' + relocate 'org.postgresql', 'me.lucko.luckperms.lib.postgresql' relocate 'com.zaxxer.hikari', 'me.lucko.luckperms.lib.hikari' relocate 'com.mongodb', 'me.lucko.luckperms.lib.mongodb' relocate 'org.bson', 'me.lucko.luckperms.lib.bson' diff --git a/nukkit/loader/build.gradle b/nukkit/loader/build.gradle index 15d5c873b..8d0211ebd 100644 --- a/nukkit/loader/build.gradle +++ b/nukkit/loader/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.github.johnrengelman.shadow' + alias(libs.plugins.shadow) } repositories { diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/PermissibleInjector.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/PermissibleInjector.java index 895ae6032..346ae83c2 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/PermissibleInjector.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/PermissibleInjector.java @@ -82,7 +82,7 @@ public static void inject(Player player, LuckPermsPermissible newPermissible) th if (oldPermissible instanceof LuckPermsPermissible) { // Nukkit seems to re-use player instances (or perhaps calls the login event twice?) // so, just uninject here instead of throwing an exception like we do on Bukkit - // See: https://github.com/lucko/LuckPerms/issues/2791 + // See: https://github.com/LuckPerms/LuckPerms/issues/2791 uninject(player, false); } diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/listeners/NukkitConnectionListener.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/listeners/NukkitConnectionListener.java index b12432af2..110c46a41 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/listeners/NukkitConnectionListener.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/listeners/NukkitConnectionListener.java @@ -193,7 +193,7 @@ public void onPlayerLoginMonitor(PlayerLoginEvent e) { public void onPlayerQuit(PlayerQuitEvent e) { final Player player = e.getPlayer(); - // https://github.com/lucko/LuckPerms/issues/2269 + // https://github.com/LuckPerms/LuckPerms/issues/2269 if (player.getUniqueId() == null) { return; } diff --git a/nukkit/src/main/resources/config.yml b/nukkit/src/main/resources/config.yml index d77169a93..a176bf3f6 100644 --- a/nukkit/src/main/resources/config.yml +++ b/nukkit/src/main/resources/config.yml @@ -8,7 +8,7 @@ # | | # # | WIKI: https://luckperms.net/wiki | # # | DISCORD: https://discord.gg/luckperms | # -# | BUG REPORTS: https://github.com/lucko/LuckPerms/issues | # +# | BUG REPORTS: https://github.com/LuckPerms/LuckPerms/issues | # # | | # # | Each option in this file is documented and explained here: | # # | ==> https://luckperms.net/wiki/Configuration | # @@ -257,6 +257,7 @@ broadcast-received-log-entries: true # Settings for Redis. # Port 6379 is used by default; set address to "host:port" if differs +# Multiple Redis nodes can be specified in the same format as a string list under the name "addresses". redis: enabled: false address: localhost diff --git a/settings.gradle b/settings.gradle index b03ef276d..91cdf3aa2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,14 +1,21 @@ -// Fabric Needs this pluginManagement { repositories { - jcenter() maven { - url 'https://maven.fabricmc.net/' + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + maven { + name = 'Forge' + url = 'https://maven.minecraftforge.net/' } gradlePluginPortal() } } +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version("0.5.0") +} + rootProject.name = 'luckperms' include ( 'api', diff --git a/sponge/build.gradle b/sponge/build.gradle index 0c19a9008..37490efbc 100644 --- a/sponge/build.gradle +++ b/sponge/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.github.johnrengelman.shadow' version '7.1.2' + alias(libs.plugins.shadow) } repositories { @@ -42,7 +42,7 @@ shadowJar { relocate 'me.lucko.commodore', 'me.lucko.luckperms.lib.commodore' relocate 'org.mariadb.jdbc', 'me.lucko.luckperms.lib.mariadb' relocate 'com.mysql', 'me.lucko.luckperms.lib.mysql' - relocate 'com.impossibl', 'me.lucko.luckperms.lib.postgresql' + relocate 'org.postgresql', 'me.lucko.luckperms.lib.postgresql' relocate 'com.zaxxer.hikari', 'me.lucko.luckperms.lib.hikari' relocate 'com.mongodb', 'me.lucko.luckperms.lib.mongodb' relocate 'org.bson', 'me.lucko.luckperms.lib.bson' diff --git a/sponge/loader/build.gradle b/sponge/loader/build.gradle index b7a89d919..14499ff80 100644 --- a/sponge/loader/build.gradle +++ b/sponge/loader/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.github.johnrengelman.shadow' + alias(libs.plugins.shadow) } repositories { diff --git a/sponge/src/main/resources/luckperms.conf b/sponge/src/main/resources/luckperms.conf index 029c69892..1d64efd49 100644 --- a/sponge/src/main/resources/luckperms.conf +++ b/sponge/src/main/resources/luckperms.conf @@ -8,7 +8,7 @@ # | | # # | WIKI: https://luckperms.net/wiki | # # | DISCORD: https://discord.gg/luckperms | # -# | BUG REPORTS: https://github.com/lucko/LuckPerms/issues | # +# | BUG REPORTS: https://github.com/LuckPerms/LuckPerms/issues | # # | | # # | Each option in this file is documented and explained here: | # # | ==> https://luckperms.net/wiki/Configuration | # @@ -265,6 +265,7 @@ broadcast-received-log-entries = true # Settings for Redis. # Port 6379 is used by default; set address to "host:port" if differs +# Multiple Redis nodes can be specified in the same format as a string list under the name "addresses". redis { enabled = false address = "localhost" diff --git a/standalone/app/build.gradle b/standalone/app/build.gradle index 696641afb..708f45f97 100644 --- a/standalone/app/build.gradle +++ b/standalone/app/build.gradle @@ -1,6 +1,6 @@ plugins { - id 'net.kyori.blossom' version '1.3.0' - id 'java-library' + alias(libs.plugins.blossom) + id("java-library") } sourceCompatibility = 17 @@ -16,26 +16,38 @@ dependencies { api 'com.google.code.gson:gson:2.9.0' api 'com.google.guava:guava:31.1-jre' + api 'io.netty:netty-all:4.1.93.Final' - api('net.kyori:adventure-api:4.11.0') { + api('net.kyori:adventure-api:4.14.0') { exclude(module: 'adventure-bom') exclude(module: 'checker-qual') exclude(module: 'annotations') } - api('net.kyori:adventure-text-serializer-gson:4.11.0') { + api('net.kyori:adventure-text-serializer-gson:4.14.0') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') exclude(module: 'gson') } - api('net.kyori:adventure-text-serializer-legacy:4.11.0') { + api('net.kyori:adventure-text-serializer-legacy:4.14.0') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') } - api('net.kyori:adventure-text-serializer-plain:4.11.0') { + api('net.kyori:adventure-text-serializer-plain:4.14.0') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') } - api('net.kyori:ansi:1.0.0-SNAPSHOT') + api('net.kyori:adventure-text-minimessage:4.14.0') { + exclude(module: 'adventure-bom') + exclude(module: 'adventure-api') + } + api('net.kyori:adventure-text-serializer-ansi:4.14.0') { + exclude(module: 'adventure-bom') + exclude(module: 'adventure-api') + exclude(module: 'annotations') + } + api('net.kyori:ansi:1.0.3') { + exclude(module: 'annotations') + } } blossom { diff --git a/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/SingletonPlayer.java b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/SingletonPlayer.java index 8d2453f74..0df81372b 100644 --- a/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/SingletonPlayer.java +++ b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/SingletonPlayer.java @@ -26,8 +26,9 @@ package me.lucko.luckperms.standalone.app.integration; import me.lucko.luckperms.standalone.app.LuckPermsApplication; -import me.lucko.luckperms.standalone.app.utils.AnsiUtils; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.ansi.ANSIComponentSerializer; +import net.kyori.ansi.ColorLevel; import java.util.Set; import java.util.UUID; @@ -46,7 +47,7 @@ public class SingletonPlayer { private static final UUID UUID = new UUID(0, 0); /** A message sink that prints the component to stdout */ - private static final Consumer PRINT_TO_STDOUT = component -> LuckPermsApplication.LOGGER.info(AnsiUtils.format(component)); + private static final Consumer PRINT_TO_STDOUT = component -> LuckPermsApplication.LOGGER.info(ANSIComponentSerializer.ansi().serialize(component)); /** Singleton instance */ public static final SingletonPlayer INSTANCE = new SingletonPlayer(); diff --git a/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/utils/AnsiUtils.java b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/utils/AnsiUtils.java deleted file mode 100644 index 048831e32..000000000 --- a/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/utils/AnsiUtils.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * This file is part of LuckPerms, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * 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.standalone.app.utils; - -import net.kyori.adventure.key.Key; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.flattener.ComponentFlattener; -import net.kyori.adventure.text.flattener.FlattenerListener; -import net.kyori.adventure.text.format.Style; -import net.kyori.adventure.text.format.TextColor; -import net.kyori.adventure.text.format.TextDecoration; -import net.kyori.ansi.ANSIComponentRenderer; -import net.kyori.ansi.StyleOps; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.Range; - -/** - * Utility to format a {@link Component} as an ANSI string. - */ -public final class AnsiUtils { - private AnsiUtils() {} - - public static String format(Component component) { - ANSIComponentRenderer.ToString