├── .github └── workflows │ └── release.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── main ├── java └── dev │ └── xpple │ └── clientarguments │ ├── ClientArguments.java │ └── arguments │ ├── CAngleArgument.java │ ├── CBlockInput.java │ ├── CBlockPosArgument.java │ ├── CBlockPredicateArgument.java │ ├── CBlockStateArgument.java │ ├── CColorArgument.java │ ├── CColumnPosArgument.java │ ├── CComponentArgument.java │ ├── CCompoundTagArgument.java │ ├── CCoordinates.java │ ├── CDimensionArgument.java │ ├── CEntityAnchorArgument.java │ ├── CEntityArgument.java │ ├── CEntitySelector.java │ ├── CEntitySelectorOptions.java │ ├── CEntitySelectorParser.java │ ├── CEnumArgument.java │ ├── CGameProfileArgument.java │ ├── CItemArgument.java │ ├── CItemPredicateArgument.java │ ├── CLocalCoordinates.java │ ├── CMessageArgument.java │ ├── CNbtPathArgument.java │ ├── CNbtTagArgument.java │ ├── CObjectiveArgument.java │ ├── CObjectiveCriteriaArgument.java │ ├── COperationArgument.java │ ├── CParticleArgument.java │ ├── CRangeArgument.java │ ├── CResourceArgument.java │ ├── CResourceKeyArgument.java │ ├── CResourceLocationArgument.java │ ├── CResourceOrIdArgument.java │ ├── CResourceOrTagArgument.java │ ├── CResourceOrTagKeyArgument.java │ ├── CResourceSelectorArgument.java │ ├── CRotationArgument.java │ ├── CScoreHolderArgument.java │ ├── CScoreboardSlotArgument.java │ ├── CSlotArgument.java │ ├── CSlotsArgument.java │ ├── CStyleArgument.java │ ├── CSuggestionProviders.java │ ├── CSwizzleArgument.java │ ├── CTeamArgument.java │ ├── CTimeArgument.java │ ├── CUuidArgument.java │ ├── CVec2Argument.java │ ├── CVec3Argument.java │ └── CWorldCoordinates.java └── resources ├── clientarguments.aw └── fabric.mod.json /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Publish package to GitHub Packages 2 | on: 3 | release: 4 | types: [created] 5 | jobs: 6 | publish: 7 | runs-on: ubuntu-latest 8 | permissions: 9 | contents: read 10 | packages: write 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: actions/setup-java@v3 14 | with: 15 | java-version: '21' 16 | distribution: 'adopt' 17 | - name: Validate Gradle wrapper 18 | uses: gradle/wrapper-validation-action@v1 19 | - name: Publish package 20 | run: gradle publish 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | MAVEN_USER: ${{ secrets.MAVEN_USER }} 24 | MAVEN_PASS: ${{ secrets.MAVEN_PASS }} 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific stuff 2 | .idea/ 3 | 4 | *.iml 5 | *.ipr 6 | *.iws 7 | 8 | # IntelliJ 9 | out/ 10 | # mpeltonen/sbt-idea plugin 11 | .idea_modules/ 12 | 13 | # JIRA plugin 14 | atlassian-ide-plugin.xml 15 | 16 | # Compiled class file 17 | *.class 18 | 19 | # Log file 20 | *.log 21 | 22 | # BlueJ files 23 | *.ctxt 24 | 25 | # Package Files # 26 | *.jar 27 | *.war 28 | *.nar 29 | *.ear 30 | *.zip 31 | *.tar.gz 32 | *.rar 33 | 34 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 35 | hs_err_pid* 36 | 37 | *~ 38 | 39 | # temporary files which can be created if a process still has a handle open of a deleted file 40 | .fuse_hidden* 41 | 42 | # KDE directory preferences 43 | .directory 44 | 45 | # Linux trash folder which might appear on any partition or disk 46 | .Trash-* 47 | 48 | # .nfs files are created when an open file is removed but is still being accessed 49 | .nfs* 50 | 51 | # General 52 | .DS_Store 53 | .AppleDouble 54 | .LSOverride 55 | 56 | # Icon must end with two \r 57 | Icon 58 | 59 | # Thumbnails 60 | ._* 61 | 62 | # Files that might appear in the root of a volume 63 | .DocumentRevisions-V100 64 | .fseventsd 65 | .Spotlight-V100 66 | .TemporaryItems 67 | .Trashes 68 | .VolumeIcon.icns 69 | .com.apple.timemachine.donotpresent 70 | 71 | # Directories potentially created on remote AFP share 72 | .AppleDB 73 | .AppleDesktop 74 | Network Trash Folder 75 | Temporary Items 76 | .apdisk 77 | 78 | # Windows thumbnail cache files 79 | Thumbs.db 80 | Thumbs.db:encryptable 81 | ehthumbs.db 82 | ehthumbs_vista.db 83 | 84 | # Dump file 85 | *.stackdump 86 | 87 | # Folder config file 88 | [Dd]esktop.ini 89 | 90 | # Recycle Bin used on file shares 91 | $RECYCLE.BIN/ 92 | 93 | # Windows Installer files 94 | *.cab 95 | *.msi 96 | *.msix 97 | *.msm 98 | *.msp 99 | 100 | # Windows shortcuts 101 | *.lnk 102 | 103 | .gradle 104 | build/ 105 | 106 | # Ignore Gradle GUI config 107 | gradle-app.setting 108 | 109 | # Cache of project 110 | .gradletasknamecache 111 | 112 | **/build/ 113 | 114 | # Common working directory 115 | run/ 116 | 117 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 118 | !gradle-wrapper.jar 119 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 xpple 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # clientarguments 2 | Quality of life library for client-sided Minecraft Fabric command building. 3 | 4 | ## Installation 5 | Replace `${version}` with the artifact version. 6 | ### Gradle 7 | You may choose between my own maven repository and GitHub's package repository. 8 | #### My own 9 | ```gradle 10 | repositories { 11 | maven { 12 | url 'https://maven.xpple.dev/maven2' 13 | } 14 | } 15 | ``` 16 | #### GitHub packages 17 | ```gradle 18 | repositories { 19 | maven { 20 | url 'https://maven.pkg.github.com/xpple/clientarguments' 21 | credentials { 22 | username = project.findProperty("gpr.user") ?: System.getenv("USERNAME") 23 | password = project.findProperty("gpr.key") ?: System.getenv("TOKEN") 24 | } 25 | } 26 | } 27 | ``` 28 | Import it: 29 | ```gradle 30 | dependencies { 31 | include modImplementation('dev.xpple:clientarguments:${version}') 32 | } 33 | ``` 34 | Note: if you choose to use GitHub packages and get `Received status code 400 from server: Bad Request`, you need to 35 | configure your environment variables for GitHub. 36 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'fabric-loom' version '1.10-SNAPSHOT' 3 | id 'maven-publish' 4 | } 5 | 6 | version = project.mod_version 7 | group = project.maven_group 8 | 9 | repositories { 10 | maven { 11 | url = "https://maven.parchmentmc.org" 12 | } 13 | } 14 | 15 | loom { 16 | accessWidenerPath = file('src/main/resources/clientarguments.aw') 17 | } 18 | 19 | dependencies { 20 | // To change the versions see the gradle.properties file 21 | minecraft "com.mojang:minecraft:${project.minecraft_version}" 22 | mappings loom.layered { 23 | officialMojangMappings { 24 | nameSyntheticMembers = true 25 | } 26 | parchment("org.parchmentmc.data:parchment-1.21:2024.07.28@zip") 27 | } 28 | modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" 29 | 30 | modImplementation fabricApi.module("fabric-command-api-v2", project.fabric_api_version) 31 | 32 | // PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs. 33 | // You may need to force-disable transitiveness on them. 34 | } 35 | 36 | processResources { 37 | inputs.property "version", project.version 38 | filteringCharset "UTF-8" 39 | 40 | filesMatching("fabric.mod.json") { 41 | expand "version": project.version 42 | } 43 | } 44 | 45 | def targetJavaVersion = 21 46 | tasks.withType(JavaCompile).configureEach { 47 | // ensure that the encoding is set to UTF-8, no matter what the system default is 48 | // this fixes some edge cases with special characters not displaying correctly 49 | // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html 50 | // If Javadoc is generated, this must be specified in that task too. 51 | it.options.encoding = "UTF-8" 52 | if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) { 53 | it.options.release = targetJavaVersion 54 | } 55 | } 56 | 57 | java { 58 | def javaVersion = JavaVersion.toVersion(targetJavaVersion) 59 | if (JavaVersion.current() < javaVersion) { 60 | toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) 61 | } 62 | archivesBaseName = project.archives_base_name 63 | // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task 64 | // if it is present. 65 | // If you remove this line, sources will not be generated. 66 | withSourcesJar() 67 | } 68 | 69 | jar { 70 | from("LICENSE") { 71 | rename { "${it}_${project.archivesBaseName}" } 72 | } 73 | } 74 | 75 | // configure the maven publication 76 | publishing { 77 | publications { 78 | mavenJava(MavenPublication) { 79 | from components.java 80 | } 81 | } 82 | 83 | // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. 84 | repositories { 85 | // Add repositories to publish to here. 86 | // Notice: This block does NOT have the same function as the block in the top level. 87 | // The repositories here will be used for publishing your artifact, not for 88 | // retrieving dependencies. 89 | maven { 90 | name = "GitHubPackages" 91 | url = "https://maven.pkg.github.com/xpple/clientarguments" 92 | credentials { 93 | username = System.getenv("GITHUB_ACTOR") 94 | password = System.getenv("GITHUB_TOKEN") 95 | } 96 | } 97 | maven { 98 | name = "xpple" 99 | url = "sftp://xpple.dev:22/maven.xpple.dev/httpdocs/maven2" 100 | credentials { 101 | username = System.getenv("MAVEN_USER") 102 | password = System.getenv("MAVEN_PASS") 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx1G 3 | # Fabric Properties 4 | # check these on https://fabricmc.net/develop/ 5 | minecraft_version=1.21.5-rc1 6 | loader_version=0.16.10 7 | 8 | # Mod Properties 9 | mod_version=1.11.1 10 | maven_group=dev.xpple 11 | archives_base_name=clientarguments 12 | 13 | # Dependencies 14 | fabric_api_version=0.119.2+1.21.5 15 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xpple/clientarguments/edcb311bfa19d6914ae070a553c6e56f95080fd9/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = 'Fabric' 5 | url = 'https://maven.fabricmc.net/' 6 | } 7 | gradlePluginPortal() 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/ClientArguments.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments; 2 | 3 | import com.mojang.brigadier.AmbiguityConsumer; 4 | import com.mojang.brigadier.Command; 5 | import com.mojang.brigadier.CommandDispatcher; 6 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 7 | import dev.xpple.clientarguments.arguments.CEntitySelectorOptions; 8 | import net.fabricmc.api.ClientModInitializer; 9 | import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; 10 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 11 | import net.fabricmc.loader.api.FabricLoader; 12 | import net.minecraft.commands.CommandBuildContext; 13 | import net.minecraft.core.registries.Registries; 14 | import net.minecraft.network.chat.Component; 15 | import net.minecraft.world.level.GameType; 16 | 17 | import static dev.xpple.clientarguments.arguments.CAngleArgument.*; 18 | import static dev.xpple.clientarguments.arguments.CBlockPosArgument.*; 19 | import static dev.xpple.clientarguments.arguments.CBlockPredicateArgument.*; 20 | import static dev.xpple.clientarguments.arguments.CBlockStateArgument.*; 21 | import static dev.xpple.clientarguments.arguments.CColorArgument.*; 22 | import static dev.xpple.clientarguments.arguments.CColumnPosArgument.*; 23 | import static dev.xpple.clientarguments.arguments.CDimensionArgument.*; 24 | import static dev.xpple.clientarguments.arguments.CEntityAnchorArgument.*; 25 | import static dev.xpple.clientarguments.arguments.CEntityArgument.*; 26 | import static dev.xpple.clientarguments.arguments.CEnumArgument.*; 27 | import static dev.xpple.clientarguments.arguments.CGameProfileArgument.*; 28 | import static dev.xpple.clientarguments.arguments.CResourceLocationArgument.*; 29 | import static dev.xpple.clientarguments.arguments.CItemPredicateArgument.*; 30 | import static dev.xpple.clientarguments.arguments.CResourceSelectorArgument.*; 31 | import static dev.xpple.clientarguments.arguments.CSlotArgument.*; 32 | import static dev.xpple.clientarguments.arguments.CItemArgument.*; 33 | import static dev.xpple.clientarguments.arguments.CMessageArgument.*; 34 | import static dev.xpple.clientarguments.arguments.CCompoundTagArgument.*; 35 | import static dev.xpple.clientarguments.arguments.CNbtTagArgument.*; 36 | import static dev.xpple.clientarguments.arguments.CNbtPathArgument.*; 37 | import static dev.xpple.clientarguments.arguments.CRangeArgument.*; 38 | import static dev.xpple.clientarguments.arguments.COperationArgument.*; 39 | import static dev.xpple.clientarguments.arguments.CParticleArgument.*; 40 | import static dev.xpple.clientarguments.arguments.CResourceOrIdArgument.*; 41 | import static dev.xpple.clientarguments.arguments.CResourceOrTagArgument.*; 42 | import static dev.xpple.clientarguments.arguments.CResourceArgument.*; 43 | import static dev.xpple.clientarguments.arguments.CResourceKeyArgument.*; 44 | import static dev.xpple.clientarguments.arguments.CResourceOrTagKeyArgument.*; 45 | import static dev.xpple.clientarguments.arguments.CRotationArgument.*; 46 | import static dev.xpple.clientarguments.arguments.CScoreHolderArgument.*; 47 | import static dev.xpple.clientarguments.arguments.CObjectiveCriteriaArgument.*; 48 | import static dev.xpple.clientarguments.arguments.CObjectiveArgument.*; 49 | import static dev.xpple.clientarguments.arguments.CScoreboardSlotArgument.*; 50 | import static dev.xpple.clientarguments.arguments.CSlotsArgument.*; 51 | import static dev.xpple.clientarguments.arguments.CStyleArgument.*; 52 | import static dev.xpple.clientarguments.arguments.CSwizzleArgument.*; 53 | import static dev.xpple.clientarguments.arguments.CTeamArgument.*; 54 | import static dev.xpple.clientarguments.arguments.CComponentArgument.*; 55 | import static dev.xpple.clientarguments.arguments.CTimeArgument.*; 56 | import static dev.xpple.clientarguments.arguments.CUuidArgument.*; 57 | import static dev.xpple.clientarguments.arguments.CVec2Argument.*; 58 | import static dev.xpple.clientarguments.arguments.CVec3Argument.*; 59 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 60 | 61 | public class ClientArguments implements ClientModInitializer { 62 | private static final DynamicCommandExceptionType STRUCTURE_INVALID_EXCEPTION = new DynamicCommandExceptionType(id -> Component.translatableEscape("commands.locate.structure.invalid", id)); 63 | 64 | @Override 65 | public void onInitializeClient() { 66 | CEntitySelectorOptions.register(); 67 | 68 | if (FabricLoader.getInstance().isDevelopmentEnvironment()) { 69 | ClientCommandRegistrationCallback.EVENT.register(ClientArguments::registerTestCommand); 70 | } 71 | } 72 | 73 | /** 74 | *

