├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── main ├── java └── org │ └── example │ ├── ExampleCommand.java │ ├── ExampleHudElement.java │ ├── ExampleModule.java │ └── ExamplePlugin.java └── resources ├── exampleplugin └── graphics │ └── rh_head.png └── rusherhack-plugin.json /.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 | 120 | lib/ 121 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 2 | All rights reserved. 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RusherHack Example Plugin 2 | 3 | An example implementation of a RusherHack plugin. 4 | 5 | RusherHack plugins are custom add-ons written by developers to add new features to RusherHack. 6 | 7 | ## Setting up the Development Environment 8 | 9 | ### Installing Java Development Kit (JDK) 10 | 11 | For Minecraft versions 1.20.1-1.20.4, you will need to have JDK 17 installed. You can download it [here](https://www.azul.com/downloads/?version=java-17-lts&package=jdk#zulu). 12 | 13 | For Minecraft versions 1.20.5 and newer, you will need to have JDK 21 installed. You can download it [here](https://www.azul.com/downloads/?version=java-21-lts&package=jdk#zulu). 14 | 15 | ### Installing IntelliJ IDEA 16 | 17 | IntelliJ IDEA is the preferred Java IDE for rusherhack plugin development. 18 | 19 | IntelliJ IDEA can be downloaded from [the official website](https://www.jetbrains.com/idea/download). 20 | 21 | The Community edition is recommended because it is free and open-source. 22 | 23 | ## Clone the repository 24 | 25 | In IntelliJ, when creating a new project there is an option to clone a repository: 26 | 27 | cloning 28 | 29 | In the URL, write the text `https://github.com/RusherDevelopment/example-plugin.git` and press `Clone` 30 | 31 | cloning2 32 | 33 | IntelliJ may prompt you asking if you trust the project. Click `Trust Project` to continue. 34 | 35 | You should now have a window that looks like this: 36 | 37 | project 38 | 39 | ## Modifying the template 40 | 41 | - Open the `gradle.properties` file and modify it to your preferences. 42 | - Open the `src/main/resources/rusherhack-plugin.json` file and modify it to your preferences. 43 | - The `Plugin-Class` property must match the main class of your plugin. 44 | - The `Name`, `Version`, and `Minecraft-Versions` properties get automatically filled by the values in the `gradle.properties` file. You can add more Minecraft versions if you wish but compatibility between multiple versions is not guaranteed. 45 | 46 | ## Building the plugin 47 | 48 | To build the plugin .jar file, you can run the `gradle build` task. 49 | 50 | 1. Click on the elephant icon on the right of the screen (Gradle) 51 | 2. Navigate to `Tasks` -> `build` -> `build` and double-click it: 52 | 53 | gradle 54 | 55 | 3. The compiled plugin .jar file will be located in the `build/libs` directory: 56 | 57 | build 58 | 59 | To install the plugin into RusherHack, you can follow the instructions on the [RusherHack plugins repository](https://github.com/RusherDevelopment/rusherhack-plugins?tab=readme-ov-file#installation) 60 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'idea' 3 | id 'java' 4 | id 'fabric-loom' version '1.9-SNAPSHOT' 5 | id 'maven-publish' 6 | } 7 | 8 | version = project.plugin_version 9 | group = project.maven_group 10 | archivesBaseName = project.plugin_name 11 | 12 | configurations { 13 | rusherhackApi 14 | compileOnly.extendsFrom(rusherhackApi) 15 | productionRuntime { 16 | extendsFrom configurations.minecraftLibraries 17 | extendsFrom configurations.loaderLibraries 18 | extendsFrom configurations.minecraftRuntimeLibraries 19 | } 20 | } 21 | 22 | repositories { 23 | // Add repositories to retrieve artifacts from in here. 24 | // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. 25 | // See https://docs.gradle.org/current/userguide/declaring_repositories.html 26 | // for more information about repositories. 27 | 28 | maven { 29 | name = "rusherhack" 30 | //releases repository will have the latest api version for last stable rusherhack release 31 | //snapshots will always be the latest api version 32 | //url = "https://maven.rusherhack.org/releases" 33 | url = "https://maven.rusherhack.org/snapshots" 34 | } 35 | 36 | maven { 37 | name = 'ParchmentMC' 38 | url = 'https://maven.parchmentmc.org' 39 | } 40 | } 41 | 42 | dependencies { 43 | minecraft "com.mojang:minecraft:${project.minecraft_version}" 44 | productionRuntime(modImplementation("net.fabricmc:fabric-loader:${project.fabric_loader_version}")) 45 | productionRuntime("net.fabricmc:intermediary:${project.minecraft_version}") 46 | 47 | //mojmap + parchment mappings 48 | mappings loom.layered() { 49 | officialMojangMappings() 50 | parchment("org.parchmentmc.data:parchment-${project.parchment_version}@zip") 51 | } 52 | 53 | //rusherhack api 54 | rusherhackApi "org.rusherhack:rusherhack-api:$minecraft_version-SNAPSHOT" 55 | } 56 | 57 | tasks.register('updateAccessWidener', Copy) { 58 | group = "build" 59 | outputs.upToDateWhen { false } 60 | 61 | def accessWidenerFile = zipTree(this.project.configurations.rusherhackApi.singleFile).matching { 62 | include "rusherhack.accesswidener" 63 | }.files.iterator().next() 64 | 65 | println("rusherhack access widener: " + accessWidenerFile) 66 | 67 | from(accessWidenerFile) 68 | into(file("build/tmp/" + this.project.minecraft_version)) 69 | } 70 | 71 | tasks.register('runPlugin', JavaExec) { 72 | group = "build" 73 | dependsOn remapJar, downloadAssets, copyPluginToRunDir 74 | classpath.from configurations.productionRuntime 75 | mainClass = "net.fabricmc.loader.impl.launch.knot.KnotClient" 76 | workingDir = file("run") 77 | 78 | doFirst { 79 | classpath.from loom.minecraftProvider.minecraftClientJar 80 | workingDir.mkdirs() 81 | 82 | args( 83 | "--assetIndex", loom.minecraftProvider.versionInfo.assetIndex().fabricId(loom.minecraftProvider.minecraftVersion()), 84 | "--assetsDir", new File(loom.files.userCache, "assets").absolutePath, 85 | "--gameDir", workingDir.absolutePath 86 | ) 87 | 88 | def rusherLoaderJarFile = project.layout.getProjectDirectory().file("lib/rusherhack-loader.jar").asFile 89 | if (!rusherLoaderJarFile.exists()) { 90 | throw new GradleException("rusherhack-loader.jar must be copied to the lib directory!") 91 | } 92 | def rusherLoaderJarPath = project.layout.getProjectDirectory().file("lib/rusherhack-loader.jar").asFile.absolutePath 93 | 94 | jvmArgs( 95 | "-Drusherhack.enablePlugins=true", 96 | "-Dfabric.addMods=${rusherLoaderJarPath}", 97 | ) 98 | } 99 | } 100 | 101 | tasks.register("copyPluginToRunDir", Copy) { 102 | group = "build" 103 | dependsOn remapJar 104 | from remapJar.outputs 105 | into file("run/rusherhack/plugins") 106 | } 107 | 108 | loom { 109 | def accessWidenerFile = new File("build/tmp/${this.project.minecraft_version}/rusherhack.accesswidener") 110 | if(accessWidenerFile.exists()) { 111 | accessWidenerPath = accessWidenerFile 112 | } else { 113 | println("RUSHERHACK ACCESS WIDENER NOT FOUND. Please run the 'updateAccessWidener' task.") 114 | } 115 | 116 | //disable run configs 117 | runConfigs.configureEach { 118 | ideConfigGenerated = false 119 | } 120 | } 121 | 122 | def targetJavaVersion = project.java_version 123 | 124 | java.sourceCompatibility = JavaLanguageVersion.of(targetJavaVersion) 125 | java.targetCompatibility = JavaLanguageVersion.of(targetJavaVersion) 126 | java.toolchain.languageVersion.set(JavaLanguageVersion.of(targetJavaVersion)) 127 | 128 | tasks.withType(JavaCompile).configureEach { 129 | // ensure that the encoding is set to UTF-8, no matter what the system default is 130 | // this fixes some edge cases with special characters not displaying correctly 131 | // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html 132 | // If Javadoc is generated, this must be specified in that task too. 133 | it.options.encoding = "UTF-8" 134 | it.options.release = Integer.parseInt(targetJavaVersion) 135 | } 136 | 137 | processResources { 138 | filesMatching("rusherhack-plugin.json") { 139 | expand(project: project) 140 | } 141 | } 142 | 143 | jar { 144 | manifest { 145 | attributes( 146 | "Minecraft-Version": project.minecraft_version 147 | ) 148 | } 149 | } 150 | 151 | //update access widener during intellij sync 152 | this.tasks.ideaSyncTask.dependsOn("updateAccessWidener") 153 | this.tasks.configureLaunch.dependsOn("updateAccessWidener") -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx1G 3 | 4 | # plugin name 5 | plugin_name = example-plugin 6 | 7 | # plugin version 8 | plugin_version = 1.0.0 9 | 10 | maven_group = org.example 11 | 12 | # use java 17 for minecraft versions 1.20.4 and older 13 | # use java 21 for minecraft versions 1.20.5 and newer 14 | java_version = 17 15 | 16 | # minecraft version to compile with 17 | # other supported versions of the game can be added in rusherhack-plugin.json 18 | minecraft_version = 1.20.1 19 | 20 | # parchment mappings 21 | parchment_version = 1.20.1:2023.09.03 22 | 23 | # fabric loader version 24 | fabric_loader_version = 0.16.9 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RusherDevelopment/example-plugin/dc02b0d5d7876bc9fb8935dabdf20cbdc5da9656/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.11-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /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 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Use the maximum available, or set MAX_FD != -1 to use that value. 89 | MAX_FD=maximum 90 | 91 | warn () { 92 | echo "$*" 93 | } >&2 94 | 95 | die () { 96 | echo 97 | echo "$*" 98 | echo 99 | exit 1 100 | } >&2 101 | 102 | # OS specific support (must be 'true' or 'false'). 103 | cygwin=false 104 | msys=false 105 | darwin=false 106 | nonstop=false 107 | case "$( uname )" in #( 108 | CYGWIN* ) cygwin=true ;; #( 109 | Darwin* ) darwin=true ;; #( 110 | MSYS* | MINGW* ) msys=true ;; #( 111 | NONSTOP* ) nonstop=true ;; 112 | esac 113 | 114 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 115 | 116 | 117 | # Determine the Java command to use to start the JVM. 118 | if [ -n "$JAVA_HOME" ] ; then 119 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 120 | # IBM's JDK on AIX uses strange locations for the executables 121 | JAVACMD=$JAVA_HOME/jre/sh/java 122 | else 123 | JAVACMD=$JAVA_HOME/bin/java 124 | fi 125 | if [ ! -x "$JAVACMD" ] ; then 126 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 127 | 128 | Please set the JAVA_HOME variable in your environment to match the 129 | location of your Java installation." 130 | fi 131 | else 132 | JAVACMD=java 133 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 134 | 135 | Please set the JAVA_HOME variable in your environment to match the 136 | location of your Java installation." 137 | fi 138 | 139 | # Increase the maximum file descriptors if we can. 140 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 141 | case $MAX_FD in #( 142 | max*) 143 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 144 | # shellcheck disable=SC3045 145 | MAX_FD=$( ulimit -H -n ) || 146 | warn "Could not query maximum file descriptor limit" 147 | esac 148 | case $MAX_FD in #( 149 | '' | soft) :;; #( 150 | *) 151 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 152 | # shellcheck disable=SC3045 153 | ulimit -n "$MAX_FD" || 154 | warn "Could not set maximum file descriptor limit to $MAX_FD" 155 | esac 156 | fi 157 | 158 | # Collect all arguments for the java command, stacking in reverse order: 159 | # * args from the command line 160 | # * the main class name 161 | # * -classpath 162 | # * -D...appname settings 163 | # * --module-path (only if needed) 164 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 165 | 166 | # For Cygwin or MSYS, switch paths to Windows format before running java 167 | if "$cygwin" || "$msys" ; then 168 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 169 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 170 | 171 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 172 | 173 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 174 | for arg do 175 | if 176 | case $arg in #( 177 | -*) false ;; # don't mess with options #( 178 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 179 | [ -e "$t" ] ;; #( 180 | *) false ;; 181 | esac 182 | then 183 | arg=$( cygpath --path --ignore --mixed "$arg" ) 184 | fi 185 | # Roll the args list around exactly as many times as the number of 186 | # args, so each arg winds up back in the position where it started, but 187 | # possibly modified. 188 | # 189 | # NB: a `for` loop captures its iteration list before it begins, so 190 | # changing the positional parameters here affects neither the number of 191 | # iterations, nor the values presented in `arg`. 192 | shift # remove old arg 193 | set -- "$@" "$arg" # push replacement arg 194 | done 195 | fi 196 | 197 | 198 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 199 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 200 | 201 | # Collect all arguments for the java command; 202 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 203 | # shell script including quotes and variable substitutions, so put them in 204 | # double quotes to make sure that they get re-expanded; and 205 | # * put everything else in single quotes, so that it's not re-expanded. 206 | 207 | set -- \ 208 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 209 | -classpath "$CLASSPATH" \ 210 | org.gradle.wrapper.GradleWrapperMain \ 211 | "$@" 212 | 213 | # Stop when "xargs" is not available. 214 | if ! command -v xargs >/dev/null 2>&1 215 | then 216 | die "xargs is not available" 217 | fi 218 | 219 | # Use "xargs" to parse quoted args. 220 | # 221 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 222 | # 223 | # In Bash we could simply go: 224 | # 225 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 226 | # set -- "${ARGS[@]}" "$@" 227 | # 228 | # but POSIX shell has neither arrays nor command substitution, so instead we 229 | # post-process each arg (as a line of input to sed) to backslash-escape any 230 | # character that might be a shell metacharacter, then use eval to reverse 231 | # that process (while maintaining the separation between arguments), and wrap 232 | # the whole thing up as a single "set" statement. 233 | # 234 | # This will of course break if any of these variables contains a newline or 235 | # an unmatched quote. 236 | # 237 | 238 | eval "set -- $( 239 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 240 | xargs -n1 | 241 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 242 | tr '\n' ' ' 243 | )" '"$@"' 244 | 245 | exec "$JAVACMD" "$@" 246 | -------------------------------------------------------------------------------- /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 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /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/org/example/ExampleCommand.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | import org.rusherhack.client.api.feature.command.Command; 4 | import org.rusherhack.core.command.annotations.CommandExecutor; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | /** 12 | * Example rusherhack command 13 | * 14 | * @author John200410 15 | */ 16 | public class ExampleCommand extends Command { 17 | 18 | private final List stringList = new ArrayList<>(Arrays.asList("test1", "test2")); 19 | 20 | public ExampleCommand() { 21 | super("ExampleCommand", "description"); 22 | } 23 | 24 | /** 25 | * base command that takes in no arguments 26 | */ 27 | @CommandExecutor 28 | private String example() { 29 | //when return type is String you return the message you want to return to the user 30 | return "Hello World!"; 31 | } 32 | 33 | /** 34 | * arguments example 35 | */ 36 | @CommandExecutor 37 | @CommandExecutor.Argument({"string", "boolean"}) //must set argument names 38 | private String exampleWithArguments(String requiredString, Optional optionalBoolean) { 39 | return requiredString + " " + optionalBoolean.orElse(false); 40 | } 41 | 42 | /** 43 | * sub command examples 44 | */ 45 | @CommandExecutor(subCommand = "list") 46 | private String exampleList() { 47 | return String.join(", ", this.stringList); 48 | } 49 | 50 | @CommandExecutor(subCommand = "add") 51 | @CommandExecutor.Argument("string") //must set argument names 52 | private String addToExampleList(String string) { 53 | this.stringList.add(string); 54 | return "Added " + string; 55 | } 56 | 57 | @CommandExecutor(subCommand = {"remove", "del"}) 58 | @CommandExecutor.Argument("string") //must set argument names 59 | private String removeFromExampleList(String string) { 60 | if(this.stringList.remove(string)) { 61 | return "Removed " + string; 62 | } 63 | return string + " not found"; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/example/ExampleHudElement.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | import org.rusherhack.client.api.feature.hud.ResizeableHudElement; 4 | import org.rusherhack.client.api.render.RenderContext; 5 | import org.rusherhack.client.api.render.graphic.TextureGraphic; 6 | 7 | /** 8 | * Example rusherhack hud element 9 | *

