├── .github ├── FUNDING.yml ├── earthbot.yml └── workflows │ ├── build.yml │ └── check.yml ├── .gitignore ├── .gitmodules ├── .idea └── icon.svg ├── LICENSE ├── README.md ├── build.gradle ├── buildSrc ├── build.gradle.kts └── src │ └── main │ └── kotlin │ └── net │ └── earthcomputer │ └── clientcommands │ └── buildscript │ ├── CheckLanguageFilesTask.kt │ └── GenerateBuildInfoTask.kt ├── checkstyle.xml ├── docs ├── CONTRIBUTING.md └── TRANSLATING.md ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── regressionTests ├── enchantmentPlaySoundEffectHierarchy.regressiontest ├── enchantmentReplaceBlockEffectHierarchy.regressiontest ├── enchantmentReplaceDiskEffectHierarchy.regressiontest ├── enchantmentSpawnParticlesEffectHierarchy.regressiontest ├── playerRandomHierarchy.regressiontest ├── teleportRandomlyHierarchy.regressiontest └── testEnchantmentRemoveBinomialHierarchy.regressiontest ├── settings.gradle └── src ├── codeGen └── java │ └── net │ └── earthcomputer │ └── clientcommands │ └── codegen │ └── CodeGenerator.java ├── main ├── java │ └── net │ │ └── earthcomputer │ │ └── clientcommands │ │ ├── ClientCommands.java │ │ ├── Configs.java │ │ ├── c2c │ │ ├── C2CFriendlyByteBuf.java │ │ ├── C2CPacket.java │ │ ├── C2CPacketHandler.java │ │ ├── C2CPacketListener.java │ │ ├── ConversionHelper.java │ │ ├── OutgoingPacketFilter.java │ │ └── packets │ │ │ ├── MessageC2CPacket.java │ │ │ ├── PutConnectFourPieceC2CPacket.java │ │ │ ├── PutTicTacToeMarkC2CPacket.java │ │ │ └── StartTwoPlayerGameC2CPacket.java │ │ ├── command │ │ ├── AliasCommand.java │ │ ├── AreaStatsCommand.java │ │ ├── AuditMixinsCommand.java │ │ ├── BookCommand.java │ │ ├── BuildInfoCommand.java │ │ ├── CDebugCommand.java │ │ ├── CEnchantCommand.java │ │ ├── CFunctionCommand.java │ │ ├── CGameModeCommand.java │ │ ├── CGiveCommand.java │ │ ├── CParticleCommand.java │ │ ├── CPlaySoundCommand.java │ │ ├── CStopSoundCommand.java │ │ ├── CTeleportCommand.java │ │ ├── CTellRawCommand.java │ │ ├── CTimeCommand.java │ │ ├── CTitleCommand.java │ │ ├── CalcCommand.java │ │ ├── CalcStackCommand.java │ │ ├── CallbackCommand.java │ │ ├── ChatCommand.java │ │ ├── ChorusCommand.java │ │ ├── ClientCommandHelper.java │ │ ├── ClientEntitySelector.java │ │ ├── ConnectFourCommand.java │ │ ├── CrackRNGCommand.java │ │ ├── CreativeTabCommand.java │ │ ├── FakeCommandSource.java │ │ ├── FindBlockCommand.java │ │ ├── FindCommand.java │ │ ├── FindItemCommand.java │ │ ├── FishCommand.java │ │ ├── Flag.java │ │ ├── FovCommand.java │ │ ├── FramerateCommand.java │ │ ├── GammaCommand.java │ │ ├── GetDataCommand.java │ │ ├── GhostBlockCommand.java │ │ ├── GlowCommand.java │ │ ├── HotbarCommand.java │ │ ├── KitCommand.java │ │ ├── ListenCommand.java │ │ ├── LookCommand.java │ │ ├── MapCommand.java │ │ ├── MinesweeperCommand.java │ │ ├── MoteCommand.java │ │ ├── NoteCommand.java │ │ ├── PermissionLevelCommand.java │ │ ├── PingCommand.java │ │ ├── PlayerInfoCommand.java │ │ ├── PluginsCommand.java │ │ ├── PosCommand.java │ │ ├── RelogCommand.java │ │ ├── RenderCommand.java │ │ ├── ReplyCommand.java │ │ ├── ShrugCommand.java │ │ ├── SignSearchCommand.java │ │ ├── SnakeCommand.java │ │ ├── SnapCommand.java │ │ ├── StartupCommand.java │ │ ├── TaskCommand.java │ │ ├── TicTacToeCommand.java │ │ ├── TooltipCommand.java │ │ ├── TranslateCommand.java │ │ ├── UsageTreeCommand.java │ │ ├── UuidCommand.java │ │ ├── VarCommand.java │ │ ├── WaypointCommand.java │ │ ├── WeatherCommand.java │ │ ├── WhisperEncryptedCommand.java │ │ ├── WikiCommand.java │ │ └── arguments │ │ │ ├── ClientBlockPredicateArgument.java │ │ │ ├── ClientItemPredicateArgument.java │ │ │ ├── EntityUUIDArgument.java │ │ │ ├── ExpressionArgument.java │ │ │ ├── ExtendedMarkdownArgument.java │ │ │ ├── ItemAndEnchantmentsPredicateArgument.java │ │ │ ├── ListArgument.java │ │ │ ├── MultibaseIntegerArgument.java │ │ │ ├── PacketTypeArgument.java │ │ │ ├── RegexArgument.java │ │ │ ├── TranslationQueryArgument.java │ │ │ └── WithStringArgument.java │ │ ├── event │ │ ├── ClientConnectionEvents.java │ │ ├── ClientLevelEvents.java │ │ ├── MoreClientEntityEvents.java │ │ ├── MoreClientEvents.java │ │ └── MoreScreenEvents.java │ │ ├── features │ │ ├── CCrackRng.java │ │ ├── CCrackRngGen.java │ │ ├── ChatLengthExtender.java │ │ ├── ChorusManipulation.java │ │ ├── ClientCommandFunctions.java │ │ ├── ClientTimeModifier.java │ │ ├── ClientWeather.java │ │ ├── ClientcommandsDataQueryHandler.java │ │ ├── CommandExecutionCustomPayload.java │ │ ├── EnchantmentCracker.java │ │ ├── EntityGlowingTicket.java │ │ ├── FishingCracker.java │ │ ├── LegacyEnchantment.java │ │ ├── PacketDumper.java │ │ ├── PlayerRandCracker.java │ │ ├── Relogger.java │ │ ├── RenderSettings.java │ │ ├── ServerBrandManager.java │ │ ├── SuggestionsHook.java │ │ ├── TwoPlayerGame.java │ │ └── WikiRetriever.java │ │ ├── interfaces │ │ ├── IClientSuggestionsProvider.java │ │ ├── IClientSuggestionsProvider_Alias.java │ │ ├── ICreativeSlot.java │ │ ├── IDroppableInventoryContainer.java │ │ ├── IEditBox.java │ │ ├── IEntity_Debug.java │ │ ├── IEntity_Glowable.java │ │ └── ILivingEntityRenderState_Glowable.java │ │ ├── mixin │ │ ├── c2c │ │ │ ├── ChatListenerMixin.java │ │ │ └── ClientPacketListenerMixin.java │ │ ├── commands │ │ │ ├── alias │ │ │ │ └── ClientSuggestionProviderMixin.java │ │ │ ├── enchant │ │ │ │ ├── EnchantmentScreenMixin.java │ │ │ │ └── MultiPlayerGameModeMixin.java │ │ │ ├── findblock │ │ │ │ ├── ClientLevelMixin.java │ │ │ │ └── LevelChunkMixin.java │ │ │ ├── fish │ │ │ │ ├── FishingHookMixin.java │ │ │ │ ├── FishingRodItemMixin.java │ │ │ │ └── ItemEntityMixin.java │ │ │ ├── fps │ │ │ │ ├── FramerateLimitTrackerMixin.java │ │ │ │ └── MinecraftMixin.java │ │ │ ├── generic │ │ │ │ ├── ChatScreenMixin.java │ │ │ │ ├── ClientSuggestionsProviderMixin.java │ │ │ │ └── CommandSuggestionsMixin.java │ │ │ ├── glow │ │ │ │ ├── ArmorStandRendererMixin.java │ │ │ │ ├── ClientLevelMixin.java │ │ │ │ ├── EntityMixin.java │ │ │ │ ├── LivingEntityRenderStateMixin.java │ │ │ │ └── LivingEntityRendererMixin.java │ │ │ ├── listen │ │ │ │ └── ConnectionMixin.java │ │ │ ├── relog │ │ │ │ └── ClientPacketListenerMixin.java │ │ │ ├── render │ │ │ │ └── EntityRendererDispatcherMixin.java │ │ │ ├── reply │ │ │ │ └── ClientPacketListenerMixin.java │ │ │ ├── snap │ │ │ │ └── MinecraftMixin.java │ │ │ ├── time │ │ │ │ └── ClientLevelDataMixin.java │ │ │ └── weather │ │ │ │ └── LevelMixin.java │ │ ├── dataqueryhandler │ │ │ └── ClientPacketListenerMixin.java │ │ ├── debug │ │ │ ├── EntityMixin.java │ │ │ └── ServerLevelMixin.java │ │ ├── events │ │ │ ├── ClientPacketListenerMixin.java │ │ │ ├── EntityMixin.java │ │ │ ├── ExperienceOrbMixin.java │ │ │ └── MinecraftMixin.java │ │ ├── lengthextender │ │ │ ├── ChatScreenMixin.java │ │ │ ├── EditBoxMixin.java │ │ │ └── StringUtilMixin.java │ │ ├── rngevents │ │ │ ├── AnvilMenuMixin.java │ │ │ ├── ClientLevelMixin.java │ │ │ ├── ClientPacketListenerMixin.java │ │ │ ├── ConsumableMixin.java │ │ │ ├── CreeperMixin.java │ │ │ ├── CrossbowItemMixin.java │ │ │ ├── CustomCreativeSlotMixin.java │ │ │ ├── EntityMixin.java │ │ │ ├── FishingRodItemMixin.java │ │ │ ├── FlintAndSteelItemMixin.java │ │ │ ├── FoodOnAStickItemMixin.java │ │ │ ├── HoeAndShovelItemMixin.java │ │ │ ├── InventoryMixin.java │ │ │ ├── ItemStackMixin.java │ │ │ ├── LivingEntityMixin.java │ │ │ ├── LocalPlayerMixin.java │ │ │ ├── MultiPlayerGameModeMixin.java │ │ │ ├── MushroomCowSheepAndSnowGolemMixin.java │ │ │ ├── PlayerMixin.java │ │ │ ├── PumpkinBlockMixin.java │ │ │ ├── ServerboundContainerClosePacketMixin.java │ │ │ ├── ShearsItemMixin.java │ │ │ └── droppableinventory │ │ │ │ ├── AbstractCraftingMenuMixin.java │ │ │ │ ├── CartographyTableMenuMixin.java │ │ │ │ ├── EnchantmentMenuMixin.java │ │ │ │ ├── GrindstoneMenuMixin.java │ │ │ │ ├── ItemCombinerMenuMixin.java │ │ │ │ ├── LoomMenuMixin.java │ │ │ │ ├── MerchantMenuMixin.java │ │ │ │ └── StonecutterMenuMixin.java │ │ ├── scrambletitle │ │ │ └── MinecraftMixin.java │ │ ├── serverbrand │ │ │ └── ClientCommonPacketListenerImplMixin.java │ │ └── suggestionshook │ │ │ └── ClientPacketListenerMixin.java │ │ ├── render │ │ ├── Cuboid.java │ │ ├── Line.java │ │ ├── RenderQueue.java │ │ └── Shape.java │ │ ├── task │ │ ├── ItemThrowTask.java │ │ ├── LongTask.java │ │ ├── LongTaskList.java │ │ ├── OneTickTask.java │ │ ├── RenderDistanceScanTask.java │ │ ├── SimpleTask.java │ │ └── TaskManager.java │ │ └── util │ │ ├── BrigadierRemover.java │ │ ├── BuildInfo.java │ │ ├── CUtil.java │ │ ├── DebugRandom.java │ │ ├── GuiBlocker.java │ │ ├── IntegratedServerUtil.java │ │ ├── MappingsHelper.java │ │ ├── MathUtil.java │ │ ├── MultiVersionCompat.java │ │ ├── ReflectionUtils.java │ │ ├── SeedfindingUtil.java │ │ ├── ThrowingPredicate.java │ │ └── UnsafeUtils.java └── resources │ ├── assets │ └── clientcommands │ │ ├── icon.png │ │ ├── lang │ │ ├── de_de.json │ │ ├── en_us.json │ │ ├── id_id.json │ │ ├── ja_jp.json │ │ ├── nl_nl.json │ │ ├── pl_pl.json │ │ ├── ru_ru.json │ │ ├── sl_si.json │ │ ├── zh_cn.json │ │ └── zh_tw.json │ │ └── textures │ │ ├── connect_four │ │ ├── board.png │ │ └── pieces.png │ │ ├── minesweeper_atlas.png │ │ ├── snake_grid.png │ │ └── tic_tac_toe │ │ ├── grid.png │ │ └── marks.png │ ├── clientcommands.aw │ ├── fabric.mod.json │ ├── mixins.clientcommands.json │ └── pack.mcmeta └── test └── java └── net └── earthcomputer └── clientcommands └── test ├── CallHierarchyWalker.java ├── EntityRandomCallHierarchyTest.java ├── ExtendedMarkdownTest.java ├── MixinApplyTest.java ├── ReferencesFinder.java ├── TestUtil.java └── WaypointLoadingTest.java /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: earthcomputer 2 | -------------------------------------------------------------------------------- /.github/earthbot.yml: -------------------------------------------------------------------------------- 1 | templates: 2 | modded: RNG manipulation features are not guaranteed to work on a modded server. If you ask your server admin for a world download, run the world in singleplayer, and the RNG feature works, then the mods are causing the problem. In particular, PaperMC is known to break player RNG manipulation features. Modded servers will never be supported. 3 | stale: "This issue has been inactive for 3 days or more, so it is assumed to be resolved and will be closed. If it was not resolved, simply reply again and it will be reopened." 4 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Check and Release Build 2 | on: 3 | push: 4 | branches: ['*'] 5 | jobs: 6 | build: 7 | name: Build 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | with: 12 | fetch-depth: 0 13 | - uses: gradle/actions/wrapper-validation@v4 14 | - uses: actions/setup-java@v4 15 | with: 16 | distribution: 'zulu' 17 | java-version: '21' 18 | - uses: actions/cache@v4 19 | with: 20 | path: | 21 | ~/.gradle/caches 22 | ~/.gradle/wrapper 23 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} 24 | - run: ./gradlew check --stacktrace --no-daemon 25 | - run: ./gradlew build -Pmod_version="$(git describe --always --tags | cut -c2-)" --stacktrace --no-daemon 26 | - uses: actions/upload-artifact@v4 27 | with: 28 | name: clientcommands-snapshot 29 | path: build/libs/ 30 | - uses: actions/upload-artifact@v4 31 | if: always() 32 | with: 33 | name: test-results 34 | path: build/reports/ 35 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | name: Check Pull Request 2 | on: 3 | pull_request: 4 | branches: ['*'] 5 | jobs: 6 | build: 7 | name: Build 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | - uses: gradle/actions/wrapper-validation@v4 12 | - uses: actions/setup-java@v4 13 | with: 14 | distribution: 'zulu' 15 | java-version: '21' 16 | - uses: actions/cache@v4 17 | with: 18 | path: | 19 | ~/.gradle/caches 20 | ~/.gradle/wrapper 21 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} 22 | - run: ./gradlew check --stacktrace --no-daemon 23 | - uses: actions/upload-artifact@v4 24 | if: always() 25 | with: 26 | name: test-results 27 | path: build/reports/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | 7 | # idea 8 | 9 | .idea/* 10 | !.idea/icon.svg 11 | *.iml 12 | *.ipr 13 | *.iws 14 | 15 | # vscode 16 | 17 | .settings/ 18 | .vscode/ 19 | bin/ 20 | .classpath 21 | .project 22 | 23 | # fabric 24 | 25 | run/ 26 | logs/ 27 | 28 | /changelog.txt 29 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Earthcomputer/clientcommands/689ecbb8ea687da513a54116e6c2b27419240f28/.gitmodules -------------------------------------------------------------------------------- /.idea/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # clientcommands 2 | Adds several useful client-side commands to Minecraft 3 | 4 | ## Social 5 | Discord: https://discord.gg/Jg7Bun7 6 | Patreon: https://www.patreon.com/earthcomputer 7 | 8 | ## Installation 9 | 1. Download and run the [Fabric installer](https://fabricmc.net/use). 10 | - Click the "vanilla" button, leave the other settings as they are, 11 | and click "download installer". 12 | - Note: this step may vary if you aren't using the vanilla launcher 13 | or an old version of Minecraft. 14 | 1. Download [Fabric API](https://minecraft.curseforge.com/projects/fabric) 15 | and move it to the mods folder (`.minecraft/mods`). 16 | 1. Download clientcommands from the [releases page](https://github.com/Earthcomputer/clientcommands/releases) or from [Modrinth](https://modrinth.com/mod/client-commands) 17 | and move it to the mods folder (`.minecraft/mods`). 18 | 19 | ## Contributing 20 | To contribute translations, see the [translation contribution guidelines](docs/TRANSLATING.md). 21 | 22 | For other contributions, see the [contribution guidelines](docs/CONTRIBUTING.md). 23 | -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | dependencies { 10 | implementation("com.google.code.gson:gson:2.11.0") 11 | } 12 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/net/earthcomputer/clientcommands/buildscript/GenerateBuildInfoTask.kt: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.buildscript 2 | 3 | import com.google.gson.Gson 4 | import com.google.gson.JsonObject 5 | import org.gradle.api.DefaultTask 6 | import org.gradle.api.file.RegularFileProperty 7 | import org.gradle.api.tasks.OutputFile 8 | import org.gradle.api.tasks.TaskAction 9 | import org.gradle.process.ExecOperations 10 | import java.io.ByteArrayOutputStream 11 | import java.nio.charset.StandardCharsets 12 | import javax.inject.Inject 13 | 14 | abstract class GenerateBuildInfoTask : DefaultTask() { 15 | 16 | companion object { 17 | private val GSON = Gson() 18 | } 19 | 20 | @get:OutputFile 21 | abstract val outputFile: RegularFileProperty 22 | 23 | @get:Inject 24 | protected abstract val execOperations: ExecOperations 25 | 26 | init { 27 | // never reuse previous outputs 28 | this.outputs.upToDateWhen { false } 29 | } 30 | 31 | @TaskAction 32 | protected fun run() { 33 | val version = this.project.version.toString() 34 | val branch = this.executeCommand("git", "branch", "--show-current") 35 | val shortCommitHash = this.executeCommand("git", "rev-parse", "--short", "HEAD") 36 | val commitHash = this.executeCommand("git", "rev-parse", "HEAD") 37 | 38 | val jsonObject = JsonObject().apply { 39 | addProperty("version", version) 40 | addProperty("branch", branch) 41 | addProperty("shortCommitHash", shortCommitHash) 42 | addProperty("commitHash", commitHash) 43 | } 44 | 45 | outputFile.asFile.get().writer().use { 46 | GSON.toJson(jsonObject, it) 47 | } 48 | } 49 | 50 | private fun executeCommand(vararg args: Any): String { 51 | val outputBytes = ByteArrayOutputStream() 52 | this.execOperations.exec { 53 | standardOutput = outputBytes 54 | commandLine(*args) 55 | }.rethrowFailure() 56 | return outputBytes.toString(StandardCharsets.UTF_8).trim() 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx2G 3 | 4 | # Fabric Properties 5 | # check these on https://fabricmc.net/develop/ 6 | minecraft_version=1.21.5 7 | minecraft_version_dependency=1.21.5 8 | minecraft_version_list=1.21.5 9 | minecraft_version_list_presentable=1.21.5 10 | loader_version=0.16.10 11 | 12 | parchment_mcversion=1.21.4 13 | parchment_version=2025.03.23 14 | 15 | # Mod Properties 16 | mod_version=2.10.1 17 | maven_group=net.earthcomputer 18 | archives_base_name=clientcommands 19 | 20 | # Dependencies 21 | # also check this on https://fabricmc.net/develop/ 22 | fabric_version=0.119.5+1.21.5 23 | clientarguments_version=1.11.1 24 | betterconfig_version=2.3.0 25 | seedfinding_core_version=1.200.1 26 | seedfinding_biome_version=1.171.1 27 | seedfinding_feature_version=1.171.9 28 | seedfinding_seed_version=1.171.2 29 | latticg_version=1.07 30 | mapping_io_version=0.7.1 31 | devauth_version=1.2.1 32 | checkstyle_version=10.21.1 33 | 34 | jazzer_junit_version=0.22.1 35 | junit_version=5.11.3 36 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Earthcomputer/clientcommands/689ecbb8ea687da513a54116e6c2b27419240f28/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.13-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /regressionTests/enchantmentPlaySoundEffectHierarchy.regressiontest: -------------------------------------------------------------------------------- 1 | net/minecraft/world/item/enchantment/Enchantments.bootstrap (Lnet/minecraft/data/worldgen/BootstrapContext;)V <- net/minecraft/world/item/enchantment/effects/PlaySoundEffect. 2 | net/minecraft/world/item/enchantment/Enchantments.bootstrap (Lnet/minecraft/data/worldgen/BootstrapContext;)V <- net/minecraft/world/item/enchantment/effects/PlaySoundEffect. 3 | net/minecraft/world/item/enchantment/Enchantments.bootstrap (Lnet/minecraft/data/worldgen/BootstrapContext;)V <- net/minecraft/world/item/enchantment/effects/PlaySoundEffect. 4 | net/minecraft/world/item/enchantment/effects/PlaySoundEffect. ()V <- net/minecraft/world/item/enchantment/effects/PlaySoundEffect.lambda$static$0 <- net/minecraft/world/item/enchantment/effects/PlaySoundEffect. 5 | -------------------------------------------------------------------------------- /regressionTests/enchantmentReplaceBlockEffectHierarchy.regressiontest: -------------------------------------------------------------------------------- 1 | net/minecraft/world/item/enchantment/effects/ReplaceBlock. ()V <- net/minecraft/world/item/enchantment/effects/ReplaceBlock.lambda$static$0 <- net/minecraft/world/item/enchantment/effects/ReplaceBlock. 2 | -------------------------------------------------------------------------------- /regressionTests/enchantmentReplaceDiskEffectHierarchy.regressiontest: -------------------------------------------------------------------------------- 1 | net/minecraft/world/item/enchantment/Enchantments.bootstrap (Lnet/minecraft/data/worldgen/BootstrapContext;)V <- net/minecraft/world/item/enchantment/effects/ReplaceDisk. 2 | net/minecraft/world/item/enchantment/effects/ReplaceDisk. ()V <- net/minecraft/world/item/enchantment/effects/ReplaceDisk.lambda$static$0 <- net/minecraft/world/item/enchantment/effects/ReplaceDisk. 3 | -------------------------------------------------------------------------------- /regressionTests/enchantmentSpawnParticlesEffectHierarchy.regressiontest: -------------------------------------------------------------------------------- 1 | net/minecraft/world/item/enchantment/Enchantments.bootstrap (Lnet/minecraft/data/worldgen/BootstrapContext;)V <- net/minecraft/world/item/enchantment/effects/SpawnParticlesEffect. 2 | net/minecraft/world/item/enchantment/effects/SpawnParticlesEffect. ()V <- net/minecraft/world/item/enchantment/effects/SpawnParticlesEffect.lambda$static$0 <- net/minecraft/world/item/enchantment/effects/SpawnParticlesEffect. 3 | -------------------------------------------------------------------------------- /regressionTests/teleportRandomlyHierarchy.regressiontest: -------------------------------------------------------------------------------- 1 | net/minecraft/world/item/consume_effects/TeleportRandomlyConsumeEffect. ()V <- net/minecraft/world/item/consume_effects/TeleportRandomlyConsumeEffect. 2 | net/minecraft/world/item/component/Consumables. ()V <- net/minecraft/world/item/consume_effects/TeleportRandomlyConsumeEffect. <- net/minecraft/world/item/consume_effects/TeleportRandomlyConsumeEffect. 3 | net/minecraft/world/item/consume_effects/TeleportRandomlyConsumeEffect. ()V <- net/minecraft/world/item/consume_effects/TeleportRandomlyConsumeEffect.lambda$static$0 <- net/minecraft/world/item/consume_effects/TeleportRandomlyConsumeEffect. 4 | -------------------------------------------------------------------------------- /regressionTests/testEnchantmentRemoveBinomialHierarchy.regressiontest: -------------------------------------------------------------------------------- 1 | net/minecraft/world/item/enchantment/Enchantments.bootstrap (Lnet/minecraft/data/worldgen/BootstrapContext;)V <- net/minecraft/world/item/enchantment/effects/RemoveBinomial. 2 | net/minecraft/world/item/enchantment/Enchantments.bootstrap (Lnet/minecraft/data/worldgen/BootstrapContext;)V <- net/minecraft/world/item/enchantment/effects/RemoveBinomial. 3 | net/minecraft/world/item/enchantment/effects/RemoveBinomial. ()V <- net/minecraft/world/item/enchantment/effects/RemoveBinomial.lambda$static$0 <- net/minecraft/world/item/enchantment/effects/RemoveBinomial. 4 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = 'Fabric' 5 | url = 'https://maven.fabricmc.net/' 6 | } 7 | gradlePluginPortal() 8 | } 9 | } 10 | 11 | rootProject.name = 'clientcommands' 12 | -------------------------------------------------------------------------------- /src/codeGen/java/net/earthcomputer/clientcommands/codegen/CodeGenerator.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.codegen; 2 | 3 | import com.seedfinding.latticg.generator.ClassGenerator; 4 | import com.seedfinding.latticg.reversal.Program; 5 | import com.seedfinding.latticg.reversal.ProgramBuilder; 6 | import com.seedfinding.latticg.reversal.calltype.java.JavaCalls; 7 | import com.seedfinding.latticg.util.LCG; 8 | import net.earthcomputer.clientcommands.features.CCrackRng; 9 | 10 | import java.io.IOException; 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | 14 | public class CodeGenerator { 15 | public static void main(String[] args) throws IOException { 16 | if (args.length != 1) { 17 | System.err.println("clientcommands-codegen "); 18 | return; 19 | } 20 | 21 | Path destDir = Path.of(args[0]); 22 | genLattiCG(destDir); 23 | } 24 | 25 | private static void genLattiCG(Path destDir) throws IOException { 26 | ProgramBuilder program = Program.builder(LCG.JAVA); 27 | program.skip(-CCrackRng.NUM_THROWS * 4); 28 | for (int i = 0; i < CCrackRng.NUM_THROWS; i++) { 29 | program.skip(1); 30 | program.add(JavaCalls.nextFloat().ranged(CCrackRng.MAX_ERROR * 2)); 31 | program.skip(2); 32 | } 33 | 34 | writeLattiCGClass(program.build(), "net.earthcomputer.clientcommands.features.CCrackRngGen", destDir); 35 | } 36 | 37 | private static void writeLattiCGClass(Program program, String fqName, Path destDir) throws IOException { 38 | int dotIndex = fqName.lastIndexOf('.'); 39 | assert dotIndex >= 0; 40 | String packageName = fqName.substring(0, dotIndex); 41 | String className = fqName.substring(dotIndex + 1); 42 | 43 | String generatedClass = new ClassGenerator(packageName, className, program).generate(); 44 | Files.writeString(destDir.resolve(fqName.replace('.', '/') + ".java"), generatedClass); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/c2c/C2CFriendlyByteBuf.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.c2c; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import net.minecraft.core.RegistryAccess; 5 | import net.minecraft.network.RegistryFriendlyByteBuf; 6 | 7 | import java.util.UUID; 8 | 9 | public class C2CFriendlyByteBuf extends RegistryFriendlyByteBuf { 10 | private final String sender; 11 | private final UUID senderUUID; 12 | 13 | public C2CFriendlyByteBuf(ByteBuf source, RegistryAccess registryAccess, String sender, UUID senderUUID) { 14 | super(source, registryAccess); 15 | this.sender = sender; 16 | this.senderUUID = senderUUID; 17 | } 18 | 19 | public String getSender() { 20 | return this.sender; 21 | } 22 | 23 | public UUID getSenderUUID() { 24 | return this.senderUUID; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/c2c/C2CPacket.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.c2c; 2 | 3 | import net.minecraft.network.protocol.Packet; 4 | 5 | public interface C2CPacket extends Packet { 6 | String sender(); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/c2c/C2CPacketListener.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.c2c; 2 | 3 | import net.earthcomputer.clientcommands.c2c.packets.MessageC2CPacket; 4 | import net.earthcomputer.clientcommands.c2c.packets.PutConnectFourPieceC2CPacket; 5 | import net.earthcomputer.clientcommands.c2c.packets.PutTicTacToeMarkC2CPacket; 6 | import net.earthcomputer.clientcommands.c2c.packets.StartTwoPlayerGameC2CPacket; 7 | import net.minecraft.network.ClientboundPacketListener; 8 | 9 | public interface C2CPacketListener extends ClientboundPacketListener { 10 | void onMessageC2CPacket(MessageC2CPacket packet); 11 | 12 | void onStartTwoPlayerGameC2CPacket(StartTwoPlayerGameC2CPacket packet); 13 | 14 | void onPutTicTacToeMarkC2CPacket(PutTicTacToeMarkC2CPacket packet); 15 | 16 | void onPutConnectFourPieceC2CPacket(PutConnectFourPieceC2CPacket packet); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/c2c/OutgoingPacketFilter.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.c2c; 2 | 3 | import com.google.common.cache.CacheBuilder; 4 | 5 | import java.time.Duration; 6 | import java.util.Collections; 7 | import java.util.Set; 8 | 9 | public class OutgoingPacketFilter { 10 | 11 | private static final Set cache = Collections.newSetFromMap(CacheBuilder.newBuilder().expireAfterWrite(Duration.ofMinutes(1)).build().asMap()); 12 | 13 | public static boolean removeIfContains(String packetString) { 14 | return cache.remove(packetString); 15 | } 16 | 17 | public static void addPacket(String packetString) { 18 | cache.add(packetString); 19 | } 20 | 21 | public static void clear() { 22 | cache.clear(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/c2c/packets/MessageC2CPacket.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.c2c.packets; 2 | 3 | import net.earthcomputer.clientcommands.c2c.C2CPacket; 4 | import net.earthcomputer.clientcommands.c2c.C2CPacketListener; 5 | import net.earthcomputer.clientcommands.c2c.C2CFriendlyByteBuf; 6 | import net.minecraft.network.codec.StreamCodec; 7 | import net.minecraft.network.protocol.Packet; 8 | import net.minecraft.network.protocol.PacketFlow; 9 | import net.minecraft.network.protocol.PacketType; 10 | import net.minecraft.resources.ResourceLocation; 11 | 12 | public record MessageC2CPacket(String sender, String message) implements C2CPacket { 13 | public static final StreamCodec CODEC = Packet.codec(MessageC2CPacket::write, MessageC2CPacket::new); 14 | public static final PacketType ID = new PacketType<>(PacketFlow.CLIENTBOUND, ResourceLocation.fromNamespaceAndPath("clientcommands", "message")); 15 | 16 | public MessageC2CPacket(C2CFriendlyByteBuf buf) { 17 | this(buf.getSender(), buf.readUtf()); 18 | } 19 | 20 | public void write(C2CFriendlyByteBuf buf) { 21 | buf.writeUtf(this.message); 22 | } 23 | 24 | @Override 25 | public void handle(C2CPacketListener handler) { 26 | handler.onMessageC2CPacket(this); 27 | } 28 | 29 | @Override 30 | public PacketType> type() { 31 | return ID; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/c2c/packets/PutConnectFourPieceC2CPacket.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.c2c.packets; 2 | 3 | import net.earthcomputer.clientcommands.c2c.C2CFriendlyByteBuf; 4 | import net.earthcomputer.clientcommands.c2c.C2CPacket; 5 | import net.earthcomputer.clientcommands.c2c.C2CPacketListener; 6 | import net.minecraft.network.FriendlyByteBuf; 7 | import net.minecraft.network.RegistryFriendlyByteBuf; 8 | import net.minecraft.network.codec.StreamCodec; 9 | import net.minecraft.network.protocol.Packet; 10 | import net.minecraft.network.protocol.PacketFlow; 11 | import net.minecraft.network.protocol.PacketType; 12 | import net.minecraft.resources.ResourceLocation; 13 | 14 | import java.util.UUID; 15 | 16 | public record PutConnectFourPieceC2CPacket(String sender, UUID senderUUID, int x) implements C2CPacket { 17 | public static final StreamCodec CODEC = Packet.codec(PutConnectFourPieceC2CPacket::write, PutConnectFourPieceC2CPacket::new); 18 | public static final PacketType ID = new PacketType<>(PacketFlow.CLIENTBOUND, ResourceLocation.fromNamespaceAndPath("clientcommands", "put_connect_four_piece")); 19 | 20 | public PutConnectFourPieceC2CPacket(C2CFriendlyByteBuf buf) { 21 | this(buf.getSender(), buf.getSenderUUID(), buf.readVarInt()); 22 | } 23 | 24 | public void write(C2CFriendlyByteBuf buf) { 25 | buf.writeVarInt(this.x); 26 | } 27 | 28 | @Override 29 | public PacketType> type() { 30 | return ID; 31 | } 32 | 33 | @Override 34 | public void handle(C2CPacketListener handler) { 35 | handler.onPutConnectFourPieceC2CPacket(this); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/c2c/packets/PutTicTacToeMarkC2CPacket.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.c2c.packets; 2 | 3 | import net.earthcomputer.clientcommands.c2c.C2CPacket; 4 | import net.earthcomputer.clientcommands.c2c.C2CPacketListener; 5 | import net.earthcomputer.clientcommands.c2c.C2CFriendlyByteBuf; 6 | import net.minecraft.network.codec.StreamCodec; 7 | import net.minecraft.network.protocol.Packet; 8 | import net.minecraft.network.protocol.PacketFlow; 9 | import net.minecraft.network.protocol.PacketType; 10 | import net.minecraft.resources.ResourceLocation; 11 | 12 | import java.util.UUID; 13 | 14 | public record PutTicTacToeMarkC2CPacket(String sender, UUID senderUUID, byte x, byte y) implements C2CPacket { 15 | public static final StreamCodec CODEC = Packet.codec(PutTicTacToeMarkC2CPacket::write, PutTicTacToeMarkC2CPacket::new); 16 | public static final PacketType ID = new PacketType<>(PacketFlow.CLIENTBOUND, ResourceLocation.fromNamespaceAndPath("clientcommands", "put_tic_tac_toe_mark")); 17 | 18 | public PutTicTacToeMarkC2CPacket(C2CFriendlyByteBuf buf) { 19 | this(buf.getSender(), buf.getSenderUUID(), buf.readByte(), buf.readByte()); 20 | } 21 | 22 | public void write(C2CFriendlyByteBuf buf) { 23 | buf.writeByte(this.x); 24 | buf.writeByte(this.y); 25 | } 26 | 27 | @Override 28 | public void handle(C2CPacketListener handler) { 29 | handler.onPutTicTacToeMarkC2CPacket(this); 30 | } 31 | 32 | @Override 33 | public PacketType> type() { 34 | return ID; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/c2c/packets/StartTwoPlayerGameC2CPacket.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.c2c.packets; 2 | 3 | import net.earthcomputer.clientcommands.c2c.C2CFriendlyByteBuf; 4 | import net.earthcomputer.clientcommands.c2c.C2CPacket; 5 | import net.earthcomputer.clientcommands.c2c.C2CPacketListener; 6 | import net.earthcomputer.clientcommands.features.TwoPlayerGame; 7 | import net.minecraft.network.FriendlyByteBuf; 8 | import net.minecraft.network.RegistryFriendlyByteBuf; 9 | import net.minecraft.network.codec.StreamCodec; 10 | import net.minecraft.network.protocol.Packet; 11 | import net.minecraft.network.protocol.PacketFlow; 12 | import net.minecraft.network.protocol.PacketType; 13 | import net.minecraft.resources.ResourceLocation; 14 | 15 | import java.util.UUID; 16 | 17 | public record StartTwoPlayerGameC2CPacket(String sender, UUID senderUUID, boolean accept, TwoPlayerGame game) implements C2CPacket { 18 | public static final StreamCodec CODEC = Packet.codec(StartTwoPlayerGameC2CPacket::write, StartTwoPlayerGameC2CPacket::new); 19 | public static final PacketType ID = new PacketType<>(PacketFlow.CLIENTBOUND, ResourceLocation.fromNamespaceAndPath("clientcommands", "start_two_player_game")); 20 | 21 | public StartTwoPlayerGameC2CPacket(C2CFriendlyByteBuf buf) { 22 | this(buf.getSender(), buf.getSenderUUID(), buf.readBoolean(), TwoPlayerGame.getById(buf.readResourceLocation())); 23 | } 24 | 25 | public void write(C2CFriendlyByteBuf buf) { 26 | buf.writeBoolean(this.accept); 27 | buf.writeResourceLocation(this.game.getId()); 28 | } 29 | 30 | @Override 31 | public void handle(C2CPacketListener handler) { 32 | handler.onStartTwoPlayerGameC2CPacket(this); 33 | } 34 | 35 | @Override 36 | public PacketType> type() { 37 | return ID; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/AuditMixinsCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 6 | import net.fabricmc.loader.api.FabricLoader; 7 | import org.spongepowered.asm.mixin.MixinEnvironment; 8 | 9 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 10 | 11 | public class AuditMixinsCommand { 12 | public static void register(CommandDispatcher dispatcher) { 13 | boolean enableAuditMixins = FabricLoader.getInstance().isDevelopmentEnvironment() || Boolean.getBoolean("clientcommands.enableAuditMixins"); 14 | if (!enableAuditMixins) { 15 | return; 16 | } 17 | dispatcher.register(literal("cauditmixins").executes(ctx -> auditMixins())); 18 | } 19 | 20 | private static int auditMixins() { 21 | MixinEnvironment.getCurrentEnvironment().audit(); 22 | return Command.SINGLE_SUCCESS; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/BuildInfoCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import net.earthcomputer.clientcommands.util.BuildInfo; 6 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 7 | import net.minecraft.network.chat.Component; 8 | 9 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 10 | 11 | public class BuildInfoCommand { 12 | public static void register(CommandDispatcher dispatcher) { 13 | dispatcher.register(literal("cbuildinfo") 14 | .executes(ctx -> buildInfo(ctx.getSource()))); 15 | } 16 | 17 | private static int buildInfo(FabricClientCommandSource source) { 18 | source.sendFeedback(Component.translatable("commands.cbuildinfo.success", BuildInfo.VERSION, BuildInfo.BRANCH, BuildInfo.SHORT_COMMIT_HASH)); 19 | return Command.SINGLE_SUCCESS; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/CDebugCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 6 | import net.minecraft.client.gui.components.DebugScreenOverlay; 7 | import net.minecraft.util.StringRepresentable; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import static dev.xpple.clientarguments.arguments.CEnumArgument.*; 11 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 12 | 13 | public class CDebugCommand { 14 | public static void register(CommandDispatcher dispatcher) { 15 | dispatcher.register(literal("cdebug") 16 | .executes(ctx -> execute(ctx.getSource(), DebugScreenType.OVERLAY)) 17 | .then(argument("type", enumArg(DebugScreenType.class)) 18 | .executes(ctx -> execute(ctx.getSource(), getEnum(ctx, "type")))) 19 | ); 20 | } 21 | 22 | private static int execute(FabricClientCommandSource source, DebugScreenType type) { 23 | DebugScreenOverlay debugScreenOverlay = source.getClient().getDebugOverlay(); 24 | switch (type) { 25 | case OVERLAY -> debugScreenOverlay.toggleOverlay(); 26 | case FPS -> debugScreenOverlay.toggleFpsCharts(); 27 | case NETWORK -> debugScreenOverlay.toggleNetworkCharts(); 28 | case PROFILER -> debugScreenOverlay.toggleProfilerChart(); 29 | } 30 | return Command.SINGLE_SUCCESS; 31 | } 32 | 33 | private enum DebugScreenType implements StringRepresentable { 34 | OVERLAY("overlay"), 35 | FPS("fps"), 36 | NETWORK("network"), 37 | PROFILER("profiler"); 38 | 39 | private final String name; 40 | 41 | DebugScreenType(String name) { 42 | this.name = name; 43 | } 44 | 45 | public @NotNull String getSerializedName() { 46 | return this.name; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/CFunctionCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.CommandDispatcher; 4 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 5 | import net.earthcomputer.clientcommands.features.ClientCommandFunctions; 6 | import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; 7 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 8 | import net.minecraft.commands.SharedSuggestionProvider; 9 | import net.minecraft.network.chat.Component; 10 | 11 | import static com.mojang.brigadier.arguments.StringArgumentType.*; 12 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 13 | 14 | public class CFunctionCommand { 15 | public static void register(CommandDispatcher dispatcher) { 16 | dispatcher.register(literal("cfunction") 17 | .then(argument("function", greedyString()) 18 | .suggests((context, builder) -> SharedSuggestionProvider.suggest(ClientCommandFunctions.allFunctions(), builder)) 19 | .executes(ctx -> run(ctx.getSource(), getString(ctx, "function"))))); 20 | } 21 | 22 | private static int run(FabricClientCommandSource source, String function) throws CommandSyntaxException { 23 | return ClientCommandFunctions.executeFunction(ClientCommandManager.getActiveDispatcher(), source, function, result -> source.sendFeedback(Component.translatable("commands.cfunction.success", result, function))); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/CStopSoundCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 6 | import dev.xpple.clientarguments.arguments.CSuggestionProviders; 7 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 8 | import net.minecraft.network.chat.Component; 9 | import net.minecraft.resources.ResourceLocation; 10 | import net.minecraft.sounds.SoundSource; 11 | 12 | import static dev.xpple.clientarguments.arguments.CResourceLocationArgument.*; 13 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 14 | 15 | public class CStopSoundCommand { 16 | 17 | public static void register(CommandDispatcher dispatcher) { 18 | var builder = literal("cstopsound"); 19 | 20 | for (SoundSource source : SoundSource.values()) { 21 | builder.then(buildArguments(source, source.getName())); 22 | } 23 | builder.then(buildArguments(null, "*")); 24 | 25 | dispatcher.register(builder); 26 | } 27 | 28 | private static LiteralArgumentBuilder buildArguments(SoundSource source, String literal) { 29 | return literal(literal) 30 | .executes(ctx -> stopSound(ctx.getSource(), source, null)) 31 | .then(argument("sound", id()) 32 | .suggests(CSuggestionProviders.AVAILABLE_SOUNDS) 33 | .executes(ctx -> stopSound(ctx.getSource(), source, getId(ctx, "sound")))); 34 | } 35 | 36 | private static int stopSound(FabricClientCommandSource source, SoundSource soundSource, ResourceLocation sound) { 37 | source.getClient().getSoundManager().stop(sound, soundSource); 38 | 39 | if (soundSource == null && sound == null) { 40 | source.sendFeedback(Component.translatable("commands.cstopsound.success.sourceless.any")); 41 | } else if (soundSource == null) { 42 | source.sendFeedback(Component.translatable("commands.cstopsound.success.sourceless.sound", sound)); 43 | } else if (sound == null) { 44 | source.sendFeedback(Component.translatable("commands.cstopsound.success.source.any", soundSource.getName())); 45 | } else { 46 | source.sendFeedback(Component.translatable("commands.cstopsound.success.source.sound", sound, soundSource.getName())); 47 | } 48 | return Command.SINGLE_SUCCESS; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/CTeleportCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 6 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 7 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 8 | import net.minecraft.client.multiplayer.ClientPacketListener; 9 | import net.minecraft.client.multiplayer.PlayerInfo; 10 | import net.minecraft.network.chat.Component; 11 | import net.minecraft.network.protocol.game.ServerboundTeleportToEntityPacket; 12 | 13 | import java.util.UUID; 14 | 15 | import static net.earthcomputer.clientcommands.command.arguments.EntityUUIDArgument.*; 16 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 17 | 18 | public class CTeleportCommand { 19 | private static final SimpleCommandExceptionType NOT_SPECTATOR_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.ctp.notSpectator")); 20 | 21 | public static void register(CommandDispatcher dispatcher) { 22 | dispatcher.register(literal("ctp") 23 | .then(argument("entity", entityUuid()) 24 | .executes(ctx -> teleport(ctx.getSource(), getEntityUuid(ctx, "entity"))))); 25 | } 26 | 27 | private static int teleport(FabricClientCommandSource source, UUID uuid) throws CommandSyntaxException { 28 | if (!source.getPlayer().isSpectator()) { 29 | throw NOT_SPECTATOR_EXCEPTION.create(); 30 | } 31 | 32 | ClientPacketListener packetListener = source.getClient().getConnection(); 33 | assert packetListener != null; 34 | 35 | packetListener.send(new ServerboundTeleportToEntityPacket(uuid)); 36 | 37 | PlayerInfo playerInfo = packetListener.getPlayerInfo(uuid); 38 | String name; 39 | if (playerInfo != null) { 40 | name = playerInfo.getProfile().getName(); 41 | } else { 42 | name = uuid.toString(); 43 | } 44 | 45 | source.sendFeedback(Component.translatable("commands.ctp.success", name)); 46 | return Command.SINGLE_SUCCESS; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/CTellRawCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 6 | import net.minecraft.commands.CommandBuildContext; 7 | import net.minecraft.network.chat.ComponentUtils; 8 | import net.minecraft.network.chat.MutableComponent; 9 | 10 | import static dev.xpple.clientarguments.arguments.CComponentArgument.*; 11 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 12 | 13 | public class CTellRawCommand { 14 | 15 | public static void register(CommandDispatcher dispatcher, CommandBuildContext context) { 16 | dispatcher.register(literal("ctellraw") 17 | .then(argument("message", textComponent(context)) 18 | .executes(ctx -> { 19 | MutableComponent component = ComponentUtils.updateForEntity(new FakeCommandSource(ctx.getSource().getPlayer()), getComponent(ctx, "message"), ctx.getSource().getPlayer(), 0); 20 | ctx.getSource().getClient().gui.getChat().addMessage(component); 21 | return Command.SINGLE_SUCCESS; 22 | }) 23 | ) 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/CalcStackCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 6 | import net.minecraft.commands.CommandBuildContext; 7 | import net.minecraft.network.chat.Component; 8 | import net.minecraft.world.InteractionHand; 9 | import net.minecraft.world.item.ItemStack; 10 | 11 | import static com.mojang.brigadier.arguments.IntegerArgumentType.*; 12 | import static dev.xpple.clientarguments.arguments.CItemArgument.*; 13 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 14 | 15 | public class CalcStackCommand { 16 | 17 | public static void register(CommandDispatcher dispatcher, CommandBuildContext context) { 18 | dispatcher.register(literal("ccalcstack") 19 | .then(argument("count", integer(0)) 20 | .then(argument("item", itemStack(context)) 21 | .executes(ctx -> { 22 | ItemStack stack = getItemStackArgument(ctx, "item").createItemStack(1, false); 23 | return getStackSize(ctx.getSource(), stack, getInteger(ctx, "count")); 24 | })) 25 | .executes(ctx -> getStackSize(ctx.getSource(), getInteger(ctx, "count"))))); 26 | } 27 | 28 | private static int getStackSize(FabricClientCommandSource source, ItemStack stack, int count) { 29 | int stacks = count / stack.getMaxStackSize(); 30 | int remainder = count % stack.getMaxStackSize(); 31 | 32 | if (stack.isEmpty()) { 33 | if (remainder == 0) { 34 | source.sendFeedback(Component.translatable("commands.ccalcstack.success.empty.exact", count, stacks)); 35 | } else { 36 | source.sendFeedback(Component.translatable("commands.ccalcstack.success.empty", count, stacks, remainder)); 37 | } 38 | } else { 39 | Component itemText = stack.getDisplayName(); 40 | if (remainder == 0) { 41 | source.sendFeedback(Component.translatable("commands.ccalcstack.success.exact", count, itemText, stacks)); 42 | } else { 43 | source.sendFeedback(Component.translatable("commands.ccalcstack.success", count, itemText, stacks, remainder)); 44 | } 45 | } 46 | 47 | return Command.SINGLE_SUCCESS; 48 | } 49 | 50 | private static int getStackSize(FabricClientCommandSource source, int count) { 51 | ItemStack heldStack = source.getPlayer().getItemInHand(InteractionHand.MAIN_HAND).copy(); 52 | return getStackSize(source, heldStack, count); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/CallbackCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 6 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 7 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 8 | import net.minecraft.network.chat.Component; 9 | 10 | import java.util.UUID; 11 | 12 | import static dev.xpple.clientarguments.arguments.CUuidArgument.*; 13 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 14 | 15 | public class CallbackCommand { 16 | private static final SimpleCommandExceptionType NO_SUCH_CALLBACK_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.ccallback.failed")); 17 | 18 | public static void register(CommandDispatcher dispatcher) { 19 | dispatcher.register(literal("ccallback") 20 | .then(argument("id", uuid()) 21 | .executes(ctx -> runCallback(getUuid(ctx, "id"))))); 22 | } 23 | 24 | private static int runCallback(UUID uuid) throws CommandSyntaxException { 25 | if (!ClientCommandHelper.runCallback(uuid)) { 26 | throw NO_SUCH_CALLBACK_EXCEPTION.create(); 27 | } 28 | 29 | return Command.SINGLE_SUCCESS; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/ChatCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 6 | import net.minecraft.client.Minecraft; 7 | 8 | import static net.earthcomputer.clientcommands.command.ClientCommandHelper.*; 9 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 10 | 11 | public class ChatCommand { 12 | public static void register(CommandDispatcher dispatcher) { 13 | dispatcher.register(literal("chat").executes(ctx -> execute())); 14 | } 15 | 16 | private static int execute() { 17 | Minecraft.getInstance().schedule(() -> { 18 | Minecraft.getInstance().openChatScreen(""); 19 | sendFeedback("commands.chat.success"); 20 | }); 21 | return Command.SINGLE_SUCCESS; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/ChorusCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.CommandDispatcher; 4 | import com.mojang.brigadier.builder.RequiredArgumentBuilder; 5 | import dev.xpple.clientarguments.arguments.CCoordinates; 6 | import net.earthcomputer.clientcommands.features.ChorusManipulation; 7 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 8 | import net.minecraft.core.Direction; 9 | 10 | import java.util.EnumSet; 11 | 12 | import static dev.xpple.clientarguments.arguments.CVec3Argument.*; 13 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 14 | 15 | public class ChorusCommand { 16 | public static void register(CommandDispatcher dispatcher) { 17 | dispatcher.register(literal("cchorus") 18 | .then(literal("setGoal") 19 | .then(literal("relative") 20 | .then(literal("area") 21 | .then(areaThen(true))) 22 | .then(literal("block") 23 | .then(blockThen(true)))) 24 | .then(literal("absolute") 25 | .then(literal("area") 26 | .then(areaThen(false))) 27 | .then(literal("block") 28 | .then(blockThen(false)))) 29 | )); 30 | } 31 | 32 | public static RequiredArgumentBuilder areaThen(boolean relative) { 33 | return argument("posFrom", vec3()) 34 | .then(argument("posTo", vec3()) 35 | .executes(ctx -> ChorusManipulation.setGoal(getVec3(ctx, "posFrom"), getVec3(ctx, "posTo"), relative))); 36 | } 37 | 38 | public static RequiredArgumentBuilder blockThen(boolean relative) { 39 | return argument("posGoal", vec3()) 40 | .executes(ctx -> ChorusManipulation.setGoal( 41 | getVec3(ctx, "posGoal").align(EnumSet.allOf(Direction.Axis.class)).add(-0.2, 0, -0.2), 42 | getVec3(ctx, "posGoal").align(EnumSet.allOf(Direction.Axis.class)).add(1.2, 1, 1.2), relative)) 43 | .then(literal("--perfectly") 44 | .executes(ctx -> ChorusManipulation.setGoal( 45 | getVec3(ctx, "posGoal").align(EnumSet.allOf(Direction.Axis.class)).add(0.3, 0, 0.3), 46 | getVec3(ctx, "posGoal").align(EnumSet.allOf(Direction.Axis.class)).add(0.7, 1, 0.7), relative))); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/ClientEntitySelector.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 4 | import net.minecraft.client.Minecraft; 5 | import net.minecraft.commands.CommandSourceStack; 6 | import net.minecraft.commands.arguments.EntityArgument; 7 | import net.minecraft.world.entity.Entity; 8 | import net.minecraft.world.phys.Vec3; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import java.util.function.BiConsumer; 14 | import java.util.function.BiPredicate; 15 | 16 | public class ClientEntitySelector { 17 | 18 | private final BiPredicate filter; 19 | private final BiConsumer> sorter; 20 | private final int limit; 21 | private final boolean senderOnly; 22 | private final Double originX; 23 | private final Double originY; 24 | private final Double originZ; 25 | 26 | public ClientEntitySelector(BiPredicate filter, BiConsumer> sorter, int limit, boolean senderOnly, Double originX, Double originY, Double originZ) { 27 | this.filter = filter; 28 | this.sorter = sorter; 29 | this.limit = limit; 30 | this.senderOnly = senderOnly; 31 | this.originX = originX; 32 | this.originY = originY; 33 | this.originZ = originZ; 34 | } 35 | 36 | public Entity getEntity(CommandSourceStack source) throws CommandSyntaxException { 37 | List entities = getEntities(source); 38 | if (entities.isEmpty()) { 39 | throw EntityArgument.NO_ENTITIES_FOUND.create(); 40 | } 41 | if (entities.size() > 1) { 42 | throw EntityArgument.ERROR_NOT_SINGLE_ENTITY.create(); 43 | } 44 | return entities.getFirst(); 45 | } 46 | 47 | public List getEntities(CommandSourceStack source) { 48 | Vec3 origin = source.getPosition(); 49 | origin = new Vec3(originX == null ? origin.x : originX, originY == null ? origin.y : originY, originZ == null ? origin.z : originZ); 50 | 51 | if (senderOnly) { 52 | return filter.test(origin, source.getEntity()) ? Collections.singletonList(source.getEntity()) : Collections.emptyList(); 53 | } 54 | 55 | List entities = new ArrayList<>(); 56 | for (Entity entity : Minecraft.getInstance().level.entitiesForRendering()) { 57 | if (filter.test(origin, entity)) { 58 | entities.add(entity); 59 | } 60 | } 61 | 62 | sorter.accept(origin, entities); 63 | 64 | return entities.size() <= limit ? entities : entities.subList(0, limit); 65 | } 66 | 67 | public int getLimit() { 68 | return limit; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/CrackRNGCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 6 | import net.earthcomputer.clientcommands.Configs; 7 | import net.earthcomputer.clientcommands.features.ServerBrandManager; 8 | import net.earthcomputer.clientcommands.features.CCrackRng; 9 | import net.earthcomputer.clientcommands.features.PlayerRandCracker; 10 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 11 | import net.minecraft.network.chat.Component; 12 | 13 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 14 | 15 | public class CrackRNGCommand { 16 | 17 | public static void register(CommandDispatcher dispatcher) { 18 | dispatcher.register(literal("ccrackrng") 19 | .executes(ctx -> crackPlayerRNG(ctx.getSource()))); 20 | } 21 | 22 | private static int crackPlayerRNG(FabricClientCommandSource source) throws CommandSyntaxException { 23 | ServerBrandManager.rngWarning(); 24 | CCrackRng.crack(seed -> { 25 | source.sendFeedback(Component.translatable("commands.ccrackrng.success", Long.toHexString(seed))); 26 | PlayerRandCracker.setSeed(seed); 27 | Configs.playerCrackState = PlayerRandCracker.CrackState.CRACKED; 28 | }); 29 | return Command.SINGLE_SUCCESS; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/FakeCommandSource.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import net.minecraft.client.Minecraft; 4 | import net.minecraft.client.player.LocalPlayer; 5 | import net.minecraft.commands.CommandSource; 6 | import net.minecraft.commands.CommandSourceStack; 7 | import net.minecraft.core.RegistryAccess; 8 | import net.minecraft.network.chat.Component; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import java.util.Collection; 12 | import java.util.stream.Collectors; 13 | 14 | public class FakeCommandSource extends CommandSourceStack { 15 | public FakeCommandSource(LocalPlayer player) { 16 | super(new CommandSource() { 17 | @Override 18 | public void sendSystemMessage(Component component) { 19 | ClientCommandHelper.sendFeedback(component); 20 | } 21 | 22 | @Override 23 | public boolean acceptsSuccess() { 24 | return true; 25 | } 26 | 27 | @Override 28 | public boolean acceptsFailure() { 29 | return true; 30 | } 31 | 32 | @Override 33 | public boolean shouldInformAdmins() { 34 | return true; 35 | } 36 | }, player.position(), player.getRotationVector(), null, 314159265, player.getScoreboardName(), player.getName(), null, player); 37 | } 38 | 39 | @NotNull 40 | @Override 41 | public Collection getOnlinePlayerNames() { 42 | return Minecraft.getInstance().getConnection().getOnlinePlayers() 43 | .stream().map(e -> e.getProfile().getName()).collect(Collectors.toList()); 44 | } 45 | 46 | @NotNull 47 | @Override 48 | public RegistryAccess registryAccess() { 49 | return Minecraft.getInstance().getConnection().registryAccess(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/FovCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 6 | import net.minecraft.network.chat.Component; 7 | 8 | import static com.mojang.brigadier.arguments.IntegerArgumentType.*; 9 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 10 | 11 | public class FovCommand { 12 | 13 | public static void register(CommandDispatcher dispatcher) { 14 | dispatcher.register(literal("cfov") 15 | .then(argument("fov", integer(0, 360)) 16 | .executes(ctx -> setFov(ctx.getSource(), getInteger(ctx, "fov")))) 17 | .then(literal("normal") 18 | .executes(ctx -> setFov(ctx.getSource(), 70))) 19 | .then(literal("quakePro") 20 | .executes(ctx -> setFov(ctx.getSource(), 110)))); 21 | } 22 | 23 | private static int setFov(FabricClientCommandSource source, int fov) { 24 | source.getClient().options.fov().value = fov; 25 | 26 | Component feedback = Component.translatable("commands.cfov.success", fov); 27 | source.sendFeedback(feedback); 28 | 29 | return Command.SINGLE_SUCCESS; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/GammaCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 6 | import net.minecraft.network.chat.Component; 7 | 8 | import static com.mojang.brigadier.arguments.DoubleArgumentType.*; 9 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 10 | 11 | public class GammaCommand { 12 | 13 | public static void register(CommandDispatcher dispatcher) { 14 | dispatcher.register(literal("cgamma") 15 | .then(argument("gamma", doubleArg()) 16 | .executes(ctx -> setGamma(ctx.getSource(), getDouble(ctx, "gamma"))))); 17 | } 18 | 19 | private static int setGamma(FabricClientCommandSource source, double gamma) { 20 | source.getClient().options.gamma().value = gamma; 21 | 22 | Component feedback = Component.translatable("commands.cgamma.success", gamma); 23 | source.sendFeedback(feedback); 24 | 25 | return Command.SINGLE_SUCCESS; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/NoteCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 6 | import net.minecraft.network.chat.MutableComponent; 7 | 8 | import static net.earthcomputer.clientcommands.command.arguments.ExtendedMarkdownArgument.*; 9 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 10 | 11 | public class NoteCommand { 12 | 13 | public static void register(CommandDispatcher dispatcher) { 14 | dispatcher.register(literal("cnote") 15 | .then(argument("message", extendedMarkdown()) 16 | .executes(ctx -> note(ctx.getSource(), getExtendedMarkdown(ctx, "message"))))); 17 | } 18 | 19 | private static int note(FabricClientCommandSource source, MutableComponent message) { 20 | source.getClient().gui.getChat().addMessage(message); 21 | return Command.SINGLE_SUCCESS; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/PermissionLevelCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.CommandDispatcher; 4 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 5 | import net.minecraft.network.chat.Component; 6 | 7 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 8 | 9 | public class PermissionLevelCommand { 10 | 11 | public static void register(CommandDispatcher dispatcher) { 12 | dispatcher.register(literal("cpermissionlevel") 13 | .executes(ctx -> getPermissionLevel(ctx.getSource()))); 14 | } 15 | 16 | private static int getPermissionLevel(FabricClientCommandSource source) { 17 | int permissionLevel = source.getPlayer().getPermissionLevel(); 18 | source.sendFeedback(Component.translatable("commands.cpermissionlevel.success", permissionLevel)); 19 | 20 | return permissionLevel; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/PlayerInfoCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.CommandDispatcher; 4 | import com.mojang.brigadier.tree.LiteralCommandNode; 5 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 6 | 7 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 8 | 9 | // Might be implemented later 10 | public class PlayerInfoCommand { 11 | public static void register(CommandDispatcher dispatcher) { 12 | LiteralCommandNode cplayerinfo = dispatcher.register(literal("cplayerinfo")); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/PluginsCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import com.mojang.brigadier.suggestion.Suggestion; 6 | import com.mojang.brigadier.tree.CommandNode; 7 | import net.earthcomputer.clientcommands.features.SuggestionsHook; 8 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 9 | import net.minecraft.network.chat.Component; 10 | 11 | import java.util.stream.Collectors; 12 | 13 | import static com.mojang.brigadier.arguments.StringArgumentType.*; 14 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 15 | 16 | public class PluginsCommand { 17 | public static void register(CommandDispatcher dispatcher) { 18 | dispatcher.register(literal("cplugins") 19 | .executes(ctx -> getPlugins(ctx.getSource())) 20 | .then(literal("partial") 21 | .then(argument("partial", greedyString()) 22 | .executes(ctx -> getPlugins(ctx.getSource(), getString(ctx, "partial"))))) 23 | .then(literal("dispatcher") 24 | .executes(ctx -> getPluginsFromDispatcher(ctx.getSource())))); 25 | } 26 | 27 | private static int getPlugins(FabricClientCommandSource source) { 28 | return getPlugins(source, ""); 29 | } 30 | 31 | private static int getPlugins(FabricClientCommandSource source, String partial) { 32 | SuggestionsHook.request(partial).whenComplete((suggestions, throwable) -> { 33 | String plugins = suggestions.getList().stream() 34 | .map(Suggestion::getText) 35 | .filter(text -> text.contains(":")) 36 | .map(text -> text.substring(0, text.indexOf(":"))) 37 | .distinct() 38 | .collect(Collectors.joining(", ")); 39 | 40 | source.sendFeedback(Component.translatable("commands.cplugins.found")); 41 | source.sendFeedback(Component.literal(plugins)); 42 | }); 43 | return Command.SINGLE_SUCCESS; 44 | } 45 | 46 | private static int getPluginsFromDispatcher(FabricClientCommandSource source) { 47 | String plugins = source.getClient().getConnection().getCommands().getRoot().getChildren().stream() 48 | .map(CommandNode::getName) 49 | .filter(name -> name.contains(":")) 50 | .map(name -> name.substring(0, name.indexOf(":"))) 51 | .distinct() 52 | .collect(Collectors.joining(", ")); 53 | 54 | source.sendFeedback(Component.translatable("commands.cplugins.found")); 55 | source.sendFeedback(Component.literal(plugins)); 56 | return Command.SINGLE_SUCCESS; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/RelogCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 6 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 7 | import net.earthcomputer.clientcommands.features.Relogger; 8 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 9 | import net.minecraft.network.chat.Component; 10 | 11 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 12 | 13 | public class RelogCommand { 14 | private static final SimpleCommandExceptionType FAILED_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.crelog.failed")); 15 | 16 | public static void register(CommandDispatcher dispatcher) { 17 | dispatcher.register(literal("crelog") 18 | .executes(ctx -> relog())); 19 | } 20 | 21 | private static int relog() throws CommandSyntaxException { 22 | if (!Relogger.relog()) { 23 | throw FAILED_EXCEPTION.create(); 24 | } 25 | return Command.SINGLE_SUCCESS; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/RenderCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import dev.xpple.clientarguments.arguments.CEntitySelector; 6 | import net.earthcomputer.clientcommands.features.RenderSettings; 7 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 8 | import net.minecraft.network.chat.Component; 9 | 10 | import static dev.xpple.clientarguments.arguments.CEntityArgument.*; 11 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 12 | 13 | public class RenderCommand { 14 | 15 | public static void register(CommandDispatcher dispatcher) { 16 | dispatcher.register(literal("crender") 17 | .then(literal("enable") 18 | .then(literal("entities") 19 | .then(argument("filter", entities()) 20 | .executes(ctx -> enableEntityRendering(ctx.getSource(), ctx.getArgument("filter", CEntitySelector.class), true))))) 21 | .then(literal("disable") 22 | .then(literal("entities") 23 | .then(argument("filter", entities()) 24 | .executes(ctx -> enableEntityRendering(ctx.getSource(), ctx.getArgument("filter", CEntitySelector.class), false)))))); 25 | } 26 | 27 | private static int enableEntityRendering(FabricClientCommandSource source, CEntitySelector selector, boolean enable) { 28 | RenderSettings.addEntityRenderSelector(selector, enable); 29 | source.sendFeedback(Component.translatable("commands.crender.entities.success")); 30 | return Command.SINGLE_SUCCESS; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/ShrugCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 6 | import net.minecraft.client.Minecraft; 7 | import net.minecraft.client.multiplayer.ClientPacketListener; 8 | 9 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 10 | 11 | public class ShrugCommand { 12 | 13 | public static void register(CommandDispatcher dispatcher) { 14 | dispatcher.register(literal("cshrug") 15 | .executes(ctx -> shrug(ctx.getSource()))); 16 | } 17 | 18 | private static int shrug(FabricClientCommandSource source) { 19 | ClientPacketListener packetListener = Minecraft.getInstance().getConnection(); 20 | if (packetListener != null) { 21 | packetListener.sendChat("¯\\_(ツ)_/¯"); 22 | } 23 | return Command.SINGLE_SUCCESS; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/SnapCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 6 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 7 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 8 | import net.minecraft.client.player.LocalPlayer; 9 | import net.minecraft.network.chat.Component; 10 | import net.minecraft.world.phys.Vec3; 11 | 12 | import static dev.xpple.clientarguments.arguments.CVec3Argument.*; 13 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 14 | 15 | public class SnapCommand { 16 | public static boolean clickToTeleport = false; 17 | 18 | private static final SimpleCommandExceptionType TOO_FAR_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.csnap.tooFar")); 19 | private static final SimpleCommandExceptionType CANNOT_FIT_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.csnap.cannotFit")); 20 | 21 | public static void register(CommandDispatcher dispatcher) { 22 | dispatcher.register(literal("csnap") 23 | .executes(ctx -> snap(ctx.getSource())) 24 | .then(argument("pos", vec3()) 25 | .executes(ctx -> snap(ctx.getSource(), getVec3(ctx, "pos"))))); 26 | } 27 | 28 | private static int snap(FabricClientCommandSource source) { 29 | clickToTeleport = !clickToTeleport; 30 | if (clickToTeleport) { 31 | source.sendFeedback(Component.translatable("commands.csnap.clickToTeleportEnabled")); 32 | } else { 33 | source.sendFeedback(Component.translatable("commands.csnap.clickToTeleportDisabled")); 34 | } 35 | return Command.SINGLE_SUCCESS; 36 | } 37 | 38 | private static int snap(FabricClientCommandSource source, Vec3 pos) throws CommandSyntaxException { 39 | if (source.getPosition().distanceToSqr(pos) > 1) { 40 | throw TOO_FAR_EXCEPTION.create(); 41 | } 42 | if (!canStay(source.getPlayer(), pos)) { 43 | throw CANNOT_FIT_EXCEPTION.create(); 44 | } 45 | source.getPlayer().setPos(pos); 46 | source.sendFeedback(Component.translatable("commands.csnap.success", pos.x, pos.y, pos.z)); 47 | return Command.SINGLE_SUCCESS; 48 | } 49 | 50 | public static boolean canStay(LocalPlayer player, Vec3 pos) { 51 | return player.level().noBlockCollision(player, player.getDimensions(player.getPose()).makeBoundingBox(pos).deflate(1e-7)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/TooltipCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.CommandDispatcher; 4 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 5 | import net.minecraft.commands.CommandBuildContext; 6 | import net.minecraft.network.chat.Component; 7 | import net.minecraft.world.item.Item; 8 | import net.minecraft.world.item.ItemStack; 9 | import net.minecraft.world.item.TooltipFlag; 10 | 11 | import java.util.List; 12 | 13 | import static dev.xpple.clientarguments.arguments.CItemArgument.*; 14 | import static net.earthcomputer.clientcommands.command.ClientCommandHelper.*; 15 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 16 | 17 | public class TooltipCommand { 18 | 19 | private static final Flag FLAG_ADVANCED = Flag.ofFlag("advanced").build(); 20 | 21 | public static void register(CommandDispatcher dispatcher, CommandBuildContext context) { 22 | var ctooltip = dispatcher.register(literal("ctooltip") 23 | .then(literal("held") 24 | .executes(ctx -> showTooltip(ctx.getSource(), ctx.getSource().getPlayer().getMainHandItem(), "held"))) 25 | .then(literal("stack") 26 | .then(argument("stack", itemStack(context)) 27 | .executes(ctx -> showTooltip(ctx.getSource(), getItemStackArgument(ctx, "stack").createItemStack(1, false), "stack"))))); 28 | FLAG_ADVANCED.addToCommand(dispatcher, ctooltip, ctx -> true); 29 | } 30 | 31 | private static int showTooltip(FabricClientCommandSource source, ItemStack stack, String type) { 32 | source.sendFeedback(Component.translatable("commands.ctooltip.header." + type)); 33 | 34 | TooltipFlag flag = getFlag(source, FLAG_ADVANCED) ? TooltipFlag.ADVANCED : TooltipFlag.NORMAL; 35 | 36 | List tooltip = stack.getTooltipLines(Item.TooltipContext.of(source.getWorld()), source.getPlayer(), flag); 37 | for (Component line : tooltip) { 38 | source.sendFeedback(line); 39 | } 40 | 41 | return tooltip.size(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/UuidCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 6 | import net.minecraft.client.multiplayer.ClientPacketListener; 7 | import net.minecraft.client.multiplayer.PlayerInfo; 8 | import net.minecraft.network.chat.ClickEvent; 9 | import net.minecraft.network.chat.Component; 10 | import net.minecraft.network.chat.HoverEvent; 11 | 12 | import java.util.UUID; 13 | 14 | import static net.earthcomputer.clientcommands.command.arguments.EntityUUIDArgument.*; 15 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 16 | 17 | public class UuidCommand { 18 | 19 | public static void register(CommandDispatcher dispatcher) { 20 | dispatcher.register(literal("cuuid") 21 | .then(argument("entity", entityUuid()) 22 | .executes(ctx -> getUuid(ctx.getSource(), getEntityUuid(ctx, "entity"))))); 23 | } 24 | 25 | private static int getUuid(FabricClientCommandSource source, UUID entity) { 26 | String uuid = entity.toString(); 27 | Component uuidComponent = Component.literal(uuid).withStyle(style -> style 28 | .withUnderlined(true) 29 | .withHoverEvent(new HoverEvent.ShowText(Component.translatable("chat.copy.click"))) 30 | .withClickEvent(new ClickEvent.CopyToClipboard(uuid)) 31 | ); 32 | 33 | ClientPacketListener packetListener = source.getClient().getConnection(); 34 | assert packetListener != null; 35 | 36 | PlayerInfo player = packetListener.getPlayerInfo(entity); 37 | if (player == null) { 38 | source.sendFeedback(Component.translatable("commands.cuuid.success.nameless", uuidComponent)); 39 | return Command.SINGLE_SUCCESS; 40 | } 41 | String name = player.getProfile().getName(); 42 | source.sendFeedback(Component.translatable("commands.cuuid.success", name, uuidComponent)); 43 | return Command.SINGLE_SUCCESS; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/WeatherCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import net.earthcomputer.clientcommands.features.ClientWeather; 6 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 7 | import net.minecraft.network.chat.Component; 8 | 9 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 10 | 11 | public class WeatherCommand { 12 | 13 | public static void register(CommandDispatcher dispatcher) { 14 | dispatcher.register(literal("cweather") 15 | .then(literal("clear").executes(ctx -> executeWeatherClear(ctx.getSource()))) 16 | .then(literal("rain").executes(ctx -> executeWeatherRain(ctx.getSource()))) 17 | .then(literal("thunder").executes(ctx -> executeWeatherThunder(ctx.getSource()))) 18 | .then(literal("reset").executes(ctx -> executeWeatherReset(ctx.getSource()))) 19 | ); 20 | } 21 | 22 | private static int executeWeatherClear(FabricClientCommandSource source) { 23 | ClientWeather.setRain(0); 24 | ClientWeather.setThunder(0); 25 | Component feedback = Component.translatable("commands.weather.set.clear"); 26 | source.sendFeedback(feedback); 27 | return Command.SINGLE_SUCCESS; 28 | } 29 | 30 | private static int executeWeatherRain(FabricClientCommandSource source) { 31 | ClientWeather.setRain(1); 32 | ClientWeather.setThunder(0); 33 | Component feedback = Component.translatable("commands.weather.set.rain"); 34 | source.sendFeedback(feedback); 35 | return Command.SINGLE_SUCCESS; 36 | } 37 | 38 | private static int executeWeatherThunder(FabricClientCommandSource source) { 39 | ClientWeather.setRain(1); 40 | ClientWeather.setThunder(1); 41 | Component feedback = Component.translatable("commands.weather.set.thunder"); 42 | source.sendFeedback(feedback); 43 | return Command.SINGLE_SUCCESS; 44 | } 45 | 46 | private static int executeWeatherReset(FabricClientCommandSource source) { 47 | ClientWeather.setRain(-1); 48 | ClientWeather.setThunder(-1); 49 | Component feedback = Component.translatable("commands.cweather.reset"); 50 | source.sendFeedback(feedback); 51 | return Command.SINGLE_SUCCESS; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/WikiCommand.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.command; 2 | 3 | import com.mojang.brigadier.CommandDispatcher; 4 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 5 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 6 | import net.earthcomputer.clientcommands.features.WikiRetriever; 7 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 8 | import net.minecraft.network.chat.Component; 9 | 10 | import static com.mojang.brigadier.arguments.StringArgumentType.*; 11 | import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; 12 | 13 | public class WikiCommand { 14 | private static final SimpleCommandExceptionType FAILED_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.cwiki.failed")); 15 | 16 | public static void register(CommandDispatcher dispatcher) { 17 | dispatcher.register(literal("cwiki") 18 | .then(argument("page", greedyString()) 19 | .executes(ctx -> displayWikiPage(ctx.getSource(), getString(ctx, "page"))))); 20 | } 21 | 22 | private static int displayWikiPage(FabricClientCommandSource source, String page) throws CommandSyntaxException { 23 | String content = WikiRetriever.getWikiSummary(page); 24 | 25 | if (content == null) { 26 | throw FAILED_EXCEPTION.create(); 27 | } 28 | 29 | content = content.trim(); 30 | for (String line : content.split("\n")) { 31 | source.sendFeedback(Component.literal(line)); 32 | } 33 | 34 | return content.length(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/command/arguments/WithStringArgument.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.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 | 10 | import java.util.Collection; 11 | import java.util.concurrent.CompletableFuture; 12 | 13 | public class WithStringArgument implements ArgumentType> { 14 | 15 | private final ArgumentType delegate; 16 | 17 | private WithStringArgument(ArgumentType delegate) { 18 | this.delegate = delegate; 19 | } 20 | 21 | public static WithStringArgument withString(ArgumentType delegate) { 22 | return new WithStringArgument<>(delegate); 23 | } 24 | 25 | @SuppressWarnings("unchecked") 26 | public static Result getWithString(CommandContext context, String arg, Class type) { 27 | return context.getArgument(arg, Result.class); 28 | } 29 | 30 | @Override 31 | public Result parse(StringReader reader) throws CommandSyntaxException { 32 | int start = reader.getCursor(); 33 | T thing = delegate.parse(reader); 34 | String str = reader.getString().substring(start, reader.getCursor()); 35 | return new Result<>(str, thing); 36 | } 37 | 38 | @Override 39 | public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { 40 | return delegate.listSuggestions(context, builder); 41 | } 42 | 43 | @Override 44 | public Collection getExamples() { 45 | return delegate.getExamples(); 46 | } 47 | 48 | public record Result(String string, T value) {} 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/event/ClientConnectionEvents.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.event; 2 | 3 | import net.fabricmc.fabric.api.event.Event; 4 | import net.fabricmc.fabric.api.event.EventFactory; 5 | 6 | public final class ClientConnectionEvents { 7 | public static final Event DISCONNECT = EventFactory.createArrayBacked(Disconnect.class, listeners -> () -> { 8 | for (Disconnect listener : listeners) { 9 | listener.onDisconnect(); 10 | } 11 | }); 12 | 13 | @FunctionalInterface 14 | public interface Disconnect { 15 | void onDisconnect(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/event/ClientLevelEvents.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.event; 2 | 3 | import net.fabricmc.fabric.api.event.Event; 4 | import net.fabricmc.fabric.api.event.EventFactory; 5 | import net.minecraft.client.multiplayer.ClientLevel; 6 | import net.minecraft.core.BlockPos; 7 | import net.minecraft.world.level.ChunkPos; 8 | import net.minecraft.world.level.block.state.BlockState; 9 | import net.minecraft.world.level.chunk.LevelChunk; 10 | 11 | public final class ClientLevelEvents { 12 | public static final Event LOAD_LEVEL = EventFactory.createArrayBacked(LoadLevel.class, listeners -> level -> { 13 | for (LoadLevel listener : listeners) { 14 | listener.onLoadLevel(level); 15 | } 16 | }); 17 | 18 | public static final Event UNLOAD_LEVEL = EventFactory.createArrayBacked(UnloadLevel.class, listeners -> isDisconnect -> { 19 | for (UnloadLevel listener : listeners) { 20 | listener.onUnloadLevel(isDisconnect); 21 | } 22 | }); 23 | 24 | public static final Event CHUNK_UPDATE = EventFactory.createArrayBacked(ChunkUpdate.class, listeners -> (level, pos, oldState, newState) -> { 25 | for (ChunkUpdate listener : listeners) { 26 | listener.onBlockStateUpdate(level, pos, oldState, newState); 27 | } 28 | }); 29 | 30 | public static final Event LOAD_CHUNK = EventFactory.createArrayBacked(LoadChunk.class, listeners -> (level, pos) -> { 31 | for (LoadChunk listener : listeners) { 32 | listener.onLoadChunk(level, pos); 33 | } 34 | }); 35 | 36 | public static final Event UNLOAD_CHUNK = EventFactory.createArrayBacked(UnloadChunk.class, listeners -> (level, pos) -> { 37 | for (UnloadChunk listener : listeners) { 38 | listener.onUnloadChunk(level, pos); 39 | } 40 | }); 41 | 42 | @FunctionalInterface 43 | public interface LoadLevel { 44 | void onLoadLevel(ClientLevel level); 45 | } 46 | 47 | @FunctionalInterface 48 | public interface UnloadLevel { 49 | void onUnloadLevel(boolean isDisconnect); 50 | } 51 | 52 | @FunctionalInterface 53 | public interface ChunkUpdate { 54 | void onBlockStateUpdate(ClientLevel level, BlockPos pos, BlockState oldState, BlockState newState); 55 | } 56 | 57 | @FunctionalInterface 58 | public interface LoadChunk { 59 | void onLoadChunk(ClientLevel level, ChunkPos chunkPos); 60 | } 61 | 62 | @FunctionalInterface 63 | public interface UnloadChunk { 64 | void onUnloadChunk(ClientLevel level, ChunkPos chunkPos); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/event/MoreClientEntityEvents.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.event; 2 | 3 | import net.fabricmc.fabric.api.event.Event; 4 | import net.fabricmc.fabric.api.event.EventFactory; 5 | import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; 6 | import net.minecraft.world.entity.ExperienceOrb; 7 | 8 | public final class MoreClientEntityEvents { 9 | /** 10 | * Called twice, first on the network thread and then on the main thread 11 | */ 12 | public static final Event PRE_ADD_MAYBE_ON_NETWORK_THREAD = EventFactory.createArrayBacked(AddEntity.class, listeners -> packet -> { 13 | for (AddEntity listener : listeners) { 14 | listener.onAddEntity(packet); 15 | } 16 | }); 17 | 18 | public static final Event POST_ADD = EventFactory.createArrayBacked(AddEntity.class, listeners -> packet -> { 19 | for (AddEntity listener : listeners) { 20 | listener.onAddEntity(packet); 21 | } 22 | }); 23 | 24 | public static final Event POST_SET_INITIAL_XP_ORB_VALUE = EventFactory.createArrayBacked(SetInitialXpOrbValue.class, listeners -> orb -> { 25 | for (SetInitialXpOrbValue listener : listeners) { 26 | listener.onSetInitialXpOrbValue(orb); 27 | } 28 | }); 29 | 30 | @FunctionalInterface 31 | public interface AddEntity { 32 | void onAddEntity(ClientboundAddEntityPacket packet); 33 | } 34 | 35 | @FunctionalInterface 36 | public interface SetInitialXpOrbValue { 37 | void onSetInitialXpOrbValue(ExperienceOrb orb); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/event/MoreClientEvents.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.event; 2 | 3 | import net.fabricmc.fabric.api.event.Event; 4 | import net.fabricmc.fabric.api.event.EventFactory; 5 | import net.minecraft.network.protocol.game.ClientboundSetTimePacket; 6 | 7 | public final class MoreClientEvents { 8 | public static final Event TIME_SYNC_ON_NETWORK_THREAD = EventFactory.createArrayBacked(TimeSync.class, listeners -> packet -> { 9 | for (TimeSync listener : listeners) { 10 | listener.onTimeSync(packet); 11 | } 12 | }); 13 | public static final Event TIME_SYNC = EventFactory.createArrayBacked(TimeSync.class, listeners -> packet -> { 14 | for (TimeSync listener : listeners) { 15 | listener.onTimeSync(packet); 16 | } 17 | }); 18 | 19 | @FunctionalInterface 20 | public interface TimeSync { 21 | void onTimeSync(ClientboundSetTimePacket packet); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/event/MoreScreenEvents.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.event; 2 | 3 | import net.fabricmc.fabric.api.event.Event; 4 | import net.fabricmc.fabric.api.event.EventFactory; 5 | import net.minecraft.client.gui.screens.Screen; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | public final class MoreScreenEvents { 9 | public static final Event BEFORE_ADD = EventFactory.createArrayBacked(BeforeAdd.class, listeners -> screen -> { 10 | boolean accept = true; 11 | for (BeforeAdd listener : listeners) { 12 | accept &= listener.beforeScreenAdd(screen); 13 | } 14 | return accept; 15 | }); 16 | 17 | @FunctionalInterface 18 | public interface BeforeAdd { 19 | boolean beforeScreenAdd(@Nullable Screen screen); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/features/ChatLengthExtender.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.features; 2 | 3 | import net.earthcomputer.clientcommands.ClientCommands; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | public class ChatLengthExtender { 7 | public static final int EXTENDED_LENGTH = 32767; 8 | @Nullable 9 | public static Integer currentLengthExtension = null; 10 | 11 | public static boolean isClientcommandsCommand(String command) { 12 | if (command.startsWith("/")) { 13 | String[] commandArgs = command.substring(1).split(" "); 14 | return commandArgs.length > 0 && ClientCommands.isClientcommandsCommand(commandArgs[0]); 15 | } 16 | 17 | return false; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/features/ClientTimeModifier.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.features; 2 | 3 | import java.util.function.Function; 4 | 5 | public class ClientTimeModifier { 6 | 7 | public enum Type { 8 | NONE(time -> time), 9 | OFFSET(time -> time + value), 10 | LOCK(time -> value); 11 | 12 | private final Function timeMappingFunction; 13 | 14 | Type(Function timeMappingFunction) { 15 | this.timeMappingFunction = timeMappingFunction; 16 | } 17 | 18 | public long getModifiedTime(long timeOfDay) { 19 | return timeMappingFunction.apply(timeOfDay); 20 | } 21 | } 22 | 23 | private static Type type = Type.NONE; 24 | private static long value = 0; 25 | 26 | public static void none() { 27 | type = Type.NONE; 28 | } 29 | 30 | public static void offset(long time) { 31 | type = Type.OFFSET; 32 | value = time; 33 | } 34 | 35 | public static void lock(long time) { 36 | type = Type.LOCK; 37 | value = time; 38 | } 39 | 40 | public static long getModifiedTime(long timeOfDay) { 41 | return type.getModifiedTime(timeOfDay); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/features/ClientWeather.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.features; 2 | 3 | public class ClientWeather { 4 | 5 | private static float rain = -1; 6 | private static float thunder = -1; 7 | 8 | public static float getRain() { 9 | return rain; 10 | } 11 | 12 | public static void setRain(float rain) { 13 | ClientWeather.rain = rain; 14 | } 15 | 16 | public static float getThunder() { 17 | return thunder; 18 | } 19 | 20 | public static void setThunder(float thunder) { 21 | ClientWeather.thunder = thunder; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/features/CommandExecutionCustomPayload.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.features; 2 | 3 | import net.minecraft.network.FriendlyByteBuf; 4 | import net.minecraft.network.codec.StreamCodec; 5 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 6 | import net.minecraft.resources.ResourceLocation; 7 | 8 | public record CommandExecutionCustomPayload(String command) implements CustomPacketPayload { 9 | public static final ResourceLocation ID = ResourceLocation.fromNamespaceAndPath("clientcommands", "command_execution"); 10 | public static final StreamCodec CODEC = CustomPacketPayload.codec(CommandExecutionCustomPayload::write, CommandExecutionCustomPayload::new); 11 | public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(ID); 12 | 13 | private CommandExecutionCustomPayload(FriendlyByteBuf buf) { 14 | this(buf.readUtf()); 15 | } 16 | 17 | private void write(FriendlyByteBuf buf) { 18 | buf.writeUtf(this.command); 19 | } 20 | 21 | @Override 22 | public CustomPacketPayload.Type type() { 23 | return TYPE; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/features/EntityGlowingTicket.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.features; 2 | 3 | public class EntityGlowingTicket { 4 | 5 | private int ticksLeft; 6 | private int color; 7 | 8 | public EntityGlowingTicket(int ticksLeft, int color) { 9 | this.ticksLeft = ticksLeft; 10 | this.color = color; 11 | } 12 | 13 | public boolean tick() { 14 | return ticksLeft-- >= 0; 15 | } 16 | 17 | public int getColor() { 18 | return color; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/features/ServerBrandManager.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.features; 2 | 3 | import net.earthcomputer.clientcommands.event.ClientConnectionEvents; 4 | import net.minecraft.ChatFormatting; 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.network.chat.Component; 7 | 8 | public class ServerBrandManager { 9 | 10 | private static String serverBrand = "vanilla"; 11 | private static boolean hasWarnedRng = false; 12 | 13 | public static void registerEvents() { 14 | ClientConnectionEvents.DISCONNECT.register(ServerBrandManager::onDisconnect); 15 | } 16 | 17 | public static void setServerBrand(String brand) { 18 | serverBrand = brand; 19 | } 20 | 21 | public static String getServerBrand() { 22 | return serverBrand; 23 | } 24 | 25 | public static boolean isVanilla() { 26 | return "vanilla".equals(serverBrand); 27 | } 28 | 29 | private static void onDisconnect() { 30 | if (hasWarnedRng && Relogger.isRelogging) { 31 | Relogger.relogSuccessTasks.add(() -> hasWarnedRng = true); 32 | } 33 | hasWarnedRng = false; 34 | } 35 | 36 | public static void rngWarning() { 37 | if (!isVanilla() && !hasWarnedRng && !Minecraft.getInstance().hasSingleplayerServer()) { 38 | Minecraft.getInstance().gui.getChat().addMessage( 39 | Component.translatable("playerManip.serverBrandWarning").withStyle(ChatFormatting.YELLOW)); 40 | hasWarnedRng = true; 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/features/SuggestionsHook.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.features; 2 | 3 | import com.mojang.brigadier.suggestion.Suggestions; 4 | import it.unimi.dsi.fastutil.ints.Int2ObjectMap; 5 | import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; 6 | import net.earthcomputer.clientcommands.event.ClientConnectionEvents; 7 | import net.minecraft.client.Minecraft; 8 | import net.minecraft.client.multiplayer.ClientPacketListener; 9 | import net.minecraft.network.protocol.game.ClientboundCommandSuggestionsPacket; 10 | import net.minecraft.network.protocol.game.ServerboundCommandSuggestionPacket; 11 | 12 | import java.util.concurrent.CompletableFuture; 13 | 14 | public final class SuggestionsHook { 15 | static { 16 | ClientConnectionEvents.DISCONNECT.register(SuggestionsHook::onDisconnect); 17 | } 18 | 19 | private SuggestionsHook() { 20 | } 21 | 22 | private static final int MAGIC_SUGGESTION_ID = -314159265; 23 | private static int currentSuggestionId = MAGIC_SUGGESTION_ID; 24 | private static final Int2ObjectMap> pendingSuggestions = new Int2ObjectOpenHashMap<>(); 25 | 26 | public static CompletableFuture fence() { 27 | return request("").thenAccept(suggestions -> {}); 28 | } 29 | 30 | public static CompletableFuture request(String command) { 31 | ClientPacketListener connection = Minecraft.getInstance().getConnection(); 32 | if (connection == null) { 33 | return Suggestions.empty(); 34 | } 35 | 36 | currentSuggestionId--; 37 | CompletableFuture future = new CompletableFuture<>(); 38 | pendingSuggestions.put(currentSuggestionId, future); 39 | connection.send(new ServerboundCommandSuggestionPacket(currentSuggestionId, command)); 40 | return future; 41 | } 42 | 43 | public static boolean onCompletions(ClientboundCommandSuggestionsPacket packet) { 44 | CompletableFuture future = pendingSuggestions.remove(packet.id()); 45 | if (future == null) { 46 | return false; 47 | } 48 | 49 | if (pendingSuggestions.isEmpty()) { 50 | currentSuggestionId = MAGIC_SUGGESTION_ID; 51 | } 52 | 53 | future.complete(packet.toSuggestions()); 54 | return true; 55 | } 56 | 57 | private static void onDisconnect() { 58 | for (CompletableFuture future : pendingSuggestions.values()) { 59 | future.complete(Suggestions.empty().join()); 60 | } 61 | pendingSuggestions.clear(); 62 | currentSuggestionId = MAGIC_SUGGESTION_ID; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/interfaces/IClientSuggestionsProvider.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.interfaces; 2 | 3 | import com.mojang.brigadier.suggestion.Suggestion; 4 | import net.earthcomputer.clientcommands.command.Flag; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import java.util.List; 8 | 9 | public interface IClientSuggestionsProvider { 10 | T clientcommands_getFlag(Flag arg); 11 | 12 | IClientSuggestionsProvider clientcommands_withFlag(Flag arg, T value); 13 | 14 | @Nullable 15 | List clientcommands_filterSuggestions(List suggestions); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/interfaces/IClientSuggestionsProvider_Alias.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.interfaces; 2 | 3 | public interface IClientSuggestionsProvider_Alias { 4 | void clientcommands_addSeenAlias(String alias); 5 | 6 | void clientcommands_removeSeenAlias(String alias); 7 | 8 | boolean clientcommands_isAliasSeen(String alias); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/interfaces/ICreativeSlot.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.interfaces; 2 | 3 | // marker interface for creative slots 4 | public interface ICreativeSlot { 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/interfaces/IDroppableInventoryContainer.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.interfaces; 2 | 3 | import net.minecraft.world.Container; 4 | 5 | public interface IDroppableInventoryContainer { 6 | 7 | Container getDroppableInventory(); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/interfaces/IEditBox.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.interfaces; 2 | 3 | public interface IEditBox { 4 | void clientcommands_setClientCommandLengthExtension(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/interfaces/IEntity_Debug.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.interfaces; 2 | 3 | public interface IEntity_Debug { 4 | void clientcommands_tickDebugRandom(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/interfaces/IEntity_Glowable.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.interfaces; 2 | 3 | public interface IEntity_Glowable { 4 | 5 | void clientcommands_addGlowingTicket(int ticks, int color); 6 | 7 | boolean clientcommands_hasGlowingTicket(); 8 | 9 | void clientcommands_tickGlowingTickets(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/interfaces/ILivingEntityRenderState_Glowable.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.interfaces; 2 | 3 | public interface ILivingEntityRenderState_Glowable { 4 | boolean clientcommands_hasGlowingTicket(); 5 | void clientcommands_setHasGlowingTicket(boolean hasGlowingTicket); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/c2c/ClientPacketListenerMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.c2c; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import net.earthcomputer.clientcommands.features.TwoPlayerGame; 5 | import net.minecraft.client.multiplayer.ClientPacketListener; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | import java.util.UUID; 12 | 13 | @Mixin(ClientPacketListener.class) 14 | public class ClientPacketListenerMixin { 15 | @Inject(method = "handlePlayerInfoRemove", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;getPlayerSocialManager()Lnet/minecraft/client/gui/screens/social/PlayerSocialManager;")) 16 | private void onHandlePlayerInfoRemove(CallbackInfo ci, @Local UUID uuid) { 17 | TwoPlayerGame.onPlayerLeave(uuid); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/alias/ClientSuggestionProviderMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.alias; 2 | 3 | import net.earthcomputer.clientcommands.interfaces.IClientSuggestionsProvider_Alias; 4 | import net.minecraft.client.multiplayer.ClientSuggestionProvider; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Unique; 7 | 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | 11 | @Mixin(ClientSuggestionProvider.class) 12 | public class ClientSuggestionProviderMixin implements IClientSuggestionsProvider_Alias { 13 | @Unique 14 | private final Set seenAliases = new HashSet<>(); 15 | 16 | @Override 17 | public void clientcommands_addSeenAlias(String alias) { 18 | seenAliases.add(alias); 19 | } 20 | 21 | @Override 22 | public void clientcommands_removeSeenAlias(String alias) { 23 | seenAliases.remove(alias); 24 | } 25 | 26 | @Override 27 | public boolean clientcommands_isAliasSeen(String alias) { 28 | return seenAliases.contains(alias); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/enchant/EnchantmentScreenMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.enchant; 2 | 3 | import net.earthcomputer.clientcommands.features.EnchantmentCracker; 4 | import net.minecraft.client.Minecraft; 5 | import net.minecraft.client.gui.GuiGraphics; 6 | import net.minecraft.client.gui.components.Button; 7 | import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; 8 | import net.minecraft.client.gui.screens.inventory.EnchantmentScreen; 9 | import net.minecraft.network.chat.Component; 10 | import net.minecraft.world.entity.player.Inventory; 11 | import net.minecraft.world.inventory.EnchantmentMenu; 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 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 17 | 18 | @Mixin(EnchantmentScreen.class) 19 | public abstract class EnchantmentScreenMixin extends AbstractContainerScreen { 20 | 21 | public EnchantmentScreenMixin(EnchantmentMenu container_1, Inventory playerInventory_1, Component text_1) { 22 | super(container_1, playerInventory_1, text_1); 23 | } 24 | 25 | @Inject(method = "render", at = @At("TAIL")) 26 | public void postRender(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks, CallbackInfo ci) { 27 | if (EnchantmentCracker.isEnchantingPredictionEnabled()) { 28 | EnchantmentCracker.drawEnchantmentGUIOverlay(graphics); 29 | } 30 | } 31 | 32 | @Inject(method = "mouseClicked", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/MultiPlayerGameMode;handleInventoryButtonClick(II)V")) 33 | public void onItemEnchanted(double mouseX, double mouseY, int mouseButton, CallbackInfoReturnable ci) { 34 | EnchantmentCracker.onEnchantedItem(); 35 | } 36 | 37 | @Inject(method = "init", at = @At("TAIL")) 38 | private void onInit(CallbackInfo ci) { 39 | if (EnchantmentCracker.isEnchantingPredictionEnabled()) { 40 | addRenderableWidget(Button.builder(Component.translatable("enchCrack.addInfo"), button -> { 41 | EnchantmentCracker.addEnchantmentSeedInfo(Minecraft.getInstance().level, getMenu()); 42 | }).pos(width - 150, 0).build()); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/enchant/MultiPlayerGameModeMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.enchant; 2 | 3 | import net.earthcomputer.clientcommands.features.EnchantmentCracker; 4 | import net.minecraft.client.multiplayer.MultiPlayerGameMode; 5 | import net.minecraft.client.player.LocalPlayer; 6 | import net.minecraft.core.BlockPos; 7 | import net.minecraft.world.InteractionHand; 8 | import net.minecraft.world.InteractionResult; 9 | import net.minecraft.world.level.block.Blocks; 10 | import net.minecraft.world.phys.BlockHitResult; 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.callback.CallbackInfoReturnable; 15 | 16 | @Mixin(MultiPlayerGameMode.class) 17 | public class MultiPlayerGameModeMixin { 18 | @Inject(method = "useItemOn", at = @At("HEAD")) 19 | public void onRightClickBlock(LocalPlayer player, InteractionHand hand, BlockHitResult hitResult, CallbackInfoReturnable ci) { 20 | BlockPos pos = hitResult.getBlockPos(); 21 | if (player.level().getBlockState(pos).getBlock() == Blocks.ENCHANTING_TABLE) { 22 | EnchantmentCracker.enchantingTablePos = pos; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/findblock/ClientLevelMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.findblock; 2 | 3 | import net.earthcomputer.clientcommands.event.ClientLevelEvents; 4 | import net.minecraft.client.multiplayer.ClientLevel; 5 | import net.minecraft.world.level.ChunkPos; 6 | import net.minecraft.world.level.chunk.LevelChunk; 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(ClientLevel.class) 13 | public class ClientLevelMixin { 14 | @Inject(method = "unload", at = @At("HEAD")) 15 | private void onChunkUnload(LevelChunk chunk, CallbackInfo ci) { 16 | ClientLevelEvents.UNLOAD_CHUNK.invoker().onUnloadChunk((ClientLevel) (Object) this, chunk.getPos()); 17 | } 18 | 19 | @Inject(method = "onChunkLoaded", at = @At("HEAD")) 20 | private void onChunkLoad(ChunkPos chunkPos, CallbackInfo ci) { 21 | ClientLevelEvents.LOAD_CHUNK.invoker().onLoadChunk((ClientLevel) (Object) this, chunkPos); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/findblock/LevelChunkMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.findblock; 2 | 3 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 4 | import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 5 | import net.earthcomputer.clientcommands.event.ClientLevelEvents; 6 | import net.minecraft.client.multiplayer.ClientLevel; 7 | import net.minecraft.core.BlockPos; 8 | import net.minecraft.world.level.Level; 9 | import net.minecraft.world.level.block.state.BlockState; 10 | import net.minecraft.world.level.chunk.LevelChunk; 11 | import net.minecraft.world.level.chunk.LevelChunkSection; 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 | 17 | @Mixin(LevelChunk.class) 18 | public class LevelChunkMixin { 19 | @Shadow @Final 20 | Level level; 21 | 22 | @WrapOperation(method = "setBlockState", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/LevelChunkSection;setBlockState(IIILnet/minecraft/world/level/block/state/BlockState;)Lnet/minecraft/world/level/block/state/BlockState;")) 23 | private BlockState onSetBlockState(LevelChunkSection instance, int x, int y, int z, BlockState state, Operation original, BlockPos pos, BlockState redundant, int flags) { 24 | BlockState oldState = original.call(instance, x, y, z, state); 25 | if (level.isClientSide) { 26 | ClientLevelEvents.CHUNK_UPDATE.invoker().onBlockStateUpdate((ClientLevel) level, pos, oldState, state); 27 | } 28 | return oldState; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/fish/FishingHookMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.fish; 2 | 3 | import net.earthcomputer.clientcommands.features.FishingCracker; 4 | import net.minecraft.client.Minecraft; 5 | import net.minecraft.world.entity.Entity; 6 | import net.minecraft.world.entity.EntityType; 7 | import net.minecraft.world.entity.player.Player; 8 | import net.minecraft.world.entity.projectile.FishingHook; 9 | import net.minecraft.world.level.Level; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Shadow; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.Slice; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 16 | 17 | @Mixin(FishingHook.class) 18 | public abstract class FishingHookMixin extends Entity { 19 | @Shadow 20 | public abstract Player getPlayerOwner(); 21 | 22 | public FishingHookMixin(EntityType type, Level world) { 23 | super(type, world); 24 | } 25 | 26 | @Inject(method = "tick", 27 | slice = @Slice(from = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/projectile/FishingHook;outOfWaterTime:I", ordinal = 0)), 28 | at = @At(value = "INVOKE", target = "Ljava/lang/Math;min(II)I", remap = false, ordinal = 0)) 29 | private void onBobOutOfWater(CallbackInfo ci) { 30 | if (FishingCracker.canManipulateFishing() && level().isClientSide && getPlayerOwner() == Minecraft.getInstance().player) { 31 | FishingCracker.onBobOutOfWater(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/fish/FishingRodItemMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.fish; 2 | 3 | import net.earthcomputer.clientcommands.features.FishingCracker; 4 | import net.minecraft.world.InteractionHand; 5 | import net.minecraft.world.InteractionResult; 6 | import net.minecraft.world.entity.player.Player; 7 | import net.minecraft.world.item.FishingRodItem; 8 | import net.minecraft.world.level.Level; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 13 | 14 | @Mixin(FishingRodItem.class) 15 | public class FishingRodItemMixin { 16 | 17 | @Inject(method = "use", at = @At(value = "FIELD", target = "Lnet/minecraft/sounds/SoundEvents;FISHING_BOBBER_RETRIEVE:Lnet/minecraft/sounds/SoundEvent;")) 18 | public void onRetrieveFishingRod(Level level, Player player, InteractionHand hand, CallbackInfoReturnable ci) { 19 | if (level.isClientSide && FishingCracker.canManipulateFishing()) { 20 | FishingCracker.onRetractedFishingRod(); 21 | } 22 | } 23 | 24 | @Inject(method = "use", at = @At(value = "FIELD", target = "Lnet/minecraft/sounds/SoundEvents;FISHING_BOBBER_THROW:Lnet/minecraft/sounds/SoundEvent;")) 25 | private void onThrowFishingRod(Level level, Player user, InteractionHand hand, CallbackInfoReturnable ci) { 26 | if (level.isClientSide && FishingCracker.canManipulateFishing()) { 27 | FishingCracker.onThrownFishingRod(user.getItemInHand(hand)); 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/fish/ItemEntityMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.fish; 2 | 3 | import net.earthcomputer.clientcommands.features.FishingCracker; 4 | import net.minecraft.world.entity.Entity; 5 | import net.minecraft.world.entity.EntityType; 6 | import net.minecraft.world.entity.item.ItemEntity; 7 | import net.minecraft.world.item.ItemStack; 8 | import net.minecraft.world.level.Level; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Shadow; 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(ItemEntity.class) 16 | public abstract class ItemEntityMixin extends Entity { 17 | 18 | @Shadow public abstract ItemStack getItem(); 19 | 20 | public ItemEntityMixin(EntityType type, Level level) { 21 | super(type, level); 22 | } 23 | 24 | @Inject(method = "onSyncedDataUpdated", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;setEntityRepresentation(Lnet/minecraft/world/entity/Entity;)V")) 25 | private void onStackSet(CallbackInfo ci) { 26 | if (level().isClientSide && FishingCracker.canManipulateFishing()) { 27 | FishingCracker.processItemSpawn(position(), getItem()); 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/fps/FramerateLimitTrackerMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.fps; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import com.mojang.blaze3d.platform.FramerateLimitTracker; 5 | import net.earthcomputer.clientcommands.Configs; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | @Mixin(FramerateLimitTracker.class) 12 | public class FramerateLimitTrackerMixin { 13 | @ModifyExpressionValue(method = "getFramerateLimit", at = @At(value = "FIELD", target = "Lcom/mojang/blaze3d/platform/FramerateLimitTracker;framerateLimit:I")) 14 | private int fixFps(int fps) { 15 | if (Configs.overriddenFps > 0) { 16 | return Configs.overriddenFps; 17 | } 18 | 19 | return fps; 20 | } 21 | 22 | @Inject(method = "setFramerateLimit", at = @At("HEAD")) 23 | private void onSetFramerateLimit(CallbackInfo ci) { 24 | // Avoid overriding the FPS when something else (e.g. the user) has explicitly set it 25 | Configs.overriddenFps = 0; 26 | Configs.save(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/fps/MinecraftMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.fps; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import net.earthcomputer.clientcommands.Configs; 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.client.Options; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | 10 | @Mixin(Minecraft.class) 11 | public class MinecraftMixin { 12 | @ModifyExpressionValue(method = "runTick", at = @At(value = "CONSTANT", args = "intValue=" + Options.UNLIMITED_FRAMERATE_CUTOFF)) 13 | private int fixMaxFps(int fps) { 14 | if (Configs.overriddenFps > 0) { 15 | return Integer.MAX_VALUE; 16 | } 17 | 18 | return fps; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/generic/ChatScreenMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.generic; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyReceiver; 4 | import com.llamalad7.mixinextras.sugar.Local; 5 | import com.llamalad7.mixinextras.sugar.ref.LocalRef; 6 | import net.earthcomputer.clientcommands.ClientCommands; 7 | import net.earthcomputer.clientcommands.Configs; 8 | import net.earthcomputer.clientcommands.command.VarCommand; 9 | import net.minecraft.client.gui.screens.ChatScreen; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | 13 | @Mixin(ChatScreen.class) 14 | public class ChatScreenMixin { 15 | // replace the text before the Fabric Command API executes it, 16 | // but ensure the message is added to the history in its raw form. 17 | @ModifyReceiver(method = "handleChatInput", at = @At(value = "INVOKE", target = "Ljava/lang/String;startsWith(Ljava/lang/String;)Z", remap = false)) 18 | private String onHandleChatInput(String instance, String slash, @Local(argsOnly = true) LocalRef message) { 19 | String prefix = Configs.autoPrefix; 20 | if (prefix.isEmpty() || instance.startsWith("/")) { 21 | prefix = ""; 22 | } else { 23 | prefix = prefix + " "; 24 | } 25 | 26 | String command = VarCommand.replaceVariables(prefix + instance); 27 | if (command.startsWith("/")) { 28 | ClientCommands.sendCommandExecutionToServer(command.substring(1)); 29 | } 30 | 31 | message.set(command); 32 | return command; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/generic/ClientSuggestionsProviderMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.generic; 2 | 3 | import com.google.common.collect.ImmutableMap; 4 | import com.mojang.brigadier.suggestion.Suggestion; 5 | import net.earthcomputer.clientcommands.command.Flag; 6 | import net.earthcomputer.clientcommands.interfaces.IClientSuggestionsProvider; 7 | import net.minecraft.client.Minecraft; 8 | import net.minecraft.client.multiplayer.ClientPacketListener; 9 | import net.minecraft.client.multiplayer.ClientSuggestionProvider; 10 | import org.jetbrains.annotations.Nullable; 11 | import org.spongepowered.asm.mixin.Final; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.Shadow; 14 | import org.spongepowered.asm.mixin.Unique; 15 | 16 | import java.util.List; 17 | 18 | @Mixin(ClientSuggestionProvider.class) 19 | public class ClientSuggestionsProviderMixin implements IClientSuggestionsProvider { 20 | @Shadow 21 | @Final 22 | private ClientPacketListener connection; 23 | @Shadow 24 | @Final 25 | private Minecraft minecraft; 26 | 27 | @Unique 28 | private ImmutableMap, Object> flags = ImmutableMap.of(); 29 | 30 | @SuppressWarnings("unchecked") 31 | @Override 32 | public T clientcommands_getFlag(Flag flag) { 33 | return (T) this.flags.getOrDefault(flag, flag.getDefaultValue()); 34 | } 35 | 36 | @Override 37 | public IClientSuggestionsProvider clientcommands_withFlag(Flag flag, T value) { 38 | ClientSuggestionsProviderMixin source = (ClientSuggestionsProviderMixin) (Object) new ClientSuggestionProvider(this.connection, this.minecraft); 39 | source.flags = ImmutableMap., Object>builderWithExpectedSize(this.flags.size() + 1).putAll(this.flags).put(flag, value).build(); 40 | return source; 41 | } 42 | 43 | @Override 44 | @Nullable 45 | public List clientcommands_filterSuggestions(List suggestions) { 46 | if (flags.isEmpty()) { 47 | return null; 48 | } else { 49 | return suggestions.stream().filter(suggestion -> { 50 | String text = suggestion.getText(); 51 | return !Flag.isFlag(text) || flags.keySet().stream().noneMatch(arg -> !arg.isRepeatable() && (text.equals(arg.getFlag()) || text.equals(arg.getShortFlag()))); 52 | }).toList(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/generic/CommandSuggestionsMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.generic; 2 | 3 | import com.mojang.brigadier.ParseResults; 4 | import com.mojang.brigadier.suggestion.Suggestion; 5 | import com.mojang.brigadier.suggestion.Suggestions; 6 | import net.earthcomputer.clientcommands.command.Flag; 7 | import net.earthcomputer.clientcommands.interfaces.IClientSuggestionsProvider; 8 | import net.minecraft.client.gui.components.CommandSuggestions; 9 | import net.minecraft.commands.SharedSuggestionProvider; 10 | import org.jetbrains.annotations.Nullable; 11 | import org.objectweb.asm.Opcodes; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.Shadow; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Inject; 16 | import org.spongepowered.asm.mixin.injection.Slice; 17 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 18 | 19 | import java.util.List; 20 | import java.util.concurrent.CompletableFuture; 21 | 22 | @Mixin(CommandSuggestions.class) 23 | public class CommandSuggestionsMixin { 24 | @Shadow private @Nullable ParseResults currentParse; 25 | @Shadow private @Nullable CompletableFuture pendingSuggestions; 26 | 27 | @Inject( 28 | method = "updateCommandInfo", 29 | slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientPacketListener;getCommands()Lcom/mojang/brigadier/CommandDispatcher;")), 30 | at = @At(value = "FIELD", target = "Lnet/minecraft/client/gui/components/CommandSuggestions;pendingSuggestions:Ljava/util/concurrent/CompletableFuture;", opcode = Opcodes.PUTFIELD, shift = At.Shift.AFTER, ordinal = 0) 31 | ) 32 | private void filterSuggestions(CallbackInfo ci) { 33 | assert this.pendingSuggestions != null && this.currentParse != null; 34 | this.pendingSuggestions = this.pendingSuggestions.thenApply(suggestions -> { 35 | SharedSuggestionProvider source = Flag.getActualSource(this.currentParse); 36 | if (source instanceof IClientSuggestionsProvider mySource) { 37 | List newSuggestions = mySource.clientcommands_filterSuggestions(suggestions.getList()); 38 | return newSuggestions == null ? suggestions : new Suggestions(suggestions.getRange(), newSuggestions); 39 | } else { 40 | return suggestions; 41 | } 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/glow/ArmorStandRendererMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.glow; 2 | 3 | import net.earthcomputer.clientcommands.interfaces.ILivingEntityRenderState_Glowable; 4 | import net.minecraft.client.model.ArmorStandArmorModel; 5 | import net.minecraft.client.model.ArmorStandModel; 6 | import net.minecraft.client.renderer.RenderType; 7 | import net.minecraft.client.renderer.entity.ArmorStandRenderer; 8 | import net.minecraft.client.renderer.entity.EntityRendererProvider; 9 | import net.minecraft.client.renderer.entity.LivingEntityRenderer; 10 | import net.minecraft.client.renderer.entity.state.ArmorStandRenderState; 11 | import net.minecraft.world.entity.decoration.ArmorStand; 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.CallbackInfoReturnable; 16 | 17 | @Mixin(ArmorStandRenderer.class) 18 | public abstract class ArmorStandRendererMixin extends LivingEntityRenderer { 19 | public ArmorStandRendererMixin(EntityRendererProvider.Context ctx, ArmorStandModel model, float shadowRadius) { 20 | super(ctx, model, shadowRadius); 21 | } 22 | 23 | @Inject(method = "getRenderType(Lnet/minecraft/client/renderer/entity/state/ArmorStandRenderState;ZZZ)Lnet/minecraft/client/renderer/RenderType;", at = @At("HEAD"), cancellable = true) 24 | private void onGetRenderType(ArmorStandRenderState renderState, boolean visible, boolean translucent, boolean shouldRenderOutline, CallbackInfoReturnable ci) { 25 | if (((ILivingEntityRenderState_Glowable) renderState).clientcommands_hasGlowingTicket()) { 26 | ci.setReturnValue(super.getRenderType(renderState, visible, translucent, shouldRenderOutline)); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/glow/ClientLevelMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.glow; 2 | 3 | import net.earthcomputer.clientcommands.interfaces.IEntity_Glowable; 4 | import net.minecraft.client.multiplayer.ClientLevel; 5 | import net.minecraft.world.entity.Entity; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | @Mixin(ClientLevel.class) 12 | public class ClientLevelMixin { 13 | 14 | @Inject(method = "tickNonPassenger", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;tick()V")) 15 | private void onTickNonPassenger(Entity entity, CallbackInfo ci) { 16 | ((IEntity_Glowable) entity).clientcommands_tickGlowingTickets(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/glow/EntityMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.glow; 2 | 3 | import net.earthcomputer.clientcommands.features.EntityGlowingTicket; 4 | import net.earthcomputer.clientcommands.interfaces.IEntity_Glowable; 5 | import net.minecraft.world.entity.Entity; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Unique; 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 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | import java.util.List; 15 | 16 | @Mixin(Entity.class) 17 | public class EntityMixin implements IEntity_Glowable { 18 | @Unique 19 | private final List glowingTickets = new ArrayList<>(0); 20 | 21 | @Override 22 | public void clientcommands_addGlowingTicket(int ticks, int color) { 23 | glowingTickets.add(new EntityGlowingTicket(ticks, color)); 24 | } 25 | 26 | @Override 27 | public boolean clientcommands_hasGlowingTicket() { 28 | return !glowingTickets.isEmpty(); 29 | } 30 | 31 | @Unique 32 | private int getGlowingTicketColor() { 33 | return glowingTickets.isEmpty() ? 0xffffff : glowingTickets.getLast().getColor(); 34 | } 35 | 36 | @Inject(method = "isCurrentlyGlowing", at = @At("HEAD"), cancellable = true) 37 | private void overrideIsCurrentlyGlowing(CallbackInfoReturnable ci) { 38 | if (!glowingTickets.isEmpty()) { 39 | ci.setReturnValue(Boolean.TRUE); 40 | } 41 | } 42 | 43 | @Override 44 | public void clientcommands_tickGlowingTickets() { 45 | if (((Entity) (Object) this).level().isClientSide) { 46 | Iterator itr = glowingTickets.iterator(); 47 | //noinspection Java8CollectionRemoveIf 48 | while (itr.hasNext()) { 49 | EntityGlowingTicket glowingTicket = itr.next(); 50 | if (!glowingTicket.tick()) { 51 | itr.remove(); 52 | } 53 | } 54 | } 55 | } 56 | 57 | @Inject(method = "getTeamColor", at = @At("HEAD"), cancellable = true) 58 | public void injectGetTeamColor(CallbackInfoReturnable ci) { 59 | if (clientcommands_hasGlowingTicket()) { 60 | ci.setReturnValue(getGlowingTicketColor()); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/glow/LivingEntityRenderStateMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.glow; 2 | 3 | import net.earthcomputer.clientcommands.interfaces.ILivingEntityRenderState_Glowable; 4 | import net.minecraft.client.renderer.entity.state.LivingEntityRenderState; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Unique; 7 | 8 | @Mixin(LivingEntityRenderState.class) 9 | public class LivingEntityRenderStateMixin implements ILivingEntityRenderState_Glowable { 10 | @Unique 11 | private boolean hasGlowingTicket; 12 | 13 | @Override 14 | public boolean clientcommands_hasGlowingTicket() { 15 | return hasGlowingTicket; 16 | } 17 | 18 | @Override 19 | public void clientcommands_setHasGlowingTicket(boolean hasGlowingTicket) { 20 | this.hasGlowingTicket = hasGlowingTicket; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/glow/LivingEntityRendererMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.glow; 2 | 3 | import net.earthcomputer.clientcommands.interfaces.IEntity_Glowable; 4 | import net.earthcomputer.clientcommands.interfaces.ILivingEntityRenderState_Glowable; 5 | import net.minecraft.client.model.EntityModel; 6 | import net.minecraft.client.renderer.RenderType; 7 | import net.minecraft.client.renderer.entity.EntityRenderer; 8 | import net.minecraft.client.renderer.entity.EntityRendererProvider; 9 | import net.minecraft.client.renderer.entity.LivingEntityRenderer; 10 | import net.minecraft.client.renderer.entity.state.LivingEntityRenderState; 11 | import net.minecraft.resources.ResourceLocation; 12 | import net.minecraft.world.entity.LivingEntity; 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 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 19 | 20 | @Mixin(LivingEntityRenderer.class) 21 | public abstract class LivingEntityRendererMixin> extends EntityRenderer { 22 | @Shadow 23 | public abstract ResourceLocation getTextureLocation(S renderState); 24 | 25 | protected LivingEntityRendererMixin(EntityRendererProvider.Context ctx) { 26 | super(ctx); 27 | } 28 | 29 | @Inject(method = "getRenderType", at = @At("RETURN"), cancellable = true) 30 | private void onGetRenderType(S renderState, boolean visible, boolean translucent, boolean showOutline, CallbackInfoReturnable ci) { 31 | if (ci.getReturnValue() == null && ((ILivingEntityRenderState_Glowable) renderState).clientcommands_hasGlowingTicket()) { 32 | ci.setReturnValue(RenderType.outline(getTextureLocation(renderState))); 33 | } 34 | } 35 | 36 | @Inject(method = "extractRenderState(Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/client/renderer/entity/state/LivingEntityRenderState;F)V", at = @At("RETURN")) 37 | private void extractGlowingRenderState(T entity, S renderState, float tickDelta, CallbackInfo ci) { 38 | ((ILivingEntityRenderState_Glowable) renderState).clientcommands_setHasGlowingTicket(((IEntity_Glowable) entity).clientcommands_hasGlowingTicket()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/listen/ConnectionMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.listen; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import net.earthcomputer.clientcommands.command.ListenCommand; 5 | import net.minecraft.network.Connection; 6 | import net.minecraft.network.PacketSendListener; 7 | import net.minecraft.network.protocol.Packet; 8 | import net.minecraft.network.protocol.PacketFlow; 9 | import org.jetbrains.annotations.Nullable; 10 | import org.spongepowered.asm.mixin.Final; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.Shadow; 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 | @Mixin(Connection.class) 18 | public class ConnectionMixin { 19 | @Shadow @Final private PacketFlow receiving; 20 | 21 | @Inject(method = "channelRead0(Lio/netty/channel/ChannelHandlerContext;Lnet/minecraft/network/protocol/Packet;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/Connection;genericsFtw(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketListener;)V")) 22 | private void onPacketReceive(ChannelHandlerContext context, Packet packet, CallbackInfo ci) { 23 | if (this.receiving == PacketFlow.CLIENTBOUND) { 24 | ListenCommand.onPacket(packet, ListenCommand.PacketFlow.CLIENTBOUND); 25 | } 26 | } 27 | 28 | @Inject(method = "doSendPacket", at = @At("HEAD")) 29 | private void onPacketSend(Packet packet, @Nullable PacketSendListener sendListener, boolean flush, CallbackInfo ci) { 30 | if (this.receiving == PacketFlow.CLIENTBOUND) { 31 | ListenCommand.onPacket(packet, ListenCommand.PacketFlow.SERVERBOUND); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/relog/ClientPacketListenerMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.relog; 2 | 3 | import net.earthcomputer.clientcommands.features.ClientCommandFunctions; 4 | import net.earthcomputer.clientcommands.features.Relogger; 5 | import net.minecraft.client.multiplayer.ClientPacketListener; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | @Mixin(value = ClientPacketListener.class, priority = 2000) 12 | public class ClientPacketListenerMixin { 13 | @Inject(method = "handleLogin", at = @At("RETURN")) 14 | private void postLogin(CallbackInfo ci) { 15 | if (!Relogger.onRelogSuccess()) { 16 | ClientCommandFunctions.runStartup(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/render/EntityRendererDispatcherMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.render; 2 | 3 | import net.earthcomputer.clientcommands.features.RenderSettings; 4 | import net.minecraft.client.Camera; 5 | import net.minecraft.client.renderer.culling.Frustum; 6 | import net.minecraft.client.renderer.entity.EntityRenderDispatcher; 7 | import net.minecraft.world.entity.Entity; 8 | import net.minecraft.world.level.Level; 9 | import org.spongepowered.asm.mixin.Mixin; 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 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 14 | 15 | @Mixin(EntityRenderDispatcher.class) 16 | public class EntityRendererDispatcherMixin { 17 | 18 | @Inject(method = "prepare", at = @At("HEAD")) 19 | public void onPrepare(Level level, Camera camera, Entity entity, CallbackInfo ci) { 20 | RenderSettings.preRenderEntities(); 21 | } 22 | 23 | @Inject(method = "shouldRender", at = @At("RETURN"), cancellable = true) 24 | public void redirectShouldRender(Entity entity, Frustum frustum, double x, double y, double z, CallbackInfoReturnable ci) { 25 | if (ci.getReturnValueZ() && !RenderSettings.shouldRenderEntity(entity)) { 26 | ci.setReturnValue(false); 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/reply/ClientPacketListenerMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.reply; 2 | 3 | import net.earthcomputer.clientcommands.command.ReplyCommand; 4 | import net.minecraft.client.multiplayer.ClientPacketListener; 5 | import net.minecraft.client.multiplayer.PlayerInfo; 6 | import net.minecraft.network.chat.ChatType; 7 | import net.minecraft.network.protocol.game.ClientboundPlayerChatPacket; 8 | import org.jetbrains.annotations.Nullable; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Shadow; 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 | import java.util.UUID; 16 | 17 | @Mixin(ClientPacketListener.class) 18 | public abstract class ClientPacketListenerMixin { 19 | @Shadow public abstract @Nullable PlayerInfo getPlayerInfo(UUID uniqueId); 20 | 21 | @Inject(method = "handlePlayerChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/PacketUtils;ensureRunningOnSameThread(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketListener;Lnet/minecraft/util/thread/BlockableEventLoop;)V", shift = At.Shift.AFTER)) 22 | private void onHandlePlayerChat(ClientboundPlayerChatPacket packet, CallbackInfo ci) { 23 | if (packet.chatType().chatType().is(ChatType.MSG_COMMAND_INCOMING) || packet.chatType().chatType().is(ChatType.MSG_COMMAND_OUTGOING)) { 24 | PlayerInfo info = getPlayerInfo(packet.sender()); 25 | if (info != null) { 26 | ReplyCommand.addReplyCandidate(info.getProfile().getName(), System.currentTimeMillis()); 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/time/ClientLevelDataMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.time; 2 | 3 | import net.earthcomputer.clientcommands.features.ClientTimeModifier; 4 | import net.minecraft.client.multiplayer.ClientLevel.ClientLevelData; 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.CallbackInfoReturnable; 9 | 10 | @Mixin(ClientLevelData.class) 11 | public class ClientLevelDataMixin { 12 | 13 | @Inject(at = @At("TAIL"), method = "getDayTime()J", cancellable = true) 14 | public void getTimeOfDay(CallbackInfoReturnable info) { 15 | var timeOfDay = info.getReturnValue(); 16 | long modifiedTimeOfDay = ClientTimeModifier.getModifiedTime(timeOfDay); 17 | info.setReturnValue(modifiedTimeOfDay); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/commands/weather/LevelMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.commands.weather; 2 | 3 | import net.earthcomputer.clientcommands.features.ClientWeather; 4 | import net.minecraft.world.level.Level; 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.CallbackInfoReturnable; 9 | 10 | @Mixin(Level.class) 11 | public class LevelMixin { 12 | 13 | @Inject(method = "getRainLevel(F)F", at = {@At("HEAD")}, cancellable = true) 14 | private void onGetRainLevel(float delta, CallbackInfoReturnable cir) { 15 | float rain = ClientWeather.getRain(); 16 | if (rain > -1) { 17 | cir.setReturnValue(rain); 18 | } 19 | } 20 | 21 | @Inject(method = "getThunderLevel(F)F", at = {@At("HEAD")}, cancellable = true) 22 | private void onGetThunderLevel(float delta, CallbackInfoReturnable cir) { 23 | float thunder = ClientWeather.getThunder(); 24 | if (thunder > -1) { 25 | cir.setReturnValue(thunder); 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/dataqueryhandler/ClientPacketListenerMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.dataqueryhandler; 2 | 3 | import net.earthcomputer.clientcommands.features.ClientcommandsDataQueryHandler; 4 | import net.minecraft.client.multiplayer.ClientPacketListener; 5 | import net.minecraft.network.protocol.game.ClientboundTagQueryPacket; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Unique; 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(ClientPacketListener.class) 13 | public class ClientPacketListenerMixin implements ClientcommandsDataQueryHandler.IClientPlayNetworkHandler { 14 | @Unique 15 | private final ClientcommandsDataQueryHandler ccDataQueryHandler = new ClientcommandsDataQueryHandler((ClientPacketListener) (Object) this); 16 | 17 | @Inject(method = "handleTagQueryPacket", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/PacketUtils;ensureRunningOnSameThread(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketListener;Lnet/minecraft/util/thread/BlockableEventLoop;)V", shift = At.Shift.AFTER), cancellable = true) 18 | private void onHandleTagQueryPacket(ClientboundTagQueryPacket packet, CallbackInfo ci) { 19 | if (ccDataQueryHandler.handleQueryResponse(packet.getTransactionId(), packet.getTag())) { 20 | ci.cancel(); 21 | } 22 | } 23 | 24 | @Override 25 | public ClientcommandsDataQueryHandler clientcommands_getCCDataQueryHandler() { 26 | return ccDataQueryHandler; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/debug/EntityMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.debug; 2 | 3 | import net.earthcomputer.clientcommands.util.DebugRandom; 4 | import net.earthcomputer.clientcommands.interfaces.IEntity_Debug; 5 | import net.minecraft.util.RandomSource; 6 | import net.minecraft.world.entity.Entity; 7 | import net.minecraft.world.entity.EntityType; 8 | import net.minecraft.world.entity.player.Player; 9 | import net.minecraft.world.level.Level; 10 | import org.objectweb.asm.Opcodes; 11 | import org.spongepowered.asm.mixin.Final; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.Mutable; 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 | @Mixin(Entity.class) 20 | public abstract class EntityMixin implements IEntity_Debug { 21 | 22 | @Shadow 23 | @Final 24 | @Mutable 25 | protected RandomSource random; 26 | 27 | @Inject(method = "", at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/Entity;random:Lnet/minecraft/util/RandomSource;", opcode = Opcodes.PUTFIELD, shift = At.Shift.AFTER)) 28 | private void onInitRandom(EntityType type, Level level, CallbackInfo ci) { 29 | if (type == DebugRandom.DEBUG_ENTITY_TYPE && !level.isClientSide) { 30 | this.random = new DebugRandom((Entity) (Object) this); 31 | } 32 | } 33 | 34 | @Override 35 | public void clientcommands_tickDebugRandom() { 36 | if (this.random instanceof DebugRandom debugRandom) { 37 | debugRandom.tick(); 38 | } 39 | } 40 | 41 | @Inject(method = "setRemoved", at = @At("HEAD")) 42 | private void onRemoved(Entity.RemovalReason reason, CallbackInfo ci) { 43 | if (this.random instanceof DebugRandom debugRandom && (!((Object) this instanceof Player) || reason != Entity.RemovalReason.CHANGED_DIMENSION)) { 44 | debugRandom.writeToFile(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/debug/ServerLevelMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.debug; 2 | 3 | import net.earthcomputer.clientcommands.interfaces.IEntity_Debug; 4 | import net.minecraft.server.level.ServerLevel; 5 | import net.minecraft.world.entity.Entity; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | @Mixin(ServerLevel.class) 12 | public class ServerLevelMixin { 13 | @Inject(method = "tickNonPassenger", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;tick()V")) 14 | private void onTickNonPassenger(Entity entity, CallbackInfo ci) { 15 | ((IEntity_Debug) entity).clientcommands_tickDebugRandom(); 16 | } 17 | 18 | @Inject(method = "tickPassenger", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;rideTick()V")) 19 | private void onTickPassenger(Entity vehicle, Entity passenger, CallbackInfo ci) { 20 | ((IEntity_Debug) passenger).clientcommands_tickDebugRandom(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/events/ClientPacketListenerMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.events; 2 | 3 | import net.earthcomputer.clientcommands.event.MoreClientEntityEvents; 4 | import net.earthcomputer.clientcommands.event.MoreClientEvents; 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.client.multiplayer.ClientPacketListener; 7 | import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; 8 | import net.minecraft.network.protocol.game.ClientboundSetTimePacket; 9 | import org.spongepowered.asm.mixin.Mixin; 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(ClientPacketListener.class) 15 | public class ClientPacketListenerMixin { 16 | 17 | @Inject(method = "handleAddEntity", at = @At("HEAD")) 18 | public void onHandleAddEntityPre(ClientboundAddEntityPacket packet, CallbackInfo ci) { 19 | MoreClientEntityEvents.PRE_ADD_MAYBE_ON_NETWORK_THREAD.invoker().onAddEntity(packet); 20 | } 21 | 22 | @Inject(method = "handleAddEntity", at = @At("TAIL")) 23 | public void onHandleAddEntity(ClientboundAddEntityPacket packet, CallbackInfo ci) { 24 | MoreClientEntityEvents.POST_ADD.invoker().onAddEntity(packet); 25 | } 26 | 27 | @Inject(method = "handleSetTime", at = @At("HEAD")) 28 | private void onHandleSetTime(ClientboundSetTimePacket packet, CallbackInfo ci) { 29 | if (Minecraft.getInstance().isSameThread()) { 30 | MoreClientEvents.TIME_SYNC.invoker().onTimeSync(packet); 31 | } else { 32 | MoreClientEvents.TIME_SYNC_ON_NETWORK_THREAD.invoker().onTimeSync(packet); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/events/EntityMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.events; 2 | 3 | import net.minecraft.network.syncher.EntityDataAccessor; 4 | import net.minecraft.world.entity.Entity; 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(Entity.class) 11 | public class EntityMixin { 12 | @Inject(method = "onSyncedDataUpdated(Lnet/minecraft/network/syncher/EntityDataAccessor;)V", at = @At("RETURN")) 13 | protected void onSynchedDataUpdated(EntityDataAccessor dataAccessor, CallbackInfo ci) { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/events/ExperienceOrbMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.events; 2 | 3 | import net.earthcomputer.clientcommands.event.MoreClientEntityEvents; 4 | import net.minecraft.network.syncher.EntityDataAccessor; 5 | import net.minecraft.world.entity.ExperienceOrb; 6 | import org.spongepowered.asm.mixin.Final; 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.callback.CallbackInfo; 11 | 12 | @Mixin(ExperienceOrb.class) 13 | public class ExperienceOrbMixin extends EntityMixin { 14 | @Shadow 15 | @Final 16 | protected static EntityDataAccessor DATA_VALUE; 17 | @Unique 18 | private boolean hasXpUpdated = false; 19 | 20 | @Override 21 | protected void onSynchedDataUpdated(EntityDataAccessor dataAccessor, CallbackInfo ci) { 22 | super.onSynchedDataUpdated(dataAccessor, ci); 23 | if (dataAccessor == DATA_VALUE && !hasXpUpdated) { 24 | hasXpUpdated = true; 25 | MoreClientEntityEvents.POST_SET_INITIAL_XP_ORB_VALUE.invoker().onSetInitialXpOrbValue((ExperienceOrb) (Object) this); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/events/MinecraftMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.events; 2 | 3 | import net.earthcomputer.clientcommands.event.ClientConnectionEvents; 4 | import net.earthcomputer.clientcommands.event.ClientLevelEvents; 5 | import net.earthcomputer.clientcommands.event.MoreScreenEvents; 6 | import net.minecraft.client.Minecraft; 7 | import net.minecraft.client.gui.screens.Screen; 8 | import net.minecraft.client.multiplayer.ClientLevel; 9 | import org.jetbrains.annotations.Nullable; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Unique; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 15 | 16 | @Mixin(Minecraft.class) 17 | public abstract class MinecraftMixin { 18 | @Unique 19 | private boolean isLevelLoaded = false; 20 | 21 | @Inject(method = "updateLevelInEngines", at = @At("HEAD")) 22 | public void onUpdateLevelInEngines(ClientLevel level, CallbackInfo ci) { 23 | if (isLevelLoaded) { 24 | ClientLevelEvents.UNLOAD_LEVEL.invoker().onUnloadLevel(level == null); 25 | } 26 | isLevelLoaded = level != null; 27 | if (isLevelLoaded) { 28 | ClientLevelEvents.LOAD_LEVEL.invoker().onLoadLevel(level); 29 | } 30 | } 31 | 32 | @Inject(method = "setScreen", at = @At("HEAD"), cancellable = true) 33 | public void onOpenScreen(@Nullable Screen screen, CallbackInfo ci) { 34 | if (!MoreScreenEvents.BEFORE_ADD.invoker().beforeScreenAdd(screen)) { 35 | ci.cancel(); 36 | } 37 | } 38 | 39 | @Inject(method = "disconnect(Lnet/minecraft/client/gui/screens/Screen;Z)V", at = @At("RETURN")) 40 | public void onDisconnect(CallbackInfo ci) { 41 | ClientConnectionEvents.DISCONNECT.invoker().onDisconnect(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/lengthextender/ChatScreenMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.lengthextender; 2 | 3 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 4 | import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 5 | import net.earthcomputer.clientcommands.features.ChatLengthExtender; 6 | import net.earthcomputer.clientcommands.interfaces.IEditBox; 7 | import net.minecraft.client.gui.components.EditBox; 8 | import net.minecraft.client.gui.screens.ChatScreen; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Shadow; 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(ChatScreen.class) 16 | public class ChatScreenMixin { 17 | @Shadow 18 | protected EditBox input; 19 | 20 | @Inject(method = "init", at = @At("TAIL")) 21 | private void init(CallbackInfo ci) { 22 | ((IEditBox) input).clientcommands_setClientCommandLengthExtension(); 23 | } 24 | 25 | @WrapOperation(method = "normalizeChatMessage", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/StringUtil;trimChatMessage(Ljava/lang/String;)Ljava/lang/String;")) 26 | private String normalizeChatMessage(String string, Operation original) { 27 | Integer originalExtension = ChatLengthExtender.currentLengthExtension; 28 | ChatLengthExtender.currentLengthExtension = input.maxLength; 29 | try { 30 | return original.call(string); 31 | } finally { 32 | ChatLengthExtender.currentLengthExtension = originalExtension; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/lengthextender/StringUtilMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.lengthextender; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import net.earthcomputer.clientcommands.features.ChatLengthExtender; 5 | import net.minecraft.SharedConstants; 6 | import net.minecraft.util.StringUtil; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | 10 | @Mixin(value = StringUtil.class, priority = 900) // lower priority for ViaFabricPlus compatibility 11 | public class StringUtilMixin { 12 | @ModifyExpressionValue(method = "trimChatMessage", at = @At(value = "CONSTANT", args = "intValue=" + SharedConstants.MAX_CHAT_LENGTH)) 13 | private static int modifyMaxChatLength(int oldMax) { 14 | if (ChatLengthExtender.currentLengthExtension != null) { 15 | return ChatLengthExtender.currentLengthExtension; 16 | } 17 | return oldMax; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/AnvilMenuMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents; 2 | 3 | import net.earthcomputer.clientcommands.features.PlayerRandCracker; 4 | import net.minecraft.world.entity.player.Player; 5 | import net.minecraft.world.inventory.AnvilMenu; 6 | import net.minecraft.world.item.ItemStack; 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(AnvilMenu.class) 13 | public class AnvilMenuMixin { 14 | 15 | @Inject(method = "onTake", at = @At("HEAD")) 16 | public void onAnvilUse(Player player, ItemStack stack, CallbackInfo ci) { 17 | if (!player.getAbilities().instabuild) { 18 | PlayerRandCracker.onAnvilUse(); 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/ClientLevelMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import net.earthcomputer.clientcommands.features.PlayerRandCracker; 5 | import net.earthcomputer.clientcommands.util.MultiVersionCompat; 6 | import net.minecraft.client.Minecraft; 7 | import net.minecraft.client.multiplayer.ClientLevel; 8 | import net.minecraft.client.player.LocalPlayer; 9 | import net.minecraft.world.entity.Entity; 10 | import net.minecraft.world.entity.EquipmentSlot; 11 | import net.minecraft.world.entity.ExperienceOrb; 12 | import net.minecraft.world.item.ItemStack; 13 | import net.minecraft.world.item.enchantment.EnchantmentEffectComponents; 14 | import net.minecraft.world.item.enchantment.EnchantmentHelper; 15 | import org.spongepowered.asm.mixin.Mixin; 16 | import org.spongepowered.asm.mixin.Unique; 17 | import org.spongepowered.asm.mixin.injection.At; 18 | import org.spongepowered.asm.mixin.injection.Inject; 19 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 20 | 21 | import java.util.Arrays; 22 | 23 | @Mixin(ClientLevel.class) 24 | public class ClientLevelMixin { 25 | @Inject(method = "removeEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;onClientRemoval()V")) 26 | private void onRemoveEntity(int entityId, Entity.RemovalReason reason, CallbackInfo ci, @Local Entity entity) { 27 | if (entity instanceof ExperienceOrb) { 28 | LocalPlayer player = Minecraft.getInstance().player; 29 | assert player != null; 30 | if (player.getBoundingBox().inflate(1.25, 0.75, 1.25).intersects(entity.getBoundingBox())) { 31 | PlayerRandCracker.onXpOrb(); 32 | if (Arrays.stream(EquipmentSlot.values()).anyMatch(slot -> couldMendingRepair(player.getItemBySlot(slot)))) { 33 | PlayerRandCracker.onMending(); 34 | } 35 | } 36 | } 37 | } 38 | 39 | @Unique 40 | private boolean couldMendingRepair(ItemStack stack) { 41 | if (!EnchantmentHelper.has(stack, EnchantmentEffectComponents.REPAIR_WITH_XP)) { 42 | return false; 43 | } 44 | if (MultiVersionCompat.INSTANCE.getProtocolVersion() <= MultiVersionCompat.V1_15_2) { 45 | return true; // xp may try to mend items even if they're fully repaired pre-1.16 46 | } 47 | return stack.isDamaged(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/ClientPacketListenerMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import net.earthcomputer.clientcommands.features.PlayerRandCracker; 5 | import net.minecraft.client.multiplayer.ClientPacketListener; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | @Mixin(ClientPacketListener.class) 12 | public abstract class ClientPacketListenerMixin { 13 | @Inject(method = "sendCommand", at = @At("HEAD")) 14 | private void onSendCommand(String command, CallbackInfo ci) { 15 | StringReader reader = new StringReader(command); 16 | String commandName = reader.canRead() ? reader.readUnquotedString() : ""; 17 | if ("give".equals(commandName)) { 18 | PlayerRandCracker.onGiveCommand(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/ConsumableMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents; 2 | 3 | import net.earthcomputer.clientcommands.features.PlayerRandCracker; 4 | import net.minecraft.client.Minecraft; 5 | import net.minecraft.util.RandomSource; 6 | import net.minecraft.world.entity.LivingEntity; 7 | import net.minecraft.world.item.ItemStack; 8 | import net.minecraft.world.item.component.Consumable; 9 | import org.spongepowered.asm.mixin.Mixin; 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(Consumable.class) 15 | public class ConsumableMixin { 16 | @Inject(method = "emitParticlesAndSounds", at = @At("HEAD")) 17 | private void onEmitParticlesAndSounds(RandomSource rand, LivingEntity entity, ItemStack stack, int particleCount, CallbackInfo ci) { 18 | if (entity == Minecraft.getInstance().player) { 19 | PlayerRandCracker.onConsume(stack, entity.position(), particleCount, entity.getUseItemRemainingTicks(), (Consumable) (Object) this); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/CreeperMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents; 2 | 3 | import net.earthcomputer.clientcommands.features.PlayerRandCracker; 4 | import net.minecraft.world.InteractionHand; 5 | import net.minecraft.world.entity.monster.Creeper; 6 | import net.minecraft.world.entity.player.Player; 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(Creeper.class) 13 | public class CreeperMixin { 14 | @Inject(method = "mobInteract", at = @At(value = "FIELD", target = "Lnet/minecraft/world/level/Level;isClientSide:Z")) 15 | public void onInteract(Player player, InteractionHand hand, CallbackInfoReturnable ci) { 16 | PlayerRandCracker.onItemDamage(1, player, player.getItemInHand(hand)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/CrossbowItemMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents; 2 | 3 | import net.earthcomputer.clientcommands.features.PlayerRandCracker; 4 | import net.minecraft.world.item.CrossbowItem; 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(CrossbowItem.class) 11 | public class CrossbowItemMixin { 12 | @Inject(method = "performShooting", at = @At("HEAD")) 13 | private void onPerformShooting(CallbackInfo ci) { 14 | PlayerRandCracker.onCrossbowUse(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/CustomCreativeSlotMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents; 2 | 3 | import net.earthcomputer.clientcommands.interfaces.ICreativeSlot; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | 6 | @Mixin(targets = "net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen$CustomCreativeSlot") 7 | public class CustomCreativeSlotMixin implements ICreativeSlot { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/EntityMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents; 2 | 3 | import net.earthcomputer.clientcommands.features.PlayerRandCracker; 4 | import net.minecraft.client.player.LocalPlayer; 5 | import net.minecraft.world.entity.Entity; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Unique; 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(Entity.class) 13 | public class EntityMixin { 14 | @Inject(method = "doWaterSplashEffect", at = @At("HEAD")) 15 | public void onDoWaterSplashEffect(CallbackInfo ci) { 16 | if (isThePlayer()) { 17 | PlayerRandCracker.onSwimmingStart(); 18 | } 19 | } 20 | 21 | @Inject(method = "playAmethystStepSound", at = @At("HEAD")) 22 | private void onPlayAmethystStepSound(CallbackInfo ci) { 23 | if (isThePlayer()) { 24 | PlayerRandCracker.onAmethystChime(); 25 | } 26 | } 27 | 28 | @Inject(method = "spawnSprintParticle", at = @At("HEAD")) 29 | public void onSprinting(CallbackInfo ci) { 30 | if (isThePlayer()) { 31 | PlayerRandCracker.onSprinting(); 32 | } 33 | } 34 | 35 | @Unique 36 | private boolean isThePlayer() { 37 | return (Object) this instanceof LocalPlayer; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/FishingRodItemMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents; 2 | 3 | import net.earthcomputer.clientcommands.features.PlayerRandCracker; 4 | import net.minecraft.world.InteractionHand; 5 | import net.minecraft.world.InteractionResult; 6 | import net.minecraft.world.entity.player.Player; 7 | import net.minecraft.world.item.FishingRodItem; 8 | import net.minecraft.world.item.ItemStack; 9 | import net.minecraft.world.level.Level; 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.CallbackInfoReturnable; 14 | 15 | @Mixin(FishingRodItem.class) 16 | public class FishingRodItemMixin { 17 | @Inject(method = "use", at = @At(value = "FIELD", target = "Lnet/minecraft/sounds/SoundEvents;FISHING_BOBBER_RETRIEVE:Lnet/minecraft/sounds/SoundEvent;")) 18 | public void onRetrieveFishingRod(Level level, Player player, InteractionHand hand, CallbackInfoReturnable ci) { 19 | ItemStack stack = player.getItemInHand(hand); 20 | PlayerRandCracker.onItemDamageUncertain(1, 5, player, stack); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/FlintAndSteelItemMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents; 2 | 3 | import net.earthcomputer.clientcommands.features.PlayerRandCracker; 4 | import net.minecraft.world.InteractionResult; 5 | import net.minecraft.world.item.FlintAndSteelItem; 6 | import net.minecraft.world.item.context.UseOnContext; 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(FlintAndSteelItem.class) 13 | public class FlintAndSteelItemMixin { 14 | 15 | @Inject(method = "useOn", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/context/UseOnContext;getItemInHand()Lnet/minecraft/world/item/ItemStack;")) 16 | public void onUseOn(UseOnContext context, CallbackInfoReturnable ci) { 17 | PlayerRandCracker.onItemDamage(1, context.getPlayer(), context.getItemInHand()); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/FoodOnAStickItemMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents; 2 | 3 | import net.earthcomputer.clientcommands.features.PlayerRandCracker; 4 | import net.minecraft.world.InteractionHand; 5 | import net.minecraft.world.InteractionResult; 6 | import net.minecraft.world.entity.animal.Pig; 7 | import net.minecraft.world.entity.player.Player; 8 | import net.minecraft.world.item.FoodOnAStickItem; 9 | import net.minecraft.world.level.Level; 10 | import org.spongepowered.asm.mixin.Final; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.Shadow; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 16 | 17 | @Mixin(FoodOnAStickItem.class) 18 | public class FoodOnAStickItemMixin { 19 | 20 | @Shadow @Final private int consumeItemDamage; 21 | 22 | @Inject(method = "use", at = @At("HEAD")) 23 | public void onUse(Level level, Player player, InteractionHand hand, CallbackInfoReturnable ci) { 24 | if (player.isPassenger() && player.getVehicle() instanceof Pig) { 25 | PlayerRandCracker.onItemDamage(consumeItemDamage, player, player.getItemInHand(hand)); 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/HoeAndShovelItemMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents; 2 | 3 | import net.earthcomputer.clientcommands.features.PlayerRandCracker; 4 | import net.minecraft.world.InteractionResult; 5 | import net.minecraft.world.item.HoeItem; 6 | import net.minecraft.world.item.ShovelItem; 7 | import net.minecraft.world.item.context.UseOnContext; 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.CallbackInfoReturnable; 12 | 13 | @Mixin({HoeItem.class, ShovelItem.class}) 14 | public class HoeAndShovelItemMixin { 15 | 16 | @Inject(method = "useOn", at = @At(value = "FIELD", target = "Lnet/minecraft/world/level/Level;isClientSide:Z")) 17 | public void onUseOn(UseOnContext context, CallbackInfoReturnable ci) { 18 | PlayerRandCracker.onItemDamage(1, context.getPlayer(), context.getItemInHand()); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/InventoryMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents; 2 | 3 | import net.earthcomputer.clientcommands.features.PlayerRandCracker; 4 | import net.minecraft.core.NonNullList; 5 | import net.minecraft.server.level.ServerPlayer; 6 | import net.minecraft.world.entity.player.Inventory; 7 | import net.minecraft.world.entity.player.Player; 8 | import net.minecraft.world.item.ItemStack; 9 | import org.spongepowered.asm.mixin.Final; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Shadow; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 15 | 16 | @Mixin(Inventory.class) 17 | public class InventoryMixin { 18 | 19 | @Shadow @Final public NonNullList items; 20 | @Shadow @Final public Player player; 21 | 22 | @Inject(method = "placeItemBackInInventory(Lnet/minecraft/world/item/ItemStack;Z)V", at = @At("HEAD")) 23 | public void onOfferOrDrop(ItemStack stack, boolean notifiesClient, CallbackInfo ci) { 24 | if (!(player instanceof ServerPlayer)) { 25 | int stackSize = stack.getCount(); 26 | for (ItemStack item : items) { 27 | if (item.isEmpty()) { 28 | stackSize -= stack.getMaxStackSize(); 29 | } else if (ItemStack.isSameItemSameComponents(item, stack)) { 30 | stackSize -= stack.getMaxStackSize() - item.getCount(); 31 | } 32 | } 33 | if (stackSize > 0) { 34 | PlayerRandCracker.onDropItem(); 35 | } 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/ItemStackMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents; 2 | 3 | import net.earthcomputer.clientcommands.features.PlayerRandCracker; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.core.component.DataComponents; 6 | import net.minecraft.world.entity.EquipmentSlot; 7 | import net.minecraft.world.entity.LivingEntity; 8 | import net.minecraft.world.entity.player.Player; 9 | import net.minecraft.world.item.ItemStack; 10 | import net.minecraft.world.item.component.Tool; 11 | import net.minecraft.world.level.Level; 12 | import net.minecraft.world.level.block.state.BlockState; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Inject; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 17 | 18 | @Mixin(ItemStack.class) 19 | public class ItemStackMixin { 20 | 21 | @Inject(method = "hurtAndBreak(ILnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/entity/EquipmentSlot;)V", at = @At("HEAD")) 22 | public void onHurtAndBreak(int amount, LivingEntity holder, EquipmentSlot slot, CallbackInfo ci) { 23 | PlayerRandCracker.onItemDamage(amount, holder, (ItemStack) (Object) this); 24 | } 25 | 26 | @Inject(method = "mineBlock", at = @At("HEAD")) 27 | private void onMineBlock(Level level, BlockState state, BlockPos pos, Player player, CallbackInfo ci) { 28 | Tool tool = ((ItemStack) (Object) this).get(DataComponents.TOOL); 29 | if (tool != null) { 30 | if (state.getDestroySpeed(level, pos) != 0) { 31 | PlayerRandCracker.onItemDamage(tool.damagePerBlock(), player, (ItemStack) (Object) this); 32 | } 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/LocalPlayerMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents; 2 | 3 | import com.mojang.authlib.GameProfile; 4 | import net.earthcomputer.clientcommands.features.PlayerRandCracker; 5 | import net.minecraft.client.multiplayer.ClientLevel; 6 | import net.minecraft.client.player.AbstractClientPlayer; 7 | import net.minecraft.client.player.LocalPlayer; 8 | import net.minecraft.world.entity.item.ItemEntity; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 13 | 14 | @Mixin(LocalPlayer.class) 15 | public class LocalPlayerMixin extends AbstractClientPlayer { 16 | 17 | public LocalPlayerMixin(ClientLevel level, GameProfile profile) { 18 | super(level, profile); 19 | } 20 | 21 | @Inject(method = "drop", at = @At("HEAD")) 22 | public void onDrop(boolean dropAll, CallbackInfoReturnable ci) { 23 | PlayerRandCracker.onDropItem(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/MultiPlayerGameModeMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents; 2 | 3 | import net.earthcomputer.clientcommands.features.PlayerRandCracker; 4 | import net.minecraft.client.Minecraft; 5 | import net.minecraft.client.multiplayer.MultiPlayerGameMode; 6 | import net.minecraft.client.player.LocalPlayer; 7 | import net.minecraft.core.BlockPos; 8 | import net.minecraft.tags.ItemTags; 9 | import net.minecraft.world.item.ItemStack; 10 | import net.minecraft.world.level.Level; 11 | import net.minecraft.world.level.block.state.BlockState; 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 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 17 | 18 | @Mixin(MultiPlayerGameMode.class) 19 | public class MultiPlayerGameModeMixin { 20 | @Inject(method = "destroyBlock", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/Block;playerWillDestroy(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/entity/player/Player;)Lnet/minecraft/world/level/block/state/BlockState;")) 21 | public void onDestroyBlock(BlockPos pos, CallbackInfoReturnable ci) { 22 | LocalPlayer player = Minecraft.getInstance().player; 23 | assert player != null; 24 | Level level = player.level(); 25 | ItemStack stack = player.getMainHandItem(); 26 | if (stack.is(ItemTags.PICKAXES) || stack.is(ItemTags.AXES) || stack.is(ItemTags.SHOVELS) || stack.is(ItemTags.HOES)) { 27 | BlockState state = level.getBlockState(pos); 28 | if (state.getDestroySpeed(level, pos) != 0) { 29 | PlayerRandCracker.onItemDamage(1, player, stack); 30 | } 31 | } 32 | } 33 | 34 | @Inject(method = "startPrediction", at = @At("HEAD")) 35 | private void preStartPrediction(CallbackInfo ci) { 36 | PlayerRandCracker.isPredictingBlockBreaking = true; 37 | } 38 | 39 | @Inject(method = "startPrediction", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientPacketListener;send(Lnet/minecraft/network/protocol/Packet;)V", shift = At.Shift.AFTER)) 40 | private void postStartPrediction(CallbackInfo ci) { 41 | PlayerRandCracker.postSendBlockBreakingPredictionPacket(); 42 | } 43 | 44 | @Inject(method = "startPrediction", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/prediction/BlockStatePredictionHandler;close()V")) 45 | private void startPredictionFinally(CallbackInfo ci) { 46 | PlayerRandCracker.isPredictingBlockBreaking = false; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/MushroomCowSheepAndSnowGolemMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents; 2 | 3 | import net.earthcomputer.clientcommands.features.PlayerRandCracker; 4 | import net.minecraft.world.InteractionHand; 5 | import net.minecraft.world.entity.animal.MushroomCow; 6 | import net.minecraft.world.entity.animal.SnowGolem; 7 | import net.minecraft.world.entity.animal.sheep.Sheep; 8 | import net.minecraft.world.entity.player.Player; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.Slice; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 14 | 15 | @Mixin({MushroomCow.class, Sheep.class, SnowGolem.class}) 16 | public class MushroomCowSheepAndSnowGolemMixin { 17 | @SuppressWarnings({"MixinAnnotationTarget", "UnqualifiedMemberReference", "UnresolvedMixinReference"}) // mcdev doesn't understand unqualified @At references 18 | @Inject( 19 | method = "mobInteract", 20 | slice = @Slice(from = @At(value = "FIELD", target = "Lnet/minecraft/world/item/Items;SHEARS:Lnet/minecraft/world/item/Item;")), 21 | at = { 22 | @At(value = "INVOKE", target = "level()Lnet/minecraft/world/level/Level;", ordinal = 0, remap = false), 23 | @At(value = "INVOKE", target = "method_37908()Lnet/minecraft/class_1937;", ordinal = 0, remap = false), 24 | } 25 | ) 26 | public void onInteract(Player player, InteractionHand hand, CallbackInfoReturnable ci) { 27 | PlayerRandCracker.onItemDamage(1, player, player.getItemInHand(hand)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/PumpkinBlockMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents; 2 | 3 | import net.earthcomputer.clientcommands.features.PlayerRandCracker; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.world.InteractionHand; 6 | import net.minecraft.world.InteractionResult; 7 | import net.minecraft.world.entity.player.Player; 8 | import net.minecraft.world.item.ItemStack; 9 | import net.minecraft.world.level.Level; 10 | import net.minecraft.world.level.block.PumpkinBlock; 11 | import net.minecraft.world.level.block.state.BlockState; 12 | import net.minecraft.world.phys.BlockHitResult; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Inject; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 17 | 18 | @Mixin(PumpkinBlock.class) 19 | public class PumpkinBlockMixin { 20 | 21 | @Inject(method = "useItemOn", at = @At(value = "FIELD", target = "Lnet/minecraft/world/level/Level;isClientSide:Z")) 22 | public void onShear(ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hitResult, CallbackInfoReturnable cir) { 23 | PlayerRandCracker.onItemDamage(1, player, stack); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/ShearsItemMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents; 2 | 3 | import net.earthcomputer.clientcommands.features.PlayerRandCracker; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.world.entity.LivingEntity; 6 | import net.minecraft.world.item.ItemStack; 7 | import net.minecraft.world.item.ShearsItem; 8 | import net.minecraft.world.level.Level; 9 | import net.minecraft.world.level.block.state.BlockState; 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.CallbackInfoReturnable; 14 | 15 | @Mixin(ShearsItem.class) 16 | public class ShearsItemMixin { 17 | 18 | @Inject(method = "mineBlock", at = @At("HEAD")) 19 | public void onMineBlock(ItemStack stack, Level level, BlockState state, BlockPos pos, LivingEntity miner, CallbackInfoReturnable ci) { 20 | PlayerRandCracker.onItemDamage(1, miner, stack); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/droppableinventory/AbstractCraftingMenuMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents.droppableinventory; 2 | 3 | import net.earthcomputer.clientcommands.interfaces.IDroppableInventoryContainer; 4 | import net.minecraft.world.Container; 5 | import net.minecraft.world.inventory.AbstractCraftingMenu; 6 | import net.minecraft.world.inventory.CraftingContainer; 7 | import net.minecraft.world.inventory.CraftingMenu; 8 | import org.spongepowered.asm.mixin.Final; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | 12 | @Mixin(AbstractCraftingMenu.class) 13 | public class AbstractCraftingMenuMixin implements IDroppableInventoryContainer { 14 | 15 | @Shadow @Final 16 | protected CraftingContainer craftSlots; 17 | 18 | @Override 19 | public Container getDroppableInventory() { 20 | return craftSlots; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/droppableinventory/CartographyTableMenuMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents.droppableinventory; 2 | 3 | import net.earthcomputer.clientcommands.interfaces.IDroppableInventoryContainer; 4 | import net.minecraft.world.Container; 5 | import net.minecraft.world.inventory.CartographyTableMenu; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | 8 | @Mixin(CartographyTableMenu.class) 9 | public class CartographyTableMenuMixin implements IDroppableInventoryContainer { 10 | 11 | @Override 12 | public Container getDroppableInventory() { 13 | return ((CartographyTableMenu) (Object) this).container; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/droppableinventory/EnchantmentMenuMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents.droppableinventory; 2 | 3 | import net.earthcomputer.clientcommands.interfaces.IDroppableInventoryContainer; 4 | import net.minecraft.world.Container; 5 | import net.minecraft.world.inventory.EnchantmentMenu; 6 | import org.spongepowered.asm.mixin.Final; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | 10 | @Mixin(EnchantmentMenu.class) 11 | public abstract class EnchantmentMenuMixin implements IDroppableInventoryContainer { 12 | 13 | @Shadow @Final private Container enchantSlots; 14 | 15 | @Override 16 | public Container getDroppableInventory() { 17 | return enchantSlots; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/droppableinventory/GrindstoneMenuMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents.droppableinventory; 2 | 3 | import net.earthcomputer.clientcommands.interfaces.IDroppableInventoryContainer; 4 | import net.minecraft.world.Container; 5 | import net.minecraft.world.inventory.GrindstoneMenu; 6 | import org.spongepowered.asm.mixin.Final; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | 10 | @Mixin(GrindstoneMenu.class) 11 | public class GrindstoneMenuMixin implements IDroppableInventoryContainer { 12 | 13 | @Shadow @Final Container repairSlots; 14 | 15 | @Override 16 | public Container getDroppableInventory() { 17 | return repairSlots; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/droppableinventory/ItemCombinerMenuMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents.droppableinventory; 2 | 3 | import net.earthcomputer.clientcommands.interfaces.IDroppableInventoryContainer; 4 | import net.minecraft.world.Container; 5 | import net.minecraft.world.inventory.ItemCombinerMenu; 6 | import org.spongepowered.asm.mixin.Final; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | 10 | @Mixin(ItemCombinerMenu.class) 11 | public class ItemCombinerMenuMixin implements IDroppableInventoryContainer { 12 | 13 | @Shadow @Final protected Container inputSlots; 14 | 15 | @Override 16 | public Container getDroppableInventory() { 17 | return inputSlots; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/droppableinventory/LoomMenuMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents.droppableinventory; 2 | 3 | import net.earthcomputer.clientcommands.interfaces.IDroppableInventoryContainer; 4 | import net.minecraft.world.Container; 5 | import net.minecraft.world.inventory.LoomMenu; 6 | import org.spongepowered.asm.mixin.Final; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | 10 | @Mixin(LoomMenu.class) 11 | public class LoomMenuMixin implements IDroppableInventoryContainer { 12 | 13 | @Shadow @Final private Container inputContainer; 14 | 15 | @Override 16 | public Container getDroppableInventory() { 17 | return inputContainer; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/droppableinventory/MerchantMenuMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents.droppableinventory; 2 | 3 | import net.earthcomputer.clientcommands.interfaces.IDroppableInventoryContainer; 4 | import net.minecraft.world.Container; 5 | import net.minecraft.world.SimpleContainer; 6 | import net.minecraft.world.inventory.MerchantContainer; 7 | import net.minecraft.world.inventory.MerchantMenu; 8 | import org.spongepowered.asm.mixin.Final; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | 12 | @Mixin(MerchantMenu.class) 13 | public class MerchantMenuMixin implements IDroppableInventoryContainer { 14 | 15 | @Shadow @Final private MerchantContainer tradeContainer; 16 | 17 | @Override 18 | public Container getDroppableInventory() { 19 | return new SimpleContainer(tradeContainer.getItem(0), tradeContainer.getItem(1)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/rngevents/droppableinventory/StonecutterMenuMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.rngevents.droppableinventory; 2 | 3 | import net.earthcomputer.clientcommands.interfaces.IDroppableInventoryContainer; 4 | import net.minecraft.world.Container; 5 | import net.minecraft.world.inventory.StonecutterMenu; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | 8 | @Mixin(StonecutterMenu.class) 9 | public class StonecutterMenuMixin implements IDroppableInventoryContainer { 10 | 11 | @Override 12 | public Container getDroppableInventory() { 13 | return ((StonecutterMenu) (Object) this).container; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/scrambletitle/MinecraftMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.scrambletitle; 2 | 3 | import net.earthcomputer.clientcommands.ClientCommands; 4 | import net.minecraft.client.Minecraft; 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.CallbackInfoReturnable; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import java.util.stream.Collectors; 14 | 15 | @Mixin(Minecraft.class) 16 | public class MinecraftMixin { 17 | // Earth annoying his friends <3 nothing to see here 18 | @Inject(method = "createTitle", at = @At("RETURN"), cancellable = true) 19 | private void modifyWindowTitle(CallbackInfoReturnable ci) { 20 | if (ClientCommands.scrambleWindowTitle) { 21 | List chars = ci.getReturnValue().chars().mapToObj(c -> (char) c).collect(Collectors.toCollection(ArrayList::new)); 22 | Collections.shuffle(chars); 23 | ci.setReturnValue(chars.stream().map(String::valueOf).collect(Collectors.joining())); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/serverbrand/ClientCommonPacketListenerImplMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.serverbrand; 2 | 3 | import net.earthcomputer.clientcommands.features.ServerBrandManager; 4 | import net.minecraft.client.multiplayer.ClientCommonPacketListenerImpl; 5 | import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; 6 | import org.jetbrains.annotations.Nullable; 7 | import org.objectweb.asm.Opcodes; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Shadow; 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(ClientCommonPacketListenerImpl.class) 15 | public class ClientCommonPacketListenerImplMixin { 16 | @Shadow 17 | @Nullable 18 | protected String serverBrand; 19 | 20 | @Inject(method = "handleCustomPayload(Lnet/minecraft/network/protocol/common/ClientboundCustomPayloadPacket;)V", at = @At(value = "FIELD", target = "Lnet/minecraft/client/multiplayer/ClientCommonPacketListenerImpl;serverBrand:Ljava/lang/String;", opcode = Opcodes.PUTFIELD, shift = At.Shift.AFTER)) 21 | private void onBrand(ClientboundCustomPayloadPacket packet, CallbackInfo ci) { 22 | ServerBrandManager.setServerBrand(serverBrand); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/mixin/suggestionshook/ClientPacketListenerMixin.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.mixin.suggestionshook; 2 | 3 | import net.earthcomputer.clientcommands.features.SuggestionsHook; 4 | import net.minecraft.client.multiplayer.ClientPacketListener; 5 | import net.minecraft.network.protocol.game.ClientboundCommandSuggestionsPacket; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | @Mixin(ClientPacketListener.class) 12 | public class ClientPacketListenerMixin { 13 | @Inject(method = "handleCommandSuggestions", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/PacketUtils;ensureRunningOnSameThread(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketListener;Lnet/minecraft/util/thread/BlockableEventLoop;)V", shift = At.Shift.AFTER), cancellable = true) 14 | private void onHandleCommandSuggestions(ClientboundCommandSuggestionsPacket packet, CallbackInfo ci) { 15 | if (SuggestionsHook.onCompletions(packet)) { 16 | ci.cancel(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/render/Cuboid.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.render; 2 | 3 | 4 | import com.mojang.blaze3d.vertex.PoseStack; 5 | import com.mojang.blaze3d.vertex.VertexConsumer; 6 | import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; 7 | import net.minecraft.world.phys.AABB; 8 | import net.minecraft.world.phys.Vec3; 9 | 10 | public class Cuboid extends Shape { 11 | 12 | private final Line[] edges = new Line[12]; 13 | public final Vec3 start; 14 | public final Vec3 size; 15 | 16 | public Cuboid(AABB box, int color) { 17 | this(new Vec3(box.minX, box.minY, box.minZ), new Vec3(box.maxX, box.maxY, box.maxZ), color); 18 | } 19 | 20 | public Cuboid(Vec3 start, Vec3 end, int color) { 21 | this.start = start; 22 | this.size = new Vec3(end.x() - start.x(), end.y() - start.y(), end.z() - start.z()); 23 | this.edges[0] = new Line(this.start, this.start.add(this.size.x(), 0, 0), color); 24 | this.edges[1] = new Line(this.start, this.start.add(0, this.size.y(), 0), color); 25 | this.edges[2] = new Line(this.start, this.start.add(0, 0, this.size.z()), color); 26 | this.edges[3] = new Line(this.start.add(this.size.x(), 0, this.size.z()), this.start.add(this.size.x(), 0, 0), color); 27 | this.edges[4] = new Line(this.start.add(this.size.x(), 0, this.size.z()), this.start.add(this.size.x(), this.size.y(), this.size.z()), color); 28 | this.edges[5] = new Line(this.start.add(this.size.x(), 0, this.size.z()), this.start.add(0, 0, this.size.z()), color); 29 | this.edges[6] = new Line(this.start.add(this.size.x(), this.size.y(), 0), this.start.add(this.size.x(), 0, 0), color); 30 | this.edges[7] = new Line(this.start.add(this.size.x(), this.size.y(), 0), this.start.add(0, this.size.y(), 0), color); 31 | this.edges[8] = new Line(this.start.add(this.size.x(), this.size.y(), 0), this.start.add(this.size.x(), this.size.y(), this.size.z()), color); 32 | this.edges[9] = new Line(this.start.add(0, this.size.y(), this.size.z()), this.start.add(0, 0, this.size.z()), color); 33 | this.edges[10] = new Line(this.start.add(0, this.size.y(), this.size.z()), this.start.add(0, this.size.y(), 0), color); 34 | this.edges[11] = new Line(this.start.add(0, this.size.y(), this.size.z()), this.start.add(this.size.x(), this.size.y(), this.size.z()), color); 35 | } 36 | 37 | @Override 38 | public void render(VertexConsumer vertexConsumer, WorldRenderContext context) { 39 | for (Line edge : this.edges) { 40 | edge.renderLine(vertexConsumer, context, prevPos.subtract(getPos())); 41 | } 42 | } 43 | 44 | @Override 45 | public Vec3 getPos() { 46 | return start; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/render/Line.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.render; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import com.mojang.blaze3d.vertex.VertexConsumer; 5 | import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; 6 | import net.minecraft.world.phys.Vec3; 7 | 8 | public class Line extends Shape { 9 | public final Vec3 start; 10 | public final Vec3 end; 11 | public final int color; 12 | public static final float THICKNESS = 2f; 13 | 14 | public Line(Vec3 start, Vec3 end, int color) { 15 | this.start = start; 16 | this.end = end; 17 | this.color = color; 18 | } 19 | 20 | @Override 21 | public void render(VertexConsumer vertexConsumer, WorldRenderContext context) { 22 | renderLine(vertexConsumer, context, prevPos.subtract(getPos())); 23 | } 24 | 25 | public void renderLine(VertexConsumer vertexConsumer, WorldRenderContext context, Vec3 prevPosOffset) { 26 | PoseStack poseStack = context.matrixStack(); 27 | float delta = context.tickCounter().getRealtimeDeltaTicks(); 28 | Vec3 cameraPos = context.camera().getPosition(); 29 | assert poseStack != null; 30 | Vec3 normal = this.end.subtract(this.start).normalize(); 31 | putVertex(poseStack, vertexConsumer, this.start.add(prevPosOffset.scale(1 - delta)).subtract(cameraPos), normal); 32 | putVertex(poseStack, vertexConsumer, this.end.add(prevPosOffset.scale(1 - delta)).subtract(cameraPos), normal); 33 | } 34 | 35 | private void putVertex(PoseStack poseStack, VertexConsumer vertexConsumer, Vec3 pos, Vec3 normal) { 36 | vertexConsumer.addVertex( 37 | poseStack.last().pose(), 38 | (float) pos.x(), 39 | (float) pos.y(), 40 | (float) pos.z() 41 | ).setColor( 42 | ((color >> 16) & 0xFF) / 255.0F, 43 | ((color >> 8) & 0xFF) / 255.0F, 44 | (color & 0xFF) / 255.0F, 45 | 1.0F 46 | ).setNormal( 47 | poseStack.last(), 48 | (float) normal.x(), 49 | (float) normal.y(), 50 | (float) normal.z() 51 | ); 52 | } 53 | 54 | @Override 55 | public Vec3 getPos() { 56 | return start; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/render/Shape.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.render; 2 | 3 | import com.mojang.blaze3d.vertex.VertexConsumer; 4 | import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; 5 | import net.minecraft.world.phys.Vec3; 6 | 7 | public abstract class Shape { 8 | int deathTime; 9 | protected Vec3 prevPos; 10 | 11 | public void tick() { 12 | } 13 | 14 | public abstract void render(VertexConsumer vertexConsumer, WorldRenderContext context); 15 | 16 | public abstract Vec3 getPos(); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/task/LongTask.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.task; 2 | 3 | import java.util.Set; 4 | 5 | /** 6 | * Acts like a for loop that can delay between iterations to allow the game to continue ticking 7 | */ 8 | public abstract class LongTask { 9 | 10 | boolean isInitialized = false; 11 | private boolean delayScheduled; 12 | private boolean broken = false; 13 | 14 | public abstract void initialize(); 15 | 16 | public abstract boolean condition(); 17 | 18 | public abstract void increment(); 19 | 20 | public abstract void body(); 21 | 22 | public final void _break() { 23 | broken = true; 24 | } 25 | 26 | public final boolean isCompleted() { 27 | return broken || !condition(); 28 | } 29 | 30 | public void onCompleted() {} 31 | 32 | public final void scheduleDelay() { 33 | delayScheduled = true; 34 | } 35 | 36 | public final void unscheduleDelay() { 37 | delayScheduled = false; 38 | } 39 | 40 | public final boolean isDelayScheduled() { 41 | return delayScheduled; 42 | } 43 | 44 | public boolean stopOnLevelUnload(boolean isDisconnect) { 45 | return true; 46 | } 47 | 48 | public Set getMutexKeys() { 49 | return Set.of(); 50 | } 51 | 52 | public final boolean conflictsWith(LongTask other) { 53 | return getMutexKeys().stream().anyMatch(other.getMutexKeys()::contains); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/task/LongTaskList.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.task; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashSet; 5 | import java.util.List; 6 | import java.util.Set; 7 | 8 | public class LongTaskList extends LongTask { 9 | 10 | private final List children = new ArrayList<>(); 11 | 12 | public void addTask(LongTask task) { 13 | children.add(task); 14 | } 15 | 16 | @Override 17 | public void initialize() { 18 | if (!children.isEmpty()) { 19 | children.getFirst().initialize(); 20 | } 21 | } 22 | 23 | @Override 24 | public boolean condition() { 25 | return !children.isEmpty(); 26 | } 27 | 28 | @Override 29 | public void increment() { 30 | } 31 | 32 | @Override 33 | public void body() { 34 | LongTask child = children.getFirst(); 35 | if (child.isCompleted()) { 36 | child.onCompleted(); 37 | if (child.isDelayScheduled()) { 38 | scheduleDelay(); 39 | } 40 | children.removeFirst(); 41 | if (!children.isEmpty()) { 42 | children.getFirst().initialize(); 43 | } 44 | } else { 45 | child.body(); 46 | if (!child.isCompleted()) { 47 | child.increment(); 48 | } 49 | if (child.isDelayScheduled()) { 50 | child.unscheduleDelay(); 51 | scheduleDelay(); 52 | } 53 | } 54 | } 55 | 56 | @Override 57 | public boolean stopOnLevelUnload(boolean isDisconnect) { 58 | boolean stop = false; 59 | for (LongTask child : children) { 60 | stop |= child.stopOnLevelUnload(isDisconnect); 61 | } 62 | return stop; 63 | } 64 | 65 | @Override 66 | public void onCompleted() { 67 | if (!children.isEmpty()) { 68 | children.getFirst().onCompleted(); 69 | } 70 | } 71 | 72 | @Override 73 | public Set getMutexKeys() { 74 | Set union = new HashSet<>(); 75 | for (LongTask child : children) { 76 | union.addAll(child.getMutexKeys()); 77 | } 78 | return union; 79 | } 80 | 81 | @Override 82 | public String toString() { 83 | Class thisClass = getClass(); 84 | String packageName = thisClass.getPackageName(); 85 | String classDisplayName = packageName.isEmpty() ? thisClass.getName() : thisClass.getName().substring(packageName.length() + 1); 86 | return classDisplayName + children; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/task/OneTickTask.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.task; 2 | 3 | public abstract class OneTickTask extends LongTask { 4 | @Override 5 | public void initialize() { 6 | run(); 7 | } 8 | 9 | @Override 10 | public boolean condition() { 11 | return false; 12 | } 13 | 14 | @Override 15 | public void increment() { 16 | } 17 | 18 | @Override 19 | public void body() { 20 | } 21 | 22 | public abstract void run(); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/task/SimpleTask.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.task; 2 | 3 | public abstract class SimpleTask extends LongTask { 4 | @Override 5 | public void initialize() { 6 | } 7 | 8 | @Override 9 | public void increment() { 10 | } 11 | 12 | @Override 13 | public void body() { 14 | onTick(); 15 | scheduleDelay(); 16 | } 17 | 18 | protected abstract void onTick(); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/util/BuildInfo.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.util; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.JsonParser; 5 | import com.mojang.logging.LogUtils; 6 | import net.fabricmc.loader.api.FabricLoader; 7 | import org.slf4j.Logger; 8 | 9 | import java.io.BufferedReader; 10 | import java.io.IOException; 11 | import java.nio.file.Files; 12 | 13 | public final class BuildInfo { 14 | private BuildInfo() { 15 | } 16 | 17 | private static final Logger LOGGER = LogUtils.getLogger(); 18 | 19 | public static final String VERSION; 20 | public static final String BRANCH; 21 | public static final String SHORT_COMMIT_HASH; 22 | public static final String COMMIT_HASH; 23 | 24 | static { 25 | String version, branch, shortCommitHash, commitHash; 26 | version = branch = shortCommitHash = commitHash = "unknown"; 27 | try (BufferedReader reader = Files.newBufferedReader(FabricLoader.getInstance() 28 | .getModContainer("clientcommands").orElseThrow() 29 | .findPath("build_info.json").orElseThrow()) 30 | ) { 31 | JsonObject object = JsonParser.parseReader(reader).getAsJsonObject(); 32 | version = object.get("version").getAsString(); 33 | branch = object.get("branch").getAsString(); 34 | shortCommitHash = object.get("shortCommitHash").getAsString(); 35 | commitHash = object.get("commitHash").getAsString(); 36 | } catch (RuntimeException | IOException e) { 37 | LOGGER.error("Error while reading build_info.json", e); 38 | } 39 | VERSION = version; 40 | BRANCH = branch; 41 | SHORT_COMMIT_HASH = shortCommitHash; 42 | COMMIT_HASH = commitHash; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/util/GuiBlocker.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.util; 2 | 3 | import net.earthcomputer.clientcommands.event.MoreScreenEvents; 4 | import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.client.gui.screens.Screen; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Iterator; 11 | import java.util.List; 12 | 13 | public abstract class GuiBlocker { 14 | static { 15 | MoreScreenEvents.BEFORE_ADD.register(GuiBlocker::onOpenGui); 16 | ClientTickEvents.START_CLIENT_TICK.register(GuiBlocker::tick); 17 | } 18 | 19 | private static final List blockers = new ArrayList<>(); 20 | 21 | private static boolean onOpenGui(@Nullable Screen screen) { 22 | boolean shouldOpen = true; 23 | Iterator itr = blockers.iterator(); 24 | while (itr.hasNext()) { 25 | GuiBlocker blocker = itr.next(); 26 | boolean pass = blocker.accept(screen); 27 | if (!pass) { 28 | shouldOpen = false; 29 | itr.remove(); 30 | } 31 | } 32 | return shouldOpen; 33 | } 34 | 35 | private static void tick(Minecraft mc) { 36 | Iterator itr = blockers.iterator(); 37 | while (itr.hasNext()) { 38 | GuiBlocker blocker = itr.next(); 39 | blocker.timeoutCounter--; 40 | if (blocker.timeoutCounter <= 0) { 41 | itr.remove(); 42 | } 43 | } 44 | } 45 | 46 | public static void addBlocker(GuiBlocker blocker) { 47 | blockers.add(blocker); 48 | } 49 | 50 | private int timeoutCounter = 100; 51 | 52 | /** 53 | * Return false to not open the GUI 54 | */ 55 | public abstract boolean accept(@Nullable Screen screen); 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/util/ReflectionUtils.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.util; 2 | 3 | import java.lang.reflect.Field; 4 | import java.util.stream.Stream; 5 | 6 | public final class ReflectionUtils { 7 | 8 | private ReflectionUtils() { 9 | } 10 | 11 | public static Stream getAllFields(Class clazz) { 12 | Stream.Builder builder = Stream.builder(); 13 | Class targetClass = clazz; 14 | while (targetClass.getSuperclass() != null) { 15 | Field[] fields = targetClass.getDeclaredFields(); 16 | for (Field field : fields) { 17 | builder.add(field); 18 | } 19 | targetClass = targetClass.getSuperclass(); 20 | } 21 | return builder.build(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/util/ThrowingPredicate.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.util; 2 | 3 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 4 | 5 | @FunctionalInterface 6 | public interface ThrowingPredicate { 7 | boolean test(T t) throws CommandSyntaxException; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/net/earthcomputer/clientcommands/util/UnsafeUtils.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.util; 2 | 3 | import com.mojang.logging.LogUtils; 4 | import net.minecraft.Util; 5 | import org.jetbrains.annotations.Nullable; 6 | import org.slf4j.Logger; 7 | import sun.misc.Unsafe; 8 | 9 | import java.lang.invoke.MethodHandles; 10 | import java.lang.reflect.Field; 11 | 12 | /** 13 | * @author Gaming32 14 | */ 15 | public final class UnsafeUtils { 16 | 17 | private UnsafeUtils() { 18 | } 19 | 20 | private static final Logger LOGGER = LogUtils.getLogger(); 21 | 22 | private static final @Nullable Unsafe UNSAFE = Util.make(() -> { 23 | try { 24 | final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); 25 | unsafeField.setAccessible(true); 26 | return (Unsafe) unsafeField.get(null); 27 | } catch (Exception e) { 28 | LOGGER.error("Could not access theUnsafe", e); 29 | return null; 30 | } 31 | }); 32 | 33 | private static final @Nullable MethodHandles.Lookup IMPL_LOOKUP = Util.make(() -> { 34 | try { 35 | //noinspection ConstantValue 36 | if (UNSAFE == null) { 37 | return null; 38 | } 39 | final Field implLookupField = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP"); 40 | return (MethodHandles.Lookup) UNSAFE.getObject(UNSAFE.staticFieldBase(implLookupField), UNSAFE.staticFieldOffset(implLookupField)); 41 | } catch (Exception e) { 42 | LOGGER.error("Could not access IMPL_LOOKUP", e); 43 | return null; 44 | } 45 | }); 46 | 47 | public static @Nullable Unsafe getUnsafe() { 48 | return UNSAFE; 49 | } 50 | 51 | public static @Nullable MethodHandles.Lookup getImplLookup() { 52 | return IMPL_LOOKUP; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/resources/assets/clientcommands/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Earthcomputer/clientcommands/689ecbb8ea687da513a54116e6c2b27419240f28/src/main/resources/assets/clientcommands/icon.png -------------------------------------------------------------------------------- /src/main/resources/assets/clientcommands/textures/connect_four/board.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Earthcomputer/clientcommands/689ecbb8ea687da513a54116e6c2b27419240f28/src/main/resources/assets/clientcommands/textures/connect_four/board.png -------------------------------------------------------------------------------- /src/main/resources/assets/clientcommands/textures/connect_four/pieces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Earthcomputer/clientcommands/689ecbb8ea687da513a54116e6c2b27419240f28/src/main/resources/assets/clientcommands/textures/connect_four/pieces.png -------------------------------------------------------------------------------- /src/main/resources/assets/clientcommands/textures/minesweeper_atlas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Earthcomputer/clientcommands/689ecbb8ea687da513a54116e6c2b27419240f28/src/main/resources/assets/clientcommands/textures/minesweeper_atlas.png -------------------------------------------------------------------------------- /src/main/resources/assets/clientcommands/textures/snake_grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Earthcomputer/clientcommands/689ecbb8ea687da513a54116e6c2b27419240f28/src/main/resources/assets/clientcommands/textures/snake_grid.png -------------------------------------------------------------------------------- /src/main/resources/assets/clientcommands/textures/tic_tac_toe/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Earthcomputer/clientcommands/689ecbb8ea687da513a54116e6c2b27419240f28/src/main/resources/assets/clientcommands/textures/tic_tac_toe/grid.png -------------------------------------------------------------------------------- /src/main/resources/assets/clientcommands/textures/tic_tac_toe/marks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Earthcomputer/clientcommands/689ecbb8ea687da513a54116e6c2b27419240f28/src/main/resources/assets/clientcommands/textures/tic_tac_toe/marks.png -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "clientcommands", 4 | "name": "Client Commands", 5 | "description": "Useful client-side commands", 6 | "version": "${version}", 7 | "environment": "client", 8 | "license": "LGPL-3.0-or-later", 9 | "icon": "assets/clientcommands/icon.png", 10 | "contact": { 11 | "homepage": "https://earthcomputer.net/", 12 | "issues": "https://github.com/Earthcomputer/clientcommands/issues", 13 | "sources": "https://github.com/Earthcomputer/clientcommands" 14 | }, 15 | "authors": [ 16 | "Earthcomputer" 17 | ], 18 | "contributors": [ 19 | "cortex", 20 | "haykam", 21 | "xpple" 22 | ], 23 | "entrypoints": { 24 | "client": [ 25 | "net.earthcomputer.clientcommands.ClientCommands" 26 | ] 27 | }, 28 | "depends": { 29 | "minecraft": "${mcversion}", 30 | "fabricloader": ">=0.4.0", 31 | "fabric-api": "*", 32 | "betterconfig": "*", 33 | "clientarguments": "*" 34 | }, 35 | "mixins": [ 36 | "mixins.clientcommands.json" 37 | ], 38 | "accessWidener": "clientcommands.aw", 39 | "custom": { 40 | "modmenu": { 41 | "update_checker": true 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "ClientCommands resources", 4 | "pack_format": 4 5 | } 6 | } -------------------------------------------------------------------------------- /src/test/java/net/earthcomputer/clientcommands/test/MixinApplyTest.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.test; 2 | 3 | import net.minecraft.SharedConstants; 4 | import net.minecraft.server.Bootstrap; 5 | import org.junit.jupiter.api.BeforeAll; 6 | import org.junit.jupiter.api.Test; 7 | import org.spongepowered.asm.mixin.MixinEnvironment; 8 | 9 | public final class MixinApplyTest { 10 | @BeforeAll 11 | public static void setup() { 12 | SharedConstants.tryDetectVersion(); 13 | Bootstrap.bootStrap(); 14 | } 15 | 16 | @Test 17 | public void auditMixins() { 18 | MixinEnvironment.getCurrentEnvironment().audit(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/net/earthcomputer/clientcommands/test/TestUtil.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.test; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | 5 | import java.io.IOException; 6 | import java.io.PrintWriter; 7 | import java.io.StringWriter; 8 | import java.nio.file.Files; 9 | import java.nio.file.NoSuchFileException; 10 | import java.nio.file.Path; 11 | import java.util.function.Consumer; 12 | 13 | public final class TestUtil { 14 | private static final Path REGRESSION_TESTS_DIR = Path.of(System.getProperty("clientcommands.regressionTestDir")); 15 | 16 | private TestUtil() { 17 | } 18 | 19 | public static void regressionTest(String name, String value) { 20 | if (!System.lineSeparator().equals("\n")) { 21 | value = value.replace(System.lineSeparator(), "\n"); 22 | } 23 | 24 | Path file = REGRESSION_TESTS_DIR.resolve(name + ".regressiontest"); 25 | String expectedValue; 26 | try { 27 | expectedValue = Files.readString(file); 28 | } catch (NoSuchFileException e) { 29 | try { 30 | Files.writeString(file, value); 31 | } catch (IOException e1) { 32 | throw new AssertionError("Failed to write regression test file", e1); 33 | } 34 | return; 35 | } catch (IOException e) { 36 | throw new AssertionError("Failed to read regression test file", e); 37 | } 38 | 39 | if (!System.lineSeparator().equals("\n")) { 40 | expectedValue = expectedValue.replace(System.lineSeparator(), "\n"); 41 | } 42 | Assertions.assertEquals(expectedValue, value); 43 | } 44 | 45 | public static void regressionTest(String name, Consumer test) { 46 | StringWriter sw = new StringWriter(); 47 | PrintWriter pw = new PrintWriter(sw); 48 | test.accept(pw); 49 | pw.flush(); 50 | regressionTest(name, sw.toString()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/net/earthcomputer/clientcommands/test/WaypointLoadingTest.java: -------------------------------------------------------------------------------- 1 | package net.earthcomputer.clientcommands.test; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 5 | import net.earthcomputer.clientcommands.command.WaypointCommand; 6 | import net.minecraft.SharedConstants; 7 | import net.minecraft.core.BlockPos; 8 | import net.minecraft.nbt.CompoundTag; 9 | import net.minecraft.nbt.TagParser; 10 | import net.minecraft.server.Bootstrap; 11 | import net.minecraft.world.level.Level; 12 | import org.junit.jupiter.api.BeforeAll; 13 | import org.junit.jupiter.api.Test; 14 | 15 | import static org.junit.jupiter.api.Assertions.*; 16 | 17 | public final class WaypointLoadingTest { 18 | @BeforeAll 19 | public static void setup() { 20 | SharedConstants.tryDetectVersion(); 21 | Bootstrap.bootStrap(); 22 | } 23 | 24 | private static CompoundTag parseSnbt(String snbt) { 25 | try { 26 | return TagParser.parseCompoundAsArgument(new StringReader(snbt)); 27 | } catch (CommandSyntaxException e) { 28 | throw new AssertionError(e); 29 | } 30 | } 31 | 32 | @Test 33 | public void testWaypointLoading() { 34 | CompoundTag waypointTag = parseSnbt(""" 35 | { 36 | DataVersion: 4189, 37 | Waypoints: { 38 | foo: { 39 | testWaypoint: { 40 | pos: [I; 1, 2, 3], 41 | Dimension: "minecraft:overworld" 42 | } 43 | } 44 | } 45 | } 46 | """); 47 | 48 | var waypoints = WaypointCommand.deserializeWaypoints(waypointTag); 49 | assertEquals(1, waypoints.size()); 50 | assertTrue(waypoints.containsKey("foo")); 51 | var worldWaypoints = waypoints.get("foo"); 52 | assertEquals(1, worldWaypoints.size()); 53 | assertTrue(worldWaypoints.containsKey("testWaypoint")); 54 | var waypoint = worldWaypoints.get("testWaypoint"); 55 | assertEquals(new BlockPos(1, 2, 3), waypoint.location()); 56 | assertEquals(Level.OVERWORLD, waypoint.dimension()); 57 | } 58 | } 59 | --------------------------------------------------------------------------------