75 | * Registering this test command will trigger {@link com.mojang.brigadier.tree.CommandNode#findAmbiguities(AmbiguityConsumer)}, 76 | * which checks the validity of the example inputs - and with that also the validity of the argument in question. 77 | */ 78 | private static void registerTestCommand(CommandDispatcher dispatcher, CommandBuildContext registryAccess) { 79 | dispatcher.register(literal("clientarguments:test") 80 | .then(literal("angle").then(argument("angle", angle()) 81 | .executes(ctx -> consume(getAngle(ctx, "angle"))))) 82 | .then(literal("blockpos").then(argument("blockpos", blockPos()) 83 | .executes(ctx -> consume(getBlockPos(ctx, "blockpos"))))) 84 | .then(literal("blockpredicate").then(argument("blockpredicate", blockPredicate(registryAccess)) 85 | .executes(ctx -> consume(getBlockPredicate(ctx, "blockpredicate"))))) 86 | .then(literal("blockstate").then(argument("blockstate", blockState(registryAccess)) 87 | .executes(ctx -> consume(getBlockState(ctx, "blockstate"))))) 88 | .then(literal("color").then(argument("color", color()) 89 | .executes(ctx -> consume(getColor(ctx, "color"))))) 90 | .then(literal("columnpos").then(argument("columnpos", columnPos()) 91 | .executes(ctx -> consume(getColumnPos(ctx, "columnpos"))))) 92 | .then(literal("dimension").then(argument("dimension", dimension()) 93 | .executes(ctx -> consume(getDimension(ctx, "dimension"))))) 94 | .then(literal("entityanchor").then(argument("entityanchor", entityAnchor()) 95 | .executes(ctx -> consume(getEntityAnchor(ctx, "entityanchor"))))) 96 | .then(literal("entity").then(argument("entity", entity()) 97 | .executes(ctx -> consume(getEntity(ctx, "entity"))))) 98 | .then(literal("enum").then(argument("enum", enumArg(GameType.class)) 99 | .executes(ctx -> consume(getEnum(ctx, "enum"))))) 100 | .then(literal("gameprofile").then(argument("gameprofile", gameProfile()) 101 | .executes(ctx -> consume(getProfileArgument(ctx, "gameprofile"))))) 102 | .then(literal("identifier").then(argument("identifier", id()) 103 | .executes(ctx -> consume(getId(ctx, "identifier"))))) 104 | .then(literal("itempredicate").then(argument("itempredicate", itemPredicate(registryAccess)) 105 | .executes(ctx -> consume(getItemStackPredicate(ctx, "itempredicate"))))) 106 | .then(literal("itemslot").then(argument("itemslot", itemSlot()) 107 | .executes(ctx -> consume(getItemSlot(ctx, "itemslot"))))) 108 | .then(literal("itemstack").then(argument("itemstack", itemStack(registryAccess)) 109 | .executes(ctx -> consume(getItemStackArgument(ctx, "itemstack"))))) 110 | .then(literal("message").then(argument("message", message()) 111 | .executes(ctx -> consume(getMessage(ctx, "message"))))) 112 | .then(literal("nbtcompound").then(argument("nbtcompound", compoundTag()) 113 | .executes(ctx -> consume(getCompoundTag(ctx, "nbtcompound"))))) 114 | .then(literal("nbtelement").then(argument("nbtelement", nbtTag()) 115 | .executes(ctx -> consume(getNbtTag(ctx, "nbtelement"))))) 116 | .then(literal("nbtpath").then(argument("nbtpath", nbtPath()) 117 | .executes(ctx -> consume(getNbtPath(ctx, "nbtpath"))))) 118 | .then(literal("intrange").then(argument("intrange", intRange()) 119 | .executes(ctx -> consume(Ints.getRangeArgument(ctx, "intrange"))))) 120 | .then(literal("floatrange").then(argument("floatrange", floatRange()) 121 | .executes(ctx -> consume(Floats.getRangeArgument(ctx, "floatrange"))))) 122 | .then(literal("operation").then(argument("operation", operation()) 123 | .executes(ctx -> consume(getOperation(ctx, "operation"))))) 124 | .then(literal("particleeffect").then(argument("particleeffect", particle(registryAccess)) 125 | .executes(ctx -> consume(getParticle(ctx, "particleeffect"))))) 126 | .then(literal("registryentry").then(argument("registryentry", lootTable(registryAccess)) 127 | .executes(ctx -> consume(getLootTable(ctx, "registryentry"))))) 128 | .then(literal("registryentrypredicate").then(argument("registryentrypredicate", resourceOrTag(registryAccess, Registries.BIOME)) 129 | .executes(ctx -> consume(getResourceOrTag(ctx, "registryentrypredicate", Registries.BIOME))))) 130 | .then(literal("registryentryreference").then(argument("registryentryreference", registryEntry(registryAccess, Registries.ENCHANTMENT)) 131 | .executes(ctx -> consume(getEnchantment(ctx, "registryentryreference"))))) 132 | .then(literal("registrykey").then(argument("registrykey", key(Registries.STRUCTURE)) 133 | .executes(ctx -> consume(getKey(ctx, "registrykey", Registries.STRUCTURE, STRUCTURE_INVALID_EXCEPTION))))) 134 | .then(literal("registrypredicate").then(argument("registrypredicate", registryPredicate(Registries.STRUCTURE)) 135 | .executes(ctx -> consume(getPredicate(ctx, "registrypredicate", Registries.STRUCTURE, STRUCTURE_INVALID_EXCEPTION))))) 136 | .then(literal("resourceselector").then(argument("resourceselector", resourceSelector(registryAccess, Registries.ITEM)) 137 | .executes(ctx -> consume(getSelectedResources(ctx, "resourceselector", Registries.ITEM))))) 138 | .then(literal("rotation").then(argument("rotation", rotation()) 139 | .executes(ctx -> consume(getRotation(ctx, "rotation"))))) 140 | .then(literal("scoreboardcriterion").then(argument("scoreboardcriterion", criteria()) 141 | .executes(ctx -> consume(getCriteria(ctx, "scoreboardcriterion"))))) 142 | .then(literal("scoreboardobjective").then(argument("scoreboardobjective", objective()) 143 | .executes(ctx -> consume(getObjective(ctx, "scoreboardobjective"))))) 144 | .then(literal("scoreboardslot").then(argument("scoreboardslot", scoreboardSlot()) 145 | .executes(ctx -> consume(getScoreboardSlot(ctx, "scoreboardslot"))))) 146 | .then(literal("scoreholder").then(argument("scoreholder", scoreHolder()) 147 | .executes(ctx -> consume(getScoreHolder(ctx, "scoreholder"))))) 148 | .then(literal("slotrange").then(argument("slotrange", slots()) 149 | .executes(ctx -> consume(getSlots(ctx, "slotrange"))))) 150 | .then(literal("style").then(argument("style", style(registryAccess)) 151 | .executes(ctx -> consume(getStyle(ctx, "style"))))) 152 | .then(literal("swizzle").then(argument("swizzle", swizzle()) 153 | .executes(ctx -> consume(getSwizzle(ctx, "swizzle"))))) 154 | .then(literal("team").then(argument("team", team()) 155 | .executes(ctx -> consume(getTeam(ctx, "team"))))) 156 | .then(literal("text").then(argument("text", textComponent(registryAccess)) 157 | .executes(ctx -> consume(getComponent(ctx, "text"))))) 158 | .then(literal("time").then(argument("time", time()) 159 | .executes(ctx -> consume(getTime(ctx, "time"))))) 160 | .then(literal("uuid").then(argument("uuid", uuid()) 161 | .executes(ctx -> consume(getUuid(ctx, "uuid"))))) 162 | .then(literal("vec2").then(argument("vec2", vec2()) 163 | .executes(ctx -> consume(getVec2(ctx, "vec2"))))) 164 | .then(literal("vec3").then(argument("vec3", vec3()) 165 | .executes(ctx -> consume(getVec3(ctx, "vec3"))))) 166 | ); 167 | } 168 | 169 | private static int consume(Object object) { 170 | return Command.SINGLE_SUCCESS; 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CAngleArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 8 | 9 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 10 | import net.minecraft.commands.arguments.coordinates.WorldCoordinate; 11 | import net.minecraft.network.chat.Component; 12 | import net.minecraft.util.Mth; 13 | 14 | import java.util.Arrays; 15 | import java.util.Collection; 16 | 17 | public class CAngleArgument implements ArgumentType { 18 | private static final Collection EXAMPLES = Arrays.asList("0", "~", "~-5"); 19 | public static final SimpleCommandExceptionType INCOMPLETE_ANGLE_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("argument.angle.incomplete")); 20 | public static final SimpleCommandExceptionType INVALID_ANGLE_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("argument.angle.invalid")); 21 | 22 | public static CAngleArgument angle() { 23 | return new CAngleArgument(); 24 | } 25 | 26 | public static float getAngle(final CommandContext context, final String name) { 27 | return context.getArgument(name, Angle.class).getAngle(context.getSource()); 28 | } 29 | 30 | @Override 31 | public Angle parse(final StringReader stringReader) throws CommandSyntaxException { 32 | if (!stringReader.canRead()) { 33 | throw INCOMPLETE_ANGLE_EXCEPTION.createWithContext(stringReader); 34 | } 35 | boolean relative = WorldCoordinate.isRelative(stringReader); 36 | float angle = stringReader.canRead() && stringReader.peek() != ' ' ? stringReader.readFloat() : 0.0F; 37 | if (Float.isNaN(angle) || Float.isInfinite(angle)) { 38 | throw INVALID_ANGLE_EXCEPTION.createWithContext(stringReader); 39 | } 40 | return new Angle(angle, relative); 41 | } 42 | 43 | @Override 44 | public Collection getExamples() { 45 | return EXAMPLES; 46 | } 47 | 48 | public static final class Angle { 49 | private final float angle; 50 | private final boolean relative; 51 | 52 | Angle(float angle, boolean relative) { 53 | this.angle = angle; 54 | this.relative = relative; 55 | } 56 | 57 | public float getAngle(FabricClientCommandSource source) { 58 | return Mth.wrapDegrees(this.relative ? this.angle + source.getRotation().y : this.angle); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CBlockInput.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import java.util.Set; 4 | import java.util.function.Predicate; 5 | 6 | import net.minecraft.world.level.block.state.BlockState; 7 | import net.minecraft.world.level.block.entity.BlockEntity; 8 | import net.minecraft.world.level.block.state.pattern.BlockInWorld; 9 | import net.minecraft.client.multiplayer.ClientLevel; 10 | import net.minecraft.nbt.CompoundTag; 11 | import net.minecraft.nbt.NbtUtils; 12 | import net.minecraft.world.level.block.state.properties.Property; 13 | import net.minecraft.core.BlockPos; 14 | import org.jetbrains.annotations.Nullable; 15 | 16 | public class CBlockInput implements Predicate { 17 | private final BlockState state; 18 | private final Set> properties; 19 | @Nullable 20 | private final CompoundTag data; 21 | 22 | public CBlockInput(BlockState state, Set> properties, @Nullable CompoundTag data) { 23 | this.state = state; 24 | this.properties = properties; 25 | this.data = data; 26 | } 27 | 28 | public BlockState getState() { 29 | return this.state; 30 | } 31 | 32 | public Set> getDefinedProperties() { 33 | return this.properties; 34 | } 35 | 36 | public boolean test(BlockInWorld blockInWorld) { 37 | BlockState blockState = blockInWorld.getState(); 38 | if (!blockState.is(this.state.getBlock())) { 39 | return false; 40 | } 41 | for (Property property : this.properties) { 42 | if (blockState.getValue(property) != this.state.getValue(property)) { 43 | return false; 44 | } 45 | } 46 | 47 | if (this.data == null) { 48 | return true; 49 | } 50 | BlockEntity blockEntity = blockInWorld.getEntity(); 51 | return blockEntity != null && NbtUtils.compareNbt(this.data, blockEntity.saveWithFullMetadata(blockInWorld.getLevel().registryAccess()), true); 52 | } 53 | 54 | public boolean test(ClientLevel world, BlockPos pos) { 55 | return this.test(new BlockInWorld(world, pos, false)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CBlockPosArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 8 | import com.mojang.brigadier.suggestion.Suggestions; 9 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 10 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 11 | import net.minecraft.client.multiplayer.ClientLevel; 12 | import net.minecraft.commands.SharedSuggestionProvider; 13 | import net.minecraft.commands.Commands; 14 | import net.minecraft.network.chat.Component; 15 | import net.minecraft.core.BlockPos; 16 | import net.minecraft.world.level.ChunkPos; 17 | import net.minecraft.world.level.Level; 18 | 19 | import java.util.Arrays; 20 | import java.util.Collection; 21 | import java.util.Collections; 22 | import java.util.concurrent.CompletableFuture; 23 | 24 | public class CBlockPosArgument implements ArgumentType { 25 | private static final Collection EXAMPLES = Arrays.asList("0 0 0", "~ ~ ~", "^ ^ ^", "^1 ^ ^-5", "~0.5 ~1 ~-5"); 26 | public static final SimpleCommandExceptionType UNLOADED_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("argument.pos.unloaded")); 27 | public static final SimpleCommandExceptionType OUT_OF_WORLD_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("argument.pos.outofworld")); 28 | public static final SimpleCommandExceptionType OUT_OF_BOUNDS_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("argument.pos.outofbounds")); 29 | 30 | public static CBlockPosArgument blockPos() { 31 | return new CBlockPosArgument(); 32 | } 33 | 34 | public static BlockPos getLoadedBlockPos(final CommandContext context, final String name) throws CommandSyntaxException { 35 | ClientLevel clientLevel = context.getSource().getWorld(); 36 | return getLoadedBlockPos(context, clientLevel, name); 37 | } 38 | 39 | public static BlockPos getLoadedBlockPos(final CommandContext context, final ClientLevel level, final String name) throws CommandSyntaxException { 40 | BlockPos blockPos = getBlockPos(context, name); 41 | ChunkPos chunkPos = new ChunkPos(blockPos); 42 | if (!level.getChunkSource().hasChunk(chunkPos.x, chunkPos.z)) { 43 | throw UNLOADED_EXCEPTION.create(); 44 | } 45 | if (!level.isInWorldBounds(blockPos)) { 46 | throw OUT_OF_WORLD_EXCEPTION.create(); 47 | } 48 | return blockPos; 49 | } 50 | 51 | public static BlockPos getBlockPos(final CommandContext context, final String name) { 52 | return context.getArgument(name, CCoordinates.class).getBlockPos(context.getSource()); 53 | } 54 | 55 | public static BlockPos getValidBlockPos(CommandContext context, String name) throws CommandSyntaxException { 56 | BlockPos blockPos = getBlockPos(context, name); 57 | if (!Level.isInSpawnableBounds(blockPos)) { 58 | throw OUT_OF_BOUNDS_EXCEPTION.create(); 59 | } 60 | return blockPos; 61 | } 62 | 63 | @Override 64 | public CCoordinates parse(final StringReader stringReader) throws CommandSyntaxException { 65 | return stringReader.canRead() && stringReader.peek() == '^' ? CLocalCoordinates.parse(stringReader) : CWorldCoordinates.parse(stringReader); 66 | } 67 | 68 | @Override 69 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 70 | if (!(context.getSource() instanceof SharedSuggestionProvider)) { 71 | return Suggestions.empty(); 72 | } 73 | String string = builder.getRemaining(); 74 | Collection collection; 75 | if (!string.isEmpty() && string.charAt(0) == '^') { 76 | collection = Collections.singleton(SharedSuggestionProvider.TextCoordinates.DEFAULT_LOCAL); 77 | } else { 78 | collection = ((SharedSuggestionProvider) context.getSource()).getRelevantCoordinates(); 79 | } 80 | 81 | return SharedSuggestionProvider.suggestCoordinates(string, collection, builder, Commands.createValidator(this::parse)); 82 | } 83 | 84 | @Override 85 | public Collection getExamples() { 86 | return EXAMPLES; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CBlockPredicateArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.suggestion.Suggestions; 8 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 9 | import java.util.Arrays; 10 | import java.util.Collection; 11 | import java.util.Map; 12 | import java.util.Set; 13 | import java.util.Map.Entry; 14 | import java.util.concurrent.CompletableFuture; 15 | import java.util.function.Predicate; 16 | import net.minecraft.world.level.block.Block; 17 | import net.minecraft.world.level.block.state.BlockState; 18 | import net.minecraft.world.level.block.entity.BlockEntity; 19 | import net.minecraft.world.level.block.state.pattern.BlockInWorld; 20 | import net.minecraft.commands.CommandBuildContext; 21 | import net.minecraft.commands.arguments.blocks.BlockStateParser; 22 | import net.minecraft.nbt.CompoundTag; 23 | import net.minecraft.nbt.NbtUtils; 24 | import net.minecraft.core.registries.Registries; 25 | import net.minecraft.core.HolderLookup; 26 | import net.minecraft.core.HolderSet; 27 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 28 | import net.minecraft.world.level.block.state.properties.Property; 29 | import org.jetbrains.annotations.Nullable; 30 | 31 | public class CBlockPredicateArgument implements ArgumentType { 32 | private static final Collection EXAMPLES = Arrays.asList("stone", "minecraft:stone", "stone[foo=bar]", "#stone", "#stone[foo=bar]{baz=nbt}"); 33 | private final HolderLookup holderLookup; 34 | 35 | public CBlockPredicateArgument(CommandBuildContext commandBuildContext) { 36 | this.holderLookup = commandBuildContext.lookupOrThrow(Registries.BLOCK); 37 | } 38 | 39 | public static CBlockPredicateArgument blockPredicate(CommandBuildContext commandBuildContext) { 40 | return new CBlockPredicateArgument(commandBuildContext); 41 | } 42 | 43 | @Override 44 | public CBlockPredicateArgument.BlockPredicate parse(final StringReader stringReader) throws CommandSyntaxException { 45 | return parse(this.holderLookup, stringReader); 46 | } 47 | 48 | public static CBlockPredicateArgument.BlockPredicate parse(HolderLookup holderLookup, StringReader reader) throws CommandSyntaxException { 49 | return BlockStateParser.parseForTesting(holderLookup, reader, true) 50 | .map( 51 | result -> new CBlockPredicateArgument.StatePredicate(result.blockState(), result.properties().keySet(), result.nbt()), 52 | result -> new CBlockPredicateArgument.TagPredicate(result.tag(), result.vagueProperties(), result.nbt()) 53 | ); 54 | } 55 | 56 | public static Predicate getBlockPredicate(final CommandContext context, final String name) { 57 | return context.getArgument(name, CBlockPredicateArgument.BlockPredicate.class); 58 | } 59 | 60 | @Override 61 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 62 | return BlockStateParser.fillSuggestions(this.holderLookup, builder, true, true); 63 | } 64 | 65 | @Override 66 | public Collection getExamples() { 67 | return EXAMPLES; 68 | } 69 | 70 | public interface BlockPredicate extends Predicate { 71 | boolean hasNbt(); 72 | } 73 | 74 | static class StatePredicate implements CBlockPredicateArgument.BlockPredicate { 75 | private final BlockState state; 76 | private final Set> properties; 77 | @Nullable 78 | private final CompoundTag nbt; 79 | 80 | public StatePredicate(BlockState state, Set> properties, @Nullable CompoundTag nbt) { 81 | this.state = state; 82 | this.properties = properties; 83 | this.nbt = nbt; 84 | } 85 | 86 | public boolean test(BlockInWorld blockInWorld) { 87 | BlockState blockState = blockInWorld.getState(); 88 | if (!blockState.is(this.state.getBlock())) { 89 | return false; 90 | } 91 | for (Property property : this.properties) { 92 | if (blockState.getValue(property) != this.state.getValue(property)) { 93 | return false; 94 | } 95 | } 96 | 97 | if (this.nbt == null) { 98 | return true; 99 | } 100 | BlockEntity blockEntity = blockInWorld.getEntity(); 101 | return blockEntity != null && NbtUtils.compareNbt(this.nbt, blockEntity.saveWithFullMetadata(blockInWorld.getLevel().registryAccess()), true); 102 | } 103 | 104 | @Override 105 | public boolean hasNbt() { 106 | return this.nbt != null; 107 | } 108 | } 109 | 110 | static class TagPredicate implements CBlockPredicateArgument.BlockPredicate { 111 | private final HolderSet tag; 112 | @Nullable 113 | private final CompoundTag nbt; 114 | private final Map properties; 115 | 116 | TagPredicate(HolderSet tag, Map properties, @Nullable CompoundTag nbt) { 117 | this.tag = tag; 118 | this.properties = properties; 119 | this.nbt = nbt; 120 | } 121 | 122 | public boolean test(BlockInWorld blockInWorld) { 123 | BlockState blockState = blockInWorld.getState(); 124 | if (!blockState.is(this.tag)) { 125 | return false; 126 | } 127 | for (Entry entry : this.properties.entrySet()) { 128 | Property property = blockState.getBlock().getStateDefinition().getProperty(entry.getKey()); 129 | if (property == null) { 130 | return false; 131 | } 132 | 133 | Comparable comparable = property.getValue(entry.getValue()).orElse(null); 134 | if (comparable == null) { 135 | return false; 136 | } 137 | 138 | if (blockState.getValue(property) != comparable) { 139 | return false; 140 | } 141 | } 142 | 143 | if (this.nbt == null) { 144 | return true; 145 | } 146 | BlockEntity blockEntity = blockInWorld.getEntity(); 147 | return blockEntity != null && NbtUtils.compareNbt(this.nbt, blockEntity.saveWithFullMetadata(blockInWorld.getLevel().registryAccess()), true); 148 | } 149 | 150 | @Override 151 | public boolean hasNbt() { 152 | return this.nbt != null; 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CBlockStateArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.suggestion.Suggestions; 8 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 9 | import java.util.Arrays; 10 | import java.util.Collection; 11 | import java.util.concurrent.CompletableFuture; 12 | 13 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 14 | import net.minecraft.commands.CommandBuildContext; 15 | import net.minecraft.commands.arguments.blocks.BlockStateParser; 16 | import net.minecraft.core.HolderLookup; 17 | import net.minecraft.core.registries.Registries; 18 | import net.minecraft.world.level.block.Block; 19 | 20 | public class CBlockStateArgument implements ArgumentType { 21 | private static final Collection EXAMPLES = Arrays.asList("stone", "minecraft:stone", "stone[foo=bar]", "foo{bar=baz}"); 22 | private final HolderLookup registryWrapper; 23 | 24 | public CBlockStateArgument(CommandBuildContext buildContext) { 25 | this.registryWrapper = buildContext.lookupOrThrow(Registries.BLOCK); 26 | } 27 | 28 | public static CBlockStateArgument blockState(CommandBuildContext buildContext) { 29 | return new CBlockStateArgument(buildContext); 30 | } 31 | 32 | @Override 33 | public CBlockInput parse(final StringReader stringReader) throws CommandSyntaxException { 34 | BlockStateParser.BlockResult blockResult = BlockStateParser.parseForBlock(this.registryWrapper, stringReader, true); 35 | return new CBlockInput(blockResult.blockState(), blockResult.properties().keySet(), blockResult.nbt()); 36 | } 37 | 38 | public static CBlockInput getBlockState(final CommandContext context, final String name) { 39 | return context.getArgument(name, CBlockInput.class); 40 | } 41 | 42 | @Override 43 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 44 | return BlockStateParser.fillSuggestions(this.registryWrapper, builder, false, true); 45 | } 46 | 47 | @Override 48 | public Collection getExamples() { 49 | return EXAMPLES; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CColorArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 8 | import com.mojang.brigadier.suggestion.Suggestions; 9 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 10 | import java.util.Arrays; 11 | import java.util.Collection; 12 | import java.util.concurrent.CompletableFuture; 13 | 14 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 15 | import net.minecraft.commands.SharedSuggestionProvider; 16 | import net.minecraft.network.chat.Component; 17 | import net.minecraft.ChatFormatting; 18 | 19 | public class CColorArgument implements ArgumentType { 20 | private static final Collection EXAMPLES = Arrays.asList("red", "green"); 21 | public static final DynamicCommandExceptionType INVALID_COLOR_EXCEPTION = new DynamicCommandExceptionType(color -> Component.translatableEscape("argument.color.invalid", color)); 22 | 23 | private CColorArgument() { 24 | } 25 | 26 | public static CColorArgument color() { 27 | return new CColorArgument(); 28 | } 29 | 30 | public static ChatFormatting getColor(final CommandContext context, final String name) { 31 | return context.getArgument(name, ChatFormatting.class); 32 | } 33 | 34 | @Override 35 | public ChatFormatting parse(final StringReader stringReader) throws CommandSyntaxException { 36 | String string = stringReader.readUnquotedString(); 37 | ChatFormatting formatting = ChatFormatting.getByName(string); 38 | if (formatting == null || formatting.isFormat()) { 39 | throw INVALID_COLOR_EXCEPTION.createWithContext(stringReader, string); 40 | } 41 | return formatting; 42 | } 43 | 44 | @Override 45 | public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { 46 | return SharedSuggestionProvider.suggest(ChatFormatting.getNames(true, false), builder); 47 | } 48 | 49 | @Override 50 | public Collection getExamples() { 51 | return EXAMPLES; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CColumnPosArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 8 | import com.mojang.brigadier.suggestion.Suggestions; 9 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 10 | import net.minecraft.commands.SharedSuggestionProvider; 11 | import net.minecraft.commands.arguments.coordinates.WorldCoordinate; 12 | import net.minecraft.commands.Commands; 13 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 14 | import net.minecraft.network.chat.Component; 15 | import net.minecraft.core.BlockPos; 16 | import net.minecraft.server.level.ColumnPos; 17 | 18 | import java.util.Arrays; 19 | import java.util.Collection; 20 | import java.util.Collections; 21 | import java.util.concurrent.CompletableFuture; 22 | 23 | public class CColumnPosArgument implements ArgumentType { 24 | private static final Collection EXAMPLES = Arrays.asList("0 0", "~ ~", "~1 ~-2", "^ ^", "^-1 ^0"); 25 | public static final SimpleCommandExceptionType INCOMPLETE_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("argument.pos2d.incomplete")); 26 | 27 | public static CColumnPosArgument columnPos() { 28 | return new CColumnPosArgument(); 29 | } 30 | 31 | public static ColumnPos getColumnPos(final CommandContext context, final String name) { 32 | BlockPos blockPos = context.getArgument(name, CCoordinates.class).getBlockPos(context.getSource()); 33 | return new ColumnPos(blockPos.getX(), blockPos.getZ()); 34 | } 35 | 36 | @Override 37 | public CCoordinates parse(final StringReader stringReader) throws CommandSyntaxException { 38 | int cursor = stringReader.getCursor(); 39 | if (!stringReader.canRead()) { 40 | throw INCOMPLETE_EXCEPTION.createWithContext(stringReader); 41 | } 42 | WorldCoordinate worldCoord = WorldCoordinate.parseInt(stringReader); 43 | if (!stringReader.canRead() || stringReader.peek() != ' ') { 44 | stringReader.setCursor(cursor); 45 | throw INCOMPLETE_EXCEPTION.createWithContext(stringReader); 46 | } 47 | stringReader.skip(); 48 | WorldCoordinate worldCoord2 = WorldCoordinate.parseInt(stringReader); 49 | return new CWorldCoordinates(worldCoord, new WorldCoordinate(true, 0.0), worldCoord2); 50 | } 51 | 52 | @Override 53 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 54 | if (!(context.getSource() instanceof SharedSuggestionProvider)) { 55 | return Suggestions.empty(); 56 | } 57 | String string = builder.getRemaining(); 58 | Collection collection; 59 | if (!string.isEmpty() && string.charAt(0) == '^') { 60 | collection = Collections.singleton(SharedSuggestionProvider.TextCoordinates.DEFAULT_LOCAL); 61 | } else { 62 | collection = ((SharedSuggestionProvider)context.getSource()).getRelevantCoordinates(); 63 | } 64 | 65 | return SharedSuggestionProvider.suggest2DCoordinates(string, collection, builder, Commands.createValidator(this::parse)); 66 | } 67 | 68 | @Override 69 | public Collection getExamples() { 70 | return EXAMPLES; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CComponentArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 5 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 6 | import net.minecraft.commands.CommandBuildContext; 7 | import net.minecraft.core.HolderLookup; 8 | import net.minecraft.nbt.NbtOps; 9 | import net.minecraft.nbt.SnbtGrammar; 10 | import net.minecraft.nbt.Tag; 11 | import net.minecraft.network.chat.Component; 12 | import net.minecraft.network.chat.ComponentSerialization; 13 | import net.minecraft.util.parsing.packrat.commands.CommandArgumentParser; 14 | import net.minecraft.util.parsing.packrat.commands.ParserBasedArgument; 15 | 16 | import java.util.Arrays; 17 | import java.util.Collection; 18 | 19 | public class CComponentArgument extends ParserBasedArgument { 20 | private static final Collection EXAMPLES = Arrays.asList("\"hello world\"", "\"\"", "\"{\"text\":\"hello world\"}", "[\"\"]"); 21 | public static final DynamicCommandExceptionType INVALID_COMPONENT_EXCEPTION = new DynamicCommandExceptionType(text -> Component.translatableEscape("argument.component.invalid", text)); 22 | private static final CommandArgumentParser TAG_PARSER = SnbtGrammar.createParser(NbtOps.INSTANCE); 23 | 24 | private CComponentArgument(HolderLookup.Provider registries) { 25 | super(TAG_PARSER.withCodec(registries.createSerializationContext(NbtOps.INSTANCE), TAG_PARSER, ComponentSerialization.CODEC, INVALID_COMPONENT_EXCEPTION)); 26 | } 27 | 28 | public static CComponentArgument textComponent(CommandBuildContext buildContext) { 29 | return new CComponentArgument(buildContext); 30 | } 31 | 32 | public static Component getComponent(final CommandContext context, final String name) { 33 | return context.getArgument(name, Component.class); 34 | } 35 | 36 | @Override 37 | public Collection getExamples() { 38 | return EXAMPLES; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CCompoundTagArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import net.minecraft.nbt.CompoundTag; 8 | import net.minecraft.nbt.TagParser; 9 | 10 | import java.util.Arrays; 11 | import java.util.Collection; 12 | 13 | public class CCompoundTagArgument implements ArgumentType { 14 | private static final Collection EXAMPLES = Arrays.asList("{}", "{foo=bar}"); 15 | 16 | private CCompoundTagArgument() { 17 | } 18 | 19 | public static CCompoundTagArgument compoundTag() { 20 | return new CCompoundTagArgument(); 21 | } 22 | 23 | public static CompoundTag getCompoundTag(final CommandContext context, final String name) { 24 | return context.getArgument(name, CompoundTag.class); 25 | } 26 | 27 | @Override 28 | public CompoundTag parse(final StringReader stringReader) throws CommandSyntaxException { 29 | return TagParser.parseCompoundAsArgument(stringReader); 30 | } 31 | 32 | @Override 33 | public Collection getExamples() { 34 | return EXAMPLES; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CCoordinates.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.world.phys.Vec2; 6 | import net.minecraft.world.phys.Vec3; 7 | 8 | public interface CCoordinates { 9 | Vec3 getPosition(FabricClientCommandSource source); 10 | 11 | Vec2 getRotation(FabricClientCommandSource source); 12 | 13 | default BlockPos getBlockPos(FabricClientCommandSource source) { 14 | return BlockPos.containing(this.getPosition(source)); 15 | } 16 | 17 | boolean isXRelative(); 18 | 19 | boolean isYRelative(); 20 | 21 | boolean isZRelative(); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CDimensionArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 8 | import com.mojang.brigadier.suggestion.Suggestions; 9 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 10 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 11 | import net.minecraft.commands.SharedSuggestionProvider; 12 | import net.minecraft.resources.ResourceKey; 13 | import net.minecraft.core.registries.Registries; 14 | import net.minecraft.network.chat.Component; 15 | import net.minecraft.resources.ResourceLocation; 16 | import net.minecraft.world.level.Level; 17 | 18 | import java.util.Collection; 19 | import java.util.concurrent.CompletableFuture; 20 | import java.util.stream.Collectors; 21 | import java.util.stream.Stream; 22 | 23 | public class CDimensionArgument implements ArgumentType { 24 | private static final Collection EXAMPLES = Stream.of(Level.OVERWORLD, Level.NETHER) 25 | .map(key -> key.location().toString()) 26 | .collect(Collectors.toList()); 27 | private static final DynamicCommandExceptionType INVALID_DIMENSION_EXCEPTION = new DynamicCommandExceptionType(id -> Component.translatableEscape("argument.dimension.invalid", id)); 28 | 29 | 30 | public static CDimensionArgument dimension() { 31 | return new CDimensionArgument(); 32 | } 33 | 34 | public static ResourceKey getDimension(final CommandContext context, final String name) throws CommandSyntaxException { 35 | ResourceLocation identifier = context.getArgument(name, ResourceLocation.class); 36 | ResourceKey resourceKey = ResourceKey.create(Registries.DIMENSION, identifier); 37 | return context.getSource().levels().stream() 38 | .filter(key -> key.registry().equals(resourceKey.registry()) && key.location().equals(resourceKey.location())) 39 | .findAny().orElseThrow(() -> INVALID_DIMENSION_EXCEPTION.create(identifier)); 40 | } 41 | 42 | @Override 43 | public ResourceLocation parse(final StringReader stringReader) throws CommandSyntaxException { 44 | return ResourceLocation.read(stringReader); 45 | } 46 | 47 | @Override 48 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 49 | return context.getSource() instanceof SharedSuggestionProvider 50 | ? SharedSuggestionProvider.suggestResource(((SharedSuggestionProvider) context.getSource()).levels().stream().map(ResourceKey::location), builder) 51 | : Suggestions.empty(); 52 | } 53 | 54 | @Override 55 | public Collection getExamples() { 56 | return EXAMPLES; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CEntityAnchorArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.google.common.collect.Maps; 4 | import com.mojang.brigadier.StringReader; 5 | import com.mojang.brigadier.arguments.ArgumentType; 6 | import com.mojang.brigadier.context.CommandContext; 7 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 8 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 9 | import com.mojang.brigadier.suggestion.Suggestions; 10 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 11 | import net.minecraft.commands.SharedSuggestionProvider; 12 | import net.minecraft.world.entity.Entity; 13 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 14 | import net.minecraft.network.chat.Component; 15 | import net.minecraft.Util; 16 | import net.minecraft.world.phys.Vec3; 17 | import org.jetbrains.annotations.Nullable; 18 | 19 | import java.util.Arrays; 20 | import java.util.Collection; 21 | import java.util.Map; 22 | import java.util.concurrent.CompletableFuture; 23 | import java.util.function.BiFunction; 24 | 25 | public class CEntityAnchorArgument implements ArgumentType { 26 | private static final Collection EXAMPLES = Arrays.asList("eyes", "feet"); 27 | private static final DynamicCommandExceptionType INVALID_ANCHOR_EXCEPTION = new DynamicCommandExceptionType(name -> Component.translatableEscape("argument.anchor.invalid", name)); 28 | 29 | public static CEntityAnchorArgument entityAnchor() { 30 | return new CEntityAnchorArgument(); 31 | } 32 | 33 | public static EntityAnchor getEntityAnchor(final CommandContext context, final String name) { 34 | return context.getArgument(name, EntityAnchor.class); 35 | } 36 | 37 | @Override 38 | public EntityAnchor parse(final StringReader stringReader) throws CommandSyntaxException { 39 | int cursor = stringReader.getCursor(); 40 | String string = stringReader.readUnquotedString(); 41 | EntityAnchor entityAnchor = EntityAnchor.fromId(string); 42 | if (entityAnchor == null) { 43 | stringReader.setCursor(cursor); 44 | throw INVALID_ANCHOR_EXCEPTION.createWithContext(stringReader, string); 45 | } 46 | return entityAnchor; 47 | } 48 | 49 | @Override 50 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 51 | return SharedSuggestionProvider.suggest(EntityAnchor.ANCHORS.keySet(), builder); 52 | } 53 | 54 | @Override 55 | public Collection getExamples() { 56 | return EXAMPLES; 57 | } 58 | 59 | public enum EntityAnchor { 60 | FEET("feet", (pos, entity) -> pos), 61 | EYES("eyes", (pos, entity) -> new Vec3(pos.x, pos.y + (double) entity.getEyeHeight(), pos.z)); 62 | 63 | static final Map ANCHORS = Util.make(Maps.newHashMap(), map -> { 64 | for (EntityAnchor entityAnchor : values()) { 65 | map.put(entityAnchor.id, entityAnchor); 66 | } 67 | }); 68 | private final String id; 69 | private final BiFunction offset; 70 | 71 | EntityAnchor(final String id, final BiFunction offset) { 72 | this.id = id; 73 | this.offset = offset; 74 | } 75 | 76 | @Nullable 77 | public static CEntityAnchorArgument.EntityAnchor fromId(String id) { 78 | return ANCHORS.get(id); 79 | } 80 | 81 | public Vec3 positionAt(Entity entity) { 82 | return this.offset.apply(entity.position(), entity); 83 | } 84 | 85 | public Vec3 positionAt(FabricClientCommandSource source) { 86 | Entity entity = source.getEntity(); 87 | return entity == null ? source.getPosition() : this.offset.apply(source.getPosition(), entity); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CEntityArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.google.common.collect.Iterables; 4 | import com.mojang.brigadier.StringReader; 5 | import com.mojang.brigadier.arguments.ArgumentType; 6 | import com.mojang.brigadier.context.CommandContext; 7 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 8 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 9 | import com.mojang.brigadier.suggestion.Suggestions; 10 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 11 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 12 | import net.minecraft.client.player.AbstractClientPlayer; 13 | import net.minecraft.commands.SharedSuggestionProvider; 14 | import net.minecraft.world.entity.Entity; 15 | import net.minecraft.network.chat.Component; 16 | 17 | import java.util.Arrays; 18 | import java.util.Collection; 19 | import java.util.List; 20 | import java.util.concurrent.CompletableFuture; 21 | 22 | public class CEntityArgument implements ArgumentType { 23 | private static final Collection EXAMPLES = Arrays.asList("Player", "0123", "@e", "@e[type=foo]", "dd12be42-52a9-4a91-a8a1-11c01849e498"); 24 | public static final SimpleCommandExceptionType TOO_MANY_ENTITIES_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("argument.entity.toomany")); 25 | public static final SimpleCommandExceptionType TOO_MANY_PLAYERS_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("argument.player.toomany")); 26 | public static final SimpleCommandExceptionType PLAYER_SELECTOR_HAS_ENTITIES_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("argument.player.entities")); 27 | public static final SimpleCommandExceptionType ENTITY_NOT_FOUND_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("argument.entity.notfound.entity")); 28 | public static final SimpleCommandExceptionType PLAYER_NOT_FOUND_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("argument.entity.notfound.player")); 29 | public static final SimpleCommandExceptionType NOT_ALLOWED_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("argument.entity.selector.not_allowed")); 30 | final boolean singleTarget; 31 | final boolean playersOnly; 32 | 33 | protected CEntityArgument(boolean singleTarget, boolean playersOnly) { 34 | this.singleTarget = singleTarget; 35 | this.playersOnly = playersOnly; 36 | } 37 | 38 | public static CEntityArgument entity() { 39 | return new CEntityArgument(true, false); 40 | } 41 | 42 | public static Entity getEntity(final CommandContext context, final String name) throws CommandSyntaxException { 43 | return context.getArgument(name, CEntitySelector.class).findSingleEntity(context.getSource()); 44 | } 45 | 46 | public static CEntityArgument entities() { 47 | return new CEntityArgument(false, false); 48 | } 49 | 50 | public static Collection getEntities(final CommandContext context, final String name) throws CommandSyntaxException { 51 | Collection collection = getOptionalEntities(context, name); 52 | if (collection.isEmpty()) { 53 | throw ENTITY_NOT_FOUND_EXCEPTION.create(); 54 | } 55 | return collection; 56 | } 57 | 58 | public static Collection getOptionalEntities(final CommandContext context, final String name) throws CommandSyntaxException { 59 | return context.getArgument(name, CEntitySelector.class).findEntities(context.getSource()); 60 | } 61 | 62 | public static Collection getOptionalPlayers(final CommandContext context, final String name) throws CommandSyntaxException { 63 | return context.getArgument(name, CEntitySelector.class).findPlayers(context.getSource()); 64 | } 65 | 66 | public static CEntityArgument player() { 67 | return new CEntityArgument(true, true); 68 | } 69 | 70 | public static AbstractClientPlayer getPlayer(final CommandContext context, final String name) throws CommandSyntaxException { 71 | return context.getArgument(name, CEntitySelector.class).findSinglePlayer(context.getSource()); 72 | } 73 | 74 | public static CEntityArgument players() { 75 | return new CEntityArgument(false, true); 76 | } 77 | 78 | public static Collection getPlayers(final CommandContext context, final String name) throws CommandSyntaxException { 79 | List list = context.getArgument(name, CEntitySelector.class).findPlayers(context.getSource()); 80 | if (list.isEmpty()) { 81 | throw PLAYER_NOT_FOUND_EXCEPTION.create(); 82 | } 83 | return list; 84 | } 85 | 86 | @Override 87 | public CEntitySelector parse(final StringReader stringReader) throws CommandSyntaxException { 88 | return this.parse(stringReader, true); 89 | } 90 | 91 | public CEntitySelector parse(final StringReader stringReader, final S source) throws CommandSyntaxException { 92 | return this.parse(stringReader, CEntitySelectorParser.allowSelectors(source)); 93 | } 94 | 95 | private CEntitySelector parse(StringReader stringReader, boolean allowSelectors) throws CommandSyntaxException { 96 | CEntitySelectorParser entitySelectorParser = new CEntitySelectorParser(stringReader, allowSelectors); 97 | CEntitySelector entitySelector = entitySelectorParser.parse(); 98 | if (entitySelector.getMaxResults() > 1 && this.singleTarget) { 99 | if (this.playersOnly) { 100 | stringReader.setCursor(0); 101 | throw TOO_MANY_PLAYERS_EXCEPTION.createWithContext(stringReader); 102 | } else { 103 | stringReader.setCursor(0); 104 | throw TOO_MANY_ENTITIES_EXCEPTION.createWithContext(stringReader); 105 | } 106 | } 107 | if (entitySelector.includesEntities() && this.playersOnly && !entitySelector.isSelfSelector()) { 108 | stringReader.setCursor(0); 109 | throw PLAYER_SELECTOR_HAS_ENTITIES_EXCEPTION.createWithContext(stringReader); 110 | } 111 | return entitySelector; 112 | } 113 | 114 | @Override 115 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 116 | if (context.getSource() instanceof SharedSuggestionProvider commandSource) { 117 | StringReader stringReader = new StringReader(builder.getInput()); 118 | stringReader.setCursor(builder.getStart()); 119 | CEntitySelectorParser entitySelectorParser = new CEntitySelectorParser(stringReader, CEntitySelectorParser.allowSelectors(commandSource)); 120 | 121 | try { 122 | entitySelectorParser.parse(); 123 | } catch (CommandSyntaxException ignored) { 124 | } 125 | 126 | return entitySelectorParser.fillSuggestions(builder, builderx -> { 127 | Collection collection = commandSource.getOnlinePlayerNames(); 128 | Iterable iterable = this.playersOnly ? collection : Iterables.concat(collection, commandSource.getSelectedEntities()); 129 | SharedSuggestionProvider.suggest(iterable, builderx); 130 | }); 131 | } 132 | return Suggestions.empty(); 133 | } 134 | 135 | @Override 136 | public Collection getExamples() { 137 | return EXAMPLES; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CEntitySelector.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.google.common.collect.Streams; 5 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 6 | import it.unimi.dsi.fastutil.objects.ObjectArrayList; 7 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 8 | import net.minecraft.Util; 9 | import net.minecraft.client.multiplayer.ClientLevel; 10 | import net.minecraft.client.player.AbstractClientPlayer; 11 | import net.minecraft.util.AbortableIterationConsumer; 12 | import net.minecraft.world.entity.Entity; 13 | import net.minecraft.world.entity.EntityType; 14 | import net.minecraft.advancements.critereon.MinMaxBounds; 15 | import net.minecraft.network.chat.Component; 16 | import net.minecraft.network.chat.ComponentUtils; 17 | import net.minecraft.world.flag.FeatureFlagSet; 18 | import net.minecraft.world.level.entity.EntityTypeTest; 19 | import net.minecraft.world.phys.AABB; 20 | import net.minecraft.world.phys.Vec3; 21 | import org.jetbrains.annotations.Nullable; 22 | 23 | import java.util.Collections; 24 | import java.util.List; 25 | import java.util.UUID; 26 | import java.util.function.BiConsumer; 27 | import java.util.function.Function; 28 | import java.util.function.Predicate; 29 | import java.util.stream.Collectors; 30 | 31 | public class CEntitySelector { 32 | public static final int INFINITE = Integer.MAX_VALUE; 33 | public static final BiConsumer> ORDER_ARBITRARY = (center, entityList) -> {}; 34 | private static final EntityTypeTest ANY_TYPE = new EntityTypeTest<>() { 35 | public Entity tryCast(Entity entity) { 36 | return entity; 37 | } 38 | 39 | @Override 40 | public Class getBaseClass() { 41 | return Entity.class; 42 | } 43 | }; 44 | private final int maxResults; 45 | private final boolean includesEntities; 46 | private final boolean worldLimited; 47 | private final List> contextFreePredicates; 48 | private final MinMaxBounds.Doubles range; 49 | private final Function position; 50 | @Nullable 51 | private final AABB aabb; 52 | private final BiConsumer> order; 53 | private final boolean currentEntity; 54 | @Nullable 55 | private final String playerName; 56 | @Nullable 57 | private final UUID entityUUID; 58 | private final EntityTypeTest type; 59 | private final boolean usesSelector; 60 | 61 | public CEntitySelector(int maxResults, boolean includesEntities, boolean worldLimited, List> contextFreePredicates, MinMaxBounds.Doubles range, Function position, @Nullable AABB aabb, BiConsumer> order, boolean currentEntity, @Nullable String playerName, @Nullable UUID entityUUID, @Nullable EntityType type, boolean usesSelector) { 62 | this.maxResults = maxResults; 63 | this.includesEntities = includesEntities; 64 | this.worldLimited = worldLimited; 65 | this.contextFreePredicates = contextFreePredicates; 66 | this.range = range; 67 | this.position = position; 68 | this.aabb = aabb; 69 | this.order = order; 70 | this.currentEntity = currentEntity; 71 | this.playerName = playerName; 72 | this.entityUUID = entityUUID; 73 | this.type = type == null ? ANY_TYPE : type; 74 | this.usesSelector = usesSelector; 75 | } 76 | 77 | public int getMaxResults() { 78 | return this.maxResults; 79 | } 80 | 81 | public boolean includesEntities() { 82 | return this.includesEntities; 83 | } 84 | 85 | public boolean isSelfSelector() { 86 | return this.currentEntity; 87 | } 88 | 89 | public boolean isWorldLimited() { 90 | return this.worldLimited; 91 | } 92 | 93 | public boolean usesSelector() { 94 | return this.usesSelector; 95 | } 96 | 97 | public Entity findSingleEntity(FabricClientCommandSource source) throws CommandSyntaxException { 98 | List list = this.findEntities(source); 99 | if (list.isEmpty()) { 100 | throw CEntityArgument.ENTITY_NOT_FOUND_EXCEPTION.create(); 101 | } 102 | if (list.size() > 1) { 103 | throw CEntityArgument.TOO_MANY_ENTITIES_EXCEPTION.create(); 104 | } 105 | return list.getFirst(); 106 | } 107 | 108 | public List findEntities(FabricClientCommandSource source) throws CommandSyntaxException { 109 | if (!this.includesEntities) { 110 | return this.findPlayers(source); 111 | } 112 | if (this.playerName != null) { 113 | AbstractClientPlayer abstractClientPlayer = Streams.stream(source.getWorld().entitiesForRendering()) 114 | .filter(entity -> entity instanceof AbstractClientPlayer) 115 | .map(entity -> (AbstractClientPlayer) entity) 116 | .filter(abstractPlayer -> abstractPlayer.getName().getString().equals(this.playerName)) 117 | .findAny().orElse(null); 118 | return abstractClientPlayer == null ? Collections.emptyList() : Lists.newArrayList(abstractClientPlayer); 119 | } 120 | if (this.entityUUID != null) { 121 | Entity foundEntity = Streams.stream(source.getWorld().entitiesForRendering()) 122 | .filter(entity -> entity.getUUID().equals(this.entityUUID)) 123 | .findAny().orElse(null); 124 | return foundEntity == null ? Collections.emptyList() : Lists.newArrayList(foundEntity); 125 | } 126 | 127 | Vec3 vec3 = this.position.apply(source.getPosition()); 128 | AABB aabb = this.getAbsoluteAabb(vec3); 129 | Predicate predicate = this.getPredicate(vec3, aabb, null); 130 | if (this.currentEntity) { 131 | return source.getEntity() != null && predicate.test(source.getEntity()) 132 | ? Lists.newArrayList(source.getEntity()) 133 | : Collections.emptyList(); 134 | } 135 | List list = Lists.newArrayList(); 136 | this.addEntities(list, source.getWorld(), aabb, predicate); 137 | return list; 138 | } 139 | 140 | private void addEntities(List entities, ClientLevel level, @Nullable AABB box, Predicate predicate) { 141 | int resultLimit = this.getResultLimit(); 142 | if (entities.size() < resultLimit) { 143 | if (box != null) { 144 | level.getEntities(this.type, box, predicate, entities, resultLimit); 145 | } else { 146 | level.getEntities().get(this.type, entity -> { 147 | if (predicate.test(entity)) { 148 | entities.add(entity); 149 | if (entities.size() >= maxResults) { 150 | return AbortableIterationConsumer.Continuation.ABORT; 151 | } 152 | } 153 | 154 | return AbortableIterationConsumer.Continuation.CONTINUE; 155 | }); 156 | } 157 | } 158 | } 159 | 160 | private int getResultLimit() { 161 | return this.order == ORDER_ARBITRARY ? this.maxResults : INFINITE; 162 | } 163 | 164 | public AbstractClientPlayer findSinglePlayer(FabricClientCommandSource source) throws CommandSyntaxException { 165 | List list = this.findPlayers(source); 166 | if (list.size() != 1) { 167 | throw CEntityArgument.PLAYER_NOT_FOUND_EXCEPTION.create(); 168 | } 169 | return list.getFirst(); 170 | } 171 | 172 | public List findPlayers(FabricClientCommandSource source) throws CommandSyntaxException { 173 | AbstractClientPlayer abstractClientPlayer; 174 | if (this.playerName != null) { 175 | abstractClientPlayer = Streams.stream(source.getWorld().entitiesForRendering()) 176 | .filter(entity -> entity instanceof AbstractClientPlayer) 177 | .map(entity -> (AbstractClientPlayer) entity) 178 | .filter(abstractPlayer -> abstractPlayer.getName().getString().equals(this.playerName)) 179 | .findAny().orElse(null); 180 | return abstractClientPlayer == null ? Collections.emptyList() : Lists.newArrayList(abstractClientPlayer); 181 | } 182 | if (this.entityUUID != null) { 183 | abstractClientPlayer = Streams.stream(source.getWorld().entitiesForRendering()) 184 | .filter(entity -> entity instanceof AbstractClientPlayer) 185 | .map(entity -> (AbstractClientPlayer) entity) 186 | .filter(entity -> entity.getUUID().equals(this.entityUUID)) 187 | .findAny().orElse(null); 188 | return abstractClientPlayer == null ? Collections.emptyList() : Lists.newArrayList(abstractClientPlayer); 189 | } 190 | Vec3 vec3d = this.position.apply(source.getPosition()); 191 | Predicate predicate = this.getPredicate(vec3d, this.getAbsoluteAabb(vec3d), null); 192 | if (this.currentEntity) { 193 | if (source.getEntity() instanceof AbstractClientPlayer player && predicate.test(player)) { 194 | return Lists.newArrayList(player); 195 | } 196 | 197 | return Collections.emptyList(); 198 | } 199 | List entities = source.getWorld().players().stream() 200 | .filter(predicate) 201 | .limit(this.getResultLimit()) 202 | .collect(Collectors.toList()); 203 | 204 | return this.sortAndLimit(vec3d, entities); 205 | } 206 | 207 | @Nullable 208 | private AABB getAbsoluteAabb(Vec3 pos) { 209 | return this.aabb != null ? this.aabb.move(pos) : null; 210 | } 211 | 212 | private Predicate getPredicate(Vec3 pos, @Nullable AABB box, @Nullable FeatureFlagSet enabledFeatures) { 213 | boolean bl = enabledFeatures != null; 214 | boolean bl2 = box != null; 215 | boolean bl3 = !this.range.isAny(); 216 | int i = (bl ? 1 : 0) + (bl2 ? 1 : 0) + (bl3 ? 1 : 0); 217 | List> list; 218 | if (i == 0) { 219 | list = this.contextFreePredicates; 220 | } else { 221 | List> list2 = new ObjectArrayList<>(this.contextFreePredicates.size() + i); 222 | list2.addAll(this.contextFreePredicates); 223 | if (bl) { 224 | list2.add(entity -> entity.getType().isEnabled(enabledFeatures)); 225 | } 226 | 227 | if (bl2) { 228 | list2.add(entity -> box.intersects(entity.getBoundingBox())); 229 | } 230 | 231 | if (bl3) { 232 | list2.add(entity -> this.range.matchesSqr(entity.distanceToSqr(pos))); 233 | } 234 | 235 | list = list2; 236 | } 237 | 238 | return Util.allOf(list); 239 | } 240 | 241 | private List sortAndLimit(Vec3 pos, List entities) { 242 | if (entities.size() > 1) { 243 | this.order.accept(pos, entities); 244 | } 245 | 246 | return entities.subList(0, Math.min(this.maxResults, entities.size())); 247 | } 248 | 249 | public static Component joinNames(List names) { 250 | return ComponentUtils.formatList(names, Entity::getDisplayName); 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CEnumArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.google.gson.JsonPrimitive; 4 | import com.mojang.brigadier.StringReader; 5 | import com.mojang.brigadier.arguments.ArgumentType; 6 | import com.mojang.brigadier.context.CommandContext; 7 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 8 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 9 | import com.mojang.brigadier.suggestion.Suggestions; 10 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 11 | import com.mojang.serialization.Codec; 12 | import com.mojang.serialization.JsonOps; 13 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 14 | import net.minecraft.commands.SharedSuggestionProvider; 15 | import net.minecraft.network.chat.Component; 16 | import net.minecraft.util.StringRepresentable; 17 | 18 | import java.util.Arrays; 19 | import java.util.Collection; 20 | import java.util.concurrent.CompletableFuture; 21 | import java.util.function.Supplier; 22 | import java.util.stream.Collectors; 23 | 24 | public class CEnumArgument & StringRepresentable> implements ArgumentType { 25 | 26 | private static final DynamicCommandExceptionType INVALID_ENUM_EXCEPTION = new DynamicCommandExceptionType((value) -> Component.translatable("argument.enum.invalid", value)); 27 | private final Codec codec; 28 | private final Supplier valuesSupplier; 29 | 30 | private CEnumArgument(Codec codec, Supplier valuesSupplier) { 31 | this.codec = codec; 32 | this.valuesSupplier = valuesSupplier; 33 | } 34 | 35 | public static & StringRepresentable> CEnumArgument enumArg(Class enumClass) { 36 | return new CEnumArgument<>(StringRepresentable.fromEnum(enumClass::getEnumConstants), enumClass::getEnumConstants); 37 | } 38 | 39 | @SuppressWarnings("unchecked") 40 | public static & StringRepresentable> T getEnum(CommandContext context, String name) { 41 | return (T) context.getArgument(name, Enum.class); 42 | } 43 | 44 | public T parse(final StringReader stringReader) throws CommandSyntaxException { 45 | String string = stringReader.readUnquotedString(); 46 | return this.codec.parse(JsonOps.INSTANCE, new JsonPrimitive(string)).result().orElseThrow(() -> INVALID_ENUM_EXCEPTION.create(string)); 47 | } 48 | 49 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 50 | return SharedSuggestionProvider.suggest(Arrays.stream(this.valuesSupplier.get()).map(StringRepresentable::getSerializedName).map(this::transformValueName).collect(Collectors.toList()), builder); 51 | } 52 | 53 | public Collection getExamples() { 54 | return Arrays.stream(this.valuesSupplier.get()).map(StringRepresentable::getSerializedName).map(this::transformValueName).limit(2L).collect(Collectors.toList()); 55 | } 56 | 57 | protected String transformValueName(String name) { 58 | return name; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CGameProfileArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.mojang.authlib.GameProfile; 5 | import com.mojang.brigadier.StringReader; 6 | import com.mojang.brigadier.arguments.ArgumentType; 7 | import com.mojang.brigadier.context.CommandContext; 8 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 9 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 10 | import com.mojang.brigadier.suggestion.Suggestions; 11 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 12 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 13 | import net.minecraft.client.Minecraft; 14 | import net.minecraft.client.multiplayer.PlayerInfo; 15 | import net.minecraft.client.player.AbstractClientPlayer; 16 | import net.minecraft.commands.SharedSuggestionProvider; 17 | import net.minecraft.network.chat.Component; 18 | 19 | import java.util.Collection; 20 | import java.util.Arrays; 21 | import java.util.Collections; 22 | import java.util.List; 23 | import java.util.concurrent.CompletableFuture; 24 | 25 | public class CGameProfileArgument implements ArgumentType { 26 | private static final Collection EXAMPLES = Arrays.asList("Player", "0123", "dd12be42-52a9-4a91-a8a1-11c01849e498", "@e"); 27 | public static final SimpleCommandExceptionType UNKNOWN_PLAYER_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("argument.player.unknown")); 28 | 29 | private final boolean singleTarget; 30 | 31 | private CGameProfileArgument(boolean singleTarget) { 32 | this.singleTarget = singleTarget; 33 | } 34 | 35 | public static CGameProfileArgument gameProfile() { 36 | return new CGameProfileArgument(false); 37 | } 38 | 39 | public static CGameProfileArgument gameProfile(boolean singleTarget) { 40 | return new CGameProfileArgument(singleTarget); 41 | } 42 | 43 | public static Collection getProfileArgument(final CommandContext context, final String name) throws CommandSyntaxException { 44 | return context.getArgument(name, Result.class).getNames(context.getSource()); 45 | } 46 | 47 | public static GameProfile getSingleProfileArgument(final CommandContext context, final String name) throws CommandSyntaxException { 48 | Collection profiles = context.getArgument(name, Result.class).getNames(context.getSource()); 49 | if (profiles.size() > 1) { 50 | throw CEntityArgument.TOO_MANY_PLAYERS_EXCEPTION.create(); 51 | } 52 | return profiles.iterator().next(); 53 | } 54 | 55 | public CGameProfileArgument.Result parse(final StringReader stringReader, final S source) throws CommandSyntaxException { 56 | return parse(stringReader, CEntitySelectorParser.allowSelectors(source)); 57 | } 58 | 59 | @Override 60 | public Result parse(final StringReader stringReader) throws CommandSyntaxException { 61 | return parse(stringReader, true); 62 | } 63 | 64 | private CGameProfileArgument.Result parse(StringReader stringReader, boolean allowSelectors) throws CommandSyntaxException { 65 | if (stringReader.canRead() && stringReader.peek() == CEntitySelectorParser.SYNTAX_SELECTOR_START) { 66 | CEntitySelectorParser entitySelectorParser = new CEntitySelectorParser(stringReader, allowSelectors); 67 | CEntitySelector entitySelector = entitySelectorParser.parse(); 68 | if (entitySelector.includesEntities()) { 69 | throw CEntityArgument.PLAYER_SELECTOR_HAS_ENTITIES_EXCEPTION.createWithContext(stringReader); 70 | } 71 | if (this.singleTarget && entitySelector.getMaxResults() > 1) { 72 | throw CEntityArgument.TOO_MANY_PLAYERS_EXCEPTION.create(); 73 | } 74 | return new SelectorResult(entitySelector); 75 | } 76 | 77 | int cursor = stringReader.getCursor(); 78 | while (stringReader.canRead() && stringReader.peek() != ' ') { 79 | stringReader.skip(); 80 | } 81 | 82 | String playerName = stringReader.getString().substring(cursor, stringReader.getCursor()); 83 | return source -> Collections.singleton(Minecraft.getInstance().getConnection().getOnlinePlayers().stream() 84 | .map(PlayerInfo::getProfile) 85 | .filter(profile -> profile.getName().equals(playerName)) 86 | .findFirst().orElseThrow(UNKNOWN_PLAYER_EXCEPTION::create)); 87 | } 88 | 89 | @Override 90 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 91 | if (context.getSource() instanceof SharedSuggestionProvider sharedSuggestionProvider) { 92 | StringReader stringReader = new StringReader(builder.getInput()); 93 | stringReader.setCursor(builder.getStart()); 94 | CEntitySelectorParser entitySelectorParser = new CEntitySelectorParser(stringReader, CEntitySelectorParser.allowSelectors(sharedSuggestionProvider)); 95 | 96 | try { 97 | entitySelectorParser.parse(); 98 | } catch (CommandSyntaxException ignored) { 99 | } 100 | 101 | return entitySelectorParser.fillSuggestions(builder, builderx -> SharedSuggestionProvider.suggest(((SharedSuggestionProvider) context.getSource()).getOnlinePlayerNames(), builderx)); 102 | } 103 | return Suggestions.empty(); 104 | } 105 | 106 | @Override 107 | public Collection getExamples() { 108 | return EXAMPLES; 109 | } 110 | 111 | @FunctionalInterface 112 | public interface Result { 113 | Collection getNames(FabricClientCommandSource source) throws CommandSyntaxException; 114 | } 115 | 116 | public static class SelectorResult implements Result { 117 | private final CEntitySelector selector; 118 | 119 | public SelectorResult(CEntitySelector selector) { 120 | this.selector = selector; 121 | } 122 | 123 | @Override 124 | public Collection getNames(FabricClientCommandSource fabricClientCommandSource) throws CommandSyntaxException { 125 | List list = this.selector.findPlayers(fabricClientCommandSource); 126 | if (list.isEmpty()) { 127 | throw CEntityArgument.PLAYER_NOT_FOUND_EXCEPTION.create(); 128 | } 129 | List list2 = Lists.newArrayList(); 130 | 131 | for (AbstractClientPlayer abstractClientPlayer : list) { 132 | list2.add(abstractClientPlayer.getGameProfile()); 133 | } 134 | 135 | return list2; 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CItemArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.suggestion.Suggestions; 8 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 9 | import net.minecraft.commands.CommandBuildContext; 10 | import net.minecraft.commands.arguments.item.ItemInput; 11 | import net.minecraft.commands.arguments.item.ItemParser; 12 | 13 | import java.util.Arrays; 14 | import java.util.Collection; 15 | import java.util.concurrent.CompletableFuture; 16 | 17 | public class CItemArgument implements ArgumentType { 18 | private static final Collection EXAMPLES = Arrays.asList("stick", "minecraft:stick", "stick{foo=bar}"); 19 | private final ItemParser reader; 20 | 21 | public CItemArgument(CommandBuildContext commandBuildContext) { 22 | this.reader = new ItemParser(commandBuildContext); 23 | } 24 | 25 | public static CItemArgument itemStack(CommandBuildContext commandBuildContext) { 26 | return new CItemArgument(commandBuildContext); 27 | } 28 | 29 | public static ItemInput getItemStackArgument(final CommandContext context, final String name) { 30 | return context.getArgument(name, ItemInput.class); 31 | } 32 | 33 | @Override 34 | public ItemInput parse(final StringReader stringReader) throws CommandSyntaxException { 35 | ItemParser.ItemResult itemResult = this.reader.parse(stringReader); 36 | return new ItemInput(itemResult.item(), itemResult.components()); 37 | } 38 | 39 | @Override 40 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 41 | return this.reader.fillSuggestions(builder); 42 | } 43 | 44 | @Override 45 | public Collection getExamples() { 46 | return EXAMPLES; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CItemPredicateArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.ImmutableStringReader; 4 | import com.mojang.brigadier.StringReader; 5 | import com.mojang.brigadier.arguments.ArgumentType; 6 | import com.mojang.brigadier.context.CommandContext; 7 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 8 | import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType; 9 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 10 | import com.mojang.brigadier.suggestion.Suggestions; 11 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 12 | import com.mojang.serialization.Codec; 13 | import com.mojang.serialization.DataResult; 14 | import com.mojang.serialization.Decoder; 15 | import java.util.Arrays; 16 | import java.util.Collection; 17 | import java.util.List; 18 | import java.util.Map; 19 | import java.util.Objects; 20 | import java.util.concurrent.CompletableFuture; 21 | import java.util.function.Predicate; 22 | import java.util.stream.Collectors; 23 | import java.util.stream.Stream; 24 | 25 | import com.mojang.serialization.Dynamic; 26 | import net.minecraft.commands.CommandBuildContext; 27 | import net.minecraft.core.component.predicates.DataComponentPredicate; 28 | import net.minecraft.util.parsing.packrat.commands.Grammar; 29 | import net.minecraft.commands.arguments.item.ComponentPredicateParser; 30 | import net.minecraft.core.component.DataComponentType; 31 | import net.minecraft.world.item.Item; 32 | import net.minecraft.world.item.ItemStack; 33 | import net.minecraft.advancements.critereon.MinMaxBounds; 34 | import net.minecraft.resources.ResourceKey; 35 | import net.minecraft.core.registries.Registries; 36 | import net.minecraft.resources.RegistryOps; 37 | import net.minecraft.core.HolderLookup; 38 | import net.minecraft.core.Holder; 39 | import net.minecraft.core.HolderSet; 40 | import net.minecraft.tags.TagKey; 41 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 42 | import net.minecraft.network.chat.Component; 43 | import net.minecraft.resources.ResourceLocation; 44 | import net.minecraft.Util; 45 | 46 | public class CItemPredicateArgument implements ArgumentType { 47 | private static final Collection EXAMPLES = Arrays.asList("stick", "minecraft:stick", "#stick", "#stick{foo:'bar'}"); 48 | static final DynamicCommandExceptionType INVALID_ITEM_ID_EXCEPTION = new DynamicCommandExceptionType(object -> Component.translatableEscape("argument.item.id.invalid", object)); 49 | static final DynamicCommandExceptionType UNKNOWN_ITEM_TAG_EXCEPTION = new DynamicCommandExceptionType(object -> Component.translatableEscape("arguments.item.tag.unknown", object)); 50 | static final DynamicCommandExceptionType UNKNOWN_ITEM_COMPONENT_EXCEPTION = new DynamicCommandExceptionType(object -> Component.translatableEscape("arguments.item.component.unknown", object)); 51 | static final Dynamic2CommandExceptionType MALFORMED_ITEM_COMPONENT_EXCEPTION = new Dynamic2CommandExceptionType((object, object2) -> Component.translatableEscape("arguments.item.component.malformed", object, object2)); 52 | static final DynamicCommandExceptionType UNKNOWN_ITEM_PREDICATE_EXCEPTION = new DynamicCommandExceptionType(object -> Component.translatableEscape("arguments.item.predicate.unknown", object)); 53 | static final Dynamic2CommandExceptionType MALFORMED_ITEM_PREDICATE_EXCEPTION = new Dynamic2CommandExceptionType((object, object2) -> Component.translatableEscape("arguments.item.predicate.malformed", object, object2)); 54 | private static final ResourceLocation COUNT_ID = ResourceLocation.withDefaultNamespace("count"); 55 | static final Map SPECIAL_COMPONENT_CHECKS = Stream.of(new ComponentWrapper(COUNT_ID, stack -> true, MinMaxBounds.Ints.CODEC.map(range -> stack -> range.matches(stack.getCount())))).collect(Collectors.toUnmodifiableMap(ComponentWrapper::id, check -> check)); 56 | static final Map SPECIAL_SUB_PREDICATE_CHECKS = Stream.of(new PredicateWrapper(COUNT_ID, MinMaxBounds.Ints.CODEC.map(range -> stack -> range.matches(stack.getCount())))).collect(Collectors.toUnmodifiableMap(PredicateWrapper::id, check -> check)); 57 | private final Grammar>> parser; 58 | 59 | public CItemPredicateArgument(CommandBuildContext commandBuildContext) { 60 | Context context = new Context(commandBuildContext); 61 | this.parser = ComponentPredicateParser.createGrammar(context); 62 | } 63 | 64 | public static CItemPredicateArgument itemPredicate(CommandBuildContext commandRegistryAccess) { 65 | return new CItemPredicateArgument(commandRegistryAccess); 66 | } 67 | 68 | @Override 69 | public CItemStackPredicateArgument parse(final StringReader stringReader) throws CommandSyntaxException { 70 | return Util.allOf(this.parser.parseForCommands(stringReader))::test; 71 | } 72 | 73 | public static CItemStackPredicateArgument getItemStackPredicate(final CommandContext context, String name) { 74 | return context.getArgument(name, CItemStackPredicateArgument.class); 75 | } 76 | 77 | @Override 78 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 79 | return this.parser.parseForSuggestions(builder); 80 | } 81 | 82 | @Override 83 | public Collection getExamples() { 84 | return EXAMPLES; 85 | } 86 | 87 | record ComponentWrapper(ResourceLocation id, Predicate presenceChecker, Decoder> valueChecker) { 88 | public static ComponentWrapper read(ImmutableStringReader reader, ResourceLocation id, DataComponentType type) throws CommandSyntaxException { 89 | Codec codec = type.codec(); 90 | if (codec == null) { 91 | throw UNKNOWN_ITEM_COMPONENT_EXCEPTION.createWithContext(reader, id); 92 | } 93 | return new ComponentWrapper(id, stack -> stack.has(type), codec.map(expected -> stack -> { 94 | T object2 = stack.get(type); 95 | return Objects.equals(expected, object2); 96 | })); 97 | } 98 | 99 | public Predicate createPredicate(ImmutableStringReader reader, Dynamic nbt) throws CommandSyntaxException { 100 | DataResult> dataResult = this.valueChecker.parse(nbt); 101 | return dataResult.getOrThrow(error -> MALFORMED_ITEM_COMPONENT_EXCEPTION.createWithContext(reader, this.id.toString(), error)); 102 | } 103 | } 104 | 105 | public interface CItemStackPredicateArgument extends Predicate { 106 | } 107 | 108 | record PredicateWrapper(ResourceLocation id, Decoder> type) { 109 | public PredicateWrapper(Holder.Reference> type) { 110 | this(type.key().location(), type.value().codec().map(predicate -> predicate::matches)); 111 | } 112 | 113 | public Predicate createPredicate(ImmutableStringReader reader, Dynamic nbt) throws CommandSyntaxException { 114 | DataResult> dataResult = this.type.parse(nbt); 115 | return dataResult.getOrThrow(error -> MALFORMED_ITEM_PREDICATE_EXCEPTION.createWithContext(reader, this.id.toString(), error)); 116 | } 117 | } 118 | 119 | static class Context implements ComponentPredicateParser.Context, ComponentWrapper, PredicateWrapper> { 120 | private final HolderLookup.Provider registries; 121 | private final HolderLookup.RegistryLookup itemHolderLookup; 122 | private final HolderLookup.RegistryLookup> dataComponentTypeHolderLookup; 123 | private final HolderLookup.RegistryLookup> itemSubPredicateTypeHolderLookup; 124 | 125 | Context(HolderLookup.Provider registries) { 126 | this.registries = registries; 127 | this.itemHolderLookup = registries.lookupOrThrow(Registries.ITEM); 128 | this.dataComponentTypeHolderLookup = registries.lookupOrThrow(Registries.DATA_COMPONENT_TYPE); 129 | this.itemSubPredicateTypeHolderLookup = registries.lookupOrThrow(Registries.DATA_COMPONENT_PREDICATE_TYPE); 130 | } 131 | 132 | @Override 133 | public Predicate forElementType(ImmutableStringReader immutableStringReader, ResourceLocation id) throws CommandSyntaxException { 134 | Holder.Reference reference = this.itemHolderLookup 135 | .get(ResourceKey.create(Registries.ITEM, id)) 136 | .orElseThrow(() -> INVALID_ITEM_ID_EXCEPTION.createWithContext(immutableStringReader, id)); 137 | return stack -> stack.is(reference); 138 | } 139 | 140 | @Override 141 | public Predicate forTagType(ImmutableStringReader immutableStringReader, ResourceLocation id) throws CommandSyntaxException { 142 | HolderSet registryEntryList = this.itemHolderLookup 143 | .get(TagKey.create(Registries.ITEM, id)) 144 | .orElseThrow(() -> UNKNOWN_ITEM_TAG_EXCEPTION.createWithContext(immutableStringReader, id)); 145 | return stack -> stack.is(registryEntryList); 146 | } 147 | 148 | @Override 149 | public ComponentWrapper lookupComponentType(ImmutableStringReader immutableStringReader, ResourceLocation id) throws CommandSyntaxException { 150 | ComponentWrapper componentWrapper = SPECIAL_COMPONENT_CHECKS.get(id); 151 | if (componentWrapper != null) { 152 | return componentWrapper; 153 | } 154 | DataComponentType dataComponentType = this.dataComponentTypeHolderLookup 155 | .get(ResourceKey.create(Registries.DATA_COMPONENT_TYPE, id)) 156 | .map(Holder::value) 157 | .orElseThrow(() -> UNKNOWN_ITEM_COMPONENT_EXCEPTION.createWithContext(immutableStringReader, id)); 158 | return ComponentWrapper.read(immutableStringReader, id, dataComponentType); 159 | } 160 | 161 | @Override 162 | public Predicate createComponentTest(ImmutableStringReader immutableStringReader, ComponentWrapper componentWrapper, Dynamic nbtElement) throws CommandSyntaxException { 163 | return componentWrapper.createPredicate(immutableStringReader, RegistryOps.injectRegistryContext(nbtElement, registries)); 164 | } 165 | 166 | @Override 167 | public Predicate createComponentTest(ImmutableStringReader immutableStringReader, ComponentWrapper componentWrapper) { 168 | return componentWrapper.presenceChecker; 169 | } 170 | 171 | @Override 172 | public PredicateWrapper lookupPredicateType(ImmutableStringReader immutableStringReader, ResourceLocation id) throws CommandSyntaxException { 173 | PredicateWrapper predicateWrapper = SPECIAL_SUB_PREDICATE_CHECKS.get(id); 174 | return predicateWrapper != null ? predicateWrapper : this.itemSubPredicateTypeHolderLookup 175 | .get(ResourceKey.create(Registries.DATA_COMPONENT_PREDICATE_TYPE, id)) 176 | .map(PredicateWrapper::new) 177 | .orElseThrow(() -> UNKNOWN_ITEM_PREDICATE_EXCEPTION.createWithContext(immutableStringReader, id)); 178 | } 179 | 180 | @Override 181 | public Predicate createPredicateTest(ImmutableStringReader immutableStringReader, PredicateWrapper predicateWrapper, Dynamic nbtElement) throws CommandSyntaxException { 182 | return predicateWrapper.createPredicate(immutableStringReader, RegistryOps.injectRegistryContext(nbtElement, registries)); 183 | } 184 | 185 | @Override 186 | public Stream listElementTypes() { 187 | return this.itemHolderLookup.listElementIds().map(ResourceKey::location); 188 | } 189 | 190 | @Override 191 | public Stream listTagTypes() { 192 | return this.itemHolderLookup.listTagIds().map(TagKey::location); 193 | } 194 | 195 | @Override 196 | public Stream listComponentTypes() { 197 | return Stream.concat( 198 | SPECIAL_COMPONENT_CHECKS.keySet().stream(), 199 | this.dataComponentTypeHolderLookup 200 | .listElements() 201 | .filter(entry -> !entry.value().isTransient()) 202 | .map(entry -> entry.key().location()) 203 | ); 204 | } 205 | 206 | @Override 207 | public Stream listPredicateTypes() { 208 | return Stream.concat(SPECIAL_SUB_PREDICATE_CHECKS.keySet().stream(), this.itemSubPredicateTypeHolderLookup.listElementIds().map(ResourceKey::location)); 209 | } 210 | 211 | @Override 212 | public Predicate negate(Predicate predicate) { 213 | return predicate.negate(); 214 | } 215 | 216 | @Override 217 | public Predicate anyOf(List> list) { 218 | return Util.anyOf(list); 219 | } 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CLocalCoordinates.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 5 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 6 | import net.minecraft.commands.arguments.coordinates.WorldCoordinate; 7 | import net.minecraft.util.Mth; 8 | import net.minecraft.world.phys.Vec2; 9 | import net.minecraft.world.phys.Vec3; 10 | 11 | import java.util.Objects; 12 | 13 | public class CLocalCoordinates implements CCoordinates { 14 | private final double x; 15 | private final double y; 16 | private final double z; 17 | 18 | public CLocalCoordinates(double x, double y, double z) { 19 | this.x = x; 20 | this.y = y; 21 | this.z = z; 22 | } 23 | 24 | @Override 25 | public Vec3 getPosition(FabricClientCommandSource source) { 26 | Vec2 vec2 = source.getRotation(); 27 | Vec3 vec3 = CEntityAnchorArgument.EntityAnchor.FEET.positionAt(source); 28 | float f = Mth.cos((vec2.y + 90.0F) * (float) (Math.PI / 180.0)); 29 | float g = Mth.sin((vec2.y + 90.0F) * (float) (Math.PI / 180.0)); 30 | float h = Mth.cos(-vec2.x * (float) (Math.PI / 180.0)); 31 | float i = Mth.sin(-vec2.x * (float) (Math.PI / 180.0)); 32 | float j = Mth.cos((-vec2.x + 90.0F) * (float) (Math.PI / 180.0)); 33 | float k = Mth.sin((-vec2.x + 90.0F) * (float) (Math.PI / 180.0)); 34 | Vec3 vec32 = new Vec3(f * h, i, g * h); 35 | Vec3 vec33 = new Vec3(f * j, k, g * j); 36 | Vec3 vec34 = vec32.cross(vec33).scale(-1.0); 37 | double d = vec32.x * this.z + vec33.x * this.y + vec34.x * this.x; 38 | double e = vec32.y * this.z + vec33.y * this.y + vec34.y * this.x; 39 | double l = vec32.z * this.z + vec33.z * this.y + vec34.z * this.x; 40 | return new Vec3(vec3.x + d, vec3.y + e, vec3.z + l); 41 | } 42 | 43 | @Override 44 | public Vec2 getRotation(FabricClientCommandSource source) { 45 | return Vec2.ZERO; 46 | } 47 | 48 | @Override 49 | public boolean isXRelative() { 50 | return true; 51 | } 52 | 53 | @Override 54 | public boolean isYRelative() { 55 | return true; 56 | } 57 | 58 | @Override 59 | public boolean isZRelative() { 60 | return true; 61 | } 62 | 63 | public static CLocalCoordinates parse(StringReader reader) throws CommandSyntaxException { 64 | int cursor = reader.getCursor(); 65 | double d = readCoordinate(reader, cursor); 66 | if (!reader.canRead() || reader.peek() != ' ') { 67 | reader.setCursor(cursor); 68 | throw CVec3Argument.INCOMPLETE_EXCEPTION.createWithContext(reader); 69 | } 70 | reader.skip(); 71 | double e = readCoordinate(reader, cursor); 72 | if (!reader.canRead() || reader.peek() != ' ') { 73 | reader.setCursor(cursor); 74 | throw CVec3Argument.INCOMPLETE_EXCEPTION.createWithContext(reader); 75 | } 76 | reader.skip(); 77 | double f = readCoordinate(reader, cursor); 78 | return new CLocalCoordinates(d, e, f); 79 | } 80 | 81 | private static double readCoordinate(StringReader reader, int startingCursorPos) throws CommandSyntaxException { 82 | if (!reader.canRead()) { 83 | throw WorldCoordinate.ERROR_EXPECTED_DOUBLE.createWithContext(reader); 84 | } 85 | if (reader.peek() != '^') { 86 | reader.setCursor(startingCursorPos); 87 | throw CVec3Argument.MIXED_COORDINATE_EXCEPTION.createWithContext(reader); 88 | } 89 | reader.skip(); 90 | return reader.canRead() && reader.peek() != ' ' ? reader.readDouble() : 0.0; 91 | } 92 | 93 | public boolean equals(Object o) { 94 | if (this == o) { 95 | return true; 96 | } 97 | if (!(o instanceof CLocalCoordinates localCoordinates)) { 98 | return false; 99 | } 100 | return this.x == localCoordinates.x && this.y == localCoordinates.y && this.z == localCoordinates.z; 101 | } 102 | 103 | public int hashCode() { 104 | return Objects.hash(this.x, this.y, this.z); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CMessageArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.mojang.brigadier.StringReader; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType; 8 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 9 | import net.minecraft.commands.arguments.SignedArgument; 10 | import net.minecraft.network.chat.MutableComponent; 11 | import net.minecraft.network.chat.Component; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.util.Arrays; 15 | import java.util.Collection; 16 | import java.util.List; 17 | 18 | public class CMessageArgument implements SignedArgument { 19 | private static final Collection EXAMPLES = Arrays.asList("Hello world!", "foo", "@e", "Hello @p :)"); 20 | static final Dynamic2CommandExceptionType TOO_LONG = new Dynamic2CommandExceptionType((length, maxLength) -> Component.translatableEscape("argument.message.too_long", length, maxLength)); 21 | 22 | public static CMessageArgument message() { 23 | return new CMessageArgument(); 24 | } 25 | 26 | public static Component getMessage(final CommandContext context, final String name) throws CommandSyntaxException { 27 | CMessageArgument.Message message = context.getArgument(name, Message.class); 28 | return message.resolveComponent(context.getSource()); 29 | } 30 | 31 | public CMessageArgument.Message parse(final StringReader stringReader) throws CommandSyntaxException { 32 | return CMessageArgument.Message.parseText(stringReader, true); 33 | } 34 | 35 | public CMessageArgument.Message parse(final StringReader stringReader, final @Nullable S source) throws CommandSyntaxException { 36 | return CMessageArgument.Message.parseText(stringReader, CEntitySelectorParser.allowSelectors(source)); 37 | } 38 | 39 | public Collection getExamples() { 40 | return EXAMPLES; 41 | } 42 | 43 | public record Message(String text, CMessageArgument.Part[] parts) { 44 | 45 | Component resolveComponent(FabricClientCommandSource fabricClientCommandSource) throws CommandSyntaxException { 46 | return this.toComponent(fabricClientCommandSource, CEntitySelectorParser.allowSelectors(fabricClientCommandSource)); 47 | } 48 | 49 | public Component toComponent(FabricClientCommandSource fabricClientCommandSource, boolean allowSelectors) throws CommandSyntaxException { 50 | if (this.parts.length == 0 || !allowSelectors) { 51 | return Component.literal(this.text); 52 | } 53 | 54 | MutableComponent mutableComponent = Component.literal(this.text.substring(0, this.parts[0].start())); 55 | int i = this.parts[0].start(); 56 | for (Part part : this.parts) { 57 | Component component = part.toComponent(fabricClientCommandSource); 58 | if (i < part.start()) { 59 | mutableComponent.append(this.text.substring(i, part.start())); 60 | } 61 | mutableComponent.append(component); 62 | i = part.end(); 63 | } 64 | 65 | if (i < this.text.length()) { 66 | mutableComponent.append(this.text.substring(i)); 67 | } 68 | 69 | return mutableComponent; 70 | } 71 | 72 | public static CMessageArgument.Message parseText(StringReader stringReader, boolean allowSelectors) throws CommandSyntaxException { 73 | if (stringReader.getRemainingLength() > 256) { 74 | throw CMessageArgument.TOO_LONG.create(stringReader.getRemainingLength(), 256); 75 | } 76 | String string = stringReader.getRemaining(); 77 | if (!allowSelectors) { 78 | stringReader.setCursor(stringReader.getTotalLength()); 79 | return new CMessageArgument.Message(string, new CMessageArgument.Part[0]); 80 | } 81 | List list = Lists.newArrayList(); 82 | int cursor = stringReader.getCursor(); 83 | while (true) { 84 | int j; 85 | CEntitySelector entitySelector; 86 | while (true) { 87 | if (!stringReader.canRead()) { 88 | return new CMessageArgument.Message(string, list.toArray(new CMessageArgument.Part[0])); 89 | } 90 | if (stringReader.peek() == CEntitySelectorParser.SYNTAX_SELECTOR_START) { 91 | j = stringReader.getCursor(); 92 | 93 | try { 94 | CEntitySelectorParser entitySelectorParser = new CEntitySelectorParser(stringReader, true); 95 | entitySelector = entitySelectorParser.parse(); 96 | break; 97 | } catch (CommandSyntaxException var8) { 98 | if (var8.getType() != CEntitySelectorParser.ERROR_MISSING_SELECTOR_TYPE 99 | && var8.getType() != CEntitySelectorParser.ERROR_UNKNOWN_SELECTOR_TYPE) { 100 | throw var8; 101 | } 102 | stringReader.setCursor(j + 1); 103 | } 104 | } else { 105 | stringReader.skip(); 106 | } 107 | } 108 | list.add(new CMessageArgument.Part(j - cursor, stringReader.getCursor() - cursor, entitySelector)); 109 | } 110 | } 111 | } 112 | 113 | public record Part(int start, int end, CEntitySelector selector) { 114 | public Component toComponent(FabricClientCommandSource fabricClientCommandSource) throws CommandSyntaxException { 115 | return CEntitySelector.joinNames(this.selector.findEntities(fabricClientCommandSource)); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CNbtTagArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import net.minecraft.nbt.NbtOps; 5 | import net.minecraft.nbt.SnbtGrammar; 6 | import net.minecraft.nbt.Tag; 7 | import net.minecraft.util.parsing.packrat.commands.CommandArgumentParser; 8 | import net.minecraft.util.parsing.packrat.commands.ParserBasedArgument; 9 | 10 | import java.util.Arrays; 11 | import java.util.Collection; 12 | 13 | public class CNbtTagArgument extends ParserBasedArgument { 14 | private static final Collection EXAMPLES = Arrays.asList("0", "0b", "0l", "0.0", "\"foo\"", "{foo=bar}", "[0]"); 15 | private static final CommandArgumentParser TAG_PARSER = SnbtGrammar.createParser(NbtOps.INSTANCE); 16 | 17 | private CNbtTagArgument() { 18 | super(TAG_PARSER); 19 | } 20 | 21 | public static CNbtTagArgument nbtTag() { 22 | return new CNbtTagArgument(); 23 | } 24 | 25 | public static Tag getNbtTag(final CommandContext context, final String name) { 26 | return context.getArgument(name, Tag.class); 27 | } 28 | 29 | @Override 30 | public Collection getExamples() { 31 | return EXAMPLES; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CObjectiveArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 8 | import com.mojang.brigadier.suggestion.Suggestions; 9 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 10 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 11 | import net.minecraft.commands.SharedSuggestionProvider; 12 | import net.minecraft.world.scores.Scoreboard; 13 | import net.minecraft.world.scores.Objective; 14 | import net.minecraft.network.chat.Component; 15 | 16 | import java.util.Arrays; 17 | import java.util.Collection; 18 | import java.util.concurrent.CompletableFuture; 19 | 20 | public class CObjectiveArgument implements ArgumentType { 21 | private static final Collection EXAMPLES = Arrays.asList("foo", "*", "012"); 22 | private static final DynamicCommandExceptionType UNKNOWN_OBJECTIVE_EXCEPTION = new DynamicCommandExceptionType(name -> Component.translatableEscape("arguments.objective.notFound", name)); 23 | private static final DynamicCommandExceptionType READONLY_OBJECTIVE_EXCEPTION = new DynamicCommandExceptionType(name -> Component.translatableEscape("arguments.objective.readonly", name)); 24 | 25 | public static CObjectiveArgument objective() { 26 | return new CObjectiveArgument(); 27 | } 28 | 29 | public static Objective getObjective(final CommandContext context, final String name) throws CommandSyntaxException { 30 | String string = context.getArgument(name, String.class); 31 | Scoreboard scoreboard = context.getSource().getWorld().getScoreboard(); 32 | Objective objective = scoreboard.getObjective(string); 33 | if (objective == null) { 34 | throw UNKNOWN_OBJECTIVE_EXCEPTION.create(string); 35 | } 36 | return objective; 37 | } 38 | 39 | public static Objective getWritableObjective(final CommandContext context, final String name) throws CommandSyntaxException { 40 | Objective scoreboardObjective = getObjective(context, name); 41 | if (scoreboardObjective.getCriteria().isReadOnly()) { 42 | throw READONLY_OBJECTIVE_EXCEPTION.create(scoreboardObjective.getName()); 43 | } 44 | return scoreboardObjective; 45 | } 46 | 47 | @Override 48 | public String parse(final StringReader stringReader) throws CommandSyntaxException { 49 | return stringReader.readUnquotedString(); 50 | } 51 | 52 | @Override 53 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 54 | if (context.getSource() instanceof FabricClientCommandSource fabricClientCommandSource) { 55 | return SharedSuggestionProvider.suggest(fabricClientCommandSource.getWorld().getScoreboard().getObjectiveNames(), builder); 56 | } else { 57 | return context.getSource() instanceof SharedSuggestionProvider commandSource ? commandSource.customSuggestion(context) : Suggestions.empty(); 58 | } 59 | } 60 | 61 | @Override 62 | public Collection getExamples() { 63 | return EXAMPLES; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CObjectiveCriteriaArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.mojang.brigadier.StringReader; 5 | import com.mojang.brigadier.arguments.ArgumentType; 6 | import com.mojang.brigadier.context.CommandContext; 7 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 8 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 9 | import com.mojang.brigadier.suggestion.Suggestions; 10 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 11 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 12 | import net.minecraft.commands.SharedSuggestionProvider; 13 | import net.minecraft.core.registries.BuiltInRegistries; 14 | import net.minecraft.world.scores.criteria.ObjectiveCriteria; 15 | import net.minecraft.stats.Stat; 16 | import net.minecraft.stats.StatType; 17 | import net.minecraft.network.chat.Component; 18 | 19 | import java.util.Arrays; 20 | import java.util.Collection; 21 | import java.util.List; 22 | import java.util.concurrent.CompletableFuture; 23 | 24 | public class CObjectiveCriteriaArgument implements ArgumentType { 25 | private static final Collection EXAMPLES = Arrays.asList("foo", "foo.bar.baz", "minecraft:foo"); 26 | public static final DynamicCommandExceptionType INVALID_CRITERION_EXCEPTION = new DynamicCommandExceptionType(name -> Component.translatableEscape("argument.criteria.invalid", name)); 27 | 28 | private CObjectiveCriteriaArgument() { 29 | } 30 | 31 | public static CObjectiveCriteriaArgument criteria() { 32 | return new CObjectiveCriteriaArgument(); 33 | } 34 | 35 | public static ObjectiveCriteria getCriteria(final CommandContext context, final String name) { 36 | return context.getArgument(name, ObjectiveCriteria.class); 37 | } 38 | 39 | @Override 40 | public ObjectiveCriteria parse(final StringReader stringReader) throws CommandSyntaxException { 41 | int cursor = stringReader.getCursor(); 42 | 43 | while (stringReader.canRead() && stringReader.peek() != ' ') { 44 | stringReader.skip(); 45 | } 46 | 47 | String string = stringReader.getString().substring(cursor, stringReader.getCursor()); 48 | return ObjectiveCriteria.byName(string).orElseThrow(() -> { 49 | stringReader.setCursor(cursor); 50 | return INVALID_CRITERION_EXCEPTION.createWithContext(stringReader, string); 51 | }); 52 | } 53 | 54 | @Override 55 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 56 | List list = Lists.newArrayList(ObjectiveCriteria.getCustomCriteriaNames()); 57 | 58 | for (StatType statType : BuiltInRegistries.STAT_TYPE) { 59 | for (var object : statType.getRegistry()) { 60 | String string = this.getStatName(statType, object); 61 | list.add(string); 62 | } 63 | } 64 | 65 | return SharedSuggestionProvider.suggest(list, builder); 66 | } 67 | 68 | public String getStatName(StatType stat, Object value) { 69 | return Stat.buildName(stat, (T) value); 70 | } 71 | 72 | @Override 73 | public Collection getExamples() { 74 | return EXAMPLES; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/COperationArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 8 | import com.mojang.brigadier.suggestion.Suggestions; 9 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 10 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 11 | import net.minecraft.commands.SharedSuggestionProvider; 12 | import net.minecraft.world.scores.ScoreAccess; 13 | import net.minecraft.network.chat.Component; 14 | import net.minecraft.util.Mth; 15 | 16 | import java.util.Arrays; 17 | import java.util.Collection; 18 | import java.util.concurrent.CompletableFuture; 19 | 20 | public class COperationArgument implements ArgumentType { 21 | private static final Collection EXAMPLES = Arrays.asList("=", ">", "<"); 22 | private static final SimpleCommandExceptionType INVALID_OPERATION = new SimpleCommandExceptionType(Component.translatable("arguments.operation.invalid")); 23 | private static final SimpleCommandExceptionType DIVISION_ZERO_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("arguments.operation.div0")); 24 | 25 | public static COperationArgument operation() { 26 | return new COperationArgument(); 27 | } 28 | 29 | public static COperationArgument.Operation getOperation(final CommandContext context, final String name) { 30 | return context.getArgument(name, COperationArgument.Operation.class); 31 | } 32 | 33 | @Override 34 | public COperationArgument.Operation parse(final StringReader stringReader) throws CommandSyntaxException { 35 | if (!stringReader.canRead()) { 36 | throw INVALID_OPERATION.createWithContext(stringReader); 37 | } 38 | int i = stringReader.getCursor(); 39 | 40 | while (stringReader.canRead() && stringReader.peek() != ' ') { 41 | stringReader.skip(); 42 | } 43 | 44 | return getOperator(stringReader.getString().substring(i, stringReader.getCursor())); 45 | } 46 | 47 | @Override 48 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 49 | return SharedSuggestionProvider.suggest(new String[]{"=", "+=", "-=", "*=", "/=", "%=", "<", ">", "><"}, builder); 50 | } 51 | 52 | @Override 53 | public Collection getExamples() { 54 | return EXAMPLES; 55 | } 56 | 57 | private static COperationArgument.Operation getOperator(String operator) throws CommandSyntaxException { 58 | return operator.equals("><") ? (a, b) -> { 59 | int i = a.get(); 60 | a.set(b.get()); 61 | b.set(i); 62 | } : getIntOperator(operator); 63 | } 64 | 65 | private static COperationArgument.IntOperator getIntOperator(String operator) throws CommandSyntaxException { 66 | return switch(operator) { 67 | case "=" -> (a, b) -> b; 68 | case "+=" -> Integer::sum; 69 | case "-=" -> (a, b) -> a - b; 70 | case "*=" -> (a, b) -> a * b; 71 | case "/=" -> (a, b) -> { 72 | if (b == 0) { 73 | throw DIVISION_ZERO_EXCEPTION.create(); 74 | } 75 | return Mth.floorDiv(a, b); 76 | }; 77 | case "%=" -> (a, b) -> { 78 | if (b == 0) { 79 | throw DIVISION_ZERO_EXCEPTION.create(); 80 | } 81 | return Mth.positiveModulo(a, b); 82 | }; 83 | case "<" -> Math::min; 84 | case ">" -> Math::max; 85 | default -> throw INVALID_OPERATION.create(); 86 | }; 87 | } 88 | 89 | @FunctionalInterface 90 | interface IntOperator extends COperationArgument.Operation { 91 | int apply(int a, int b) throws CommandSyntaxException; 92 | 93 | @Override 94 | default void apply(ScoreAccess scoreAccess, ScoreAccess scoreAccess2) throws CommandSyntaxException { 95 | scoreAccess.set(this.apply(scoreAccess.get(), scoreAccess2.get())); 96 | } 97 | } 98 | 99 | @FunctionalInterface 100 | public interface Operation { 101 | void apply(ScoreAccess a, ScoreAccess b) throws CommandSyntaxException; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CParticleArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 8 | import com.mojang.brigadier.suggestion.Suggestions; 9 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 10 | import net.minecraft.commands.CommandBuildContext; 11 | import net.minecraft.commands.SharedSuggestionProvider; 12 | import net.minecraft.nbt.NbtOps; 13 | import net.minecraft.nbt.TagParser; 14 | import net.minecraft.core.particles.ParticleOptions; 15 | import net.minecraft.core.particles.ParticleType; 16 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 17 | import net.minecraft.resources.RegistryOps; 18 | import net.minecraft.resources.ResourceKey; 19 | import net.minecraft.core.registries.Registries; 20 | import net.minecraft.core.HolderLookup; 21 | import net.minecraft.network.chat.Component; 22 | import net.minecraft.resources.ResourceLocation; 23 | 24 | import java.util.Arrays; 25 | import java.util.Collection; 26 | import java.util.concurrent.CompletableFuture; 27 | 28 | public class CParticleArgument implements ArgumentType { 29 | private static final Collection EXAMPLES = Arrays.asList("foo", "foo:bar", "particle{foo:bar}"); 30 | public static final DynamicCommandExceptionType UNKNOWN_PARTICLE_EXCEPTION = new DynamicCommandExceptionType(id -> Component.translatableEscape("particle.notFound", id)); 31 | public static final DynamicCommandExceptionType INVALID_OPTIONS_EXCEPTION = new DynamicCommandExceptionType(error -> Component.translatableEscape("particle.invalidOptions", error)); 32 | private final HolderLookup.Provider holderLookupProvider; 33 | private static final TagParser VALUE_PARSER = TagParser.create(NbtOps.INSTANCE); 34 | 35 | public CParticleArgument(CommandBuildContext buildContext) { 36 | this.holderLookupProvider = buildContext; 37 | } 38 | 39 | public static CParticleArgument particle(CommandBuildContext buildContext) { 40 | return new CParticleArgument(buildContext); 41 | } 42 | 43 | public static ParticleOptions getParticle(final CommandContext context, final String name) { 44 | return context.getArgument(name, ParticleOptions.class); 45 | } 46 | 47 | @Override 48 | public ParticleOptions parse(final StringReader stringReader) throws CommandSyntaxException { 49 | return readParameters(stringReader, this.holderLookupProvider); 50 | } 51 | 52 | @Override 53 | public Collection getExamples() { 54 | return EXAMPLES; 55 | } 56 | 57 | public static ParticleOptions readParameters(StringReader reader, HolderLookup.Provider holderLookupProvider) throws CommandSyntaxException { 58 | ParticleType particleType = getType(reader, holderLookupProvider.lookupOrThrow(Registries.PARTICLE_TYPE)); 59 | return readParameters(VALUE_PARSER, reader, particleType, holderLookupProvider); 60 | } 61 | 62 | private static ParticleType getType(StringReader reader, HolderLookup> holderLookup) throws CommandSyntaxException { 63 | ResourceLocation id = ResourceLocation.read(reader); 64 | ResourceKey> resourceKey = ResourceKey.create(Registries.PARTICLE_TYPE, id); 65 | return holderLookup.get(resourceKey) 66 | .orElseThrow(() -> UNKNOWN_PARTICLE_EXCEPTION.createWithContext(reader, id)) 67 | .value(); 68 | } 69 | 70 | private static T readParameters(TagParser tagParser, StringReader reader, ParticleType type, HolderLookup.Provider provider) throws CommandSyntaxException { 71 | RegistryOps ops = provider.createSerializationContext(tagParser.getOps()); 72 | O nbt; 73 | if (reader.canRead() && reader.peek() == '{') { 74 | nbt = tagParser.parseAsArgument(reader); 75 | } else { 76 | nbt = ops.emptyMap(); 77 | } 78 | 79 | return type.codec().codec().parse(ops, nbt).getOrThrow(INVALID_OPTIONS_EXCEPTION::create); 80 | } 81 | 82 | @Override 83 | public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { 84 | HolderLookup.RegistryLookup> registryLookup = this.holderLookupProvider.lookupOrThrow(Registries.PARTICLE_TYPE); 85 | return SharedSuggestionProvider.suggestResource(registryLookup.listElementIds().map(ResourceKey::location), builder); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CRangeArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import net.minecraft.advancements.critereon.MinMaxBounds; 8 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 9 | 10 | import java.util.Arrays; 11 | import java.util.Collection; 12 | 13 | public interface CRangeArgument> extends ArgumentType { 14 | static Ints intRange() { 15 | return new Ints(); 16 | } 17 | 18 | static Floats floatRange() { 19 | return new Floats(); 20 | } 21 | 22 | class Floats implements CRangeArgument { 23 | private static final Collection EXAMPLES = Arrays.asList("0..5.2", "0", "-5.4", "-100.76..", "..100"); 24 | 25 | public static MinMaxBounds.Doubles getRangeArgument(final CommandContext context, final String name) { 26 | return context.getArgument(name, MinMaxBounds.Doubles.class); 27 | } 28 | 29 | @Override 30 | public MinMaxBounds.Doubles parse(final StringReader stringReader) throws CommandSyntaxException { 31 | return MinMaxBounds.Doubles.fromReader(stringReader); 32 | } 33 | 34 | @Override 35 | public Collection getExamples() { 36 | return EXAMPLES; 37 | } 38 | } 39 | 40 | class Ints implements CRangeArgument { 41 | private static final Collection EXAMPLES = Arrays.asList("0..5", "0", "-5", "-100..", "..100"); 42 | 43 | public static MinMaxBounds.Ints getRangeArgument(final CommandContext context, final String name) { 44 | return context.getArgument(name, MinMaxBounds.Ints.class); 45 | } 46 | 47 | @Override 48 | public MinMaxBounds.Ints parse(final StringReader stringReader) throws CommandSyntaxException { 49 | return MinMaxBounds.Ints.fromReader(stringReader); 50 | } 51 | 52 | @Override 53 | public Collection getExamples() { 54 | return EXAMPLES; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CResourceArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType; 8 | import com.mojang.brigadier.exceptions.Dynamic3CommandExceptionType; 9 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 10 | import com.mojang.brigadier.suggestion.Suggestions; 11 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 12 | import net.minecraft.commands.CommandBuildContext; 13 | import net.minecraft.commands.SharedSuggestionProvider; 14 | import net.minecraft.world.item.enchantment.Enchantment; 15 | import net.minecraft.world.entity.EntityType; 16 | import net.minecraft.world.entity.ai.attributes.Attribute; 17 | import net.minecraft.world.effect.MobEffect; 18 | import net.minecraft.core.Registry; 19 | import net.minecraft.resources.ResourceKey; 20 | import net.minecraft.core.registries.Registries; 21 | import net.minecraft.core.HolderLookup; 22 | import net.minecraft.core.Holder; 23 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 24 | import net.minecraft.network.chat.Component; 25 | import net.minecraft.resources.ResourceLocation; 26 | import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; 27 | import net.minecraft.world.level.levelgen.structure.Structure; 28 | 29 | import java.util.Arrays; 30 | import java.util.Collection; 31 | import java.util.concurrent.CompletableFuture; 32 | 33 | public class CResourceArgument implements ArgumentType> { 34 | private static final Collection EXAMPLES = Arrays.asList("foo", "foo:bar", "012"); 35 | private static final DynamicCommandExceptionType NOT_SUMMONABLE_EXCEPTION = new DynamicCommandExceptionType(id -> Component.translatableEscape("entity.not_summonable", id)); 36 | public static final Dynamic2CommandExceptionType NOT_FOUND_EXCEPTION = new Dynamic2CommandExceptionType((element, type) -> Component.translatableEscape("argument.resource.not_found", element, type)); 37 | public static final Dynamic3CommandExceptionType INVALID_TYPE_EXCEPTION = new Dynamic3CommandExceptionType((element, type, expectedType) -> Component.translatableEscape("argument.resource.invalid_type", element, type, expectedType)); 38 | final ResourceKey> registryRef; 39 | private final HolderLookup holderLookup; 40 | 41 | public CResourceArgument(CommandBuildContext buildContext, ResourceKey> registryRef) { 42 | this.registryRef = registryRef; 43 | this.holderLookup = buildContext.lookupOrThrow(registryRef); 44 | } 45 | 46 | public static CResourceArgument registryEntry(CommandBuildContext buildContext, ResourceKey> registryRef) { 47 | return new CResourceArgument<>(buildContext, registryRef); 48 | } 49 | 50 | @SuppressWarnings("unchecked") 51 | public static Holder.Reference getRegistryEntry(final CommandContext context, final String name, ResourceKey> registryRef) throws CommandSyntaxException { 52 | Holder.Reference reference = (Holder.Reference) context.getArgument(name, Holder.Reference.class); 53 | ResourceKey resourceKey = reference.key(); 54 | if (!resourceKey.isFor(registryRef)) { 55 | throw INVALID_TYPE_EXCEPTION.create(resourceKey.location(), resourceKey.registry(), registryRef.location()); 56 | } 57 | return reference; 58 | } 59 | 60 | public static Holder.Reference getEntityAttribute(final CommandContext context, final String name) throws CommandSyntaxException { 61 | return getRegistryEntry(context, name, Registries.ATTRIBUTE); 62 | } 63 | 64 | public static Holder.Reference> getConfiguredFeature(final CommandContext context, final String name) throws CommandSyntaxException { 65 | return getRegistryEntry(context, name, Registries.CONFIGURED_FEATURE); 66 | } 67 | 68 | public static Holder.Reference getStructure(final CommandContext context, final String name) throws CommandSyntaxException { 69 | return getRegistryEntry(context, name, Registries.STRUCTURE); 70 | } 71 | 72 | public static Holder.Reference> getEntityType(final CommandContext context, final String name) throws CommandSyntaxException { 73 | return getRegistryEntry(context, name, Registries.ENTITY_TYPE); 74 | } 75 | 76 | public static Holder.Reference> getSummonableEntityType(final CommandContext context, final String name) throws CommandSyntaxException { 77 | Holder.Reference> reference = getRegistryEntry(context, name, Registries.ENTITY_TYPE); 78 | if (!reference.value().canSummon()) { 79 | throw NOT_SUMMONABLE_EXCEPTION.create(reference.key().location().toString()); 80 | } 81 | return reference; 82 | } 83 | 84 | public static Holder.Reference getStatusEffect(final CommandContext context, final String name) throws CommandSyntaxException { 85 | return getRegistryEntry(context, name, Registries.MOB_EFFECT); 86 | } 87 | 88 | public static Holder.Reference getEnchantment(final CommandContext context, final String name) throws CommandSyntaxException { 89 | return getRegistryEntry(context, name, Registries.ENCHANTMENT); 90 | } 91 | 92 | public Holder.Reference parse(StringReader stringReader) throws CommandSyntaxException { 93 | ResourceLocation id = ResourceLocation.read(stringReader); 94 | ResourceKey resourceKey = ResourceKey.create(this.registryRef, id); 95 | return this.holderLookup 96 | .get(resourceKey) 97 | .orElseThrow(() -> NOT_FOUND_EXCEPTION.createWithContext(stringReader, id, this.registryRef.location())); 98 | } 99 | 100 | @Override 101 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 102 | return SharedSuggestionProvider.suggestResource(this.holderLookup.listElementIds().map(ResourceKey::location), builder); 103 | } 104 | 105 | @Override 106 | public Collection getExamples() { 107 | return EXAMPLES; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CResourceKeyArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 8 | import com.mojang.brigadier.suggestion.Suggestions; 9 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 10 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 11 | import net.minecraft.advancements.Advancement; 12 | import net.minecraft.advancements.AdvancementHolder; 13 | import net.minecraft.commands.SharedSuggestionProvider; 14 | import net.minecraft.core.Registry; 15 | import net.minecraft.resources.ResourceKey; 16 | import net.minecraft.core.registries.Registries; 17 | import net.minecraft.core.Holder; 18 | import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool; 19 | import net.minecraft.network.chat.Component; 20 | import net.minecraft.resources.ResourceLocation; 21 | import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; 22 | import net.minecraft.world.level.levelgen.structure.Structure; 23 | 24 | import java.util.Arrays; 25 | import java.util.Collection; 26 | import java.util.Optional; 27 | import java.util.concurrent.CompletableFuture; 28 | 29 | public class CResourceKeyArgument implements ArgumentType> { 30 | private static final Collection EXAMPLES = Arrays.asList("foo", "foo:bar", "012"); 31 | private static final DynamicCommandExceptionType INVALID_FEATURE_EXCEPTION = new DynamicCommandExceptionType(id -> Component.translatableEscape("commands.place.feature.invalid", id)); 32 | private static final DynamicCommandExceptionType INVALID_STRUCTURE_EXCEPTION = new DynamicCommandExceptionType(id -> Component.translatableEscape("commands.place.structure.invalid", id)); 33 | private static final DynamicCommandExceptionType INVALID_JIGSAW_EXCEPTION = new DynamicCommandExceptionType(id -> Component.translatableEscape("commands.place.jigsaw.invalid", id)); 34 | private static final DynamicCommandExceptionType ERROR_INVALID_RECIPE = new DynamicCommandExceptionType(object -> Component.translatableEscape("recipe.notFound", object)); 35 | private static final DynamicCommandExceptionType ERROR_INVALID_ADVANCEMENT = new DynamicCommandExceptionType(object -> Component.translatableEscape("advancement.advancementNotFound", object)); 36 | final ResourceKey> registryRef; 37 | 38 | public CResourceKeyArgument(ResourceKey> registryRef) { 39 | this.registryRef = registryRef; 40 | } 41 | 42 | public static CResourceKeyArgument key(ResourceKey> registryRef) { 43 | return new CResourceKeyArgument<>(registryRef); 44 | } 45 | 46 | public static ResourceKey getKey(final CommandContext context, final String name, final ResourceKey> registryRef, final DynamicCommandExceptionType invalidException) throws CommandSyntaxException { 47 | ResourceKey registryKey = context.getArgument(name, ResourceKey.class); 48 | Optional> optional = registryKey.cast(registryRef); 49 | return optional.orElseThrow(() -> invalidException.create(registryKey.location())); 50 | } 51 | 52 | public static Registry getRegistry(final CommandContext context, ResourceKey> registryRef) { 53 | return context.getSource().registryAccess().lookupOrThrow(registryRef); 54 | } 55 | 56 | public static Holder.Reference getRegistryEntry(final CommandContext context, final String name, ResourceKey> registryRef, DynamicCommandExceptionType invalidException) throws CommandSyntaxException { 57 | ResourceKey registryKey = getKey(context, name, registryRef, invalidException); 58 | return getRegistry(context, registryRef) 59 | .get(registryKey) 60 | .orElseThrow(() -> invalidException.create(registryKey.location())); 61 | } 62 | 63 | public static Holder.Reference> getConfiguredFeature(final CommandContext context, final String name) throws CommandSyntaxException { 64 | return getRegistryEntry(context, name, Registries.CONFIGURED_FEATURE, INVALID_FEATURE_EXCEPTION); 65 | } 66 | 67 | public static Holder.Reference getStructure(final CommandContext context, final String name) throws CommandSyntaxException { 68 | return getRegistryEntry(context, name, Registries.STRUCTURE, INVALID_STRUCTURE_EXCEPTION); 69 | } 70 | 71 | public static Holder.Reference getStructurePool(final CommandContext context, final String name) throws CommandSyntaxException { 72 | return getRegistryEntry(context, name, Registries.TEMPLATE_POOL, INVALID_JIGSAW_EXCEPTION); 73 | } 74 | 75 | public static AdvancementHolder getAdvancement(final CommandContext context, final String string) throws CommandSyntaxException { 76 | ResourceKey resourceKey = getKey(context, string, Registries.ADVANCEMENT, ERROR_INVALID_ADVANCEMENT); 77 | AdvancementHolder advancementHolder = context.getSource().getPlayer().connection.getAdvancements().get(resourceKey.location()); 78 | if (advancementHolder == null) { 79 | throw ERROR_INVALID_ADVANCEMENT.create(resourceKey.location()); 80 | } 81 | return advancementHolder; 82 | } 83 | 84 | @Override 85 | public ResourceKey parse(final StringReader stringReader) throws CommandSyntaxException { 86 | ResourceLocation identifier = ResourceLocation.read(stringReader); 87 | return ResourceKey.create(this.registryRef, identifier); 88 | } 89 | 90 | @Override 91 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 92 | return context.getSource() instanceof SharedSuggestionProvider commandSource 93 | ? commandSource.suggestRegistryElements(this.registryRef, SharedSuggestionProvider.ElementSuggestionType.ELEMENTS, builder, context) 94 | : builder.buildFuture(); 95 | } 96 | 97 | @Override 98 | public Collection getExamples() { 99 | return EXAMPLES; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CResourceLocationArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | 8 | import java.util.Arrays; 9 | import java.util.Collection; 10 | 11 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 12 | import net.minecraft.resources.ResourceLocation; 13 | 14 | public class CResourceLocationArgument implements ArgumentType { 15 | private static final Collection EXAMPLES = Arrays.asList("foo", "foo:bar", "012"); 16 | 17 | public static CResourceLocationArgument id() { 18 | return new CResourceLocationArgument(); 19 | } 20 | 21 | public static ResourceLocation getId(final CommandContext context, final String name) { 22 | return context.getArgument(name, ResourceLocation.class); 23 | } 24 | 25 | @Override 26 | public ResourceLocation parse(final StringReader stringReader) throws CommandSyntaxException { 27 | return ResourceLocation.read(stringReader); 28 | } 29 | 30 | @Override 31 | public Collection getExamples() { 32 | return EXAMPLES; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CResourceOrIdArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.google.common.annotations.VisibleForTesting; 4 | import com.mojang.brigadier.StringReader; 5 | import com.mojang.brigadier.arguments.ArgumentType; 6 | import com.mojang.brigadier.context.CommandContext; 7 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 8 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 9 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 10 | import com.mojang.serialization.Codec; 11 | import net.minecraft.commands.CommandBuildContext; 12 | import net.minecraft.world.level.storage.loot.LootTable; 13 | import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; 14 | import net.minecraft.world.level.storage.loot.functions.LootItemFunction; 15 | import net.minecraft.world.level.storage.loot.functions.LootItemFunctions; 16 | import net.minecraft.nbt.NbtOps; 17 | import net.minecraft.nbt.TagParser; 18 | import net.minecraft.core.Registry; 19 | import net.minecraft.resources.ResourceKey; 20 | import net.minecraft.core.registries.Registries; 21 | import net.minecraft.resources.RegistryOps; 22 | import net.minecraft.core.HolderLookup; 23 | import net.minecraft.core.Holder; 24 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 25 | import net.minecraft.network.chat.Component; 26 | import net.minecraft.resources.ResourceLocation; 27 | import org.jetbrains.annotations.Nullable; 28 | 29 | import java.util.Collection; 30 | import java.util.List; 31 | 32 | public class CResourceOrIdArgument implements ArgumentType> { 33 | private static final Collection EXAMPLES = List.of("foo", "foo:bar", "012", "{}", "true"); 34 | public static final DynamicCommandExceptionType FAILED_TO_PARSE_EXCEPTION = new DynamicCommandExceptionType(argument -> Component.translatableEscape("argument.resource_or_id.failed_to_parse", argument)); 35 | private static final SimpleCommandExceptionType INVALID_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("argument.resource_or_id.invalid")); 36 | private static final TagParser VALUE_PARSER = TagParser.create(NbtOps.INSTANCE); 37 | private final HolderLookup.Provider holderLookupProvider; 38 | private final boolean canLookupRegistry; 39 | private final Codec> entryCodec; 40 | 41 | public CResourceOrIdArgument(CommandBuildContext buildContext, ResourceKey> registry, Codec> entryCodec) { 42 | this.holderLookupProvider = buildContext; 43 | this.canLookupRegistry = buildContext.lookup(registry).isPresent(); 44 | this.entryCodec = entryCodec; 45 | } 46 | 47 | public static LootTableArgument lootTable(CommandBuildContext buildContext) { 48 | return new LootTableArgument(buildContext); 49 | } 50 | 51 | public static Holder getLootTable(final CommandContext context, final String argument) throws CommandSyntaxException { 52 | return getArgument(context, argument); 53 | } 54 | 55 | public static LootModifierArgument lootModifier(CommandBuildContext buildContext) { 56 | return new LootModifierArgument(buildContext); 57 | } 58 | 59 | public static Holder getLootModifier(final CommandContext context, final String argument) { 60 | return getArgument(context, argument); 61 | } 62 | 63 | public static LootPredicateArgument lootPredicate(CommandBuildContext buildContext) { 64 | return new LootPredicateArgument(buildContext); 65 | } 66 | 67 | public static Holder getLootPredicate(final CommandContext context, final String argument) { 68 | return getArgument(context, argument); 69 | } 70 | 71 | @SuppressWarnings("unchecked") 72 | private static Holder getArgument(final CommandContext context, final String argument) { 73 | return (Holder) context.getArgument(argument, Holder.class); 74 | } 75 | 76 | @Nullable 77 | public Holder parse(final StringReader stringReader) throws CommandSyntaxException { 78 | return parse(stringReader, VALUE_PARSER); 79 | } 80 | 81 | private Holder parse(StringReader reader, TagParser tagParser) throws CommandSyntaxException { 82 | O nbtElement = parseAsNbt(reader, tagParser); 83 | if (!this.canLookupRegistry) { 84 | return null; 85 | } 86 | RegistryOps registryOps = this.holderLookupProvider.createSerializationContext(tagParser.getOps()); 87 | return this.entryCodec.parse(registryOps, nbtElement).getOrThrow(argument -> FAILED_TO_PARSE_EXCEPTION.createWithContext(reader, argument)); 88 | } 89 | 90 | @VisibleForTesting 91 | static O parseAsNbt(StringReader stringReader, TagParser tagParser) throws CommandSyntaxException { 92 | int i = stringReader.getCursor(); 93 | O nbtElement = tagParser.parseAsArgument(stringReader); 94 | if (hasFinishedReading(stringReader)) { 95 | return nbtElement; 96 | } 97 | stringReader.setCursor(i); 98 | ResourceLocation id = ResourceLocation.read(stringReader); 99 | if (!hasFinishedReading(stringReader)) { 100 | stringReader.setCursor(i); 101 | throw INVALID_EXCEPTION.createWithContext(stringReader); 102 | } 103 | return tagParser.getOps().createString(id.toString()); 104 | } 105 | 106 | private static boolean hasFinishedReading(StringReader stringReader) { 107 | return !stringReader.canRead() || stringReader.peek() == ' '; 108 | } 109 | 110 | @Override 111 | public Collection getExamples() { 112 | return EXAMPLES; 113 | } 114 | 115 | public static class LootPredicateArgument extends CResourceOrIdArgument { 116 | protected LootPredicateArgument(CommandBuildContext buildContext) { 117 | super(buildContext, Registries.PREDICATE, LootItemCondition.CODEC); 118 | } 119 | } 120 | 121 | public static class LootModifierArgument extends CResourceOrIdArgument { 122 | protected LootModifierArgument(CommandBuildContext buildContext) { 123 | super(buildContext, Registries.ITEM_MODIFIER, LootItemFunctions.CODEC); 124 | } 125 | } 126 | 127 | public static class LootTableArgument extends CResourceOrIdArgument { 128 | protected LootTableArgument(CommandBuildContext buildContext) { 129 | super(buildContext, Registries.LOOT_TABLE, LootTable.CODEC); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CResourceOrTagArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType; 8 | import com.mojang.brigadier.exceptions.Dynamic3CommandExceptionType; 9 | import com.mojang.brigadier.suggestion.Suggestions; 10 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 11 | import com.mojang.datafixers.util.Either; 12 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 13 | import net.minecraft.commands.CommandBuildContext; 14 | import net.minecraft.commands.SharedSuggestionProvider; 15 | import net.minecraft.core.Registry; 16 | import net.minecraft.resources.ResourceKey; 17 | import net.minecraft.core.HolderLookup; 18 | import net.minecraft.core.Holder; 19 | import net.minecraft.core.HolderSet; 20 | import net.minecraft.tags.TagKey; 21 | import net.minecraft.network.chat.Component; 22 | import net.minecraft.resources.ResourceLocation; 23 | 24 | import java.util.Arrays; 25 | import java.util.Collection; 26 | import java.util.Optional; 27 | import java.util.concurrent.CompletableFuture; 28 | import java.util.function.Predicate; 29 | 30 | public class CResourceOrTagArgument implements ArgumentType> { 31 | private static final Collection EXAMPLES = Arrays.asList("foo", "foo:bar", "012", "#skeletons", "#minecraft:skeletons"); 32 | private static final Dynamic2CommandExceptionType NOT_FOUND_EXCEPTION = new Dynamic2CommandExceptionType((tag, type) -> Component.translatableEscape("argument.resource_tag.not_found", tag, type)); 33 | private static final Dynamic3CommandExceptionType WRONG_TYPE_EXCEPTION = new Dynamic3CommandExceptionType((tag, type, expectedType) -> Component.translatableEscape("argument.resource_tag.invalid_type", tag, type, expectedType)); 34 | private final HolderLookup holderLookup; 35 | final ResourceKey> registryRef; 36 | 37 | public CResourceOrTagArgument(CommandBuildContext buildContext, ResourceKey> registryRef) { 38 | this.registryRef = registryRef; 39 | this.holderLookup = buildContext.lookupOrThrow(registryRef); 40 | } 41 | 42 | public static CResourceOrTagArgument resourceOrTag(CommandBuildContext buildContext, ResourceKey> registryRef) { 43 | return new CResourceOrTagArgument<>(buildContext, registryRef); 44 | } 45 | 46 | public static Result getResourceOrTag(final CommandContext context, final String name, final ResourceKey> registryRef) throws CommandSyntaxException { 47 | Result result = context.getArgument(name, Result.class); 48 | Optional> optional = result.tryCast(registryRef); 49 | return optional.orElseThrow(() -> result.getEntry().map(entry -> { 50 | ResourceKey resourceKey2 = entry.key(); 51 | return CResourceArgument.INVALID_TYPE_EXCEPTION.create(resourceKey2.location(), resourceKey2.registry(), registryRef.location()); 52 | }, entryList -> { 53 | TagKey tagKey = entryList.key(); 54 | return WRONG_TYPE_EXCEPTION.create(tagKey.location(), tagKey.registry(), registryRef.location()); 55 | })); 56 | } 57 | 58 | @Override 59 | public Result parse(final StringReader stringReader) throws CommandSyntaxException { 60 | if (stringReader.canRead() && stringReader.peek() == CEntitySelectorParser.SYNTAX_TAG) { 61 | int i = stringReader.getCursor(); 62 | 63 | try { 64 | stringReader.skip(); 65 | ResourceLocation id = ResourceLocation.read(stringReader); 66 | TagKey tagKey = TagKey.create(this.registryRef, id); 67 | HolderSet.Named named = this.holderLookup 68 | .get(tagKey) 69 | .orElseThrow(() -> NOT_FOUND_EXCEPTION.createWithContext(stringReader, id, this.registryRef.location())); 70 | return new TagBased<>(named); 71 | } catch (CommandSyntaxException var6) { 72 | stringReader.setCursor(i); 73 | throw var6; 74 | } 75 | } else { 76 | ResourceLocation id = ResourceLocation.read(stringReader); 77 | ResourceKey resourceKey = ResourceKey.create(this.registryRef, id); 78 | Holder.Reference reference = this.holderLookup 79 | .get(resourceKey) 80 | .orElseThrow(() -> CResourceArgument.NOT_FOUND_EXCEPTION.createWithContext(stringReader, id, this.registryRef.location())); 81 | return new EntryBased<>(reference); 82 | } 83 | } 84 | 85 | @Override 86 | public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { 87 | SharedSuggestionProvider.suggestResource(this.holderLookup.listTagIds().map(TagKey::location), builder, "#"); 88 | return SharedSuggestionProvider.suggestResource(this.holderLookup.listElementIds().map(ResourceKey::location), builder); 89 | } 90 | 91 | @Override 92 | public Collection getExamples() { 93 | return EXAMPLES; 94 | } 95 | 96 | record EntryBased(Holder.Reference value) implements Result { 97 | @Override 98 | public Either, HolderSet.Named> getEntry() { 99 | return Either.left(this.value); 100 | } 101 | 102 | @Override 103 | public Optional> tryCast(ResourceKey> registryRef) { 104 | return this.value.key().isFor(registryRef) ? Optional.of((Result) this) : Optional.empty(); 105 | } 106 | 107 | public boolean test(Holder registryEntry) { 108 | return registryEntry.equals(this.value); 109 | } 110 | 111 | @Override 112 | public String asString() { 113 | return this.value.key().location().toString(); 114 | } 115 | } 116 | 117 | public interface Result extends Predicate> { 118 | Either, HolderSet.Named> getEntry(); 119 | 120 | Optional> tryCast(ResourceKey> registryRef); 121 | 122 | String asString(); 123 | } 124 | 125 | record TagBased(HolderSet.Named tag) implements Result { 126 | @Override 127 | public Either, HolderSet.Named> getEntry() { 128 | return Either.right(this.tag); 129 | } 130 | 131 | @Override 132 | public Optional> tryCast(ResourceKey> registryRef) { 133 | return this.tag.key().isFor(registryRef) ? Optional.of((Result) this) : Optional.empty(); 134 | } 135 | 136 | public boolean test(Holder registryEntry) { 137 | return this.tag.contains(registryEntry); 138 | } 139 | 140 | @Override 141 | public String asString() { 142 | return "#" + this.tag.key().location(); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CResourceOrTagKeyArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 8 | import com.mojang.brigadier.suggestion.Suggestions; 9 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 10 | import com.mojang.datafixers.util.Either; 11 | import net.minecraft.commands.SharedSuggestionProvider; 12 | import net.minecraft.core.Registry; 13 | import net.minecraft.resources.ResourceKey; 14 | import net.minecraft.core.Holder; 15 | import net.minecraft.tags.TagKey; 16 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 17 | import net.minecraft.resources.ResourceLocation; 18 | 19 | import java.util.Arrays; 20 | import java.util.Collection; 21 | import java.util.Optional; 22 | import java.util.concurrent.CompletableFuture; 23 | import java.util.function.Predicate; 24 | 25 | public class CResourceOrTagKeyArgument implements ArgumentType> { 26 | private static final Collection EXAMPLES = Arrays.asList("foo", "foo:bar", "012", "#skeletons", "#minecraft:skeletons"); 27 | final ResourceKey> registryRef; 28 | 29 | public CResourceOrTagKeyArgument(ResourceKey> registryRef) { 30 | this.registryRef = registryRef; 31 | } 32 | 33 | public static CResourceOrTagKeyArgument registryPredicate(ResourceKey> registryRef) { 34 | return new CResourceOrTagKeyArgument<>(registryRef); 35 | } 36 | 37 | public static Result getPredicate(final CommandContext context, final String name, final ResourceKey> registryRef, final DynamicCommandExceptionType invalidException) throws CommandSyntaxException { 38 | Result result = context.getArgument(name, Result.class); 39 | Optional> optional = result.tryCast(registryRef); 40 | return optional.orElseThrow(() -> invalidException.create(result)); 41 | } 42 | 43 | @Override 44 | public Result parse(final StringReader stringReader) throws CommandSyntaxException { 45 | if (stringReader.canRead() && stringReader.peek() == CEntitySelectorParser.SYNTAX_TAG) { 46 | int cursor = stringReader.getCursor(); 47 | 48 | try { 49 | stringReader.skip(); 50 | ResourceLocation id = ResourceLocation.read(stringReader); 51 | return new TagResult<>(TagKey.create(this.registryRef, id)); 52 | } catch (CommandSyntaxException var4) { 53 | stringReader.setCursor(cursor); 54 | throw var4; 55 | } 56 | } else { 57 | ResourceLocation id = ResourceLocation.read(stringReader); 58 | return new ResourceResult<>(ResourceKey.create(this.registryRef, id)); 59 | } 60 | } 61 | 62 | @Override 63 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 64 | return context.getSource() instanceof SharedSuggestionProvider commandSource 65 | ? commandSource.suggestRegistryElements(this.registryRef, SharedSuggestionProvider.ElementSuggestionType.ALL, builder, context) 66 | : builder.buildFuture(); 67 | } 68 | 69 | @Override 70 | public Collection getExamples() { 71 | return EXAMPLES; 72 | } 73 | 74 | record ResourceResult(ResourceKey key) implements Result { 75 | @Override 76 | public Either, TagKey> getKey() { 77 | return Either.left(this.key); 78 | } 79 | 80 | @Override 81 | public Optional> tryCast(ResourceKey> registryRef) { 82 | return this.key.cast(registryRef).map(ResourceResult::new); 83 | } 84 | 85 | public boolean test(Holder registryEntry) { 86 | return registryEntry.is(this.key); 87 | } 88 | 89 | @Override 90 | public String asString() { 91 | return this.key.location().toString(); 92 | } 93 | } 94 | 95 | public interface Result extends Predicate> { 96 | Either, TagKey> getKey(); 97 | 98 | Optional> tryCast(ResourceKey> registryRef); 99 | 100 | String asString(); 101 | } 102 | 103 | record TagResult(TagKey key) implements Result { 104 | @Override 105 | public Either, TagKey> getKey() { 106 | return Either.right(this.key); 107 | } 108 | 109 | @Override 110 | public Optional> tryCast(ResourceKey> registryRef) { 111 | return this.key.cast(registryRef).map(TagResult::new); 112 | } 113 | 114 | public boolean test(Holder registryEntry) { 115 | return registryEntry.is(this.key); 116 | } 117 | 118 | @Override 119 | public String asString() { 120 | return "#" + this.key.location(); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CResourceSelectorArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.mojang.brigadier.StringReader; 5 | import com.mojang.brigadier.arguments.ArgumentType; 6 | import com.mojang.brigadier.context.CommandContext; 7 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 8 | import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType; 9 | import com.mojang.brigadier.suggestion.Suggestions; 10 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 11 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 12 | import net.minecraft.commands.CommandBuildContext; 13 | import net.minecraft.commands.SharedSuggestionProvider; 14 | import net.minecraft.commands.synchronization.ArgumentTypeInfo; 15 | import net.minecraft.core.Holder; 16 | import net.minecraft.core.HolderLookup; 17 | import net.minecraft.core.Registry; 18 | import net.minecraft.network.FriendlyByteBuf; 19 | import net.minecraft.network.chat.Component; 20 | import net.minecraft.resources.ResourceKey; 21 | import net.minecraft.resources.ResourceLocation; 22 | import org.apache.commons.io.FilenameUtils; 23 | 24 | import java.util.Collection; 25 | import java.util.List; 26 | import java.util.concurrent.CompletableFuture; 27 | 28 | public class CResourceSelectorArgument implements ArgumentType>> { 29 | private static final Collection EXAMPLES = List.of("minecraft:*", "*:asset", "*"); 30 | public static final Dynamic2CommandExceptionType ERROR_NO_MATCHES = new Dynamic2CommandExceptionType( 31 | (object, object2) -> Component.translatableEscape("argument.resource_selector.not_found", object, object2) 32 | ); 33 | final ResourceKey> registryKey; 34 | private final HolderLookup registryLookup; 35 | 36 | CResourceSelectorArgument(CommandBuildContext commandBuildContext, ResourceKey> resourceKey) { 37 | this.registryKey = resourceKey; 38 | this.registryLookup = commandBuildContext.lookupOrThrow(resourceKey); 39 | } 40 | 41 | public Collection> parse(StringReader stringReader) throws CommandSyntaxException { 42 | String string = ensureNamespaced(readPattern(stringReader)); 43 | List> list = this.registryLookup.listElements().filter(reference -> matches(string, reference.key().location())).toList(); 44 | if (list.isEmpty()) { 45 | throw ERROR_NO_MATCHES.createWithContext(stringReader, string, this.registryKey.location()); 46 | } else { 47 | return list; 48 | } 49 | } 50 | 51 | public static Collection> parse(StringReader stringReader, HolderLookup holderLookup) { 52 | String string = ensureNamespaced(readPattern(stringReader)); 53 | return holderLookup.listElements().filter(reference -> matches(string, reference.key().location())).toList(); 54 | } 55 | 56 | private static String readPattern(StringReader stringReader) { 57 | int i = stringReader.getCursor(); 58 | 59 | while (stringReader.canRead() && isAllowedPatternCharacter(stringReader.peek())) { 60 | stringReader.skip(); 61 | } 62 | 63 | return stringReader.getString().substring(i, stringReader.getCursor()); 64 | } 65 | 66 | private static boolean isAllowedPatternCharacter(char c) { 67 | return ResourceLocation.isAllowedInResourceLocation(c) || c == '*' || c == '?'; 68 | } 69 | 70 | private static String ensureNamespaced(String string) { 71 | return !string.contains(":") ? "minecraft:" + string : string; 72 | } 73 | 74 | private static boolean matches(String string, ResourceLocation resourceLocation) { 75 | return FilenameUtils.wildcardMatch(resourceLocation.toString(), string); 76 | } 77 | 78 | public static CResourceSelectorArgument resourceSelector(CommandBuildContext commandBuildContext, ResourceKey> resourceKey) { 79 | return new CResourceSelectorArgument<>(commandBuildContext, resourceKey); 80 | } 81 | 82 | @SuppressWarnings("unchecked") 83 | public static Collection> getSelectedResources( 84 | CommandContext commandContext, String string, ResourceKey> resourceKey 85 | ) { 86 | return commandContext.getArgument(string, Collection.class); 87 | } 88 | 89 | @Override 90 | public CompletableFuture listSuggestions(CommandContext commandContext, SuggestionsBuilder suggestionsBuilder) { 91 | return commandContext.getSource() instanceof SharedSuggestionProvider sharedSuggestionProvider 92 | ? sharedSuggestionProvider.suggestRegistryElements( 93 | this.registryKey, SharedSuggestionProvider.ElementSuggestionType.ELEMENTS, suggestionsBuilder, commandContext 94 | ) 95 | : SharedSuggestionProvider.suggest(this.registryLookup.listElementIds().map(ResourceKey::location).map(ResourceLocation::toString), suggestionsBuilder); 96 | } 97 | 98 | @Override 99 | public Collection getExamples() { 100 | return EXAMPLES; 101 | } 102 | 103 | public static class Info implements ArgumentTypeInfo, CResourceSelectorArgument.Info.Template> { 104 | public void serializeToNetwork(CResourceSelectorArgument.Info.Template template, FriendlyByteBuf friendlyByteBuf) { 105 | friendlyByteBuf.writeResourceKey(template.registryKey); 106 | } 107 | 108 | public CResourceSelectorArgument.Info.Template deserializeFromNetwork(FriendlyByteBuf friendlyByteBuf) { 109 | return new Template(friendlyByteBuf.readRegistryKey()); 110 | } 111 | 112 | public void serializeToJson(CResourceSelectorArgument.Info.Template template, JsonObject jsonObject) { 113 | jsonObject.addProperty("registry", template.registryKey.location().toString()); 114 | } 115 | 116 | public CResourceSelectorArgument.Info.Template unpack(CResourceSelectorArgument resourceSelectorArgument) { 117 | return new Template(resourceSelectorArgument.registryKey); 118 | } 119 | 120 | public final class Template implements ArgumentTypeInfo.Template> { 121 | final ResourceKey> registryKey; 122 | 123 | Template(final ResourceKey> resourceKey) { 124 | this.registryKey = resourceKey; 125 | } 126 | 127 | public CResourceSelectorArgument instantiate(CommandBuildContext commandBuildContext) { 128 | return new CResourceSelectorArgument<>(commandBuildContext, this.registryKey); 129 | } 130 | 131 | @Override 132 | public ArgumentTypeInfo, ?> type() { 133 | return CResourceSelectorArgument.Info.this; 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CRotationArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 8 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 9 | import net.minecraft.commands.arguments.coordinates.WorldCoordinate; 10 | import net.minecraft.network.chat.Component; 11 | 12 | import java.util.Arrays; 13 | import java.util.Collection; 14 | 15 | public class CRotationArgument implements ArgumentType { 16 | private static final Collection EXAMPLES = Arrays.asList("0 0", "~ ~", "~-5 ~5"); 17 | public static final SimpleCommandExceptionType INCOMPLETE_ROTATION_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("argument.rotation.incomplete")); 18 | 19 | public static CRotationArgument rotation() { 20 | return new CRotationArgument(); 21 | } 22 | 23 | public static CCoordinates getRotation(final CommandContext context, final String name) { 24 | return context.getArgument(name, CCoordinates.class); 25 | } 26 | 27 | @Override 28 | public CCoordinates parse(final StringReader stringReader) throws CommandSyntaxException { 29 | int cursor = stringReader.getCursor(); 30 | if (!stringReader.canRead()) { 31 | throw INCOMPLETE_ROTATION_EXCEPTION.createWithContext(stringReader); 32 | } 33 | WorldCoordinate worldCoordinate = WorldCoordinate.parseDouble(stringReader, false); 34 | if (!stringReader.canRead() || stringReader.peek() != ' ') { 35 | stringReader.setCursor(cursor); 36 | throw INCOMPLETE_ROTATION_EXCEPTION.createWithContext(stringReader); 37 | } 38 | stringReader.skip(); 39 | WorldCoordinate worldCoordinate2 = WorldCoordinate.parseDouble(stringReader, false); 40 | return new CWorldCoordinates(worldCoordinate2, worldCoordinate, new WorldCoordinate(true, 0.0)); 41 | } 42 | 43 | @Override 44 | public Collection getExamples() { 45 | return EXAMPLES; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CScoreHolderArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.google.common.collect.Streams; 4 | import com.mojang.brigadier.StringReader; 5 | import com.mojang.brigadier.arguments.ArgumentType; 6 | import com.mojang.brigadier.context.CommandContext; 7 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 8 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 9 | import com.mojang.brigadier.suggestion.SuggestionProvider; 10 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 11 | import net.minecraft.client.player.AbstractClientPlayer; 12 | import net.minecraft.commands.SharedSuggestionProvider; 13 | import net.minecraft.world.entity.Entity; 14 | import net.minecraft.world.scores.ScoreHolder; 15 | import net.minecraft.network.chat.Component; 16 | 17 | import java.util.Collection; 18 | import java.util.Arrays; 19 | import java.util.Collections; 20 | import java.util.List; 21 | import java.util.UUID; 22 | import java.util.function.Supplier; 23 | 24 | public class CScoreHolderArgument implements ArgumentType { 25 | public static final SuggestionProvider SUGGESTION_PROVIDER = (context, builder) -> { 26 | StringReader stringReader = new StringReader(builder.getInput()); 27 | stringReader.setCursor(builder.getStart()); 28 | CEntitySelectorParser entitySelectorParser = new CEntitySelectorParser(stringReader, CEntitySelectorParser.allowSelectors(context.getSource())); 29 | 30 | try { 31 | entitySelectorParser.parse(); 32 | } catch (CommandSyntaxException ignored) { 33 | } 34 | 35 | return entitySelectorParser.fillSuggestions(builder, builderx -> SharedSuggestionProvider.suggest(context.getSource().getOnlinePlayerNames(), builderx)); 36 | }; 37 | private static final Collection EXAMPLES = Arrays.asList("Player", "0123", "*", "@e"); 38 | private static final SimpleCommandExceptionType EMPTY_SCORE_HOLDER_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("argument.scoreHolder.empty")); 39 | final boolean multiple; 40 | 41 | public CScoreHolderArgument(boolean multiple) { 42 | this.multiple = multiple; 43 | } 44 | 45 | public static ScoreHolder getScoreHolder(final CommandContext context, final String name) throws CommandSyntaxException { 46 | return getScoreHolders(context, name).iterator().next(); 47 | } 48 | 49 | public static Collection getScoreHolders(final CommandContext context, final String name) throws CommandSyntaxException { 50 | return getScoreHolders(context, name, Collections::emptyList); 51 | } 52 | 53 | public static Collection getScoreboardScoreHolders(final CommandContext context, final String name) throws CommandSyntaxException { 54 | return getScoreHolders(context, name, context.getSource().getWorld().getScoreboard()::getTrackedPlayers); 55 | } 56 | 57 | public static Collection getScoreHolders(final CommandContext context, final String name, final Supplier> players) throws CommandSyntaxException { 58 | Collection collection = context.getArgument(name, Result.class).getNames(context.getSource(), players); 59 | if (collection.isEmpty()) { 60 | throw CEntityArgument.ENTITY_NOT_FOUND_EXCEPTION.create(); 61 | } 62 | return collection; 63 | } 64 | 65 | public static CScoreHolderArgument scoreHolder() { 66 | return new CScoreHolderArgument(false); 67 | } 68 | 69 | public static CScoreHolderArgument scoreHolders() { 70 | return new CScoreHolderArgument(true); 71 | } 72 | 73 | @Override 74 | public Result parse(final StringReader stringReader) throws CommandSyntaxException { 75 | return this.parse(stringReader, true); 76 | } 77 | 78 | public CScoreHolderArgument.Result parse(final StringReader stringReader, final S source) throws CommandSyntaxException { 79 | return this.parse(stringReader, CEntitySelectorParser.allowSelectors(source)); 80 | } 81 | 82 | private CScoreHolderArgument.Result parse(final StringReader stringReader, final boolean selectorsAllowed) throws CommandSyntaxException { 83 | if (stringReader.canRead() && stringReader.peek() == CEntitySelectorParser.SYNTAX_SELECTOR_START) { 84 | CEntitySelectorParser entitySelectorParser = new CEntitySelectorParser(stringReader, selectorsAllowed); 85 | CEntitySelector entitySelector = entitySelectorParser.parse(); 86 | if (!this.multiple && entitySelector.getMaxResults() > 1) { 87 | throw CEntityArgument.TOO_MANY_ENTITIES_EXCEPTION.createWithContext(stringReader); 88 | } 89 | return new SelectorResult(entitySelector); 90 | } 91 | int cursor = stringReader.getCursor(); 92 | 93 | while (stringReader.canRead() && stringReader.peek() != ' ') { 94 | stringReader.skip(); 95 | } 96 | 97 | String string = stringReader.getString().substring(cursor, stringReader.getCursor()); 98 | if (string.equals("*")) { 99 | return (source, players) -> { 100 | Collection collection = players.get(); 101 | if (collection.isEmpty()) { 102 | throw EMPTY_SCORE_HOLDER_EXCEPTION.create(); 103 | } 104 | return collection; 105 | }; 106 | } 107 | List list = List.of(ScoreHolder.forNameOnly(string)); 108 | if (string.startsWith("#")) { 109 | return (source, players) -> list; 110 | } 111 | try { 112 | UUID uuid = UUID.fromString(string); 113 | return (source, holders) -> { 114 | ScoreHolder scoreHolder = Streams.stream(source.getWorld().entitiesForRendering()) 115 | .filter(e -> e.getUUID().equals(uuid)) 116 | .findAny().orElse(null); 117 | 118 | return scoreHolder != null ? List.of(scoreHolder) : list; 119 | }; 120 | } catch (IllegalArgumentException var6) { 121 | return (source, holders) -> { 122 | AbstractClientPlayer abstractClientPlayer = Streams.stream(source.getWorld().entitiesForRendering()) 123 | .filter(entity -> entity instanceof AbstractClientPlayer) 124 | .map(entity -> (AbstractClientPlayer) entity) 125 | .filter(abstractPlayer -> abstractPlayer.getName().getString().equals(string)) 126 | .findAny().orElse(null); 127 | return abstractClientPlayer != null ? List.of(abstractClientPlayer) : list; 128 | }; 129 | } 130 | } 131 | 132 | @Override 133 | public Collection getExamples() { 134 | return EXAMPLES; 135 | } 136 | 137 | @FunctionalInterface 138 | public interface Result { 139 | Collection getNames(FabricClientCommandSource source, Supplier> holders) throws CommandSyntaxException; 140 | } 141 | 142 | public static class SelectorResult implements Result { 143 | private final CEntitySelector selector; 144 | 145 | public SelectorResult(CEntitySelector selector) { 146 | this.selector = selector; 147 | } 148 | 149 | @Override 150 | public Collection getNames(FabricClientCommandSource fabricClientCommandSource, Supplier> supplier) throws CommandSyntaxException { 151 | List list = this.selector.findEntities(fabricClientCommandSource); 152 | if (list.isEmpty()) { 153 | throw CEntityArgument.ENTITY_NOT_FOUND_EXCEPTION.create(); 154 | } 155 | return List.copyOf(list); 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CScoreboardSlotArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 8 | import com.mojang.brigadier.suggestion.Suggestions; 9 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 10 | import net.minecraft.commands.SharedSuggestionProvider; 11 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 12 | import net.minecraft.world.scores.DisplaySlot; 13 | import net.minecraft.network.chat.Component; 14 | 15 | import java.util.Arrays; 16 | import java.util.Collection; 17 | import java.util.concurrent.CompletableFuture; 18 | 19 | public class CScoreboardSlotArgument implements ArgumentType { 20 | private static final Collection EXAMPLES = Arrays.asList("sidebar", "foo.bar"); 21 | public static final DynamicCommandExceptionType INVALID_SLOT_EXCEPTION = new DynamicCommandExceptionType(name -> Component.translatableEscape("argument.scoreboardDisplaySlot.invalid", name)); 22 | 23 | private CScoreboardSlotArgument() { 24 | } 25 | 26 | public static CScoreboardSlotArgument scoreboardSlot() { 27 | return new CScoreboardSlotArgument(); 28 | } 29 | 30 | public static DisplaySlot getScoreboardSlot(final CommandContext context, final String name) { 31 | return context.getArgument(name, DisplaySlot.class); 32 | } 33 | 34 | @Override 35 | public DisplaySlot parse(final StringReader stringReader) throws CommandSyntaxException { 36 | String string = stringReader.readUnquotedString(); 37 | DisplaySlot displaySlot = DisplaySlot.CODEC.byName(string); 38 | if (displaySlot == null) { 39 | throw INVALID_SLOT_EXCEPTION.createWithContext(stringReader, string); 40 | } 41 | return displaySlot; 42 | } 43 | 44 | @Override 45 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 46 | return SharedSuggestionProvider.suggest(Arrays.stream(DisplaySlot.values()).map(DisplaySlot::getSerializedName), builder); 47 | } 48 | 49 | @Override 50 | public Collection getExamples() { 51 | return EXAMPLES; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CSlotArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 8 | import com.mojang.brigadier.suggestion.Suggestions; 9 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 10 | import net.minecraft.commands.SharedSuggestionProvider; 11 | import net.minecraft.world.inventory.SlotRange; 12 | import net.minecraft.world.inventory.SlotRanges; 13 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 14 | import net.minecraft.network.chat.Component; 15 | import net.minecraft.commands.ParserUtils; 16 | 17 | import java.util.Arrays; 18 | import java.util.Collection; 19 | import java.util.concurrent.CompletableFuture; 20 | 21 | public class CSlotArgument implements ArgumentType { 22 | private static final Collection EXAMPLES = Arrays.asList("container.5", "weapon"); 23 | private static final DynamicCommandExceptionType UNKNOWN_SLOT_EXCEPTION = new DynamicCommandExceptionType(name -> Component.translatableEscape("slot.unknown", name)); 24 | private static final DynamicCommandExceptionType ONLY_SINGLE_ALLOWED_EXCEPTION = new DynamicCommandExceptionType(name -> Component.translatableEscape("slot.only_single_allowed", name)); 25 | 26 | public static CSlotArgument itemSlot() { 27 | return new CSlotArgument(); 28 | } 29 | 30 | public static int getItemSlot(final CommandContext context, final String name) { 31 | return context.getArgument(name, Integer.class); 32 | } 33 | 34 | @Override 35 | public Integer parse(final StringReader stringReader) throws CommandSyntaxException { 36 | String string = ParserUtils.readWhile(stringReader, c -> c != ' '); 37 | SlotRange slotRange = SlotRanges.nameToIds(string); 38 | if (slotRange == null) { 39 | throw UNKNOWN_SLOT_EXCEPTION.createWithContext(stringReader, string); 40 | } 41 | if (slotRange.size() != 1) { 42 | throw ONLY_SINGLE_ALLOWED_EXCEPTION.createWithContext(stringReader, string); 43 | } 44 | return slotRange.slots().getInt(0); 45 | } 46 | 47 | @Override 48 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 49 | return SharedSuggestionProvider.suggest(SlotRanges.singleSlotNames(), builder); 50 | } 51 | 52 | @Override 53 | public Collection getExamples() { 54 | return EXAMPLES; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CSlotsArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 8 | import com.mojang.brigadier.suggestion.Suggestions; 9 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 10 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 11 | import net.minecraft.commands.SharedSuggestionProvider; 12 | import net.minecraft.world.inventory.SlotRange; 13 | import net.minecraft.world.inventory.SlotRanges; 14 | import net.minecraft.network.chat.Component; 15 | import net.minecraft.commands.ParserUtils; 16 | 17 | import java.util.Collection; 18 | import java.util.List; 19 | import java.util.concurrent.CompletableFuture; 20 | 21 | public class CSlotsArgument implements ArgumentType { 22 | private static final Collection EXAMPLES = List.of("container.*", "container.5", "weapon"); 23 | private static final DynamicCommandExceptionType UNKNOWN_SLOT_EXCEPTION = new DynamicCommandExceptionType(slotRange -> Component.translatableEscape("slot.unknown", slotRange)); 24 | 25 | public static CSlotsArgument slots() { 26 | return new CSlotsArgument(); 27 | } 28 | 29 | public static SlotRange getSlots(final CommandContext context, final String name) { 30 | return context.getArgument(name, SlotRange.class); 31 | } 32 | 33 | @Override 34 | public SlotRange parse(final StringReader stringReader) throws CommandSyntaxException { 35 | String string = ParserUtils.readWhile(stringReader, c -> c != ' '); 36 | SlotRange slotRange = SlotRanges.nameToIds(string); 37 | if (slotRange == null) { 38 | throw UNKNOWN_SLOT_EXCEPTION.createWithContext(stringReader, string); 39 | } 40 | return slotRange; 41 | } 42 | 43 | @Override 44 | public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder suggestionsBuilder) { 45 | return SharedSuggestionProvider.suggest(SlotRanges.allNames(), suggestionsBuilder); 46 | } 47 | 48 | @Override 49 | public Collection getExamples() { 50 | return EXAMPLES; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/dev/xpple/clientarguments/arguments/CStyleArgument.java: -------------------------------------------------------------------------------- 1 | package dev.xpple.clientarguments.arguments; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 5 | import net.minecraft.commands.CommandBuildContext; 6 | import net.minecraft.core.HolderLookup; 7 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 8 | import net.minecraft.nbt.NbtOps; 9 | import net.minecraft.nbt.SnbtGrammar; 10 | import net.minecraft.nbt.Tag; 11 | import net.minecraft.network.chat.Style; 12 | import net.minecraft.network.chat.Component; 13 | import net.minecraft.util.parsing.packrat.commands.CommandArgumentParser; 14 | import net.minecraft.util.parsing.packrat.commands.ParserBasedArgument; 15 | 16 | import java.util.Collection; 17 | import java.util.List; 18 | 19 | public class CStyleArgument extends ParserBasedArgument