10 | * There are other hud element types than ResizeableHudElement, look at the other classes in the package 11 | * 12 | * @author John200410 13 | */ 14 | public class ExampleHudElement extends ResizeableHudElement { 15 | 16 | private TextureGraphic graphic = null; 17 | 18 | public ExampleHudElement() { 19 | super("ExampleHudElement"); 20 | 21 | //try loading graphic 22 | try { 23 | this.graphic = new TextureGraphic("exampleplugin/graphics/rh_head.png", 235, 234); 24 | } catch (Throwable t) { 25 | this.getLogger().error("Failed to load graphic", t); 26 | } 27 | } 28 | 29 | @Override 30 | public void renderContent(RenderContext context, double mouseX, double mouseY) { 31 | //positions are relative to the top left corner of the hud element, so start drawing stuff from 0,0 32 | 33 | if (this.graphic != null) { 34 | this.getRenderer().drawGraphicRectangle(this.graphic, 0, 0, this.getWidth(), this.getHeight()); 35 | } 36 | } 37 | 38 | @Override 39 | public double getWidth() { 40 | return 200; 41 | } 42 | 43 | @Override 44 | public double getHeight() { 45 | return 200; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/example/ExampleModule.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | import net.minecraft.world.entity.Entity; 4 | import net.minecraft.world.entity.LivingEntity; 5 | import org.rusherhack.client.api.RusherHackAPI; 6 | import org.rusherhack.client.api.events.client.EventUpdate; 7 | import org.rusherhack.client.api.events.render.EventRender2D; 8 | import org.rusherhack.client.api.events.render.EventRender3D; 9 | import org.rusherhack.client.api.feature.module.ModuleCategory; 10 | import org.rusherhack.client.api.feature.module.ToggleableModule; 11 | import org.rusherhack.client.api.render.IRenderer2D; 12 | import org.rusherhack.client.api.render.IRenderer3D; 13 | import org.rusherhack.client.api.render.font.IFontRenderer; 14 | import org.rusherhack.client.api.setting.BindSetting; 15 | import org.rusherhack.client.api.setting.ColorSetting; 16 | import org.rusherhack.client.api.utils.ChatUtils; 17 | import org.rusherhack.client.api.utils.WorldUtils; 18 | import org.rusherhack.core.bind.key.NullKey; 19 | import org.rusherhack.core.event.subscribe.Subscribe; 20 | import org.rusherhack.core.setting.BooleanSetting; 21 | import org.rusherhack.core.setting.NumberSetting; 22 | import org.rusherhack.core.setting.StringSetting; 23 | import org.rusherhack.core.utils.ColorUtils; 24 | 25 | import java.awt.*; 26 | 27 | /** 28 | * Example rusherhack module 29 | * 30 | * @author John200410 31 | */ 32 | public class ExampleModule extends ToggleableModule { 33 | 34 | /** 35 | * Settings 36 | */ 37 | private final BooleanSetting exampleBoolean = new BooleanSetting("Boolean", "Settings can optionally have a description", true); 38 | 39 | private final NumberSetting exampleDouble = new NumberSetting<>("Double", 0.0, -10.0, 10.0) 40 | 41 | //specifies incremental step for precise numbers 42 | .incremental(0.1) 43 | 44 | //predicate that determines conditions for the setting to be visible in the clickgui 45 | .setVisibility(this.exampleBoolean::getValue) 46 | 47 | //consumer that is called when the setting is changed 48 | .onChange(d -> ChatUtils.print("Changed double to " + d)); 49 | 50 | private final ColorSetting exampleColor = new ColorSetting("Color", Color.CYAN) 51 | 52 | //set whether alpha is enabled in the color picker 53 | .setAlphaAllowed(false) 54 | 55 | //sync the color with the theme color 56 | .setThemeSync(true); 57 | 58 | private final StringSetting exampleString = new StringSetting("String", "Hello World!") 59 | 60 | //disables the rendering of the setting name in the clickgui 61 | .setNameVisible(false); 62 | 63 | private final BindSetting rotate = new BindSetting("RotateBind", NullKey.INSTANCE /* unbound */); 64 | private final NumberSetting rotateYaw = new NumberSetting<>("Yaw", 0f, 0f, 360f).incremental(0.1f); 65 | private final NumberSetting rotatePitch = new NumberSetting<>("Pitch", 0f, -90f, 90f).incremental(0.1f); 66 | 67 | /** 68 | * Constructor 69 | */ 70 | public ExampleModule() { 71 | super("Example", "Example plugin module", ModuleCategory.CLIENT); 72 | 73 | //subsettings 74 | this.rotate.addSubSettings(this.rotateYaw, this.rotatePitch); 75 | 76 | //register settings 77 | this.registerSettings( 78 | this.exampleBoolean, 79 | this.exampleDouble, 80 | this.exampleColor, 81 | this.exampleString, 82 | this.rotate 83 | ); 84 | } 85 | 86 | /** 87 | * 2d renderer demo 88 | */ 89 | @Subscribe 90 | private void onRender2D(EventRender2D event) { 91 | 92 | //renderers 93 | final IRenderer2D renderer = RusherHackAPI.getRenderer2D(); 94 | final IFontRenderer fontRenderer = RusherHackAPI.fonts().getFontRenderer(); 95 | 96 | //must begin renderer first 97 | renderer.begin(event.getMatrixStack(), fontRenderer); 98 | 99 | //draw stuff 100 | renderer.drawRectangle(100, 100 + this.exampleDouble.getValue(), 100, 100, this.exampleColor.getValueRGB()); 101 | fontRenderer.drawString(this.exampleString.getValue(), 110, 110, Color.WHITE.getRGB()); 102 | 103 | //end renderer 104 | renderer.end(); 105 | 106 | } 107 | 108 | /** 109 | * Rotation demo 110 | */ 111 | @Subscribe 112 | private void onUpdate(EventUpdate event) { 113 | 114 | //only rotate while bind is held 115 | if(this.rotate.getValue().isKeyDown()) { 116 | 117 | //loop through entities to find a target 118 | Entity target = null; 119 | double dist = 999d; 120 | for(Entity entity : WorldUtils.getEntitiesSorted()) { 121 | if(mc.player.distanceTo(entity) < dist && entity instanceof LivingEntity) { 122 | target = entity; 123 | dist = mc.player.distanceTo(entity); 124 | } 125 | } 126 | 127 | //rotate to target 128 | if(target != null) { 129 | RusherHackAPI.getRotationManager().updateRotation(target); 130 | } else { //or rotate to the custom yaw 131 | RusherHackAPI.getRotationManager().updateRotation(this.rotateYaw.getValue(), this.rotatePitch.getValue()); 132 | } 133 | } 134 | } 135 | 136 | //3d renderer demo 137 | @Subscribe 138 | private void onRender3D(EventRender3D event) { 139 | final IRenderer3D renderer = event.getRenderer(); 140 | 141 | final int color = ColorUtils.transparency(this.exampleColor.getValueRGB(), 0.5f); //fill colors look better when the alpha is not 100% 142 | 143 | //begin renderer 144 | renderer.begin(event.getMatrixStack()); 145 | 146 | //highlight targets 147 | for(Entity entity : WorldUtils.getEntities()) { 148 | renderer.drawBox(entity, event.getPartialTicks(), true, true, color); 149 | } 150 | 151 | //end renderer 152 | renderer.end(); 153 | } 154 | 155 | @Override 156 | public void onEnable() { 157 | if(mc.level != null) { 158 | ChatUtils.print("Hello World! Example module is enabled"); 159 | } 160 | } 161 | 162 | @Override 163 | public void onDisable() { 164 | if(mc.level != null) { 165 | ChatUtils.print("Goodbye World! Example module has been disabled"); 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/main/java/org/example/ExamplePlugin.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | import org.rusherhack.client.api.RusherHackAPI; 4 | import org.rusherhack.client.api.plugin.Plugin; 5 | 6 | /** 7 | * Example rusherhack plugin 8 | * 9 | * @author John200410 10 | */ 11 | public class ExamplePlugin extends Plugin { 12 | 13 | @Override 14 | public void onLoad() { 15 | 16 | //logger 17 | this.getLogger().info("Hello World!"); 18 | 19 | //creating and registering a new module 20 | final ExampleModule exampleModule = new ExampleModule(); 21 | RusherHackAPI.getModuleManager().registerFeature(exampleModule); 22 | 23 | //creating and registering a new hud element 24 | final ExampleHudElement exampleHudElement = new ExampleHudElement(); 25 | RusherHackAPI.getHudManager().registerFeature(exampleHudElement); 26 | 27 | //creating and registering a new command 28 | final ExampleCommand exampleCommand = new ExampleCommand(); 29 | RusherHackAPI.getCommandManager().registerFeature(exampleCommand); 30 | } 31 | 32 | @Override 33 | public void onUnload() { 34 | this.getLogger().info("Example plugin unloaded!"); 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /src/main/resources/exampleplugin/graphics/rh_head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RusherDevelopment/example-plugin/dc02b0d5d7876bc9fb8935dabdf20cbdc5da9656/src/main/resources/exampleplugin/graphics/rh_head.png -------------------------------------------------------------------------------- /src/main/resources/rusherhack-plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin-Class": "org.example.ExamplePlugin", 3 | "Name": "${project.plugin_name}", 4 | "Version": "${project.plugin_version}", 5 | "Description": "Example rusherhack plugin", 6 | "URL": "https://github.com/RusherDevelopment/example-plugin", 7 | "Authors": [ 8 | "John200410" 9 | ], 10 | "Minecraft-Versions": [ 11 | "${project.minecraft_version}" 12 | ] 13 | } --------------------------------------------------------------------------------