├── fabric ├── 1.20.1 │ ├── .gitkeep │ └── src │ │ └── main │ │ └── resources │ │ ├── uniform.mixins.json │ │ └── fabric.mod.json ├── 1.21.1 │ ├── .gitkeep │ └── src │ │ └── main │ │ └── resources │ │ └── uniform.mixins.json ├── 1.21.4 │ ├── .gitkeep │ └── src │ │ └── main │ │ └── resources │ │ └── uniform.mixins.json ├── mainProject ├── src │ └── main │ │ ├── resources │ │ ├── uniform.accesswidener │ │ ├── uniform.mixins.json │ │ └── fabric.mod.json │ │ └── java │ │ └── net │ │ └── william278 │ │ └── uniform │ │ └── fabric │ │ ├── FabricCommandUser.java │ │ ├── mixins │ │ ├── CommandManagerMixin.java │ │ └── ArgumentNodeMixin.java │ │ ├── FabricCommand.java │ │ └── FabricUniform.java ├── 1.21.5 │ ├── gradle.properties │ └── src │ │ └── main │ │ └── resources │ │ └── uniform.mixins.json ├── 1.21.8 │ └── gradle.properties ├── gradle.properties ├── root.gradle └── build.gradle ├── images └── banner.png ├── .github ├── funding.yml ├── dependabot.yml └── workflows │ ├── release.yml │ └── ci.yml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── example-plugin ├── src │ └── main │ │ ├── resources │ │ └── paper-plugin.yml │ │ └── java │ │ └── net │ │ └── william278 │ │ └── uniform │ │ ├── UniformExample.java │ │ ├── AnnotatedCommand.java │ │ └── ExtendedCommand.java └── build.gradle ├── paper ├── build.gradle └── src │ └── main │ └── java │ └── net │ └── william278 │ └── uniform │ └── paper │ ├── element │ └── PaperArgumentElement.java │ ├── LegacyPaperCommandUser.java │ ├── PaperCommandUser.java │ ├── PaperUniform.java │ ├── LegacyPaperCommand.java │ └── PaperCommand.java ├── velocity ├── build.gradle └── src │ └── main │ └── java │ └── net │ └── william278 │ └── uniform │ └── velocity │ ├── VelocityCommandUser.java │ ├── VelocityUniform.java │ └── VelocityCommand.java ├── sponge-11 ├── build.gradle └── src │ └── main │ └── java │ └── net │ └── william278 │ └── uniform │ └── sponge │ ├── SpongeCommandUser.java │ ├── SpongeUniform.java │ └── SpongeCommand.java ├── common ├── build.gradle └── src │ └── main │ └── java │ └── net │ └── william278 │ └── uniform │ ├── CommandProvider.java │ ├── element │ ├── CommandElement.java │ ├── LiteralElement.java │ └── ArgumentElement.java │ ├── annotations │ ├── CommandDescription.java │ ├── PermissionNode.java │ ├── Syntax.java │ ├── CommandNode.java │ └── Argument.java │ ├── CommandSyntax.java │ ├── CommandUser.java │ ├── Uniform.java │ ├── Node.java │ ├── Graph.java │ ├── Permission.java │ ├── ConversionNode.java │ ├── CommandExecutor.java │ ├── Execution.java │ └── Command.java ├── bungee ├── build.gradle └── src │ └── main │ └── java │ └── net │ └── william278 │ └── uniform │ └── bungee │ ├── BungeeCommandUser.java │ ├── BungeeUniform.java │ └── BungeeCommand.java ├── bukkit ├── build.gradle └── src │ └── main │ └── java │ └── net │ └── william278 │ └── uniform │ └── bukkit │ ├── BukkitCommandUser.java │ ├── BukkitUniform.java │ └── BukkitCommand.java ├── gradle.properties ├── .gitignore ├── HEADER ├── settings.gradle ├── gradlew.bat ├── gradlew └── README.md /fabric/1.20.1/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fabric/1.21.1/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fabric/1.21.4/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fabric/mainProject: -------------------------------------------------------------------------------- 1 | 1.21.8 -------------------------------------------------------------------------------- /images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WiIIiam278/Uniform/HEAD/images/banner.png -------------------------------------------------------------------------------- /.github/funding.yml: -------------------------------------------------------------------------------- 1 | # Funding metadata for GitHub 2 | 3 | github: WiIIiam278 4 | custom: https://buymeacoff.ee/william278 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WiIIiam278/Uniform/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example-plugin/src/main/resources/paper-plugin.yml: -------------------------------------------------------------------------------- 1 | name: UniformExample 2 | version: "1.0" 3 | api-version: "1.20" 4 | main: net.william278.uniform.UniformExample -------------------------------------------------------------------------------- /fabric/src/main/resources/uniform.accesswidener: -------------------------------------------------------------------------------- 1 | accessWidener v2 named 2 | 3 | extendable class net/minecraft/network/packet/s2c/play/CommandTreeS2CPacket$ArgumentNode -------------------------------------------------------------------------------- /fabric/1.21.5/gradle.properties: -------------------------------------------------------------------------------- 1 | essential.defaults.loom.minecraft=com.mojang:minecraft:1.21.5 2 | essential.defaults.loom.mappings=net.fabricmc:yarn:1.21.5+build.1:v2 3 | -------------------------------------------------------------------------------- /fabric/1.21.8/gradle.properties: -------------------------------------------------------------------------------- 1 | essential.defaults.loom.minecraft=com.mojang:minecraft:1.21.8 2 | essential.defaults.loom.mappings=net.fabricmc:yarn:1.21.8+build.1:v2 3 | -------------------------------------------------------------------------------- /fabric/gradle.properties: -------------------------------------------------------------------------------- 1 | loom.platform=fabric 2 | 3 | essential.defaults.loom=1 4 | 5 | org.gradle.daemon=false 6 | org.gradle.parallel=true 7 | org.gradle.configureoncommand=true 8 | org.gradle.parallel.threads=4 9 | org.gradle.jvmargs=-Xmx8G -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /fabric/src/main/resources/uniform.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "net.william278.uniform.fabric.mixins", 5 | "compatibilityLevel": "JAVA_17", 6 | "server": [ 7 | "CommandManagerMixin" 8 | ], 9 | "injectors": { 10 | "defaultRequire": 1 11 | } 12 | } -------------------------------------------------------------------------------- /fabric/1.20.1/src/main/resources/uniform.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "net.william278.uniform.fabric.mixins", 5 | "compatibilityLevel": "JAVA_17", 6 | "server": [ 7 | "ArgumentNodeMixin" 8 | ], 9 | "injectors": { 10 | "defaultRequire": 1 11 | } 12 | } -------------------------------------------------------------------------------- /fabric/1.21.1/src/main/resources/uniform.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "net.william278.uniform.fabric.mixins", 5 | "compatibilityLevel": "JAVA_17", 6 | "server": [ 7 | "ArgumentNodeMixin" 8 | ], 9 | "injectors": { 10 | "defaultRequire": 1 11 | } 12 | } -------------------------------------------------------------------------------- /fabric/1.21.4/src/main/resources/uniform.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "net.william278.uniform.fabric.mixins", 5 | "compatibilityLevel": "JAVA_17", 6 | "server": [ 7 | "ArgumentNodeMixin" 8 | ], 9 | "injectors": { 10 | "defaultRequire": 1 11 | } 12 | } -------------------------------------------------------------------------------- /fabric/1.21.5/src/main/resources/uniform.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "net.william278.uniform.fabric.mixins", 5 | "compatibilityLevel": "JAVA_17", 6 | "server": [ 7 | "ArgumentNodeMixin" 8 | ], 9 | "injectors": { 10 | "defaultRequire": 1 11 | } 12 | } -------------------------------------------------------------------------------- /paper/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'maven-publish' 4 | } 5 | 6 | dependencies { 7 | api project(path: ':common') 8 | 9 | compileOnlyApi 'io.papermc.paper:paper-api:1.20.6-R0.1-SNAPSHOT' 10 | compileOnly 'org.projectlombok:lombok:1.18.36' 11 | 12 | annotationProcessor 'org.projectlombok:lombok:1.18.36' 13 | } -------------------------------------------------------------------------------- /velocity/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'maven-publish' 4 | } 5 | 6 | dependencies { 7 | api project(path: ':common') 8 | 9 | compileOnly 'com.velocitypowered:velocity-api:3.3.0-SNAPSHOT' 10 | compileOnly 'org.projectlombok:lombok:1.18.36' 11 | 12 | annotationProcessor 'org.projectlombok:lombok:1.18.36' 13 | } -------------------------------------------------------------------------------- /sponge-11/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'maven-publish' 4 | } 5 | 6 | dependencies { 7 | implementation project(path: ':common') 8 | 9 | compileOnly 'org.spongepowered:spongeapi:11.0.0' 10 | compileOnly 'org.projectlombok:lombok:1.18.36' 11 | 12 | annotationProcessor 'org.projectlombok:lombok:1.18.36' 13 | } 14 | -------------------------------------------------------------------------------- /common/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'maven-publish' 4 | } 5 | 6 | dependencies { 7 | compileOnlyApi 'com.mojang:brigadier:1.1.8' 8 | compileOnlyApi 'net.kyori:adventure-api:4.23.0' 9 | 10 | compileOnly 'org.jetbrains:annotations:26.0.2' 11 | compileOnly 'org.projectlombok:lombok:1.18.36' 12 | 13 | annotationProcessor 'org.projectlombok:lombok:1.18.36' 14 | } -------------------------------------------------------------------------------- /bungee/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'maven-publish' 4 | } 5 | 6 | dependencies { 7 | api project(path: ':common') 8 | 9 | compileOnly 'net.md-5:bungeecord-api:1.21-R0.4-SNAPSHOT' 10 | compileOnly 'net.kyori:adventure-platform-bungeecord:4.4.1' 11 | compileOnly 'org.projectlombok:lombok:1.18.36' 12 | 13 | annotationProcessor 'org.projectlombok:lombok:1.18.36' 14 | } -------------------------------------------------------------------------------- /bukkit/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'maven-publish' 4 | } 5 | 6 | dependencies { 7 | api project(path: ':common') 8 | 9 | implementation 'space.arim.morepaperlib:morepaperlib:0.4.4' 10 | 11 | compileOnly 'org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT' 12 | compileOnly 'net.kyori:adventure-platform-bukkit:4.4.1' 13 | compileOnly 'org.projectlombok:lombok:1.18.36' 14 | 15 | annotationProcessor 'org.projectlombok:lombok:1.18.36' 16 | } -------------------------------------------------------------------------------- /example-plugin/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'xyz.jpenilla.run-paper' version '2.3.1' 4 | } 5 | 6 | dependencies { 7 | implementation(project(":paper")) 8 | 9 | compileOnly 'io.papermc.paper:paper-api:1.20.6-R0.1-SNAPSHOT' 10 | } 11 | 12 | tasks { 13 | runServer { 14 | minecraftVersion("1.21.4") 15 | 16 | downloadPlugins { 17 | url('https://download.luckperms.net/1573/bukkit/loader/LuckPerms-Bukkit-5.4.156.jar') 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | javaVersion=17 2 | 3 | org.gradle.jvmargs='-Dfile.encoding=UTF-8' 4 | org.gradle.daemon=true 5 | 6 | # Ignore loom version errors - Essential gradle multi-version doesn't work with Loom 1.10 yet (needed for 1.21.5) :-( 7 | loom.ignoreDependencyLoomVersionValidation=true 8 | 9 | library_version=1.3.9 10 | library_archive=uniform 11 | library_description=Cross-platform wrapper for making Brigadier commands, based on BrigadierWrapper by Tofaa2, itself inspired by emortalmcs command system. 12 | -------------------------------------------------------------------------------- /fabric/root.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("gg.essential.multi-version.root") 3 | } 4 | 5 | preprocess { 6 | def fabric12108 = createNode("1.21.8", 12108, "yarn") 7 | def fabric12105 = createNode("1.21.5", 12105, "yarn") 8 | def fabric12104 = createNode("1.21.4", 12104, "yarn") 9 | def fabric12101 = createNode("1.21.1", 12101, "yarn") 10 | def fabric12001 = createNode("1.20.1", 12001, "yarn") 11 | 12 | fabric12105.link(fabric12108, null) 13 | fabric12104.link(fabric12108, null) 14 | fabric12101.link(fabric12108, null) 15 | fabric12001.link(fabric12108, null) 16 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Build/run paths ### 2 | build/ 3 | run/ 4 | target/ 5 | 6 | ### Gradle ### 7 | .gradle 8 | !gradle/wrapper/gradle-wrapper.jar 9 | 10 | ### IntelliJ IDEA ### 11 | .idea/ 12 | out/ 13 | *.iws 14 | *.iml 15 | *.ipr 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Dependabot configuration file for GitHub 2 | 3 | version: 2 4 | updates: 5 | # CI workflow action updates 6 | - package-ecosystem: "github-actions" 7 | directory: "/" 8 | schedule: 9 | interval: "weekly" 10 | commit-message: 11 | prefix: "ci" 12 | 13 | # Gradle package updates 14 | - package-ecosystem: "gradle" 15 | directory: "/" 16 | schedule: 17 | interval: "weekly" 18 | commit-message: 19 | prefix: "deps" 20 | ignore: 21 | - dependency-name: 'org.spigotmc:spigot-api' 22 | - dependency-name: 'io.papermc.paper:paper-api' 23 | - dependency-name: 'io.papermc.paper:paper-mojangapi' 24 | - dependency-name: 'com.velocitypowered:velocity-api' -------------------------------------------------------------------------------- /fabric/src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "uniform", 4 | "version": "${version}", 5 | "description": "${description}", 6 | "name": "uniform", 7 | "authors": [ 8 | "William278", 9 | "Tofaa2" 10 | ], 11 | "contact": { 12 | "homepage": "https://william278.net/project/uniform", 13 | "issues": "https://github.com/WiIIiam278/Uniform/issues", 14 | "sources": "https://github.com/WiIIiam278/Uniform" 15 | }, 16 | "license": "GPL-3.0", 17 | "custom": { 18 | "modmenu:api": true, 19 | "modmenu": { 20 | "badges": [ 21 | "library" 22 | ] 23 | } 24 | }, 25 | "depends": { 26 | "fabric-api": "*" 27 | }, 28 | "mixins": [ 29 | "uniform.mixins.json" 30 | ] 31 | } -------------------------------------------------------------------------------- /fabric/1.20.1/src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "uniform", 4 | "version": "${version}", 5 | "description": "${description}", 6 | "name": "uniform", 7 | "authors": [ 8 | "William278", 9 | "Tofaa2" 10 | ], 11 | "contact": { 12 | "homepage": "https://william278.net/project/uniform", 13 | "issues": "https://github.com/WiIIiam278/Uniform/issues", 14 | "sources": "https://github.com/WiIIiam278/Uniform" 15 | }, 16 | "license": "GPL-3.0", 17 | "custom": { 18 | "modmenu:api": true, 19 | "modmenu": { 20 | "badges": [ 21 | "library" 22 | ] 23 | } 24 | }, 25 | "depends": { 26 | "fabric-api": "*" 27 | }, 28 | "mixins": [ 29 | "uniform.mixins.json" 30 | ], 31 | "accessWidener": "uniform.accesswidener" 32 | } -------------------------------------------------------------------------------- /HEADER: -------------------------------------------------------------------------------- 1 | This file is part of Uniform, licensed under the GNU General Public License v3.0. 2 | 3 | Copyright (c) Tofaa2 4 | Copyright (c) William278 5 | Copyright (c) contributors 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # Builds, tests and publishes to maven when a release is published 2 | name: Release Tests & Publish 3 | 4 | on: 5 | release: 6 | types: [ published ] 7 | 8 | permissions: 9 | contents: read 10 | checks: write 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: 'Checkout for CI 🛎️' 17 | uses: actions/checkout@v4 18 | - name: 'Set up JDK 21 📦' 19 | uses: actions/setup-java@v4 20 | with: 21 | java-version: '21' 22 | distribution: 'temurin' 23 | - name: 'Build with Gradle 🏗️' 24 | uses: gradle/gradle-build-action@v3 25 | with: 26 | arguments: build test publish 27 | env: 28 | RELEASES_MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} 29 | RELEASES_MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # Builds, tests and publishes to maven when a commit is pushed 2 | name: CI Tests & Publish 3 | 4 | on: 5 | push: 6 | branches: [ 'master' ] 7 | paths-ignore: 8 | - 'workflows/**' 9 | - 'README.md' 10 | 11 | permissions: 12 | contents: read 13 | checks: write 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: 'Checkout for CI 🛎️' 20 | uses: actions/checkout@v4 21 | - name: 'Set up JDK 21 📦' 22 | uses: actions/setup-java@v4 23 | with: 24 | java-version: '21' 25 | distribution: 'temurin' 26 | - name: 'Build with Gradle 🏗️' 27 | uses: gradle/gradle-build-action@v3 28 | with: 29 | arguments: build test publish 30 | env: 31 | SNAPSHOTS_MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} 32 | SNAPSHOTS_MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} 33 | - name: 'Publish Test Report 📊' 34 | uses: mikepenz/action-junit-report@v5 35 | if: success() || failure() # always run even if the previous step fails 36 | with: 37 | report_paths: '**/build/test-results/test/TEST-*.xml' -------------------------------------------------------------------------------- /common/src/main/java/net/william278/uniform/CommandProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform; 23 | 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | @FunctionalInterface 27 | public interface CommandProvider { 28 | 29 | void provide(@NotNull BaseCommand command); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/uniform/element/CommandElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.element; 23 | 24 | import com.mojang.brigadier.builder.ArgumentBuilder; 25 | import org.jetbrains.annotations.NotNull; 26 | 27 | public interface CommandElement { 28 | 29 | @NotNull 30 | ArgumentBuilder toBuilder(); 31 | 32 | } -------------------------------------------------------------------------------- /common/src/main/java/net/william278/uniform/annotations/CommandDescription.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.annotations; 23 | 24 | import java.lang.annotation.ElementType; 25 | import java.lang.annotation.Retention; 26 | import java.lang.annotation.RetentionPolicy; 27 | import java.lang.annotation.Target; 28 | 29 | @Target({ElementType.FIELD}) 30 | @Retention(RetentionPolicy.RUNTIME) 31 | public @interface CommandDescription { 32 | 33 | } 34 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | maven { url 'https://maven.fabricmc.net/' } 5 | maven { url 'https://maven.architectury.dev/' } 6 | maven { url 'https://maven.minecraftforge.net' } 7 | maven { url 'https://repo.essential.gg/repository/maven-public' } 8 | } 9 | 10 | plugins { 11 | def egtVersion = "0.6.6" 12 | id("gg.essential.defaults") version egtVersion 13 | id("gg.essential.loom") version egtVersion 14 | id("gg.essential.multi-version.root") version egtVersion 15 | } 16 | } 17 | 18 | rootProject.name = 'Uniform' 19 | include( 20 | 'common', 21 | 22 | // Server Plugins 23 | 'paper', 24 | 'bukkit', 25 | 'sponge-11', 26 | 27 | // Proxy Plugins 28 | 'velocity', 29 | 'bungee', 30 | 31 | // Example plugin 32 | 'example-plugin' 33 | ) 34 | 35 | // Fabric Server-Side Mods 36 | include("fabric") 37 | project(":fabric").with { 38 | projectDir = file("fabric/") 39 | buildFileName = "root.gradle" 40 | } 41 | 42 | file('fabric').listFiles((FileFilter) ((File file) -> file.isDirectory() && file.name ==~ /(\d+)\.(\d+)(\.(\d+))?/)).each { 43 | include("fabric:$it.name") 44 | project(":fabric:$it.name").with { 45 | projectDir = file("fabric/${it.name}") 46 | buildFileName = '../build.gradle' 47 | } 48 | } -------------------------------------------------------------------------------- /common/src/main/java/net/william278/uniform/CommandSyntax.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform; 23 | 24 | import net.william278.uniform.element.CommandElement; 25 | import org.jetbrains.annotations.NotNull; 26 | import org.jetbrains.annotations.Nullable; 27 | 28 | import java.util.List; 29 | import java.util.function.Predicate; 30 | 31 | public record CommandSyntax(@Nullable Predicate condition, @NotNull CommandExecutor executor, 32 | @NotNull List> elements) { 33 | 34 | } 35 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/uniform/element/LiteralElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.element; 23 | 24 | import com.mojang.brigadier.builder.ArgumentBuilder; 25 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 26 | import org.jetbrains.annotations.NotNull; 27 | 28 | public record LiteralElement(@NotNull String name) implements CommandElement { 29 | 30 | @Override 31 | @NotNull 32 | public ArgumentBuilder toBuilder() { 33 | return LiteralArgumentBuilder.literal(this.name); 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /common/src/main/java/net/william278/uniform/annotations/PermissionNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.annotations; 23 | 24 | import net.william278.uniform.Permission; 25 | 26 | import java.lang.annotation.ElementType; 27 | import java.lang.annotation.Retention; 28 | import java.lang.annotation.RetentionPolicy; 29 | import java.lang.annotation.Target; 30 | 31 | @Target({ElementType.ANNOTATION_TYPE}) 32 | @Retention(RetentionPolicy.RUNTIME) 33 | public @interface PermissionNode { 34 | 35 | String value(); 36 | Permission.Default defaultValue() default Permission.Default.FALSE; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/uniform/annotations/Syntax.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.annotations; 23 | 24 | import net.william278.uniform.Command; 25 | 26 | import java.lang.annotation.ElementType; 27 | import java.lang.annotation.Retention; 28 | import java.lang.annotation.RetentionPolicy; 29 | import java.lang.annotation.Target; 30 | 31 | @Target({ElementType.METHOD}) 32 | @Retention(RetentionPolicy.RUNTIME) 33 | public @interface Syntax { 34 | 35 | PermissionNode permission() default @PermissionNode(""); 36 | Command.ExecutionScope scope() default Command.ExecutionScope.ALL; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/uniform/annotations/CommandNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.annotations; 23 | 24 | import net.william278.uniform.Command; 25 | 26 | import java.lang.annotation.ElementType; 27 | import java.lang.annotation.Retention; 28 | import java.lang.annotation.RetentionPolicy; 29 | import java.lang.annotation.Target; 30 | 31 | @Target({ElementType.TYPE}) 32 | @Retention(RetentionPolicy.RUNTIME) 33 | public @interface CommandNode { 34 | 35 | String value(); 36 | String[] aliases() default {}; 37 | String description() default ""; 38 | PermissionNode permission() default @PermissionNode(""); 39 | Command.ExecutionScope scope() default Command.ExecutionScope.ALL; 40 | 41 | } -------------------------------------------------------------------------------- /common/src/main/java/net/william278/uniform/CommandUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform; 23 | 24 | import net.kyori.adventure.audience.Audience; 25 | import org.jetbrains.annotations.NotNull; 26 | import org.jetbrains.annotations.Nullable; 27 | 28 | import java.util.UUID; 29 | 30 | @SuppressWarnings("unused") 31 | public interface CommandUser { 32 | 33 | @NotNull 34 | Audience getAudience(); 35 | 36 | @Nullable 37 | String getName(); 38 | 39 | @Nullable 40 | UUID getUuid(); 41 | 42 | boolean checkPermission(@NotNull Permission permission); 43 | 44 | default boolean checkPermission(@NotNull String permission, 45 | @NotNull Permission.Default permissionDefault) { 46 | return checkPermission(new Permission(permission, permissionDefault)); 47 | } 48 | 49 | default boolean isConsole() { 50 | return getName() == null; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/uniform/Uniform.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform; 23 | 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | import java.util.Arrays; 27 | import java.util.function.Function; 28 | 29 | public interface Uniform { 30 | 31 | void register(@NotNull Command... commands); 32 | 33 | default void register(@NotNull Object... annotated) { 34 | register(Arrays.stream(annotated) 35 | .map(c -> c instanceof Command cmd ? cmd : new Command.AnnotatedCommand(c)) 36 | .toArray(Command[]::new)); 37 | } 38 | 39 | @SuppressWarnings("unchecked") 40 | > void register(T... commands); 41 | 42 | @NotNull 43 | Function getCommandUserSupplier(); 44 | 45 | void setCommandUserSupplier(@NotNull Function supplier); 46 | 47 | default void shutdown() { 48 | BaseCommand.CACHED_EXECUTOR.shutdownNow(); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/uniform/Node.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform; 23 | 24 | import com.mojang.brigadier.builder.ArgumentBuilder; 25 | import com.mojang.brigadier.tree.CommandNode; 26 | import net.william278.uniform.element.CommandElement; 27 | import org.jetbrains.annotations.NotNull; 28 | import org.jetbrains.annotations.Nullable; 29 | 30 | import java.util.List; 31 | 32 | record Node(@NotNull CommandElement element, @Nullable Execution execution, @NotNull List> children) { 33 | 34 | static @NotNull Node command(@NotNull BaseCommand command) { 35 | return ConversionNode.fromCommand(command).toNode(); 36 | } 37 | 38 | @NotNull 39 | CommandNode build() { 40 | ArgumentBuilder builder = this.element.toBuilder(); 41 | if (this.execution != null) this.execution.addToBuilder(builder); 42 | 43 | for (Node child : this.children) { 44 | builder.then(child.build()); 45 | } 46 | 47 | return builder.build(); 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /paper/src/main/java/net/william278/uniform/paper/element/PaperArgumentElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.paper.element; 23 | 24 | import com.mojang.brigadier.StringReader; 25 | import com.mojang.brigadier.arguments.ArgumentType; 26 | import com.mojang.brigadier.arguments.StringArgumentType; 27 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 28 | import io.papermc.paper.command.brigadier.argument.CustomArgumentType; 29 | import lombok.AllArgsConstructor; 30 | import org.jetbrains.annotations.NotNull; 31 | 32 | @AllArgsConstructor 33 | @SuppressWarnings("UnstableApiUsage") 34 | public class PaperArgumentElement implements CustomArgumentType { 35 | 36 | private final ArgumentType wrapped; 37 | 38 | @Override 39 | @NotNull 40 | public T parse(@NotNull StringReader reader) throws CommandSyntaxException { 41 | return wrapped.parse(reader); 42 | } 43 | 44 | @Override 45 | @NotNull 46 | public ArgumentType getNativeType() { 47 | return StringArgumentType.string(); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /fabric/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'gg.essential.multi-version' 3 | id 'gg.essential.defaults' 4 | id 'java-library' 5 | } 6 | 7 | dependencies { 8 | def fabricApiVersion 9 | def fabricPermissionsApiVersion = "0.3.1" 10 | if (platform.mcVersion == 12001) { 11 | fabricApiVersion = "0.92.2+1.20.1" 12 | } else if (platform.mcVersion == 12101) { 13 | fabricApiVersion = "0.110.0+1.21.1" 14 | } else if (platform.mcVersion == 12104) { 15 | fabricApiVersion = "0.111.0+1.21.4" 16 | fabricPermissionsApiVersion = "0.3.3" 17 | } else if (platform.mcVersion == 12105) { 18 | fabricApiVersion = "0.119.6+1.21.5" 19 | fabricPermissionsApiVersion = "0.3.3" 20 | } else if (platform.mcVersion == 12108) { 21 | fabricApiVersion = "0.131.0+1.21.8" 22 | fabricPermissionsApiVersion = "0.4.1" 23 | } else { 24 | throw org.gradle.api.GradleException("Unsupported platform $platform") 25 | } 26 | 27 | modCompileOnly "net.fabricmc.fabric-api:fabric-api:$fabricApiVersion" 28 | 29 | modImplementation include("me.lucko:fabric-permissions-api:$fabricPermissionsApiVersion") 30 | 31 | modCompileOnly 'org.projectlombok:lombok:1.18.36' 32 | annotationProcessor 'org.projectlombok:lombok:1.18.36' 33 | 34 | shadow project(path: ':common') 35 | } 36 | 37 | if (platform.mcVersion < 12108) { 38 | loom.setAccessWidenerPath(parent.file("src/main/resources/uniform.accesswidener")) 39 | } 40 | 41 | shadowJar { 42 | configurations = [project.configurations.shadow] 43 | destinationDirectory.set(file("$projectDir/build/libs")) 44 | 45 | exclude('net.fabricmc:.*') 46 | exclude('net.kyori:.*') 47 | exclude '/mappings/*' 48 | } 49 | 50 | remapJar { 51 | dependsOn tasks.shadowJar 52 | mustRunAfter tasks.shadowJar 53 | inputFile = shadowJar.archiveFile.get() 54 | addNestedDependencies = true 55 | 56 | destinationDirectory.set(file("$rootDir/target/")) 57 | archiveClassifier.set('') 58 | } 59 | 60 | shadowJar.finalizedBy(remapJar) -------------------------------------------------------------------------------- /fabric/src/main/java/net/william278/uniform/fabric/FabricCommandUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.fabric; 23 | 24 | import me.lucko.fabric.api.permissions.v0.Permissions; 25 | import net.kyori.adventure.audience.Audience; 26 | import net.minecraft.server.command.ServerCommandSource; 27 | import net.william278.uniform.CommandUser; 28 | import net.william278.uniform.Permission; 29 | import org.jetbrains.annotations.NotNull; 30 | import org.jetbrains.annotations.Nullable; 31 | 32 | import java.util.UUID; 33 | 34 | public record FabricCommandUser(@NotNull ServerCommandSource source) implements CommandUser { 35 | 36 | @Override 37 | public @NotNull Audience getAudience() { 38 | return (Audience) source.getPlayer(); 39 | } 40 | 41 | @Override 42 | @Nullable 43 | public String getName() { 44 | return source.getName(); 45 | } 46 | 47 | @Override 48 | @Nullable 49 | public UUID getUuid() { 50 | return source.getPlayer() != null ? source.getPlayer().getUuid() : null; 51 | } 52 | 53 | @Override 54 | public boolean checkPermission(@NotNull Permission permission) { 55 | return Permissions.check( 56 | source, permission.node(), 57 | permission.defaultValue().check(source.hasPermissionLevel(4)) 58 | ); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /bungee/src/main/java/net/william278/uniform/bungee/BungeeCommandUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.bungee; 23 | 24 | import net.kyori.adventure.audience.Audience; 25 | import net.md_5.bungee.api.CommandSender; 26 | import net.md_5.bungee.api.connection.ProxiedPlayer; 27 | import net.william278.uniform.CommandUser; 28 | import net.william278.uniform.Permission; 29 | import org.jetbrains.annotations.NotNull; 30 | import org.jetbrains.annotations.Nullable; 31 | 32 | import java.util.UUID; 33 | 34 | public record BungeeCommandUser(CommandSender source) implements CommandUser { 35 | 36 | @Override 37 | @NotNull 38 | public Audience getAudience() { 39 | return BungeeUniform.getAudiences().sender(source); 40 | } 41 | 42 | @Override 43 | @Nullable 44 | public String getName() { 45 | return source instanceof ProxiedPlayer ? source.getName() : null; 46 | } 47 | 48 | @Override 49 | @Nullable 50 | public UUID getUuid() { 51 | return source instanceof ProxiedPlayer ? ((ProxiedPlayer) source).getUniqueId() : null; 52 | } 53 | 54 | @Override 55 | public boolean checkPermission(@NotNull Permission permission) { 56 | if (source.hasPermission(permission.node())) { 57 | return source.hasPermission(permission.node()); 58 | } 59 | return permission.defaultValue().check(source.hasPermission(permission.node())); 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /common/src/main/java/net/william278/uniform/element/ArgumentElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.element; 23 | 24 | 25 | import com.mojang.brigadier.arguments.ArgumentType; 26 | import com.mojang.brigadier.builder.ArgumentBuilder; 27 | import com.mojang.brigadier.builder.RequiredArgumentBuilder; 28 | import com.mojang.brigadier.suggestion.SuggestionProvider; 29 | import lombok.AllArgsConstructor; 30 | import lombok.Getter; 31 | import lombok.RequiredArgsConstructor; 32 | import lombok.experimental.Accessors; 33 | import org.jetbrains.annotations.NotNull; 34 | import org.jetbrains.annotations.Nullable; 35 | 36 | @Accessors(fluent = true) 37 | @Getter 38 | @AllArgsConstructor 39 | @RequiredArgsConstructor 40 | public final class ArgumentElement implements CommandElement { 41 | 42 | @NotNull 43 | private final String name; 44 | @NotNull 45 | private final ArgumentType type; 46 | @Nullable 47 | private final SuggestionProvider suggestionProvider; 48 | private boolean custom = true; 49 | 50 | @Override 51 | @NotNull 52 | public ArgumentBuilder toBuilder() { 53 | var builder = RequiredArgumentBuilder.argument(this.name, this.type); 54 | if (this.suggestionProvider != null) builder.suggests(this.suggestionProvider); 55 | return builder; 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /paper/src/main/java/net/william278/uniform/paper/LegacyPaperCommandUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.paper; 23 | 24 | import net.kyori.adventure.audience.Audience; 25 | import net.william278.uniform.CommandUser; 26 | import net.william278.uniform.Permission; 27 | import org.bukkit.command.CommandSender; 28 | import org.bukkit.command.ConsoleCommandSender; 29 | import org.bukkit.entity.Player; 30 | import org.jetbrains.annotations.NotNull; 31 | import org.jetbrains.annotations.Nullable; 32 | 33 | import java.util.UUID; 34 | 35 | public record LegacyPaperCommandUser(@NotNull CommandSender sender) implements CommandUser { 36 | 37 | @Override 38 | @NotNull 39 | public Audience getAudience() { 40 | return sender; 41 | } 42 | 43 | @Override 44 | public String getName() { 45 | return sender.getName(); 46 | } 47 | 48 | @Override 49 | @Nullable 50 | public UUID getUuid() { 51 | return sender instanceof Player player ? player.getUniqueId() : null; 52 | } 53 | 54 | @Override 55 | public boolean checkPermission(@NotNull Permission permission) { 56 | if (sender.isPermissionSet(permission.node())) { 57 | return sender.hasPermission(permission.node()); 58 | } 59 | return permission.defaultValue().check( 60 | sender.isOp() || sender instanceof ConsoleCommandSender 61 | ); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /bukkit/src/main/java/net/william278/uniform/bukkit/BukkitCommandUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.bukkit; 23 | 24 | import net.kyori.adventure.audience.Audience; 25 | import net.william278.uniform.CommandUser; 26 | import net.william278.uniform.Permission; 27 | import org.bukkit.command.CommandSender; 28 | import org.bukkit.command.ConsoleCommandSender; 29 | import org.bukkit.entity.Player; 30 | import org.jetbrains.annotations.NotNull; 31 | import org.jetbrains.annotations.Nullable; 32 | 33 | import java.util.UUID; 34 | 35 | public record BukkitCommandUser(@NotNull CommandSender sender) implements CommandUser { 36 | 37 | @Override 38 | @NotNull 39 | public Audience getAudience() { 40 | return BukkitUniform.getAudiences().sender(sender); 41 | } 42 | 43 | @Override 44 | public String getName() { 45 | return sender.getName(); 46 | } 47 | 48 | @Override 49 | @Nullable 50 | public UUID getUuid() { 51 | return sender instanceof Player player ? player.getUniqueId() : null; 52 | } 53 | 54 | @Override 55 | public boolean checkPermission(@NotNull Permission permission) { 56 | if (sender.isPermissionSet(permission.node())) { 57 | return sender.hasPermission(permission.node()); 58 | } 59 | return permission.defaultValue().check( 60 | sender.isOp() || sender instanceof ConsoleCommandSender 61 | ); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /velocity/src/main/java/net/william278/uniform/velocity/VelocityCommandUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.velocity; 23 | 24 | import com.velocitypowered.api.command.CommandSource; 25 | import com.velocitypowered.api.permission.Tristate; 26 | import com.velocitypowered.api.proxy.Player; 27 | import net.kyori.adventure.audience.Audience; 28 | import net.william278.uniform.CommandUser; 29 | import net.william278.uniform.Permission; 30 | import org.jetbrains.annotations.NotNull; 31 | import org.jetbrains.annotations.Nullable; 32 | 33 | import java.util.UUID; 34 | 35 | public record VelocityCommandUser(CommandSource source) implements CommandUser { 36 | 37 | @Override 38 | @NotNull 39 | public Audience getAudience() { 40 | return source; 41 | } 42 | 43 | @Override 44 | @Nullable 45 | public String getName() { 46 | return source instanceof Player ? ((Player) source).getUsername() : null; 47 | } 48 | 49 | @Override 50 | @Nullable 51 | public UUID getUuid() { 52 | return source instanceof Player ? ((Player) source).getUniqueId() : null; 53 | } 54 | 55 | @Override 56 | public boolean checkPermission(@NotNull Permission permission) { 57 | if (source.getPermissionValue(permission.node()) != Tristate.UNDEFINED) { 58 | return source.getPermissionValue(permission.node()) == Tristate.TRUE; 59 | } 60 | return permission.defaultValue().check(source.hasPermission(permission.node())); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /sponge-11/src/main/java/net/william278/uniform/sponge/SpongeCommandUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.sponge; 23 | 24 | import net.kyori.adventure.audience.Audience; 25 | import net.william278.uniform.CommandUser; 26 | import net.william278.uniform.Permission; 27 | import org.jetbrains.annotations.NotNull; 28 | import org.jetbrains.annotations.Nullable; 29 | import org.spongepowered.api.command.CommandCause; 30 | import org.spongepowered.api.entity.living.player.Player; 31 | import org.spongepowered.api.util.Tristate; 32 | 33 | import java.util.UUID; 34 | 35 | public record SpongeCommandUser(@NotNull CommandCause executor) implements CommandUser { 36 | 37 | @Override 38 | @NotNull 39 | public Audience getAudience() { 40 | return executor.audience(); 41 | } 42 | 43 | @Override 44 | @Nullable 45 | public String getName() { 46 | return executor instanceof Player player ? player.name() : null; 47 | } 48 | 49 | @Override 50 | @Nullable 51 | public UUID getUuid() { 52 | return executor instanceof Player player ? player.uniqueId() : null; 53 | } 54 | 55 | @Override 56 | public boolean checkPermission(@NotNull Permission permission) { 57 | final Tristate state = executor.permissionValue(permission.toString()); 58 | if (state == Tristate.UNDEFINED && permission.defaultValue() == Permission.Default.IF_OP) { 59 | return executor.hasPermission(permission.toString()); 60 | } 61 | return state == Tristate.TRUE; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /paper/src/main/java/net/william278/uniform/paper/PaperCommandUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.paper; 23 | 24 | import io.papermc.paper.command.brigadier.CommandSourceStack; 25 | import net.kyori.adventure.audience.Audience; 26 | import net.william278.uniform.CommandUser; 27 | import net.william278.uniform.Permission; 28 | import org.bukkit.command.ConsoleCommandSender; 29 | import org.jetbrains.annotations.NotNull; 30 | import org.jetbrains.annotations.Nullable; 31 | 32 | import java.util.UUID; 33 | 34 | @SuppressWarnings("UnstableApiUsage") 35 | public record PaperCommandUser(@NotNull CommandSourceStack source) implements CommandUser { 36 | 37 | 38 | @Override 39 | @NotNull 40 | public Audience getAudience() { 41 | return source.getSender(); 42 | } 43 | 44 | @Override 45 | @Nullable 46 | public String getName() { 47 | return source.getExecutor() != null ? source.getExecutor().getName() : null; 48 | } 49 | 50 | @Override 51 | @Nullable 52 | public UUID getUuid() { 53 | return source.getExecutor() != null ? source.getExecutor().getUniqueId() : null; 54 | } 55 | 56 | @Override 57 | public boolean checkPermission(@NotNull Permission permission) { 58 | if (source.getSender().isPermissionSet(permission.node())) { 59 | return source.getSender().hasPermission(permission.node()); 60 | } 61 | return permission.defaultValue().check( 62 | source.getSender().isOp() || source.getSender() instanceof ConsoleCommandSender 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/uniform/Graph.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform; 23 | 24 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 25 | import com.mojang.brigadier.tree.CommandNode; 26 | import com.mojang.brigadier.tree.LiteralCommandNode; 27 | import net.william278.uniform.element.CommandElement; 28 | import net.william278.uniform.element.LiteralElement; 29 | import org.jetbrains.annotations.NotNull; 30 | 31 | record Graph(@NotNull Node root) { 32 | 33 | static @NotNull Graph create(@NotNull BaseCommand command) { 34 | return new Graph<>(Node.command(command)); 35 | } 36 | 37 | static @NotNull CommandElement commandToElement(@NotNull BaseCommand command) { 38 | return new LiteralElement<>(command.getName()); 39 | } 40 | 41 | @NotNull 42 | LiteralCommandNode build() { 43 | CommandNode node = this.root.build(); 44 | if (!(node instanceof LiteralCommandNode literalNode)) { 45 | throw new IllegalStateException("Root node is somehow not a literal node. This should be impossible."); 46 | } 47 | return literalNode; 48 | } 49 | 50 | @NotNull 51 | LiteralArgumentBuilder literal(@NotNull String name) { 52 | final LiteralArgumentBuilder builder = LiteralArgumentBuilder.literal(name); 53 | final CommandNode command = this.root.build(); 54 | builder.executes(command.getCommand()); 55 | this.root.children().forEach(child -> builder.then(child.build())); 56 | return builder; 57 | } 58 | 59 | 60 | } -------------------------------------------------------------------------------- /fabric/src/main/java/net/william278/uniform/fabric/mixins/CommandManagerMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | //#if MC>=12108 23 | package net.william278.uniform.fabric.mixins; 24 | 25 | import com.mojang.brigadier.arguments.ArgumentType; 26 | import com.mojang.brigadier.arguments.StringArgumentType; 27 | import net.minecraft.command.argument.ArgumentTypes; 28 | import net.minecraft.command.argument.serialize.ArgumentSerializer; 29 | import net.minecraft.network.packet.s2c.play.CommandTreeS2CPacket; 30 | import org.spongepowered.asm.mixin.Mixin; 31 | import org.spongepowered.asm.mixin.injection.At; 32 | import org.spongepowered.asm.mixin.injection.Redirect; 33 | 34 | @Mixin(CommandTreeS2CPacket.class) 35 | public class CommandManagerMixin { 36 | 37 | @Redirect( 38 | method = "createNodeData", 39 | at = @At( 40 | value = "INVOKE", 41 | target = "Lnet/minecraft/command/argument/ArgumentTypes;getArgumentTypeProperties(Lcom/mojang/brigadier/arguments/ArgumentType;)Lnet/minecraft/command/argument/serialize/ArgumentSerializer$ArgumentTypeProperties;" 42 | ) 43 | ) 44 | private static ArgumentSerializer.ArgumentTypeProperties onConstruct(ArgumentType type) { 45 | ArgumentSerializer.ArgumentTypeProperties properties; 46 | try { 47 | properties = ArgumentTypes.getArgumentTypeProperties(type); 48 | } catch (IllegalArgumentException e) { 49 | properties = ArgumentTypes.getArgumentTypeProperties(StringArgumentType.string()); 50 | } 51 | return properties; 52 | } 53 | 54 | } 55 | //#endif -------------------------------------------------------------------------------- /common/src/main/java/net/william278/uniform/Permission.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform; 23 | 24 | import net.william278.uniform.annotations.PermissionNode; 25 | import org.jetbrains.annotations.NotNull; 26 | 27 | import java.util.Optional; 28 | import java.util.function.Predicate; 29 | 30 | @SuppressWarnings("unused") 31 | public record Permission(@NotNull String node, @NotNull Default defaultValue) { 32 | 33 | Permission(@NotNull String node) { 34 | this(node, Default.FALSE); 35 | } 36 | 37 | @NotNull 38 | public static Permission defaultIfOp(@NotNull String node) { 39 | return new Permission(node, Default.IF_OP); 40 | } 41 | 42 | @NotNull 43 | public static Permission defaultTrue(@NotNull String node) { 44 | return new Permission(node, Default.TRUE); 45 | } 46 | 47 | @NotNull 48 | public static Permission defaultFalse(@NotNull String node) { 49 | return new Permission(node, Default.FALSE); 50 | } 51 | 52 | @NotNull 53 | static Optional annotated(@NotNull PermissionNode annotation) { 54 | if (annotation.value().isBlank()) { 55 | return Optional.empty(); 56 | } 57 | return Optional.of(new Permission(annotation.value(), annotation.defaultValue())); 58 | } 59 | 60 | public enum Default { 61 | IF_OP, 62 | TRUE, 63 | FALSE; 64 | 65 | public boolean check(boolean op) { 66 | return switch (this) { 67 | case IF_OP -> op; 68 | case TRUE -> true; 69 | case FALSE -> false; 70 | }; 71 | } 72 | } 73 | 74 | @NotNull 75 | public Predicate toPredicate(@NotNull BaseCommand command) { 76 | return user -> command.getUser(user).checkPermission(this); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /fabric/src/main/java/net/william278/uniform/fabric/mixins/ArgumentNodeMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | //#if MC<=12105 23 | //$$ 24 | //$$ package net.william278.uniform.fabric.mixins; 25 | //$$ 26 | //$$ import com.mojang.brigadier.arguments.ArgumentType; 27 | //$$ import com.mojang.brigadier.arguments.StringArgumentType; 28 | //$$ import net.minecraft.command.argument.ArgumentTypes; 29 | //$$ import net.minecraft.command.argument.serialize.ArgumentSerializer; 30 | //$$ import net.minecraft.network.packet.s2c.play.CommandTreeS2CPacket.ArgumentNode; 31 | //$$ import org.spongepowered.asm.mixin.Mixin; 32 | //$$ import org.spongepowered.asm.mixin.injection.At; 33 | //$$ import org.spongepowered.asm.mixin.injection.Inject; 34 | //$$ import org.spongepowered.asm.mixin.injection.Redirect; 35 | //$$ 36 | //$$ @Mixin(ArgumentNode.class) 37 | //$$ public class ArgumentNodeMixin { 38 | //$$ 39 | //$$ @Redirect( 40 | //$$ method = "(Lcom/mojang/brigadier/tree/ArgumentCommandNode;)V", 41 | //$$ at = @At( 42 | //$$ value = "INVOKE", 43 | //$$ target = "Lnet/minecraft/command/argument/ArgumentTypes;getArgumentTypeProperties(Lcom/mojang/brigadier/arguments/ArgumentType;)Lnet/minecraft/command/argument/serialize/ArgumentSerializer$ArgumentTypeProperties;" 44 | //$$ ) 45 | //$$ ) 46 | //$$ private static ArgumentSerializer.ArgumentTypeProperties onConstruct(ArgumentType type) { 47 | //$$ ArgumentSerializer.ArgumentTypeProperties properties; 48 | //$$ try { 49 | //$$ properties = ArgumentTypes.getArgumentTypeProperties(type); 50 | //$$ } catch (IllegalArgumentException e) { 51 | //$$ properties = ArgumentTypes.getArgumentTypeProperties(StringArgumentType.string()); 52 | //$$ } 53 | //$$ return properties; 54 | //$$ } 55 | //$$ 56 | //$$ } 57 | //$$ 58 | //#endif -------------------------------------------------------------------------------- /common/src/main/java/net/william278/uniform/ConversionNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform; 23 | 24 | import net.william278.uniform.element.CommandElement; 25 | import org.jetbrains.annotations.NotNull; 26 | import org.jetbrains.annotations.Nullable; 27 | 28 | import java.util.LinkedHashMap; 29 | import java.util.List; 30 | import java.util.Map; 31 | 32 | import static net.william278.uniform.Graph.commandToElement; 33 | 34 | record ConversionNode(@NotNull CommandElement element, @Nullable Execution execution, 35 | @NotNull Map, ConversionNode> nextMap) { 36 | 37 | static @NotNull ConversionNode fromCommand(@NotNull BaseCommand command) { 38 | ConversionNode root = new ConversionNode<>(commandToElement(command), Execution.fromCommand(command)); 39 | 40 | for (CommandSyntax syntax : command.getSyntaxes()) { 41 | ConversionNode syntaxNode = root; 42 | 43 | for (CommandElement element : syntax.elements()) { 44 | boolean last = element == syntax.elements().get(syntax.elements().size() - 1); 45 | syntaxNode = syntaxNode.nextMap.computeIfAbsent(element, e -> { 46 | Execution execution = last ? Execution.fromSyntax(syntax) : null; 47 | return new ConversionNode<>(e, execution); 48 | }); 49 | } 50 | } 51 | 52 | for (BaseCommand subCommand : command.getSubCommands()) { 53 | root.nextMap.put(commandToElement(subCommand), fromCommand(subCommand)); 54 | } 55 | 56 | return root; 57 | } 58 | 59 | ConversionNode(@NotNull CommandElement element, @Nullable Execution execution) { 60 | this(element, execution, new LinkedHashMap<>()); 61 | } 62 | 63 | Node toNode() { 64 | @SuppressWarnings("unchecked") // this is fine - we only put Node in to this array 65 | Node[] nodes = (Node[]) new Node[this.nextMap.size()]; 66 | 67 | int i = 0; 68 | for (ConversionNode entry : this.nextMap.values()) { 69 | nodes[i++] = entry.toNode(); 70 | } 71 | 72 | return new Node<>(this.element, this.execution, List.of(nodes)); 73 | } 74 | 75 | } -------------------------------------------------------------------------------- /common/src/main/java/net/william278/uniform/CommandExecutor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform; 23 | 24 | import com.mojang.brigadier.context.CommandContext; 25 | import net.william278.uniform.annotations.Argument; 26 | import org.jetbrains.annotations.NotNull; 27 | import org.jetbrains.annotations.Nullable; 28 | 29 | import java.lang.reflect.InvocationTargetException; 30 | import java.lang.reflect.Method; 31 | import java.lang.reflect.Parameter; 32 | 33 | public interface CommandExecutor { 34 | 35 | void execute(@NotNull CommandContext context); 36 | 37 | @NotNull 38 | static CommandExecutor methodToExecutor(@NotNull Method method, @NotNull Object instance, 39 | @NotNull BaseCommand cmd) { 40 | return (context) -> { 41 | try { 42 | method.invoke(instance, injectParams(method, context, cmd)); 43 | } catch (IllegalAccessException | InvocationTargetException e) { 44 | throw new IllegalStateException("Failed to invoke command executor from annotated method", e); 45 | } 46 | }; 47 | } 48 | 49 | @Nullable 50 | private static Object @NotNull [] injectParams(@NotNull Method method, @NotNull CommandContext context, 51 | @NotNull BaseCommand cmd) { 52 | final Object[] params = new Object[method.getParameterCount()]; 53 | for (int i = 0; i < method.getParameterCount(); i++) { 54 | final Parameter param = method.getParameters()[i]; 55 | final Class type = param.getType(); 56 | final Argument arg = param.getAnnotation(Argument.class); 57 | if (arg != null) { 58 | params[i] = context.getArgument(arg.name().isEmpty() ? param.getName() : arg.name(), type); 59 | continue; 60 | } 61 | if (CommandUser.class.isAssignableFrom(type)) { 62 | params[i] = cmd.getUser(context.getSource()); 63 | continue; 64 | } 65 | if (type.isAssignableFrom(context.getClass())) { 66 | params[i] = context; 67 | continue; 68 | } 69 | params[i] = null; 70 | } 71 | return params; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /example-plugin/src/main/java/net/william278/uniform/UniformExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform; 23 | 24 | import io.papermc.paper.command.brigadier.CommandSourceStack; 25 | import net.william278.uniform.paper.PaperCommand; 26 | import net.william278.uniform.paper.PaperUniform; 27 | import org.bukkit.plugin.java.JavaPlugin; 28 | 29 | import java.util.List; 30 | 31 | @SuppressWarnings("unused") 32 | public class UniformExample extends JavaPlugin { 33 | 34 | @Override 35 | public void onEnable() { 36 | PaperUniform uniform = PaperUniform.getInstance(this); 37 | uniform.register(new ExtendedCommand(), getBuiltCommand(),new AnnotatedCommand()); 38 | } 39 | 40 | public BaseCommand getBuiltCommand() { 41 | return PaperCommand.builder("builded") 42 | .setDescription("A builded command") 43 | .setAliases(List.of("builded", "build")) 44 | .setPermission(new Permission("uniform.builded")) 45 | .addSubCommand(builtSubCommand()) 46 | .build(); 47 | } 48 | 49 | private BaseCommand builtSubCommand() { 50 | return PaperCommand.builder("get-pet") 51 | .setDescription("Gets a pet") 52 | .setAliases(List.of("pet", "lilpet")) 53 | .setPermission(new Permission("uniform.pet")) 54 | .addStringArgument("petName", ((commandContext, suggestionsBuilder) -> { 55 | if (commandContext.getSource().getSender().hasPermission("uniform.cat")) { 56 | suggestionsBuilder.suggest("cat"); 57 | } 58 | return suggestionsBuilder.suggest("dog").buildFuture(); 59 | })) 60 | .execute(commandContext -> { 61 | String petName = commandContext.getArgument("petName", String.class); 62 | commandContext.getSource().getSender().sendMessage("You have a " + petName); 63 | },"petName") //Here you can specify the argument to use for this "command syntax" 64 | .execute(commandContext -> { 65 | commandContext.getSource().getSender().sendMessage("Missing argument petName"); 66 | }) //Syntax without arguments 67 | .build(); 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/uniform/Execution.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform; 23 | 24 | import com.mojang.brigadier.builder.ArgumentBuilder; 25 | import org.jetbrains.annotations.NotNull; 26 | import org.jetbrains.annotations.Nullable; 27 | 28 | import java.util.function.Predicate; 29 | 30 | record Execution(@NotNull Predicate predicate, @Nullable CommandExecutor defaultExecutor, 31 | @Nullable CommandExecutor executor, @Nullable Predicate condition) implements Predicate { 32 | 33 | @NotNull 34 | static Execution fromCommand(@NotNull BaseCommand command) { 35 | CommandExecutor defaultExecutor = command.getDefaultExecutor(); 36 | Predicate defaultCondition = command.getCondition(); 37 | 38 | CommandExecutor executor = defaultExecutor; 39 | Predicate condition = defaultCondition; 40 | for (CommandSyntax syntax : command.getSyntaxes()) { 41 | if (!syntax.elements().isEmpty()) continue; 42 | executor = syntax.executor(); 43 | condition = syntax.condition(); 44 | break; 45 | } 46 | 47 | return new Execution<>(source -> defaultCondition == null || defaultCondition.test(source), defaultExecutor, executor, condition); 48 | } 49 | 50 | @NotNull 51 | static Execution fromSyntax(@NotNull CommandSyntax syntax) { 52 | CommandExecutor executor = syntax.executor(); 53 | Predicate condition = syntax.condition(); 54 | return new Execution<>(source -> condition == null || condition.test(source), null, executor, condition); 55 | } 56 | 57 | @Override 58 | public boolean test(@NotNull S source) { 59 | return this.predicate.test(source); 60 | } 61 | 62 | void addToBuilder(@NotNull ArgumentBuilder builder) { 63 | if (this.condition != null) builder.requires(this.condition); 64 | if (this.executor != null) { 65 | builder.executes(convertExecutor(this.executor)); 66 | } else if (this.defaultExecutor != null) { 67 | builder.executes(convertExecutor(this.defaultExecutor)); 68 | } 69 | } 70 | 71 | @NotNull 72 | private static com.mojang.brigadier.Command convertExecutor(@NotNull CommandExecutor executor) { 73 | return context -> { 74 | BaseCommand.CACHED_EXECUTOR.execute(() -> executor.execute(context)); 75 | return 1; 76 | }; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /example-plugin/src/main/java/net/william278/uniform/AnnotatedCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform; 23 | 24 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 25 | import net.kyori.adventure.text.Component; 26 | import net.kyori.adventure.text.format.NamedTextColor; 27 | import net.william278.uniform.annotations.*; 28 | import net.william278.uniform.element.ArgumentElement; 29 | import org.jetbrains.annotations.NotNull; 30 | 31 | import java.util.Locale; 32 | 33 | @CommandNode( 34 | value = "annotated", 35 | permission = @PermissionNode(value = "uniform.annotated", defaultValue = Permission.Default.TRUE) 36 | ) 37 | public class AnnotatedCommand { 38 | 39 | @CommandDescription // Can use @CommandDescription instead of @CommandNode field for localization support here 40 | public final String DESCRIPTION = "An example Uniform annotated command"; 41 | 42 | public AnnotatedCommand() { 43 | } 44 | 45 | @Syntax 46 | void defaultExecutor(CommandUser user) { 47 | user.getAudience().sendMessage(Component.text("No arguments passed!")); 48 | } 49 | 50 | @CommandNode("ping") 51 | static class Ping { 52 | 53 | public Ping() { 54 | } 55 | 56 | @Syntax 57 | public void pong(CommandUser user) { 58 | user.getAudience().sendMessage(Component.text("Pong!")); 59 | } 60 | 61 | @Syntax 62 | public void pongMessage( 63 | CommandUser user, 64 | @Argument(parser = Argument.StringArg.class) String message 65 | ) { 66 | user.getAudience().sendMessage(Component.text("Pong! " + message, NamedTextColor.GREEN)); 67 | } 68 | 69 | @Syntax 70 | public void pongMessageWithColor( 71 | CommandUser user, 72 | @Argument(name = "message", parser = Argument.StringArg.class) String message, 73 | @Argument(name = "color", parser = ColorArg.class) NamedTextColor color 74 | ) { 75 | user.getAudience().sendMessage(Component.text("Colored Pong! " + message, color)); 76 | } 77 | 78 | public static class ColorArg extends Argument.ArgumentProvider { 79 | @Override 80 | public ArgumentElement provide(@NotNull String name) { 81 | return new ArgumentElement<>(name, (r) -> { 82 | final NamedTextColor color = NamedTextColor.NAMES.value(r.readString().toLowerCase(Locale.ENGLISH)); 83 | if (color == null) { 84 | throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().create(); 85 | } 86 | return color; 87 | }, (context, builder) -> { 88 | NamedTextColor.NAMES.keys().forEach(color -> builder.suggest(color.toLowerCase(Locale.ENGLISH))); 89 | return builder.buildFuture(); 90 | }); 91 | } 92 | } 93 | 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /example-plugin/src/main/java/net/william278/uniform/ExtendedCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform; 23 | 24 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 25 | import net.kyori.adventure.text.Component; 26 | import net.william278.uniform.element.ArgumentElement; 27 | import org.jetbrains.annotations.NotNull; 28 | 29 | import java.util.Arrays; 30 | import java.util.List; 31 | import java.util.Locale; 32 | 33 | import static net.william278.uniform.BaseCommand.greedyString; 34 | 35 | // Example using the API to extend "Command" 36 | public class ExtendedCommand extends Command { 37 | 38 | public ExtendedCommand() { 39 | super("example"); 40 | setDescription("An example command for Uniform"); 41 | setAliases(List.of("helloworld")); 42 | setPermission(Permission.defaultIfOp("uniform.example")); 43 | } 44 | 45 | @Override 46 | public void provide(@NotNull BaseCommand command) { 47 | command.setDefaultExecutor((ctx) -> { 48 | final CommandUser user = command.getUser(ctx.getSource()); 49 | user.getAudience().sendMessage(Component.text("Hello, World!")); 50 | }); 51 | command.addSubCommand("message", (sub) -> sub.addSyntax((ctx) -> { 52 | final CommandUser user = sub.getUser(ctx.getSource()); 53 | user.getAudience().sendMessage(Component.text(ctx.getArgument("message", String.class))); 54 | }, greedyString("message"))); 55 | command.addSubCommand("flavor", (sub) -> sub.addSyntax((ctx) -> { 56 | final CommandUser user = sub.getUser(ctx.getSource()); 57 | final IceCreamFlavor flavor = ctx.getArgument("flavor", IceCreamFlavor.class); 58 | switch (flavor) { 59 | case VANILLA -> user.getAudience().sendMessage(Component.text("Vanilla ice cream is fine!")); 60 | case CHOCOLATE -> user.getAudience().sendMessage(Component.text("Chocolate ice cream is kino!")); 61 | case STRAWBERRY -> user.getAudience().sendMessage(Component.text("Strawberry ice cream is ok...")); 62 | } 63 | }, exampleCustomArg())); 64 | } 65 | 66 | private static ArgumentElement exampleCustomArg() { 67 | return new ArgumentElement<>("flavor", reader -> { 68 | final String flavor = reader.readString(); 69 | try { 70 | return IceCreamFlavor.valueOf(flavor.toUpperCase(Locale.ENGLISH)); 71 | } catch (IllegalArgumentException e) { 72 | throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().create(); 73 | } 74 | }, (context, builder) -> { 75 | Arrays.stream(IceCreamFlavor.values()).forEach( 76 | flavor -> builder.suggest(flavor.name().toLowerCase(Locale.ENGLISH)) 77 | ); 78 | return builder.buildFuture(); 79 | }); 80 | } 81 | 82 | enum IceCreamFlavor { 83 | VANILLA, 84 | CHOCOLATE, 85 | STRAWBERRY 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /bukkit/src/main/java/net/william278/uniform/bukkit/BukkitUniform.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.bukkit; 23 | 24 | import lombok.Getter; 25 | import lombok.Setter; 26 | import net.kyori.adventure.platform.bukkit.BukkitAudiences; 27 | import net.william278.uniform.BaseCommand; 28 | import net.william278.uniform.Command; 29 | import net.william278.uniform.CommandUser; 30 | import net.william278.uniform.Uniform; 31 | import org.bukkit.command.CommandSender; 32 | import org.bukkit.plugin.java.JavaPlugin; 33 | import org.jetbrains.annotations.NotNull; 34 | import space.arim.morepaperlib.MorePaperLib; 35 | import space.arim.morepaperlib.commands.CommandRegistration; 36 | 37 | import java.util.Arrays; 38 | import java.util.Locale; 39 | import java.util.function.Function; 40 | 41 | /** 42 | * A class for registering commands with the Bukkit server's CommandMap 43 | * 44 | * @since 1.0 45 | */ 46 | @SuppressWarnings("unused") 47 | public final class BukkitUniform implements Uniform { 48 | 49 | static BukkitUniform INSTANCE; 50 | private static BukkitAudiences AUDIENCES; 51 | private static JavaPlugin PLUGIN; 52 | 53 | private final CommandRegistration registrar; 54 | 55 | @Getter 56 | @Setter 57 | Function commandUserSupplier = (user) -> new BukkitCommandUser((CommandSender) user); 58 | 59 | private BukkitUniform(@NotNull JavaPlugin plugin) { 60 | PLUGIN = plugin; 61 | this.registrar = new MorePaperLib(plugin).commandRegistration(); 62 | } 63 | 64 | static BukkitAudiences getAudiences() { 65 | return AUDIENCES != null ? AUDIENCES : (AUDIENCES = BukkitAudiences.create(PLUGIN)); 66 | } 67 | 68 | /** 69 | * Get the BukkitUniform instance for registering commands 70 | * 71 | * @param plugin The plugin instance 72 | * @return BukkitUniform instance 73 | * @since 1.0 74 | */ 75 | @NotNull 76 | public static BukkitUniform getInstance(@NotNull JavaPlugin plugin) { 77 | return INSTANCE != null ? INSTANCE : (INSTANCE = new BukkitUniform(plugin)); 78 | } 79 | 80 | /** 81 | * Register a command with the server's command manager 82 | * 83 | * @param commands The commands to register 84 | * @param The command source type 85 | * @param The command type 86 | * @since 1.0 87 | */ 88 | @SafeVarargs 89 | @Override 90 | public final > void register(T... commands) { 91 | registrar.getServerCommandMap().registerAll( 92 | PLUGIN.getName().toLowerCase(Locale.ENGLISH).replaceAll("[^a-z0-9_]", ""), 93 | Arrays.stream(commands).map(c -> (BukkitCommand) c) 94 | .map(c -> (org.bukkit.command.Command) c.getImpl(this)).toList() 95 | ); 96 | } 97 | 98 | /** 99 | * Register command(s) to be added to the server's command map 100 | * 101 | * @param commands The commands to register 102 | * @since 1.0 103 | */ 104 | @Override 105 | public void register(@NotNull Command... commands) { 106 | register(Arrays.stream(commands).map(BukkitCommand::new).toArray(BukkitCommand[]::new)); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /velocity/src/main/java/net/william278/uniform/velocity/VelocityUniform.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.velocity; 23 | 24 | import com.velocitypowered.api.command.BrigadierCommand; 25 | import com.velocitypowered.api.command.CommandManager; 26 | import com.velocitypowered.api.command.CommandSource; 27 | import com.velocitypowered.api.proxy.ProxyServer; 28 | import lombok.Getter; 29 | import lombok.Setter; 30 | import net.william278.uniform.BaseCommand; 31 | import net.william278.uniform.Command; 32 | import net.william278.uniform.CommandUser; 33 | import net.william278.uniform.Uniform; 34 | import org.jetbrains.annotations.NotNull; 35 | 36 | import java.util.Arrays; 37 | import java.util.function.Function; 38 | 39 | /** 40 | * A class for registering commands with the Velocity server's command manager 41 | * 42 | * @since 1.0 43 | */ 44 | @SuppressWarnings("unused") 45 | public final class VelocityUniform implements Uniform { 46 | 47 | static VelocityUniform INSTANCE; 48 | 49 | private final ProxyServer server; 50 | 51 | @Getter 52 | @Setter 53 | Function commandUserSupplier = (user) -> new VelocityCommandUser((CommandSource) user); 54 | 55 | private VelocityUniform(@NotNull ProxyServer server) { 56 | this.server = server; 57 | } 58 | 59 | /** 60 | * Get the VelocityUniform instance for registering commands 61 | * 62 | * @param server The server instance 63 | * @return The VelocityUniform instance 64 | * @since 1.0 65 | */ 66 | @NotNull 67 | public static VelocityUniform getInstance(@NotNull ProxyServer server) { 68 | return INSTANCE != null ? INSTANCE : (INSTANCE = new VelocityUniform(server)); 69 | } 70 | 71 | /** 72 | * Register a command with the server's command manager 73 | * 74 | * @param commands The commands to register 75 | * @param The command source type 76 | * @param The command type 77 | * @since 1.0 78 | */ 79 | @SafeVarargs 80 | @Override 81 | public final > void register(@NotNull T... commands) { 82 | final CommandManager commandManager = server.getCommandManager(); 83 | Arrays.stream(commands).map(c -> (VelocityCommand) c).forEach(c -> commandManager 84 | .register(commandManager.metaBuilder(c.getName()) 85 | .aliases(c.getAliases().toArray(new String[0])) 86 | .build(), new BrigadierCommand(c.build()))); 87 | } 88 | 89 | /** 90 | * Register a command with the server's command manager 91 | * 92 | * @param commands The commands to register 93 | * @since 1.0 94 | */ 95 | @Override 96 | public void register(@NotNull Command... commands) { 97 | register(Arrays.stream(commands).map(VelocityCommand::new).toArray(VelocityCommand[]::new)); 98 | } 99 | 100 | /** 101 | * Unregister a command with the Velocity's command manager 102 | * 103 | * @param commands The commands to unregister 104 | * @since 1.0 105 | */ 106 | public void unregister(@NotNull String... commands) { 107 | for (String command : commands) { 108 | server.getCommandManager().unregister(command); 109 | } 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /sponge-11/src/main/java/net/william278/uniform/sponge/SpongeUniform.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.sponge; 23 | 24 | import lombok.Getter; 25 | import lombok.Setter; 26 | import net.william278.uniform.BaseCommand; 27 | import net.william278.uniform.Command; 28 | import net.william278.uniform.CommandUser; 29 | import net.william278.uniform.Uniform; 30 | import org.jetbrains.annotations.NotNull; 31 | import org.spongepowered.api.Game; 32 | import org.spongepowered.api.command.Command.Raw; 33 | import org.spongepowered.api.command.CommandCause; 34 | import org.spongepowered.api.event.Listener; 35 | import org.spongepowered.api.event.lifecycle.RegisterCommandEvent; 36 | import org.spongepowered.plugin.PluginContainer; 37 | 38 | import java.util.Arrays; 39 | import java.util.HashSet; 40 | import java.util.Set; 41 | import java.util.function.Function; 42 | 43 | @SuppressWarnings("unused") 44 | public final class SpongeUniform implements Uniform { 45 | 46 | static SpongeUniform INSTANCE; 47 | 48 | private final Set commands = new HashSet<>(); 49 | private final PluginContainer plugin; 50 | 51 | @Getter 52 | @Setter 53 | Function commandUserSupplier = (cause) -> new SpongeCommandUser((CommandCause) cause); 54 | 55 | private SpongeUniform(@NotNull PluginContainer plugin, @NotNull Game game) { 56 | this.plugin = plugin; 57 | game.eventManager().registerListeners(plugin, this); 58 | } 59 | 60 | /** 61 | * Get the SpongeUniform instance for registering commands 62 | * 63 | * @param plugin The plugin container 64 | * @param game The game instance 65 | * @return The SpongeUniform instance 66 | * @since 1.1.10 67 | */ 68 | @NotNull 69 | public static SpongeUniform getInstance(@NotNull PluginContainer plugin, @NotNull Game game) { 70 | return INSTANCE != null ? INSTANCE : (INSTANCE = new SpongeUniform(plugin, game)); 71 | } 72 | 73 | @Listener 74 | public void onRegisterCommands(@NotNull RegisterCommandEvent event) { 75 | commands.forEach(command -> event.register( 76 | plugin, command.getImpl(), command.getName(), command.getAliases().toArray(String[]::new) 77 | )); 78 | } 79 | 80 | /** 81 | * Register a command with the server's command manager 82 | * 83 | * @param commands The commands to register 84 | * @param The command source type 85 | * @param The command type 86 | * @since 1.1.10 87 | */ 88 | @SafeVarargs 89 | @Override 90 | public final > void register(T... commands) { 91 | Arrays.stream(commands).map(c -> (SpongeCommand) c).forEach(this.commands::add); 92 | } 93 | 94 | /** 95 | * Register a command with the server's command manager 96 | * 97 | * @param commands The commands to register 98 | * @since 1.1.10 99 | */ 100 | @Override 101 | public void register(Command... commands) { 102 | register(Arrays.stream(commands).map(SpongeCommand::new).toArray(SpongeCommand[]::new)); 103 | } 104 | 105 | /** 106 | * Unregister command(s) from the server's command manager 107 | * 108 | * @param commands The commands to unregister 109 | */ 110 | public void unregister(@NotNull String... commands) { 111 | this.commands.removeIf(command -> Arrays.stream(commands).anyMatch(c -> command.getName().equals(c))); 112 | } 113 | 114 | 115 | } 116 | -------------------------------------------------------------------------------- /fabric/src/main/java/net/william278/uniform/fabric/FabricCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.fabric; 23 | 24 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 25 | import net.minecraft.block.Block; 26 | import net.minecraft.item.Item; 27 | import net.minecraft.registry.Registries; 28 | import net.minecraft.registry.Registry; 29 | import net.minecraft.server.command.ServerCommandSource; 30 | import net.minecraft.util.Identifier; 31 | import net.minecraft.util.InvalidIdentifierException; 32 | import net.william278.uniform.BaseCommand; 33 | import net.william278.uniform.Command; 34 | import net.william278.uniform.Uniform; 35 | import net.william278.uniform.element.ArgumentElement; 36 | import org.jetbrains.annotations.NotNull; 37 | 38 | import java.util.List; 39 | 40 | @SuppressWarnings("unused") 41 | public class FabricCommand extends BaseCommand { 42 | 43 | public FabricCommand(@NotNull Command command) { 44 | super(command); 45 | } 46 | 47 | public FabricCommand(@NotNull String name, @NotNull List aliases) { 48 | super(name, aliases); 49 | } 50 | 51 | public FabricCommand(@NotNull String name, @NotNull String description, 52 | @NotNull List aliases) { 53 | super(name, description, aliases); 54 | } 55 | 56 | public static FabricCommandBuilder builder(@NotNull String name) { 57 | return new FabricCommandBuilder(name); 58 | } 59 | 60 | public static ArgumentElement item(@NotNull String name) { 61 | return registry(name, Registries.ITEM); 62 | } 63 | 64 | public static ArgumentElement block(@NotNull String name) { 65 | return registry(name, Registries.BLOCK); 66 | } 67 | 68 | public static ArgumentElement registry(@NotNull String name, @NotNull Registry registry) { 69 | return new ArgumentElement<>(name, reader -> { 70 | String itemId = reader.readString(); 71 | final Identifier id; 72 | try { 73 | id = Identifier.tryParse(itemId); 74 | } catch (InvalidIdentifierException e) { 75 | throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(reader); 76 | } 77 | if (!registry.containsId(id)) { 78 | throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(reader); 79 | } 80 | return registry.get(id); 81 | }, (context, builder) -> { 82 | registry.getIds().forEach(id -> builder.suggest(id.toString())); 83 | return builder.buildFuture(); 84 | }); 85 | } 86 | 87 | @Override 88 | public void addSubCommand(@NotNull Command command) { 89 | addSubCommand(new FabricCommand(command)); 90 | } 91 | 92 | @Override 93 | public Uniform getUniform() { 94 | return FabricUniform.INSTANCE; 95 | } 96 | 97 | 98 | public static class FabricCommandBuilder extends BaseCommandBuilder { 99 | 100 | public FabricCommandBuilder(@NotNull String name) { 101 | super(name); 102 | } 103 | 104 | public final FabricCommandBuilder addSubCommand(@NotNull Command command) { 105 | subCommands.add(new FabricCommand(command)); 106 | return this; 107 | } 108 | 109 | @Override 110 | public @NotNull FabricCommand build() { 111 | var command = new FabricCommand(name, description, aliases); 112 | command.addPermissions(permissions); 113 | subCommands.forEach(command::addSubCommand); 114 | command.setDefaultExecutor(defaultExecutor); 115 | command.syntaxes.addAll(syntaxes); 116 | command.setExecutionScope(executionScope); 117 | command.setCondition(condition); 118 | return command; 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /fabric/src/main/java/net/william278/uniform/fabric/FabricUniform.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.fabric; 23 | 24 | import com.google.common.collect.Sets; 25 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 26 | import lombok.Getter; 27 | import lombok.Setter; 28 | import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; 29 | import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; 30 | import net.minecraft.server.command.ServerCommandSource; 31 | import net.william278.uniform.BaseCommand; 32 | import net.william278.uniform.Command; 33 | import net.william278.uniform.CommandUser; 34 | import net.william278.uniform.Uniform; 35 | import org.jetbrains.annotations.NotNull; 36 | 37 | import java.util.Arrays; 38 | import java.util.Locale; 39 | import java.util.Set; 40 | import java.util.function.Function; 41 | 42 | /** 43 | * A class for registering commands with the Fabric (1.21) server 44 | * 45 | * @since 1.0 46 | */ 47 | @SuppressWarnings("unused") 48 | public final class FabricUniform implements Uniform { 49 | 50 | static FabricUniform INSTANCE; 51 | 52 | private final Set commands = Sets.newHashSet(); 53 | @Getter 54 | private final String modId; 55 | 56 | @Getter 57 | @Setter 58 | Function commandUserSupplier = (user) -> new FabricCommandUser((ServerCommandSource) user); 59 | 60 | private FabricUniform(@NotNull String modId) { 61 | this.modId = modId.toLowerCase(Locale.ENGLISH).replaceAll("[^a-z0-9_-]", ""); 62 | CommandRegistrationCallback.EVENT.register((dispatcher, registry, environment) -> 63 | commands.forEach(command -> { 64 | final LiteralArgumentBuilder builder = command.createBuilder(); 65 | dispatcher.register(builder); 66 | 67 | final Set aliases = Sets.newHashSet(command.getAliases()); 68 | command.getAliases().forEach(a -> aliases.add(modId + ":" + a)); 69 | aliases.add(modId + ":" + command.getName()); 70 | aliases.forEach(alias -> dispatcher.register( 71 | LiteralArgumentBuilder.literal(alias) 72 | .requires(builder.getRequirement()).executes(builder.getCommand()) 73 | .redirect(builder.build()) 74 | )); 75 | }) 76 | ); 77 | 78 | ServerLifecycleEvents.SERVER_STOPPED.register(server -> shutdown()); 79 | } 80 | 81 | /** 82 | * Get the FabricUniform instance for registering commands 83 | * 84 | * @return The FabricUniform instance 85 | * @since 1.0 86 | */ 87 | @NotNull 88 | public static FabricUniform getInstance(@NotNull String modId) { 89 | return INSTANCE != null ? INSTANCE : (INSTANCE = new FabricUniform(modId)); 90 | } 91 | 92 | /** 93 | * Register a command with the server's command manager 94 | * 95 | * @param commands The commands to register 96 | * @param The command source type 97 | * @param The command type 98 | * @since 1.0 99 | */ 100 | @SafeVarargs 101 | @Override 102 | public final > void register(@NotNull T... commands) { 103 | Arrays.stream(commands).map(c -> (FabricCommand) c).forEach(this.commands::add); 104 | } 105 | 106 | /** 107 | * Register a command with the server's command manager 108 | * 109 | * @param commands The commands to register 110 | * @since 1.0 111 | */ 112 | @Override 113 | public void register(@NotNull Command... commands) { 114 | register(Arrays.stream(commands).map(FabricCommand::new).toArray(FabricCommand[]::new)); 115 | } 116 | 117 | /** 118 | * Unregister command(s) from the server's command manager 119 | * 120 | * @param commands The commands to unregister 121 | */ 122 | public void unregister(@NotNull String... commands) { 123 | this.commands.removeIf(command -> Arrays.stream(commands).anyMatch(c -> command.getName().equals(c))); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /bungee/src/main/java/net/william278/uniform/bungee/BungeeUniform.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.bungee; 23 | 24 | import lombok.Getter; 25 | import lombok.Setter; 26 | import net.kyori.adventure.platform.bungeecord.BungeeAudiences; 27 | import net.md_5.bungee.api.CommandSender; 28 | import net.md_5.bungee.api.plugin.Command; 29 | import net.md_5.bungee.api.plugin.Plugin; 30 | import net.william278.uniform.BaseCommand; 31 | import net.william278.uniform.CommandUser; 32 | import net.william278.uniform.Uniform; 33 | import org.jetbrains.annotations.NotNull; 34 | 35 | import java.lang.reflect.Field; 36 | import java.util.Arrays; 37 | import java.util.Map; 38 | import java.util.function.Function; 39 | 40 | /** 41 | * A class for registering commands with the Bungee server's command manager 42 | * 43 | * @since 1.3.3 44 | */ 45 | @SuppressWarnings("unused") 46 | public final class BungeeUniform implements Uniform { 47 | private static Plugin PLUGIN; 48 | static BungeeUniform INSTANCE; 49 | private static BungeeAudiences AUDIENCES; 50 | 51 | 52 | @Getter 53 | @Setter 54 | Function commandUserSupplier = (user) -> new BungeeCommandUser((CommandSender) user); 55 | 56 | private BungeeUniform(@NotNull Plugin plugin) { 57 | PLUGIN = plugin; 58 | } 59 | 60 | static BungeeAudiences getAudiences() { 61 | return AUDIENCES != null ? AUDIENCES : (AUDIENCES = BungeeAudiences.create(PLUGIN)); 62 | } 63 | 64 | /** 65 | * Get the BungeeUniform instance for registering commands 66 | * 67 | * @param server The server instance 68 | * @return The BungeeUniform instance 69 | * @since 1.3.3 70 | */ 71 | @NotNull 72 | public static BungeeUniform getInstance(@NotNull Plugin server) { 73 | return INSTANCE != null ? INSTANCE : (INSTANCE = new BungeeUniform(server)); 74 | } 75 | 76 | /** 77 | * Register a command with the server's command manager 78 | * 79 | * @param commands The commands to register 80 | * @param The command source type 81 | * @param The command type 82 | * @since 1.3.3 83 | */ 84 | @SafeVarargs 85 | @Override 86 | public final > void register(@NotNull T... commands) { 87 | getCommandMap().putAll(Arrays.stream(commands) 88 | .map(command -> ((BungeeCommand) command)) 89 | .collect(java.util.stream.Collectors.toMap( 90 | BaseCommand::getName, 91 | c -> (Command) c.getImpl(this) 92 | ))); 93 | } 94 | 95 | /** 96 | * Register a command with the server's command manager 97 | * 98 | * @param commands The commands to register 99 | * @since 1.3.3 100 | */ 101 | @Override 102 | public void register(@NotNull net.william278.uniform.Command... commands) { 103 | register(Arrays.stream(commands).map(BungeeCommand::new).toArray(BungeeCommand[]::new)); 104 | } 105 | 106 | /** 107 | * Get the command map from the server's command manager 108 | */ 109 | private Map getCommandMap() { 110 | try { 111 | return getFieldValue(PLUGIN.getProxy().getPluginManager().getClass(), PLUGIN.getProxy().getPluginManager(), 112 | "commandMap", Map.class); 113 | } catch (FeatureFailedException ex) { 114 | throw new FeatureFailedException("Unable to access command map", ex); 115 | } 116 | } 117 | 118 | /** 119 | * Access the private commandMap from PluginManager using reflection 120 | * 121 | * @return The command map or null if access failed 122 | */ 123 | F getFieldValue(Class clazz, T object, String fieldName, Class fieldType) { 124 | Field field; 125 | try { 126 | field = clazz.getDeclaredField(fieldName); 127 | } catch (NoSuchFieldException | SecurityException ex) { 128 | throw new FeatureFailedException("Unable to find field " + fieldName, ex); 129 | } 130 | Object fieldValue; 131 | try { 132 | field.setAccessible(true); 133 | fieldValue = field.get(object); 134 | } catch (SecurityException | IllegalArgumentException | IllegalAccessException ex) { 135 | throw new FeatureFailedException("Unable to access field " + fieldName, ex); 136 | } 137 | if (!fieldType.isInstance(fieldValue)) { 138 | throw new FeatureFailedException( 139 | "Field " + fieldName + " not an instance of " + fieldType.getName()); 140 | } 141 | return fieldType.cast(fieldValue); 142 | } 143 | 144 | public static class FeatureFailedException extends RuntimeException { 145 | public FeatureFailedException(String message, Throwable cause) { 146 | super(message, cause); 147 | } 148 | 149 | public FeatureFailedException(String message) { 150 | super(message); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /sponge-11/src/main/java/net/william278/uniform/sponge/SpongeCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.sponge; 23 | 24 | import com.mojang.brigadier.CommandDispatcher; 25 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 26 | import net.kyori.adventure.text.Component; 27 | import net.kyori.adventure.text.format.NamedTextColor; 28 | import net.william278.uniform.BaseCommand; 29 | import net.william278.uniform.Command; 30 | import net.william278.uniform.Permission; 31 | import net.william278.uniform.Uniform; 32 | import org.jetbrains.annotations.NotNull; 33 | import org.jetbrains.annotations.Nullable; 34 | import org.spongepowered.api.command.Command.Raw; 35 | import org.spongepowered.api.command.CommandCause; 36 | import org.spongepowered.api.command.CommandCompletion; 37 | import org.spongepowered.api.command.CommandResult; 38 | import org.spongepowered.api.command.exception.CommandException; 39 | import org.spongepowered.api.command.parameter.ArgumentReader; 40 | 41 | import java.util.List; 42 | import java.util.Optional; 43 | import java.util.stream.Collectors; 44 | 45 | @SuppressWarnings("unused") 46 | public class SpongeCommand extends BaseCommand { 47 | 48 | private @Nullable Permission permission; 49 | 50 | public SpongeCommand(@NotNull Command command) { 51 | super(command); 52 | this.permission = command.getPermission().orElse(null); 53 | } 54 | 55 | public SpongeCommand(@NotNull String name, @NotNull String description, @NotNull List aliases) { 56 | super(name, description, aliases); 57 | } 58 | 59 | public SpongeCommand(@NotNull String name, @NotNull List aliases) { 60 | super(name, aliases); 61 | } 62 | 63 | @NotNull 64 | Impl getImpl() { 65 | return new Impl(this); 66 | } 67 | 68 | static final class Impl implements Raw { 69 | 70 | private static final int COMMAND_SUCCESS = com.mojang.brigadier.Command.SINGLE_SUCCESS; 71 | private final CommandDispatcher dispatcher = new CommandDispatcher<>(); 72 | private final SpongeCommand command; 73 | private final @Nullable Permission permission; 74 | 75 | public Impl(@NotNull SpongeCommand command) { 76 | this.dispatcher.register(command.createBuilder()); 77 | this.command = command; 78 | this.permission = command.permission; 79 | } 80 | 81 | @Override 82 | public CommandResult process(CommandCause cause, ArgumentReader.Mutable arguments) throws CommandException { 83 | try { 84 | return dispatcher.execute(arguments.immutable().remaining(), cause) == COMMAND_SUCCESS 85 | ? CommandResult.success() 86 | : CommandResult.error(Component.translatable("command.failed", NamedTextColor.RED)); 87 | } catch (CommandSyntaxException e) { 88 | throw new CommandException(Component 89 | .translatable("command.context.parse_error", NamedTextColor.RED).arguments( 90 | Component.text(e.getRawMessage().getString()), 91 | Component.text(e.getCursor()), 92 | Component.text(e.getContext()) 93 | ), e, true); 94 | } 95 | } 96 | 97 | @Override 98 | public List complete(CommandCause cause, ArgumentReader.Mutable arguments) throws CommandException { 99 | return dispatcher.getCompletionSuggestions( 100 | dispatcher.parse(arguments.remaining(), cause), 101 | arguments.cursor() 102 | ) 103 | .thenApply(suggestions -> suggestions.getList().stream().map(s -> CommandCompletion.of( 104 | s.getText(), Component.text(s.getTooltip().getString()) 105 | )).toList()) 106 | .join(); 107 | } 108 | 109 | @Override 110 | public boolean canExecute(CommandCause cause) { 111 | if (permission == null) { 112 | return true; 113 | } 114 | return new SpongeCommandUser(cause).checkPermission(permission); 115 | } 116 | 117 | @Override 118 | public Optional shortDescription(CommandCause cause) { 119 | return Optional.of(Component.text(command.getDescription())); 120 | } 121 | 122 | @Override 123 | public Optional extendedDescription(CommandCause cause) { 124 | return Optional.of(Component.text(command.getDescription())); 125 | } 126 | 127 | @Override 128 | public Component usage(CommandCause cause) { 129 | return Component.text(dispatcher.getSmartUsage(dispatcher.getRoot(), cause) 130 | .values().stream().map("/%s"::formatted).collect(Collectors.joining("\n"))); 131 | } 132 | 133 | } 134 | 135 | @Override 136 | public void addSubCommand(@NotNull Command command) { 137 | addSubCommand(new SpongeCommand(command)); 138 | } 139 | 140 | @Override 141 | public Uniform getUniform() { 142 | return SpongeUniform.INSTANCE; 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /velocity/src/main/java/net/william278/uniform/velocity/VelocityCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.velocity; 23 | 24 | import com.mojang.brigadier.arguments.ArgumentType; 25 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 26 | import com.mojang.brigadier.suggestion.SuggestionProvider; 27 | import com.velocitypowered.api.command.CommandSource; 28 | import com.velocitypowered.api.proxy.Player; 29 | import com.velocitypowered.api.proxy.ProxyServer; 30 | import com.velocitypowered.api.proxy.server.RegisteredServer; 31 | import net.william278.uniform.BaseCommand; 32 | import net.william278.uniform.Command; 33 | import net.william278.uniform.Uniform; 34 | import net.william278.uniform.element.ArgumentElement; 35 | import org.jetbrains.annotations.NotNull; 36 | 37 | import java.util.List; 38 | 39 | @SuppressWarnings("unused") 40 | public class VelocityCommand extends BaseCommand { 41 | 42 | public VelocityCommand(@NotNull Command command) { 43 | super(command); 44 | } 45 | 46 | public VelocityCommand(@NotNull String name, @NotNull List aliases) { 47 | super(name, aliases); 48 | } 49 | 50 | public VelocityCommand(@NotNull String name, @NotNull String description, 51 | @NotNull List aliases) { 52 | super(name, description, aliases); 53 | } 54 | 55 | public static VelocityCommandBuilder builder(@NotNull String name) { 56 | return new VelocityCommandBuilder(name); 57 | } 58 | 59 | public static ArgumentElement server(@NotNull ProxyServer server, @NotNull String name, 60 | @NotNull SuggestionProvider suggestionProvider) { 61 | ArgumentType argumentType = reader -> { 62 | String s = reader.readUnquotedString(); 63 | RegisteredServer server1 = server.getServer(s).orElse(null); 64 | if (server1 == null) { 65 | throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(reader); 66 | } 67 | return server1; 68 | }; 69 | return new ArgumentElement<>(name, argumentType, suggestionProvider); 70 | } 71 | 72 | public static ArgumentElement server(@NotNull ProxyServer server, @NotNull String name) { 73 | return server(server, name, (context, builder) -> { 74 | for (RegisteredServer server1 : server.getAllServers()) { 75 | builder.suggest(server1.getServerInfo().getName()); 76 | } 77 | return builder.buildFuture(); 78 | }); 79 | } 80 | 81 | public static ArgumentElement source(@NotNull ProxyServer server, @NotNull String name, 82 | @NotNull SuggestionProvider suggestionProvider) { 83 | ArgumentType argumentType = reader -> { 84 | String s = reader.readUnquotedString(); 85 | CommandSource source = server.getPlayer(s).orElse(null); 86 | if (source == null) { 87 | throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(reader); 88 | } 89 | return source; 90 | }; 91 | return new ArgumentElement<>(name, argumentType, suggestionProvider); 92 | } 93 | 94 | public static ArgumentElement source(@NotNull ProxyServer server, @NotNull String name) { 95 | return source(server, name, (context, builder) -> { 96 | for (Player source : server.getAllPlayers()) { 97 | builder.suggest(source.getUsername()); 98 | } 99 | return builder.buildFuture(); 100 | }); 101 | } 102 | 103 | @Override 104 | public void addSubCommand(@NotNull Command command) { 105 | addSubCommand(new VelocityCommand(command)); 106 | } 107 | 108 | @Override 109 | public Uniform getUniform() { 110 | return VelocityUniform.INSTANCE; 111 | } 112 | 113 | public static class VelocityCommandBuilder extends BaseCommandBuilder { 114 | 115 | public VelocityCommandBuilder(@NotNull String name) { 116 | super(name); 117 | } 118 | 119 | public final VelocityCommand.VelocityCommandBuilder addSubCommand(@NotNull Command command) { 120 | subCommands.add(new VelocityCommand(command)); 121 | return this; 122 | } 123 | 124 | @Override 125 | public @NotNull VelocityCommand build() { 126 | var command = new VelocityCommand(name, description, aliases); 127 | command.addPermissions(permissions); 128 | subCommands.forEach(command::addSubCommand); 129 | command.setDefaultExecutor(defaultExecutor); 130 | command.syntaxes.addAll(syntaxes); 131 | command.setExecutionScope(executionScope); 132 | command.setCondition(condition); 133 | 134 | return command; 135 | } 136 | 137 | public VelocityCommand register(@NotNull ProxyServer proxyServer) { 138 | final VelocityCommand builtCmd = build(); 139 | VelocityUniform.getInstance(proxyServer).register(builtCmd); 140 | return builtCmd; 141 | } 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/uniform/annotations/Argument.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.annotations; 23 | 24 | import lombok.AllArgsConstructor; 25 | import lombok.NoArgsConstructor; 26 | import net.william278.uniform.BaseCommand; 27 | import net.william278.uniform.element.ArgumentElement; 28 | import org.jetbrains.annotations.NotNull; 29 | 30 | import java.lang.annotation.ElementType; 31 | import java.lang.annotation.Retention; 32 | import java.lang.annotation.RetentionPolicy; 33 | import java.lang.annotation.Target; 34 | 35 | @Target({ElementType.ANNOTATION_TYPE, ElementType.PARAMETER}) 36 | @Retention(RetentionPolicy.RUNTIME) 37 | public @interface Argument { 38 | 39 | String name() default ""; 40 | Class> parser(); 41 | String[] parserProperties() default {}; 42 | 43 | @NoArgsConstructor 44 | abstract class ArgumentProvider { 45 | 46 | public abstract ArgumentElement provide(@NotNull String name); 47 | 48 | } 49 | 50 | class StringArg extends ArgumentProvider { 51 | @Override 52 | public ArgumentElement provide(@NotNull String name) { 53 | return BaseCommand.string(name); 54 | } 55 | } 56 | 57 | class WordArg extends ArgumentProvider { 58 | @Override 59 | public ArgumentElement provide(@NotNull String name) { 60 | return BaseCommand.word(name); 61 | } 62 | } 63 | 64 | class BooleanArg extends ArgumentProvider { 65 | @Override 66 | public ArgumentElement provide(@NotNull String name) { 67 | return BaseCommand.bool(name); 68 | } 69 | } 70 | 71 | class GreedyStringArg extends ArgumentProvider { 72 | @Override 73 | public ArgumentElement provide(@NotNull String name) { 74 | return BaseCommand.greedyString(name); 75 | } 76 | } 77 | 78 | class IntegerArg extends ArgumentProvider { 79 | @Override 80 | public ArgumentElement provide(@NotNull String name) { 81 | return BaseCommand.intNum(name); 82 | } 83 | } 84 | 85 | @AllArgsConstructor 86 | class BoundedIntegerArg extends ArgumentProvider { 87 | private final int min; 88 | private final Integer max; 89 | 90 | public BoundedIntegerArg(@NotNull String[] properties) { 91 | if (properties.length == 0) { 92 | throw new IllegalArgumentException("BoundedIntegerArg requires at least one property (min, max)"); 93 | } 94 | this.min = Integer.parseInt(properties[0]); 95 | if (properties.length == 1) { 96 | this.max = null; 97 | return; 98 | } 99 | this.max = Integer.parseInt(properties[1]); 100 | } 101 | 102 | @Override 103 | public ArgumentElement provide(@NotNull String name) { 104 | if (max == null) { 105 | return BaseCommand.intNum(name, min); 106 | } 107 | return BaseCommand.intNum(name, min, max); 108 | } 109 | } 110 | 111 | class FloatArg extends ArgumentProvider { 112 | @Override 113 | public ArgumentElement provide(@NotNull String name) { 114 | return BaseCommand.floatNum(name); 115 | } 116 | } 117 | 118 | @AllArgsConstructor 119 | class BoundedFloatArg extends ArgumentProvider { 120 | private final float min; 121 | private final Float max; 122 | 123 | public BoundedFloatArg(@NotNull String[] properties) { 124 | if (properties.length == 0) { 125 | throw new IllegalArgumentException("BoundedFloatArg requires at least one property (min, max)"); 126 | } 127 | this.min = Float.parseFloat(properties[0]); 128 | if (properties.length == 1) { 129 | this.max = null; 130 | return; 131 | } 132 | this.max = Float.parseFloat(properties[1]); 133 | } 134 | 135 | @Override 136 | public ArgumentElement provide(@NotNull String name) { 137 | if (max == null) { 138 | return BaseCommand.floatNum(name, min); 139 | } 140 | return BaseCommand.floatNum(name, min, max); 141 | } 142 | } 143 | 144 | class DoubleArg extends ArgumentProvider { 145 | @Override 146 | public ArgumentElement provide(@NotNull String name) { 147 | return BaseCommand.doubleNum(name); 148 | } 149 | } 150 | 151 | @AllArgsConstructor 152 | class BoundedDoubleArg extends ArgumentProvider { 153 | private final double min; 154 | private final Double max; 155 | 156 | public BoundedDoubleArg(@NotNull String[] properties) { 157 | if (properties.length == 0) { 158 | throw new IllegalArgumentException("BoundedDoubleArg requires at least one property (min, max)"); 159 | } 160 | this.min = Double.parseDouble(properties[0]); 161 | if (properties.length == 1) { 162 | this.max = null; 163 | return; 164 | } 165 | this.max = Double.parseDouble(properties[1]); 166 | } 167 | 168 | @Override 169 | public ArgumentElement provide(@NotNull String name) { 170 | if (max == null) { 171 | return BaseCommand.doubleNum(name, min); 172 | } 173 | return BaseCommand.doubleNum(name, min, max); 174 | } 175 | } 176 | 177 | } 178 | -------------------------------------------------------------------------------- /paper/src/main/java/net/william278/uniform/paper/PaperUniform.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.paper; 23 | 24 | import com.google.common.collect.Sets; 25 | import lombok.Getter; 26 | import lombok.Setter; 27 | import net.william278.uniform.BaseCommand; 28 | import net.william278.uniform.Command; 29 | import net.william278.uniform.CommandUser; 30 | import net.william278.uniform.Uniform; 31 | import org.bukkit.plugin.java.JavaPlugin; 32 | import org.jetbrains.annotations.NotNull; 33 | 34 | import java.util.Arrays; 35 | import java.util.List; 36 | import java.util.Locale; 37 | import java.util.Set; 38 | import java.util.function.Function; 39 | import java.util.stream.Stream; 40 | 41 | /** 42 | * A class for registering commands with the Paper server's command manager 43 | * 44 | * @since 1.0 45 | */ 46 | public final class PaperUniform implements Uniform { 47 | 48 | static PaperUniform INSTANCE; 49 | 50 | private final Set commands = Sets.newHashSet(); 51 | private final boolean useModernApi; 52 | private final JavaPlugin plugin; 53 | 54 | @Getter 55 | @Setter 56 | Function commandUserSupplier; 57 | 58 | private PaperUniform(@NotNull JavaPlugin plugin) { 59 | this(plugin, isUseModernApi()); 60 | } 61 | 62 | private PaperUniform(@NotNull JavaPlugin plugin, boolean useModernApi) { 63 | this.plugin = plugin; 64 | this.useModernApi = useModernApi; 65 | // Modern (1.20.6+) Lifecycle event based Paper Brigadier API 66 | if (this.useModernApi) { 67 | this.commandUserSupplier = PaperCommand.USER_SUPPLIER; 68 | PaperCommand.register(plugin, commands); 69 | return; 70 | } 71 | this.commandUserSupplier = LegacyPaperCommand.USER_SUPPLIER; 72 | } 73 | 74 | /** 75 | * Get the PaperUniform instance for registering commands 76 | * 77 | * @param plugin The plugin instance 78 | * @return The PaperUniform instance 79 | * @since 1.0 80 | */ 81 | @NotNull 82 | public static PaperUniform getInstance(@NotNull JavaPlugin plugin) { 83 | return INSTANCE != null ? INSTANCE : (INSTANCE = new PaperUniform(plugin)); 84 | } 85 | 86 | /** 87 | * Get the PaperUniform instance for registering commands 88 | * 89 | * @param plugin The plugin instance 90 | * @param useModernApi Force the use of the modern Paper API or not 91 | * @return The PaperUniform instance 92 | * @since 1.0 93 | */ 94 | @NotNull 95 | public static PaperUniform getInstance(@NotNull JavaPlugin plugin, boolean useModernApi) { 96 | return INSTANCE != null && INSTANCE.useModernApi == useModernApi ? INSTANCE : (INSTANCE = new PaperUniform(plugin, useModernApi)); 97 | } 98 | 99 | // Check if the modern Paper API is available 100 | private static boolean isUseModernApi() { 101 | try { 102 | Class.forName("io.papermc.paper.command.brigadier.CommandSourceStack"); 103 | return true; 104 | } catch (ClassNotFoundException e) { 105 | return false; 106 | } 107 | } 108 | 109 | /** 110 | * Register a command with the server's command manager 111 | * 112 | * @param commands The commands to register 113 | * @param The command source type 114 | * @param The command type 115 | * @since 1.0 116 | */ 117 | @SafeVarargs 118 | @Override 119 | public final > void register(@NotNull T... commands) { 120 | // Mark as to be registered with modern API 121 | final Stream s = Arrays.stream(commands); 122 | if (useModernApi) { 123 | s.forEach(c -> this.commands.add((PaperCommand) c)); 124 | return; 125 | } 126 | 127 | // Register with the legacy API 128 | plugin.getServer().getCommandMap().registerAll( 129 | plugin.getName().toLowerCase(Locale.ENGLISH).replaceAll("[^a-z0-9_]", ""), 130 | s.map(c -> (LegacyPaperCommand) c).map(c -> (org.bukkit.command.Command) c.getImpl(this)).toList() 131 | ); 132 | } 133 | 134 | /** 135 | * Unregister a command with the server's command manager 136 | * 137 | * @param commands The commands to unregister 138 | * @since 1.0 139 | */ 140 | public void unregister(@NotNull String... commands) { 141 | // Mark as to be registered with modern API 142 | final List s = List.of(commands); 143 | if (useModernApi) { 144 | s.forEach(c -> this.commands.removeIf(cmd -> cmd.getName().equals(c))); 145 | return; 146 | } 147 | 148 | // Unregister with the legacy API 149 | final String pluginName = plugin.getName().toLowerCase(Locale.ENGLISH).replaceAll("[^a-z0-9_]", ""); 150 | plugin.getServer().getCommandMap().getKnownCommands() 151 | .keySet() 152 | .removeIf(cmdName -> { 153 | String[] splitPluginName = cmdName.split(":"); 154 | if (splitPluginName.length > 1) { 155 | return splitPluginName[0].equalsIgnoreCase(pluginName) && s.contains(splitPluginName[1]); 156 | } 157 | return cmdName.equalsIgnoreCase(plugin.getName()) && s.contains(cmdName); 158 | }); 159 | } 160 | 161 | /** 162 | * Register command(s) to be added to the server's command manager 163 | * 164 | * @param commands The commands to register 165 | * @since 1.0 166 | */ 167 | public void register(@NotNull Command... commands) { 168 | if (useModernApi) { 169 | register(Arrays.stream(commands).map(PaperCommand::new).toArray(PaperCommand[]::new)); 170 | return; 171 | } 172 | register(Arrays.stream(commands).map(LegacyPaperCommand::new).toArray(LegacyPaperCommand[]::new)); 173 | } 174 | 175 | /** 176 | * Unregister command(s) to be added to the server's command manager 177 | * 178 | * @param commands The commands to register 179 | * @since 1.0 180 | */ 181 | public void unregister(@NotNull Command... commands) { 182 | unregister(Arrays.stream(commands).map(Command::getName).toArray(String[]::new)); 183 | } 184 | 185 | } 186 | -------------------------------------------------------------------------------- /bukkit/src/main/java/net/william278/uniform/bukkit/BukkitCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.bukkit; 23 | 24 | import com.mojang.brigadier.CommandDispatcher; 25 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 26 | import com.mojang.brigadier.suggestion.Suggestion; 27 | import net.kyori.adventure.audience.Audience; 28 | import net.kyori.adventure.platform.bukkit.BukkitAudiences; 29 | import net.kyori.adventure.text.Component; 30 | import net.kyori.adventure.text.format.NamedTextColor; 31 | import net.william278.uniform.BaseCommand; 32 | import net.william278.uniform.Command; 33 | import net.william278.uniform.Permission; 34 | import net.william278.uniform.Uniform; 35 | import org.bukkit.Bukkit; 36 | import org.bukkit.command.CommandException; 37 | import org.bukkit.command.CommandSender; 38 | import org.bukkit.plugin.java.JavaPlugin; 39 | import org.jetbrains.annotations.NotNull; 40 | import org.jetbrains.annotations.Nullable; 41 | 42 | import java.util.List; 43 | import java.util.stream.Collectors; 44 | 45 | @SuppressWarnings("unused") 46 | public class BukkitCommand extends BaseCommand { 47 | 48 | private BukkitAudiences audiences; 49 | private @Nullable Permission permission; 50 | 51 | public BukkitCommand(@NotNull Command command) { 52 | super(command); 53 | this.permission = command.getPermission().orElse(null); 54 | } 55 | 56 | public BukkitCommand(@NotNull String name, @NotNull String description, @NotNull List aliases) { 57 | super(name, description, aliases); 58 | } 59 | 60 | public BukkitCommand(@NotNull String name, @NotNull List aliases) { 61 | super(name, aliases); 62 | } 63 | 64 | public static BukkitCommandBuilder builder(@NotNull String name) { 65 | return new BukkitCommandBuilder(name); 66 | } 67 | 68 | @NotNull 69 | Impl getImpl(@NotNull Uniform uniform) { 70 | return new Impl(uniform, this); 71 | } 72 | 73 | static final class Impl extends org.bukkit.command.Command { 74 | 75 | private static final int COMMAND_SUCCESS = com.mojang.brigadier.Command.SINGLE_SUCCESS; 76 | 77 | private final CommandDispatcher dispatcher = new CommandDispatcher<>(); 78 | private final @Nullable Permission permission; 79 | 80 | public Impl(@NotNull Uniform uniform, @NotNull BukkitCommand command) { 81 | super(command.getName()); 82 | this.dispatcher.register(command.createBuilder()); 83 | this.permission = command.permission; 84 | 85 | // Setup command properties 86 | this.setDescription(command.getDescription()); 87 | this.setAliases(command.getAliases()); 88 | this.setUsage(getUsageText()); 89 | if (permission != null) { 90 | this.setPermission(permission.node()); 91 | } 92 | } 93 | 94 | @NotNull 95 | private String getUsageText() { 96 | return dispatcher.getSmartUsage(dispatcher.getRoot(), Bukkit.getConsoleSender()).values().stream() 97 | .map("/%s"::formatted).collect(Collectors.joining("\n")); 98 | } 99 | 100 | @SuppressWarnings("deprecation") 101 | @Override 102 | public boolean execute(@NotNull CommandSender commandSender, @NotNull String alias, @NotNull String[] args) { 103 | try { 104 | return dispatcher.execute(getInput(args), commandSender) == COMMAND_SUCCESS; 105 | } catch (CommandSyntaxException e) { 106 | getAudience(commandSender).sendMessage(Component 107 | .translatable("command.context.parse_error", NamedTextColor.RED) 108 | .args( 109 | Component.text(e.getRawMessage().getString()), 110 | Component.text(e.getCursor()), 111 | Component.text(e.getContext()) 112 | )); 113 | return false; 114 | } catch (CommandException e) { 115 | getAudience(commandSender).sendMessage(Component.text(e.getMessage(), NamedTextColor.RED)); 116 | return false; 117 | } 118 | } 119 | 120 | @NotNull 121 | @Override 122 | public List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) 123 | throws IllegalArgumentException { 124 | final String passed = getInput(args); 125 | return dispatcher.getCompletionSuggestions( 126 | dispatcher.parse(passed, sender), 127 | passed.length() // Spigot API limitation - we can only TAB complete the full text length :( 128 | ) 129 | .thenApply(suggestions -> suggestions.getList().stream().map(Suggestion::getText).toList()) 130 | .join(); 131 | } 132 | 133 | @Override 134 | public boolean testPermissionSilent(@NotNull CommandSender target) { 135 | if (permission == null || permission.node().isBlank()) { 136 | return true; 137 | } 138 | return new BukkitCommandUser(target).checkPermission(permission); 139 | } 140 | 141 | @NotNull 142 | private Audience getAudience(@NotNull CommandSender sender) { 143 | return BukkitUniform.getAudiences().sender(sender); 144 | } 145 | 146 | @NotNull 147 | private String getInput(@NotNull String[] args) { 148 | return args.length == 0 ? getName() : "%s %s".formatted(getName(), String.join(" ", args)); 149 | } 150 | } 151 | 152 | @Override 153 | public void addSubCommand(@NotNull Command command) { 154 | addSubCommand(new BukkitCommand(command)); 155 | } 156 | 157 | @Override 158 | public Uniform getUniform() { 159 | return BukkitUniform.INSTANCE; 160 | } 161 | 162 | public static class BukkitCommandBuilder extends BaseCommandBuilder { 163 | 164 | public BukkitCommandBuilder(@NotNull String name) { 165 | super(name); 166 | } 167 | 168 | public final BukkitCommand.BukkitCommandBuilder addSubCommand(@NotNull Command command) { 169 | subCommands.add(new BukkitCommand(command)); 170 | return this; 171 | } 172 | 173 | @Override 174 | public @NotNull BukkitCommand build() { 175 | var command = new BukkitCommand(name, description, aliases); 176 | command.addPermissions(permissions); 177 | subCommands.forEach(command::addSubCommand); 178 | command.setDefaultExecutor(defaultExecutor); 179 | command.syntaxes.addAll(syntaxes); 180 | command.setExecutionScope(executionScope); 181 | command.setCondition(condition); 182 | 183 | return command; 184 | } 185 | 186 | public BukkitCommand register(@NotNull JavaPlugin plugin) { 187 | final BukkitCommand builtCmd = build(); 188 | BukkitUniform.getInstance(plugin).register(builtCmd); 189 | return builtCmd; 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /paper/src/main/java/net/william278/uniform/paper/LegacyPaperCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.paper; 23 | 24 | import com.mojang.brigadier.CommandDispatcher; 25 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 26 | import com.mojang.brigadier.suggestion.Suggestion; 27 | import net.kyori.adventure.text.Component; 28 | import net.kyori.adventure.text.format.NamedTextColor; 29 | import net.william278.uniform.*; 30 | import org.bukkit.Bukkit; 31 | import org.bukkit.command.CommandException; 32 | import org.bukkit.command.CommandSender; 33 | import org.bukkit.plugin.java.JavaPlugin; 34 | import org.jetbrains.annotations.NotNull; 35 | import org.jetbrains.annotations.Nullable; 36 | 37 | import java.util.List; 38 | import java.util.function.Function; 39 | import java.util.stream.Collectors; 40 | 41 | @SuppressWarnings("unused") 42 | public class LegacyPaperCommand extends BaseCommand { 43 | 44 | private @Nullable Permission permission; 45 | 46 | static final Function USER_SUPPLIER = (user) -> new LegacyPaperCommandUser( 47 | (CommandSender) user 48 | ); 49 | 50 | public LegacyPaperCommand(@NotNull Command command) { 51 | super(command); 52 | this.permission = command.getPermission().orElse(null); 53 | } 54 | 55 | public LegacyPaperCommand(@NotNull String name, @NotNull String description, @NotNull List aliases) { 56 | super(name, description, aliases); 57 | } 58 | 59 | public LegacyPaperCommand(@NotNull String name, @NotNull List aliases) { 60 | super(name, aliases); 61 | } 62 | 63 | public static LegacyPaperCommandBuilder builder(@NotNull String name) { 64 | return new LegacyPaperCommandBuilder(name); 65 | } 66 | 67 | @NotNull 68 | Impl getImpl(@NotNull Uniform uniform) { 69 | return new Impl(uniform, this); 70 | } 71 | 72 | @Override 73 | public final void setPermission(@NotNull Permission permission) { 74 | this.permission = permission; 75 | super.addPermissions(permission); 76 | } 77 | 78 | static final class Impl extends org.bukkit.command.Command { 79 | 80 | private static final int COMMAND_SUCCESS = com.mojang.brigadier.Command.SINGLE_SUCCESS; 81 | 82 | private final CommandDispatcher dispatcher = new CommandDispatcher<>(); 83 | private final @Nullable Permission permission; 84 | 85 | public Impl(@NotNull Uniform uniform, @NotNull LegacyPaperCommand command) { 86 | super(command.getName()); 87 | this.dispatcher.register(command.createBuilder()); 88 | this.permission = command.permission; 89 | 90 | // Setup command properties 91 | this.setDescription(command.getDescription()); 92 | this.setAliases(command.getAliases()); 93 | this.setUsage(getUsageText()); 94 | if (permission != null) { 95 | this.setPermission(permission.node()); 96 | } 97 | } 98 | 99 | @NotNull 100 | private String getUsageText() { 101 | return dispatcher.getSmartUsage(dispatcher.getRoot(), Bukkit.getConsoleSender()).values().stream() 102 | .map("/%s"::formatted).collect(Collectors.joining("\n")); 103 | } 104 | 105 | @SuppressWarnings("deprecation") 106 | @Override 107 | public boolean execute(@NotNull CommandSender commandSender, @NotNull String alias, @NotNull String[] args) { 108 | try { 109 | return dispatcher.execute(getInput(args), commandSender) == COMMAND_SUCCESS; 110 | } catch (CommandSyntaxException e) { 111 | commandSender.sendMessage(Component 112 | .translatable("command.context.parse_error", NamedTextColor.RED) 113 | .args( 114 | Component.text(e.getRawMessage().getString()), 115 | Component.text(e.getCursor()), 116 | Component.text(e.getContext()) 117 | )); 118 | return false; 119 | } catch (CommandException e) { 120 | commandSender.sendMessage(Component.text(e.getMessage(), NamedTextColor.RED)); 121 | return false; 122 | } 123 | } 124 | 125 | @NotNull 126 | @Override 127 | public List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) 128 | throws IllegalArgumentException { 129 | final String passed = getInput(args); 130 | return dispatcher.getCompletionSuggestions( 131 | dispatcher.parse(passed, sender), 132 | passed.length() // Spigot API limitation - we can only TAB complete the full text length :( 133 | ) 134 | .thenApply(suggestions -> suggestions.getList().stream().map(Suggestion::getText).toList()) 135 | .join(); 136 | } 137 | 138 | @Override 139 | public boolean testPermissionSilent(@NotNull CommandSender target) { 140 | if (permission == null || permission.node().isBlank()) { 141 | return true; 142 | } 143 | return new LegacyPaperCommandUser(target).checkPermission(permission); 144 | } 145 | 146 | @NotNull 147 | private String getInput(@NotNull String[] args) { 148 | return args.length == 0 ? getName() : "%s %s".formatted(getName(), String.join(" ", args)); 149 | } 150 | } 151 | 152 | @Override 153 | public void addSubCommand(@NotNull Command command) { 154 | addSubCommand(new LegacyPaperCommand(command)); 155 | } 156 | 157 | @Override 158 | public Uniform getUniform() { 159 | return PaperUniform.INSTANCE; 160 | } 161 | 162 | public static class LegacyPaperCommandBuilder extends BaseCommandBuilder { 163 | 164 | public LegacyPaperCommandBuilder(@NotNull String name) { 165 | super(name); 166 | } 167 | 168 | public final LegacyPaperCommand.LegacyPaperCommandBuilder addSubCommand(@NotNull Command command) { 169 | subCommands.add(new LegacyPaperCommand(command)); 170 | return this; 171 | } 172 | 173 | @Override 174 | public @NotNull LegacyPaperCommand build() { 175 | var command = new LegacyPaperCommand(name, description, aliases); 176 | command.addPermissions(permissions); 177 | subCommands.forEach(command::addSubCommand); 178 | command.setDefaultExecutor(defaultExecutor); 179 | command.syntaxes.addAll(syntaxes); 180 | command.setExecutionScope(executionScope); 181 | //Condition is not yet implemented in this type of command 182 | 183 | return command; 184 | } 185 | 186 | @NotNull 187 | public LegacyPaperCommand register(@NotNull JavaPlugin plugin) { 188 | final LegacyPaperCommand builtCmd = build(); 189 | PaperUniform.getInstance(plugin).register(builtCmd); 190 | return builtCmd; 191 | } 192 | } 193 | 194 | } 195 | -------------------------------------------------------------------------------- /paper/src/main/java/net/william278/uniform/paper/PaperCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.paper; 23 | 24 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 25 | import io.papermc.paper.command.brigadier.CommandSourceStack; 26 | import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; 27 | import net.william278.uniform.*; 28 | import net.william278.uniform.element.ArgumentElement; 29 | import net.william278.uniform.element.CommandElement; 30 | import net.william278.uniform.paper.element.PaperArgumentElement; 31 | import org.bukkit.Bukkit; 32 | import org.bukkit.Material; 33 | import org.bukkit.Sound; 34 | import org.bukkit.World; 35 | import org.bukkit.entity.EntityType; 36 | import org.bukkit.entity.Player; 37 | import org.bukkit.plugin.java.JavaPlugin; 38 | import org.jetbrains.annotations.NotNull; 39 | 40 | import java.util.Arrays; 41 | import java.util.Collection; 42 | import java.util.List; 43 | import java.util.Set; 44 | import java.util.function.Function; 45 | 46 | @SuppressWarnings({"unused", "UnstableApiUsage"}) 47 | public class PaperCommand extends BaseCommand { 48 | 49 | static final Function USER_SUPPLIER = (user) -> new PaperCommandUser( 50 | (CommandSourceStack) user 51 | ); 52 | 53 | public PaperCommand(@NotNull Command command) { 54 | super(command); 55 | } 56 | 57 | public PaperCommand(@NotNull String name, @NotNull List aliases) { 58 | super(name, aliases); 59 | } 60 | 61 | public PaperCommand(@NotNull String name, @NotNull String description, @NotNull List aliases) { 62 | super(name, description, aliases); 63 | } 64 | 65 | static void register(@NotNull JavaPlugin plugin, @NotNull Set commands) { 66 | plugin.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, (event) -> { 67 | commands.forEach(command -> event.registrar().register( 68 | plugin.getPluginMeta(), 69 | command.build(), 70 | command.getDescription(), 71 | command.getAliases() 72 | )); 73 | commands.clear(); 74 | }); 75 | } 76 | 77 | public static PaperCommandBuilder builder(@NotNull String name) { 78 | return new PaperCommandBuilder(name); 79 | } 80 | 81 | public static ArgumentElement material(@NotNull String name) { 82 | return new ArgumentElement<>(name, reader -> { 83 | try { 84 | return Material.valueOf(reader.readString()); 85 | } catch (IllegalArgumentException e) { 86 | throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(reader); 87 | } 88 | }, (context, builder) -> { 89 | if (builder.getRemainingLowerCase().isEmpty()) return builder.buildFuture(); 90 | Arrays.stream(Material.values()) 91 | .filter(material -> material.name().toLowerCase().contains(builder.getRemainingLowerCase())) 92 | .forEach(material -> builder.suggest(material.name())); 93 | return builder.buildFuture(); 94 | }); 95 | } 96 | 97 | public static ArgumentElement> player(@NotNull String name) { 98 | return new ArgumentElement<>(name, reader -> { 99 | String playerName = reader.readString(); 100 | if (playerName.equals("@a")) { 101 | return Bukkit.getOnlinePlayers(); 102 | } 103 | var player = Bukkit.getPlayer(playerName); 104 | if (player == null) { 105 | throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(reader); 106 | } 107 | return List.of(player); 108 | }, (context, builder) -> { 109 | builder.suggest("@a"); 110 | for (Player player : Bukkit.getOnlinePlayers()) { 111 | builder.suggest(player.getName()); 112 | } 113 | return builder.buildFuture(); 114 | }); 115 | } 116 | 117 | public static ArgumentElement sound(@NotNull String name) { 118 | return enumArgument(name, Sound.class); 119 | } 120 | 121 | public static ArgumentElement entityType(@NotNull String name) { 122 | return enumArgument(name, EntityType.class); 123 | } 124 | 125 | public static ArgumentElement world(@NotNull String name) { 126 | return new ArgumentElement<>(name, reader -> { 127 | String worldName = reader.readString(); 128 | World world = Bukkit.getWorld(worldName); 129 | if (world == null) { 130 | throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(reader); 131 | } 132 | return world; 133 | }, (context, builder) -> { 134 | for (World world : Bukkit.getWorlds()) { 135 | builder.suggest(world.getName()); 136 | } 137 | return builder.buildFuture(); 138 | }); 139 | } 140 | 141 | @Override 142 | @NotNull 143 | @SuppressWarnings("unchecked") 144 | public List> getSyntaxes() { 145 | return super.getSyntaxes().stream().map( 146 | syntax -> new CommandSyntax<>( 147 | syntax.condition(), 148 | syntax.executor(), 149 | syntax.elements().stream() 150 | .filter(e -> e instanceof ArgumentElement) 151 | .map(e -> (ArgumentElement) e) 152 | .map(e -> e.custom() ? new ArgumentElement<>( 153 | e.name(), 154 | new PaperArgumentElement<>(e.type()), 155 | e.suggestionProvider() 156 | ) : e) 157 | .map(e -> (CommandElement) e) 158 | .toList() 159 | ) 160 | ).toList(); 161 | } 162 | 163 | @Override 164 | public void addSubCommand(@NotNull Command command) { 165 | addSubCommand(new PaperCommand(command)); 166 | } 167 | 168 | @Override 169 | public Uniform getUniform() { 170 | return PaperUniform.INSTANCE; 171 | } 172 | 173 | public static class PaperCommandBuilder extends BaseCommandBuilder { 174 | 175 | public PaperCommandBuilder(@NotNull String name) { 176 | super(name); 177 | } 178 | 179 | public final PaperCommandBuilder addSubCommand(@NotNull Command command) { 180 | subCommands.add(new PaperCommand(command)); 181 | return this; 182 | } 183 | 184 | @Override 185 | public @NotNull PaperCommand build() { 186 | final PaperCommand command = new PaperCommand(name, description, aliases); 187 | command.addPermissions(permissions); 188 | subCommands.forEach(command::addSubCommand); 189 | command.setDefaultExecutor(defaultExecutor); 190 | command.syntaxes.addAll(syntaxes); 191 | command.setExecutionScope(executionScope); 192 | command.setCondition(condition); 193 | 194 | return command; 195 | } 196 | 197 | @NotNull 198 | public PaperCommand register(@NotNull JavaPlugin plugin) { 199 | final PaperCommand builtCmd = build(); 200 | PaperCommand.register(plugin, Set.of(builtCmd)); 201 | return builtCmd; 202 | } 203 | 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /bungee/src/main/java/net/william278/uniform/bungee/BungeeCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform.bungee; 23 | 24 | import com.mojang.brigadier.CommandDispatcher; 25 | import com.mojang.brigadier.arguments.ArgumentType; 26 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 27 | import com.mojang.brigadier.suggestion.Suggestion; 28 | import com.mojang.brigadier.suggestion.SuggestionProvider; 29 | import net.kyori.adventure.audience.Audience; 30 | import net.kyori.adventure.text.Component; 31 | import net.kyori.adventure.text.format.NamedTextColor; 32 | import net.md_5.bungee.api.CommandSender; 33 | import net.md_5.bungee.api.ProxyServer; 34 | import net.md_5.bungee.api.config.ServerInfo; 35 | import net.md_5.bungee.api.connection.ProxiedPlayer; 36 | import net.md_5.bungee.api.plugin.Plugin; 37 | import net.md_5.bungee.api.plugin.TabExecutor; 38 | import net.william278.uniform.BaseCommand; 39 | import net.william278.uniform.Command; 40 | import net.william278.uniform.Permission; 41 | import net.william278.uniform.Uniform; 42 | import net.william278.uniform.element.ArgumentElement; 43 | import org.jetbrains.annotations.NotNull; 44 | import org.jetbrains.annotations.Nullable; 45 | 46 | import java.util.List; 47 | 48 | @SuppressWarnings("unused") 49 | public class BungeeCommand extends BaseCommand { 50 | 51 | private @Nullable Permission permission; 52 | 53 | public BungeeCommand(@NotNull Command command) { 54 | super(command); 55 | this.permission = command.getPermission().orElse(null); 56 | } 57 | 58 | public BungeeCommand(@NotNull String name, @NotNull List aliases) { 59 | super(name, aliases); 60 | } 61 | 62 | public BungeeCommand(@NotNull String name, @NotNull String description, 63 | @NotNull List aliases) { 64 | super(name, description, aliases); 65 | } 66 | 67 | public static BungeeCommandBuilder builder(@NotNull String name) { 68 | return new BungeeCommandBuilder(name); 69 | } 70 | 71 | public static ArgumentElement server(@NotNull ProxyServer server, @NotNull String name, 72 | @NotNull SuggestionProvider suggestionProvider) { 73 | ArgumentType argumentType = reader -> { 74 | String s = reader.readUnquotedString(); 75 | ServerInfo server1 = server.getServerInfo(s); 76 | if (server1 == null) { 77 | throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(reader); 78 | } 79 | return server1; 80 | }; 81 | return new ArgumentElement<>(name, argumentType, suggestionProvider); 82 | } 83 | 84 | public static ArgumentElement server(@NotNull ProxyServer server, @NotNull String name) { 85 | return server(server, name, (context, builder) -> { 86 | server.getServers().forEach((name1, server1) -> { 87 | builder.suggest(server1.getName()); 88 | }); 89 | return builder.buildFuture(); 90 | }); 91 | } 92 | 93 | public static ArgumentElement source(@NotNull ProxyServer server, @NotNull String name, 94 | @NotNull SuggestionProvider suggestionProvider) { 95 | ArgumentType argumentType = reader -> { 96 | String s = reader.readUnquotedString(); 97 | CommandSender source = server.getPlayer(s); 98 | if (source == null) { 99 | throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(reader); 100 | } 101 | return source; 102 | }; 103 | return new ArgumentElement<>(name, argumentType, suggestionProvider); 104 | } 105 | 106 | public static ArgumentElement source(@NotNull ProxyServer server, @NotNull String name) { 107 | return source(server, name, (context, builder) -> { 108 | for (ProxiedPlayer source : server.getPlayers()) { 109 | builder.suggest(source.getName()); 110 | } 111 | return builder.buildFuture(); 112 | }); 113 | } 114 | 115 | @Override 116 | public void addSubCommand(@NotNull Command command) { 117 | addSubCommand(new BungeeCommand(command)); 118 | } 119 | 120 | @Override 121 | public Uniform getUniform() { 122 | return BungeeUniform.INSTANCE; 123 | } 124 | 125 | @NotNull 126 | Impl getImpl(@NotNull Uniform uniform) { 127 | return new Impl(uniform, this); 128 | } 129 | 130 | static final class Impl extends net.md_5.bungee.api.plugin.Command implements TabExecutor { 131 | 132 | private final CommandDispatcher dispatcher = new CommandDispatcher<>(); 133 | private final @Nullable Permission permission; 134 | 135 | public Impl(@NotNull Uniform uniform, @NotNull BungeeCommand command) { 136 | super(command.getName(), 137 | command.permission != null ? command.permission.node() : null, 138 | command.getAliases().toArray(new String[0])); 139 | this.dispatcher.register(command.createBuilder()); 140 | this.permission = command.permission; 141 | } 142 | 143 | @Override 144 | public void execute(CommandSender commandSender, String[] args) { 145 | try { 146 | dispatcher.execute(getInput(args), commandSender); 147 | } catch (CommandSyntaxException e) { 148 | getAudience(commandSender).sendMessage(Component 149 | .translatable("command.context.parse_error", NamedTextColor.RED) 150 | .args( 151 | Component.text(e.getRawMessage().getString()), 152 | Component.text(e.getCursor()), 153 | Component.text(e.getContext()) 154 | )); 155 | } catch (Exception e) { 156 | getAudience(commandSender).sendMessage(Component.text(e.getMessage(), NamedTextColor.RED)); 157 | } 158 | } 159 | 160 | @NotNull 161 | @Override 162 | public Iterable onTabComplete(CommandSender sender, String[] args) 163 | throws IllegalArgumentException { 164 | if (!testPermissionSilent(sender)) { 165 | return List.of(); 166 | } 167 | final String passed = getInput(args); 168 | return dispatcher.getCompletionSuggestions( 169 | dispatcher.parse(passed, sender), 170 | passed.length() // Spigot API limitation - we can only TAB complete the full text length :( - Also making a guess that this is the same on Bungee 171 | ) 172 | .thenApply(suggestions -> suggestions.getList().stream().map(Suggestion::getText).toList()) 173 | .join(); 174 | } 175 | 176 | public boolean testPermissionSilent(@NotNull CommandSender target) { 177 | if (permission == null || permission.node().isBlank()) { 178 | return true; 179 | } 180 | return new BungeeCommandUser(target).checkPermission(permission); 181 | } 182 | 183 | @NotNull 184 | private Audience getAudience(@NotNull CommandSender sender) { 185 | return BungeeUniform.getAudiences().sender(sender); 186 | } 187 | 188 | @NotNull 189 | private String getInput(@NotNull String[] args) { 190 | return args.length == 0 ? getName() : "%s %s".formatted(getName(), String.join(" ", args)); 191 | } 192 | } 193 | 194 | public static class BungeeCommandBuilder extends BaseCommandBuilder { 195 | 196 | public BungeeCommandBuilder(@NotNull String name) { 197 | super(name); 198 | } 199 | 200 | public final BungeeCommandBuilder addSubCommand(@NotNull Command command) { 201 | subCommands.add(new BungeeCommand(command)); 202 | return this; 203 | } 204 | 205 | @Override 206 | public @NotNull BungeeCommand build() { 207 | var command = new BungeeCommand(name, description, aliases); 208 | command.addPermissions(permissions); 209 | subCommands.forEach(command::addSubCommand); 210 | command.setDefaultExecutor(defaultExecutor); 211 | command.syntaxes.addAll(syntaxes); 212 | command.setExecutionScope(executionScope); 213 | command.setCondition(condition); 214 | 215 | return command; 216 | } 217 | 218 | public BungeeCommand register(@NotNull Plugin plugin) { 219 | final BungeeCommand builtCmd = build(); 220 | BungeeUniform.getInstance(plugin).register(builtCmd); 221 | return builtCmd; 222 | } 223 | } 224 | } -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s 90 | ' "$PWD" ) || exit 91 | 92 | # Use the maximum available, or set MAX_FD != -1 to use that value. 93 | MAX_FD=maximum 94 | 95 | warn () { 96 | echo "$*" 97 | } >&2 98 | 99 | die () { 100 | echo 101 | echo "$*" 102 | echo 103 | exit 1 104 | } >&2 105 | 106 | # OS specific support (must be 'true' or 'false'). 107 | cygwin=false 108 | msys=false 109 | darwin=false 110 | nonstop=false 111 | case "$( uname )" in #( 112 | CYGWIN* ) cygwin=true ;; #( 113 | Darwin* ) darwin=true ;; #( 114 | MSYS* | MINGW* ) msys=true ;; #( 115 | NONSTOP* ) nonstop=true ;; 116 | esac 117 | 118 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 119 | 120 | 121 | # Determine the Java command to use to start the JVM. 122 | if [ -n "$JAVA_HOME" ] ; then 123 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 124 | # IBM's JDK on AIX uses strange locations for the executables 125 | JAVACMD=$JAVA_HOME/jre/sh/java 126 | else 127 | JAVACMD=$JAVA_HOME/bin/java 128 | fi 129 | if [ ! -x "$JAVACMD" ] ; then 130 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 131 | 132 | Please set the JAVA_HOME variable in your environment to match the 133 | location of your Java installation." 134 | fi 135 | else 136 | JAVACMD=java 137 | if ! command -v java >/dev/null 2>&1 138 | then 139 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 140 | 141 | Please set the JAVA_HOME variable in your environment to match the 142 | location of your Java installation." 143 | fi 144 | fi 145 | 146 | # Increase the maximum file descriptors if we can. 147 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 148 | case $MAX_FD in #( 149 | max*) 150 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 151 | # shellcheck disable=SC2039,SC3045 152 | MAX_FD=$( ulimit -H -n ) || 153 | warn "Could not query maximum file descriptor limit" 154 | esac 155 | case $MAX_FD in #( 156 | '' | soft) :;; #( 157 | *) 158 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 159 | # shellcheck disable=SC2039,SC3045 160 | ulimit -n "$MAX_FD" || 161 | warn "Could not set maximum file descriptor limit to $MAX_FD" 162 | esac 163 | fi 164 | 165 | # Collect all arguments for the java command, stacking in reverse order: 166 | # * args from the command line 167 | # * the main class name 168 | # * -classpath 169 | # * -D...appname settings 170 | # * --module-path (only if needed) 171 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 172 | 173 | # For Cygwin or MSYS, switch paths to Windows format before running java 174 | if "$cygwin" || "$msys" ; then 175 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 176 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 177 | 178 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 179 | 180 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 181 | for arg do 182 | if 183 | case $arg in #( 184 | -*) false ;; # don't mess with options #( 185 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 186 | [ -e "$t" ] ;; #( 187 | *) false ;; 188 | esac 189 | then 190 | arg=$( cygpath --path --ignore --mixed "$arg" ) 191 | fi 192 | # Roll the args list around exactly as many times as the number of 193 | # args, so each arg winds up back in the position where it started, but 194 | # possibly modified. 195 | # 196 | # NB: a `for` loop captures its iteration list before it begins, so 197 | # changing the positional parameters here affects neither the number of 198 | # iterations, nor the values presented in `arg`. 199 | shift # remove old arg 200 | set -- "$@" "$arg" # push replacement arg 201 | done 202 | fi 203 | 204 | 205 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 206 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 207 | 208 | # Collect all arguments for the java command: 209 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 210 | # and any embedded shellness will be escaped. 211 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 212 | # treated as '${Hostname}' itself on the command line. 213 | 214 | set -- \ 215 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 216 | -classpath "$CLASSPATH" \ 217 | org.gradle.wrapper.GradleWrapperMain \ 218 | "$@" 219 | 220 | # Stop when "xargs" is not available. 221 | if ! command -v xargs >/dev/null 2>&1 222 | then 223 | die "xargs is not available" 224 | fi 225 | 226 | # Use "xargs" to parse quoted args. 227 | # 228 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 229 | # 230 | # In Bash we could simply go: 231 | # 232 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 233 | # set -- "${ARGS[@]}" "$@" 234 | # 235 | # but POSIX shell has neither arrays nor command substitution, so instead we 236 | # post-process each arg (as a line of input to sed) to backslash-escape any 237 | # character that might be a shell metacharacter, then use eval to reverse 238 | # that process (while maintaining the separation between arguments), and wrap 239 | # the whole thing up as a single "set" statement. 240 | # 241 | # This will of course break if any of these variables contains a newline or 242 | # an unmatched quote. 243 | # 244 | 245 | eval "set -- $( 246 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 247 | xargs -n1 | 248 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 249 | tr '\n' ' ' 250 | )" '"$@"' 251 | 252 | exec "$JAVACMD" "$@" 253 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/uniform/Command.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Uniform, licensed under the GNU General Public License v3.0. 3 | * 4 | * Copyright (c) Tofaa2 5 | * Copyright (c) William278 6 | * Copyright (c) contributors 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | package net.william278.uniform; 23 | 24 | import lombok.AccessLevel; 25 | import lombok.AllArgsConstructor; 26 | import lombok.Getter; 27 | import lombok.Setter; 28 | import net.william278.uniform.annotations.Argument; 29 | import net.william278.uniform.annotations.CommandDescription; 30 | import net.william278.uniform.annotations.CommandNode; 31 | import net.william278.uniform.annotations.Syntax; 32 | import net.william278.uniform.element.ArgumentElement; 33 | import net.william278.uniform.element.CommandElement; 34 | import org.jetbrains.annotations.NotNull; 35 | import org.jetbrains.annotations.Nullable; 36 | 37 | import java.lang.reflect.InvocationTargetException; 38 | import java.lang.reflect.Method; 39 | import java.lang.reflect.Parameter; 40 | import java.util.ArrayList; 41 | import java.util.Arrays; 42 | import java.util.List; 43 | import java.util.Optional; 44 | import java.util.function.Predicate; 45 | 46 | import static net.william278.uniform.CommandExecutor.methodToExecutor; 47 | 48 | @Getter 49 | @Setter 50 | @AllArgsConstructor 51 | public abstract class Command implements CommandProvider { 52 | 53 | private final String name; 54 | private List aliases = List.of(); 55 | private String description = ""; 56 | @Getter(AccessLevel.NONE) 57 | private @Nullable Permission permission = null; 58 | private ExecutionScope scope = ExecutionScope.ALL; 59 | 60 | public Optional getPermission() { 61 | return Optional.ofNullable(permission); 62 | } 63 | 64 | Command(@NotNull String name) { 65 | this.name = name; 66 | } 67 | 68 | Command(@Nullable CommandNode node) { 69 | if (node == null) { 70 | throw new IllegalArgumentException("@CommandNode annotation is required on annotated command/sub-commands"); 71 | } 72 | this.name = node.value(); 73 | this.aliases = List.of(node.aliases()); 74 | this.description = node.description(); 75 | this.scope = node.scope(); 76 | Permission.annotated(node.permission()).ifPresent(this::setPermission); 77 | } 78 | 79 | public enum ExecutionScope { 80 | IN_GAME, 81 | CONSOLE, 82 | ALL; 83 | 84 | @Nullable 85 | public Predicate toPredicate(@NotNull BaseCommand command) { 86 | return this == ALL ? null : user -> this.contains(command.getUser(user)); 87 | } 88 | 89 | public boolean contains(@NotNull CommandUser user) { 90 | return switch (this) { 91 | case IN_GAME -> !user.isConsole(); 92 | case CONSOLE -> user.isConsole(); 93 | case ALL -> true; 94 | }; 95 | } 96 | } 97 | 98 | static class AnnotatedCommand extends Command { 99 | 100 | private final Object annotated; 101 | 102 | AnnotatedCommand(@NotNull Object annotated) { 103 | super(annotated.getClass().getAnnotation(CommandNode.class)); 104 | this.annotated = annotated; 105 | this.setDescriptionFromAnnotation(); 106 | } 107 | 108 | @Override 109 | @SuppressWarnings({"rawtypes", "unchecked"}) 110 | public void provide(@NotNull BaseCommand cmd) { 111 | // Add syntaxes 112 | for (Method method : annotated.getClass().getDeclaredMethods()) { 113 | method.setAccessible(true); 114 | final Syntax syntax = method.getAnnotation(Syntax.class); 115 | if (syntax == null) { 116 | continue; 117 | } 118 | 119 | // Default executor 120 | final CommandElement[] args = getMethodArguments(method); 121 | if (args.length == 0) { 122 | cmd.setDefaultExecutor(methodToExecutor(method, annotated, cmd)); 123 | continue; 124 | } 125 | 126 | // Determine predicates 127 | final Optional perm = Permission.annotated(syntax.permission()).map(p -> p.toPredicate(cmd)); 128 | final Optional scope = Optional.ofNullable(syntax.scope().toPredicate(cmd)); 129 | final Optional combined = scope.map(sp -> perm.map(pp -> sp.and(pp)).orElse(sp)); 130 | 131 | // Conditional & unconditional syntax 132 | final CommandExecutor executor = methodToExecutor(method, annotated, cmd); 133 | if (combined.isPresent()) { 134 | cmd.addConditionalSyntax(combined.get(), executor, args); 135 | continue; 136 | } 137 | cmd.addSyntax(executor, args); 138 | } 139 | 140 | // Add subcommands 141 | for (Class subClass : annotated.getClass().getDeclaredClasses()) { 142 | if (subClass.getAnnotation(CommandNode.class) == null) { 143 | continue; 144 | } 145 | try { 146 | cmd.addSubCommand(new AnnotatedCommand( 147 | subClass.getDeclaredConstructor().newInstance() 148 | )); 149 | } catch (InstantiationException | IllegalAccessException | InvocationTargetException | 150 | NoSuchMethodException e) { 151 | throw new IllegalArgumentException( 152 | "Failed to create sub-command instance (does it have a zero-arg constructor?)", e 153 | ); 154 | } 155 | } 156 | } 157 | 158 | @NotNull 159 | private static CommandElement[] getMethodArguments(@NotNull Method method) { 160 | try { 161 | final List> elements = new ArrayList<>(); 162 | for (Parameter param : method.getParameters()) { 163 | final Argument arg = param.getAnnotation(Argument.class); 164 | if (arg == null) { 165 | continue; 166 | } 167 | // Get the argument name 168 | final String argName = arg.name().isEmpty() ? param.getName() : arg.name(); 169 | 170 | // Pass parser properties if needed 171 | if (arg.parserProperties().length == 0) { 172 | elements.add(arg.parser().getDeclaredConstructor() 173 | .newInstance().provide(argName)); 174 | continue; 175 | } 176 | elements.add(arg.parser().getDeclaredConstructor(String[].class) 177 | .newInstance((Object) arg.parserProperties()).provide(argName)); 178 | } 179 | return elements.toArray(new ArgumentElement[0]); 180 | } catch (Throwable e) { 181 | throw new IllegalArgumentException("Failed to create argument elements from method parameters", e); 182 | } 183 | } 184 | 185 | private void setDescriptionFromAnnotation() { 186 | Arrays.stream(annotated.getClass().getFields()) 187 | .filter(f -> f.getAnnotation(CommandDescription.class) != null) 188 | .findFirst().ifPresent(f -> { 189 | try { 190 | f.setAccessible(true); 191 | this.setDescription((String) f.get(annotated)); 192 | } catch (IllegalAccessException e) { 193 | throw new IllegalArgumentException("Failed to set command description from field", e); 194 | } 195 | }); 196 | } 197 | } 198 | 199 | public record SubCommand(@NotNull String name, @NotNull List aliases, @Nullable Permission permission, 200 | @NotNull ExecutionScope scope, @NotNull CommandProvider provider) { 201 | public SubCommand(@NotNull String name, @NotNull List aliases, @Nullable Permission permission, 202 | @NotNull CommandProvider provider) { 203 | this(name, aliases, permission, ExecutionScope.ALL, provider); 204 | } 205 | 206 | public SubCommand(@NotNull String name, @NotNull List aliases, @NotNull CommandProvider provider) { 207 | this(name, aliases, null, ExecutionScope.ALL, provider); 208 | } 209 | 210 | public SubCommand(@NotNull String name, @Nullable Permission permission, @NotNull CommandProvider provider) { 211 | this(name, List.of(), permission, ExecutionScope.ALL, provider); 212 | } 213 | 214 | public SubCommand(@NotNull String name, @NotNull CommandProvider provider) { 215 | this(name, List.of(), null, ExecutionScope.ALL, provider); 216 | } 217 | 218 | 219 | @NotNull 220 | Command command() { 221 | return new Command(name, aliases, "", permission, scope) { 222 | @Override 223 | public void provide(@NotNull BaseCommand command) { 224 | provider.provide(command); 225 | } 226 | }; 227 | } 228 | } 229 | 230 | } 231 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | Claim Operations Library 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

