├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── client ├── java └── net │ └── staro │ ├── api │ ├── Command.java │ ├── Manager.java │ ├── Module.java │ ├── PlayerWorldInteractionManagerConsumer.java │ ├── Priority.java │ ├── Setting.java │ ├── SettingFactory.java │ ├── SettingManager.java │ ├── annotation │ │ └── SafeListener.java │ ├── injection │ │ ├── Client.java │ │ └── PreClient.java │ ├── listener │ │ ├── SafeEventConsumer.java │ │ └── SafeMultiEventConsumer.java │ ├── trait │ │ ├── Bindable.java │ │ ├── Completable.java │ │ ├── Constants.java │ │ ├── HasDescription.java │ │ ├── Jsonable.java │ │ ├── Nameable.java │ │ ├── Subscriber.java │ │ └── Toggleable.java │ └── util │ │ └── SafeUtil.java │ └── lego │ ├── Lego.java │ ├── LegoClient.java │ ├── PreClientInitializer.java │ ├── command │ ├── Chat.java │ ├── LegoArgument.java │ ├── LegoCommand.java │ ├── arguments │ │ ├── BindArgument.java │ │ ├── ConfigArgument.java │ │ ├── FriendArgument.java │ │ ├── ModuleArgument.java │ │ ├── PlayerArgument.java │ │ ├── SettingArgument.java │ │ ├── SettingValueArgument.java │ │ └── StringArgument.java │ └── commands │ │ ├── BindCommand.java │ │ ├── ConfigCommand.java │ │ ├── FriendCommand.java │ │ ├── HelpCommand.java │ │ ├── MessageCommand.java │ │ ├── ModuleCommand.java │ │ ├── ModulesCommand.java │ │ ├── PrefixCommand.java │ │ ├── QuitCommand.java │ │ └── ToggleCommand.java │ ├── ducks │ └── IChatHud.java │ ├── events │ ├── CancellableEvent.java │ ├── PacketEvent.java │ ├── camera │ │ └── CameraUpdateEvent.java │ ├── chat │ │ ├── ChatEvent.java │ │ └── InputSuggestorEvent.java │ ├── client │ │ ├── InitEvent.java │ │ ├── KeyEvent.java │ │ ├── ShutdownEvent.java │ │ └── ToggleEvent.java │ └── tick │ │ ├── GameloopEvent.java │ │ ├── PostUpdateEvent.java │ │ ├── PreUpdateEvent.java │ │ ├── TickEvent.java │ │ └── UpdateEvent.java │ ├── manager │ ├── GenericManager.java │ └── managers │ │ ├── CommandManager.java │ │ ├── ConfigManager.java │ │ ├── FriendManager.java │ │ └── ModuleManager.java │ ├── mixin │ ├── MixinMain.java │ ├── MixinMinecraftClient.java │ ├── chat │ │ ├── MixinChatHud.java │ │ └── MixinChatInputSuggestor.java │ ├── client │ │ ├── MixinGameOptions.java │ │ └── MixinKeyboard.java │ ├── entity │ │ ├── MixinClientPlayerEntity.java │ │ ├── MixinEntity.java │ │ └── MixinLivingEntity.java │ ├── network │ │ ├── IExplosionS2CPacket.java │ │ ├── MixinClientConnection.java │ │ └── MixinClientPlayNetworkHandler.java │ └── render │ │ ├── MixinCamera.java │ │ └── MixinLightmapTextureManager.java │ ├── module │ ├── AbstractModule.java │ ├── Category.java │ ├── LegoModule.java │ └── modules │ │ ├── ClickGui.java │ │ ├── Fullbright.java │ │ ├── Heaven.java │ │ ├── Notifications.java │ │ ├── Step.java │ │ └── Velocity.java │ ├── service │ ├── BindService.java │ ├── ChatMessageService.java │ ├── FirstLaunchService.java │ ├── KeyboardService.java │ └── ShutdownService.java │ ├── setting │ ├── AbstractSetting.java │ ├── GenericSetting.java │ ├── SettingFactoryImpl.java │ ├── converter │ │ └── EnumConverter.java │ ├── settings │ │ ├── BindSetting.java │ │ ├── BooleanSetting.java │ │ ├── ColorSetting.java │ │ ├── DoubleSetting.java │ │ ├── FloatSetting.java │ │ ├── IntegerSetting.java │ │ └── StringSetting.java │ └── type │ │ └── Bind.java │ └── utility │ ├── ColorUtil.java │ ├── ConfigUtil.java │ ├── MixinHelper.java │ └── SettingUtil.java └── resources ├── fabric.mod.json └── lego.client.mixins.json /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # Linux start script should use lf 5 | /gradlew text eol=lf 6 | 7 | # These are Windows script files and should use crlf 8 | *.bat text eol=crlf 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | 35 | # java 36 | 37 | hs_err_*.log 38 | replay_*.log 39 | *.hprof 40 | *.jfr 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 selfpop 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LEGO CLIENT 2 | [![CodeFactor](https://www.codefactor.io/repository/github/starobot/legoclient/badge)](https://www.codefactor.io/repository/github/starobot/legoclient) 3 | [![](https://tokei.rs/b1/github/starobot/LegoClient)](https://github.com/starobot/LegoClient) 4 | 5 | An open-source utility client base for making a minecraft client/mod as fast and easy as possible. 6 | This base only supports fabric api. 7 | 8 | // TODO: Update to 1.21.4 9 | // TODO: Update EventBus to the latest Rokit. 10 | // TODO: Proper registry. 11 | // TODO: Proper serialization. 12 | 13 | ### Make modules 14 | ```java 15 | public class YourModule extends LegoModule { 16 | public YourModule(Lego lego) { 17 | super(lego, "YourModule", Category.RENDER, "Does something on your screen"); 18 | } 19 | 20 | } 21 | ``` 22 | ### Make commands 23 | ```java 24 | public class YourCommand extends LegoCommand { 25 | public YourCommand(Lego lego) { 26 | super(lego, "command", "Does something"); 27 | } 28 | 29 | @Override 30 | public void build(LiteralArgumentBuilder builder) { 31 | builder.executes(context -> { // execute immediately 32 | // do something 33 | return COMPLETED; 34 | }); 35 | } 36 | 37 | } 38 | ``` 39 | 40 | ## Why Lego class is the argument in everything? 41 | Lego is the record class with all the necessary managers and the eventbus. It serves as an access point for all of them. Whenever you need a manager instance, you can do this: 42 | ```java 43 | lego.moduleManager().getModules() 44 | ``` 45 | ## How to use the event bus? 46 | ```java 47 | // subscribe the listener class to the eventBus 48 | lego.eventBus().subscribe(new ListenerClass()); 49 | 50 | // post the event to the eventBus. 51 | lego.eventBus().post(new CustomEvent()); 52 | ``` 53 | 54 | Using events within the listener classes 55 | ```java 56 | @Listener // for a regular listener 57 | public void onEvent(CustomEvent event) { // the method MUST BE PUBLIC 58 | // do something. 59 | } 60 | 61 | @SafeListener // for a nullsafe listener (the listeer that checks mc.player, mc.world and mc.interactionManager for being null) 62 | public void onEvent(CustomEvent event) { 63 | // do something. 64 | } 65 | ``` 66 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'fabric-loom' version '1.7-SNAPSHOT' 3 | id 'maven-publish' 4 | } 5 | 6 | version = project.mod_version 7 | group = project.maven_group 8 | 9 | base { 10 | archivesName = project.archives_base_name 11 | } 12 | 13 | repositories { 14 | mavenCentral() 15 | maven { 16 | url 'https://jitpack.io' 17 | } 18 | } 19 | 20 | loom { 21 | splitEnvironmentSourceSets() 22 | 23 | mods { 24 | "lego" { 25 | sourceSet sourceSets.main 26 | sourceSet sourceSets.client 27 | } 28 | } 29 | 30 | } 31 | 32 | dependencies { 33 | // To change the versions see the gradle.properties file 34 | minecraft "com.mojang:minecraft:${project.minecraft_version}" 35 | mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" 36 | modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" 37 | 38 | // Fabric API. This is technically optional, but you probably want it anyway. 39 | modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" 40 | 41 | //Lombok dependency. Please check the lombok plugin if you're using Intellij 42 | compileOnly 'org.projectlombok:lombok:1.18.34' 43 | annotationProcessor 'org.projectlombok:lombok:1.18.34' 44 | testCompileOnly 'org.projectlombok:lombok:1.18.34' 45 | testAnnotationProcessor 'org.projectlombok:lombok:1.18.34' 46 | 47 | // Rokit EventBus 48 | include(implementation 'com.github.starobot:Rokit:1.2') 49 | } 50 | 51 | processResources { 52 | inputs.property "version", project.version 53 | 54 | filesMatching("fabric.mod.json") { 55 | expand "version": project.version 56 | } 57 | } 58 | 59 | tasks.withType(JavaCompile).configureEach { 60 | it.options.release = 21 61 | } 62 | 63 | java { 64 | // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task 65 | // if it is present. 66 | // If you remove this line, sources will not be generated. 67 | withSourcesJar() 68 | 69 | sourceCompatibility = JavaVersion.VERSION_21 70 | targetCompatibility = JavaVersion.VERSION_21 71 | } 72 | 73 | jar { 74 | from("LICENSE") { 75 | rename { "${it}_${project.base.archivesName.get()}"} 76 | } 77 | } 78 | 79 | // configure the maven publication 80 | publishing { 81 | publications { 82 | create("mavenJava", MavenPublication) { 83 | artifactId = project.archives_base_name 84 | from components.java 85 | } 86 | } 87 | 88 | // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. 89 | repositories { 90 | // Add repositories to publish to here. 91 | // Notice: This block does NOT have the same function as the block in the top level. 92 | // The repositories here will be used for publishing your artifact, not for 93 | // retrieving dependencies. 94 | } 95 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx1G 3 | org.gradle.parallel=true 4 | 5 | # Fabric Properties 6 | # check these on https://fabricmc.net/develop 7 | minecraft_version=1.21.1 8 | yarn_mappings=1.21.1+build.3 9 | loader_version=0.16.2 10 | 11 | # Mod Properties 12 | mod_version=1.0.2 13 | maven_group=net.staro.lego 14 | archives_base_name=lego 15 | 16 | # Dependencies 17 | fabric_version=0.102.1+1.21.1 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starobot/LegoClient/f0b7eb66a3bafbd0c1ea05a7c1020dc3dc361648/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.10-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 | mavenCentral() 8 | gradlePluginPortal() 9 | } 10 | } 11 | include 'api' 12 | 13 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/Command.java: -------------------------------------------------------------------------------- 1 | package net.staro.api; 2 | 3 | import com.mojang.brigadier.CommandDispatcher; 4 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 5 | import net.minecraft.command.CommandSource; 6 | import net.staro.api.trait.Completable; 7 | import net.staro.api.trait.HasDescription; 8 | import net.staro.api.trait.Nameable; 9 | 10 | /** 11 | * Represents a client's command which can be accessible via minecraft chat by the user. 12 | */ 13 | public interface Command extends Nameable, HasDescription, Completable 14 | { 15 | /** 16 | * A method which allows to execute a sequence of actions whenever the command is entered. 17 | * @param builder is the mojang brigadier's builder class used to parse and execute the command. 18 | */ 19 | void build(LiteralArgumentBuilder builder); 20 | 21 | /** 22 | * Registers a command with a chosen dispatcher. 23 | * @param dispatcher is the core command dispatcher, for registering, parsing, and executing commands. 24 | */ 25 | void register(CommandDispatcher dispatcher); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/Manager.java: -------------------------------------------------------------------------------- 1 | package net.staro.api; 2 | 3 | import net.staro.lego.Lego; 4 | 5 | /** 6 | * Represents a manager responsible for creating instances of a certain aspect of the project. 7 | */ 8 | public interface Manager 9 | { 10 | /** 11 | * Initializes the manager with the provided Lego instance 12 | * 13 | * @param lego The Lego instance providing access to various components and functionalities 14 | */ 15 | void initialize(Lego lego); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/Module.java: -------------------------------------------------------------------------------- 1 | package net.staro.api; 2 | 3 | import net.staro.api.trait.*; 4 | import net.staro.lego.module.Category; 5 | 6 | /** 7 | * Represents a module which is the main functional part of the client. 8 | * Modules are object that could be toggled, bound and written intoJson. Also, modules are the only objects to have settings withing them. 9 | */ 10 | public interface Module extends 11 | Toggleable, 12 | Nameable, 13 | HasDescription, 14 | SettingFactory, 15 | Jsonable, 16 | Bindable 17 | { 18 | /** 19 | * Gets the category of the module. 20 | * @return a category. 21 | */ 22 | Category getCategory(); 23 | 24 | /** 25 | * Checks if the module is drawn (visible). 26 | * @return true if drawn and false otherwise. 27 | */ 28 | boolean isDrawn(); 29 | 30 | /** 31 | * A method that sets the drawn state to true of false 32 | * @param state true if drawn and false otherwise. 33 | */ 34 | void drawn(boolean state); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/PlayerWorldInteractionManagerConsumer.java: -------------------------------------------------------------------------------- 1 | package net.staro.api; 2 | 3 | import net.minecraft.client.MinecraftClient; 4 | import net.minecraft.client.network.ClientPlayerEntity; 5 | import net.minecraft.client.network.ClientPlayerInteractionManager; 6 | import net.minecraft.client.world.ClientWorld; 7 | 8 | /** 9 | * @author 3arthqu4ke 10 | */ 11 | @FunctionalInterface 12 | public interface PlayerWorldInteractionManagerConsumer 13 | { 14 | /** 15 | * @param player {@link MinecraftClient#player} 16 | * @param world {@link MinecraftClient#world} 17 | * @param interactionManager {@link MinecraftClient#interactionManager} 18 | */ 19 | void accept(ClientPlayerEntity player, ClientWorld world, ClientPlayerInteractionManager interactionManager); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/Priority.java: -------------------------------------------------------------------------------- 1 | package net.staro.api; 2 | 3 | import lombok.Getter; 4 | 5 | import static java.lang.Integer.MAX_VALUE; 6 | 7 | /** 8 | * Represents the priority of an event listener. 9 | */ 10 | @Getter 11 | public enum Priority 12 | { 13 | HIGHEST(MAX_VALUE), 14 | HIGH(3), 15 | MEDIUM(2), 16 | LOW(1), 17 | DEFAULT(0); 18 | 19 | private final int val; 20 | 21 | Priority(int val) 22 | { 23 | this.val = val; 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /src/client/java/net/staro/api/Setting.java: -------------------------------------------------------------------------------- 1 | package net.staro.api; 2 | 3 | import net.staro.api.trait.Nameable; 4 | 5 | /** 6 | * A setting is an object that is linked to {@link Module}. 7 | * Settings can be created and registered for each module to set its values within the game. 8 | * @see SettingManager 9 | * @param 10 | */ 11 | public interface Setting extends Nameable 12 | { 13 | /** 14 | * Gets the value of the setting. 15 | * @return a generic T value. 16 | */ 17 | T getValue(); 18 | 19 | /** 20 | * A method used to set setting to a certain T value. 21 | * @param value a generic T value. 22 | */ 23 | void setValue(T value); 24 | 25 | /** 26 | * Gets the default set value from the setting. 27 | * @return a generic default T value. 28 | */ 29 | T getDefaultValue(); 30 | 31 | /** 32 | * Gets the minimal value of the setting. 33 | * @return a generic minimal T value. 34 | */ 35 | T getMin(); 36 | 37 | /** 38 | * Gets the maximum value of the setting. 39 | * @return a generic maximum T value. 40 | */ 41 | T getMax(); 42 | 43 | String getDescription(); 44 | 45 | /** 46 | * A method used to append a setting to a certain module. 47 | * @param module requires no explanation. 48 | */ 49 | void setModule(Module module); 50 | 51 | /** 52 | * Get the setting value in the form of a String. 53 | * @return generic T type as a String. 54 | */ 55 | String getType(); 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/SettingFactory.java: -------------------------------------------------------------------------------- 1 | package net.staro.api; 2 | 3 | import net.staro.lego.setting.type.Bind; 4 | 5 | import java.awt.*; 6 | 7 | /** 8 | * A factory class that handles creating new setting objects. 9 | * @see Setting 10 | * @see SettingManager 11 | */ 12 | public interface SettingFactory extends SettingManager 13 | { 14 | /** 15 | * Creates and registers a new Setting object with the boolean value. 16 | * @param name is the name of the setting. 17 | * @param defaultValue is the default value of the setting. 18 | * @return a new Setting object with the Boolean value. 19 | */ 20 | Setting bool(String name, boolean defaultValue, String description); 21 | 22 | /** 23 | * Creates and registers a new Setting object with the Integer value. 24 | * @param name is the name of the setting. 25 | * @param defaultValue is the default value of the setting. 26 | * @param minValue is the minimum value of the setting. 27 | * @param maxValue is the maximum value of the setting. 28 | * @return a new Setting object with the Integer value. 29 | */ 30 | Setting integer(String name, int defaultValue, int minValue, int maxValue, String description); 31 | 32 | /** 33 | * Creates and registers a new Setting object with the Float value. 34 | * @param name is the name of the setting. 35 | * @param defaultValue is the default value of the setting. 36 | * @param minValue is the minimum value of the setting. 37 | * @param maxValue is the maximum value of the setting. 38 | * @return a new Setting object with the Float value. 39 | */ 40 | Setting floating(String name, float defaultValue, float minValue, float maxValue, String description); 41 | 42 | /** 43 | * Creates and registers a new Setting object with the Double value. 44 | * @param name is the name of the setting. 45 | * @param defaultValue is the default value of the setting. 46 | * @param minValue is the minimum value of the setting. 47 | * @param maxValue is the maximum value of the setting. 48 | * @return a new Setting object with the Double value. 49 | */ 50 | Setting precise(String name, double defaultValue, double minValue, double maxValue, String description); 51 | 52 | /** 53 | * Creates and registers a new Setting object with the Enum value. 54 | * @param name is the name of the setting. 55 | * @param defaultValue is the default enum value of the setting. 56 | * @return a new Setting object with the Enum value. 57 | */ 58 | > Setting enumSetting(String name, E defaultValue, String description); 59 | 60 | /** 61 | * Creates and registers a new Setting object with the String value. 62 | * @param name is the name of the setting. 63 | * @param defaultValue is the default value of the setting. 64 | * @return a new Setting object with the String value. 65 | */ 66 | Setting string(String name, String defaultValue, String description); 67 | 68 | /** 69 | * Creates and registers a new Setting object with the Color value. 70 | * @param name is the name of the setting. 71 | * @param defaultValue is the default value of the setting. 72 | * @return a new Setting object with the Color value. 73 | */ 74 | Setting color(String name, Color defaultValue, String description); 75 | 76 | /** 77 | * Creates and registers a new Setting object with the {@link Bind} value. 78 | * @param name is the name of the setting. 79 | * @param defaultValue is the default value of the setting. 80 | * @return a new Setting object with the {@link Bind} value. 81 | */ 82 | Setting bind(String name, Bind defaultValue, String description); 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/SettingManager.java: -------------------------------------------------------------------------------- 1 | package net.staro.api; 2 | 3 | import net.staro.lego.setting.GenericSetting; 4 | 5 | import java.util.List; 6 | 7 | public interface SettingManager 8 | { 9 | List> getSettings(); 10 | 11 | GenericSetting getSettingByName(String name); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/annotation/SafeListener.java: -------------------------------------------------------------------------------- 1 | package net.staro.api.annotation; 2 | 3 | import net.staro.api.Priority; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * Annotation to mark methods as safe event listeners, ensuring null checks for {@code mc.player && mc.world && mc.interactionManager}. 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target(ElementType.METHOD) 15 | public @interface SafeListener 16 | { 17 | Priority priority() default Priority.DEFAULT; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/injection/Client.java: -------------------------------------------------------------------------------- 1 | package net.staro.api.injection; 2 | 3 | import net.minecraft.client.MinecraftClient; 4 | 5 | /** 6 | * Self-explanatory. Only the main client class can implement this. 7 | */ 8 | public interface Client 9 | { 10 | /** 11 | * Initializes the client in {@link net.staro.lego.mixin.MixinMinecraftClient} right after the initialization of the MinecraftClient instance. 12 | * @param mc is the null safe MinecraftClass object. 13 | */ 14 | void onInitializeClient(MinecraftClient mc); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/injection/PreClient.java: -------------------------------------------------------------------------------- 1 | package net.staro.api.injection; 2 | 3 | /** 4 | * This is for the part of the client which can be initialized before the MinecraftClient itself. 5 | * Not suitable for most managers and services. 6 | */ 7 | public interface PreClient 8 | { 9 | /** 10 | * Initialize whatever object needed in {@link net.staro.lego.mixin.MixinMain} 11 | */ 12 | void onInitialize(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/listener/SafeEventConsumer.java: -------------------------------------------------------------------------------- 1 | package net.staro.api.listener; 2 | 3 | import bot.staro.rokit.impl.EventConsumerImpl; 4 | import net.minecraft.client.MinecraftClient; 5 | import net.staro.api.util.SafeUtil; 6 | 7 | import java.lang.reflect.Method; 8 | /** 9 | * An extent of Listener to assert {@code mc.player} and {@code mc.world} != null 10 | */ 11 | public class SafeEventConsumer extends EventConsumerImpl 12 | { 13 | private final MinecraftClient mc; 14 | 15 | public SafeEventConsumer(Object instance, Method method, int priority) 16 | { 17 | super(instance, method, priority); 18 | this.mc = MinecraftClient.getInstance(); 19 | } 20 | 21 | @Override 22 | public void invoke(Object event) 23 | { 24 | SafeUtil.safe(mc, 25 | (player, 26 | world, 27 | interactionManager 28 | ) -> super.invoke(event) 29 | ); 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /src/client/java/net/staro/api/listener/SafeMultiEventConsumer.java: -------------------------------------------------------------------------------- 1 | package net.staro.api.listener; 2 | 3 | import bot.staro.rokit.function.EventWrapper; 4 | import bot.staro.rokit.impl.MultiEventConsumer; 5 | import net.minecraft.client.MinecraftClient; 6 | import net.staro.api.util.SafeUtil; 7 | 8 | import java.lang.reflect.Method; 9 | 10 | public class SafeMultiEventConsumer extends MultiEventConsumer 11 | { 12 | private final MinecraftClient mc; 13 | 14 | public SafeMultiEventConsumer(Object instance, Method method, int priority, EventWrapper eventHandler) 15 | { 16 | super(instance, method, priority, eventHandler); 17 | this.mc = MinecraftClient.getInstance(); 18 | } 19 | 20 | @Override 21 | public void invoke(Object event) 22 | { 23 | SafeUtil.safe(mc, 24 | (player, 25 | world, 26 | interactionManager 27 | ) -> super.invoke(event) 28 | ); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/trait/Bindable.java: -------------------------------------------------------------------------------- 1 | package net.staro.api.trait; 2 | 3 | import net.staro.lego.setting.type.Bind; 4 | 5 | /** 6 | * A trait for something that can be bound. 7 | */ 8 | public interface Bindable 9 | { 10 | void setBind(int key); 11 | 12 | Bind getBind(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/trait/Completable.java: -------------------------------------------------------------------------------- 1 | package net.staro.api.trait; 2 | 3 | import static com.mojang.brigadier.Command.SINGLE_SUCCESS; 4 | 5 | // ????????????????? 6 | public interface Completable 7 | { 8 | int COMPLETED = SINGLE_SUCCESS; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/trait/Constants.java: -------------------------------------------------------------------------------- 1 | package net.staro.api.trait; 2 | 3 | import net.fabricmc.loader.api.FabricLoader; 4 | import net.minecraft.client.MinecraftClient; 5 | 6 | import java.io.File; 7 | import java.nio.file.Path; 8 | 9 | public interface Constants 10 | { 11 | // ________ CLIENT NAME AND VERSION ________ // 12 | String NAME = "Lego"; 13 | String NAME_TO_LOWER_CASE = NAME.toLowerCase(); 14 | 15 | // _______________ CONFIGS AND FOLDERS _______________ // 16 | File MINECRAFT_FOLDER = MinecraftClient.getInstance().runDirectory; 17 | File LEGO_FOLDER = new File(MINECRAFT_FOLDER, NAME_TO_LOWER_CASE); 18 | 19 | String MINECRAFT_FOLDER_PATH = MINECRAFT_FOLDER.getPath(); 20 | 21 | Path LEGO_FOLDER_PATH = FabricLoader.getInstance().getGameDir().resolve(NAME_TO_LOWER_CASE); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/trait/HasDescription.java: -------------------------------------------------------------------------------- 1 | package net.staro.api.trait; 2 | 3 | /** 4 | * A trait for something that has a description. Useful to let users know what certain object do. 5 | */ 6 | public interface HasDescription 7 | { 8 | /** 9 | * Gets the description of the object. 10 | * @return description as a String. 11 | */ 12 | String getDescription(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/trait/Jsonable.java: -------------------------------------------------------------------------------- 1 | package net.staro.api.trait; 2 | 3 | import com.google.gson.JsonElement; 4 | 5 | /** 6 | * A trait for anything that ca be converted to a json object. 7 | */ 8 | public interface Jsonable 9 | { 10 | /** 11 | * Creates a new json object. 12 | * @return json object as JsonElement. 13 | */ 14 | JsonElement toJson(); 15 | 16 | /** 17 | * Reads the json and implements its values to the client. 18 | * @param element is the object within the json file. 19 | */ 20 | void fromJson(JsonElement element); 21 | 22 | /** 23 | * Gets the name of the config file. 24 | * @return name as a String. 25 | */ 26 | default String getConfigName() { 27 | return null; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/trait/Nameable.java: -------------------------------------------------------------------------------- 1 | package net.staro.api.trait; 2 | 3 | public interface Nameable 4 | { 5 | String getName(); 6 | 7 | default String getNameLowerCase() 8 | { 9 | return this.getName().toLowerCase(); 10 | } 11 | 12 | static T getByName(Iterable nameables, String name) 13 | { 14 | for (T t : nameables) 15 | { 16 | if (name.equals(t.getName())) 17 | { 18 | return t; 19 | } 20 | } 21 | 22 | return null; 23 | } 24 | 25 | static T getByNameIgnoreCase(Iterable nameables, String name) 26 | { 27 | for (T t : nameables) 28 | { 29 | if (name.equalsIgnoreCase(t.getName())) 30 | { 31 | return t; 32 | } 33 | } 34 | 35 | return null; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/trait/Subscriber.java: -------------------------------------------------------------------------------- 1 | package net.staro.api.trait; 2 | 3 | import bot.staro.rokit.EventBus; 4 | 5 | // rofl 6 | public interface Subscriber 7 | { 8 | default void subscribe(EventBus eventBus) 9 | { 10 | eventBus.subscribe(this); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/trait/Toggleable.java: -------------------------------------------------------------------------------- 1 | package net.staro.api.trait; 2 | 3 | public interface Toggleable 4 | { 5 | boolean isEnabled(); 6 | 7 | void enable(); 8 | 9 | void disable(); 10 | 11 | void toggle(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/client/java/net/staro/api/util/SafeUtil.java: -------------------------------------------------------------------------------- 1 | package net.staro.api.util; 2 | 3 | import lombok.experimental.UtilityClass; 4 | import net.minecraft.client.MinecraftClient; 5 | import net.minecraft.client.network.ClientPlayerEntity; 6 | import net.minecraft.client.network.ClientPlayerInteractionManager; 7 | import net.minecraft.client.world.ClientWorld; 8 | import net.staro.api.PlayerWorldInteractionManagerConsumer; 9 | 10 | /** 11 | * @author 3arthqu4ke 12 | */ 13 | @UtilityClass 14 | public class SafeUtil 15 | { 16 | public static void safe(MinecraftClient mc, PlayerWorldInteractionManagerConsumer action) 17 | { 18 | safeOr(mc, action, () -> {}); 19 | } 20 | 21 | private static void safeOr(MinecraftClient mc, PlayerWorldInteractionManagerConsumer action, Runnable or) 22 | { 23 | ClientWorld world = mc.world; 24 | ClientPlayerEntity player = mc.player; 25 | ClientPlayerInteractionManager interactionManager = mc.interactionManager; 26 | if (world != null && player != null && interactionManager != null) 27 | { 28 | action.accept(player, world, interactionManager); 29 | } else { 30 | or.run(); 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/Lego.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego; 2 | 3 | import bot.staro.rokit.EventBus; 4 | import net.minecraft.client.MinecraftClient; 5 | import net.staro.api.Manager; 6 | import net.staro.api.annotation.SafeListener; 7 | import net.staro.api.listener.SafeEventConsumer; 8 | import net.staro.api.trait.Jsonable; 9 | import net.staro.lego.command.Chat; 10 | import net.staro.lego.manager.managers.CommandManager; 11 | import net.staro.lego.manager.managers.ConfigManager; 12 | import net.staro.lego.manager.managers.FriendManager; 13 | import net.staro.lego.manager.managers.ModuleManager; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | /** 19 | * A record that serves as an access point for the entire project to access managers and eventbus 20 | * without the need of creating new instances or using singleton classes. 21 | * @param eventBus is {@link EventBus} 22 | * @param commandManager is {@link CommandManager} 23 | */ 24 | public record Lego(MinecraftClient mc, 25 | EventBus eventBus, 26 | Chat chat, 27 | ModuleManager moduleManager, 28 | CommandManager commandManager, 29 | FriendManager friendManager, 30 | ConfigManager configManager) { 31 | /** 32 | * A static eventBus reference for mixin usage. 33 | */ 34 | public static final EventBus EVENT_BUS = EventBus.builder() 35 | .registerListenerFactory(SafeListener.class, (instance, method) -> 36 | new SafeEventConsumer( 37 | instance, 38 | method, 39 | method.getAnnotation(SafeListener.class).priority().getVal() 40 | ) 41 | ) 42 | .build(); 43 | /** 44 | * A static arraylist for manager's initialization. 45 | */ 46 | public static final List MANAGERS = new ArrayList<>(); 47 | /** 48 | * A static arraylist for managers serialization. 49 | */ 50 | public static final List JSONABLES = new ArrayList<>(); 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/LegoClient.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | import lombok.extern.slf4j.Slf4j; 6 | import net.minecraft.client.MinecraftClient; 7 | import net.staro.api.injection.Client; 8 | import net.staro.lego.command.Chat; 9 | import net.staro.lego.events.client.InitEvent; 10 | import net.staro.lego.manager.managers.CommandManager; 11 | import net.staro.lego.manager.managers.ConfigManager; 12 | import net.staro.lego.manager.managers.FriendManager; 13 | import net.staro.lego.manager.managers.ModuleManager; 14 | import net.staro.lego.service.ChatMessageService; 15 | import net.staro.lego.service.FirstLaunchService; 16 | import net.staro.lego.service.KeyboardService; 17 | import net.staro.lego.service.ShutdownService; 18 | 19 | @Slf4j 20 | @NoArgsConstructor(access = AccessLevel.PUBLIC) 21 | public class LegoClient implements Client { 22 | @Override 23 | public void onInitializeClient(MinecraftClient mc) { 24 | log.info("LEGO_LAUNCH_START"); 25 | 26 | // _______________ Create a lego instance _______________ // 27 | var eventBus = Lego.EVENT_BUS; 28 | var configManager = new ConfigManager(); 29 | var chat = new Chat(mc); 30 | var moduleManager = new ModuleManager(); // module manager comes before command manager!!!! 31 | var commandManager = new CommandManager(); 32 | var friendManager = new FriendManager(); 33 | var lego = new Lego( 34 | mc, 35 | eventBus, 36 | chat, 37 | moduleManager, 38 | commandManager, 39 | friendManager, 40 | configManager 41 | ); 42 | 43 | // Managers put in this list will get serialized and deserialized. 44 | var jsonables = Lego.JSONABLES; 45 | 46 | jsonables.add(moduleManager); 47 | jsonables.add(commandManager); 48 | jsonables.add(friendManager); 49 | 50 | // _____________ Initialize managers stuff _____________ // 51 | Lego.MANAGERS.forEach(manager -> manager.initialize(lego)); 52 | 53 | // _________________ Load up configs __________________ // 54 | configManager.loadEverything(moduleManager); 55 | 56 | // ________________ Subscribe services ________________ // 57 | eventBus.subscribe(new FirstLaunchService()); 58 | eventBus.subscribe(new ShutdownService(configManager, moduleManager)); 59 | eventBus.subscribe(new KeyboardService(moduleManager)); 60 | eventBus.subscribe(new ChatMessageService(commandManager)); 61 | 62 | // ___________________ End of init ____________________ // 63 | eventBus.post(new InitEvent(lego)); 64 | 65 | log.info("LEGO_LAUNCH_FINISH"); 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/PreClientInitializer.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import net.staro.api.injection.PreClient; 5 | 6 | @Slf4j 7 | public class PreClientInitializer implements PreClient { 8 | @Override 9 | public void onInitialize() { 10 | log.info("LEGO_PRE_LAUNCH_START"); 11 | 12 | // Put whatever you want here. Be careful, this method runs before instantiation of MinecraftClient. 13 | 14 | log.info("LEGO_PRE_LAUNCH_FINISH"); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/Chat.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import net.minecraft.client.MinecraftClient; 5 | import net.minecraft.client.gui.hud.ChatHud; 6 | import net.minecraft.client.gui.hud.ChatHudLine; 7 | import net.minecraft.client.gui.hud.MessageIndicator; 8 | import net.minecraft.client.network.ClientPlayerEntity; 9 | import net.minecraft.network.message.MessageSignatureData; 10 | import net.minecraft.text.Text; 11 | import net.staro.lego.ducks.IChatHud; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.nio.charset.StandardCharsets; 15 | 16 | // IMPORTANT: THESE ARE NOT NULL SAFE. 17 | @RequiredArgsConstructor 18 | public class Chat { 19 | private final MinecraftClient mc; 20 | 21 | /** 22 | * Sends a server-side message in chat. 23 | * @param message - the string to send in chat. 24 | * @param player - is a client-side player. 25 | */ 26 | public void sendPlayerMessage(String message, ClientPlayerEntity player) { 27 | addToMessageHistory(message); 28 | player.networkHandler.sendChatMessage(message); 29 | } 30 | 31 | /** 32 | * Sends a server-side command in chat. 33 | * @param message - the string to send in chat. 34 | * @param player - is a client-side player. 35 | */ 36 | public void sendCommand(String message, ClientPlayerEntity player) { 37 | addToMessageHistory(message); 38 | player.networkHandler.sendCommand(message); 39 | } 40 | 41 | /** 42 | * Sends a client-side message in chat. 43 | * @param message is a String to send. 44 | */ 45 | public void send(String message) { 46 | send(Text.literal(message)); 47 | } 48 | 49 | /** 50 | * Sends a client-side message in chat. 51 | * @param message is a Text to send. 52 | */ 53 | public void send(Text message) { 54 | mc.inGameHud.getChatHud().addMessage(message); 55 | } 56 | 57 | public void send(Text message, String identifier) { 58 | send(mc.inGameHud.getChatHud(), message, identifier); 59 | } 60 | 61 | /** 62 | * Sends a client-side message in chat without leaving it in the log file. 63 | * @param message is a Text to send. 64 | */ 65 | public void sendLogless(Text message) { 66 | sendLogless((IChatHud) mc.inGameHud.getChatHud(), message, null); 67 | } 68 | 69 | /** 70 | * Sends a client-side message in chat without leaving it in the log file. 71 | * @param message is a Text to send. 72 | * @param identifier is a String to mark the message. Useful in notifications 73 | * so only the messages with the same module 74 | * get deleted before sending another one. 75 | */ 76 | public void sendLogless(Text message, String identifier) { 77 | sendLogless((IChatHud) mc.inGameHud.getChatHud(), message, identifier); 78 | } 79 | 80 | /** 81 | * Deletes an identifier of the message. 82 | * @param identifier is a String to mark the message. Useful in notifications 83 | * * so only the messages with the same module 84 | * * get deleted before sending another one. 85 | */ 86 | public void delete(String identifier) { 87 | delete(new MessageSignatureData(get256Bytes(identifier))); 88 | } 89 | 90 | private void send(ChatHud chatHud, Text message, @Nullable String identifier) { 91 | var signature = identifier != null ? new MessageSignatureData(get256Bytes(identifier)) : null; 92 | delete(signature); 93 | chatHud.addMessage(message, signature, MessageIndicator.system()); 94 | } 95 | 96 | private void sendLogless(IChatHud chatHud, Text message, @Nullable String identifier) { 97 | var signature = identifier != null ? new MessageSignatureData(get256Bytes(identifier)) : null; 98 | delete(signature); 99 | var chatHudLine = new ChatHudLine( 100 | mc.inGameHud.getTicks(), 101 | message, 102 | signature, 103 | MessageIndicator.system()); 104 | chatHud.lego$addVisibleMessage(chatHudLine); 105 | chatHud.lego$addMessage(chatHudLine); 106 | } 107 | 108 | private void delete(@Nullable MessageSignatureData signature) { 109 | ((IChatHud) mc.inGameHud.getChatHud()).lego$deleteMessage(signature, true); 110 | } 111 | 112 | private void addToMessageHistory(String message) { 113 | mc.inGameHud.getChatHud().addToMessageHistory(message); 114 | } 115 | 116 | private byte[] get256Bytes(String identifier) { 117 | byte[] bytes = new byte[256]; 118 | byte[] identifierBytes = identifier.getBytes(StandardCharsets.UTF_8); 119 | System.arraycopy(identifierBytes, 0, bytes, 0, Math.min(bytes.length, identifierBytes.length)); 120 | return bytes; 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/LegoArgument.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command; 2 | 3 | import com.mojang.brigadier.arguments.ArgumentType; 4 | import lombok.RequiredArgsConstructor; 5 | import net.staro.lego.Lego; 6 | 7 | @RequiredArgsConstructor 8 | public abstract class LegoArgument implements ArgumentType { 9 | protected final Lego lego; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/LegoCommand.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command; 2 | 3 | import com.mojang.brigadier.CommandDispatcher; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 6 | import com.mojang.brigadier.builder.RequiredArgumentBuilder; 7 | import lombok.Getter; 8 | import net.minecraft.client.MinecraftClient; 9 | import net.minecraft.command.CommandSource; 10 | import net.staro.api.Command; 11 | import net.staro.lego.Lego; 12 | 13 | @Getter 14 | public abstract class LegoCommand implements Command { 15 | protected final Lego lego; 16 | private final String name; 17 | private final String description; 18 | protected final MinecraftClient mc; 19 | 20 | protected LegoCommand(Lego lego, String name, String description) { 21 | this.lego = lego; 22 | this.name = name; 23 | this.description = description; 24 | mc = lego.mc(); 25 | } 26 | 27 | protected static RequiredArgumentBuilder argument(final String name, final ArgumentType type) { 28 | return RequiredArgumentBuilder.argument(name, type); 29 | } 30 | 31 | protected static LiteralArgumentBuilder literal(final String name) { 32 | return LiteralArgumentBuilder.literal(name); 33 | } 34 | 35 | public void register(CommandDispatcher dispatcher) { 36 | LiteralArgumentBuilder builder = LiteralArgumentBuilder.literal(name); 37 | build(builder); 38 | dispatcher.register(builder); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/arguments/BindArgument.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command.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.command.CommandSource; 10 | import net.staro.lego.setting.type.Bind; 11 | import org.lwjgl.glfw.GLFW; 12 | 13 | import java.lang.reflect.Field; 14 | import java.util.Arrays; 15 | import java.util.List; 16 | import java.util.Objects; 17 | import java.util.Optional; 18 | import java.util.concurrent.CompletableFuture; 19 | 20 | public class BindArgument implements ArgumentType { 21 | public static final List ALL_KEY_CODES = Arrays.stream(GLFW.class.getDeclaredFields()) 22 | .filter(field -> field.getName().startsWith("GLFW_KEY_")).map(field -> { 23 | try { 24 | return field.getInt(null); 25 | } catch (IllegalAccessException e) { 26 | throw new RuntimeException(e); // This should never happen, I think, but whatever. 27 | } 28 | }).toList(); 29 | 30 | @Override 31 | public Bind parse(StringReader reader) throws CommandSyntaxException { 32 | String keyName = reader.readString().toUpperCase(); 33 | Optional matchingKeyCode = ALL_KEY_CODES.stream() 34 | .filter(keyCode -> { 35 | String glfwKeyName = GLFW.glfwGetKeyName(keyCode, 0); 36 | if (glfwKeyName != null && glfwKeyName.startsWith("GLFW_KEY_")) { 37 | glfwKeyName = glfwKeyName.substring("GLFW_KEY_".length()); 38 | } 39 | return glfwKeyName != null && glfwKeyName.toUpperCase().equals(keyName); 40 | }).findFirst(); 41 | if (matchingKeyCode.isPresent()) { 42 | return new Bind(matchingKeyCode.get()); 43 | } else { 44 | throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(reader); 45 | } 46 | } 47 | 48 | @Override 49 | public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { 50 | return CommandSource.suggestMatching(ALL_KEY_CODES.stream().map(this::getKeys).toList(), builder); 51 | } 52 | 53 | private String getKeys(int keyCode) { 54 | String kn = keyCode > 0 ? GLFW.glfwGetKeyName(keyCode, GLFW.glfwGetKeyScancode(keyCode)) : "None"; 55 | if (kn == null) { 56 | try { 57 | for (Field declaredField : GLFW.class.getDeclaredFields()) { 58 | if (declaredField.getName().startsWith("GLFW_KEY_")) { 59 | int a = (int) declaredField.get(null); 60 | 61 | if (a == keyCode) { 62 | String nb = declaredField.getName().substring("GLFW_KEY_".length()); 63 | kn = nb.substring(0, 1).toUpperCase() + nb.substring(1).toLowerCase(); 64 | } 65 | } 66 | } 67 | } catch (Exception ignore) { 68 | kn = "unknown." + keyCode; 69 | } 70 | } 71 | 72 | return keyCode == -1 ? "None" : Objects.requireNonNull(kn).toUpperCase(); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/arguments/ConfigArgument.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.context.CommandContext; 5 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 6 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 7 | import com.mojang.brigadier.suggestion.Suggestions; 8 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 9 | import net.minecraft.command.CommandSource; 10 | import net.minecraft.text.Text; 11 | import net.staro.lego.Lego; 12 | import net.staro.lego.command.LegoArgument; 13 | 14 | import java.util.concurrent.CompletableFuture; 15 | 16 | public class ConfigArgument extends LegoArgument { 17 | public ConfigArgument(Lego lego) { 18 | super(lego); 19 | } 20 | 21 | @Override 22 | public String parse(StringReader reader) throws CommandSyntaxException { 23 | var config = reader.readString(); 24 | if (!lego.configManager().getModuleConfigs().contains(config)) { 25 | throw new DynamicCommandExceptionType( 26 | name -> Text.literal("Config with name " + name.toString() + " does not exist") 27 | ).create(config); 28 | } 29 | 30 | return config; 31 | } 32 | 33 | @Override 34 | public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { 35 | return CommandSource.suggestMatching(lego.configManager().getModuleConfigs(), builder); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/arguments/FriendArgument.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.context.CommandContext; 5 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 6 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 7 | import com.mojang.brigadier.suggestion.Suggestions; 8 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 9 | import net.minecraft.command.CommandSource; 10 | import net.minecraft.text.Text; 11 | import net.staro.lego.Lego; 12 | import net.staro.lego.command.LegoArgument; 13 | 14 | import java.util.concurrent.CompletableFuture; 15 | 16 | public class FriendArgument extends LegoArgument { 17 | public FriendArgument(Lego lego) { 18 | super(lego); 19 | } 20 | 21 | @Override 22 | public String parse(StringReader reader) throws CommandSyntaxException { 23 | var friend = reader.readString(); 24 | if (!lego.friendManager().isFriend(friend)) { 25 | throw new DynamicCommandExceptionType( 26 | name -> Text.literal("Friend with name " + name.toString() + " does not exist") 27 | ).create(friend); 28 | } 29 | 30 | return friend; 31 | } 32 | 33 | @Override 34 | public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { 35 | return CommandSource.suggestMatching(lego.friendManager().getFriends(), builder); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/arguments/ModuleArgument.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.context.CommandContext; 5 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 6 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 7 | import com.mojang.brigadier.suggestion.Suggestions; 8 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 9 | import lombok.Getter; 10 | import net.minecraft.command.CommandSource; 11 | import net.minecraft.text.Text; 12 | import net.staro.api.Module; 13 | import net.staro.api.trait.Nameable; 14 | import net.staro.lego.Lego; 15 | import net.staro.lego.command.LegoArgument; 16 | 17 | import java.util.concurrent.CompletableFuture; 18 | 19 | @Getter 20 | public class ModuleArgument extends LegoArgument { 21 | private static final DynamicCommandExceptionType NO_SUCH_MODULE = new DynamicCommandExceptionType(name -> 22 | Text.literal("There's no " + name + " try again.")); 23 | 24 | public ModuleArgument(Lego lego) { 25 | super(lego); 26 | } 27 | 28 | @Override 29 | public Module parse(StringReader reader) throws CommandSyntaxException { 30 | var argument = reader.readString(); 31 | var module = lego.moduleManager().getModuleByName(argument); 32 | if (module == null) { 33 | throw NO_SUCH_MODULE.create(argument); 34 | } 35 | 36 | return module; 37 | } 38 | 39 | @Override 40 | public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { 41 | return CommandSource.suggestMatching(lego.moduleManager().getModules().stream().map(Nameable::getName), builder); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/arguments/PlayerArgument.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command.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 lombok.RequiredArgsConstructor; 10 | import lombok.SneakyThrows; 11 | import net.minecraft.client.MinecraftClient; 12 | import net.minecraft.command.CommandSource; 13 | 14 | import java.util.Objects; 15 | import java.util.concurrent.CompletableFuture; 16 | 17 | @RequiredArgsConstructor 18 | public class PlayerArgument implements ArgumentType { 19 | private final MinecraftClient mc; 20 | 21 | @SneakyThrows 22 | @Override 23 | public String parse(StringReader reader) throws CommandSyntaxException { 24 | return reader.readString(); 25 | } 26 | 27 | @Override 28 | public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { 29 | return CommandSource.suggestMatching( 30 | Objects.requireNonNull(mc.getNetworkHandler()).getPlayerList() 31 | .stream() 32 | .map(playerListEntry -> playerListEntry.getProfile().getName()), builder 33 | ); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/arguments/SettingArgument.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command.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.command.CommandSource; 11 | import net.minecraft.text.Text; 12 | import net.minecraft.util.Formatting; 13 | import net.staro.api.Module; 14 | import net.staro.api.trait.Nameable; 15 | import net.staro.lego.setting.GenericSetting; 16 | 17 | import java.util.concurrent.CompletableFuture; 18 | 19 | public class SettingArgument implements ArgumentType { 20 | private final Module module; 21 | private static final DynamicCommandExceptionType NO_SUCH_SETTING = 22 | new DynamicCommandExceptionType(name -> Text.literal("A setting called '") 23 | .append(name.toString()).formatted(Formatting.RED) 24 | .append("' doesn't exist.")); 25 | 26 | public SettingArgument(Module module) { 27 | this.module = module; 28 | } 29 | 30 | public GenericSetting get(CommandContext context) throws CommandSyntaxException { 31 | var settingName = context.getArgument("setting", String.class); 32 | 33 | GenericSetting setting = module.getSettingByName(settingName); 34 | if (setting == null) { 35 | throw NO_SUCH_SETTING.create(settingName); 36 | } 37 | 38 | return setting; 39 | } 40 | 41 | @Override 42 | public String parse(StringReader reader) throws CommandSyntaxException { 43 | return reader.readString(); 44 | } 45 | 46 | @Override 47 | public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { 48 | return CommandSource.suggestMatching(module.getSettings().stream().map(Nameable::getName), builder); 49 | 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/arguments/SettingValueArgument.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command.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 lombok.RequiredArgsConstructor; 10 | import lombok.SneakyThrows; 11 | import net.minecraft.command.CommandSource; 12 | import net.staro.lego.setting.GenericSetting; 13 | import net.staro.lego.setting.converter.EnumConverter; 14 | import org.lwjgl.glfw.GLFW; 15 | 16 | import java.awt.*; 17 | import java.lang.reflect.Field; 18 | import java.util.Arrays; 19 | import java.util.Collections; 20 | import java.util.List; 21 | import java.util.Objects; 22 | import java.util.concurrent.CompletableFuture; 23 | import java.util.stream.Collectors; 24 | 25 | @RequiredArgsConstructor 26 | public class SettingValueArgument implements ArgumentType { 27 | private final SettingArgument settingArgument; 28 | 29 | @Override 30 | public String parse(StringReader reader) throws CommandSyntaxException { 31 | return reader.readString(); 32 | } 33 | 34 | @Override 35 | @SneakyThrows // This is terrible, but i cba to make proper suggestions. 36 | public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { 37 | GenericSetting setting = settingArgument.get(context); 38 | List suggestions = switch (setting.getType()) { 39 | case "Boolean", "Enabled" -> Arrays.asList("true", "false"); 40 | case "Integer" -> generateNumberSuggestions((Integer) setting.getMin(), (Integer) setting.getValue(), (Integer) setting.getMax()); 41 | case "Double" -> generateNumberSuggestions((Double) setting.getMin(), (Double) setting.getValue(), (Double) setting.getMax()); 42 | case "Float" -> generateNumberSuggestions((Float) setting.getMin(), (Float) setting.getValue(), (Float) setting.getMax()); 43 | case "Color" -> generateColorSuggestion((Color) setting.getValue()); 44 | case "Enum" -> { 45 | Enum enumValue = (Enum) setting.getValue(); 46 | yield Arrays.stream(EnumConverter.getNames(enumValue)).collect(Collectors.toList()); 47 | } 48 | case "Bind" -> BindArgument.ALL_KEY_CODES.stream() 49 | .map(keyCode -> { 50 | String kn = keyCode > 0 ? GLFW.glfwGetKeyName(keyCode, GLFW.glfwGetKeyScancode(keyCode)) : "None"; 51 | if (kn == null) { 52 | try { 53 | for (Field declaredField : GLFW.class.getDeclaredFields()) { 54 | if (declaredField.getName().startsWith("GLFW_KEY_")) { 55 | int a = (int) declaredField.get(null); 56 | 57 | if (a == keyCode) { 58 | String nb = declaredField.getName().substring("GLFW_KEY_".length()); 59 | kn = nb.substring(0, 1).toUpperCase() + nb.substring(1).toLowerCase(); 60 | } 61 | } 62 | } 63 | } catch (Exception 64 | ignore) { 65 | kn = "unknown." + keyCode; 66 | } 67 | } 68 | return keyCode == -1 ? "None" : Objects.requireNonNull(kn).toUpperCase(); 69 | }) 70 | .toList(); 71 | default -> List.of(""); 72 | }; 73 | 74 | return CommandSource.suggestMatching(suggestions, builder); 75 | } 76 | 77 | private List generateNumberSuggestions(Number min, Number current ,Number max) { 78 | return Arrays.asList(min.toString(), current.toString(), max.toString()); 79 | } 80 | 81 | private List generateColorSuggestion(Color color) { 82 | return Collections.singletonList(Integer.toHexString(color.getRGB()).substring(2).toUpperCase()); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/arguments/StringArgument.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command.arguments; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 6 | import lombok.Getter; 7 | import lombok.RequiredArgsConstructor; 8 | 9 | import java.util.Arrays; 10 | import java.util.Collection; 11 | 12 | @SuppressWarnings("ClassCanBeRecord") 13 | @Getter 14 | @RequiredArgsConstructor 15 | public class StringArgument implements ArgumentType { 16 | private final StringType type; 17 | 18 | public static StringArgument word() { 19 | return new StringArgument(StringType.SINGLE_WORD); 20 | } 21 | 22 | public static StringArgument string() { 23 | return new StringArgument(StringType.QUOTABLE_PHRASE); 24 | } 25 | 26 | public static StringArgument greedyString() { 27 | return new StringArgument(StringType.GREEDY_PHRASE); 28 | } 29 | 30 | @Override 31 | public String parse(final StringReader reader) throws CommandSyntaxException { 32 | if (type == StringType.GREEDY_PHRASE) { 33 | final String text = reader.getRemaining(); 34 | reader.setCursor(reader.getTotalLength()); 35 | return text; 36 | } else if (type == StringType.SINGLE_WORD) { 37 | return reader.readUnquotedString(); 38 | } else { 39 | return reader.readString(); 40 | } 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return "string()"; 46 | } 47 | 48 | @Override 49 | public Collection getExamples() { 50 | return type.getExamples(); 51 | } 52 | 53 | @Getter 54 | public enum StringType { 55 | SINGLE_WORD("word", "words_with_underscores"), 56 | QUOTABLE_PHRASE("\"quoted phrase\"", "word", "\"\""), 57 | GREEDY_PHRASE("word", "words with spaces", "\"and symbols\""),; 58 | 59 | private final Collection examples; 60 | 61 | StringType(final String... examples) { 62 | this.examples = Arrays.asList(examples); 63 | } 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/commands/BindCommand.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import net.minecraft.command.CommandSource; 5 | import net.minecraft.text.Text; 6 | import net.minecraft.util.Formatting; 7 | import net.staro.api.Module; 8 | import net.staro.lego.Lego; 9 | import net.staro.lego.command.LegoCommand; 10 | import net.staro.lego.command.arguments.BindArgument; 11 | import net.staro.lego.command.arguments.ModuleArgument; 12 | import net.staro.lego.service.BindService; 13 | import net.staro.lego.setting.type.Bind; 14 | import org.lwjgl.glfw.GLFW; 15 | 16 | public class BindCommand extends LegoCommand { 17 | private final BindArgument bindArgument = new BindArgument(); 18 | private final BindService bindService = new BindService(lego.chat()); 19 | 20 | public BindCommand(Lego lego) { 21 | super(lego, "bind", "Sets the bind of the module"); 22 | } 23 | 24 | @Override 25 | public void build(LiteralArgumentBuilder builder) { 26 | builder.then(argument("module", new ModuleArgument(lego)).executes(context -> { 27 | var module = context.getArgument("module", Module.class); 28 | if (module.getBind().getKey() != -1) { 29 | lego.chat().send(Text.literal("Current bind for " 30 | + module.getName() 31 | + ": " 32 | + GLFW.glfwGetKeyName( 33 | module.getBind().getKey(), GLFW.glfwGetKeyScancode(module.getBind() 34 | .getKey()))).formatted(Formatting.DARK_GRAY)); 35 | } 36 | 37 | lego.chat().send(Text.literal("Press the Key").formatted(Formatting.GRAY)); 38 | bindService.setModule(module); 39 | lego.eventBus().subscribe(bindService); 40 | return COMPLETED; 41 | }).then(argument("key", bindArgument).executes(context -> { 42 | var module = context.getArgument("module", Module.class); 43 | Bind key = context.getArgument("key", Bind.class); 44 | if (key.isEmpty()) { 45 | lego.chat().send(Text.literal("The bind for" + module.getName().formatted(Formatting.WHITE).formatted(Formatting.BOLD) 46 | + " is currently set to " + module.getBind().getBindName()).formatted(Formatting.GRAY)); 47 | } 48 | 49 | module.setBind(key.getKey()); 50 | lego.chat().send(Text.literal("The bind for " + module.getName().formatted(Formatting.WHITE).formatted(Formatting.BOLD) 51 | + " has been set to " + module.getBind().getBindName()).formatted(Formatting.GRAY)); 52 | return COMPLETED; 53 | }))); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/commands/ConfigCommand.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import net.minecraft.command.CommandSource; 5 | import net.minecraft.text.Text; 6 | import net.minecraft.util.Formatting; 7 | import net.staro.lego.Lego; 8 | import net.staro.lego.command.LegoCommand; 9 | import net.staro.lego.command.arguments.ConfigArgument; 10 | import net.staro.lego.command.arguments.StringArgument; 11 | 12 | public class ConfigCommand extends LegoCommand { 13 | private final ConfigArgument configArgument; 14 | 15 | public ConfigCommand(Lego lego) { 16 | super(lego, "config", "Manage configs"); 17 | this.configArgument = new ConfigArgument(lego); 18 | } 19 | 20 | @Override 21 | public void build(LiteralArgumentBuilder builder) { 22 | builder.executes(context -> { 23 | lego.chat().sendLogless(Text.literal("The command usage is: config ")); 24 | return COMPLETED; 25 | }).then(literal("save").then(argument("name", StringArgument.string()).executes(context -> { 26 | String name = context.getArgument("name", String.class); 27 | lego.configManager().saveConfig(name, lego.moduleManager()); 28 | lego.chat().sendLogless(Text.literal("") 29 | .append(Text.literal("Config by the name ")) 30 | .append(Text.literal(name).formatted(Formatting.AQUA)) 31 | .append(Text.literal(" has been created successfully")).formatted(Formatting.BOLD)); 32 | return COMPLETED; 33 | })) 34 | ).then(literal("load").then(argument("config", configArgument).executes(context -> { 35 | String config = context.getArgument("config", String.class); 36 | lego.configManager().loadConfig(config, lego.moduleManager()); 37 | lego.chat().sendLogless(Text.literal("") 38 | .append(Text.literal("Config by the name ")) 39 | .append(Text.literal(config).formatted(Formatting.AQUA)) 40 | .append(Text.literal(" has been loaded successfully")).formatted(Formatting.BOLD)); 41 | return COMPLETED; 42 | })) 43 | ).then(literal("del").then(argument("config", configArgument).executes(context -> { 44 | String config = context.getArgument("config", String.class); 45 | lego.configManager().deleteConfig(config); 46 | lego.chat().sendLogless(Text.literal("") 47 | .append(Text.literal("Config by the name ")) 48 | .append(Text.literal(config).formatted(Formatting.RED)) 49 | .append(Text.literal(" has been deleted successfully")).formatted(Formatting.BOLD)); 50 | return COMPLETED; 51 | })) 52 | ).then(literal("list").executes(context -> { 53 | var text = new StringBuilder(); 54 | var configs = lego.configManager().getModuleConfigs(); 55 | text.append(String.join(", ", configs)); 56 | lego.chat().send(Text.literal("") 57 | .append(Text.literal("Config list: ").formatted(Formatting.BOLD).formatted(Formatting.AQUA)) 58 | .append(Text.literal(text.toString()).formatted(Formatting.WHITE))); 59 | return COMPLETED; 60 | })); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/commands/FriendCommand.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import lombok.SneakyThrows; 5 | import net.minecraft.command.CommandSource; 6 | import net.minecraft.text.Text; 7 | import net.minecraft.util.Formatting; 8 | import net.staro.lego.Lego; 9 | import net.staro.lego.command.LegoCommand; 10 | import net.staro.lego.command.arguments.FriendArgument; 11 | import net.staro.lego.command.arguments.PlayerArgument; 12 | 13 | public class FriendCommand extends LegoCommand { 14 | private final PlayerArgument playerArgument; 15 | 16 | public FriendCommand(Lego lego) { 17 | super(lego, "friend", "Manage friends"); 18 | playerArgument = new PlayerArgument(lego.mc()); 19 | } 20 | 21 | @Override 22 | @SneakyThrows 23 | public void build(LiteralArgumentBuilder builder) { 24 | builder.executes(context -> { 25 | lego.chat().send("Please enter the correct sequence: +player "); 26 | return COMPLETED; 27 | }).then(literal("add").then(argument("player", playerArgument).executes(context -> { 28 | String nickname; 29 | nickname = context.getArgument("player", String.class); 30 | if (!lego.friendManager().getFriends().contains(nickname)) { 31 | lego.friendManager().addFriend(nickname); 32 | lego.chat().send(Text.literal("") 33 | .append(Text.literal(nickname).formatted(Formatting.BOLD).formatted(Formatting.AQUA)) 34 | .append(Text.literal(" is a friend now").formatted(Formatting.WHITE))); 35 | } else { 36 | lego.chat().send(Text.literal("") 37 | .append(Text.literal(nickname).formatted(Formatting.BOLD).formatted(Formatting.AQUA)) 38 | .append(Text.literal(" is already a friend").formatted(Formatting.WHITE))); 39 | } 40 | return COMPLETED; 41 | })) 42 | ).then(literal("del").then(argument("player", new FriendArgument(lego)).executes(context -> { 43 | var nickname = context.getArgument("player", String.class); 44 | lego.friendManager().removeFriend(nickname); 45 | lego.chat().send(Text.literal("") 46 | .append(Text.literal(nickname).formatted(Formatting.BOLD).formatted(Formatting.RED)) 47 | .append(Text.literal(" is not a friend anymore").formatted(Formatting.WHITE))); 48 | return COMPLETED; 49 | })) 50 | ).then(literal("list").executes(context -> { 51 | var friendList = new StringBuilder(); 52 | var friends = lego.friendManager().getFriends(); 53 | friendList.append(String.join(", ", friends)); 54 | lego.chat().send(Text.literal("") 55 | .append(Text.literal("Friend list: ").formatted(Formatting.BOLD).formatted(Formatting.AQUA)) 56 | .append(Text.literal(friendList.toString()).formatted(Formatting.WHITE))); 57 | return COMPLETED; 58 | }) 59 | ).then(literal("clear").executes(context -> { 60 | lego.friendManager().getFriends().clear(); 61 | return COMPLETED; 62 | }) 63 | ); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/commands/HelpCommand.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import net.minecraft.command.CommandSource; 5 | import net.minecraft.text.HoverEvent; 6 | import net.minecraft.text.Text; 7 | import net.staro.lego.Lego; 8 | import net.staro.lego.command.LegoCommand; 9 | 10 | public class HelpCommand extends LegoCommand { 11 | public HelpCommand(Lego lego) { 12 | super(lego, "help", "Shows the list of available commands."); 13 | } 14 | 15 | @Override 16 | public void build(LiteralArgumentBuilder builder) { 17 | builder.executes(context -> { 18 | lego.chat().sendLogless(Text.literal("Available commands:")); 19 | lego.commandManager().getCommands().forEach(command -> { 20 | var commandText = Text.literal(command.getName()); 21 | lego.chat().send(commandText.styled(style -> style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, 22 | Text.literal(command.getDescription()))))); 23 | }); 24 | return COMPLETED; 25 | }); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/commands/MessageCommand.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import net.minecraft.command.CommandSource; 5 | import net.staro.lego.Lego; 6 | import net.staro.lego.command.LegoCommand; 7 | import net.staro.lego.command.arguments.StringArgument; 8 | 9 | public class MessageCommand extends LegoCommand { 10 | public MessageCommand(Lego lego) { 11 | super(lego, "message", "Sends a server-side message in chat."); 12 | } 13 | 14 | @Override 15 | public void build(LiteralArgumentBuilder builder) { 16 | builder.then(argument("message", StringArgument.greedyString()).executes(context -> { 17 | String message = context.getArgument("message", String.class); 18 | var player = mc.player; 19 | if (player != null) { 20 | lego.chat().sendPlayerMessage(message, player); 21 | } 22 | 23 | return COMPLETED; 24 | })); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/commands/ModuleCommand.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command.commands; 2 | 3 | import com.google.gson.JsonParser; 4 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 5 | import lombok.extern.slf4j.Slf4j; 6 | import net.minecraft.command.CommandSource; 7 | import net.minecraft.text.HoverEvent; 8 | import net.minecraft.text.Text; 9 | import net.minecraft.util.Formatting; 10 | import net.staro.api.Module; 11 | import net.staro.api.Setting; 12 | import net.staro.lego.Lego; 13 | import net.staro.lego.command.Chat; 14 | import net.staro.lego.command.LegoCommand; 15 | import net.staro.lego.command.arguments.SettingArgument; 16 | import net.staro.lego.command.arguments.SettingValueArgument; 17 | import net.staro.lego.setting.GenericSetting; 18 | import net.staro.lego.utility.SettingUtil; 19 | import org.apache.logging.log4j.util.TriConsumer; 20 | 21 | import java.util.List; 22 | import java.util.function.Predicate; 23 | 24 | @Slf4j 25 | public class ModuleCommand extends LegoCommand { 26 | private final Module module; 27 | private final SettingArgument settingArgument; 28 | private final SettingValueArgument settingValueArgument; 29 | private final TriConsumer, String, Module> setEnabledModule = (setting, settingValue, module) -> { 30 | if (setting.getName().equalsIgnoreCase("Enabled")) { 31 | if (settingValue.equalsIgnoreCase("true")) { 32 | module.enable(); 33 | } else if (settingValue.equalsIgnoreCase("false")) { 34 | module.disable(); 35 | } 36 | } 37 | }; 38 | 39 | public ModuleCommand(Module module, Lego lego, String name, String description) { 40 | super(lego, name, description); 41 | this.module = module; 42 | this.settingArgument = new SettingArgument(module); 43 | this.settingValueArgument = new SettingValueArgument(settingArgument); 44 | } 45 | 46 | @Override 47 | @SuppressWarnings({"rawtypes"}) 48 | public void build(LiteralArgumentBuilder builder) { 49 | Chat chat = lego.chat(); 50 | builder.then(argument("setting", settingArgument).executes(context -> { 51 | chat.send("nope"); 52 | return COMPLETED; 53 | }).then(argument("value", settingValueArgument).executes(context -> { 54 | Setting setting = settingArgument.get(context); 55 | var settingValue = context.getArgument("value", String.class); 56 | if (setting.getName().equalsIgnoreCase("Bind")) { 57 | try { 58 | module.setBind(SettingUtil.convertToBind(settingValue.toUpperCase()).getKey()); 59 | } catch (Exception e) { 60 | chat.send(Text.literal("") 61 | .append(Text.literal("Bad value! There's no key ")) 62 | .append(Text.literal(settingValue).formatted(Formatting.RED))); 63 | return COMPLETED; 64 | } 65 | } else { 66 | try { 67 | setEnabledModule.accept(setting, settingValue, module); 68 | SettingUtil.setCommandValue(module, setting, JsonParser.parseString(settingValue)); 69 | } catch (Exception e) { 70 | chat.send(Text.literal("Bad Value! This setting requires a: " + setting.getType() + " value.")); 71 | return COMPLETED; 72 | } 73 | } 74 | 75 | chat.send(Text.literal("") 76 | .append(Text.literal(module.getName()).formatted(Formatting.BOLD).styled(style -> 77 | style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.literal(module.getDescription()))))) 78 | .append(Text.literal(" " + setting.getName() + " has been set to ").formatted(Formatting.GRAY)) 79 | .append(Text.literal(settingValue))); 80 | return COMPLETED; 81 | }))); 82 | builder.executes(context -> { 83 | chat.send(Text.literal((module.isEnabled() ? Formatting.AQUA : Formatting.RED) + module.getName() + ": ") 84 | .styled(style -> style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, 85 | Text.literal(module.getDescription()))))); 86 | List> filteredSettings = module.getSettings().stream() 87 | .filter(setting -> { 88 | Predicate settingName = name -> setting.getName().equalsIgnoreCase(name); 89 | return !(settingName.test("Bind") || settingName.test("Drawn")); 90 | }).toList(); 91 | filteredSettings.forEach(setting -> { 92 | String value = setting.getValue().toString(); 93 | Formatting valueColor = Formatting.WHITE; 94 | if (setting.getType().equalsIgnoreCase("Boolean") && value.equalsIgnoreCase("false")) { 95 | valueColor = Formatting.OBFUSCATED; 96 | } 97 | 98 | var settingName = Text.literal(setting.getName()).formatted(Formatting.GRAY); 99 | settingName = settingName.styled(style -> style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, 100 | Text.literal(setting.getDescription())))); 101 | chat.send(Text.literal("") 102 | .append(settingName) 103 | .append(Text.literal(": ")) 104 | .append(Text.literal(value).formatted(valueColor))); 105 | }); 106 | return COMPLETED; 107 | }); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/commands/ModulesCommand.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import net.minecraft.command.CommandSource; 5 | import net.minecraft.text.HoverEvent; 6 | import net.minecraft.text.Text; 7 | import net.minecraft.util.Formatting; 8 | import net.staro.lego.Lego; 9 | import net.staro.lego.command.LegoCommand; 10 | 11 | public class ModulesCommand extends LegoCommand { 12 | public ModulesCommand(Lego lego) { 13 | super(lego, "modules", "Shows available modules"); 14 | } 15 | 16 | @Override 17 | public void build(LiteralArgumentBuilder builder) { 18 | builder.executes(context -> { 19 | lego.chat().sendLogless(Text.of(Formatting.GRAY + "Modules: ")); 20 | lego.moduleManager().getCategories().forEach(category -> { 21 | var categoryText = Text.literal(category.getName() + ": ").formatted(Formatting.GRAY); 22 | var modulesText = lego.moduleManager().getModulesByCategory(category).stream() 23 | .map(module -> Text.literal(module.getName()) 24 | .formatted(module.isEnabled() ? Formatting.AQUA : Formatting.RED) 25 | .styled(style -> style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, 26 | Text.literal(module.getDescription()))))) 27 | .reduce((text1, text2) -> text1.append(Text.literal(", ")).append(text2)) 28 | .orElse(Text.literal("")); 29 | lego.chat().send(categoryText.append(modulesText)); 30 | }); 31 | return COMPLETED; 32 | }); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/commands/PrefixCommand.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import net.minecraft.command.CommandSource; 5 | import net.minecraft.text.Text; 6 | import net.minecraft.util.Formatting; 7 | import net.staro.lego.Lego; 8 | import net.staro.lego.command.LegoCommand; 9 | import net.staro.lego.command.arguments.StringArgument; 10 | 11 | public class PrefixCommand extends LegoCommand { 12 | public PrefixCommand(Lego lego) { 13 | super(lego, "prefix", "Sets up the prefix for the client."); 14 | } 15 | 16 | @Override 17 | public void build(LiteralArgumentBuilder builder) { 18 | builder.then(argument("prefix", StringArgument.string()).executes(context -> { 19 | var prefix = context.getArgument("prefix", String.class); 20 | lego.commandManager().setPrefix(prefix); 21 | lego.chat().send(Text.literal("The prefix is set to ").formatted(Formatting.BOLD) 22 | .append(Text.literal(prefix).formatted(Formatting.BOLD).formatted(Formatting.RED))); 23 | return COMPLETED; 24 | })); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/commands/QuitCommand.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import net.minecraft.command.CommandSource; 5 | import net.staro.lego.Lego; 6 | import net.staro.lego.command.LegoCommand; 7 | 8 | public class QuitCommand extends LegoCommand { 9 | public QuitCommand(Lego lego) { 10 | super(lego, "quit", "Closes minecraft instance"); 11 | } 12 | 13 | @Override 14 | public void build(LiteralArgumentBuilder builder) { 15 | builder.executes(context -> { 16 | mc.close(); 17 | return COMPLETED; 18 | }); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/command/commands/ToggleCommand.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.command.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import net.minecraft.command.CommandSource; 5 | import net.staro.api.Module; 6 | import net.staro.lego.Lego; 7 | import net.staro.lego.command.LegoCommand; 8 | import net.staro.lego.command.arguments.ModuleArgument; 9 | 10 | public class ToggleCommand extends LegoCommand { 11 | public ToggleCommand(Lego lego) { 12 | super(lego, "toggle", "Toggles modules on or off"); 13 | } 14 | 15 | @Override 16 | public void build(LiteralArgumentBuilder builder) { 17 | builder.then(argument("module", new ModuleArgument(lego)).executes(context -> { 18 | Module module = context.getArgument("module", Module.class); 19 | module.toggle(); 20 | return COMPLETED; 21 | })); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/ducks/IChatHud.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.ducks; 2 | 3 | import net.minecraft.client.gui.hud.ChatHudLine; 4 | import net.minecraft.network.message.MessageSignatureData; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | public interface IChatHud { 8 | void lego$addMessage(@Nullable ChatHudLine message); 9 | 10 | void lego$addVisibleMessage(@Nullable ChatHudLine message); 11 | 12 | void lego$deleteMessage(@Nullable MessageSignatureData signature, boolean all); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/events/CancellableEvent.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.events; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | /** 7 | * An event that can be canceled by listeners. 8 | */ 9 | @Getter 10 | @Setter 11 | public class CancellableEvent { 12 | private boolean cancelled = false; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/events/PacketEvent.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.events; 2 | 3 | import net.minecraft.network.ClientConnection; 4 | import net.minecraft.network.packet.Packet; 5 | 6 | /** 7 | * PacketEvent form meteor. 8 | * @author seasnail8169 9 | */ 10 | public class PacketEvent { 11 | public static class Receive extends CancellableEvent { 12 | private static final Receive INSTANCE = new Receive(); 13 | 14 | public Packet packet; 15 | public ClientConnection connection; 16 | 17 | public static Receive get(Packet packet, ClientConnection connection) { 18 | INSTANCE.setCancelled(false); 19 | INSTANCE.packet = packet; 20 | INSTANCE.connection = connection; 21 | return INSTANCE; 22 | } 23 | } 24 | 25 | public static class Send extends CancellableEvent { 26 | private static final Send INSTANCE = new Send(); 27 | 28 | public Packet packet; 29 | public ClientConnection connection; 30 | 31 | public static Send get(Packet packet, ClientConnection connection) { 32 | INSTANCE.setCancelled(false); 33 | INSTANCE.packet = packet; 34 | INSTANCE.connection = connection; 35 | return INSTANCE; 36 | } 37 | } 38 | 39 | public static class Sent { 40 | private static final Sent INSTANCE = new Sent(); 41 | 42 | public Packet packet; 43 | public ClientConnection connection; 44 | 45 | public static Sent get(Packet packet, ClientConnection connection) { 46 | INSTANCE.packet = packet; 47 | INSTANCE.connection = connection; 48 | return INSTANCE; 49 | } 50 | } 51 | 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/events/camera/CameraUpdateEvent.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.events.camera; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import net.minecraft.entity.Entity; 6 | import net.minecraft.world.BlockView; 7 | import net.staro.lego.events.CancellableEvent; 8 | 9 | @Getter 10 | @Setter 11 | public class CameraUpdateEvent extends CancellableEvent { 12 | public static final CameraUpdateEvent INSTANCE = new CameraUpdateEvent(); 13 | 14 | private BlockView area; 15 | private Entity focusedEntity; 16 | private boolean thirdPerson; 17 | private boolean inverseView; 18 | private float tickDelta; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/events/chat/ChatEvent.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.events.chat; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import net.staro.lego.events.CancellableEvent; 6 | 7 | @Getter 8 | @Setter 9 | public class ChatEvent extends CancellableEvent { 10 | private String message; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/events/chat/InputSuggestorEvent.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.events.chat; 2 | 3 | import com.mojang.brigadier.ParseResults; 4 | import com.mojang.brigadier.StringReader; 5 | import com.mojang.brigadier.suggestion.Suggestions; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Getter; 8 | import lombok.Setter; 9 | import net.minecraft.client.gui.screen.ChatInputSuggestor; 10 | import net.minecraft.client.gui.widget.TextFieldWidget; 11 | import net.minecraft.command.CommandSource; 12 | import net.staro.lego.events.CancellableEvent; 13 | import org.jetbrains.annotations.Nullable; 14 | 15 | import java.util.concurrent.CompletableFuture; 16 | 17 | @Getter 18 | @Setter 19 | @AllArgsConstructor 20 | public class InputSuggestorEvent extends CancellableEvent { 21 | private final TextFieldWidget textField; 22 | private final StringReader reader; 23 | private @Nullable ParseResults parse; 24 | private @Nullable CompletableFuture pendingSuggestions; 25 | private @Nullable ChatInputSuggestor.SuggestionWindow window; 26 | private boolean completingSuggestions; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/events/client/InitEvent.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.events.client; 2 | 3 | import net.staro.lego.Lego; 4 | 5 | public record InitEvent(Lego lego) { 6 | } 7 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/events/client/KeyEvent.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.events.client; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import net.staro.lego.events.CancellableEvent; 6 | 7 | @Getter 8 | @Setter 9 | public class KeyEvent extends CancellableEvent { 10 | public static final KeyEvent INSTANCE = new KeyEvent(); 11 | private int action; 12 | private int key; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/events/client/ShutdownEvent.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.events.client; 2 | 3 | public class ShutdownEvent { 4 | } 5 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/events/client/ToggleEvent.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.events.client; 2 | 3 | import net.staro.api.Module; 4 | 5 | public record ToggleEvent(Module module, boolean state) { 6 | } 7 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/events/tick/GameloopEvent.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.events.tick; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | @Getter 9 | @Setter 10 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 11 | public class GameloopEvent { 12 | public static final GameloopEvent INSTANCE = new GameloopEvent(); 13 | private int ticks; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/events/tick/PostUpdateEvent.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.events.tick; 2 | 3 | public class PostUpdateEvent { 4 | } 5 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/events/tick/PreUpdateEvent.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.events.tick; 2 | 3 | public class PreUpdateEvent { 4 | } 5 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/events/tick/TickEvent.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.events.tick; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | 6 | @SuppressWarnings("InstantiationOfUtilityClass") 7 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 8 | public class TickEvent { 9 | public static final TickEvent INSTANCE = new TickEvent(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/events/tick/UpdateEvent.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.events.tick; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | import net.minecraft.client.network.ClientPlayerEntity; 6 | import net.staro.lego.events.CancellableEvent; 7 | 8 | @Getter 9 | @RequiredArgsConstructor 10 | public class UpdateEvent extends CancellableEvent { 11 | private final ClientPlayerEntity player; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/manager/GenericManager.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.manager; 2 | 3 | import net.staro.api.Manager; 4 | import net.staro.lego.Lego; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | // This is incredibly stupid, what have I been smoking? 10 | 11 | public abstract class GenericManager implements Manager { 12 | protected final List items = new ArrayList<>(); 13 | 14 | protected GenericManager() { 15 | Lego.MANAGERS.add(this); 16 | } 17 | 18 | protected void register(T item) { 19 | items.add(item); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/manager/managers/CommandManager.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.manager.managers; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | import com.mojang.brigadier.CommandDispatcher; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import lombok.Getter; 8 | import lombok.Setter; 9 | import net.minecraft.client.MinecraftClient; 10 | import net.minecraft.client.network.ClientCommandSource; 11 | import net.minecraft.command.CommandSource; 12 | import net.staro.api.Command; 13 | import net.staro.api.trait.Jsonable; 14 | import net.staro.lego.Lego; 15 | import net.staro.lego.command.LegoCommand; 16 | import net.staro.lego.command.commands.*; 17 | import net.staro.lego.manager.GenericManager; 18 | 19 | import java.util.List; 20 | 21 | @Getter 22 | public class CommandManager extends GenericManager implements Jsonable { 23 | @Setter 24 | private String prefix = "+"; 25 | private final String configName = "prefix.json"; 26 | protected final CommandDispatcher dispatcher = new CommandDispatcher<>(); 27 | private final CommandSource commandSource = new ClientCommandSource(null, MinecraftClient.getInstance()); 28 | 29 | @Override 30 | public void initialize(Lego lego) { 31 | registerCommand(new HelpCommand(lego)); 32 | registerCommand(new ToggleCommand(lego)); 33 | registerCommand(new ModulesCommand(lego)); 34 | registerCommand(new QuitCommand(lego)); 35 | registerCommand(new MessageCommand(lego)); 36 | registerCommand(new PrefixCommand(lego)); 37 | registerCommand(new FriendCommand(lego)); 38 | registerCommand(new BindCommand(lego)); 39 | registerCommand(new ConfigCommand(lego)); 40 | lego.moduleManager().getModules() 41 | .forEach(module -> registerCommand( 42 | new ModuleCommand(module, lego, module.getName(), module.getDescription())) 43 | ); 44 | } 45 | 46 | @Override 47 | public JsonElement toJson() { 48 | JsonObject object = new JsonObject(); 49 | object.addProperty("prefix", prefix); 50 | return object; 51 | } 52 | 53 | @Override 54 | public void fromJson(JsonElement element) { 55 | setPrefix(element.getAsJsonObject().get("prefix").getAsString()); 56 | } 57 | 58 | public List getCommands() { 59 | return items; 60 | } 61 | 62 | public void dispatch(String message) throws CommandSyntaxException { 63 | dispatcher.execute(message, commandSource); 64 | } 65 | 66 | /** 67 | * Initialize a command with this method. 68 | * @param command is the command???? I mean, usual stuff, it extends {@link LegoCommand}. 69 | */ 70 | private void registerCommand(Command command) { 71 | command.register(dispatcher); 72 | register(command); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/manager/managers/ConfigManager.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.manager.managers; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonParser; 5 | import lombok.Getter; 6 | import lombok.SneakyThrows; 7 | import lombok.extern.slf4j.Slf4j; 8 | import net.staro.api.trait.Constants; 9 | import net.staro.api.trait.Jsonable; 10 | import net.staro.lego.Lego; 11 | import net.staro.lego.utility.ConfigUtil; 12 | 13 | import java.io.File; 14 | import java.io.IOException; 15 | import java.nio.file.Files; 16 | import java.nio.file.Path; 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | @Slf4j 21 | @Getter 22 | public class ConfigManager { 23 | private static final Path CURRENT_CONFIG_PATH = Constants.LEGO_FOLDER_PATH.resolve("current_config.json"); 24 | private static final Path MODULE_CONFIGS_PATH = Constants.LEGO_FOLDER_PATH.resolve("modules"); 25 | private final List moduleConfigs = new ArrayList<>(); 26 | private final Gson gson = ConfigUtil.createGson(); 27 | private final boolean firstLaunch; 28 | 29 | private String currentConfigName; 30 | 31 | public ConfigManager() { 32 | File folder = Constants.LEGO_FOLDER; 33 | firstLaunch = ConfigUtil.initializeFolder(folder); 34 | ConfigUtil.initializeFolder(MODULE_CONFIGS_PATH.toFile()); 35 | currentConfigName = ConfigUtil.loadCurrentConfigName(CURRENT_CONFIG_PATH); 36 | ConfigUtil.loadModuleConfigNames(MODULE_CONFIGS_PATH, moduleConfigs); 37 | } 38 | 39 | public void saveEverything(ModuleManager moduleManager) { 40 | saveConfig(currentConfigName, moduleManager); 41 | Lego.JSONABLES.forEach(this::saveJsonable); 42 | } 43 | 44 | public void loadEverything(ModuleManager moduleManager) { 45 | loadConfig(currentConfigName, moduleManager); 46 | Lego.JSONABLES.forEach(this::readJsonable); 47 | } 48 | 49 | @SneakyThrows(value = IOException.class) 50 | public void saveConfig(String name, ModuleManager moduleManager) { 51 | Path configPath = MODULE_CONFIGS_PATH.resolve(name + ".json"); 52 | Files.writeString(configPath, gson.toJson(moduleManager.toJson())); 53 | currentConfigName = name; 54 | ConfigUtil.saveCurrentConfigName(currentConfigName, CURRENT_CONFIG_PATH, gson); 55 | if (!moduleConfigs.contains(name)) { 56 | moduleConfigs.add(name); 57 | } 58 | } 59 | 60 | @SneakyThrows(value = IOException.class) 61 | public void loadConfig(String name, ModuleManager moduleManager) { 62 | Path configPath = MODULE_CONFIGS_PATH.resolve(name + ".json"); 63 | if (Files.exists(configPath)) { 64 | String read = Files.readString(configPath); 65 | moduleManager.fromJson(JsonParser.parseString(read)); 66 | currentConfigName = name; 67 | ConfigUtil.saveCurrentConfigName(currentConfigName, CURRENT_CONFIG_PATH, gson); 68 | } else { 69 | log.warn("Config '{}' not found. Failed to load it", name); 70 | } 71 | } 72 | 73 | public void deleteConfig(String name) { 74 | Path configPath = MODULE_CONFIGS_PATH.resolve(name + ".json"); 75 | if (Files.exists(configPath)) { 76 | try { 77 | Files.delete(configPath); 78 | moduleConfigs.remove(name); 79 | log.info("Config '{}' deleted successfully.", name); 80 | if (currentConfigName.equals(name)) { 81 | currentConfigName = "modules"; 82 | ConfigUtil.saveCurrentConfigName(currentConfigName, CURRENT_CONFIG_PATH, gson); 83 | } 84 | 85 | } catch (IOException e) { 86 | log.error("Failed to delete config '{}'.", name, e); 87 | } 88 | } else { 89 | log.warn("Config '{}' not found. Failed to delete it", name); 90 | } 91 | } 92 | 93 | @SneakyThrows(value = IOException.class) 94 | private void saveJsonable(Jsonable jsonable) { 95 | Path configPath = Constants.LEGO_FOLDER_PATH.resolve(jsonable.getConfigName() + ".json"); 96 | Files.writeString(configPath, gson.toJson(jsonable.toJson())); 97 | } 98 | 99 | private void readJsonable(Jsonable jsonable) { 100 | try { 101 | String read = Files.readString(Constants.LEGO_FOLDER_PATH.resolve(jsonable.getConfigName() + ".json")); 102 | jsonable.fromJson(JsonParser.parseString(read)); 103 | } catch (IOException e) { 104 | log.error("Error while trying to read the file: ", e); 105 | } 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/manager/managers/FriendManager.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.manager.managers; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonObject; 6 | import lombok.Getter; 7 | import net.minecraft.entity.player.PlayerEntity; 8 | import net.staro.api.trait.Jsonable; 9 | import net.staro.lego.Lego; 10 | import net.staro.lego.manager.GenericManager; 11 | 12 | import java.util.List; 13 | 14 | @Getter 15 | public class FriendManager extends GenericManager implements Jsonable { 16 | private final String configName = "friends.json"; 17 | 18 | @Override 19 | public void initialize(Lego lego) { } 20 | 21 | @Override 22 | public JsonElement toJson() { 23 | JsonObject object = new JsonObject(); 24 | JsonArray array = new JsonArray(); 25 | getFriends().forEach(friend -> { 26 | array.add(friend); 27 | object.add("friends", array); 28 | }); 29 | return object; 30 | } 31 | 32 | @Override 33 | public void fromJson(JsonElement element) { 34 | element.getAsJsonObject() 35 | .get("friends") 36 | .getAsJsonArray() 37 | .forEach(e -> getFriends().add(e.getAsString())); 38 | } 39 | 40 | public boolean isFriend(String name) { 41 | return getFriends().stream().anyMatch(friend -> friend.equalsIgnoreCase(name)); 42 | } 43 | 44 | public boolean isFriend(PlayerEntity player) { 45 | return this.isFriend(player.getGameProfile().getName()); 46 | } 47 | 48 | public void addFriend(String name) { 49 | if (!getFriends().contains(name)) { 50 | this.getFriends().add(name); 51 | } 52 | } 53 | 54 | public void removeFriend(String name) { 55 | getFriends().removeIf(s -> s.equalsIgnoreCase(name)); 56 | } 57 | 58 | public List getFriends() { 59 | return items; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/manager/managers/ModuleManager.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.manager.managers; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | import lombok.Getter; 6 | import net.staro.api.Module; 7 | import net.staro.api.trait.Jsonable; 8 | import net.staro.lego.Lego; 9 | import net.staro.lego.manager.GenericManager; 10 | import net.staro.lego.module.Category; 11 | import net.staro.lego.module.modules.*; 12 | 13 | import java.util.ArrayList; 14 | import java.util.Arrays; 15 | import java.util.List; 16 | import java.util.stream.Collectors; 17 | 18 | @Getter 19 | public class ModuleManager extends GenericManager implements Jsonable { 20 | private final String configName = "modules.json"; 21 | 22 | @Override 23 | public void initialize(Lego lego) { 24 | register(new Step(lego)); 25 | register(new Notifications(lego)); 26 | register(new Velocity(lego)); 27 | register(new ClickGui(lego)); 28 | register(new Fullbright(lego)); 29 | register(new Heaven(lego)); 30 | } 31 | 32 | @Override 33 | public JsonElement toJson() { 34 | JsonObject object = new JsonObject(); 35 | getModules().forEach(module -> object.add(module.getName(), module.toJson())); 36 | return object; 37 | } 38 | 39 | @Override 40 | public void fromJson(JsonElement element) { 41 | getModules().forEach(module -> module.fromJson(element.getAsJsonObject().get(module.getName()))); 42 | } 43 | 44 | public List getModules() { 45 | return items; 46 | } 47 | 48 | public ArrayList getEnabledModules() { 49 | return getModules().stream() 50 | .filter(Module::isEnabled) 51 | .collect(Collectors.toCollection(ArrayList::new)); 52 | } 53 | 54 | public Module getModuleByName(String name) { 55 | return getModules().stream() 56 | .filter(module -> module.getName().equalsIgnoreCase(name)) 57 | .findFirst() 58 | .orElse(null); 59 | } 60 | 61 | public List getCategories() { 62 | return Arrays.asList(Category.values()); 63 | } 64 | 65 | public ArrayList getModulesByCategory(Category category) { 66 | ArrayList modulesCategory = new ArrayList<>(); 67 | getModules().forEach(module -> { 68 | if (module.getCategory() == category) { 69 | modulesCategory.add(module); 70 | } 71 | }); 72 | return modulesCategory; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/mixin/MixinMain.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.mixin; 2 | 3 | import net.minecraft.client.main.Main; 4 | import net.staro.lego.PreClientInitializer; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Inject; 8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 9 | 10 | @Mixin(Main.class) 11 | public abstract class MixinMain { 12 | @Inject(method = "main", at = @At(value = "INVOKE", target = "Ljoptsimple/OptionParser;allowsUnrecognizedOptions()V")) 13 | private static void onInit(String[] args, CallbackInfo ci) { 14 | new PreClientInitializer().onInitialize(); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/mixin/MixinMinecraftClient.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.mixin; 2 | 3 | import net.minecraft.client.MinecraftClient; 4 | import net.minecraft.client.RunArgs; 5 | import net.minecraft.util.crash.CrashReport; 6 | import net.staro.lego.Lego; 7 | import net.staro.lego.LegoClient; 8 | import net.staro.lego.events.client.ShutdownEvent; 9 | import net.staro.lego.events.tick.GameloopEvent; 10 | import net.staro.lego.events.tick.TickEvent; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 16 | 17 | import java.io.File; 18 | 19 | @Mixin(MinecraftClient.class) 20 | public abstract class MixinMinecraftClient { 21 | /** 22 | * Client's custom entry point. 23 | * Not using fabric's default client initialization due to several reasons which im too lazy to explain. 24 | * This is better, trust me bro! 25 | */ 26 | @Inject(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;setOverlay(Lnet/minecraft/client/gui/screen/Overlay;)V")) 27 | public void initializeClientHook(RunArgs args, CallbackInfo ci) { 28 | new LegoClient().onInitializeClient(MinecraftClient.class.cast(this)); 29 | } 30 | 31 | @ModifyVariable(method = "render", ordinal = 0, at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;runTasks()V", shift = At.Shift.AFTER)) 32 | private int runTickHook(int i) { 33 | GameloopEvent.INSTANCE.setTicks(i); 34 | Lego.EVENT_BUS.post(GameloopEvent.INSTANCE); 35 | return GameloopEvent.INSTANCE.getTicks(); 36 | } 37 | 38 | @Inject(method = "tick", at = @At("HEAD")) 39 | private void tickHook(CallbackInfo ci) { 40 | Lego.EVENT_BUS.post(TickEvent.INSTANCE); 41 | } 42 | 43 | @Inject(method = "close", at = @At("HEAD")) 44 | public void stop(CallbackInfo info) { 45 | Lego.EVENT_BUS.post(new ShutdownEvent()); 46 | } 47 | 48 | @Inject(method = "printCrashReport(Lnet/minecraft/client/MinecraftClient;Ljava/io/File;Lnet/minecraft/util/crash/CrashReport;)V", at = @At(value = "HEAD")) 49 | private static void printCrashReportHook(MinecraftClient client, File runDirectory, CrashReport crashReport, CallbackInfo ci) { 50 | Lego.EVENT_BUS.post(new ShutdownEvent()); 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/mixin/chat/MixinChatHud.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.mixin.chat; 2 | 3 | import net.minecraft.client.gui.hud.ChatHud; 4 | import net.minecraft.client.gui.hud.ChatHudLine; 5 | import net.minecraft.network.message.MessageSignatureData; 6 | import net.staro.lego.ducks.IChatHud; 7 | import org.jetbrains.annotations.Nullable; 8 | import org.spongepowered.asm.mixin.Final; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | import org.spongepowered.asm.mixin.gen.Invoker; 12 | 13 | import java.util.List; 14 | import java.util.ListIterator; 15 | 16 | @Mixin(ChatHud.class) 17 | public abstract class MixinChatHud implements IChatHud { 18 | @Shadow 19 | @Final 20 | private List messages; 21 | 22 | @Shadow 23 | protected abstract void refresh(); 24 | 25 | @Override 26 | @Invoker("addMessage") 27 | public abstract void lego$addMessage(@Nullable ChatHudLine message); 28 | 29 | @Override 30 | @Invoker("addVisibleMessage") 31 | public abstract void lego$addVisibleMessage(@Nullable ChatHudLine message); 32 | 33 | @Override 34 | public void lego$deleteMessage(@Nullable MessageSignatureData signature, boolean all) { 35 | if (signature == null) { 36 | return; 37 | } 38 | 39 | ListIterator listIterator = this.messages.listIterator(); 40 | boolean changed = false; 41 | while (listIterator.hasNext()) { 42 | var message = listIterator.next(); 43 | if (signature.equals(message.signature())) { 44 | listIterator.remove(); 45 | changed = true; 46 | if (!all) { 47 | break; 48 | } 49 | } 50 | } 51 | 52 | if (changed) { 53 | this.refresh(); 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/mixin/chat/MixinChatInputSuggestor.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.mixin.chat; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import com.mojang.brigadier.ParseResults; 5 | import com.mojang.brigadier.StringReader; 6 | import com.mojang.brigadier.suggestion.Suggestions; 7 | import net.minecraft.client.gui.screen.ChatInputSuggestor; 8 | import net.minecraft.client.gui.widget.TextFieldWidget; 9 | import net.minecraft.command.CommandSource; 10 | import net.staro.lego.Lego; 11 | import net.staro.lego.events.chat.InputSuggestorEvent; 12 | import org.spongepowered.asm.mixin.Final; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.Shadow; 15 | import org.spongepowered.asm.mixin.injection.At; 16 | import org.spongepowered.asm.mixin.injection.Inject; 17 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 18 | 19 | import java.util.concurrent.CompletableFuture; 20 | 21 | @Mixin(ChatInputSuggestor.class) 22 | public abstract class MixinChatInputSuggestor { 23 | @Shadow 24 | private ParseResults parse; 25 | @Shadow 26 | @Final 27 | TextFieldWidget textField; 28 | @Shadow 29 | boolean completingSuggestions; 30 | @Shadow 31 | private CompletableFuture pendingSuggestions; 32 | @Shadow 33 | private ChatInputSuggestor.SuggestionWindow window; 34 | @Shadow 35 | protected abstract void showCommandSuggestions(); 36 | 37 | @SuppressWarnings("DataFlowIssue") 38 | @Inject(method = "refresh", 39 | at = @At(value = "INVOKE", target = "Lcom/mojang/brigadier/StringReader;canRead()Z", remap = false), 40 | cancellable = true) 41 | public void onRefresh(CallbackInfo ci, @Local StringReader reader) { 42 | var event = new InputSuggestorEvent(textField, reader, parse, pendingSuggestions, window, completingSuggestions); 43 | Lego.EVENT_BUS.post(event); 44 | parse = event.getParse(); 45 | if (event.isCancelled()) { 46 | int cursor = event.getTextField().getCursor(); 47 | if (cursor >= 1 && (window == null || !completingSuggestions)) { 48 | pendingSuggestions = event.getPendingSuggestions(); 49 | // if this ever crashes - then ill add a nullcheck. 50 | pendingSuggestions.thenRun(() -> { 51 | if (pendingSuggestions.isDone()) { 52 | showCommandSuggestions(); 53 | } 54 | }); 55 | } 56 | 57 | ci.cancel(); 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/mixin/client/MixinGameOptions.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.mixin.client; 2 | 3 | import net.fabricmc.api.EnvType; 4 | import net.fabricmc.api.Environment; 5 | import net.minecraft.client.option.GameOptions; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.Constant; 8 | import org.spongepowered.asm.mixin.injection.ModifyConstant; 9 | 10 | @Environment(EnvType.CLIENT) 11 | @Mixin(GameOptions.class) 12 | public abstract class MixinGameOptions { 13 | @ModifyConstant(method = "", constant = @Constant(intValue = 110)) 14 | private int maxFovValueHook(int constant) { 15 | return 180; 16 | } 17 | 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/mixin/client/MixinKeyboard.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.mixin.client; 2 | 3 | import net.minecraft.client.Keyboard; 4 | import net.minecraft.client.MinecraftClient; 5 | import net.staro.lego.events.client.KeyEvent; 6 | import net.staro.lego.utility.MixinHelper; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 11 | 12 | @Mixin(Keyboard.class) 13 | public abstract class MixinKeyboard { 14 | @Inject(method = "onKey", at = @At("TAIL"), cancellable = true) 15 | private void onKey(long windowPointer, int key, int scanCode, int action, int modifiers, CallbackInfo ci) { 16 | if (MinecraftClient.getInstance().currentScreen == null) { 17 | var event = KeyEvent.INSTANCE; 18 | event.setKey(key); 19 | event.setAction(action); 20 | MixinHelper.hook(event, ci); 21 | } 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/mixin/entity/MixinClientPlayerEntity.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.mixin.entity; 2 | 3 | import net.minecraft.client.network.ClientPlayerEntity; 4 | import net.staro.lego.Lego; 5 | import net.staro.lego.events.tick.PostUpdateEvent; 6 | import net.staro.lego.events.tick.PreUpdateEvent; 7 | import net.staro.lego.events.tick.UpdateEvent; 8 | import net.staro.lego.module.modules.Velocity; 9 | import net.staro.lego.utility.MixinHelper; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 14 | 15 | @Mixin(ClientPlayerEntity.class) 16 | public abstract class MixinClientPlayerEntity { 17 | @Inject(method = "pushOutOfBlocks", at = @At("HEAD"), cancellable = true) 18 | private void pushOutOfBlocksSpaceHook(double d, double e, CallbackInfo ci) { 19 | MixinHelper.hook(new Velocity.PushOutOfBlocks(), ci); 20 | } 21 | 22 | @Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/AbstractClientPlayerEntity;tick()V", shift = At.Shift.AFTER)) 23 | private void tickMotionHook(CallbackInfo ci) { 24 | MixinHelper.hook(new UpdateEvent(ClientPlayerEntity.class.cast(this)), ci); 25 | } 26 | 27 | @Inject(method = "tick", at = @At(value = "FIELD", target = "Lnet/minecraft/client/network/ClientPlayerEntity;tickables:Ljava/util/List;", shift = At.Shift.BEFORE)) 28 | private void postMotionPlayerUpdateHook(CallbackInfo ci) { 29 | Lego.EVENT_BUS.post(new PostUpdateEvent()); 30 | } 31 | 32 | @Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/AbstractClientPlayerEntity;tick()V", shift = At.Shift.AFTER)) 33 | private void postSuperTickHook(CallbackInfo ci) { 34 | Lego.EVENT_BUS.post(new PreUpdateEvent()); 35 | } 36 | 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/mixin/entity/MixinEntity.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.mixin.entity; 2 | 3 | import net.minecraft.client.MinecraftClient; 4 | import net.minecraft.entity.Entity; 5 | import net.staro.lego.module.modules.Velocity; 6 | import net.staro.lego.utility.MixinHelper; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 11 | 12 | import java.util.Objects; 13 | 14 | @SuppressWarnings({"EqualsBetweenInconvertibleTypes", "DiscouragedShift"}) 15 | @Mixin(Entity.class) 16 | public class MixinEntity { 17 | @Inject(method = "pushAwayFrom", 18 | at = @At(value = "INVOKE", target = "Ljava/lang/Math;sqrt(D)D", shift = At.Shift.BEFORE), 19 | cancellable = true) 20 | private void entityPushHook(Entity entity, CallbackInfo ci) { 21 | if (Objects.equals(this, MinecraftClient.getInstance().player) || Objects.equals(entity, MinecraftClient.getInstance().player)) { 22 | MixinHelper.hook(new Velocity.EntityPush(), ci); 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/mixin/entity/MixinLivingEntity.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.mixin.entity; 2 | 3 | import net.minecraft.client.network.ClientPlayerEntity; 4 | import net.minecraft.entity.LivingEntity; 5 | import net.staro.lego.Lego; 6 | import net.staro.lego.module.modules.Step; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 11 | 12 | @Mixin(LivingEntity.class) 13 | public class MixinLivingEntity { 14 | @SuppressWarnings("ConstantValue") 15 | @Inject(method = "getStepHeight", at = @At("RETURN"), cancellable = true) 16 | private void getStepHeightHook(CallbackInfoReturnable cir) { 17 | if (ClientPlayerEntity.class.isInstance(this)) { 18 | var event = new Step.StepHeightEvent(ClientPlayerEntity.class.cast(this), cir.getReturnValueF()); 19 | Lego.EVENT_BUS.post(event); 20 | if (event.isCancelled()) { 21 | cir.setReturnValue(event.getHeight()); 22 | } 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/mixin/network/IExplosionS2CPacket.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.mixin.network; 2 | 3 | import net.minecraft.network.packet.s2c.play.ExplosionS2CPacket; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Mutable; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin({ExplosionS2CPacket.class}) 9 | public interface IExplosionS2CPacket { 10 | @Mutable 11 | @Accessor("playerVelocityX") 12 | void setMotionX(float velocityX); 13 | 14 | @Mutable 15 | @Accessor("playerVelocityY") 16 | void setMotionY(float velocityY); 17 | 18 | @Mutable 19 | @Accessor("playerVelocityZ") 20 | void setMotionZ(float velocityZ); 21 | } 22 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/mixin/network/MixinClientConnection.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.mixin.network; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import net.minecraft.network.ClientConnection; 5 | import net.minecraft.network.PacketCallbacks; 6 | import net.minecraft.network.listener.ClientPlayPacketListener; 7 | import net.minecraft.network.packet.Packet; 8 | import net.minecraft.network.packet.s2c.play.BundleS2CPacket; 9 | import net.staro.lego.Lego; 10 | import net.staro.lego.events.PacketEvent; 11 | import org.jetbrains.annotations.Nullable; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 16 | 17 | import java.util.Iterator; 18 | 19 | @SuppressWarnings("UnreachableCode") 20 | @Mixin(ClientConnection.class) 21 | public class MixinClientConnection { 22 | @Inject(method = "channelRead0(Lio/netty/channel/ChannelHandlerContext;Lnet/minecraft/network/packet/Packet;)V", 23 | at = @At(value = "INVOKE", target = "Lnet/minecraft/network/ClientConnection;handlePacket(Lnet/minecraft/network/packet/Packet;Lnet/minecraft/network/listener/PacketListener;)V", shift = At.Shift.BEFORE), cancellable = true) 24 | private void onHandlePacket(ChannelHandlerContext channelHandlerContext, Packet packet, CallbackInfo ci) { 25 | if (packet instanceof BundleS2CPacket bundle) { 26 | for (Iterator> it = bundle.getPackets().iterator(); it.hasNext(); ) { 27 | var event = PacketEvent.Receive.get(it.next(), (ClientConnection) (Object) this); 28 | Lego.EVENT_BUS.post(event); 29 | if (event.isCancelled()) { 30 | it.remove(); 31 | } 32 | } 33 | } else { 34 | var event = PacketEvent.Receive.get(packet, (ClientConnection) (Object) this); 35 | Lego.EVENT_BUS.post(event); 36 | if (event.isCancelled()) { 37 | ci.cancel(); 38 | } 39 | } 40 | } 41 | 42 | @Inject(at = @At("HEAD"), method = "send(Lnet/minecraft/network/packet/Packet;Lnet/minecraft/network/PacketCallbacks;)V", cancellable = true) 43 | private void onSendPacketHead(Packet packet, PacketCallbacks callbacks, CallbackInfo ci) { 44 | var event = (PacketEvent.Send.get(packet, (ClientConnection) (Object) this)); 45 | Lego.EVENT_BUS.post(event); 46 | if (event.isCancelled()) { 47 | ci.cancel(); 48 | } 49 | } 50 | 51 | @Inject(method = "send(Lnet/minecraft/network/packet/Packet;Lnet/minecraft/network/PacketCallbacks;)V", at = @At("TAIL")) 52 | private void onSendPacketTail(Packet packet, @Nullable PacketCallbacks callbacks, CallbackInfo ci) { 53 | var event = PacketEvent.Sent.get(packet, (ClientConnection) (Object) this); 54 | Lego.EVENT_BUS.post(event); 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/mixin/network/MixinClientPlayNetworkHandler.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.mixin.network; 2 | 3 | import net.minecraft.client.MinecraftClient; 4 | import net.minecraft.client.network.ClientPlayNetworkHandler; 5 | import net.staro.lego.Lego; 6 | import net.staro.lego.events.chat.ChatEvent; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | import org.spongepowered.asm.mixin.Unique; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 13 | 14 | @Mixin(ClientPlayNetworkHandler.class) 15 | public abstract class MixinClientPlayNetworkHandler { 16 | @Unique 17 | private boolean acceptMessage = true; 18 | 19 | @Shadow 20 | public abstract void sendChatMessage(String content); 21 | 22 | @Inject(method = "sendChatMessage", at = @At("HEAD"), cancellable = true) 23 | private void onSendChatMessage(String message, CallbackInfo ci) { 24 | if (acceptMessage) { 25 | var event = new ChatEvent(); 26 | event.setMessage(message); 27 | Lego.EVENT_BUS.post(event); 28 | if (event.isCancelled()) { 29 | MinecraftClient.getInstance().inGameHud.getChatHud().addToMessageHistory(event.getMessage()); 30 | } else { 31 | acceptMessage = false; 32 | sendChatMessage(event.getMessage()); 33 | acceptMessage = true; 34 | } 35 | 36 | ci.cancel(); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/mixin/render/MixinCamera.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.mixin.render; 2 | 3 | import net.minecraft.client.render.Camera; 4 | import net.minecraft.entity.Entity; 5 | import net.minecraft.world.BlockView; 6 | import net.staro.lego.events.camera.CameraUpdateEvent; 7 | import net.staro.lego.utility.MixinHelper; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 12 | 13 | @Mixin(Camera.class) 14 | public abstract class MixinCamera { 15 | @Inject(method = "update", at = @At("HEAD"), cancellable = true) 16 | private void onUpdate(BlockView area, 17 | Entity focusedEntity, 18 | boolean thirdPerson, 19 | boolean inverseView, 20 | float tickDelta, 21 | CallbackInfo ci) { 22 | var event = CameraUpdateEvent.INSTANCE; 23 | event.setArea(area); 24 | event.setFocusedEntity(focusedEntity); 25 | event.setThirdPerson(thirdPerson); 26 | event.setInverseView(inverseView); 27 | event.setTickDelta(tickDelta); 28 | MixinHelper.hook(event, ci); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/mixin/render/MixinLightmapTextureManager.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.mixin.render; 2 | 3 | import net.minecraft.client.render.LightmapTextureManager; 4 | import net.staro.lego.Lego; 5 | import net.staro.lego.module.modules.Fullbright; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.ModifyArg; 9 | 10 | @Mixin(LightmapTextureManager.class) 11 | public abstract class MixinLightmapTextureManager { 12 | @ModifyArg( 13 | method = "update", 14 | at = @At( 15 | value = "INVOKE", 16 | target = "Lnet/minecraft/client/texture/NativeImage;setColor(III)V"), 17 | index = 2) 18 | private int colorHook(int color) { 19 | var event = Fullbright.LightmapTexture.INSTANCE; 20 | event.setColor(color); 21 | Lego.EVENT_BUS.post(event); 22 | return event.getColor(); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/module/AbstractModule.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.module; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | import net.staro.api.Module; 6 | import net.staro.lego.Lego; 7 | import net.staro.lego.setting.SettingFactoryImpl; 8 | 9 | @Getter 10 | @RequiredArgsConstructor 11 | public abstract class AbstractModule extends SettingFactoryImpl implements Module { 12 | protected final Lego lego; 13 | private final String name; 14 | private final Category category; 15 | private final String description; 16 | 17 | protected abstract void onEnable(); 18 | 19 | protected abstract void onDisable(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/module/Category.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.module; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import net.staro.api.trait.Nameable; 6 | 7 | @Getter 8 | @AllArgsConstructor 9 | public enum Category implements Nameable { 10 | COMBAT("Combat"), 11 | MISC("Misc"), 12 | RENDER("Render"), 13 | MOVEMENT("Movement"), 14 | CLIENT("Client"); 15 | 16 | private final String name; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/module/LegoModule.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.module; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | import lombok.SneakyThrows; 6 | import lombok.extern.slf4j.Slf4j; 7 | import net.minecraft.client.MinecraftClient; 8 | import net.staro.api.Setting; 9 | import net.staro.lego.Lego; 10 | import net.staro.lego.events.client.ToggleEvent; 11 | import net.staro.lego.setting.GenericSetting; 12 | import net.staro.lego.setting.type.Bind; 13 | import net.staro.lego.utility.SettingUtil; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Arrays; 17 | import java.util.List; 18 | import java.util.Objects; 19 | import java.util.concurrent.atomic.AtomicBoolean; 20 | import java.util.stream.Collectors; 21 | import java.util.stream.Stream; 22 | 23 | @Slf4j 24 | public class LegoModule extends AbstractModule { 25 | private final Setting enabled = bool("Enabled", false, "Enables or disables the module."); 26 | private final Setting drawn = bool("Drawn", true, "Sets up the visibility of the module."); 27 | private final Setting bind = bind("Bind", Bind.none(), "Binds the module to a specified key."); 28 | private final AtomicBoolean atomicEnabled = new AtomicBoolean(); 29 | private final AtomicBoolean atomicDrawn = new AtomicBoolean(); 30 | protected final MinecraftClient mc; 31 | 32 | public LegoModule(Lego lego, String name, Category category, String description) { 33 | super(lego, name, category, description); 34 | this.mc = lego.mc(); 35 | } 36 | 37 | @Override 38 | public boolean isEnabled() { 39 | atomicEnabled.set(enabled.getValue()); 40 | return atomicEnabled.get(); 41 | } 42 | 43 | @Override 44 | public void enable() { 45 | if (!this.isEnabled()) { 46 | enabled.setValue(true); 47 | onEnable(); 48 | lego.eventBus().subscribe(this); 49 | } 50 | } 51 | 52 | @Override 53 | public void disable() { 54 | if (this.isEnabled()) { 55 | enabled.setValue(false); 56 | onDisable(); 57 | lego.eventBus().unsubscribe(this); 58 | } 59 | } 60 | 61 | @Override 62 | public void toggle() { 63 | lego.eventBus().post(new ToggleEvent(this, !this.isEnabled())); 64 | if (isEnabled()) { 65 | disable(); 66 | } else { 67 | enable(); 68 | } 69 | 70 | } 71 | 72 | @Override 73 | protected void onEnable() {} 74 | 75 | @Override 76 | protected void onDisable() {} 77 | 78 | @Override 79 | public List> getSettings() { 80 | return Stream.of(getClass(), getClass().getSuperclass(), getClass().getSuperclass().getSuperclass()) 81 | .flatMap(c -> Arrays.stream(c.getDeclaredFields())) 82 | .filter(field -> field.getType().isAssignableFrom(GenericSetting.class)) 83 | .peek(field -> field.setAccessible(true)) 84 | .map(field -> { 85 | try { 86 | return (GenericSetting) field.get(this); 87 | } 88 | catch (IllegalAccessException e) { 89 | System.out.println(e.getMessage()); 90 | return null; 91 | } 92 | }) 93 | .filter(Objects::nonNull) 94 | .peek(s -> s.setModule(this)) 95 | .collect(Collectors.toCollection(ArrayList::new)); 96 | } 97 | 98 | @Override 99 | public GenericSetting getSettingByName(String name) { 100 | return getSettings().stream().filter(setting -> setting.getName().equalsIgnoreCase(name)) 101 | .findFirst().orElse(null); 102 | } 103 | 104 | @Override 105 | public JsonElement toJson() { 106 | JsonObject object = new JsonObject(); 107 | getSettings().forEach(setting -> { 108 | try { 109 | if (setting.getValue() instanceof Bind) { 110 | object.addProperty(setting.getName(), ((Bind) setting.getValue()).getKey()); 111 | } else { 112 | object.addProperty(setting.getName(), setting.getValue().toString()); 113 | } 114 | } catch (Throwable e) { 115 | log.debug(e.getMessage()); 116 | } 117 | }); 118 | return object; 119 | } 120 | 121 | @Override 122 | @SneakyThrows 123 | public void fromJson(JsonElement element) { 124 | if (element != null) { 125 | JsonObject object = element.getAsJsonObject(); 126 | String enabled = object.get("Enabled").getAsString(); 127 | if (Boolean.parseBoolean(enabled)) toggle(); 128 | getSettings().forEach(setting -> { 129 | try { 130 | SettingUtil.setValueFromJson(this, setting, object.get(setting.getName())); 131 | } catch (Throwable throwable) { 132 | log.debug(throwable.getMessage()); 133 | } 134 | }); 135 | } 136 | } 137 | 138 | @Override 139 | public void setBind(int key) { 140 | this.bind.setValue(new Bind(key)); 141 | } 142 | 143 | @Override 144 | public Bind getBind() { 145 | return this.bind.getValue(); 146 | } 147 | 148 | @Override 149 | public boolean isDrawn() { 150 | atomicDrawn.set(drawn.getValue()); 151 | return atomicDrawn.get(); 152 | } 153 | 154 | @Override 155 | public void drawn(boolean state) { 156 | drawn.setValue(state); 157 | } 158 | 159 | } 160 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/module/modules/ClickGui.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.module.modules; 2 | 3 | import net.staro.lego.Lego; 4 | import net.staro.lego.module.Category; 5 | import net.staro.lego.module.LegoModule; 6 | 7 | // cope 8 | public class ClickGui extends LegoModule { 9 | public ClickGui(Lego lego) { 10 | super(lego, "ClickGui", Category.CLIENT, "A GUI for modules"); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/module/modules/Fullbright.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.module.modules; 2 | 3 | import bot.staro.rokit.annotation.Listener; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import net.staro.lego.Lego; 7 | import net.staro.lego.module.Category; 8 | import net.staro.lego.module.LegoModule; 9 | 10 | public class Fullbright extends LegoModule { 11 | public Fullbright(Lego lego) { 12 | super(lego, "Fullbright", Category.RENDER, "Makes the world bright."); 13 | } 14 | 15 | @SuppressWarnings("unused") 16 | @Listener 17 | public void onLightmapTexture(LightmapTexture event) { 18 | event.setColor(0xFFFFFFFF); 19 | } 20 | 21 | @Getter 22 | @Setter 23 | public static class LightmapTexture { 24 | public static final LightmapTexture INSTANCE = new LightmapTexture(); 25 | private int color; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/module/modules/Heaven.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.module.modules; 2 | 3 | import net.staro.api.annotation.SafeListener; 4 | import net.staro.lego.Lego; 5 | import net.staro.lego.events.camera.CameraUpdateEvent; 6 | import net.staro.lego.module.Category; 7 | import net.staro.lego.module.LegoModule; 8 | 9 | public class Heaven extends LegoModule { 10 | public Heaven(Lego lego) { 11 | super(lego, "Heaven", Category.RENDER, "Shows you what heaven is like."); 12 | } 13 | 14 | @SuppressWarnings("unused") 15 | @SafeListener 16 | public void onCameraUpdate(CameraUpdateEvent event) { 17 | mc.worldRenderer.reload(); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/module/modules/Notifications.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.module.modules; 2 | 3 | import net.minecraft.text.Text; 4 | import net.minecraft.util.Formatting; 5 | import net.staro.api.annotation.SafeListener; 6 | import net.staro.lego.Lego; 7 | import net.staro.lego.events.client.ToggleEvent; 8 | import net.staro.lego.module.Category; 9 | import net.staro.lego.module.LegoModule; 10 | 11 | public class Notifications extends LegoModule { 12 | public Notifications(Lego lego) { 13 | super(lego, "Notifications", Category.CLIENT, "Shows selected notifications in chat."); 14 | } 15 | 16 | @SuppressWarnings("unused") 17 | @SafeListener 18 | public void onSettingChanged(ToggleEvent event) { 19 | var module = event.module(); 20 | String message = event.state() ? " enabled." : " disabled."; 21 | Formatting formatting = event.state() ? Formatting.AQUA : Formatting.RED; 22 | lego.chat().sendLogless(Text.literal("") 23 | .append(Text.literal(module.getName()).formatted(Formatting.BOLD)) 24 | .append(Text.literal(message).formatted(formatting)), module.getName()); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/module/modules/Step.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.module.modules; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import net.minecraft.client.network.ClientPlayerEntity; 6 | import net.staro.api.Setting; 7 | import net.staro.api.annotation.SafeListener; 8 | import net.staro.lego.Lego; 9 | import net.staro.lego.events.CancellableEvent; 10 | import net.staro.lego.module.Category; 11 | import net.staro.lego.module.LegoModule; 12 | 13 | public class Step extends LegoModule { 14 | private final Setting height = floating("Height", 2, 0, 10, "The allowed Step height."); 15 | 16 | public Step(Lego lego) { 17 | super(lego, "Step", Category.MOVEMENT, "Steps up"); 18 | } 19 | 20 | @SuppressWarnings("unused") 21 | @SafeListener 22 | public void onStepHeight(StepHeightEvent event) { 23 | event.setHeight(height.getValue()); 24 | } 25 | 26 | @Getter 27 | @AllArgsConstructor 28 | public static class StepHeightEvent extends CancellableEvent { 29 | private final ClientPlayerEntity player; 30 | private float height; 31 | 32 | public void setHeight(float height) { 33 | setCancelled(true); 34 | this.height = height; 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/module/modules/Velocity.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.module.modules; 2 | 3 | import bot.staro.rokit.annotation.Listener; 4 | import net.minecraft.network.packet.s2c.play.EntityVelocityUpdateS2CPacket; 5 | import net.minecraft.network.packet.s2c.play.ExplosionS2CPacket; 6 | import net.staro.api.Setting; 7 | import net.staro.lego.Lego; 8 | import net.staro.lego.events.CancellableEvent; 9 | import net.staro.lego.events.PacketEvent; 10 | import net.staro.lego.mixin.network.IExplosionS2CPacket; 11 | import net.staro.lego.module.Category; 12 | import net.staro.lego.module.LegoModule; 13 | 14 | @SuppressWarnings("unused") 15 | public class Velocity extends LegoModule { 16 | private final Setting pushOutOfBlocks = bool("PushOutOfBlocks", true, "No push from blocks."); 17 | private final Setting entityPush = bool("EntityPush", true, "No push from entities."); 18 | 19 | public Velocity(Lego lego) { 20 | super(lego, "Velocity", Category.MOVEMENT, "Manipulates the velocity"); 21 | } 22 | 23 | @Listener 24 | public void onPacketReceived(PacketEvent.Receive event) { 25 | if (event.packet instanceof EntityVelocityUpdateS2CPacket) { 26 | event.setCancelled(true); 27 | } 28 | 29 | if (event.packet instanceof ExplosionS2CPacket p) { 30 | var explodePacket = ((IExplosionS2CPacket) p); 31 | explodePacket.setMotionX(0); 32 | explodePacket.setMotionY(0); 33 | explodePacket.setMotionZ(0); 34 | } 35 | } 36 | 37 | @Listener 38 | public void onPushOutOfBlocks(PushOutOfBlocks event) { 39 | event.setCancelled(pushOutOfBlocks.getValue()); 40 | } 41 | 42 | @Listener 43 | public void onEntityPush(EntityPush event) { 44 | event.setCancelled(entityPush.getValue()); 45 | } 46 | 47 | public static final class PushOutOfBlocks extends CancellableEvent {} 48 | 49 | public static final class EntityPush extends CancellableEvent {} 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/service/BindService.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.service; 2 | 3 | import bot.staro.rokit.Priority; 4 | import bot.staro.rokit.annotation.Listener; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.Setter; 7 | import net.minecraft.text.Text; 8 | import net.minecraft.util.Formatting; 9 | import net.staro.api.Module; 10 | import net.staro.lego.Lego; 11 | import net.staro.lego.command.Chat; 12 | import net.staro.lego.events.client.KeyEvent; 13 | import org.lwjgl.glfw.GLFW; 14 | 15 | @Setter 16 | @RequiredArgsConstructor 17 | public class BindService { 18 | private final Chat chat; 19 | private Module module; 20 | 21 | @SuppressWarnings("unused") 22 | @Listener(priority = Priority.MAX) 23 | private void onBindSet(KeyEvent event) { 24 | int key = event.getKey(); 25 | if (key == GLFW.GLFW_KEY_ENTER) { 26 | return; 27 | } 28 | 29 | if (key == GLFW.GLFW_KEY_ESCAPE) { 30 | chat.sendLogless(Text.literal("Operation cancelled").formatted(Formatting.GRAY)); 31 | } else { 32 | module.setBind(key); 33 | chat.sendLogless(Text.literal("The bind for " + module.getName().formatted(Formatting.WHITE, Formatting.BOLD) 34 | + " has been set to " + module.getBind().getBindName()).formatted(Formatting.GRAY)); 35 | event.setCancelled(true); 36 | } 37 | 38 | Lego.EVENT_BUS.unsubscribe(this); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/service/ChatMessageService.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.service; 2 | 3 | import bot.staro.rokit.annotation.Listener; 4 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import net.staro.lego.events.chat.ChatEvent; 8 | import net.staro.lego.events.chat.InputSuggestorEvent; 9 | import net.staro.lego.manager.managers.CommandManager; 10 | 11 | @Slf4j 12 | @RequiredArgsConstructor 13 | public class ChatMessageService { 14 | private final CommandManager commandManager; 15 | 16 | @SuppressWarnings("unused") 17 | @Listener 18 | public void onChatEvent(ChatEvent event) { 19 | var prefix = commandManager.getPrefix(); 20 | if (event.getMessage().startsWith(prefix)) { 21 | try { 22 | commandManager.dispatch(event.getMessage().substring(prefix.length())); 23 | } catch (CommandSyntaxException e) { 24 | log.debug(e.toString()); 25 | } 26 | 27 | event.setCancelled(true); 28 | } 29 | } 30 | 31 | @SuppressWarnings("unused") 32 | @Listener 33 | public void onInputSuggestion(InputSuggestorEvent event) { 34 | var prefix = commandManager.getPrefix(); 35 | int length = prefix.length(); 36 | var reader = event.getReader(); 37 | if (reader.canRead(length) && reader.getString().startsWith(prefix, reader.getCursor())) { 38 | reader.setCursor(reader.getCursor() + length); 39 | if (event.getParse() == null) { 40 | event.setParse(commandManager.getDispatcher().parse(reader, 41 | commandManager.getCommandSource())); 42 | } 43 | 44 | int cursor = event.getTextField().getCursor(); 45 | event.setPendingSuggestions(commandManager.getDispatcher().getCompletionSuggestions(event.getParse(), cursor)); 46 | event.setCancelled(true); 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/service/FirstLaunchService.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.service; 2 | 3 | import bot.staro.rokit.annotation.Listener; 4 | import net.staro.lego.events.client.InitEvent; 5 | 6 | import java.io.IOException; 7 | 8 | public class FirstLaunchService { 9 | private final String URL = "https://youtu.be/f9iE8b8jh2A?si=EjFttPahMeqfe9Z2"; 10 | 11 | @SuppressWarnings({"unused", "deprecation"}) 12 | @Listener 13 | public void onInitialize(InitEvent event) { 14 | if (event.lego().configManager().isFirstLaunch()) { 15 | new Thread(() -> { 16 | try { 17 | Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + URL); 18 | } catch (IOException e) { 19 | throw new RuntimeException(e); 20 | } 21 | }).start(); 22 | event.lego().eventBus().unsubscribe(this); 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/service/KeyboardService.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.service; 2 | 3 | import bot.staro.rokit.annotation.Listener; 4 | import lombok.RequiredArgsConstructor; 5 | import net.staro.lego.events.client.KeyEvent; 6 | import net.staro.lego.manager.managers.ModuleManager; 7 | 8 | @RequiredArgsConstructor 9 | public class KeyboardService { 10 | private final ModuleManager moduleManager; 11 | 12 | @SuppressWarnings("unused") 13 | @Listener 14 | public void onKeyPressed(KeyEvent event) { 15 | for (var module : moduleManager.getModules()) { 16 | if (module.getBind().getKey() == event.getKey()) { 17 | module.toggle(); 18 | } 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/service/ShutdownService.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.service; 2 | 3 | import bot.staro.rokit.annotation.Listener; 4 | import lombok.RequiredArgsConstructor; 5 | import net.staro.lego.events.client.ShutdownEvent; 6 | import net.staro.lego.manager.managers.ConfigManager; 7 | import net.staro.lego.manager.managers.ModuleManager; 8 | 9 | @RequiredArgsConstructor 10 | public class ShutdownService { 11 | private final ConfigManager configManager; 12 | private final ModuleManager moduleManager; 13 | 14 | @SuppressWarnings("unused") 15 | @Listener 16 | public void onShutdown(ShutdownEvent event) { 17 | configManager.saveEverything(moduleManager); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/setting/AbstractSetting.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.setting; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import net.staro.api.Setting; 7 | 8 | @Getter 9 | @Setter 10 | @AllArgsConstructor 11 | public abstract class AbstractSetting implements Setting { 12 | protected final String name; 13 | protected final T defaultValue; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/setting/GenericSetting.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.setting; 2 | 3 | import lombok.Getter; 4 | import lombok.NonNull; 5 | import lombok.Setter; 6 | import net.staro.api.Module; 7 | import net.staro.lego.setting.type.Bind; 8 | 9 | import java.awt.*; 10 | 11 | @Getter 12 | @Setter 13 | public class GenericSetting extends AbstractSetting { 14 | private final String name; 15 | private T defaultValue; 16 | private T value; 17 | private T min; 18 | private T max; 19 | private boolean hasRestriction; 20 | private T plannedValue; 21 | private final String description; 22 | 23 | private Module module; 24 | 25 | public GenericSetting(String name, T defaultValue, String description) { 26 | super(name, defaultValue); 27 | this.name = name; 28 | this.value = defaultValue; 29 | this.description = description; 30 | } 31 | 32 | public GenericSetting(String name, T defaultValue, T min, T max, String description) { 33 | super(name, defaultValue); 34 | this.name = name; 35 | this.min = min; 36 | this.max = max; 37 | this.value = defaultValue; 38 | this.hasRestriction = true; 39 | this.description = description; 40 | } 41 | 42 | @Override 43 | public void setValue(T value) { 44 | this.setPlannedValue(value); 45 | if (this.hasRestriction) { 46 | if (((Number) this.min).floatValue() > ((Number) value).floatValue()) { 47 | this.setPlannedValue(this.min); 48 | } 49 | if (((Number) this.max).floatValue() < ((Number) value).floatValue()) { 50 | this.setPlannedValue(this.max); 51 | } 52 | } 53 | 54 | this.value = this.plannedValue; 55 | } 56 | 57 | @Override 58 | @NonNull 59 | public String getType() { 60 | if (this.isEnumSetting()) { 61 | return "Enum"; 62 | } 63 | 64 | return this.getClassName(this.value); 65 | } 66 | 67 | public boolean isEnumSetting() { 68 | return !this.isNumberSetting() 69 | && !(this.value instanceof String) 70 | && !(this.value instanceof Bind) 71 | && !(this.value instanceof Character) 72 | && !(this.value instanceof Boolean) 73 | && !(this.value instanceof Color); 74 | } 75 | 76 | private boolean isNumberSetting() { 77 | return this.value instanceof Double 78 | || this.value instanceof Integer 79 | || this.value instanceof Short 80 | || this.value instanceof Long 81 | || this.value instanceof Float; 82 | } 83 | 84 | @SuppressWarnings("TypeParameterHidesVisibleType") 85 | private String getClassName(T value) { 86 | return value.getClass().getSimpleName(); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/setting/SettingFactoryImpl.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.setting; 2 | 3 | import net.staro.api.Setting; 4 | import net.staro.api.SettingFactory; 5 | import net.staro.lego.setting.settings.*; 6 | import net.staro.lego.setting.type.Bind; 7 | 8 | import java.awt.*; 9 | 10 | public abstract class SettingFactoryImpl implements SettingFactory { 11 | @Override 12 | public Setting bool(String name, boolean defaultValue, String description) { 13 | return new BooleanSetting(name, defaultValue, description); 14 | } 15 | 16 | @Override 17 | public Setting integer(String name, int defaultValue, int minValue, int maxValue, String description) { 18 | return new IntegerSetting(name, defaultValue, minValue, maxValue, description); 19 | } 20 | 21 | @Override 22 | public Setting floating(String name, float defaultValue, float minValue, float maxValue, String description) { 23 | return new FloatSetting(name, defaultValue, minValue, maxValue, description); 24 | } 25 | 26 | @Override 27 | public Setting precise(String name, double defaultValue, double minValue, double maxValue, String description) { 28 | return new DoubleSetting(name, defaultValue, minValue, maxValue, description); 29 | } 30 | 31 | @Override 32 | public > Setting enumSetting(String name, E defaultValue, String description) { 33 | return new GenericSetting<>(name, defaultValue, description); 34 | } 35 | 36 | @Override 37 | public Setting string(String name, String defaultValue, String description) { 38 | return new StringSetting(name, defaultValue, description); 39 | } 40 | 41 | @Override 42 | public Setting color(String name, Color defaultValue, String description) { 43 | return new ColorSetting(name, defaultValue, description); 44 | } 45 | 46 | @Override 47 | public Setting bind(String name, Bind defaultValue, String description) { 48 | return new BindSetting(name, defaultValue, description); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/setting/converter/EnumConverter.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.setting.converter; 2 | 3 | import com.google.common.base.Converter; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonPrimitive; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.Arrays; 9 | 10 | @SuppressWarnings("rawtypes") 11 | public class EnumConverter extends Converter { 12 | private final Class clazz; 13 | 14 | public EnumConverter(Class clazz) { 15 | this.clazz = clazz; 16 | } 17 | 18 | public static int currentEnum(Enum clazz) { 19 | return Arrays.stream(clazz.getClass().getEnumConstants()) 20 | .map(Enum::name) 21 | .toList() 22 | .indexOf(clazz.name()); 23 | } 24 | 25 | public static Enum increaseEnum(Enum clazz) { 26 | Enum[] constants = clazz.getClass().getEnumConstants(); 27 | int index = currentEnum(clazz); 28 | if (index >= 0 && index < constants.length - 1) { 29 | return constants[index + 1]; 30 | } 31 | return constants[0]; 32 | } 33 | 34 | public static Enum setEnumInt(@NotNull Enum clazz, int id) { 35 | Enum[] constants = clazz.getClass().getEnumConstants(); 36 | if (id >= 0 && id < constants.length) { 37 | return constants[id]; 38 | } 39 | return constants[0]; 40 | } 41 | 42 | public static String getProperName(Enum clazz) { 43 | return Character.toUpperCase(clazz.name().charAt(0)) + clazz.name().toLowerCase().substring(1); 44 | } 45 | 46 | public @NotNull JsonElement doForward(Enum anEnum) { 47 | return new JsonPrimitive(anEnum.toString()); 48 | } 49 | 50 | public @NotNull Enum doBackward(JsonElement jsonElement) { 51 | try { 52 | return Enum.valueOf(this.clazz, jsonElement.getAsString()); 53 | } catch (IllegalArgumentException e) { 54 | return null; 55 | } 56 | } 57 | 58 | public static String[] getNames(Enum clazz) { 59 | return Arrays.stream(clazz.getClass().getEnumConstants()).map(Enum::name).toArray(String[]::new); 60 | } 61 | 62 | } 63 | 64 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/setting/settings/BindSetting.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.setting.settings; 2 | 3 | import net.staro.lego.setting.GenericSetting; 4 | import net.staro.lego.setting.type.Bind; 5 | 6 | public class BindSetting extends GenericSetting { 7 | public BindSetting(String name, Bind defaultValue, String description) { 8 | super(name, defaultValue, description); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/setting/settings/BooleanSetting.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.setting.settings; 2 | 3 | import net.staro.lego.setting.GenericSetting; 4 | 5 | public class BooleanSetting extends GenericSetting { 6 | public BooleanSetting(String name, Boolean defaultValue, String description) { 7 | super(name, defaultValue, description); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/setting/settings/ColorSetting.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.setting.settings; 2 | 3 | import net.staro.lego.setting.GenericSetting; 4 | 5 | import java.awt.*; 6 | 7 | public class ColorSetting extends GenericSetting { 8 | public ColorSetting(String name, Color defaultValue, String description) { 9 | super(name, defaultValue, description); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/setting/settings/DoubleSetting.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.setting.settings; 2 | 3 | import net.staro.lego.setting.GenericSetting; 4 | 5 | public class DoubleSetting extends GenericSetting { 6 | public DoubleSetting(String name, double defaultValue, double minValue, double maxValue, String description) { 7 | super(name, defaultValue, minValue, maxValue, description); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/setting/settings/FloatSetting.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.setting.settings; 2 | 3 | import net.staro.lego.setting.GenericSetting; 4 | 5 | public class FloatSetting extends GenericSetting { 6 | public FloatSetting(String name, float defaultValue, float minValue, float maxValue, String description) { 7 | super(name, defaultValue, minValue, maxValue, description); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/setting/settings/IntegerSetting.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.setting.settings; 2 | 3 | import net.staro.lego.setting.GenericSetting; 4 | 5 | public class IntegerSetting extends GenericSetting { 6 | public IntegerSetting(String name, int defaultValue, int minValue, int maxValue, String description) { 7 | super(name, defaultValue, minValue, maxValue, description); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/setting/settings/StringSetting.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.setting.settings; 2 | 3 | import net.staro.lego.setting.GenericSetting; 4 | 5 | public class StringSetting extends GenericSetting { 6 | public StringSetting(String name, String defaultValue, String description) { 7 | super(name, defaultValue, description); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/setting/type/Bind.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.setting.type; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import net.minecraft.client.util.InputUtil; 7 | import org.lwjgl.glfw.GLFW; 8 | 9 | import java.lang.reflect.Field; 10 | import java.util.Objects; 11 | 12 | @Getter 13 | @Setter 14 | @AllArgsConstructor 15 | public class Bind { 16 | private int key; 17 | 18 | public static Bind none() { 19 | return new Bind(-1); 20 | } 21 | 22 | public boolean isEmpty() { 23 | return key < 0; 24 | } 25 | 26 | public String toString() { 27 | return isEmpty() ? "None" : (key < 0 ? "None" : capitalise(InputUtil.fromKeyCode(key, 0).getTranslationKey())); 28 | } 29 | 30 | private String capitalise(String str) { 31 | if (str.isEmpty()) return ""; 32 | return Character.toUpperCase(str.charAt(0)) + (str.length() != 1 ? str.substring(1).toLowerCase() : ""); 33 | } 34 | 35 | public String getBindName() { 36 | String kn = this.key > 0 ? GLFW.glfwGetKeyName(this.key, GLFW.glfwGetKeyScancode(this.key)) : "None"; 37 | if (kn == null) { 38 | try { 39 | for (Field declaredField : GLFW.class.getDeclaredFields()) { 40 | if (declaredField.getName().startsWith("GLFW_KEY_")) { 41 | int a = (int) declaredField.get(null); 42 | if (a == this.key) { 43 | String nb = declaredField.getName().substring("GLFW_KEY_".length()); 44 | kn = nb.substring(0, 1).toUpperCase() + nb.substring(1).toLowerCase(); 45 | } 46 | } 47 | } 48 | } catch (Exception ignore) { 49 | kn = "unknown." + this.key; 50 | } 51 | } 52 | 53 | return this.key == -1 ? "None" : (Objects.requireNonNull(kn)).toUpperCase(); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/utility/ColorUtil.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.utility; 2 | 3 | import lombok.experimental.UtilityClass; 4 | 5 | import java.awt.*; 6 | 7 | @UtilityClass 8 | public class ColorUtil { 9 | /** 10 | * Turns a hex string into a color variable. 11 | * @param hex is a hex string. 12 | * @return the decoded color. 13 | * @throws IllegalArgumentException handles the exceptions related to the incorrect hex string. 14 | */ 15 | public Color hexToColor(String hex) throws IllegalArgumentException { 16 | if (hex.startsWith("#")) { 17 | hex = hex.substring(1); 18 | } 19 | 20 | if (hex.length() != 6) { 21 | throw new IllegalArgumentException("Invalid hex color code"); 22 | } 23 | 24 | return Color.decode("#" + hex); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/utility/ConfigUtil.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.utility; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.google.gson.JsonObject; 6 | import com.google.gson.JsonParser; 7 | import lombok.SneakyThrows; 8 | import lombok.experimental.UtilityClass; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | import java.nio.file.Files; 13 | import java.nio.file.Path; 14 | import java.util.List; 15 | 16 | @UtilityClass 17 | public class ConfigUtil { 18 | public static Gson createGson() { 19 | return new GsonBuilder().setLenient().setPrettyPrinting().create(); 20 | } 21 | 22 | public static boolean initializeFolder(File folder) { 23 | boolean firstLaunch = false; 24 | if (!folder.exists()) { 25 | firstLaunch = true; 26 | if (!folder.mkdirs()) { 27 | throw new IllegalStateException("Failed to create directory: " + folder.getAbsolutePath()); 28 | } 29 | } 30 | 31 | return firstLaunch; 32 | } 33 | 34 | @SneakyThrows(value = IOException.class) 35 | public static void saveCurrentConfigName(String currentConfigName, Path path, Gson gson) { 36 | JsonObject jsonObject = new JsonObject(); 37 | jsonObject.addProperty("currentConfig", currentConfigName); 38 | Files.writeString(path, gson.toJson(jsonObject)); 39 | } 40 | 41 | @SneakyThrows(value = IOException.class) 42 | public static String loadCurrentConfigName(Path path) { 43 | if (Files.exists(path)) { 44 | String json = Files.readString(path); 45 | JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject(); 46 | return jsonObject.get("currentConfig").getAsString(); 47 | } 48 | 49 | return "modules"; 50 | } 51 | 52 | public static void loadModuleConfigNames(Path modulePath, List moduleConfigs) { 53 | File[] files = modulePath.toFile().listFiles((dir, name) -> name.endsWith(".json")); 54 | if (files != null) { 55 | for (File file : files) { 56 | String configName = file.getName().replace(".json", ""); 57 | moduleConfigs.add(configName); 58 | } 59 | } 60 | } 61 | 62 | } 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/utility/MixinHelper.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.utility; 2 | 3 | import lombok.experimental.UtilityClass; 4 | import net.staro.lego.Lego; 5 | import net.staro.lego.events.CancellableEvent; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 7 | 8 | /** 9 | * Utility for {@link org.spongepowered.asm.mixin.Mixin}. 10 | * 11 | * @author 3arthqu4ke. 12 | */ 13 | @UtilityClass 14 | public class MixinHelper { 15 | /** 16 | * Posts the given event on the {@link Lego#EVENT_BUS} and cancels the given {@link CallbackInfo} if 17 | * the event has been cancelled. Do not forget to mark your {@link org.spongepowered.asm.mixin.injection.Inject} 18 | * annotation as cancellable! 19 | * 20 | * @param event the event to post. 21 | * @param callbackInfo the CallbackInfo to cancel if the event has been cancelled. 22 | */ 23 | public void hook(CancellableEvent event, CallbackInfo callbackInfo) { 24 | Lego.EVENT_BUS.post(event); 25 | if (event.isCancelled()) { 26 | callbackInfo.cancel(); 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/client/java/net/staro/lego/utility/SettingUtil.java: -------------------------------------------------------------------------------- 1 | package net.staro.lego.utility; 2 | 3 | import com.google.gson.JsonElement; 4 | import lombok.experimental.UtilityClass; 5 | import lombok.extern.slf4j.Slf4j; 6 | import net.staro.api.Module; 7 | import net.staro.api.Setting; 8 | import net.staro.lego.command.arguments.BindArgument; 9 | import net.staro.lego.setting.GenericSetting; 10 | import net.staro.lego.setting.converter.EnumConverter; 11 | import net.staro.lego.setting.type.Bind; 12 | import org.lwjgl.glfw.GLFW; 13 | 14 | import java.awt.*; 15 | import java.util.Objects; 16 | import java.util.Optional; 17 | 18 | @Slf4j 19 | @UtilityClass 20 | public class SettingUtil { 21 | @SuppressWarnings({"rawtypes", "unchecked"}) 22 | public void setCommandValue(Module module, Setting setting, JsonElement element) { 23 | for (GenericSetting checkSetting : module.getSettings()) { 24 | if (Objects.equals(setting.getName(), checkSetting.getName())) { 25 | switch (checkSetting.getType()) { 26 | case "Parent" -> { 27 | return; 28 | } 29 | case "Boolean" -> { 30 | checkSetting.setValue(element.getAsBoolean()); 31 | return; 32 | } 33 | case "Double" -> { 34 | checkSetting.setValue(element.getAsDouble()); 35 | return; 36 | } 37 | case "Float" -> { 38 | checkSetting.setValue(element.getAsFloat()); 39 | return; 40 | } 41 | case "Integer" -> { 42 | checkSetting.setValue(element.getAsInt()); 43 | return; 44 | } 45 | case "String" -> { 46 | var string = element.getAsString(); 47 | checkSetting.setValue(string.replace("_", " ")); 48 | return; 49 | } 50 | case "Color" -> { 51 | try { 52 | Color color = ColorUtil.hexToColor(element.getAsString()); 53 | checkSetting.setValue(color); 54 | } catch (IllegalArgumentException e) { 55 | throw new RuntimeException("Invalid color code."); 56 | } 57 | return; 58 | } 59 | case "Enum" -> { 60 | try { 61 | EnumConverter converter = new EnumConverter(((Enum) checkSetting.getValue()).getClass()); 62 | Enum value = converter.doBackward(element); 63 | checkSetting.setValue(value); 64 | } catch (Exception ignored) { 65 | } 66 | } 67 | } 68 | } 69 | } 70 | } 71 | 72 | // Oyvey technologies. 73 | @SuppressWarnings({"rawtypes", "unchecked"}) 74 | public static void setValueFromJson(Module feature, Setting setting, JsonElement element) { 75 | String str; 76 | switch (setting.getType()) { 77 | case "Boolean" -> setting.setValue(element.getAsBoolean()); 78 | case "Double" -> setting.setValue(element.getAsDouble()); 79 | case "Float" -> setting.setValue(element.getAsFloat()); 80 | case "Integer" -> setting.setValue(element.getAsInt()); 81 | case "String" -> { 82 | str = element.getAsString(); 83 | setting.setValue(str.replace("_", " ")); 84 | } 85 | case "Bind" -> setting.setValue(new Bind(element.getAsInt())); 86 | case "Enum" -> { 87 | try { 88 | EnumConverter converter = new EnumConverter(((Enum) setting.getValue()).getClass()); 89 | Enum value = converter.doBackward(element); 90 | setting.setValue(value); 91 | } catch (Exception ignored) { 92 | } 93 | } 94 | case "Color" -> { 95 | try { 96 | Color color = ColorUtil.hexToColor(element.getAsString()); 97 | setting.setValue(color); 98 | } catch (IllegalArgumentException e) { 99 | throw new RuntimeException("Invalid color code."); 100 | } 101 | } 102 | default -> log.error("Unknown Setting type for: {} : {}", feature.getName(), setting.getName()); 103 | } 104 | } 105 | 106 | public Bind convertToBind(String key) { 107 | Optional matchingKeyCode = BindArgument.ALL_KEY_CODES.stream() 108 | .filter(keyCode -> { 109 | String glfwKeyName = GLFW.glfwGetKeyName(keyCode, 0); 110 | if (glfwKeyName != null && glfwKeyName.startsWith("GLFW_KEY_")) { 111 | glfwKeyName = glfwKeyName.substring("GLFW_KEY_".length()); 112 | } 113 | return glfwKeyName != null && glfwKeyName.toUpperCase().equals(key); 114 | }) 115 | .findFirst(); 116 | if (matchingKeyCode.isPresent()) { 117 | return new Bind(matchingKeyCode.get()); 118 | } else { 119 | throw new RuntimeException(); 120 | } 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /src/client/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "lego", 4 | "version": "1.0.2", 5 | "name": "Lego", 6 | "description": "A powerful base constructor for your own minecraft client", 7 | "authors": [ 8 | "St4ro" 9 | ], 10 | "license": "MIT", 11 | "environment": "client", 12 | "entrypoints": { }, 13 | "mixins": [ 14 | "lego.client.mixins.json" 15 | ], 16 | "depends": { 17 | "fabricloader": ">=0.15.11", 18 | "minecraft": "~1.21", 19 | "java": ">=21", 20 | "fabric-api": "*" 21 | }, 22 | "suggests": { 23 | "another-mod": "*" 24 | } 25 | } -------------------------------------------------------------------------------- /src/client/resources/lego.client.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "net.staro.lego.mixin", 4 | "compatibilityLevel": "JAVA_21", 5 | "client": [ 6 | "MixinMain", 7 | "MixinMinecraftClient", 8 | "chat.MixinChatHud", 9 | "chat.MixinChatInputSuggestor", 10 | "client.MixinGameOptions", 11 | "client.MixinKeyboard", 12 | "entity.MixinClientPlayerEntity", 13 | "network.MixinClientPlayNetworkHandler", 14 | "render.MixinCamera", 15 | "render.MixinLightmapTextureManager" 16 | ], 17 | "injectors": { 18 | "defaultRequire": 1 19 | }, 20 | "mixins": [ 21 | "entity.MixinEntity", 22 | "entity.MixinLivingEntity", 23 | "network.IExplosionS2CPacket", 24 | "network.MixinClientConnection" 25 | ] 26 | } --------------------------------------------------------------------------------