14 |
15 | 16 | **Uniform** is cross-platform wrapper for making Brigadier commands, based on [`BrigadierWrapper` by Tofaa2](https://github.com/Tofaa2/BrigadierWrapper/), which itself was inspired by [EmortalMC's `command-system`](https://github.com/emortalmc/command-system). 17 | 18 | ## Compatibility 19 | 20 | Versions are available on maven in the format `net.william278.uniform:ARTIFACT:VERSION`. See below for a table of supported platforms. 21 | 22 | Note that Uniform versions omit the `v` prefix. Fabric versions are suffixed with the target Minecraft version (e.g. `1.2.1+1.21`) and also require Fabric API installed on the server. Sponge versions are suffixed with the target Sponge API version (e.g. `1.2.1+11`). 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 |
Uniform Version Table
PlatformArtifactMinecraft ver.Java ver.Uniform ver.
Commonuniform-commonN/A17
Supported Platforms
Bukkit / Spigotuniform-bukkit1.17.117
Paperuniform-paper
Velocity (3.3.0)uniform-velocity1.8.9
Bungee / Waterfalluniform-bungee1.17.1
Sponge (api 11)uniform-sponge=1.20.621
Fabric (1.20.1)uniform-fabric=1.20.1
Fabric (1.21.1)=1.21.1
Fabric (1.21.4)=1.21.4
Fabric (1.21.5)=1.21.5
Fabric (1.21.7)=1.21.7
Formerly Supported Platforms
Fabric (1.20.6)uniform-fabric=1.20.621v1.1.8
Fabric (1.21)uniform-fabric=1.21v1.2.1
Fabric (1.21.3)uniform-fabric=1.21.3v1.2.2
Fabric (1.21.6)uniform-fabric=1.21.6v1.3.5
Fabric (1.21.7)uniform-fabric=1.21.7v1.3.8
131 | 132 | Example: To target Uniform on Bukkit, the artifact is `net.william278.uniform:uniform-bukkit:1.2.1` (check that this version is up-to-date – make sure you target the latest available!). 133 | 134 | ## Setup 135 | Uniform is available [on Maven](https://repo.william278.net/#/releases/net/william278/uniform/). You can browse the Javadocs [here](https://repo.william278.net/javadoc/releases/net/william278/uniform/latest). 136 | 137 |
138 | Gradle setup instructions 139 | 140 | First, add the Maven repository to your `build.gradle` file: 141 | ```groovy 142 | repositories { 143 | maven { url "https://repo.william278.net/releases" } 144 | } 145 | ``` 146 | 147 | Then, add the dependency itself. Replace `VERSION` with the latest release version. (e.g., `1.2.1`) and `PLATFORM` with the platform you are targeting (e.g., `paper`). If you want to target pre-release "snapshot" versions (not recommended), you should use the `/snapshots` repository instead. 148 | 149 | ```groovy 150 | dependencies { 151 | implementation "net.william278.uniform:uniform-PLATFORM:VERSION" 152 | } 153 | ``` 154 |
155 | 156 | Using Maven/something else? There's instructions on how to include Uniform on [the repo browser](https://repo.william278.net/#/releases/net/william278/uniform). 157 | 158 | ## Basic use 159 | Uniform lets you create commands either natively per-platform, or cross-platform (by compiling against `uniform-common` in a common module, then implementing `uniform-PLATFORM` in each platform, getting the platform specific Uniform manager instance and registering your commands). 160 | 161 | Check `example-plugin` for a full example of a cross-platform command being registered on Paper. 162 | 163 | ### Cross-platform commands 164 | Cross-platform commands can be created by registering `Command` objects; you can create these from `@CommandNode` annotated objects, or by extending `Command` and providing these yourself. 165 | 166 | #### Using annotations 167 | You can use the `@CommandNode` annotations to easily create cross-platform Brigadier commands (since: v1.2). This is the recommended way to create commands. 168 | This is useful for creating commands with not so many subcommands and arguments. 169 | 170 | ```java 171 | @CommandNode( 172 | value = "helloworld", 173 | aliases = {"hello", "hi"}, 174 | description = "A simple hello world command", 175 | permission = @PermissionNode( 176 | value = "example.command.helloworld", 177 | defaultValue = Permission.Default.TRUE 178 | ) 179 | ) 180 | public class AnnotatedCommand { 181 | 182 | @Syntax 183 | public void execute(CommandUser user) { 184 | user.getAudience().sendMessage(Component.text("Hello, world!")); 185 | } 186 | 187 | @Syntax 188 | public void pongMessage( 189 | CommandUser user, 190 | @Argument(name = "message", parser = Argument.StringArg.class) String message 191 | ) { 192 | user.getAudience().sendMessage(Component.text("Hello, " + message, NamedTextColor.GREEN)); 193 | } 194 | 195 | @CommandNode( 196 | value = "subcommand", 197 | aliases = {"sub", "hi"} 198 | ) 199 | static class SubCommand { 200 | @Syntax 201 | public void execute(CommandUser user) { 202 | user.getAudience().sendMessage(Component.text("Subcommand executed!")); 203 | } 204 | } 205 | 206 | } 207 | ``` 208 | 209 | #### By using the command builder 210 | You can use the builder available for paper and fabric (PaperCommand and FabricCommand) to create commands 211 | This is mostly useful for creating commands with many subcommands and arguments 212 | 213 | ```java 214 | public class WhicheverClass { 215 | public PaperCommand getBuiltCommand() { 216 | return PaperCommand.builder("helloworld") 217 | .setDescription("A simple hello world command") 218 | .setAliases(List.of("hello", "hi")) 219 | .setPermission(new Permission("example.command.helloworld", Permission.Default.TRUE)) 220 | .addSubCommand(messageSubCommand()) 221 | .build(); 222 | } 223 | 224 | private PaperCommand messageSubCommand() { 225 | return PaperCommand.builder("message") 226 | .setDescription("Echoes message") 227 | .addStringArgument("message", ((commandContext, suggestionsBuilder) -> { 228 | //Here is for suggestions but you can skip this part 229 | return suggestionsBuilder.suggest("Message content...").buildFuture(); 230 | })) 231 | .execute(commandContext -> { 232 | String message = commandContext.getArgument("message", String.class); 233 | commandContext.getSource().getSender() 234 | .sendMessage(Component.text("Hello, " + message, NamedTextColor.GREEN)); 235 | }, "message") //Specify the arguments used in the correct order 236 | .execute(commandContext -> { 237 | commandContext.getSource().getSender().sendMessage("Missing argument 'message'"); 238 | }) 239 | .build(); 240 | } 241 | } 242 | ``` 243 | 244 | #### By extending the Command class. 245 | You can also extend the `Command` class to create a Command object you can register. You'll want to use `BaseCommand#getUser` to get a platform-agnostic User from which you can acquire the adventure `Audience` to send messages to. 246 | 247 | ```java 248 | public class ExampleCrossPlatCommand extends Command { 249 | public ExampleCrossPlatCommand() { 250 | super("example", "cross-platform"); 251 | } 252 | 253 | @Override 254 | public void provide(@NotNull BaseCommand command) { 255 | // What gets executed when no args are passed. 256 | // For tidiness, feel free to delegate this stuff to methods! 257 | command.setDefaultExecutor((context) -> { 258 | // Use command.getUser(context.getSource()) to get the user 259 | final Audience user = command.getUser(context.getSource()).getAudience(); 260 | user.sendMessage(Component.text("Hello, world!")); 261 | }); 262 | 263 | // Add syntax to the command 264 | command.addSyntax((context) -> { 265 | final Audience user = command.getUser(ctx.getSource()).getAudience(); 266 | user.sendMessage(Component.text("Woah!!!!")); 267 | String arg = context.getArgument("message", String.class); 268 | user.sendMessage(MiniMessage.miniMessage().deserialize(arg)); 269 | }, stringArg("message")); 270 | 271 | // Sub-commands, too 272 | command.addSubCommand("subcommand", (sub) -> { 273 | sub.setDefaultExecutor((context) -> { 274 | final Audience user = sub.getUser(context.getSource()).getAudience(); 275 | user.sendMessage(Component.text("Subcommand executed!")); 276 | }); 277 | }); 278 | } 279 | } 280 | ``` 281 | 282 | ### Platform-specific commands 283 | If you need platform-specific features, extend the platform-specific `PlatformCommand` class and add your Brigadier syntax. 284 | 285 | ```java 286 | public class ExampleCommand extends PaperCommand { 287 | public ExampleCommand() { 288 | super("example", "platform-specific"); 289 | command.setDefaultExecutor((context) -> { 290 | context.getSource().getBukkitSender().sendMessage("Hello, world!"); 291 | }); 292 | addSyntax((context) -> { 293 | context.getSource().getBukkitSender().sendMessage("Woah!!!!"); 294 | String arg = context.getArgument("message", String.class); 295 | context.getSource().getBukkitSender() 296 | .sendMessage(MiniMessage.miniMessage().deserialize(arg)); 297 | }, stringArg("message")); 298 | } 299 | } 300 | ``` 301 | 302 | 303 | ### Registering 304 | Then, register the command with the platform-specific Uniform instance (e.g. `FabricUniform.getInstance()`, `PaperUniform.getInstance()`, etc...) 305 | 306 | ## Building 307 | To build Uniform, run `clean build` in the root directory. The output JARs will be in `target/`. 308 | 309 | ## License 310 | Uniform is licensed under GPL v3 as it derives from BrigadierWrapper. See [LICENSE](https://github.com/WiIIiam278/Uniform/raw/master/LICENSE) for more information. 311 | --------------------------------------------------------------------------------