├── .gitattributes ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .idea └── scopes │ ├── Fabric_sources.xml │ └── Forge_sources.xml ├── LICENSE ├── LICENSE_ControllableSDL ├── LICENSE_GameControllerDB ├── Project.xml ├── README.md ├── build.gradle ├── buildSrc ├── build.gradle └── src │ └── main │ └── groovy │ ├── multiloader-common.gradle │ └── multiloader-loader.gradle ├── common ├── build.gradle └── src │ └── main │ ├── java │ └── com │ │ └── mrcrayfish │ │ └── controllable │ │ ├── Config.java │ │ ├── Constants.java │ │ ├── Controllable.java │ │ ├── client │ │ ├── Action.java │ │ ├── ActionDescriptions.java │ │ ├── CameraHandler.java │ │ ├── ClientBootstrap.java │ │ ├── ControllerEvents.java │ │ ├── InputHandler.java │ │ ├── InputProcessor.java │ │ ├── RadialMenu.java │ │ ├── RumbleHandler.java │ │ ├── ScrollingHandler.java │ │ ├── VirtualCursor.java │ │ ├── binding │ │ │ ├── BindingRegistry.java │ │ │ ├── ButtonBinding.java │ │ │ ├── ButtonBindings.java │ │ │ ├── KeyAdapterBinding.java │ │ │ ├── context │ │ │ │ ├── BindingContext.java │ │ │ │ ├── GlobalContext.java │ │ │ │ ├── InGameContext.java │ │ │ │ ├── InGameWithScreenContext.java │ │ │ │ ├── InScreenContext.java │ │ │ │ └── rule │ │ │ │ │ ├── ContextRule.java │ │ │ │ │ ├── HasPlayerRule.java │ │ │ │ │ ├── HasScreenRule.java │ │ │ │ │ └── NoScreenRule.java │ │ │ └── handlers │ │ │ │ ├── ButtonHandler.java │ │ │ │ ├── EmptyHandler.java │ │ │ │ ├── MovementInputHandler.java │ │ │ │ ├── OnPressAndReleaseHandler.java │ │ │ │ ├── OnPressHandler.java │ │ │ │ ├── OnReleaseHandler.java │ │ │ │ ├── RenderingHandler.java │ │ │ │ ├── TickingHandler.java │ │ │ │ ├── action │ │ │ │ ├── BindingMovementInput.java │ │ │ │ ├── BindingOnRender.java │ │ │ │ ├── BindingOnTick.java │ │ │ │ ├── BindingPressed.java │ │ │ │ ├── BindingReleased.java │ │ │ │ └── context │ │ │ │ │ ├── Context.java │ │ │ │ │ ├── MovementInputContext.java │ │ │ │ │ └── RenderingContext.java │ │ │ │ └── impl │ │ │ │ ├── AttackHandler.java │ │ │ │ ├── DropHandler.java │ │ │ │ ├── QuickMoveHandler.java │ │ │ │ └── SneakHandler.java │ │ ├── gui │ │ │ ├── ControllerAxis.java │ │ │ ├── ControllerButton.java │ │ │ ├── ISearchable.java │ │ │ ├── Icons.java │ │ │ ├── RadialItemList.java │ │ │ ├── RadialMenuAction.java │ │ │ ├── components │ │ │ │ ├── ButtonBindingList.java │ │ │ │ ├── ControllerList.java │ │ │ │ ├── FilteredItem.java │ │ │ │ ├── FilteredTabSelectionList.java │ │ │ │ ├── TabOptionBaseItem.java │ │ │ │ ├── TabOptionEnumItem.java │ │ │ │ ├── TabOptionSliderItem.java │ │ │ │ ├── TabOptionTitleItem.java │ │ │ │ ├── TabOptionToggleItem.java │ │ │ │ └── TabSelectionList.java │ │ │ ├── navigation │ │ │ │ ├── BasicNavigationPoint.java │ │ │ │ ├── HideCursor.java │ │ │ │ ├── ListEntryNavigationPoint.java │ │ │ │ ├── ListWidgetNavigationPoint.java │ │ │ │ ├── Navigatable.java │ │ │ │ ├── NavigationPoint.java │ │ │ │ ├── SkipItem.java │ │ │ │ ├── SlotNavigationPoint.java │ │ │ │ └── WidgetNavigationPoint.java │ │ │ ├── screens │ │ │ │ ├── ButtonBindingListMenuScreen.java │ │ │ │ ├── ConfirmationScreen.java │ │ │ │ ├── ControllerLayoutScreen.java │ │ │ │ ├── KeyBindingListMenuScreen.java │ │ │ │ ├── ListMenuScreen.java │ │ │ │ ├── PendingScreen.java │ │ │ │ ├── RadialMenuAddBindingsScreen.java │ │ │ │ ├── RadialMenuConfigureScreen.java │ │ │ │ ├── SelectKeyBindingScreen.java │ │ │ │ ├── SettingsScreen.java │ │ │ │ └── ThumbstickSettingsScreen.java │ │ │ ├── toasts │ │ │ │ └── ConnectionToast.java │ │ │ └── widget │ │ │ │ ├── BackgroundStringWidget.java │ │ │ │ ├── ButtonBindingButton.java │ │ │ │ ├── ColorButton.java │ │ │ │ ├── ControllerButton.java │ │ │ │ ├── ImageButton.java │ │ │ │ ├── LazySlider.java │ │ │ │ ├── TabListWidget.java │ │ │ │ └── TabNavigationHint.java │ │ ├── input │ │ │ ├── AdaptiveControllerManager.java │ │ │ ├── ButtonStates.java │ │ │ ├── Buttons.java │ │ │ ├── Controller.java │ │ │ ├── DeviceInfo.java │ │ │ ├── InputLibrary.java │ │ │ ├── MultiController.java │ │ │ ├── glfw │ │ │ │ ├── GLFWController.java │ │ │ │ └── GLFWControllerManager.java │ │ │ └── sdl2 │ │ │ │ ├── SDL2Controller.java │ │ │ │ └── SDL2ControllerManager.java │ │ ├── overlay │ │ │ ├── ActionHintOverlay.java │ │ │ ├── IOverlay.java │ │ │ ├── OverlayRenderer.java │ │ │ ├── PaperDollPlayerOverlay.java │ │ │ ├── RadialMenuOverlay.java │ │ │ ├── RecipeBookOverlay.java │ │ │ ├── TabNavigationOverlay.java │ │ │ └── VirtualCursorOverlay.java │ │ ├── settings │ │ │ ├── ActionVisibility.java │ │ │ ├── AnalogMovement.java │ │ │ ├── ButtonIcons.java │ │ │ ├── CursorStyle.java │ │ │ ├── SettingEnum.java │ │ │ ├── SettingProvider.java │ │ │ ├── SneakMode.java │ │ │ ├── SprintMode.java │ │ │ ├── Thumbstick.java │ │ │ └── VanillaSetting.java │ │ └── util │ │ │ ├── ClientHelper.java │ │ │ ├── EventHelper.java │ │ │ ├── InputHelper.java │ │ │ ├── MouseHooks.java │ │ │ ├── MutableClientInput.java │ │ │ └── ScreenHelper.java │ │ ├── event │ │ ├── ControllerEvents.java │ │ └── Value.java │ │ ├── integration │ │ ├── ControllableJeiPlugin.java │ │ ├── EmiSupport.java │ │ ├── JeiSupport.java │ │ └── ReiSupport.java │ │ ├── mixin │ │ └── client │ │ │ ├── ClientRecipeBookMixin.java │ │ │ ├── ContainerScreenMixin.java │ │ │ ├── GameRendererMixin.java │ │ │ ├── GuiMixin.java │ │ │ ├── LocalPlayerMixin.java │ │ │ ├── MinecraftMixin.java │ │ │ ├── MouseHandlerMixin.java │ │ │ ├── MouseHelperMixin.java │ │ │ ├── OverlayRecipeComponentAccessor.java │ │ │ ├── RecipeBookComponentAccessor.java │ │ │ ├── RecipeBookPageAccessor.java │ │ │ ├── RecipeBookPageMixin.java │ │ │ ├── TimerAccessor.java │ │ │ ├── jei │ │ │ ├── GuiIconToggleButtonMixin.java │ │ │ ├── IngredientGridMixin.java │ │ │ ├── IngredientGridWithNavigationMixin.java │ │ │ ├── IngredientListOverlayMixin.java │ │ │ ├── MouseUtilMixin.java │ │ │ └── PageNavigationMixin.java │ │ │ └── rei │ │ │ └── PointHelperMixin.java │ │ ├── platform │ │ ├── ClientServices.java │ │ ├── Services.java │ │ └── services │ │ │ ├── IClientHelper.java │ │ │ └── IPlatformHelper.java │ │ └── util │ │ └── Utils.java │ └── resources │ ├── assets │ └── controllable │ │ ├── background.png │ │ ├── banner.png │ │ ├── font │ │ ├── buttons.json │ │ └── icons.json │ │ ├── icon.png │ │ ├── lang │ │ ├── en_gb.json │ │ ├── en_us.json │ │ ├── fr_fr.json │ │ └── zh_tw.json │ │ └── textures │ │ └── gui │ │ ├── buttons.png │ │ ├── controller.png │ │ ├── cursor.png │ │ ├── icons.png │ │ └── sprites │ │ └── toast │ │ └── connection.png │ ├── controllable.common.mixins.json │ ├── gamecontrollerdb.txt │ ├── mappings │ └── defender_game_racer_x7.json │ └── pack.mcmeta ├── fabric ├── build.gradle └── src │ └── main │ ├── java │ └── com │ │ └── mrcrayfish │ │ └── controllable │ │ ├── ControllableMod.java │ │ ├── client │ │ ├── ClientControllableMod.java │ │ └── util │ │ │ └── ReflectUtil.java │ │ ├── integration │ │ └── ArchitecturySupport.java │ │ ├── mixin │ │ ├── ControllableMixinPlugin.java │ │ └── client │ │ │ ├── FabricGuiMixin.java │ │ │ ├── FabricKeyMappingMixin.java │ │ │ ├── FabricMinecraftMixin.java │ │ │ └── FabricRecipeBookPageMixin.java │ │ └── platform │ │ ├── FabricClientHelper.java │ │ └── FabricPlatformHelper.java │ └── resources │ ├── META-INF │ └── services │ │ ├── com.mrcrayfish.controllable.platform.services.IClientHelper │ │ └── com.mrcrayfish.controllable.platform.services.IPlatformHelper │ ├── controllable.accesswidener │ ├── controllable.fabric.mixins.json │ └── fabric.mod.json ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── neoforge ├── build.gradle └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── mrcrayfish │ │ │ └── controllable │ │ │ ├── ControllableMod.java │ │ │ ├── client │ │ │ ├── ClientEvents.java │ │ │ ├── binding │ │ │ │ └── context │ │ │ │ │ └── NeoForgeKeyContext.java │ │ │ └── util │ │ │ │ └── ReflectUtil.java │ │ │ ├── mixin │ │ │ ├── ControllableMixinPlugin.java │ │ │ └── client │ │ │ │ ├── GuiLayerManagerMixin.java │ │ │ │ ├── NeoForgeKeyMappingMixin.java │ │ │ │ └── NeoForgeRecipeBookPageMixin.java │ │ │ └── platform │ │ │ ├── NeoForgeClientHelper.java │ │ │ └── NeoForgePlatformHelper.java │ └── resources │ │ ├── META-INF │ │ ├── accesstransformer.cfg │ │ ├── neoforge.mods.toml │ │ └── services │ │ │ ├── com.mrcrayfish.controllable.platform.services.IClientHelper │ │ │ └── com.mrcrayfish.controllable.platform.services.IPlatformHelper │ │ └── controllable.neoforge.mixins.json │ └── test │ └── java │ └── test │ └── ScreenRuleTest.java ├── release_build └── settings.gradle /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | *.bat text eol=crlf 3 | *.patch text eol=lf 4 | *.java text eol=lf 5 | *.gradle text eol=crlf 6 | *.png binary 7 | *.gif binary 8 | *.exe binary 9 | *.dll binary 10 | *.jar binary 11 | *.lzma binary 12 | *.zip binary 13 | *.pyd binary 14 | *.cfg text eol=lf 15 | *.jks binary -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # eclipse 2 | bin 3 | *.launch 4 | .settings 5 | .metadata 6 | .classpath 7 | .project 8 | 9 | # idea 10 | out 11 | *.ipr 12 | *.iws 13 | *.iml 14 | .idea/* 15 | !.idea/scopes 16 | 17 | # gradle 18 | build 19 | .gradle 20 | 21 | # other 22 | eclipse 23 | /fabric/run/ 24 | /neoforge/runs/ 25 | /forge/ 26 | -------------------------------------------------------------------------------- /.idea/scopes/Fabric_sources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/scopes/Forge_sources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 MrCrayfish 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /LICENSE_ControllableSDL: -------------------------------------------------------------------------------- 1 | This software is provided 'as-is', without any express or implied 2 | warranty. In no event will the authors be held liable for any damages 3 | arising from the use of this software. 4 | 5 | Permission is granted to anyone to use this software for any purpose, 6 | including commercial applications, and to alter it and redistribute it 7 | freely, subject to the following restrictions: 8 | 9 | 1. The origin of this software must not be misrepresented; you must not 10 | claim that you wrote the original software. If you use this software 11 | in a product, an acknowledgment in the product documentation would be 12 | appreciated but is not required. 13 | 2. Altered source versions must be plainly marked as such, and must not be 14 | misrepresented as being the original software. 15 | 3. This notice may not be removed or altered from any source distribution. -------------------------------------------------------------------------------- /LICENSE_GameControllerDB: -------------------------------------------------------------------------------- 1 | Copyright (C) 1997-2025 Sam Lantinga 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. -------------------------------------------------------------------------------- /Project.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'fabric-loom' version '1.10-SNAPSHOT' apply false 3 | id 'net.neoforged.moddev' version '2.0.73' apply false 4 | } -------------------------------------------------------------------------------- /buildSrc/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'groovy-gradle-plugin' 3 | } -------------------------------------------------------------------------------- /buildSrc/src/main/groovy/multiloader-loader.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'multiloader-common' 3 | } 4 | 5 | configurations { 6 | commonJava { 7 | canBeResolved = true 8 | } 9 | commonResources { 10 | canBeResolved = true 11 | } 12 | } 13 | 14 | dependencies { 15 | compileOnly(project(':common')) { 16 | capabilities { 17 | requireCapability "$group:$mod_id" 18 | } 19 | } 20 | commonJava project(path: ':common', configuration: 'commonJava') 21 | commonResources project(path: ':common', configuration: 'commonResources') 22 | } 23 | 24 | tasks.named('compileJava', JavaCompile) { 25 | dependsOn(configurations.commonJava) 26 | source(configurations.commonJava) 27 | } 28 | 29 | processResources { 30 | dependsOn(configurations.commonResources) 31 | from(configurations.commonResources) 32 | } 33 | 34 | tasks.named('javadoc', Javadoc).configure { 35 | dependsOn(configurations.commonJava) 36 | source(configurations.commonJava) 37 | } 38 | 39 | tasks.named("sourcesJar", Jar) { 40 | dependsOn(configurations.commonJava) 41 | from(configurations.commonJava) 42 | dependsOn(configurations.commonResources) 43 | from(configurations.commonResources) 44 | } -------------------------------------------------------------------------------- /common/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'multiloader-common' 3 | id 'net.neoforged.moddev' 4 | id 'de.undercouch.download' version '5.4.0' 5 | } 6 | 7 | neoForge { 8 | // Look for versions on https://projects.neoforged.net/neoforged/neoform 9 | neoFormVersion = neo_form_version 10 | 11 | // Automatically enable AccessTransformers if the file exists 12 | def at = file('src/main/resources/META-INF/accesstransformer.cfg') 13 | if (at.exists()) { 14 | accessTransformers.add(at.absolutePath) 15 | } 16 | } 17 | 18 | dependencies { 19 | compileOnly "com.mrcrayfish:framework-common:${minecraft_version}-${framework_version}" 20 | compileOnly "mezz.jei:jei-${jei_minecraft_version}-common:${jei_version}" 21 | compileOnly "mezz.jei:jei-${jei_minecraft_version}-gui:${jei_version}" 22 | compileOnly "mezz.jei:jei-${jei_minecraft_version}-lib:${jei_version}" 23 | compileOnly "com.mrcrayfish:controllable-sdl:${controllable_sdl_version}" 24 | compileOnly "dev.emi:emi-xplat-mojmap:${emi_version}" 25 | compileOnly "me.shedaniel:RoughlyEnoughItems-neoforge:${rei_version}" 26 | compileOnly group: 'org.spongepowered', name: 'mixin', version: '0.8.7' 27 | compileOnly group: 'io.github.llamalad7', name: 'mixinextras-common', version: '0.4.1' 28 | annotationProcessor group: 'io.github.llamalad7', name: 'mixinextras-common', version: '0.4.1' 29 | } 30 | 31 | tasks.register('downloadMappings', Download) { 32 | src 'https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt' 33 | dest 'src/main/resources/gamecontrollerdb.txt' 34 | } 35 | 36 | configurations { 37 | commonJava { 38 | canBeResolved = false 39 | canBeConsumed = true 40 | } 41 | commonResources { 42 | canBeResolved = false 43 | canBeConsumed = true 44 | } 45 | } 46 | 47 | artifacts { 48 | commonJava sourceSets.main.java.sourceDirectories.singleFile 49 | commonResources sourceSets.main.resources.sourceDirectories.singleFile 50 | } -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/Constants.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | /** 7 | * Author: MrCrayfish 8 | */ 9 | public class Constants 10 | { 11 | public static final String MOD_ID = "controllable"; 12 | public static final String MOD_NAME = "Controllable"; 13 | public static final Logger LOG = LoggerFactory.getLogger(MOD_NAME); 14 | } 15 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/Action.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client; 2 | 3 | import net.minecraft.network.chat.Component; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import java.util.Comparator; 7 | 8 | /** 9 | * Author: MrCrayfish 10 | */ 11 | public class Action implements Comparable 12 | { 13 | private Component description; 14 | private Side side; 15 | 16 | public Action(Component description, Side side) 17 | { 18 | this.description = description; 19 | this.side = side; 20 | } 21 | 22 | public Component getDescription() 23 | { 24 | return this.description; 25 | } 26 | 27 | public void setDescription(Component description) 28 | { 29 | this.description = description; 30 | } 31 | 32 | public Side getSide() 33 | { 34 | return this.side; 35 | } 36 | 37 | public void setSide(Side side) 38 | { 39 | this.side = side; 40 | } 41 | 42 | @Override 43 | public int compareTo(@NotNull Action o) 44 | { 45 | int result = this.side.compareTo(o.side); 46 | if(result == 0) 47 | { 48 | return o.description.getString().compareTo(o.description.getString()); 49 | } 50 | return result; 51 | } 52 | 53 | public enum Side 54 | { 55 | LEFT, RIGHT; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/ActionDescriptions.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client; 2 | 3 | import net.minecraft.network.chat.Component; 4 | 5 | /** 6 | * Author: MrCrayfish 7 | */ 8 | public class ActionDescriptions 9 | { 10 | public static final Component PICKUP_STACK = Component.translatable("controllable.action.pickup_stack"); 11 | public static final Component SPLIT_STACK = Component.translatable("controllable.action.split_stack"); 12 | public static final Component QUICK_MOVE = Component.translatable("controllable.action.quick_move"); 13 | public static final Component PLACE_STACK = Component.translatable("controllable.action.place_stack"); 14 | public static final Component PLACE_ITEM = Component.translatable("controllable.action.place_item"); 15 | public static final Component PERFORM_ACTION = Component.translatable("controllable.action.radial.perform_action"); 16 | public static final Component CLOSE_MENU = Component.translatable("controllable.action.radial.close_menu"); 17 | public static final Component BREAK = Component.translatable("controllable.action.break"); 18 | public static final Component ATTACK = Component.translatable("controllable.action.attack"); 19 | public static final Component EAT = Component.translatable("controllable.action.eat"); 20 | public static final Component DRINK = Component.translatable("controllable.action.drink"); 21 | public static final Component BLOCK = Component.translatable("controllable.action.block"); 22 | public static final Component PULL_BOW = Component.translatable("controllable.action.pull_bow"); 23 | public static final Component PLACE_BLOCK = Component.translatable("controllable.action.place_block"); 24 | public static final Component USE_ITEM = Component.translatable("controllable.action.use_item"); 25 | public static final Component INTERACT = Component.translatable("controllable.action.interact"); 26 | public static final Component JUMP = Component.translatable("controllable.action.jump"); 27 | public static final Component OPEN_INVENTORY = Component.translatable("controllable.action.inventory"); 28 | public static final Component CLOSE_INVENTORY = Component.translatable("controllable.action.close_inventory"); 29 | public static final Component SWAP_HANDS = Component.translatable("controllable.action.swap_hands"); 30 | public static final Component DISMOUNT = Component.translatable("controllable.action.dismount"); 31 | public static final Component SNEAK = Component.translatable("controllable.action.sneak"); 32 | public static final Component DROP_ITEM = Component.translatable("controllable.action.drop_item"); 33 | } 34 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/ClientBootstrap.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client; 2 | 3 | import com.mrcrayfish.controllable.Controllable; 4 | import com.mrcrayfish.controllable.client.overlay.OverlayRenderer; 5 | 6 | /** 7 | * Author: MrCrayfish 8 | */ 9 | public class ClientBootstrap 10 | { 11 | public static void init() 12 | { 13 | Controllable.init(); 14 | ControllerEvents.init(); 15 | OverlayRenderer.init(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/ControllerEvents.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client; 2 | 3 | import com.mrcrayfish.controllable.Controllable; 4 | import com.mrcrayfish.controllable.client.binding.ButtonBinding; 5 | import com.mrcrayfish.controllable.client.gui.screens.SettingsScreen; 6 | import com.mrcrayfish.controllable.client.gui.widget.ControllerButton; 7 | import com.mrcrayfish.controllable.client.input.Controller; 8 | import com.mrcrayfish.framework.api.event.client.FrameworkScreenEvents; 9 | import net.minecraft.client.Minecraft; 10 | import net.minecraft.client.gui.components.AbstractWidget; 11 | import net.minecraft.client.gui.components.Button; 12 | import net.minecraft.client.gui.screens.Screen; 13 | import net.minecraft.client.gui.screens.options.OptionsScreen; 14 | import net.minecraft.network.chat.Component; 15 | 16 | import java.util.List; 17 | import java.util.Optional; 18 | import java.util.function.Consumer; 19 | 20 | /** 21 | * Author: MrCrayfish 22 | */ 23 | public class ControllerEvents 24 | { 25 | public static void init() 26 | { 27 | FrameworkScreenEvents.INIT.register(ControllerEvents::onModifyScreenWidgets); 28 | } 29 | 30 | private static void onModifyScreenWidgets(Screen screen, List widgets, Consumer add, Consumer remove) 31 | { 32 | ButtonBinding.resetButtonStates(); 33 | 34 | // Fixes an issue where using item is not stopped after opening a screen 35 | Controller controller = Controllable.getController(); 36 | if(controller != null && controller.isBeingUsed()) 37 | { 38 | Minecraft mc = Minecraft.getInstance(); 39 | if(mc.gameMode != null && mc.player != null && mc.player.isUsingItem()) 40 | { 41 | mc.gameMode.releaseUsingItem(mc.player); 42 | } 43 | } 44 | 45 | if(screen instanceof OptionsScreen) 46 | { 47 | Optional btn = widgets.stream().filter(widget -> widget instanceof Button button && 48 | button.getMessage().equals(Component.translatable("options.controls"))).findFirst(); 49 | btn.ifPresent(widget -> add.accept(new ControllerButton(widget, button -> { 50 | Minecraft.getInstance().setScreen(new SettingsScreen(screen)); 51 | }))); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/KeyAdapterBinding.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding; 2 | 3 | import com.mrcrayfish.controllable.client.binding.handlers.EmptyHandler; 4 | import com.mrcrayfish.controllable.platform.ClientServices; 5 | import net.minecraft.client.KeyMapping; 6 | import net.minecraft.client.Minecraft; 7 | import net.minecraft.client.gui.screens.Screen; 8 | import org.lwjgl.glfw.GLFW; 9 | 10 | /** 11 | * A special binding that translates button presses to key presses. This binding does not need to be 12 | * registered and is added by players during runtime. 13 | * 14 | * Author: MrCrayfish 15 | */ 16 | public final class KeyAdapterBinding extends ButtonBinding 17 | { 18 | private final KeyMapping keyMapping; 19 | private final String labelKey; 20 | 21 | public KeyAdapterBinding(int button, KeyMapping mapping) 22 | { 23 | super(button, mapping.getName() + ".custom", "key.categories.controllable_custom", ClientServices.CLIENT.createBindingContext(mapping), EmptyHandler.INSTANCE); 24 | this.keyMapping = mapping; 25 | this.labelKey = mapping.getName(); 26 | } 27 | 28 | @Override 29 | public String getLabelKey() 30 | { 31 | return this.labelKey; 32 | } 33 | 34 | public KeyMapping getKeyMapping() 35 | { 36 | return this.keyMapping; 37 | } 38 | 39 | @Override 40 | protected void setPressed(boolean pressed) 41 | { 42 | boolean wasPressed = this.isButtonDown(); 43 | super.setPressed(pressed); 44 | this.keyMapping.setDown(pressed); 45 | if(!wasPressed && pressed) 46 | { 47 | this.updateKeyBindPressTime(); 48 | int key = ClientServices.CLIENT.getKeyValue(this.keyMapping); 49 | this.handlePressed(GLFW.GLFW_PRESS, key, 0); 50 | } 51 | else if(wasPressed && !pressed) 52 | { 53 | int key = ClientServices.CLIENT.getKeyValue(this.keyMapping); 54 | this.handlePressed(GLFW.GLFW_RELEASE, key, 0); 55 | } 56 | } 57 | 58 | private void updateKeyBindPressTime() 59 | { 60 | ClientServices.CLIENT.setKeyPressTime(this.keyMapping, 1); 61 | } 62 | 63 | private void handlePressed(int action, int key, int modifiers) 64 | { 65 | Screen screen = Minecraft.getInstance().screen; 66 | if(screen != null && ClientServices.CLIENT.sendScreenInput(screen, key, action, modifiers)) 67 | return; 68 | ClientServices.CLIENT.sendKeyInputEvent(key, 0, action, modifiers); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/context/GlobalContext.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.context; 2 | 3 | import com.mrcrayfish.controllable.client.binding.context.rule.ContextRule; 4 | import com.mrcrayfish.controllable.util.Utils; 5 | import net.minecraft.resources.ResourceLocation; 6 | 7 | import java.util.Set; 8 | 9 | /** 10 | * 11 | * 12 | * Author: MrCrayfish 13 | */ 14 | public class GlobalContext extends BindingContext 15 | { 16 | public static final GlobalContext INSTANCE = new GlobalContext(Utils.resource("global")); 17 | 18 | protected GlobalContext(ResourceLocation id) 19 | { 20 | super(id); 21 | } 22 | 23 | @Override 24 | protected Set createRules() 25 | { 26 | return Set.of(); 27 | } 28 | 29 | @Override 30 | public int priority() 31 | { 32 | return 0; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/context/InGameContext.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.context; 2 | 3 | import com.mrcrayfish.controllable.client.binding.context.rule.ContextRule; 4 | import com.mrcrayfish.controllable.client.binding.context.rule.HasPlayerRule; 5 | import com.mrcrayfish.controllable.client.binding.context.rule.NoScreenRule; 6 | import com.mrcrayfish.controllable.util.Utils; 7 | import net.minecraft.resources.ResourceLocation; 8 | 9 | import java.util.Set; 10 | 11 | /** 12 | * Author: MrCrayfish 13 | */ 14 | public class InGameContext extends BindingContext 15 | { 16 | public static final InGameContext INSTANCE = new InGameContext(Utils.resource("in_game")); 17 | 18 | protected InGameContext(ResourceLocation id) 19 | { 20 | super(id); 21 | } 22 | 23 | @Override 24 | public Set createRules() 25 | { 26 | return Set.of(NoScreenRule.get(), HasPlayerRule.get()); 27 | } 28 | 29 | @Override 30 | public int priority() 31 | { 32 | return 0; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/context/InGameWithScreenContext.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.context; 2 | 3 | import com.mrcrayfish.controllable.client.binding.context.rule.ContextRule; 4 | import com.mrcrayfish.controllable.client.binding.context.rule.HasPlayerRule; 5 | import com.mrcrayfish.controllable.client.binding.context.rule.HasScreenRule; 6 | import com.mrcrayfish.controllable.util.Utils; 7 | import net.minecraft.resources.ResourceLocation; 8 | 9 | import java.util.Set; 10 | 11 | /** 12 | * Author: MrCrayfish 13 | */ 14 | public class InGameWithScreenContext extends InScreenContext 15 | { 16 | public static final InGameWithScreenContext INSTANCE = new InGameWithScreenContext(Utils.resource("in_game_with_screen")); 17 | 18 | protected InGameWithScreenContext(ResourceLocation id) 19 | { 20 | super(id); 21 | } 22 | 23 | @Override 24 | public Set createRules() 25 | { 26 | return Set.of(HasScreenRule.any(), HasPlayerRule.get()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/context/InScreenContext.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.context; 2 | 3 | import com.mrcrayfish.controllable.client.binding.context.rule.ContextRule; 4 | import com.mrcrayfish.controllable.client.binding.context.rule.HasScreenRule; 5 | import com.mrcrayfish.controllable.util.Utils; 6 | import net.minecraft.resources.ResourceLocation; 7 | 8 | import java.util.Set; 9 | 10 | /** 11 | * Author: MrCrayfish 12 | */ 13 | public class InScreenContext extends BindingContext 14 | { 15 | public static final InScreenContext INSTANCE = new InScreenContext(Utils.resource("in_screen")); 16 | 17 | protected InScreenContext(ResourceLocation id) 18 | { 19 | super(id); 20 | } 21 | 22 | @Override 23 | public Set createRules() 24 | { 25 | return Set.of(HasScreenRule.any()); 26 | } 27 | 28 | @Override 29 | public int priority() 30 | { 31 | return 0; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/context/rule/ContextRule.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.context.rule; 2 | 3 | /** 4 | * Author: MrCrayfish 5 | */ 6 | public abstract sealed class ContextRule permits HasPlayerRule, NoScreenRule, HasScreenRule 7 | { 8 | public abstract boolean isActive(); 9 | 10 | public abstract boolean matches(ContextRule other); 11 | } 12 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/context/rule/HasPlayerRule.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.context.rule; 2 | 3 | import net.minecraft.client.Minecraft; 4 | 5 | /** 6 | * Author: MrCrayfish 7 | */ 8 | public final class HasPlayerRule extends ContextRule 9 | { 10 | private static final HasPlayerRule INSTANCE = new HasPlayerRule(); 11 | 12 | private HasPlayerRule() {} 13 | 14 | @Override 15 | public boolean isActive() 16 | { 17 | Minecraft mc = Minecraft.getInstance(); 18 | return mc.player != null; 19 | } 20 | 21 | @Override 22 | public boolean matches(ContextRule other) 23 | { 24 | return other == INSTANCE; 25 | } 26 | 27 | public static HasPlayerRule get() 28 | { 29 | return INSTANCE; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/context/rule/HasScreenRule.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.context.rule; 2 | 3 | import net.minecraft.client.Minecraft; 4 | import net.minecraft.client.gui.screens.Screen; 5 | 6 | import java.util.Collections; 7 | import java.util.Set; 8 | 9 | /** 10 | * Author: MrCrayfish 11 | */ 12 | public final class HasScreenRule extends ContextRule 13 | { 14 | private final Set> validScreens; 15 | 16 | private HasScreenRule(Set> validScreens) 17 | { 18 | this.validScreens = Set.copyOf(validScreens); 19 | } 20 | 21 | @Override 22 | public boolean isActive() 23 | { 24 | Minecraft mc = Minecraft.getInstance(); 25 | return mc.screen != null && (this.validScreens.isEmpty() || this.validScreens.contains(mc.screen.getClass())); 26 | } 27 | 28 | @Override 29 | public boolean matches(ContextRule other) 30 | { 31 | if(!(other instanceof HasScreenRule that)) 32 | return false; 33 | if(this.validScreens.isEmpty()) 34 | return true; 35 | if(that.validScreens.isEmpty()) 36 | return true; 37 | return !Collections.disjoint(this.validScreens, that.validScreens); 38 | } 39 | 40 | public static HasScreenRule any() 41 | { 42 | return new HasScreenRule(Set.of()); 43 | } 44 | 45 | public static HasScreenRule of(Set> screenClasses) 46 | { 47 | return new HasScreenRule(screenClasses); 48 | } 49 | 50 | @Override 51 | public int hashCode() 52 | { 53 | // Enforces that only one type of screen rule can be added to binding contexts 54 | return HasScreenRule.class.hashCode(); 55 | } 56 | 57 | @Override 58 | public boolean equals(Object obj) 59 | { 60 | // Enforces that only one type of screen rule can be added to binding contexts 61 | return obj != null && obj.getClass() == HasScreenRule.class; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/context/rule/NoScreenRule.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.context.rule; 2 | 3 | import net.minecraft.client.Minecraft; 4 | 5 | /** 6 | * Author: MrCrayfish 7 | */ 8 | public final class NoScreenRule extends ContextRule 9 | { 10 | private static final NoScreenRule INSTANCE = new NoScreenRule(); 11 | 12 | private NoScreenRule() {} 13 | 14 | @Override 15 | public boolean isActive() 16 | { 17 | return Minecraft.getInstance().screen == null; 18 | } 19 | 20 | @Override 21 | public boolean matches(ContextRule other) 22 | { 23 | return other == INSTANCE; 24 | } 25 | 26 | public static NoScreenRule get() 27 | { 28 | return INSTANCE; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/ButtonHandler.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers; 2 | 3 | /** 4 | * Author: MrCrayfish 5 | */ 6 | public sealed class ButtonHandler permits MovementInputHandler, OnPressAndReleaseHandler, OnPressHandler, OnReleaseHandler, RenderingHandler, TickingHandler 7 | { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/EmptyHandler.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers; 2 | 3 | import com.mrcrayfish.controllable.client.binding.handlers.action.context.Context; 4 | 5 | import java.util.Optional; 6 | 7 | /** 8 | * Author: MrCrayfish 9 | */ 10 | public final class EmptyHandler extends OnPressHandler 11 | { 12 | public static final EmptyHandler INSTANCE = new EmptyHandler(); 13 | 14 | private EmptyHandler() {} 15 | 16 | @Override 17 | public Optional createPressedHandler(Context context) 18 | { 19 | return Optional.of(() -> {}); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/MovementInputHandler.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers; 2 | 3 | import com.mrcrayfish.controllable.client.binding.handlers.action.context.Context; 4 | import com.mrcrayfish.controllable.client.binding.handlers.action.context.MovementInputContext; 5 | import com.mrcrayfish.controllable.client.binding.handlers.action.BindingMovementInput; 6 | import com.mrcrayfish.controllable.client.binding.handlers.action.BindingPressed; 7 | 8 | import java.util.Optional; 9 | import java.util.function.Consumer; 10 | 11 | /** 12 | * Author: MrCrayfish 13 | */ 14 | public abstract non-sealed class MovementInputHandler extends ButtonHandler implements BindingPressed, BindingMovementInput 15 | { 16 | @Override 17 | public Optional createPressedHandler(Context context) 18 | { 19 | // Must capture on pressed for movement input to be run 20 | return Optional.of(() -> {}); 21 | } 22 | 23 | public static MovementInputHandler create(Consumer inputHandler) 24 | { 25 | return new MovementInputHandler() 26 | { 27 | @Override 28 | public void handleMovementInput(MovementInputContext context) 29 | { 30 | inputHandler.accept(context); 31 | } 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/OnPressAndReleaseHandler.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers; 2 | 3 | import com.mrcrayfish.controllable.client.binding.handlers.action.context.Context; 4 | import com.mrcrayfish.controllable.client.binding.handlers.action.BindingPressed; 5 | import com.mrcrayfish.controllable.client.binding.handlers.action.BindingReleased; 6 | 7 | import java.util.Optional; 8 | import java.util.function.Function; 9 | 10 | /** 11 | * Author: MrCrayfish 12 | */ 13 | public abstract non-sealed class OnPressAndReleaseHandler extends ButtonHandler implements BindingPressed, BindingReleased 14 | { 15 | public static OnPressAndReleaseHandler create(Function> pressHandler, Function releaseHandler) 16 | { 17 | return new OnPressAndReleaseHandler() 18 | { 19 | @Override 20 | public Optional createPressedHandler(Context context) 21 | { 22 | return pressHandler.apply(context); 23 | } 24 | 25 | @Override 26 | public boolean handleReleased(Context context) 27 | { 28 | return releaseHandler.apply(context); 29 | } 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/OnPressHandler.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers; 2 | 3 | import com.mrcrayfish.controllable.client.binding.handlers.action.context.Context; 4 | import com.mrcrayfish.controllable.client.binding.handlers.action.BindingPressed; 5 | 6 | import java.util.Optional; 7 | import java.util.function.Function; 8 | 9 | /** 10 | * Author: MrCrayfish 11 | */ 12 | public abstract non-sealed class OnPressHandler extends ButtonHandler implements BindingPressed 13 | { 14 | public static OnPressHandler create(Function> handler) 15 | { 16 | return new OnPressHandler() 17 | { 18 | @Override 19 | public Optional createPressedHandler(Context context) 20 | { 21 | return handler.apply(context); 22 | } 23 | }; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/OnReleaseHandler.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers; 2 | 3 | import com.mrcrayfish.controllable.client.binding.handlers.action.BindingPressed; 4 | import com.mrcrayfish.controllable.client.binding.handlers.action.context.Context; 5 | import com.mrcrayfish.controllable.client.binding.handlers.action.BindingReleased; 6 | 7 | import java.util.Optional; 8 | import java.util.function.Function; 9 | 10 | /** 11 | * Author: MrCrayfish 12 | */ 13 | public abstract non-sealed class OnReleaseHandler extends ButtonHandler implements BindingPressed, BindingReleased 14 | { 15 | @Override 16 | public Optional createPressedHandler(Context context) 17 | { 18 | return Optional.of(() -> {}); 19 | } 20 | 21 | public static OnReleaseHandler create(Function handler) 22 | { 23 | return new OnReleaseHandler() 24 | { 25 | @Override 26 | public boolean handleReleased(Context context) 27 | { 28 | return handler.apply(context); 29 | } 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/RenderingHandler.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers; 2 | 3 | import com.mrcrayfish.controllable.client.binding.handlers.action.BindingPressed; 4 | import com.mrcrayfish.controllable.client.binding.handlers.action.context.Context; 5 | import com.mrcrayfish.controllable.client.binding.handlers.action.context.RenderingContext; 6 | import com.mrcrayfish.controllable.client.binding.handlers.action.BindingOnRender; 7 | 8 | import java.util.Optional; 9 | import java.util.function.Consumer; 10 | 11 | /** 12 | * Author: MrCrayfish 13 | */ 14 | public abstract non-sealed class RenderingHandler extends ButtonHandler implements BindingPressed, BindingOnRender 15 | { 16 | @Override 17 | public Optional createPressedHandler(Context context) 18 | { 19 | return Optional.of(() -> {}); 20 | } 21 | 22 | public static RenderingHandler create(RenderPhase phase, Consumer handler) 23 | { 24 | return new RenderingHandler() 25 | { 26 | @Override 27 | public RenderPhase phase() 28 | { 29 | return phase; 30 | } 31 | 32 | @Override 33 | public void handleRender(RenderingContext context) 34 | { 35 | handler.accept(context); 36 | } 37 | }; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/TickingHandler.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers; 2 | 3 | import com.mrcrayfish.controllable.client.binding.handlers.action.BindingPressed; 4 | import com.mrcrayfish.controllable.client.binding.handlers.action.context.Context; 5 | import com.mrcrayfish.controllable.client.binding.handlers.action.BindingOnTick; 6 | 7 | import java.util.Optional; 8 | import java.util.function.Function; 9 | 10 | /** 11 | * Author: MrCrayfish 12 | */ 13 | public abstract non-sealed class TickingHandler extends ButtonHandler implements BindingPressed, BindingOnTick 14 | { 15 | @Override 16 | public Optional createPressedHandler(Context context) 17 | { 18 | return Optional.of(() -> {}); 19 | } 20 | 21 | public static TickingHandler create(TickPhase phase, Function handler) 22 | { 23 | return new TickingHandler() 24 | { 25 | @Override 26 | public TickPhase phase() 27 | { 28 | return phase; 29 | } 30 | 31 | @Override 32 | public void handleTick(Context context) 33 | { 34 | handler.apply(context); 35 | } 36 | }; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/action/BindingMovementInput.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers.action; 2 | 3 | import com.mrcrayfish.controllable.client.binding.handlers.action.context.MovementInputContext; 4 | 5 | /** 6 | * Author: MrCrayfish 7 | */ 8 | public interface BindingMovementInput 9 | { 10 | void handleMovementInput(MovementInputContext context); 11 | } 12 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/action/BindingOnRender.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers.action; 2 | 3 | import com.mrcrayfish.controllable.client.binding.handlers.action.context.RenderingContext; 4 | 5 | /** 6 | * Author: MrCrayfish 7 | */ 8 | public interface BindingOnRender 9 | { 10 | RenderPhase phase(); 11 | 12 | void handleRender(RenderingContext context); 13 | 14 | enum RenderPhase 15 | { 16 | START_RENDER, 17 | END_RENDER 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/action/BindingOnTick.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers.action; 2 | 3 | import com.mrcrayfish.controllable.client.binding.handlers.action.context.Context; 4 | 5 | /** 6 | * Author: MrCrayfish 7 | */ 8 | public interface BindingOnTick 9 | { 10 | TickPhase phase(); 11 | 12 | void handleTick(Context context); 13 | 14 | enum TickPhase 15 | { 16 | START_CLIENT, 17 | END_CLIENT, 18 | START_PLAYER, 19 | END_PLAYER 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/action/BindingPressed.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers.action; 2 | 3 | import com.mrcrayfish.controllable.client.binding.handlers.action.context.Context; 4 | 5 | import java.util.Optional; 6 | 7 | /** 8 | * Author: MrCrayfish 9 | */ 10 | public interface BindingPressed 11 | { 12 | /** 13 | * Creates a runnable to execute or return empty if unable to run action. Returning a non-empty 14 | * optional indicates that the runnable be executed. For example, 15 | * 16 | * @param context 17 | * @return 18 | */ 19 | Optional createPressedHandler(Context context); 20 | } 21 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/action/BindingReleased.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers.action; 2 | 3 | import com.mrcrayfish.controllable.client.binding.handlers.action.context.Context; 4 | 5 | /** 6 | * Author: MrCrayfish 7 | */ 8 | public interface BindingReleased 9 | { 10 | boolean handleReleased(Context context); 11 | } 12 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/action/context/Context.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers.action.context; 2 | 3 | import com.mrcrayfish.controllable.client.binding.ButtonBinding; 4 | import com.mrcrayfish.controllable.client.input.Controller; 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.client.gui.screens.Screen; 7 | import net.minecraft.client.player.LocalPlayer; 8 | import net.minecraft.world.level.Level; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import java.util.Optional; 12 | 13 | /** 14 | * Author: MrCrayfish 15 | */ 16 | public class Context 17 | { 18 | private final ButtonBinding binding; 19 | private final Controller controller; 20 | private final Minecraft minecraft; 21 | private final @Nullable LocalPlayer player; 22 | private final @Nullable Level level; 23 | private final @Nullable Screen screen; 24 | private final boolean simulated; 25 | 26 | public Context(ButtonBinding binding, Controller controller, Minecraft minecraft, @Nullable LocalPlayer player, @Nullable Level level, @Nullable Screen screen, boolean simulated) 27 | { 28 | this.binding = binding; 29 | this.controller = controller; 30 | this.minecraft = minecraft; 31 | this.player = player; 32 | this.level = level; 33 | this.screen = screen; 34 | this.simulated = simulated; 35 | } 36 | 37 | public ButtonBinding binding() 38 | { 39 | return this.binding; 40 | } 41 | 42 | public Controller controller() 43 | { 44 | return this.controller; 45 | } 46 | 47 | public Minecraft minecraft() 48 | { 49 | return this.minecraft; 50 | } 51 | 52 | public Optional player() 53 | { 54 | return Optional.ofNullable(this.player); 55 | } 56 | 57 | public Optional level() 58 | { 59 | return Optional.ofNullable(this.level); 60 | } 61 | 62 | public Optional screen() 63 | { 64 | return Optional.ofNullable(this.screen); 65 | } 66 | 67 | public boolean simulated() 68 | { 69 | return this.simulated; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/action/context/MovementInputContext.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers.action.context; 2 | 3 | import com.mrcrayfish.controllable.client.binding.ButtonBinding; 4 | import com.mrcrayfish.controllable.client.input.Controller; 5 | import com.mrcrayfish.controllable.client.util.MutableClientInput; 6 | import net.minecraft.client.Minecraft; 7 | import net.minecraft.client.gui.screens.Screen; 8 | import net.minecraft.client.player.ClientInput; 9 | import net.minecraft.client.player.LocalPlayer; 10 | import net.minecraft.world.level.Level; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | /** 14 | * Author: MrCrayfish 15 | */ 16 | public class MovementInputContext extends Context 17 | { 18 | private final ClientInput input; 19 | private final MutableClientInput mutableInput; 20 | 21 | public MovementInputContext(ButtonBinding binding, Controller controller, Minecraft minecraft, @Nullable LocalPlayer player, @Nullable Level level, @Nullable Screen screen, boolean simulated, ClientInput input) 22 | { 23 | super(binding, controller, minecraft, player, level, screen, simulated); 24 | this.input = input; 25 | this.mutableInput = new MutableClientInput(input); 26 | } 27 | 28 | public ClientInput input() 29 | { 30 | return this.input; 31 | } 32 | 33 | public MutableClientInput mutableInput() 34 | { 35 | return this.mutableInput; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/action/context/RenderingContext.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers.action.context; 2 | 3 | import com.mrcrayfish.controllable.client.binding.ButtonBinding; 4 | import com.mrcrayfish.controllable.client.input.Controller; 5 | import net.minecraft.client.DeltaTracker; 6 | import net.minecraft.client.Minecraft; 7 | import net.minecraft.client.gui.screens.Screen; 8 | import net.minecraft.client.player.LocalPlayer; 9 | import net.minecraft.world.level.Level; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | /** 13 | * Author: MrCrayfish 14 | */ 15 | public class RenderingContext extends Context 16 | { 17 | private final DeltaTracker tracker; 18 | 19 | public RenderingContext(ButtonBinding binding, Controller controller, Minecraft minecraft, @Nullable LocalPlayer player, @Nullable Level level, @Nullable Screen screen, boolean simulated, DeltaTracker tracker) 20 | { 21 | super(binding, controller, minecraft, player, level, screen, simulated); 22 | this.tracker = tracker; 23 | } 24 | 25 | public DeltaTracker tracker() 26 | { 27 | return this.tracker; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/impl/AttackHandler.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers.impl; 2 | 3 | import com.mrcrayfish.controllable.client.binding.handlers.OnPressHandler; 4 | import com.mrcrayfish.controllable.client.binding.handlers.action.context.Context; 5 | import com.mrcrayfish.controllable.platform.ClientServices; 6 | 7 | import java.util.Optional; 8 | 9 | /** 10 | * Author: MrCrayfish 11 | */ 12 | public class AttackHandler extends OnPressHandler 13 | { 14 | private static boolean preventContinue; 15 | 16 | @Override 17 | public Optional createPressedHandler(Context context) 18 | { 19 | return Optional.of(() -> { 20 | context.player().ifPresent(player -> { 21 | if(!player.isUsingItem()) { 22 | preventContinue = ClientServices.CLIENT.startAttack(context.minecraft()); 23 | } 24 | }); 25 | }); 26 | } 27 | 28 | public static boolean shouldPreventContinue() 29 | { 30 | boolean result = preventContinue; 31 | preventContinue = false; 32 | return result; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/impl/DropHandler.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers.impl; 2 | 3 | import com.mrcrayfish.controllable.client.binding.handlers.TickingHandler; 4 | import com.mrcrayfish.controllable.client.binding.handlers.action.BindingReleased; 5 | import com.mrcrayfish.controllable.client.binding.handlers.action.context.Context; 6 | 7 | import java.util.Optional; 8 | 9 | /** 10 | * Author: MrCrayfish 11 | */ 12 | public final class DropHandler extends TickingHandler implements BindingReleased 13 | { 14 | private int timer; 15 | private boolean dropped; 16 | 17 | @Override 18 | public TickPhase phase() 19 | { 20 | return TickPhase.START_CLIENT; 21 | } 22 | 23 | @Override 24 | public void handleTick(Context context) 25 | { 26 | if(context.screen().isEmpty()) 27 | { 28 | context.controller().updateInputTime(); 29 | if(!this.dropped && this.timer++ >= 10) 30 | { 31 | this.dropped = true; 32 | context.player().ifPresent(player -> { 33 | if(!player.isSpectator()) { 34 | player.drop(true); 35 | } 36 | }); 37 | } 38 | } 39 | } 40 | 41 | @Override 42 | public Optional createPressedHandler(Context context) 43 | { 44 | return Optional.of(() -> { 45 | if(context.screen().isEmpty()) { 46 | this.timer = 0; 47 | this.dropped = false; 48 | } 49 | }); 50 | } 51 | 52 | @Override 53 | public boolean handleReleased(Context context) 54 | { 55 | if(context.screen().isEmpty()) 56 | { 57 | if(this.timer < 10) 58 | { 59 | this.dropped = true; 60 | context.player().ifPresent(player -> { 61 | player.drop(false); 62 | }); 63 | context.controller().updateInputTime(); 64 | return true; 65 | } 66 | } 67 | return false; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/impl/QuickMoveHandler.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers.impl; 2 | 3 | import com.mrcrayfish.controllable.client.binding.handlers.OnPressHandler; 4 | import com.mrcrayfish.controllable.client.binding.handlers.action.context.Context; 5 | import com.mrcrayfish.controllable.client.util.MouseHooks; 6 | import org.lwjgl.glfw.GLFW; 7 | 8 | import java.util.Optional; 9 | 10 | /** 11 | * Author: MrCrayfish 12 | */ 13 | public class QuickMoveHandler extends OnPressHandler 14 | { 15 | @Override 16 | public Optional createPressedHandler(Context context) 17 | { 18 | return context.screen().map(screen -> () -> { 19 | context.player().ifPresent(player -> { 20 | if(player.inventoryMenu.getCarried().isEmpty()) { 21 | MouseHooks.invokeMouseClick(screen, GLFW.GLFW_MOUSE_BUTTON_LEFT); 22 | } else { 23 | MouseHooks.invokeMouseReleased(screen, GLFW.GLFW_MOUSE_BUTTON_RIGHT); 24 | } 25 | }); 26 | }); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/binding/handlers/impl/SneakHandler.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.handlers.impl; 2 | 3 | import com.mrcrayfish.controllable.client.binding.handlers.MovementInputHandler; 4 | import com.mrcrayfish.controllable.client.binding.handlers.action.context.Context; 5 | import com.mrcrayfish.controllable.client.binding.handlers.action.context.MovementInputContext; 6 | 7 | import java.util.Optional; 8 | 9 | /** 10 | * Author: MrCrayfish 11 | */ 12 | public class SneakHandler extends MovementInputHandler 13 | { 14 | @Override 15 | public Optional createPressedHandler(Context context) 16 | { 17 | return Optional.of(() -> 18 | { 19 | if(context.minecraft().options.toggleCrouch().get()) 20 | { 21 | context.minecraft().options.keyShift.setDown(true); 22 | } 23 | }); 24 | } 25 | 26 | @Override 27 | public void handleMovementInput(MovementInputContext context) 28 | { 29 | if(!context.minecraft().options.toggleCrouch().get()) 30 | { 31 | context.mutableInput().setShift(true); 32 | context.controller().updateInputTime(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/ControllerAxis.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui; 2 | 3 | import com.mrcrayfish.controllable.Controllable; 4 | import com.mrcrayfish.controllable.client.gui.screens.ControllerLayoutScreen; 5 | import com.mrcrayfish.controllable.client.input.Controller; 6 | import net.minecraft.client.gui.GuiGraphics; 7 | 8 | /** 9 | * Author: MrCrayfish 10 | */ 11 | public class ControllerAxis extends ControllerButton 12 | { 13 | public ControllerAxis(ControllerLayoutScreen screen, int button, int x, int y, int u, int v, int width, int height, int scale) 14 | { 15 | super(screen, button, x, y, u, v, width, height, scale); 16 | } 17 | 18 | @Override 19 | public void draw(GuiGraphics graphics, int x, int y, int mouseX, int mouseY, boolean selected) 20 | { 21 | graphics.pose().pushMatrix(); 22 | Controller controller = Controllable.getController(); 23 | if(controller != null) 24 | { 25 | /*GLFWGamepadState gamepad = controller.getGamepadState(); 26 | switch(this.button) 27 | { 28 | case Buttons.LEFT_THUMB_STICK -> 29 | { 30 | float leftX = gamepad.axes(this.screen.getEntry().isThumbsticksSwitched() ? GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X : GLFW.GLFW_GAMEPAD_AXIS_LEFT_X); 31 | float leftY = gamepad.axes(this.screen.getEntry().isThumbsticksSwitched() ? GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y : GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y); 32 | leftX *= this.screen.getEntry().isFlipLeftX() ? -1 : 1; 33 | leftY *= this.screen.getEntry().isFlipLeftY() ? -1 : 1; 34 | poseStack.translate(leftX * 5, leftY * 5, 0); 35 | } 36 | case Buttons.RIGHT_THUMB_STICK -> 37 | { 38 | float rightX = gamepad.axes(this.screen.getEntry().isThumbsticksSwitched() ? GLFW.GLFW_GAMEPAD_AXIS_LEFT_X : GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X); 39 | float rightY = gamepad.axes(this.screen.getEntry().isThumbsticksSwitched() ? GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y : GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y); 40 | rightX *= this.screen.getEntry().isFlipRightX() ? -1 : 1; 41 | rightY *= this.screen.getEntry().isFlipRightY() ? -1 : 1; 42 | poseStack.translate(rightX * 5, rightY * 5, 0); 43 | } 44 | }*/ 45 | 46 | if(!this.screen.isButtonPressed(this.button)) 47 | { 48 | graphics.pose().translate(0, -5); 49 | } 50 | } 51 | super.draw(graphics, x, y, mouseX, mouseY, selected); 52 | graphics.pose().popMatrix(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/ControllerButton.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui; 2 | 3 | import com.mojang.blaze3d.systems.RenderSystem; 4 | import com.mrcrayfish.controllable.Controllable; 5 | import com.mrcrayfish.controllable.client.gui.screens.ControllerLayoutScreen; 6 | import com.mrcrayfish.controllable.client.input.Controller; 7 | import net.minecraft.client.gui.GuiGraphics; 8 | import net.minecraft.client.renderer.RenderPipelines; 9 | import net.minecraft.client.renderer.RenderType; 10 | 11 | /** 12 | * Author: MrCrayfish 13 | */ 14 | public class ControllerButton 15 | { 16 | protected ControllerLayoutScreen screen; 17 | protected int button; 18 | private final int x, y; 19 | private final int u, v; 20 | private final int width, height; 21 | private final int scale; 22 | private boolean hovered; 23 | 24 | public ControllerButton(ControllerLayoutScreen screen, int button, int x, int y, int u, int v, int width, int height, int scale) 25 | { 26 | this.screen = screen; 27 | this.button = button; 28 | this.x = x; 29 | this.y = y; 30 | this.u = u; 31 | this.v = v; 32 | this.width = width; 33 | this.height = height; 34 | this.scale = scale; 35 | } 36 | 37 | public void draw(GuiGraphics graphics, int x, int y, int mouseX, int mouseY, boolean selected) 38 | { 39 | int buttonU = this.u; 40 | int buttonV = this.v; 41 | int buttonX = x + this.x * this.scale; 42 | int buttonY = y + this.y * this.scale; 43 | int buttonWidth = this.width * this.scale; 44 | int buttonHeight = this.height * this.scale; 45 | Controller controller = Controllable.getController(); 46 | this.hovered = mouseX >= buttonX && mouseY >= buttonY && mouseX < buttonX + buttonWidth && mouseY < buttonY + buttonHeight; 47 | if(this.hovered) 48 | { 49 | buttonV += this.height * 2; 50 | } 51 | else if(controller != null && this.screen.isButtonPressed(this.button) || selected) 52 | { 53 | buttonV += this.height; 54 | } 55 | graphics.blit(RenderPipelines.GUI_TEXTURED, ControllerLayoutScreen.TEXTURE, buttonX, buttonY, buttonU, buttonV, this.width * this.scale, this.height * this.scale, this.width, this.height, 256, 256); 56 | } 57 | 58 | public int getButton() 59 | { 60 | return this.button; 61 | } 62 | 63 | public boolean isHovered() 64 | { 65 | return this.hovered; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/ISearchable.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui; 2 | 3 | import net.minecraft.network.chat.Component; 4 | 5 | /** 6 | * Author: MrCrayfish 7 | */ 8 | public interface ISearchable 9 | { 10 | Component getLabel(); 11 | } 12 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/Icons.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui; 2 | 3 | import com.mrcrayfish.controllable.util.Utils; 4 | import net.minecraft.resources.ResourceLocation; 5 | 6 | /** 7 | * Author: MrCrayfish 8 | */ 9 | public enum Icons 10 | { 11 | CONTROLLER, 12 | SETTINGS, 13 | BINDINGS, 14 | ADD, 15 | RESET, 16 | WORLD, 17 | DOWNLOAD, 18 | CROSS, 19 | SAVE, 20 | KEY_CAP, 21 | LINK, 22 | INFO; 23 | 24 | public static final ResourceLocation TEXTURE = Utils.resource("textures/gui/icons.png"); 25 | public static final int TEXTURE_WIDTH = Icons.values().length * 11; 26 | public static final int TEXTURE_HEIGHT = 11; 27 | } 28 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/RadialMenuAction.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui; 2 | 3 | import com.mrcrayfish.controllable.client.binding.ButtonBinding; 4 | import net.minecraft.ChatFormatting; 5 | 6 | /** 7 | * Author: MrCrayfish 8 | */ 9 | public class RadialMenuAction 10 | { 11 | private final ButtonBinding binding; 12 | private ChatFormatting color; 13 | 14 | public RadialMenuAction(ButtonBinding binding, ChatFormatting color) 15 | { 16 | this.binding = binding; 17 | this.color = color; 18 | } 19 | 20 | public ButtonBinding getBinding() 21 | { 22 | return binding; 23 | } 24 | 25 | public ChatFormatting getColor() 26 | { 27 | return color; 28 | } 29 | 30 | public void setColor(ChatFormatting color) 31 | { 32 | this.color = color; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/components/FilteredItem.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.components; 2 | 3 | public interface FilteredItem 4 | { 5 | boolean isVisible(); 6 | } 7 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/components/FilteredTabSelectionList.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.components; 2 | 3 | import net.minecraft.client.Minecraft; 4 | import net.minecraft.client.gui.components.ContainerObjectSelectionList; 5 | 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | 9 | public class FilteredTabSelectionList> extends TabSelectionList 10 | { 11 | private final List original = new LinkedList<>(); 12 | 13 | public FilteredTabSelectionList(Minecraft mc, int itemHeight) 14 | { 15 | super(mc, itemHeight); 16 | } 17 | 18 | public void rebuildList(boolean scroll) 19 | { 20 | List newEntries = this.original.stream().filter(e -> { 21 | if(e instanceof FilteredItem item) { 22 | return item.isVisible(); 23 | } 24 | return true; 25 | }).toList(); 26 | this.replaceEntries(newEntries); 27 | if(scroll) 28 | { 29 | this.setScrollAmount(this.maxScrollAmount()); 30 | } 31 | } 32 | 33 | @Override 34 | public int addEntry(E entry) 35 | { 36 | this.original.add(entry); 37 | return super.addEntry(entry); 38 | } 39 | 40 | @Override 41 | protected void addEntryToTop(E entry) 42 | { 43 | this.original.addFirst(entry); 44 | super.addEntryToTop(entry); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/components/TabOptionTitleItem.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.components; 2 | 3 | import com.mrcrayfish.controllable.client.gui.navigation.SkipItem; 4 | import com.mrcrayfish.controllable.client.util.ScreenHelper; 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.client.gui.Font; 7 | import net.minecraft.client.gui.GuiGraphics; 8 | import net.minecraft.client.gui.components.events.GuiEventListener; 9 | import net.minecraft.client.gui.narration.NarratableEntry; 10 | import net.minecraft.network.chat.Component; 11 | 12 | import java.util.Collections; 13 | import java.util.List; 14 | 15 | /** 16 | * Author: MrCrayfish 17 | */ 18 | public class TabOptionTitleItem extends TabOptionBaseItem implements SkipItem 19 | { 20 | public TabOptionTitleItem(Component label) 21 | { 22 | super(label); 23 | } 24 | 25 | @Override 26 | public void render(GuiGraphics graphics, int x, int top, int left, int width, int height, int mouseX, int mouseY, boolean selected, float partialTicks) 27 | { 28 | Font font = Minecraft.getInstance().font; 29 | int labelWidth = font.width(this.label) + 2; 30 | ScreenHelper.drawRoundedBox(graphics, left + width / 2 - labelWidth / 2, top + 2, labelWidth, 14, 0x88000000); 31 | graphics.drawCenteredString(font, this.label, left + width / 2, top + 5, 0xFFFFFFFF); 32 | } 33 | 34 | @Override 35 | public List narratables() 36 | { 37 | return Collections.emptyList(); 38 | } 39 | 40 | @Override 41 | public List children() 42 | { 43 | return Collections.emptyList(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/navigation/BasicNavigationPoint.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.navigation; 2 | 3 | /** 4 | * Author: MrCrayfish 5 | */ 6 | public class BasicNavigationPoint extends NavigationPoint 7 | { 8 | public BasicNavigationPoint(double x, double y) 9 | { 10 | super(x, y, Type.BASIC); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/navigation/HideCursor.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.navigation; 2 | 3 | /** 4 | * Author: MrCrayfish 5 | */ 6 | public interface HideCursor 7 | { 8 | } 9 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/navigation/ListWidgetNavigationPoint.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.navigation; 2 | 3 | import com.mrcrayfish.controllable.platform.ClientServices; 4 | import net.minecraft.client.gui.components.AbstractButton; 5 | import net.minecraft.client.gui.components.AbstractSelectionList; 6 | import net.minecraft.client.gui.components.AbstractWidget; 7 | import net.minecraft.client.gui.components.Button; 8 | import net.minecraft.client.gui.components.TabButton; 9 | import net.minecraft.client.gui.components.events.GuiEventListener; 10 | 11 | /** 12 | * Author: MrCrayfish 13 | */ 14 | @SuppressWarnings("IntegerDivisionInFloatingPointContext") 15 | public class ListWidgetNavigationPoint extends NavigationPoint 16 | { 17 | private final AbstractWidget widget; 18 | private final AbstractSelectionList list; 19 | private final GuiEventListener listEntry; 20 | 21 | public ListWidgetNavigationPoint(AbstractWidget widget, AbstractSelectionList list, GuiEventListener listEntry) 22 | { 23 | super(0, 0, Type.WIDGET); 24 | this.widget = widget; 25 | this.list = list; 26 | this.listEntry = listEntry; 27 | } 28 | 29 | @Override 30 | public double distanceTo(double x, double y) 31 | { 32 | return Math.sqrt(Math.pow(this.getX() - x, 2) + Math.pow(this.getY() - y, 2)); 33 | } 34 | 35 | @Override 36 | public double getX() 37 | { 38 | return this.widget.getX() + this.widget.getWidth() / 2; 39 | } 40 | 41 | @Override 42 | public double getY() 43 | { 44 | return this.widget.getY() + this.widget.getHeight() / 2; 45 | } 46 | 47 | @Override 48 | public void onNavigate() 49 | { 50 | int itemHeight = ClientServices.CLIENT.getListItemHeight(this.list); 51 | int index = this.list.children().indexOf(this.listEntry); 52 | int rowTop = ClientServices.CLIENT.getAbstractListRowTop(this.list, index); 53 | int rowBottom = ClientServices.CLIENT.getAbstractListRowBottom(this.list, index); 54 | int listTop = ClientServices.CLIENT.getAbstractListTop(this.list); 55 | int listBottom = ClientServices.CLIENT.getAbstractListBottom(this.list); 56 | if(rowTop < listTop) 57 | { 58 | double scroll = this.list.children().indexOf(this.listEntry) * itemHeight - itemHeight / 2; 59 | this.list.setScrollAmount(scroll); 60 | } 61 | if(rowBottom > listBottom) // Is not/partially visible 62 | { 63 | double scroll = this.list.children().indexOf(this.listEntry) * itemHeight + itemHeight - (listBottom - listTop) + 4 + itemHeight / 2; 64 | this.list.setScrollAmount(scroll); 65 | } 66 | } 67 | 68 | @Override 69 | public boolean shouldHide() 70 | { 71 | return this.widget instanceof AbstractButton || this.widget instanceof TabButton || this.widget instanceof HideCursor; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/navigation/Navigatable.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.navigation; 2 | 3 | import net.minecraft.client.gui.components.events.GuiEventListener; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Author: MrCrayfish 9 | */ 10 | public interface Navigatable 11 | { 12 | List elements(); 13 | } 14 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/navigation/NavigationPoint.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.navigation; 2 | 3 | /** 4 | * Author: MrCrayfish 5 | */ 6 | public abstract class NavigationPoint 7 | { 8 | private final double x, y; 9 | private final Type type; 10 | 11 | public NavigationPoint(double x, double y, Type type) 12 | { 13 | this.x = x; 14 | this.y = y; 15 | this.type = type; 16 | } 17 | 18 | public double distanceTo(double x, double y) 19 | { 20 | return Math.sqrt(Math.pow(this.x - x, 2) + Math.pow(this.y - y, 2)); 21 | } 22 | 23 | public double getX() 24 | { 25 | return this.x; 26 | } 27 | 28 | public double getY() 29 | { 30 | return this.y; 31 | } 32 | 33 | public Type getType() 34 | { 35 | return this.type; 36 | } 37 | 38 | public boolean shouldHide() 39 | { 40 | return false; 41 | } 42 | 43 | /** 44 | * Called when the cursor navigates to this point 45 | */ 46 | public void onNavigate() {} 47 | 48 | protected enum Type 49 | { 50 | BASIC, 51 | WIDGET, 52 | SLOT; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/navigation/SkipItem.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.navigation; 2 | 3 | /** 4 | * Author: MrCrayfish 5 | */ 6 | public interface SkipItem 7 | { 8 | } 9 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/navigation/SlotNavigationPoint.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.navigation; 2 | 3 | import net.minecraft.world.inventory.Slot; 4 | 5 | /** 6 | * Author: MrCrayfish 7 | */ 8 | public class SlotNavigationPoint extends NavigationPoint 9 | { 10 | private final Slot slot; 11 | 12 | public SlotNavigationPoint(double x, double y, Slot slot) 13 | { 14 | super(x, y, Type.SLOT); 15 | this.slot = slot; 16 | } 17 | 18 | public Slot getSlot() 19 | { 20 | return this.slot; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/navigation/WidgetNavigationPoint.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.navigation; 2 | 3 | import net.minecraft.client.gui.components.AbstractButton; 4 | import net.minecraft.client.gui.components.AbstractWidget; 5 | import net.minecraft.client.gui.components.Button; 6 | import net.minecraft.client.gui.components.TabButton; 7 | 8 | /** 9 | * Author: MrCrayfish 10 | */ 11 | public class WidgetNavigationPoint extends NavigationPoint 12 | { 13 | private final AbstractWidget widget; 14 | 15 | public WidgetNavigationPoint(AbstractWidget widget) 16 | { 17 | super(widget.getX() + widget.getWidth() / 2.0, widget.getY() + widget.getHeight() / 2.0, Type.WIDGET); 18 | this.widget = widget; 19 | } 20 | 21 | public AbstractWidget getWidget() 22 | { 23 | return this.widget; 24 | } 25 | 26 | @Override 27 | public boolean shouldHide() 28 | { 29 | return this.widget instanceof AbstractButton || this.widget instanceof TabButton || this.widget instanceof HideCursor; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/screens/PendingScreen.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.screens; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import net.minecraft.Util; 5 | import net.minecraft.client.gui.GuiGraphics; 6 | import net.minecraft.client.gui.screens.Screen; 7 | import net.minecraft.network.chat.Component; 8 | 9 | /** 10 | * Author: MrCrayfish 11 | */ 12 | public class PendingScreen extends Screen 13 | { 14 | public PendingScreen(Component title) 15 | { 16 | super(title); 17 | } 18 | 19 | @Override 20 | public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) 21 | { 22 | super.render(graphics, mouseX, mouseY, partialTick); 23 | String loadingBar = switch((int) (Util.getMillis() / 300L % 4L)) { 24 | case 1, 3 -> "o O o"; 25 | case 2 -> "o o O"; 26 | default -> "O o o"; 27 | }; 28 | int centerX = this.width / 2; 29 | int centerY = this.height / 2; 30 | graphics.drawCenteredString(this.font, loadingBar, centerX, centerY - 9, -1); 31 | graphics.drawCenteredString(this.font, this.title, centerX, centerY + 5, 0x808080); 32 | } 33 | } -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/screens/RadialMenuConfigureScreen.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.screens; 2 | 3 | import com.mrcrayfish.controllable.Controllable; 4 | import com.mrcrayfish.controllable.client.gui.RadialMenuAction; 5 | import com.mrcrayfish.controllable.client.gui.Icons; 6 | import com.mrcrayfish.controllable.client.gui.RadialItemList; 7 | import com.mrcrayfish.controllable.client.util.ClientHelper; 8 | import com.mrcrayfish.controllable.client.util.ScreenHelper; 9 | import net.minecraft.client.gui.components.StringWidget; 10 | import net.minecraft.client.gui.layouts.HeaderAndFooterLayout; 11 | import net.minecraft.client.gui.layouts.LinearLayout; 12 | import net.minecraft.client.gui.screens.Screen; 13 | import net.minecraft.network.chat.CommonComponents; 14 | import net.minecraft.network.chat.Component; 15 | import org.jetbrains.annotations.Nullable; 16 | 17 | import java.util.ArrayList; 18 | import java.util.LinkedHashSet; 19 | import java.util.List; 20 | import java.util.Objects; 21 | 22 | /** 23 | * Author: MrCrayfish 24 | */ 25 | public class RadialMenuConfigureScreen extends Screen 26 | { 27 | private final @Nullable Screen parent; 28 | private final List actions = new ArrayList<>(); 29 | protected final HeaderAndFooterLayout layout = new HeaderAndFooterLayout(this); 30 | private RadialItemList list; 31 | 32 | public RadialMenuConfigureScreen(@Nullable Screen parent) 33 | { 34 | super(Component.translatable("controllable.gui.title.radial_menu_configure")); 35 | this.parent = parent; 36 | this.actions.addAll(Controllable.getRadialMenu().getActions()); 37 | } 38 | 39 | @Override 40 | protected void init() 41 | { 42 | LinearLayout headerLayout = this.layout.addToHeader(LinearLayout.vertical()); 43 | headerLayout.addChild(new StringWidget(this.title, this.font)); 44 | 45 | this.list = new RadialItemList(this.minecraft, this.actions); 46 | this.layout.addToContents(this.list); 47 | 48 | LinearLayout footerLayout = this.layout.addToFooter(LinearLayout.horizontal().spacing(4)); 49 | Component saveLabel = ClientHelper.join(Icons.SAVE, Component.translatable("controllable.gui.save")); 50 | footerLayout.addChild(ScreenHelper.button(this.width / 2 - 155, this.height - 29, 100, 20, saveLabel, buttons -> { 51 | Controllable.getRadialMenu().setActions(this.actions); 52 | Objects.requireNonNull(this.minecraft).setScreen(this.parent); 53 | })); 54 | Component addLabel = ClientHelper.join(Icons.ADD, Component.translatable("controllable.gui.add_binding")); 55 | footerLayout.addChild(ScreenHelper.button(this.width / 2 - 50, this.height - 29, 100, 20, addLabel, buttons -> { 56 | Objects.requireNonNull(this.minecraft).setScreen(new RadialMenuAddBindingsScreen(this)); 57 | })); 58 | footerLayout.addChild(ScreenHelper.button(this.width / 2 + 55, this.height - 29, 100, 20, CommonComponents.GUI_CANCEL, buttons -> { 59 | Objects.requireNonNull(this.minecraft).setScreen(this.parent); 60 | })); 61 | 62 | this.layout.visitWidgets(this::addRenderableWidget); 63 | this.repositionElements(); 64 | } 65 | 66 | @Override 67 | protected void repositionElements() 68 | { 69 | this.layout.arrangeElements(); 70 | this.list.updateEntries(); 71 | this.list.updateSize(this.width, this.layout); 72 | } 73 | 74 | public List getActions() 75 | { 76 | return this.actions; 77 | } 78 | 79 | public void scrollToBottomAndSelectLast() 80 | { 81 | this.list.setScrollAmount(this.list.maxScrollAmount()); 82 | this.list.setSelected(this.list.children().getLast()); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/screens/ThumbstickSettingsScreen.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.screens; 2 | 3 | import com.mrcrayfish.controllable.client.util.ScreenHelper; 4 | import net.minecraft.client.gui.GuiGraphics; 5 | import net.minecraft.client.gui.components.OptionsList; 6 | import net.minecraft.client.gui.screens.Screen; 7 | import net.minecraft.network.chat.CommonComponents; 8 | import net.minecraft.network.chat.Component; 9 | 10 | import java.util.Objects; 11 | 12 | /** 13 | * Author: MrCrayfish 14 | */ 15 | public class ThumbstickSettingsScreen extends Screen 16 | { 17 | private OptionsList optionsRowList; 18 | private final ControllerLayoutScreen layoutScreen; 19 | 20 | protected ThumbstickSettingsScreen(ControllerLayoutScreen layoutScreen) 21 | { 22 | super(Component.translatable("controllable.gui.title.thumbstick_settings")); 23 | this.layoutScreen = layoutScreen; 24 | } 25 | 26 | @Override 27 | protected void init() 28 | { 29 | //this.optionsRowList = new OptionsList(Objects.requireNonNull(this.minecraft), this.width, this.height, 32, this.height - 32, 25); 30 | 31 | /*this.optionsRowList.addBig(ControllerOptions.createOnOff("controllable.options.switchThumbsticks", gameSettings -> { 32 | return this.layoutScreen.getEntry().isThumbsticksSwitched(); 33 | }, (options, option, value) -> { 34 | this.layoutScreen.getEntry().setSwitchThumbsticks(value); 35 | })); 36 | 37 | this.optionsRowList.addSmall(ControllerOptions.createOnOff("controllable.options.flipLeftThumbstickX", gameSettings -> { 38 | return this.layoutScreen.getEntry().isFlipLeftX(); 39 | }, (options, option, value) -> { 40 | this.layoutScreen.getEntry().setFlipLeftX(value); 41 | }), ControllerOptions.createOnOff("controllable.options.flipRightThumbstickX", gameSettings -> { 42 | return this.layoutScreen.getEntry().isFlipRightX(); 43 | }, (options, option, value) -> { 44 | this.layoutScreen.getEntry().setFlipRightX(value); 45 | })); 46 | 47 | this.optionsRowList.addSmall(ControllerOptions.createOnOff("controllable.options.flipLeftThumbstickY", gameSettings -> { 48 | return this.layoutScreen.getEntry().isFlipLeftY(); 49 | }, (options, option, value) -> { 50 | this.layoutScreen.getEntry().setFlipLeftY(value); 51 | }), ControllerOptions.createOnOff("controllable.options.flipRightThumbstickY", gameSettings -> { 52 | return this.layoutScreen.getEntry().isFlipRightY(); 53 | }, (options, option, value) -> { 54 | this.layoutScreen.getEntry().setFlipRightY(value); 55 | }));*/ 56 | 57 | //this.addWidget(this.optionsRowList); 58 | 59 | this.addRenderableWidget(ScreenHelper.button(this.width / 2 - 100, this.height - 27, 200, 20, CommonComponents.GUI_BACK, (button) -> { 60 | this.minecraft.setScreen(this.layoutScreen); 61 | })); 62 | } 63 | 64 | @Override 65 | public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) 66 | { 67 | //this.optionsRowList.render(graphics, mouseX, mouseY, partialTicks); 68 | graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, 0xFFFFFFFF); 69 | super.render(graphics, mouseX, mouseY, partialTicks); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/toasts/ConnectionToast.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.toasts; 2 | 3 | import com.mrcrayfish.controllable.client.gui.screens.ControllerLayoutScreen; 4 | import com.mrcrayfish.controllable.util.Utils; 5 | import net.minecraft.ChatFormatting; 6 | import net.minecraft.client.gui.Font; 7 | import net.minecraft.client.gui.GuiGraphics; 8 | import net.minecraft.client.gui.components.toasts.Toast; 9 | import net.minecraft.client.gui.components.toasts.ToastManager; 10 | import net.minecraft.client.renderer.RenderPipelines; 11 | import net.minecraft.client.renderer.RenderType; 12 | import net.minecraft.network.chat.Component; 13 | import net.minecraft.resources.ResourceLocation; 14 | 15 | /** 16 | * Author: MrCrayfish 17 | */ 18 | public class ConnectionToast implements Toast 19 | { 20 | private static final ResourceLocation BACKGROUND_SPRITE = Utils.resource("toast/connection"); 21 | private static final Component LABEL_CONNECTED = Component.translatable("controllable.toast.connected").withStyle(ChatFormatting.BLUE).withStyle(ChatFormatting.BOLD); 22 | private static final Component LABEL_DISCONNECTED = Component.translatable("controllable.toast.disconnected").withStyle(ChatFormatting.RED).withStyle(ChatFormatting.BOLD); 23 | 24 | private final boolean connected; 25 | private final Component controllerName; 26 | private Visibility visibility = Visibility.HIDE; 27 | 28 | public ConnectionToast(boolean connected, String controllerName) 29 | { 30 | this.connected = connected; 31 | this.controllerName = Component.literal(controllerName); 32 | } 33 | 34 | @Override 35 | public Visibility getWantedVisibility() 36 | { 37 | return this.visibility; 38 | } 39 | 40 | @Override 41 | public void update(ToastManager manager, long delta) 42 | { 43 | long displayTime = (long) (3000L * manager.getNotificationDisplayTimeMultiplier()); 44 | this.visibility = delta < displayTime ? Visibility.SHOW : Visibility.HIDE; 45 | } 46 | 47 | @Override 48 | public void render(GuiGraphics graphics, Font font, long delta) 49 | { 50 | graphics.blitSprite(RenderPipelines.GUI_TEXTURED, BACKGROUND_SPRITE, 0, 0, 160, 32); 51 | graphics.blit(RenderPipelines.GUI_TEXTURED, ControllerLayoutScreen.TEXTURE, 8, 8, 20, 43, 20, 16, 256, 256); 52 | String title = font.plainSubstrByWidth(this.controllerName.getString(), 120); 53 | graphics.drawString(font, title, 35, 7, 0xFFFFFFFF, false); 54 | Component message = this.connected ? LABEL_CONNECTED : LABEL_DISCONNECTED; 55 | graphics.drawString(font, message, 35, 18, 0xFFFFFF00, false); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/widget/BackgroundStringWidget.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.widget; 2 | 3 | import com.mrcrayfish.controllable.client.util.ScreenHelper; 4 | import net.minecraft.client.gui.Font; 5 | import net.minecraft.client.gui.GuiGraphics; 6 | import net.minecraft.client.gui.components.AbstractStringWidget; 7 | import net.minecraft.network.chat.Component; 8 | 9 | /** 10 | * Author: MrCrayfish 11 | */ 12 | public class BackgroundStringWidget extends AbstractStringWidget 13 | { 14 | public BackgroundStringWidget(Component message, Font font) 15 | { 16 | this(0, 0, message, font); 17 | } 18 | 19 | public BackgroundStringWidget(int x, int y, Component message, Font font) 20 | { 21 | super(x, y, font.width(message.getVisualOrderText()) + 4, font.lineHeight + 4, message, font); 22 | } 23 | 24 | @Override 25 | public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) 26 | { 27 | ScreenHelper.drawRoundedBox(graphics, this.getX(), this.getY(), this.getWidth(), this.getHeight(), 0x55000000); 28 | graphics.drawString(this.getFont(), this.getMessage().getVisualOrderText(), this.getX() + 2, this.getY() + 2, this.getColor()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/widget/ButtonBindingButton.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.widget; 2 | 3 | import com.mojang.blaze3d.systems.RenderSystem; 4 | import com.mrcrayfish.controllable.Config; 5 | import com.mrcrayfish.controllable.client.binding.ButtonBinding; 6 | import com.mrcrayfish.controllable.client.settings.ButtonIcons; 7 | import net.minecraft.client.Minecraft; 8 | import net.minecraft.client.gui.GuiGraphics; 9 | import net.minecraft.client.gui.components.Button; 10 | import net.minecraft.client.renderer.RenderPipelines; 11 | import net.minecraft.client.renderer.RenderType; 12 | import net.minecraft.network.chat.CommonComponents; 13 | 14 | /** 15 | * Author: MrCrayfish 16 | */ 17 | public class ButtonBindingButton extends Button 18 | { 19 | private final ButtonBinding binding; 20 | private final ButtonOnPress onPress; 21 | 22 | public ButtonBindingButton(int x, int y, ButtonBinding binding, ButtonOnPress onPress) 23 | { 24 | super(x, y, 40, 20, CommonComponents.EMPTY, btn -> {}, DEFAULT_NARRATION); 25 | this.binding = binding; 26 | this.onPress = onPress; 27 | } 28 | 29 | public ButtonBinding getBinding() 30 | { 31 | return this.binding; 32 | } 33 | 34 | @Override 35 | public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) 36 | { 37 | super.renderWidget(graphics, mouseX, mouseY, partialTicks); 38 | if(this.binding.getButton() < 0) 39 | return; 40 | int texU = this.binding.getButton() * 13; 41 | int texV = Config.CLIENT.options.controllerIcons.get().ordinal() * 13; 42 | int size = 13; 43 | graphics.blit(RenderPipelines.GUI_TEXTURED, ButtonIcons.TEXTURE, this.getX() + (this.width - size) / 2 + 1, this.getY() + 3, texU, texV, size, size, ButtonIcons.TEXTURE_WIDTH, ButtonIcons.TEXTURE_HEIGHT); 44 | } 45 | 46 | @Override 47 | public boolean mouseClicked(double mouseX, double mouseY, int button) 48 | { 49 | if(this.active && this.visible && this.isMouseOver(mouseX, mouseY)) 50 | { 51 | if(this.onPress.onPress(button)) 52 | { 53 | this.playDownSound(Minecraft.getInstance().getSoundManager()); 54 | } 55 | return true; 56 | } 57 | return false; 58 | } 59 | 60 | public interface ButtonOnPress 61 | { 62 | boolean onPress(int button); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/widget/ColorButton.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.widget; 2 | 3 | import com.mojang.blaze3d.systems.RenderSystem; 4 | import net.minecraft.ChatFormatting; 5 | import net.minecraft.client.gui.GuiGraphics; 6 | import net.minecraft.client.gui.components.Button; 7 | import net.minecraft.network.chat.CommonComponents; 8 | import org.apache.commons.lang3.ArrayUtils; 9 | 10 | /** 11 | * Author: MrCrayfish 12 | */ 13 | public class ColorButton extends Button 14 | { 15 | private static final ChatFormatting[] COLORS = { 16 | ChatFormatting.BLACK, 17 | ChatFormatting.DARK_BLUE, 18 | ChatFormatting.DARK_GREEN, 19 | ChatFormatting.DARK_AQUA, 20 | ChatFormatting.DARK_RED, 21 | ChatFormatting.DARK_PURPLE, 22 | ChatFormatting.GOLD, 23 | ChatFormatting.GRAY, 24 | ChatFormatting.DARK_GRAY, 25 | ChatFormatting.BLUE, 26 | ChatFormatting.GREEN, 27 | ChatFormatting.AQUA, 28 | ChatFormatting.RED, 29 | ChatFormatting.LIGHT_PURPLE, 30 | ChatFormatting.YELLOW, 31 | ChatFormatting.WHITE 32 | }; 33 | 34 | private int index = 14; 35 | 36 | public ColorButton(int x, int y, OnPress onPress) 37 | { 38 | super(x, y, 20, 20, CommonComponents.EMPTY, onPress, DEFAULT_NARRATION); 39 | } 40 | 41 | public void setColor(ChatFormatting color) 42 | { 43 | int index = ArrayUtils.indexOf(COLORS, color); 44 | if(index != -1) 45 | { 46 | this.index = index; 47 | } 48 | } 49 | 50 | public ChatFormatting getColor() 51 | { 52 | return COLORS[this.index]; 53 | } 54 | 55 | @Override 56 | public void onClick(double mouseX, double mouseY) 57 | { 58 | this.index = (this.index + 1) % COLORS.length; 59 | super.onClick(mouseX, mouseY); 60 | } 61 | 62 | @Override 63 | public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) 64 | { 65 | super.renderWidget(graphics, mouseX, mouseY, partialTicks); 66 | graphics.fill(this.getX() + 4, this.getY() + 4, this.getX() + 16, this.getY() + 16, 0xFF000000); 67 | graphics.fill(this.getX() + 5, this.getY() + 5, this.getX() + 15, this.getY() + 15, COLORS[this.index].getColor() + 0xFF000000); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/widget/ControllerButton.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.widget; 2 | 3 | import com.mojang.blaze3d.systems.RenderSystem; 4 | import com.mrcrayfish.controllable.client.util.ScreenHelper; 5 | import com.mrcrayfish.controllable.util.Utils; 6 | import net.minecraft.client.gui.GuiGraphics; 7 | import net.minecraft.client.gui.components.AbstractWidget; 8 | import net.minecraft.client.gui.components.Button; 9 | import net.minecraft.client.renderer.RenderPipelines; 10 | import net.minecraft.client.renderer.RenderType; 11 | import net.minecraft.network.chat.CommonComponents; 12 | import net.minecraft.resources.ResourceLocation; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | /** 16 | * Author: MrCrayfish 17 | */ 18 | public class ControllerButton extends Button 19 | { 20 | private static final ResourceLocation TEXTURE = Utils.resource("textures/gui/controller.png"); 21 | private final AbstractWidget widget; 22 | 23 | public ControllerButton(AbstractWidget widget, OnPress onPress) 24 | { 25 | super(0, 0, 20, 20, CommonComponents.EMPTY, onPress, DEFAULT_NARRATION); 26 | this.widget = widget; 27 | } 28 | 29 | @Override 30 | public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) 31 | { 32 | this.setX(this.widget.getRight() + 4); 33 | this.setY(this.widget.getY()); 34 | super.renderWidget(graphics, mouseX, mouseY, partialTicks); 35 | boolean mouseOver = ScreenHelper.isMouseWithin(mouseX, mouseY, this.getX(), this.getY(), this.width, this.height); 36 | int textureV = 43; 37 | if(mouseOver) 38 | { 39 | textureV += this.height; 40 | } 41 | graphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, this.getX(), this.getY(), 0, textureV, this.width, this.height, 256, 256); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/widget/ImageButton.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.widget; 2 | 3 | import com.mojang.blaze3d.systems.RenderSystem; 4 | import com.mojang.blaze3d.vertex.PoseStack; 5 | import net.minecraft.client.gui.GuiGraphics; 6 | import net.minecraft.client.gui.components.Button; 7 | import net.minecraft.client.renderer.GameRenderer; 8 | import net.minecraft.client.renderer.RenderPipelines; 9 | import net.minecraft.client.renderer.RenderType; 10 | import net.minecraft.network.chat.CommonComponents; 11 | import net.minecraft.resources.ResourceLocation; 12 | import net.minecraft.util.ARGB; 13 | 14 | /** 15 | * Author: MrCrayfish 16 | */ 17 | public class ImageButton extends Button 18 | { 19 | private final ResourceLocation texture; 20 | private final int imageU, imageV; 21 | private final int imageWidth, imageHeight; 22 | private final int textureWidth, textureHeight; 23 | 24 | public ImageButton(int x, int y, int width, ResourceLocation texture, int imageU, int imageV, int imageWidth, int imageHeight, OnPress onPress) 25 | { 26 | this(x, y, width, texture, imageU, imageV, imageWidth, imageHeight, 256, 256, onPress); 27 | } 28 | 29 | public ImageButton(int x, int y, int width, ResourceLocation texture, int imageU, int imageV, int imageWidth, int imageHeight, int textureWidth, int textureHeight, OnPress onPress) 30 | { 31 | super(x, y, width, 20, CommonComponents.EMPTY, onPress, DEFAULT_NARRATION); 32 | this.texture = texture; 33 | this.imageU = imageU; 34 | this.imageV = imageV; 35 | this.imageWidth = imageWidth; 36 | this.imageHeight = imageHeight; 37 | this.textureWidth = textureWidth; 38 | this.textureHeight = textureHeight; 39 | } 40 | 41 | @Override 42 | public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) 43 | { 44 | super.renderWidget(graphics, mouseX, mouseY, partialTicks); 45 | graphics.blit(RenderPipelines.GUI_TEXTURED, this.texture, this.getX() + (this.width - this.imageWidth) / 2, this.getY() + (this.height - this.imageHeight) / 2, this.imageU, this.imageV, this.imageWidth, this.imageHeight, this.textureWidth, this.textureHeight, !this.active ? ARGB.white(0.5F) : ARGB.white(1)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/widget/TabListWidget.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.widget; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import com.mojang.blaze3d.vertex.PoseStack; 5 | import com.mrcrayfish.controllable.client.gui.components.TabSelectionList; 6 | import net.minecraft.client.gui.GuiGraphics; 7 | import net.minecraft.client.gui.components.AbstractWidget; 8 | import net.minecraft.client.gui.components.events.ContainerEventHandler; 9 | import net.minecraft.client.gui.components.events.GuiEventListener; 10 | import net.minecraft.client.gui.narration.NarrationElementOutput; 11 | import net.minecraft.client.gui.navigation.ScreenRectangle; 12 | import net.minecraft.network.chat.CommonComponents; 13 | 14 | import org.jetbrains.annotations.Nullable; 15 | import java.util.List; 16 | import java.util.function.Supplier; 17 | 18 | /** 19 | * Author: MrCrayfish 20 | */ 21 | public class TabListWidget extends AbstractWidget implements ContainerEventHandler 22 | { 23 | private final Supplier dimensions; 24 | private final TabSelectionList list; 25 | 26 | public TabListWidget(Supplier dimensions, TabSelectionList list) 27 | { 28 | super(0, 0, 100, 0, CommonComponents.EMPTY); 29 | this.dimensions = dimensions; 30 | this.list = list; 31 | } 32 | 33 | @Override 34 | public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) 35 | { 36 | ScreenRectangle dimensions = this.dimensions.get(); 37 | this.setX(dimensions.left()); 38 | this.setY(dimensions.top()); 39 | this.width = dimensions.width(); 40 | this.height = dimensions.height(); 41 | this.list.updateDimensions(dimensions); 42 | this.list.render(graphics, mouseX, mouseY, partialTick); 43 | } 44 | 45 | @Override 46 | protected void updateWidgetNarration(NarrationElementOutput output) 47 | { 48 | this.list.updateNarration(output); 49 | } 50 | 51 | @Override 52 | public List children() 53 | { 54 | return ImmutableList.of(this.list); 55 | } 56 | 57 | @Override 58 | public boolean mouseClicked(double mouseX, double mouseY, int button) 59 | { 60 | return this.list.mouseClicked(mouseX, mouseY, button); 61 | } 62 | 63 | @Override 64 | public boolean mouseDragged(double $$0, double $$1, int $$2, double $$3, double $$4) 65 | { 66 | return this.list.mouseDragged($$0, $$1, $$2, $$3, $$4); 67 | } 68 | 69 | @Override 70 | public boolean mouseReleased(double $$0, double $$1, int $$2) 71 | { 72 | return this.list.mouseReleased($$0, $$1, $$2); 73 | } 74 | 75 | @Override 76 | public boolean mouseScrolled(double mouseX, double mouseY, double xScroll, double yScroll) 77 | { 78 | return this.list.mouseScrolled(mouseX, mouseY, xScroll, yScroll); 79 | } 80 | 81 | @Override 82 | public boolean isDragging() 83 | { 84 | return this.list.isDragging(); 85 | } 86 | 87 | @Override 88 | public void setDragging(boolean dragging) 89 | { 90 | this.list.setDragging(dragging); 91 | } 92 | 93 | @Nullable 94 | @Override 95 | public GuiEventListener getFocused() 96 | { 97 | return this.list.getFocused(); 98 | } 99 | 100 | @Override 101 | public void setFocused(@Nullable GuiEventListener listener) 102 | { 103 | this.list.setFocused(listener); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/gui/widget/TabNavigationHint.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.gui.widget; 2 | 3 | import com.mrcrayfish.controllable.Controllable; 4 | import com.mrcrayfish.controllable.client.input.Buttons; 5 | import com.mrcrayfish.controllable.client.input.Controller; 6 | import com.mrcrayfish.controllable.client.util.ClientHelper; 7 | import net.minecraft.client.gui.GuiGraphics; 8 | import net.minecraft.client.gui.components.Renderable; 9 | import net.minecraft.client.gui.components.events.GuiEventListener; 10 | import net.minecraft.client.gui.navigation.ScreenRectangle; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * Author: MrCrayfish 16 | */ 17 | public class TabNavigationHint implements Renderable 18 | { 19 | private final List tabs; 20 | 21 | public TabNavigationHint(List tabs) 22 | { 23 | this.tabs = tabs; 24 | } 25 | 26 | @Override 27 | public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) 28 | { 29 | // Don't render if tabs can't be navigated 30 | Controller controller = Controllable.getController(); 31 | if(controller == null || !controller.isBeingUsed()) 32 | return; 33 | 34 | ScreenRectangle firstTab = this.tabs.get(0).getRectangle(); 35 | ClientHelper.drawButton(graphics, firstTab.left() - 18, (firstTab.height() - 11) / 2, Buttons.LEFT_BUMPER); 36 | 37 | ScreenRectangle lastTab = this.tabs.get(this.tabs.size() - 1).getRectangle(); 38 | ClientHelper.drawButton(graphics, lastTab.right() + 5, (lastTab.height() - 11) / 2, Buttons.RIGHT_BUMPER); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/input/ButtonStates.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.input; 2 | 3 | /** 4 | * Author: MrCrayfish 5 | */ 6 | public class ButtonStates 7 | { 8 | private final boolean[] states; 9 | 10 | public ButtonStates() 11 | { 12 | this.states = new boolean[Buttons.LENGTH]; 13 | } 14 | 15 | public boolean getState(int index) 16 | { 17 | if(index < 0 || index >= states.length) 18 | return false; 19 | return this.states[index]; 20 | } 21 | 22 | public void setState(int index, boolean state) 23 | { 24 | if(index < 0 || index >= states.length) 25 | return; 26 | this.states[index] = state; 27 | } 28 | 29 | public int getSize() 30 | { 31 | return this.states.length; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/input/InputLibrary.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.input; 2 | 3 | /** 4 | * Author: MrCrayfish 5 | */ 6 | public enum InputLibrary 7 | { 8 | GLFW, 9 | SDL2 // Default 10 | } 11 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/overlay/IOverlay.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.overlay; 2 | 3 | import net.minecraft.client.DeltaTracker; 4 | import net.minecraft.client.gui.GuiGraphics; 5 | 6 | /** 7 | * Author: MrCrayfish 8 | */ 9 | public interface IOverlay 10 | { 11 | boolean isVisible(); 12 | 13 | default void tick() {} 14 | 15 | void render(GuiGraphics graphics, int mouseX, int mouseY, DeltaTracker tracker); 16 | } 17 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/overlay/OverlayRenderer.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.overlay; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import com.mrcrayfish.framework.api.event.client.FrameworkClientTickEvents; 5 | import net.minecraft.Util; 6 | import net.minecraft.client.DeltaTracker; 7 | import net.minecraft.client.gui.GuiGraphics; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Author: MrCrayfish 13 | */ 14 | public class OverlayRenderer 15 | { 16 | private static final List OVERLAYS = Util.make(() -> { 17 | ImmutableList.Builder builder = new ImmutableList.Builder<>(); 18 | builder.add(new TabNavigationOverlay()); 19 | builder.add(new RecipeBookOverlay()); 20 | builder.add(new PaperDollPlayerOverlay()); 21 | builder.add(new RadialMenuOverlay()); 22 | builder.add(new ActionHintOverlay()); 23 | builder.add(new VirtualCursorOverlay()); 24 | return builder.build(); 25 | }); 26 | 27 | public static void init() 28 | { 29 | FrameworkClientTickEvents.START_CLIENT.register(() -> OVERLAYS.forEach(IOverlay::tick)); 30 | } 31 | 32 | public static void draw(GuiGraphics graphics, int mouseX, int mouseY, DeltaTracker tracker) 33 | { 34 | for(IOverlay overlay : OVERLAYS) 35 | { 36 | if(overlay.isVisible()) 37 | { 38 | overlay.render(graphics, mouseX, mouseY, tracker); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/overlay/PaperDollPlayerOverlay.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.overlay; 2 | 3 | import com.mrcrayfish.controllable.Config; 4 | import com.mrcrayfish.controllable.Controllable; 5 | import com.mrcrayfish.controllable.client.input.Controller; 6 | import com.mrcrayfish.controllable.client.util.EventHelper; 7 | import net.minecraft.client.DeltaTracker; 8 | import net.minecraft.client.Minecraft; 9 | import net.minecraft.client.gui.GuiGraphics; 10 | import net.minecraft.client.gui.screens.inventory.InventoryScreen; 11 | 12 | /** 13 | * Author: MrCrayfish 14 | */ 15 | public class PaperDollPlayerOverlay implements IOverlay 16 | { 17 | @Override 18 | public boolean isVisible() 19 | { 20 | Controller controller = Controllable.getController(); 21 | return !Minecraft.getInstance().options.hideGui && controller != null && (!Config.CLIENT.options.overlayTimeout.get() || controller.isBeingUsed()); 22 | } 23 | 24 | @Override 25 | public void render(GuiGraphics graphics, int mouseX, int mouseY, DeltaTracker tracker) 26 | { 27 | Minecraft mc = Minecraft.getInstance(); 28 | if(mc.player != null && mc.screen == null && Config.CLIENT.options.paperDoll.get()) 29 | { 30 | if(!EventHelper.postRenderMiniPlayer()) 31 | { 32 | InventoryScreen.renderEntityInInventoryFollowsMouse(graphics, 0, 0, 50, 70, 20, 0.0625F, 25, 35, mc.player); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/overlay/RadialMenuOverlay.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.overlay; 2 | 3 | import com.mrcrayfish.controllable.Controllable; 4 | import net.minecraft.client.DeltaTracker; 5 | import net.minecraft.client.gui.GuiGraphics; 6 | 7 | /** 8 | * Author: MrCrayfish 9 | */ 10 | public class RadialMenuOverlay implements IOverlay 11 | { 12 | @Override 13 | public boolean isVisible() 14 | { 15 | return true; 16 | } 17 | 18 | @Override 19 | public void render(GuiGraphics graphics, int mouseX, int mouseY, DeltaTracker tracker) 20 | { 21 | Controllable.getRadialMenu().onRenderEnd(graphics, tracker); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/overlay/TabNavigationOverlay.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.overlay; 2 | 3 | import com.mrcrayfish.controllable.Config; 4 | import com.mrcrayfish.controllable.Controllable; 5 | import com.mrcrayfish.controllable.client.binding.ButtonBindings; 6 | import com.mrcrayfish.controllable.client.input.Buttons; 7 | import com.mrcrayfish.controllable.client.input.Controller; 8 | import com.mrcrayfish.controllable.client.util.ClientHelper; 9 | import net.minecraft.client.DeltaTracker; 10 | import net.minecraft.client.Minecraft; 11 | import net.minecraft.client.gui.GuiGraphics; 12 | import net.minecraft.client.gui.components.events.GuiEventListener; 13 | import net.minecraft.client.gui.components.tabs.TabNavigationBar; 14 | import net.minecraft.client.gui.navigation.ScreenRectangle; 15 | import net.minecraft.client.gui.screens.Screen; 16 | 17 | import java.util.List; 18 | 19 | /** 20 | * Author: MrCrayfish 21 | */ 22 | public class TabNavigationOverlay implements IOverlay 23 | { 24 | private TabNavigationBar navigationBar; 25 | 26 | @Override 27 | public void tick() 28 | { 29 | this.navigationBar = null; 30 | Minecraft mc = Minecraft.getInstance(); 31 | Screen screen = mc.screen; 32 | if(screen != null) 33 | { 34 | this.navigationBar = screen.children().stream().filter(e -> e instanceof TabNavigationBar).map(listener -> (TabNavigationBar) listener).findFirst().orElse(null); 35 | } 36 | } 37 | 38 | @Override 39 | public boolean isVisible() 40 | { 41 | Controller controller = Controllable.getController(); 42 | return controller != null && (!Config.CLIENT.options.overlayTimeout.get() || controller.isBeingUsed()) && this.navigationBar != null; 43 | } 44 | 45 | @Override 46 | public void render(GuiGraphics graphics, int mouseX, int mouseY, DeltaTracker tracker) 47 | { 48 | List tabs = this.navigationBar.children(); 49 | ScreenRectangle firstTab = tabs.get(0).getRectangle(); 50 | ClientHelper.drawButton(graphics, firstTab.left() - 18, (firstTab.height() - 11) / 2, ButtonBindings.NEXT_RECIPE_TAB.getButton()); 51 | ScreenRectangle lastTab = tabs.get(tabs.size() - 1).getRectangle(); 52 | ClientHelper.drawButton(graphics, lastTab.right() + 5, (lastTab.height() - 11) / 2, ButtonBindings.PREVIOUS_RECIPE_TAB.getButton()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/overlay/VirtualCursorOverlay.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.overlay; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import com.mrcrayfish.controllable.Config; 5 | import com.mrcrayfish.controllable.Controllable; 6 | import com.mrcrayfish.controllable.client.input.Controller; 7 | import com.mrcrayfish.controllable.client.settings.CursorStyle; 8 | import com.mrcrayfish.controllable.platform.ClientServices; 9 | import net.minecraft.client.DeltaTracker; 10 | import net.minecraft.client.Minecraft; 11 | import net.minecraft.client.gui.GuiGraphics; 12 | import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; 13 | import net.minecraft.client.renderer.RenderPipelines; 14 | import net.minecraft.client.renderer.RenderType; 15 | import net.minecraft.world.inventory.Slot; 16 | 17 | /** 18 | * Author: MrCrayfish 19 | */ 20 | public class VirtualCursorOverlay implements IOverlay 21 | { 22 | @Override 23 | public boolean isVisible() 24 | { 25 | Controller controller = Controllable.getController(); 26 | return Minecraft.getInstance().getOverlay() == null && Minecraft.getInstance().screen != null && controller != null && controller.isUsingVirtualCursor() && Controllable.getCursor().isVisible(); 27 | } 28 | 29 | @Override 30 | public void render(GuiGraphics graphics, int mouseX, int mouseY, DeltaTracker tracker) 31 | { 32 | graphics.pose().pushMatrix(); 33 | Minecraft mc = Minecraft.getInstance(); 34 | CursorStyle type = Config.CLIENT.options.cursorType.get(); 35 | if(mc.player == null || (mc.player.inventoryMenu.getCarried().isEmpty() || type.getBehaviour() == CursorStyle.ItemHeldBehaviour.SHOW)) 36 | { 37 | double guiScale = mc.getWindow().getGuiScale(); 38 | double cursorX = Controllable.getCursor().getRenderX(); 39 | double cursorY = Controllable.getCursor().getRenderY(); 40 | graphics.pose().translate((float) (cursorX / guiScale), (float) (cursorY / guiScale)); 41 | boolean isHoveringSlot = this.isHoveringFilledContainerSlot(); 42 | if(isHoveringSlot && type.isScaleHover()) 43 | { 44 | graphics.pose().scale(1.33F, 1.33F); 45 | } 46 | graphics.blit(RenderPipelines.GUI_TEXTURED, CursorStyle.TEXTURE, -8, -8, isHoveringSlot ? 32 : 0, type.ordinal() * 32, 16, 16, 32, 32, 64, CursorStyle.values().length * 32); 47 | } 48 | graphics.pose().popMatrix(); 49 | } 50 | 51 | /** 52 | * @return True if the cursor is hovering a container slot 53 | */ 54 | private boolean isHoveringFilledContainerSlot() 55 | { 56 | Minecraft mc = Minecraft.getInstance(); 57 | if(mc.screen instanceof AbstractContainerScreen screen) 58 | { 59 | Slot slot = ClientServices.CLIENT.getSlotUnderMouse(screen); 60 | return slot != null && slot.hasItem(); 61 | } 62 | return false; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/settings/ActionVisibility.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.settings; 2 | 3 | /** 4 | * Author: MrCrayfish 5 | */ 6 | public enum ActionVisibility implements SettingEnum 7 | { 8 | ALL("controllable.action_visibility.all"), 9 | MINIMAL("controllable.action_visibility.minimal"), 10 | NONE("controllable.action_visibility.none"); 11 | 12 | private final String key; 13 | 14 | ActionVisibility(String key) 15 | { 16 | this.key = key; 17 | } 18 | 19 | @Override 20 | public String getKey() 21 | { 22 | return this.key; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/settings/AnalogMovement.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.settings; 2 | 3 | /** 4 | * Author: MrCrayfish 5 | */ 6 | public enum AnalogMovement implements SettingEnum 7 | { 8 | DISABLED("controllable.analog_movement.disabled"), 9 | LOCAL_ONLY("controllable.analog_movement.local_only"), 10 | ALWAYS("controllable.analog_movement.always"); 11 | 12 | private final String key; 13 | 14 | AnalogMovement(String key) 15 | { 16 | this.key = key; 17 | } 18 | 19 | @Override 20 | public String getKey() 21 | { 22 | return this.key; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/settings/ButtonIcons.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.settings; 2 | 3 | import com.mrcrayfish.controllable.client.input.Buttons; 4 | import com.mrcrayfish.controllable.util.Utils; 5 | import net.minecraft.resources.ResourceLocation; 6 | 7 | /** 8 | * Author: MrCrayfish 9 | */ 10 | public enum ButtonIcons implements SettingEnum 11 | { 12 | DEFAULT("controllable.controller.default"), 13 | PLAYSTATION_5("controllable.controller.playstation_5"), 14 | PLAYSTATION_4("controllable.controller.playstation_4"), 15 | PLAYSTATION_3("controllable.controller.playstation_3"), 16 | XBOX_ONE("controllable.controller.xbox_one"), 17 | XBOX_360("controllable.controller.xbox_360"), 18 | SWITCH_JOYCONS("controllable.controller.switch_joycons"), 19 | SWITCH_CONTROLLER("controllable.controller.switch_controller"), 20 | GAMECUBE("controllable.controller.gamecube"), 21 | STEAM("controllable.controller.steam"); 22 | 23 | public static final ResourceLocation TEXTURE = Utils.resource("textures/gui/buttons.png"); 24 | public static final int TEXTURE_WIDTH = Buttons.LENGTH * 13; 25 | public static final int TEXTURE_HEIGHT = values().length * 13; 26 | 27 | private final String key; 28 | 29 | ButtonIcons(String key) 30 | { 31 | this.key = key; 32 | } 33 | 34 | @Override 35 | public String getKey() 36 | { 37 | return this.key; 38 | } 39 | 40 | public static void main(String[] args) 41 | { 42 | System.out.print("\""); 43 | for(int i = 0; i < values().length * Buttons.LENGTH; i++) 44 | { 45 | System.out.print("\\u" + Integer.toHexString((33 + i) | 0x10000).substring(1)); 46 | if(i > 0 && (i + 1) % Buttons.LENGTH == 0) 47 | { 48 | System.out.print("\","); 49 | System.out.println(); 50 | System.out.print("\""); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/settings/CursorStyle.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.settings; 2 | 3 | import com.mrcrayfish.controllable.util.Utils; 4 | import net.minecraft.resources.ResourceLocation; 5 | 6 | /** 7 | * Author: MrCrayfish 8 | */ 9 | public enum CursorStyle implements SettingEnum 10 | { 11 | LIGHT("controllable.cursor.light", ItemHeldBehaviour.HIDE, true), 12 | DARK("controllable.cursor.dark", ItemHeldBehaviour.HIDE, true), 13 | CONSOLE("controllable.cursor.console", ItemHeldBehaviour.SHOW, false), 14 | CONSOLE_PLUS("controllable.cursor.console_plus", ItemHeldBehaviour.HIDE, true), 15 | LEGACY_LIGHT("controllable.cursor.legacy_light", ItemHeldBehaviour.HIDE, true), 16 | LEGACY_DARK("controllable.cursor.legacy_dark", ItemHeldBehaviour.HIDE, true); 17 | 18 | public static final ResourceLocation TEXTURE = Utils.resource("textures/gui/cursor.png"); 19 | 20 | private final String key; 21 | private final ItemHeldBehaviour behaviour; 22 | private final boolean scaleHover; 23 | 24 | CursorStyle(String key, ItemHeldBehaviour behaviour, boolean scaleHover) 25 | { 26 | this.key = key; 27 | this.behaviour = behaviour; 28 | this.scaleHover = scaleHover; 29 | } 30 | 31 | @Override 32 | public String getKey() 33 | { 34 | return this.key; 35 | } 36 | 37 | public ItemHeldBehaviour getBehaviour() 38 | { 39 | return this.behaviour; 40 | } 41 | 42 | public boolean isScaleHover() 43 | { 44 | return this.scaleHover; 45 | } 46 | 47 | public enum ItemHeldBehaviour 48 | { 49 | SHOW, HIDE; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/settings/SettingEnum.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.settings; 2 | 3 | import net.minecraft.network.chat.Component; 4 | 5 | /** 6 | * Author: MrCrayfish 7 | */ 8 | public interface SettingEnum 9 | { 10 | String getKey(); 11 | 12 | default Component getLabel() 13 | { 14 | return Component.translatable(this.getKey()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/settings/SettingProvider.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.settings; 2 | 3 | import net.minecraft.client.gui.components.AbstractWidget; 4 | 5 | import java.util.function.Supplier; 6 | 7 | /** 8 | * Author: MrCrayfish 9 | */ 10 | public interface SettingProvider 11 | { 12 | Supplier createWidget(int x, int y, int width, int height); 13 | } 14 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/settings/SneakMode.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.settings; 2 | 3 | /** 4 | * Author: MrCrayfish 5 | */ 6 | public enum SneakMode implements SettingEnum 7 | { 8 | TOGGLE("controllable.sneak_mode.toggle"), 9 | HOLD("controllable.sneak_mode.hold"); 10 | 11 | private final String key; 12 | 13 | SneakMode(String key) 14 | { 15 | this.key = key; 16 | } 17 | 18 | @Override 19 | public String getKey() 20 | { 21 | return this.key; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/settings/SprintMode.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.settings; 2 | 3 | /** 4 | * Author: MrCrayfish 5 | */ 6 | public enum SprintMode implements SettingEnum 7 | { 8 | TOGGLE("controllable.sprint_mode.toggle"), 9 | ONCE("controllable.sprint_mode.once"); 10 | 11 | private final String key; 12 | 13 | SprintMode(String key) 14 | { 15 | this.key = key; 16 | } 17 | 18 | @Override 19 | public String getKey() 20 | { 21 | return this.key; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/settings/Thumbstick.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.settings; 2 | 3 | /** 4 | * Author: MrCrayfish 5 | */ 6 | public enum Thumbstick implements SettingEnum 7 | { 8 | LEFT("controllable.thumbstick.left"), 9 | RIGHT("controllable.thumbstick.right"); 10 | 11 | private final String key; 12 | 13 | Thumbstick(String key) 14 | { 15 | this.key = key; 16 | } 17 | 18 | @Override 19 | public String getKey() 20 | { 21 | return this.key; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/settings/VanillaSetting.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.settings; 2 | 3 | import net.minecraft.client.Minecraft; 4 | import net.minecraft.client.OptionInstance; 5 | import net.minecraft.client.gui.components.AbstractWidget; 6 | 7 | import java.util.function.Supplier; 8 | 9 | /** 10 | * Author: MrCrayfish 11 | */ 12 | public class VanillaSetting implements SettingProvider 13 | { 14 | private final Supplier> optionSupplier; 15 | 16 | public VanillaSetting(Supplier> optionSupplier) 17 | { 18 | this.optionSupplier = optionSupplier; 19 | } 20 | 21 | @Override 22 | public Supplier createWidget(int x, int y, int width, int height) 23 | { 24 | return () -> this.optionSupplier.get().createButton(Minecraft.getInstance().options, x, y, width); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/util/EventHelper.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.util; 2 | 3 | import com.mrcrayfish.controllable.client.input.Controller; 4 | import com.mrcrayfish.controllable.event.ControllerEvents; 5 | import com.mrcrayfish.controllable.event.Value; 6 | 7 | /** 8 | * Author: MrCrayfish 9 | */ 10 | public class EventHelper 11 | { 12 | public static boolean postMoveEvent() 13 | { 14 | return ControllerEvents.UPDATE_MOVEMENT.post().handle(); 15 | } 16 | 17 | public static boolean postInputEvent(Controller controller, Value newButton, int button, boolean state) 18 | { 19 | return ControllerEvents.INPUT.post().handle(controller, newButton, button, state); 20 | } 21 | 22 | public static boolean postButtonEvent(Controller controller) 23 | { 24 | return ControllerEvents.BUTTON.post().handle(controller); 25 | } 26 | 27 | public static boolean postUpdateCameraEvent(Value yawSpeed, Value pitchSpeed) 28 | { 29 | return ControllerEvents.UPDATE_CAMERA.post().handle(yawSpeed, pitchSpeed); 30 | } 31 | 32 | public static boolean postRenderMiniPlayer() 33 | { 34 | return ControllerEvents.RENDER_MINI_PLAYER.post().handle(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/util/InputHelper.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.util; 2 | 3 | import com.mrcrayfish.controllable.Controllable; 4 | import com.mrcrayfish.controllable.client.binding.ButtonBinding; 5 | import com.mrcrayfish.controllable.client.binding.ButtonBindings; 6 | import com.mrcrayfish.controllable.client.input.Controller; 7 | import net.minecraft.util.Mth; 8 | 9 | /** 10 | * Author: MrCrayfish 11 | */ 12 | public class InputHelper 13 | { 14 | public static float getCombinedPressedValue(Controller controller, ButtonBinding first, ButtonBinding second) 15 | { 16 | ButtonBinding active = Controllable.getInputHandler().getActiveVirtualBinding(); 17 | float firstValue = active == first ? controller.getPressedValue(ButtonBindings.RADIAL_MENU.getButton()) : controller.getPressedValue(first.getButton()); 18 | float secondValue = active == second ? controller.getPressedValue(ButtonBindings.RADIAL_MENU.getButton()) : controller.getPressedValue(second.getButton()); 19 | if(firstValue > 0 && secondValue > 0) // Both pressed equals centered 20 | return 0; 21 | return secondValue - firstValue; 22 | } 23 | 24 | public static float applyDeadzone(float input, float deadZone) 25 | { 26 | return Mth.sign(input) * Math.max(Mth.abs(input) - deadZone, 0.0F) / (1.0F - deadZone); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/client/util/MutableClientInput.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.util; 2 | 3 | import com.mrcrayfish.controllable.platform.ClientServices; 4 | import net.minecraft.client.player.ClientInput; 5 | import net.minecraft.world.entity.player.Input; 6 | import org.joml.Vector2f; 7 | 8 | /** 9 | * Author: MrCrayfish 10 | */ 11 | public class MutableClientInput 12 | { 13 | private final ClientInput original; 14 | private boolean changed; 15 | private boolean forward; 16 | private boolean backward; 17 | private boolean left; 18 | private boolean right; 19 | private boolean jump; 20 | private boolean shift; 21 | private boolean sprint; 22 | private final Vector2f moveVector; 23 | 24 | public MutableClientInput(ClientInput input) 25 | { 26 | this.original = input; 27 | this.moveVector = new Vector2f(input.getMoveVector().x, input.getMoveVector().y); 28 | } 29 | 30 | public void apply() 31 | { 32 | if(this.changed) 33 | { 34 | this.original.keyPresses = new Input( 35 | this.original.keyPresses.forward() || this.forward, 36 | this.original.keyPresses.backward() || this.backward, 37 | this.original.keyPresses.left() || this.left, 38 | this.original.keyPresses.right() || this.right, 39 | this.original.keyPresses.jump() || this.jump, 40 | this.original.keyPresses.shift() || this.shift, 41 | this.original.keyPresses.sprint() || this.sprint 42 | ); 43 | ClientServices.CLIENT.updateMoveVector(this.original, this.moveVector); 44 | } 45 | } 46 | 47 | public void setForward(boolean forward) 48 | { 49 | this.forward = forward; 50 | this.changed = true; 51 | } 52 | 53 | public void setBackward(boolean backward) 54 | { 55 | this.backward = backward; 56 | this.changed = true; 57 | } 58 | 59 | public void setLeft(boolean left) 60 | { 61 | this.left = left; 62 | this.changed = true; 63 | } 64 | 65 | public void setRight(boolean right) 66 | { 67 | this.right = right; 68 | this.changed = true; 69 | } 70 | 71 | public void setJump(boolean jump) 72 | { 73 | this.jump = jump; 74 | this.changed = true; 75 | } 76 | 77 | public void setShift(boolean shift) 78 | { 79 | this.shift = shift; 80 | this.changed = true; 81 | } 82 | 83 | public void setSprint(boolean sprint) 84 | { 85 | this.sprint = sprint; 86 | this.changed = true; 87 | } 88 | 89 | public void setLeftImpulse(float leftImpulse) 90 | { 91 | this.moveVector.set(leftImpulse, this.moveVector.y); 92 | this.changed = true; 93 | } 94 | 95 | public void setForwardImpulse(float forwardImpulse) 96 | { 97 | this.moveVector.set(this.moveVector.x, forwardImpulse); 98 | this.changed = true; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/event/Value.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.event; 2 | 3 | /** 4 | * Author: MrCrayfish 5 | */ 6 | public class Value 7 | { 8 | private T value; 9 | 10 | public Value(T initialValue) 11 | { 12 | this.value = initialValue; 13 | } 14 | 15 | public void set(T value) 16 | { 17 | this.value = value; 18 | } 19 | 20 | public T get() 21 | { 22 | return this.value; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/integration/ControllableJeiPlugin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.integration; 2 | 3 | import com.mrcrayfish.controllable.Constants; 4 | import com.mrcrayfish.controllable.util.Utils; 5 | import mezz.jei.api.IModPlugin; 6 | import mezz.jei.api.JeiPlugin; 7 | import mezz.jei.api.runtime.IJeiRuntime; 8 | import net.minecraft.resources.ResourceLocation; 9 | 10 | /** 11 | * Author: MrCrayfish 12 | */ 13 | @JeiPlugin 14 | public class ControllableJeiPlugin implements IModPlugin 15 | { 16 | private static final ResourceLocation ID = Utils.resource("jei_plugin"); 17 | 18 | private static IJeiRuntime runtime; 19 | 20 | @Override 21 | public ResourceLocation getPluginUid() 22 | { 23 | return ID; 24 | } 25 | 26 | @Override 27 | public void onRuntimeAvailable(IJeiRuntime runtime) 28 | { 29 | ControllableJeiPlugin.runtime = runtime; 30 | } 31 | 32 | @Override 33 | public void onRuntimeUnavailable() 34 | { 35 | ControllableJeiPlugin.runtime = null; 36 | } 37 | 38 | public static IJeiRuntime getRuntime() 39 | { 40 | return runtime; 41 | } 42 | } -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/integration/JeiSupport.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.integration; 2 | 3 | import com.mrcrayfish.controllable.client.gui.navigation.BasicNavigationPoint; 4 | import com.mrcrayfish.controllable.client.gui.navigation.NavigationPoint; 5 | import com.mrcrayfish.controllable.client.gui.navigation.WidgetNavigationPoint; 6 | import com.mrcrayfish.controllable.mixin.client.jei.GuiIconToggleButtonMixin; 7 | import com.mrcrayfish.controllable.mixin.client.jei.IngredientGridMixin; 8 | import com.mrcrayfish.controllable.mixin.client.jei.IngredientGridWithNavigationMixin; 9 | import com.mrcrayfish.controllable.mixin.client.jei.IngredientListOverlayMixin; 10 | import com.mrcrayfish.controllable.mixin.client.jei.PageNavigationMixin; 11 | import mezz.jei.common.util.ImmutableRect2i; 12 | import mezz.jei.gui.PageNavigation; 13 | import mezz.jei.gui.elements.GuiIconButton; 14 | import mezz.jei.gui.elements.GuiIconToggleButton; 15 | import mezz.jei.gui.overlay.IngredientGrid; 16 | import mezz.jei.gui.overlay.IngredientGridWithNavigation; 17 | import mezz.jei.gui.overlay.IngredientListRenderer; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.Optional; 22 | 23 | /** 24 | * Author: MrCrayfish 25 | */ 26 | public class JeiSupport 27 | { 28 | public static List getNavigationPoints() 29 | { 30 | List points = new ArrayList<>(); 31 | Optional.ofNullable(ControllableJeiPlugin.getRuntime()).ifPresent(runtime -> 32 | { 33 | if(runtime.getIngredientListOverlay().isListDisplayed()) 34 | { 35 | // JEI just needs getters, and I wouldn't have to do this mess 36 | IngredientGridWithNavigation ingredientGridWithNavigation = ((IngredientListOverlayMixin) runtime.getIngredientListOverlay()).controllableGetContents(); 37 | IngredientGrid ingredientGrid = ((IngredientGridWithNavigationMixin) ingredientGridWithNavigation).controllableGetIngredientGrid(); 38 | IngredientListRenderer ingredientListRenderer = ((IngredientGridMixin) ingredientGrid).controllableGetIngredientListRenderer(); 39 | 40 | // Add each item on the screen as a navigation point 41 | ingredientListRenderer.getSlots().forEach(slot -> 42 | { 43 | ImmutableRect2i area = slot.getArea(); 44 | points.add(new BasicNavigationPoint(area.getX() + area.getWidth() / 2.0, area.getY() + area.getHeight() / 2.0)); 45 | }); 46 | 47 | PageNavigation navigation = ((IngredientGridWithNavigationMixin) ingredientGridWithNavigation).controllableGetNavigation(); 48 | GuiIconButton backButton = ((PageNavigationMixin) navigation).controllableGetBackButton(); 49 | points.add(new WidgetNavigationPoint(backButton)); 50 | GuiIconButton nextButton = ((PageNavigationMixin) navigation).controllableGetNextButton(); 51 | points.add(new WidgetNavigationPoint(nextButton)); 52 | 53 | GuiIconToggleButton configToggleButton = ((IngredientListOverlayMixin) runtime.getIngredientListOverlay()).controllableGetConfigButton(); 54 | GuiIconButton configButton = ((GuiIconToggleButtonMixin) configToggleButton).controllableGetButton(); 55 | points.add(new WidgetNavigationPoint(nextButton)); 56 | } 57 | }); 58 | return points; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/mixin/client/ClientRecipeBookMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import com.mrcrayfish.controllable.Config; 5 | import net.minecraft.client.ClientRecipeBook; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | 9 | @Mixin(ClientRecipeBook.class) 10 | public class ClientRecipeBookMixin 11 | { 12 | /* 13 | * Prevents grouping recipes in the recipe book when quick craft mode is enabled. Interacting 14 | * with grouped recipes is difficult on controller, so flattening them all to single entries 15 | * feels a lot better. If quick craft option is toggled on or off during runtime, it will 16 | * automatically group/ungroup the recipes. 17 | */ 18 | @ModifyExpressionValue(method = "categorizeAndGroupRecipes", at = @At(value = "INVOKE", target = "Ljava/util/OptionalInt;isEmpty()Z")) 19 | private static boolean test(boolean original) 20 | { 21 | return original || Config.CLIENT.options.quickCraft.get(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/mixin/client/ContainerScreenMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import com.mrcrayfish.controllable.Controllable; 5 | import com.mrcrayfish.controllable.client.binding.ButtonBindings; 6 | import com.mrcrayfish.controllable.client.input.Controller; 7 | import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | 11 | /** 12 | * Author: MrCrayfish 13 | */ 14 | @Mixin(AbstractContainerScreen.class) 15 | public abstract class ContainerScreenMixin 16 | { 17 | @ModifyExpressionValue(method = "mouseClicked", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/InputConstants;isKeyDown(JI)Z", ordinal = 0)) 18 | private boolean isQuickMovePressedOnClick(boolean original) 19 | { 20 | Controller controller = Controllable.getController(); 21 | if(controller != null && ButtonBindings.QUICK_MOVE.isButtonDown()) 22 | { 23 | return true; 24 | } 25 | return original; 26 | } 27 | 28 | @ModifyExpressionValue(method = "mouseReleased", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/InputConstants;isKeyDown(JI)Z", ordinal = 0)) 29 | private boolean isQuickMovePressedOnReleased(boolean original) 30 | { 31 | Controller controller = Controllable.getController(); 32 | if(controller != null && ButtonBindings.QUICK_MOVE.isButtonDown()) 33 | { 34 | return true; 35 | } 36 | return original; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/mixin/client/GameRendererMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import com.mrcrayfish.controllable.Controllable; 5 | import com.mrcrayfish.controllable.client.input.Controller; 6 | import com.mrcrayfish.controllable.client.overlay.OverlayRenderer; 7 | import net.minecraft.client.DeltaTracker; 8 | import net.minecraft.client.Minecraft; 9 | import net.minecraft.client.gui.GuiGraphics; 10 | import net.minecraft.client.renderer.GameRenderer; 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 | import org.spongepowered.asm.mixin.injection.At; 16 | import org.spongepowered.asm.mixin.injection.Inject; 17 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 18 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 19 | 20 | /** 21 | * Author: MrCrayfish 22 | */ 23 | @Mixin(GameRenderer.class) 24 | public class GameRendererMixin 25 | { 26 | @Unique 27 | private int controllable$captureMouseX; 28 | 29 | @Unique 30 | private int controllable$captureMouseY; 31 | 32 | @Unique 33 | private GuiGraphics controllable$graphics; 34 | 35 | @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;getOverlay()Lnet/minecraft/client/gui/screens/Overlay;", ordinal = 0)) 36 | private void captureLocals(DeltaTracker tracker, boolean p_109096_, CallbackInfo ci, @Local(ordinal = 0) int mouseX, @Local(ordinal = 1) int mouseY, @Local(ordinal = 0) GuiGraphics graphics) 37 | { 38 | this.controllable$captureMouseX = mouseX; 39 | this.controllable$captureMouseY = mouseY; 40 | this.controllable$graphics = graphics; 41 | } 42 | 43 | @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/render/GuiRenderer;render(Lcom/mojang/blaze3d/buffers/GpuBufferSlice;)V")) 44 | private void renderOverlay(DeltaTracker tracker, boolean p_109096_, CallbackInfo ci) 45 | { 46 | if(this.controllable$graphics != null) 47 | { 48 | OverlayRenderer.draw(this.controllable$graphics, this.controllable$captureMouseX, this.controllable$captureMouseY, tracker); 49 | this.controllable$graphics = null; 50 | } 51 | } 52 | 53 | @ModifyVariable(method = "render", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/CommandEncoder;clearDepthTexture(Lcom/mojang/blaze3d/textures/GpuTexture;D)V", remap = false, ordinal = 0), index = 4, ordinal = 0, require = 1) 54 | private int controllableModifyMouseX(int original) 55 | { 56 | Controller controller = Controllable.getController(); 57 | if(controller != null && controller.isUsingVirtualCursor()) 58 | { 59 | return (int) Controllable.getCursor().getRenderScreenX(); 60 | } 61 | return original; 62 | } 63 | 64 | @ModifyVariable(method = "render", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/CommandEncoder;clearDepthTexture(Lcom/mojang/blaze3d/textures/GpuTexture;D)V", remap = false, ordinal = 0), index = 5, ordinal = 1, require = 1) 65 | private int controllableModifyMouseY(int original) 66 | { 67 | Controller controller = Controllable.getController(); 68 | if(controller != null && controller.isUsingVirtualCursor()) 69 | { 70 | return (int) Controllable.getCursor().getRenderScreenY(); 71 | } 72 | return original; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/mixin/client/GuiMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import com.mrcrayfish.controllable.Config; 5 | import com.mrcrayfish.controllable.Controllable; 6 | import com.mrcrayfish.controllable.client.binding.ButtonBindings; 7 | import net.minecraft.client.DeltaTracker; 8 | import net.minecraft.client.gui.Gui; 9 | import net.minecraft.client.gui.GuiGraphics; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 14 | 15 | /** 16 | * Author: MrCrayfish 17 | */ 18 | @Mixin(Gui.class) 19 | public class GuiMixin 20 | { 21 | @ModifyExpressionValue(method = "renderTabList", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/KeyMapping;isDown()Z")) 22 | private boolean controllableRenderPlayerList(boolean original) 23 | { 24 | return original || Controllable.getController() != null && ButtonBindings.PLAYER_LIST.isButtonDown(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/mixin/client/LocalPlayerMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client; 2 | 3 | import com.mrcrayfish.controllable.Controllable; 4 | import net.minecraft.client.player.LocalPlayer; 5 | import net.minecraft.util.Mth; 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 | /** 13 | * Author: MrCrayfish 14 | */ 15 | @Mixin(LocalPlayer.class) 16 | public class LocalPlayerMixin 17 | { 18 | @Unique 19 | private float controllable$lastHealth; 20 | 21 | @Inject(method = "hurtTo", at = @At(value = "HEAD")) 22 | private void controllableOnPlayerDamage(float newHealth, CallbackInfo ci) 23 | { 24 | if(newHealth < this.controllable$lastHealth) 25 | { 26 | float damage = Math.max(0, this.controllable$lastHealth - newHealth); 27 | LocalPlayer player = (LocalPlayer) (Object) this; 28 | Controllable.getRumbleHandler().onDamage(player, damage); 29 | } 30 | this.controllable$lastHealth = newHealth; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/mixin/client/MouseHandlerMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client; 2 | 3 | import com.mrcrayfish.controllable.Controllable; 4 | import com.mrcrayfish.controllable.client.VirtualCursor; 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.client.MouseHandler; 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(MouseHandler.class) 13 | public class MouseHandlerMixin 14 | { 15 | @Inject(method = "onMove", at = @At(value = "HEAD")) 16 | private void controllableOnMouseMoved(long windowId, double mouseX, double mouseY, CallbackInfo ci) 17 | { 18 | if(windowId == Minecraft.getInstance().getWindow().getWindow()) 19 | { 20 | Controllable.getCursor().setMode(VirtualCursor.CursorMode.MOUSE); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/mixin/client/MouseHelperMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client; 2 | 3 | import com.mrcrayfish.controllable.Controllable; 4 | import com.mrcrayfish.controllable.client.InputHandler; 5 | import com.mrcrayfish.controllable.client.input.Controller; 6 | import net.minecraft.client.Minecraft; 7 | import net.minecraft.client.MouseHandler; 8 | import org.spongepowered.asm.mixin.Final; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | import org.spongepowered.asm.mixin.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 | /** 17 | * Author: MrCrayfish 18 | */ 19 | @Mixin(MouseHandler.class) 20 | public abstract class MouseHelperMixin 21 | { 22 | @Unique 23 | private boolean controllable$releaseBypass; 24 | 25 | @Shadow 26 | private double accumulatedDX; 27 | 28 | @Shadow 29 | private double accumulatedDY; 30 | 31 | @Shadow 32 | public abstract void releaseMouse(); 33 | 34 | /* 35 | * In Controllable, the mouse remains grabbed (aka hidden) when in screens like the inventory. 36 | * However, this allows the player to turn the camera while in a screen, something that isn't 37 | * normally possible. To fix that, as soon as we detect movement from the mouse, we make it 38 | * appear again. 39 | */ 40 | @Inject(method = "handleAccumulatedMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MouseHandler;isMouseGrabbed()Z"), cancellable = true) 41 | private void controllableBeforeUpdateLook(CallbackInfo ci) 42 | { 43 | Minecraft minecraft = Minecraft.getInstance(); 44 | if(minecraft.screen != null) 45 | { 46 | if(Math.abs(this.accumulatedDX) > 0 || Math.abs(this.accumulatedDY) > 0) 47 | { 48 | this.controllable$releaseBypass = true; 49 | this.releaseMouse(); // Release mouse since it may be grabbed 50 | this.controllable$releaseBypass = false; 51 | this.accumulatedDX = 0; 52 | this.accumulatedDY = 0; 53 | ci.cancel(); 54 | } 55 | } 56 | } 57 | 58 | /* 59 | * Prevents the cursor from being released when opening screens when using a controller. Since 60 | * Controllable uses a virtual cursor, it doesn't make sense to have the system cursor appear 61 | * when opening a screen (like the inventory). 62 | */ 63 | @Inject(method = "releaseMouse", at = @At(value = "HEAD"), cancellable = true) 64 | private void controllableGrabCursor(CallbackInfo ci) 65 | { 66 | Controller controller = Controllable.getController(); 67 | if(controller != null && controller.isUsingVirtualCursor() && !this.controllable$releaseBypass) 68 | { 69 | ci.cancel(); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/mixin/client/OverlayRecipeComponentAccessor.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client; 2 | 3 | import net.minecraft.client.gui.components.AbstractWidget; 4 | import net.minecraft.client.gui.screens.recipebook.OverlayRecipeComponent; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Author: MrCrayfish 12 | */ 13 | @Mixin(OverlayRecipeComponent.class) 14 | public interface OverlayRecipeComponentAccessor 15 | { 16 | @Accessor("recipeButtons") 17 | List controllableGetRecipeButtons(); 18 | } 19 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/mixin/client/RecipeBookComponentAccessor.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client; 2 | 3 | import net.minecraft.client.gui.components.StateSwitchingButton; 4 | import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent; 5 | import net.minecraft.client.gui.screens.recipebook.RecipeBookPage; 6 | import net.minecraft.client.gui.screens.recipebook.RecipeBookTabButton; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.gen.Accessor; 9 | import org.spongepowered.asm.mixin.gen.Invoker; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * Author: MrCrayfish 15 | */ 16 | @Mixin(RecipeBookComponent.class) 17 | public interface RecipeBookComponentAccessor 18 | { 19 | @Accessor("filterButton") 20 | StateSwitchingButton controllableGetFilterButton(); 21 | 22 | @Accessor("tabButtons") 23 | List controllableGetRecipeTabs(); 24 | 25 | @Accessor("recipeBookPage") 26 | RecipeBookPage controllableGetRecipeBookPage(); 27 | 28 | @Accessor("selectedTab") 29 | RecipeBookTabButton controllableGetCurrentTab(); 30 | 31 | @Accessor("selectedTab") 32 | void controllableSetCurrentTab(RecipeBookTabButton tab); 33 | 34 | @Invoker("updateCollections") 35 | void controllableUpdateCollections(boolean resetPages, boolean filtering); 36 | 37 | @Accessor("tabButtons") 38 | List controllableGetTabButtons(); 39 | 40 | @Invoker("isFiltering") 41 | boolean controllableIsFiltering(); 42 | } 43 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/mixin/client/RecipeBookPageAccessor.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client; 2 | 3 | import net.minecraft.client.gui.components.StateSwitchingButton; 4 | import net.minecraft.client.gui.screens.recipebook.OverlayRecipeComponent; 5 | import net.minecraft.client.gui.screens.recipebook.RecipeBookPage; 6 | import net.minecraft.client.gui.screens.recipebook.RecipeButton; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.gen.Accessor; 9 | import org.spongepowered.asm.mixin.gen.Invoker; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * Author: MrCrayfish 15 | */ 16 | @Mixin(RecipeBookPage.class) 17 | public interface RecipeBookPageAccessor 18 | { 19 | @Accessor("buttons") 20 | List controllableGetButtons(); 21 | 22 | @Accessor("forwardButton") 23 | StateSwitchingButton controllableGetForwardButton(); 24 | 25 | @Accessor("backButton") 26 | StateSwitchingButton controllableGetBackButton(); 27 | 28 | @Accessor("currentPage") 29 | int controllableGetCurrentPage(); 30 | 31 | @Accessor("currentPage") 32 | void controllableSetCurrentPage(int page); 33 | 34 | @Invoker("updateButtonsForPage") 35 | void controllableUpdateButtonsForPage(); 36 | 37 | @Accessor("overlay") 38 | OverlayRecipeComponent controllableGetOverlay(); 39 | } 40 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/mixin/client/RecipeBookPageMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client; 2 | 3 | import com.mrcrayfish.controllable.Config; 4 | import com.mrcrayfish.controllable.Controllable; 5 | import com.mrcrayfish.controllable.client.binding.ButtonBindings; 6 | import com.mrcrayfish.controllable.client.input.Controller; 7 | import com.mrcrayfish.controllable.client.util.ClientHelper; 8 | import com.mrcrayfish.controllable.platform.ClientServices; 9 | import net.minecraft.ChatFormatting; 10 | import net.minecraft.client.Minecraft; 11 | import net.minecraft.client.gui.GuiGraphics; 12 | import net.minecraft.client.gui.components.AbstractWidget; 13 | import net.minecraft.client.gui.screens.recipebook.OverlayRecipeComponent; 14 | import net.minecraft.client.gui.screens.recipebook.RecipeBookPage; 15 | import net.minecraft.network.chat.Component; 16 | import org.spongepowered.asm.mixin.Final; 17 | import org.spongepowered.asm.mixin.Mixin; 18 | import org.spongepowered.asm.mixin.Shadow; 19 | import org.spongepowered.asm.mixin.injection.At; 20 | import org.spongepowered.asm.mixin.injection.Inject; 21 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 22 | 23 | import java.util.List; 24 | 25 | /** 26 | * Author: MrCrayfish 27 | */ 28 | @Mixin(RecipeBookPage.class) 29 | public class RecipeBookPageMixin 30 | { 31 | @Shadow 32 | @Final 33 | private OverlayRecipeComponent overlay; 34 | 35 | @Shadow 36 | private Minecraft minecraft; 37 | 38 | @Inject(method = "renderTooltip", at = @At(value = "TAIL")) 39 | private void controllableRenderTooltipTail(GuiGraphics graphics, int mouseX, int mouseY, CallbackInfo ci) 40 | { 41 | Controller controller = Controllable.getController(); 42 | if(controller == null || !controller.isBeingUsed() || !Config.CLIENT.options.quickCraft.get()) 43 | return; 44 | 45 | if(this.minecraft.screen == null || !this.overlay.isVisible()) 46 | return; 47 | 48 | List recipeButtons = ClientHelper.mixinGetRecipeButtons(this.overlay); 49 | recipeButtons.stream().filter(AbstractWidget::isHoveredOrFocused).findFirst().ifPresent(btn -> { 50 | if(ClientServices.CLIENT.isOverlayRecipeButtonCraftable(btn)) { 51 | Component craftText = Component.translatable("controllable.tooltip.craft", ClientHelper.getButtonComponent(ButtonBindings.PICKUP_ITEM.getButton())).withStyle(ChatFormatting.YELLOW); 52 | graphics.setTooltipForNextFrame(craftText, mouseX, mouseY); 53 | } 54 | }); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/mixin/client/TimerAccessor.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client; 2 | 3 | import net.minecraft.client.DeltaTracker; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Accessor; 6 | 7 | /** 8 | * Author: MrCrayfish 9 | */ 10 | @Mixin(DeltaTracker.Timer.class) 11 | public interface TimerAccessor 12 | { 13 | @Accessor("deltaTickResidual") 14 | float controllable$DeltaTickResidual(); 15 | } 16 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/mixin/client/jei/GuiIconToggleButtonMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client.jei; 2 | 3 | import mezz.jei.gui.elements.GuiIconButton; 4 | import mezz.jei.gui.elements.GuiIconToggleButton; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Pseudo; 7 | import org.spongepowered.asm.mixin.gen.Accessor; 8 | 9 | /** 10 | * Author: MrCrayfish 11 | */ 12 | @Pseudo 13 | @Mixin(GuiIconToggleButton.class) 14 | public interface GuiIconToggleButtonMixin 15 | { 16 | @Accessor(value = "button", remap = false) 17 | GuiIconButton controllableGetButton(); 18 | } 19 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/mixin/client/jei/IngredientGridMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client.jei; 2 | 3 | import mezz.jei.gui.overlay.IngredientGrid; 4 | import mezz.jei.gui.overlay.IngredientListRenderer; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Pseudo; 7 | import org.spongepowered.asm.mixin.gen.Accessor; 8 | 9 | /** 10 | * Yes this is terrible. You should never Mixin to other mods. Do I expect mezz to 11 | * support Controllable? No. This is the terrible solution we have to live with. 12 | * 13 | * Author: MrCrayfish 14 | */ 15 | @Pseudo 16 | @Mixin(IngredientGrid.class) 17 | public interface IngredientGridMixin 18 | { 19 | @Accessor(value = "ingredientListRenderer", remap = false) 20 | IngredientListRenderer controllableGetIngredientListRenderer(); 21 | } 22 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/mixin/client/jei/IngredientGridWithNavigationMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client.jei; 2 | 3 | import mezz.jei.gui.PageNavigation; 4 | import mezz.jei.gui.overlay.IngredientGrid; 5 | import mezz.jei.gui.overlay.IngredientGridWithNavigation; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Pseudo; 8 | import org.spongepowered.asm.mixin.gen.Accessor; 9 | 10 | /** 11 | * Author: MrCrayfish 12 | */ 13 | @Pseudo 14 | @Mixin(IngredientGridWithNavigation.class) 15 | public interface IngredientGridWithNavigationMixin 16 | { 17 | @Accessor(value = "navigation", remap = false) 18 | PageNavigation controllableGetNavigation(); 19 | 20 | @Accessor(value = "ingredientGrid", remap = false) 21 | IngredientGrid controllableGetIngredientGrid(); 22 | } 23 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/mixin/client/jei/IngredientListOverlayMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client.jei; 2 | 3 | import mezz.jei.gui.elements.GuiIconToggleButton; 4 | import mezz.jei.gui.overlay.IngredientGridWithNavigation; 5 | import mezz.jei.gui.overlay.IngredientListOverlay; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Pseudo; 8 | import org.spongepowered.asm.mixin.gen.Accessor; 9 | 10 | /** 11 | * Author: MrCrayfish 12 | */ 13 | @Pseudo 14 | @Mixin(IngredientListOverlay.class) 15 | public interface IngredientListOverlayMixin 16 | { 17 | @Accessor(value = "contents", remap = false) 18 | IngredientGridWithNavigation controllableGetContents(); 19 | 20 | @Accessor(value = "configButton", remap = false) 21 | GuiIconToggleButton controllableGetConfigButton(); 22 | } 23 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/mixin/client/jei/MouseUtilMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client.jei; 2 | 3 | import com.mrcrayfish.controllable.Controllable; 4 | import com.mrcrayfish.controllable.client.input.Controller; 5 | import mezz.jei.gui.input.MouseUtil; 6 | import net.minecraft.client.Minecraft; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Pseudo; 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 | /** 14 | * JEI has a utility class to get the X and Y position of the mouse when it's not possible to 15 | * retrieve them. This causes issues when virtual mouse is turned on (which is most of the time). 16 | * This fixes compatibility with the mod. 17 | * 18 | * Author: MrCrayfish 19 | */ 20 | @Pseudo 21 | @Mixin(MouseUtil.class) 22 | public class MouseUtilMixin 23 | { 24 | @Inject(method = "getX", at = @At(value = "TAIL"), remap = false, cancellable = true) 25 | private static void controllableGetX(CallbackInfoReturnable cir) 26 | { 27 | Controller controller = Controllable.getController(); 28 | if(controller != null && controller.isUsingVirtualCursor()) 29 | { 30 | cir.setReturnValue(Controllable.getCursor().getRenderScreenX()); 31 | } 32 | } 33 | 34 | @Inject(method = "getY", at = @At(value = "TAIL"), remap = false, cancellable = true) 35 | private static void controllableGetY(CallbackInfoReturnable cir) 36 | { 37 | Controller controller = Controllable.getController(); 38 | if(controller != null && controller.isUsingVirtualCursor()) 39 | { 40 | cir.setReturnValue(Controllable.getCursor().getRenderScreenY()); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/mixin/client/jei/PageNavigationMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client.jei; 2 | 3 | import mezz.jei.gui.PageNavigation; 4 | import mezz.jei.gui.elements.GuiIconButton; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Pseudo; 7 | import org.spongepowered.asm.mixin.gen.Accessor; 8 | 9 | /** 10 | * Author: MrCrayfish 11 | */ 12 | @Pseudo 13 | @Mixin(PageNavigation.class) 14 | public interface PageNavigationMixin 15 | { 16 | @Accessor(value = "nextButton", remap = false) 17 | GuiIconButton controllableGetNextButton(); 18 | 19 | @Accessor(value = "backButton", remap = false) 20 | GuiIconButton controllableGetBackButton(); 21 | } 22 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/mixin/client/rei/PointHelperMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client.rei; 2 | import com.mrcrayfish.controllable.Controllable; 3 | import com.mrcrayfish.controllable.client.input.Controller; 4 | import me.shedaniel.math.FloatingPoint; 5 | import me.shedaniel.math.Point; 6 | import me.shedaniel.math.impl.PointHelper; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Pseudo; 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 | /** 14 | * Author: MrCrayfish 15 | */ 16 | @Pseudo 17 | @Mixin(PointHelper.class) 18 | public class PointHelperMixin 19 | { 20 | @Inject(method = "ofMouse", at = @At(value = "HEAD"), cancellable = true, remap = false) 21 | private static void controllableMouse(CallbackInfoReturnable cir) 22 | { 23 | Controller controller = Controllable.getController(); 24 | if(controller != null && controller.isUsingVirtualCursor()) 25 | { 26 | double mouseX = Controllable.getCursor().getRenderScreenX(); 27 | double mouseY = Controllable.getCursor().getRenderScreenY(); 28 | cir.setReturnValue(new Point(mouseX, mouseY)); 29 | } 30 | } 31 | 32 | @Inject(method = "ofFloatingMouse", at = @At(value = "HEAD"), cancellable = true, remap = false) 33 | private static void controllableFloatMouse(CallbackInfoReturnable cir) 34 | { 35 | Controller controller = Controllable.getController(); 36 | if(controller != null && controller.isUsingVirtualCursor()) 37 | { 38 | double mouseX = Controllable.getCursor().getRenderScreenX(); 39 | double mouseY = Controllable.getCursor().getRenderScreenY(); 40 | cir.setReturnValue(new FloatingPoint(mouseX, mouseY)); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/platform/ClientServices.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.platform; 2 | 3 | import com.mrcrayfish.controllable.platform.services.IClientHelper; 4 | 5 | public class ClientServices 6 | { 7 | public static final IClientHelper CLIENT = Services.load(IClientHelper.class); 8 | } -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/platform/Services.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.platform; 2 | 3 | import com.mrcrayfish.controllable.platform.services.IPlatformHelper; 4 | import com.mrcrayfish.framework.Constants; 5 | 6 | import java.util.ServiceLoader; 7 | 8 | /** 9 | * Author: MrCrayfish 10 | */ 11 | public class Services 12 | { 13 | public static final IPlatformHelper PLATFORM = load(IPlatformHelper.class); 14 | 15 | public static T load(Class clazz) 16 | { 17 | final T loadedService = ServiceLoader.load(clazz).findFirst().orElseThrow(() -> new NullPointerException("Failed to load service for " + clazz.getName())); 18 | Constants.LOG.debug("Loaded {} for service {}", loadedService, clazz); 19 | return loadedService; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/platform/services/IPlatformHelper.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.platform.services; 2 | 3 | /** 4 | * Author: MrCrayfish 5 | */ 6 | public interface IPlatformHelper 7 | { 8 | default boolean isNeoForge() 9 | { 10 | return false; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/controllable/util/Utils.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.util; 2 | 3 | import com.mrcrayfish.controllable.Constants; 4 | import net.minecraft.resources.ResourceLocation; 5 | 6 | import java.nio.file.Path; 7 | 8 | /** 9 | * Author: MrCrayfish 10 | */ 11 | public class Utils 12 | { 13 | public static ResourceLocation resource(String name) 14 | { 15 | return ResourceLocation.fromNamespaceAndPath(Constants.MOD_ID, name); 16 | } 17 | 18 | public static Path getGamePath() 19 | { 20 | return com.mrcrayfish.framework.platform.Services.CONFIG.getGamePath(); 21 | } 22 | 23 | public static Path getConfigDirectory() 24 | { 25 | return com.mrcrayfish.framework.platform.Services.CONFIG.getConfigPath(); 26 | } 27 | 28 | public static boolean isModLoaded(String modId) 29 | { 30 | return com.mrcrayfish.framework.platform.Services.PLATFORM.isModLoaded(modId); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /common/src/main/resources/assets/controllable/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/Controllable/a177d203b5e0ef86c401a7819df72af5936e6153/common/src/main/resources/assets/controllable/background.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/controllable/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/Controllable/a177d203b5e0ef86c401a7819df72af5936e6153/common/src/main/resources/assets/controllable/banner.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/controllable/font/buttons.json: -------------------------------------------------------------------------------- 1 | { 2 | "providers": [ 3 | { 4 | "type": "space", 5 | "advances": { 6 | " ": 4 7 | } 8 | }, 9 | { 10 | "type": "bitmap", 11 | "file": "controllable:gui/buttons.png", 12 | "height": 12, 13 | "ascent": 9, 14 | "chars": [ 15 | "\u0021\u0022\u0023\u0024\u0025\u0026\u0027\u0028\u0029\u002a\u002b\u002c\u002d\u002e\u002f\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039\u003a\u003b\u003c\u003d\u003e\u003f", 16 | "\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004a\u004b\u004c\u004d\u004e\u004f\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057\u0058\u0059\u005a\u005b\u005c\u005d\u005e", 17 | "\u005f\u0060\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006a\u006b\u006c\u006d\u006e\u006f\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077\u0078\u0079\u007a\u007b\u007c\u007d", 18 | "\u007e\u007f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c", 19 | "\u009d\u009e\u009f\u00a0\u00a1\u00a2\u00a3\u00a4\u00a5\u00a6\u00a7\u00a8\u00a9\u00aa\u00ab\u00ac\u00ad\u00ae\u00af\u00b0\u00b1\u00b2\u00b3\u00b4\u00b5\u00b6\u00b7\u00b8\u00b9\u00ba\u00bb", 20 | "\u00bc\u00bd\u00be\u00bf\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf\u00d0\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d7\u00d8\u00d9\u00da", 21 | "\u00db\u00dc\u00dd\u00de\u00df\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed\u00ee\u00ef\u00f0\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f7\u00f8\u00f9", 22 | "\u00fa\u00fb\u00fc\u00fd\u00fe\u00ff\u0100\u0101\u0102\u0103\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b\u010c\u010d\u010e\u010f\u0110\u0111\u0112\u0113\u0114\u0115\u0116\u0117\u0118", 23 | "\u0119\u011a\u011b\u011c\u011d\u011e\u011f\u0120\u0121\u0122\u0123\u0124\u0125\u0126\u0127\u0128\u0129\u012a\u012b\u012c\u012d\u012e\u012f\u0130\u0131\u0132\u0133\u0134\u0135\u0136\u0137", 24 | "\u0138\u0139\u013a\u013b\u013c\u013d\u013e\u013f\u0140\u0141\u0142\u0143\u0144\u0145\u0146\u0147\u0148\u0149\u014a\u014b\u014c\u014d\u014e\u014f\u0150\u0151\u0152\u0153\u0154\u0155\u0156" 25 | ] 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /common/src/main/resources/assets/controllable/font/icons.json: -------------------------------------------------------------------------------- 1 | { 2 | "providers": [ 3 | { 4 | "type": "space", 5 | "advances": { 6 | " ": 4 7 | } 8 | }, 9 | { 10 | "type": "bitmap", 11 | "file": "controllable:gui/icons.png", 12 | "height": 11, 13 | "ascent": 9, 14 | "chars": [ 15 | "\u0021\u0022\u0023\u0024\u0025\u0026\u0027\u0028\u0029\u002A\u002B\u002C" 16 | ] 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /common/src/main/resources/assets/controllable/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/Controllable/a177d203b5e0ef86c401a7819df72af5936e6153/common/src/main/resources/assets/controllable/icon.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/controllable/textures/gui/buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/Controllable/a177d203b5e0ef86c401a7819df72af5936e6153/common/src/main/resources/assets/controllable/textures/gui/buttons.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/controllable/textures/gui/controller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/Controllable/a177d203b5e0ef86c401a7819df72af5936e6153/common/src/main/resources/assets/controllable/textures/gui/controller.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/controllable/textures/gui/cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/Controllable/a177d203b5e0ef86c401a7819df72af5936e6153/common/src/main/resources/assets/controllable/textures/gui/cursor.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/controllable/textures/gui/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/Controllable/a177d203b5e0ef86c401a7819df72af5936e6153/common/src/main/resources/assets/controllable/textures/gui/icons.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/controllable/textures/gui/sprites/toast/connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/Controllable/a177d203b5e0ef86c401a7819df72af5936e6153/common/src/main/resources/assets/controllable/textures/gui/sprites/toast/connection.png -------------------------------------------------------------------------------- /common/src/main/resources/controllable.common.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "com.mrcrayfish.controllable.mixin", 4 | "compatibilityLevel": "JAVA_21", 5 | "minVersion": "0.8", 6 | "refmap": "controllable.refmap.json", 7 | "client": [ 8 | "client.ClientRecipeBookMixin", 9 | "client.ContainerScreenMixin", 10 | "client.GameRendererMixin", 11 | "client.GuiMixin", 12 | "client.LocalPlayerMixin", 13 | "client.MinecraftMixin", 14 | "client.MouseHandlerMixin", 15 | "client.MouseHelperMixin", 16 | "client.OverlayRecipeComponentAccessor", 17 | "client.RecipeBookComponentAccessor", 18 | "client.RecipeBookPageAccessor", 19 | "client.RecipeBookPageMixin", 20 | "client.TimerAccessor", 21 | "client.jei.GuiIconToggleButtonMixin", 22 | "client.jei.IngredientGridMixin", 23 | "client.jei.IngredientGridWithNavigationMixin", 24 | "client.jei.IngredientListOverlayMixin", 25 | "client.jei.MouseUtilMixin", 26 | "client.jei.PageNavigationMixin", 27 | "client.rei.PointHelperMixin" 28 | ], 29 | "injectors": { 30 | "defaultRequire": 1 31 | } 32 | } -------------------------------------------------------------------------------- /common/src/main/resources/mappings/defender_game_racer_x7.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "Defender Game Racer X7", 3 | "name": "Defender Game Racer X7", 4 | "reassign": [ 5 | { 6 | "index": 0, 7 | "with": 3 8 | }, 9 | { 10 | "index": 2, 11 | "with": 0 12 | }, 13 | { 14 | "index": 3, 15 | "with": 2 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /common/src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "${mod_name}", 4 | "pack_format": 12, 5 | "forge:resource_pack_format": 12, 6 | "forge:data_pack_format": 10 7 | } 8 | } -------------------------------------------------------------------------------- /fabric/build.gradle: -------------------------------------------------------------------------------- 1 | import net.fabricmc.loom.task.RemapJarTask 2 | 3 | plugins { 4 | id 'multiloader-loader' 5 | id 'fabric-loom' 6 | } 7 | 8 | dependencies { 9 | minecraft "com.mojang:minecraft:${minecraft_version}" 10 | mappings loom.officialMojangMappings() 11 | modImplementation "net.fabricmc:fabric-loader:${fabric_loader_version}" 12 | modImplementation "net.fabricmc.fabric-api:fabric-api:${fabric_version}" 13 | modImplementation "com.mrcrayfish:framework-fabric:${minecraft_version}-${framework_version}" 14 | modCompileOnly ("dev.architectury:architectury-fabric:${architectury_api_version}") transitive false 15 | modCompileOnly ("mezz.jei:jei-${jei_minecraft_version}-fabric:${jei_version}") transitive false 16 | modCompileOnly ("dev.emi:emi-fabric:${emi_version}") transitive false 17 | modCompileOnly ("me.shedaniel:RoughlyEnoughItems-fabric:${rei_version}") transitive false 18 | modCompileOnly ("me.shedaniel.cloth:cloth-config-fabric:18.0.145") 19 | //modRuntimeOnly "squeek.appleskin:appleskin-fabric:mc1.21.3-3.0.6" 20 | library include("com.mrcrayfish:controllable-sdl:${controllable_sdl_version}") { 21 | transitive false 22 | } 23 | implementation "com.google.code.findbugs:jsr305:3.0.1" 24 | } 25 | 26 | sourceSets.main.resources.srcDir 'src/generated/resources' 27 | 28 | fabricApi { 29 | configureDataGeneration { 30 | outputDirectory.set(file("src/generated/resources")) 31 | } 32 | } 33 | 34 | loom { 35 | def aw = file("src/main/resources/${mod_id}.accesswidener") 36 | if (aw.exists()) { 37 | accessWidenerPath.set(aw) 38 | } 39 | mixin { 40 | defaultRefmapName.set("${mod_id}.refmap.json") 41 | } 42 | runs { 43 | client { 44 | client() 45 | setConfigName("Fabric Client") 46 | ideConfigGenerated(true) 47 | runDir("run") 48 | } 49 | server { 50 | server() 51 | setConfigName("Fabric Server") 52 | ideConfigGenerated(true) 53 | runDir("run") 54 | } 55 | } 56 | } 57 | 58 | remapJar { 59 | addNestedDependencies = false 60 | } 61 | 62 | task releaseJar(type: RemapJarTask) { 63 | dependsOn tasks.jar 64 | input = tasks.jar.archiveFile 65 | addNestedDependencies = true 66 | archiveClassifier = 'release' 67 | } 68 | build.finalizedBy(releaseJar) -------------------------------------------------------------------------------- /fabric/src/main/java/com/mrcrayfish/controllable/ControllableMod.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable; 2 | 3 | import com.mrcrayfish.framework.FrameworkSetup; 4 | import net.fabricmc.api.ModInitializer; 5 | 6 | public class ControllableMod implements ModInitializer 7 | { 8 | @Override 9 | public void onInitialize() 10 | { 11 | // On Fabric, no guarantee Framework initialization happens before Controllable 12 | // So we need to run the setup ourselves. 13 | FrameworkSetup.run(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /fabric/src/main/java/com/mrcrayfish/controllable/client/ClientControllableMod.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client; 2 | 3 | import com.mrcrayfish.controllable.Controllable; 4 | import net.fabricmc.api.ClientModInitializer; 5 | import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; 6 | 7 | /** 8 | * Author: MrCrayfish 9 | */ 10 | public class ClientControllableMod implements ClientModInitializer 11 | { 12 | @Override 13 | public void onInitializeClient() 14 | { 15 | ClientBootstrap.init(); 16 | ClientLifecycleEvents.CLIENT_STOPPING.register(client -> { 17 | Controllable.getControllerManager().dispose(); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /fabric/src/main/java/com/mrcrayfish/controllable/integration/ArchitecturySupport.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.integration; 2 | 3 | import dev.architectury.event.events.client.ClientScreenInputEvent; 4 | import net.minecraft.client.Minecraft; 5 | import net.minecraft.client.gui.screens.Screen; 6 | 7 | /** 8 | * Author: MrCrayfish 9 | */ 10 | public class ArchitecturySupport 11 | { 12 | public static void sendScreenMouseReleased(Screen screen, double mouseX, double mouseY, int button) 13 | { 14 | Minecraft mc = Minecraft.getInstance(); 15 | if(ClientScreenInputEvent.MOUSE_RELEASED_PRE.invoker().mouseReleased(mc, screen, mouseX, mouseY, button).isPresent()) 16 | return; 17 | if(screen.mouseReleased(mouseX, mouseY, button)) 18 | return; 19 | ClientScreenInputEvent.MOUSE_RELEASED_POST.invoker().mouseReleased(mc, screen, mouseX, mouseY, button); 20 | } 21 | 22 | public static void sendScreenMouseClick(Screen screen, double mouseX, double mouseY, int button) 23 | { 24 | Minecraft mc = Minecraft.getInstance(); 25 | if(ClientScreenInputEvent.MOUSE_CLICKED_PRE.invoker().mouseClicked(mc, screen, mouseX, mouseY, button).isPresent()) 26 | return; 27 | if(screen.mouseClicked(mouseX, mouseY, button)) 28 | return; 29 | ClientScreenInputEvent.MOUSE_CLICKED_POST.invoker().mouseClicked(mc, screen, mouseX, mouseY, button); 30 | } 31 | 32 | public static void sendMouseDrag(Screen screen, double finalMouseX, double finalMouseY, double finalDragX, double finalDragY, int activeButton) 33 | { 34 | Minecraft mc = Minecraft.getInstance(); 35 | if(ClientScreenInputEvent.MOUSE_DRAGGED_PRE.invoker().mouseDragged(mc, screen, finalMouseX, finalMouseY, activeButton, finalDragX, finalDragY).isPresent()) 36 | return; 37 | if(screen.mouseDragged(finalMouseX, finalMouseY, activeButton, finalDragX, finalDragY)) 38 | return; 39 | ClientScreenInputEvent.MOUSE_DRAGGED_POST.invoker().mouseDragged(mc, screen, finalMouseX, finalMouseY, activeButton, finalDragX, finalDragY); 40 | } 41 | 42 | public static boolean sendScreenKeyReleased(Screen screen, int key, int scanCode, int modifiers) 43 | { 44 | Minecraft mc = Minecraft.getInstance(); 45 | if(ClientScreenInputEvent.KEY_RELEASED_PRE.invoker().keyReleased(mc, screen, key, scanCode, modifiers).isPresent()) 46 | return true; 47 | if(screen.keyReleased(key, -1, modifiers)) 48 | return true; 49 | return ClientScreenInputEvent.KEY_RELEASED_POST.invoker().keyReleased(mc, screen, key, scanCode, modifiers).isPresent(); 50 | } 51 | 52 | public static boolean sendScreenKeyPressed(Screen screen, int key, int scanCode, int modifiers) 53 | { 54 | Minecraft mc = Minecraft.getInstance(); 55 | if(ClientScreenInputEvent.KEY_PRESSED_PRE.invoker().keyPressed(mc, screen, key, scanCode, modifiers).isPresent()) 56 | return true; 57 | if(screen.keyPressed(key, -1, modifiers)) 58 | return true; 59 | return ClientScreenInputEvent.KEY_PRESSED_POST.invoker().keyPressed(mc, screen, key, scanCode, modifiers).isPresent(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /fabric/src/main/java/com/mrcrayfish/controllable/mixin/ControllableMixinPlugin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin; 2 | 3 | import org.objectweb.asm.tree.ClassNode; 4 | import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; 5 | import org.spongepowered.asm.mixin.extensibility.IMixinInfo; 6 | 7 | import java.util.List; 8 | import java.util.Set; 9 | 10 | /** 11 | * Author: MrCrayfish 12 | */ 13 | public class ControllableMixinPlugin implements IMixinConfigPlugin 14 | { 15 | private boolean frameworkLoaded; 16 | 17 | @Override 18 | public void onLoad(String mixinPackage) 19 | { 20 | this.frameworkLoaded = this.isClassAvailable("com.mrcrayfish.framework.Constants"); 21 | } 22 | 23 | @Override 24 | public String getRefMapperConfig() 25 | { 26 | return null; 27 | } 28 | 29 | @Override 30 | public boolean shouldApplyMixin(String targetClassName, String mixinClassName) 31 | { 32 | // Prevent any loading of mixins if Framework is not installed. 33 | return this.frameworkLoaded; 34 | } 35 | 36 | @Override 37 | public void acceptTargets(Set myTargets, Set otherTargets) 38 | { 39 | 40 | } 41 | 42 | @Override 43 | public List getMixins() 44 | { 45 | return null; 46 | } 47 | 48 | @Override 49 | public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) 50 | { 51 | 52 | } 53 | 54 | @Override 55 | public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) 56 | { 57 | 58 | } 59 | 60 | private boolean isClassAvailable(String className) 61 | { 62 | try 63 | { 64 | Class.forName(className, false, getClass().getClassLoader()); 65 | return true; 66 | } 67 | catch (ClassNotFoundException e) 68 | { 69 | return false; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /fabric/src/main/java/com/mrcrayfish/controllable/mixin/client/FabricGuiMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client; 2 | 3 | import com.mrcrayfish.controllable.Config; 4 | import net.minecraft.client.DeltaTracker; 5 | import net.minecraft.client.gui.Gui; 6 | import net.minecraft.client.gui.GuiGraphics; 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 | /** 13 | * Author: MrCrayfish 14 | */ 15 | @Mixin(Gui.class) 16 | public class FabricGuiMixin 17 | { 18 | @Inject(method = "renderHotbarAndDecorations", at = @At(value = "HEAD")) 19 | private void consoleHotbarOffsetHead(GuiGraphics graphics, DeltaTracker tracker, CallbackInfo ci) 20 | { 21 | if(Config.CLIENT.options.consoleHotbar.get()) 22 | { 23 | graphics.pose().pushMatrix(); 24 | graphics.pose().translate(0, -25); 25 | } 26 | } 27 | 28 | @Inject(method = "renderHotbarAndDecorations", at = @At(value = "TAIL")) 29 | private void consoleHotbarOffsetTail(GuiGraphics graphics, DeltaTracker tracker, CallbackInfo ci) 30 | { 31 | if(Config.CLIENT.options.consoleHotbar.get()) 32 | { 33 | graphics.pose().popMatrix(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /fabric/src/main/java/com/mrcrayfish/controllable/mixin/client/FabricKeyMappingMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client; 2 | 3 | import com.mojang.blaze3d.platform.InputConstants; 4 | import com.mrcrayfish.controllable.Controllable; 5 | import com.mrcrayfish.controllable.client.binding.BindingRegistry; 6 | import com.mrcrayfish.controllable.client.binding.KeyAdapterBinding; 7 | import net.minecraft.client.KeyMapping; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Shadow; 10 | import org.spongepowered.asm.mixin.Unique; 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 | /** 16 | * Author: MrCrayfish 17 | */ 18 | @Mixin(KeyMapping.class) 19 | public abstract class FabricKeyMappingMixin 20 | { 21 | @Shadow 22 | private InputConstants.Key key; 23 | 24 | @Shadow 25 | private boolean isDown; 26 | 27 | @Shadow 28 | public abstract String getName(); 29 | 30 | @Inject(method = "isDown", at = @At(value = "HEAD"), cancellable = true) 31 | private void controllableIsDown(CallbackInfoReturnable cir) 32 | { 33 | if(this.isDown && this.controllable$IsActiveAndMatches(this.key)) 34 | { 35 | cir.setReturnValue(true); 36 | } 37 | } 38 | 39 | // TODO needs testing 40 | @Unique 41 | private boolean controllable$IsActiveAndMatches(InputConstants.Key keyCode) 42 | { 43 | String customKey = this.getName() + ".custom"; 44 | KeyAdapterBinding adapter = Controllable.getBindingRegistry().getKeyAdapters().get(customKey); 45 | if(adapter != null && adapter.isButtonDown()) 46 | { 47 | return true; 48 | } 49 | return keyCode != InputConstants.UNKNOWN && keyCode.equals(this.key); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /fabric/src/main/java/com/mrcrayfish/controllable/mixin/client/FabricMinecraftMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client; 2 | 3 | import com.mrcrayfish.controllable.Controllable; 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.CallbackInfo; 9 | 10 | /** 11 | * Author: MrCrayfish 12 | */ 13 | @Mixin(Minecraft.class) 14 | public class FabricMinecraftMixin 15 | { 16 | @Inject(method = "onGameLoadFinished", at = @At(value = "HEAD")) 17 | private void controllableGameLoaded(CallbackInfo ci) 18 | { 19 | Controllable.getBindingRegistry().completeSetup(); 20 | Controllable.getControllerManager().completeSetup(); 21 | Controllable.getCursor().resetToCenter(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /fabric/src/main/java/com/mrcrayfish/controllable/mixin/client/FabricRecipeBookPageMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client; 2 | 3 | import com.mrcrayfish.controllable.Config; 4 | import com.mrcrayfish.controllable.Controllable; 5 | import com.mrcrayfish.controllable.client.binding.ButtonBindings; 6 | import com.mrcrayfish.controllable.client.input.Controller; 7 | import com.mrcrayfish.controllable.client.util.ClientHelper; 8 | import net.minecraft.ChatFormatting; 9 | import net.minecraft.client.gui.screens.recipebook.RecipeBookPage; 10 | import net.minecraft.network.chat.Component; 11 | import net.minecraft.network.chat.contents.TranslatableContents; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.ModifyArg; 15 | 16 | import java.util.List; 17 | 18 | /** 19 | * Author: MrCrayfish 20 | */ 21 | @Mixin(RecipeBookPage.class) 22 | public class FabricRecipeBookPageMixin 23 | { 24 | @ModifyArg(method = "renderTooltip", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphics;setComponentTooltipForNextFrame(Lnet/minecraft/client/gui/Font;Ljava/util/List;IILnet/minecraft/resources/ResourceLocation;)V"), index = 1) 25 | private List controllableModifyRenderToolTip(List components) 26 | { 27 | Controller controller = Controllable.getController(); 28 | if(controller != null && controller.isBeingUsed() && Config.CLIENT.options.quickCraft.get()) 29 | { 30 | if(components.removeIf(c -> c.getContents() instanceof TranslatableContents t && t.getKey().equals("gui.recipebook.moreRecipes"))) 31 | { 32 | components.add(Component.translatable("controllable.tooltip.more_recipes", ClientHelper.getButtonComponent(ButtonBindings.SPLIT_STACK.getButton())).withStyle(ChatFormatting.YELLOW)); 33 | } 34 | components.add(Component.translatable("controllable.tooltip.craft", ClientHelper.getButtonComponent(ButtonBindings.PICKUP_ITEM.getButton())).withStyle(ChatFormatting.YELLOW)); 35 | } 36 | return components; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /fabric/src/main/java/com/mrcrayfish/controllable/platform/FabricPlatformHelper.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.platform; 2 | 3 | import com.mrcrayfish.controllable.platform.services.IPlatformHelper; 4 | 5 | /** 6 | * Author: MrCrayfish 7 | */ 8 | public class FabricPlatformHelper implements IPlatformHelper 9 | { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /fabric/src/main/resources/META-INF/services/com.mrcrayfish.controllable.platform.services.IClientHelper: -------------------------------------------------------------------------------- 1 | com.mrcrayfish.controllable.platform.FabricClientHelper -------------------------------------------------------------------------------- /fabric/src/main/resources/META-INF/services/com.mrcrayfish.controllable.platform.services.IPlatformHelper: -------------------------------------------------------------------------------- 1 | com.mrcrayfish.controllable.platform.FabricPlatformHelper -------------------------------------------------------------------------------- /fabric/src/main/resources/controllable.accesswidener: -------------------------------------------------------------------------------- 1 | accessWidener v1 named 2 | accessible field net/minecraft/client/Minecraft rightClickDelay I 3 | accessible field net/minecraft/client/MouseHandler activeButton I 4 | accessible field net/minecraft/client/MouseHandler lastHandleMovementTime D 5 | accessible field net/minecraft/client/gui/components/ChatComponent trimmedMessages Ljava/util/List; 6 | accessible field net/minecraft/client/gui/screens/inventory/AbstractContainerScreen hoveredSlot Lnet/minecraft/world/inventory/Slot; 7 | accessible field net/minecraft/client/gui/screens/inventory/AbstractContainerScreen topPos I 8 | accessible field net/minecraft/client/gui/screens/inventory/AbstractContainerScreen leftPos I 9 | accessible field net/minecraft/client/gui/screens/inventory/AbstractContainerScreen imageWidth I 10 | accessible field net/minecraft/client/gui/screens/inventory/AbstractContainerScreen imageHeight I 11 | accessible field net/minecraft/client/OptionInstance caption Lnet/minecraft/network/chat/Component; 12 | accessible field net/minecraft/client/OptionInstance tooltip Lnet/minecraft/client/OptionInstance$TooltipSupplier; 13 | accessible field net/minecraft/client/gui/components/AbstractSelectionList itemHeight I 14 | accessible field net/minecraft/client/gui/screens/inventory/CreativeModeInventoryScreen scrollOffs F 15 | accessible field net/minecraft/client/KeyMapping clickCount I 16 | accessible field net/minecraft/client/gui/components/Tooltip cachedTooltip Ljava/util/List; 17 | accessible field net/minecraft/client/gui/screens/inventory/StonecutterScreen startIndex I 18 | accessible field net/minecraft/client/gui/screens/inventory/LoomScreen startRow I 19 | accessible field net/minecraft/client/gui/screens/recipebook/OverlayRecipeComponent$OverlayRecipeButton isCraftable Z 20 | accessible method net/minecraft/client/Minecraft startUseItem ()V 21 | accessible method net/minecraft/client/Minecraft startAttack ()Z 22 | accessible method net/minecraft/client/Minecraft pickBlock ()V 23 | accessible method net/minecraft/client/Minecraft openChatScreen (Ljava/lang/String;)V 24 | accessible method net/minecraft/client/player/LocalPlayer canStartSprinting ()Z 25 | accessible class net/minecraft/client/gui/screens/recipebook/OverlayRecipeComponent$OverlayRecipeButton 26 | accessible class net/minecraft/client/gui/components/AbstractSelectionList$Entry 27 | -------------------------------------------------------------------------------- /fabric/src/main/resources/controllable.fabric.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "com.mrcrayfish.controllable.mixin", 5 | "plugin": "com.mrcrayfish.controllable.mixin.ControllableMixinPlugin", 6 | "compatibilityLevel": "JAVA_21", 7 | "refmap": "controllable.refmap.json", 8 | "client": [ 9 | "client.FabricGuiMixin", 10 | "client.FabricKeyMappingMixin", 11 | "client.FabricMinecraftMixin", 12 | "client.FabricRecipeBookPageMixin" 13 | ], 14 | "injectors": { 15 | "defaultRequire": 1 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /fabric/src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "${mod_id}", 4 | "version": "${mod_version}", 5 | "name": "${mod_name}", 6 | "description": "${mod_desc}", 7 | "authors": [ "${mod_author}" ], 8 | "contact": { 9 | "homepage": "${mod_homepage}", 10 | "sources": "${mod_source}", 11 | "issues": "${mod_issues}" 12 | }, 13 | "license": "${mod_license}", 14 | "icon": "assets/${mod_id}/icon.png", 15 | "environment": "client", 16 | "entrypoints": { 17 | "main": [ 18 | "com.mrcrayfish.controllable.ControllableMod" 19 | ], 20 | "client": [ 21 | "com.mrcrayfish.controllable.client.ClientControllableMod" 22 | ] 23 | }, 24 | "mixins": [ 25 | "controllable.common.mixins.json", 26 | "controllable.fabric.mixins.json" 27 | ], 28 | "accessWidener": "controllable.accesswidener", 29 | "depends": { 30 | "fabricloader": ">=${fabric_loader_version}", 31 | "fabric": "*", 32 | "minecraft": "${minecraft_version}", 33 | "java": ">=21", 34 | "framework": ">=${framework_version}" 35 | }, 36 | "breaks": { 37 | "jei": "<${jei_version}", 38 | "emi": "<${emi_version}", 39 | "roughlyenoughitems": "<${rei_version}" 40 | }, 41 | "custom": { 42 | "framework": { 43 | "configs": [ 44 | "com.mrcrayfish.controllable.Config" 45 | ] 46 | }, 47 | "configured": { 48 | "configs": [], 49 | "background": "minecraft:textures/block/dirt.png" 50 | }, 51 | "catalogue": { 52 | "banner": "assets/${mod_id}/banner.png", 53 | "background": "assets/${mod_id}/background.png" 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project 2 | group=com.mrcrayfish 3 | 4 | # Java 5 | java_version=21 6 | 7 | # Common 8 | minecraft_version=1.21.7 9 | neo_form_version=1.21.7-20250630.141722 10 | 11 | # Fabric 12 | fabric_version=0.128.1+1.21.7 13 | fabric_loader_version=0.16.14 14 | 15 | # NeoForge 16 | neoforge_version=21.7.1-beta 17 | neoforge_version_range=[21.7,) 18 | neoforge_loader_version_range=[2,) 19 | 20 | # Mod options 21 | mod_version=0.25.2 22 | mod_name=Controllable 23 | mod_author=MrCrayfish 24 | mod_id=controllable 25 | mod_desc=Adds the ability to use a controller to play Minecraft 26 | mod_homepage=https://mrcrayfish.com/mods?id=controllable 27 | mod_source=https://github.com/MrCrayfish/Controllable 28 | mod_issues=https://github.com/MrCrayfish/Controllable/issues 29 | mod_license=MIT 30 | 31 | # Dependency options 32 | controllable_sdl_version=2.32.8-1.1.0 33 | framework_version=0.12.3 34 | architectury_api_version=17.0.6 35 | jei_version=19.14.1.144 36 | jei_minecraft_version=1.21.1 37 | emi_version=1.1.19+1.21.1 38 | rei_version=18.0.796 39 | 40 | # Gradle 41 | org.gradle.jvmargs=-Xmx3G 42 | org.gradle.daemon=false -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/Controllable/a177d203b5e0ef86c401a7819df72af5936e6153/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /neoforge/build.gradle: -------------------------------------------------------------------------------- 1 | import net.neoforged.gradle.common.tasks.PotentiallySignJar 2 | 3 | plugins { 4 | id 'multiloader-loader' 5 | id 'net.neoforged.gradle.userdev' version '7.0.184' 6 | } 7 | 8 | def at = file('src/main/resources/META-INF/accesstransformer.cfg') 9 | if (at.exists()) { 10 | minecraft.accessTransformers.file at 11 | } 12 | 13 | jarJar.enable() 14 | 15 | /*mixin { 16 | add sourceSets.main, "controllable.refmap.json" 17 | config 'controllable.common.mixins.json' 18 | config 'controllable.neoforge.mixins.json' 19 | }*/ 20 | 21 | sourceSets { 22 | test { 23 | compileClasspath += project(':common').sourceSets.main.output 24 | runtimeClasspath += project(':common').sourceSets.main.output 25 | } 26 | } 27 | 28 | configurations { 29 | testImplementation.extendsFrom implementation 30 | testRuntimeOnly.extendsFrom runtimeOnly 31 | } 32 | 33 | runs { 34 | configureEach { 35 | modSource project.sourceSets.main 36 | dependencies { 37 | runtime project.configurations.library 38 | } 39 | } 40 | } 41 | 42 | sourceSets.main.resources { srcDir 'src/generated/resources' } 43 | 44 | dependencies { 45 | // Core dependencies 46 | implementation "net.neoforged:neoforge:${neoforge_version}" 47 | implementation "com.mrcrayfish:framework-neoforge:${minecraft_version}-${framework_version}" 48 | 49 | // JEI 50 | compileOnly "mezz.jei:jei-${jei_minecraft_version}-common-api:${jei_version}" 51 | compileOnly "mezz.jei:jei-${jei_minecraft_version}-neoforge:${jei_version}" 52 | 53 | // EMI 54 | compileOnly "dev.emi:emi-neoforge:${emi_version}" 55 | 56 | // Roughly Enough Items 57 | compileOnly "me.shedaniel:RoughlyEnoughItems-neoforge:${rei_version}" 58 | 59 | // AppleSkin 60 | //rumtimeOnly "squeek.appleskin:appleskin-neoforge:mc1.21.3-3.0.6" 61 | 62 | // Controllable SDL (https://github.com/MrCrayfish/ControllableSDL) 63 | library jarJar("com.mrcrayfish:controllable-sdl:${controllable_sdl_version}") { 64 | transitive(false) 65 | } 66 | 67 | 68 | testImplementation "org.junit.jupiter:junit-jupiter:5.7.2" 69 | testRuntimeOnly "org.junit.platform:junit-platform-launcher" 70 | } 71 | 72 | tasks.jarJar.configure { 73 | archiveClassifier = 'release' 74 | from(rootProject.file("LICENSE_LibSDL4J")) 75 | from(rootProject.file("LICENSE")) { 76 | rename { "${it}_${mod_name}" } 77 | } 78 | } 79 | 80 | tasks.register('signJar', PotentiallySignJar) { 81 | dependsOn jar 82 | onlyIf { 83 | hasProperty('keyStore') || System.getenv("KEYSTORE") 84 | } 85 | keyStore = findProperty('keyStore') ?: System.getenv("KEYSTORE") 86 | alias = findProperty('keyStoreAlias') ?: System.getenv("KEYSTORE_ALIAS") 87 | storePass = findProperty('keyStorePass') ?: System.getenv("KEYSTORE_PASS") 88 | input = jar.archiveFile 89 | } 90 | 91 | jar.finalizedBy 'signJar' 92 | 93 | idea { 94 | module { 95 | downloadSources = true 96 | downloadJavadoc = true 97 | } 98 | } 99 | 100 | test { 101 | useJUnitPlatform() 102 | } 103 | -------------------------------------------------------------------------------- /neoforge/src/main/java/com/mrcrayfish/controllable/ControllableMod.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable; 2 | 3 | import com.mrcrayfish.controllable.client.ClientBootstrap; 4 | import net.neoforged.api.distmarker.Dist; 5 | import net.neoforged.bus.api.SubscribeEvent; 6 | import net.neoforged.fml.common.EventBusSubscriber; 7 | import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; 8 | import net.neoforged.fml.event.lifecycle.FMLLoadCompleteEvent; 9 | 10 | /** 11 | * Author: MrCrayfish 12 | */ 13 | @EventBusSubscriber(value = Dist.CLIENT, modid = Constants.MOD_ID) 14 | public class ControllableMod 15 | { 16 | @SubscribeEvent 17 | private static void onClientSetup(FMLClientSetupEvent event) 18 | { 19 | event.enqueueWork(ClientBootstrap::init); 20 | } 21 | 22 | @SubscribeEvent 23 | private static void onLoadComplete(FMLLoadCompleteEvent event) 24 | { 25 | event.enqueueWork(() -> { 26 | Controllable.getBindingRegistry().completeSetup(); 27 | Controllable.getControllerManager().completeSetup(); 28 | Controllable.getCursor().resetToCenter(); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /neoforge/src/main/java/com/mrcrayfish/controllable/client/ClientEvents.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import com.mrcrayfish.controllable.Config; 5 | import com.mrcrayfish.controllable.Constants; 6 | import com.mrcrayfish.controllable.Controllable; 7 | import net.minecraft.Util; 8 | import net.minecraft.client.Minecraft; 9 | import net.minecraft.client.gui.Gui; 10 | import net.minecraft.client.gui.GuiGraphics; 11 | import net.minecraft.resources.ResourceLocation; 12 | import net.neoforged.api.distmarker.Dist; 13 | import net.neoforged.bus.api.EventPriority; 14 | import net.neoforged.bus.api.SubscribeEvent; 15 | import net.neoforged.fml.common.EventBusSubscriber; 16 | import net.neoforged.neoforge.client.event.RenderGuiEvent; 17 | import net.neoforged.neoforge.client.event.RenderGuiLayerEvent; 18 | import net.neoforged.neoforge.client.gui.GuiLayerManager; 19 | import net.neoforged.neoforge.client.gui.VanillaGuiLayers; 20 | import net.neoforged.neoforge.event.GameShuttingDownEvent; 21 | 22 | import java.util.HashSet; 23 | import java.util.Set; 24 | 25 | /** 26 | * Author: MrCrayfish 27 | */ 28 | @EventBusSubscriber(modid = Constants.MOD_ID, value = Dist.CLIENT) 29 | public class ClientEvents 30 | { 31 | private static final int CONSOLE_HOTBAR_OFFSET = 25; 32 | 33 | private static final Set OFFSET_LAYERS = Util.make(new HashSet<>(), set -> { 34 | set.add(VanillaGuiLayers.HOTBAR); 35 | set.add(VanillaGuiLayers.CONTEXTUAL_INFO_BAR); 36 | set.add(VanillaGuiLayers.CONTEXTUAL_INFO_BAR_BACKGROUND); 37 | set.add(VanillaGuiLayers.SPECTATOR_TOOLTIP); 38 | set.add(VanillaGuiLayers.EXPERIENCE_LEVEL); 39 | set.add(VanillaGuiLayers.SELECTED_ITEM_NAME); 40 | set.add(VanillaGuiLayers.CHAT); 41 | }); 42 | 43 | @SubscribeEvent 44 | public static void onGameShuttingDown(GameShuttingDownEvent event) 45 | { 46 | Controllable.getControllerManager().dispose(); 47 | } 48 | 49 | @SuppressWarnings("UnstableApiUsage") 50 | public static void beforeRenderLayer(GuiGraphics graphics, GuiLayerManager.NamedLayer layer) 51 | { 52 | if(Config.CLIENT.options.consoleHotbar.get() && OFFSET_LAYERS.contains(layer.name())) 53 | { 54 | graphics.pose().pushMatrix(); 55 | graphics.pose().translate(0, -CONSOLE_HOTBAR_OFFSET); 56 | } 57 | } 58 | 59 | @SubscribeEvent(priority = EventPriority.LOWEST) 60 | public static void onRenderLayer(RenderGuiLayerEvent.Post event) 61 | { 62 | if(Config.CLIENT.options.consoleHotbar.get() && OFFSET_LAYERS.contains(event.getName())) 63 | { 64 | event.getGuiGraphics().pose().popMatrix(); 65 | } 66 | } 67 | 68 | @SubscribeEvent(priority = EventPriority.LOWEST) 69 | public static void onRenderGui(RenderGuiEvent.Pre event) 70 | { 71 | if(Config.CLIENT.options.consoleHotbar.get()) 72 | { 73 | Minecraft.getInstance().gui.leftHeight += CONSOLE_HOTBAR_OFFSET; 74 | Minecraft.getInstance().gui.rightHeight += CONSOLE_HOTBAR_OFFSET; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /neoforge/src/main/java/com/mrcrayfish/controllable/client/binding/context/NeoForgeKeyContext.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.client.binding.context; 2 | 3 | import com.mrcrayfish.controllable.client.binding.context.rule.ContextRule; 4 | import com.mrcrayfish.controllable.client.binding.context.rule.HasPlayerRule; 5 | import com.mrcrayfish.controllable.client.binding.context.rule.HasScreenRule; 6 | import com.mrcrayfish.controllable.client.binding.context.rule.NoScreenRule; 7 | import net.minecraft.resources.ResourceLocation; 8 | import net.neoforged.neoforge.client.settings.IKeyConflictContext; 9 | import net.neoforged.neoforge.client.settings.KeyConflictContext; 10 | 11 | import java.util.Set; 12 | import java.util.UUID; 13 | 14 | /** 15 | * Author: MrCrayfish 16 | */ 17 | public class NeoForgeKeyContext extends BindingContext 18 | { 19 | private final IKeyConflictContext context; 20 | 21 | public NeoForgeKeyContext(IKeyConflictContext context) 22 | { 23 | super(generateId(context)); 24 | this.context = context; 25 | } 26 | 27 | @Override 28 | public Set createRules() 29 | { 30 | return generateRules(this.context); 31 | } 32 | 33 | @Override 34 | public int priority() 35 | { 36 | return 0; 37 | } 38 | 39 | private static ResourceLocation generateId(IKeyConflictContext context) 40 | { 41 | if(context == KeyConflictContext.UNIVERSAL) 42 | { 43 | return ResourceLocation.fromNamespaceAndPath("neoforge", "universal"); 44 | } 45 | if(context == KeyConflictContext.GUI) 46 | { 47 | return ResourceLocation.fromNamespaceAndPath("neoforge", "gui"); 48 | } 49 | if(context == KeyConflictContext.IN_GAME) 50 | { 51 | return ResourceLocation.fromNamespaceAndPath("neoforge", "in_game"); 52 | } 53 | return ResourceLocation.fromNamespaceAndPath("neoforge", UUID.randomUUID().toString()); 54 | } 55 | 56 | private static Set generateRules(IKeyConflictContext context) 57 | { 58 | if(context == KeyConflictContext.GUI) 59 | { 60 | return Set.of(HasScreenRule.any()); 61 | } 62 | if(context == KeyConflictContext.IN_GAME) 63 | { 64 | return Set.of(NoScreenRule.get(), HasPlayerRule.get()); 65 | } 66 | return Set.of(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /neoforge/src/main/java/com/mrcrayfish/controllable/mixin/ControllableMixinPlugin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin; 2 | 3 | import org.objectweb.asm.tree.ClassNode; 4 | import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; 5 | import org.spongepowered.asm.mixin.extensibility.IMixinInfo; 6 | 7 | import java.util.List; 8 | import java.util.Set; 9 | 10 | /** 11 | * Author: MrCrayfish 12 | */ 13 | public class ControllableMixinPlugin implements IMixinConfigPlugin 14 | { 15 | private boolean frameworkLoaded; 16 | 17 | @Override 18 | public void onLoad(String mixinPackage) 19 | { 20 | this.frameworkLoaded = this.isClassAvailable("com.mrcrayfish.framework.Constants"); 21 | } 22 | 23 | @Override 24 | public String getRefMapperConfig() 25 | { 26 | return null; 27 | } 28 | 29 | @Override 30 | public boolean shouldApplyMixin(String targetClassName, String mixinClassName) 31 | { 32 | // Prevent any loading of mixins if Framework is not installed. 33 | return this.frameworkLoaded; 34 | } 35 | 36 | @Override 37 | public void acceptTargets(Set myTargets, Set otherTargets) {} 38 | 39 | @Override 40 | public List getMixins() 41 | { 42 | return null; 43 | } 44 | 45 | @Override 46 | public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {} 47 | 48 | @Override 49 | public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {} 50 | 51 | private boolean isClassAvailable(String className) 52 | { 53 | try 54 | { 55 | Class.forName(className, false, getClass().getClassLoader()); 56 | return true; 57 | } 58 | catch (ClassNotFoundException e) 59 | { 60 | return false; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /neoforge/src/main/java/com/mrcrayfish/controllable/mixin/client/GuiLayerManagerMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import com.mrcrayfish.controllable.client.ClientEvents; 5 | import net.minecraft.client.DeltaTracker; 6 | import net.minecraft.client.gui.GuiGraphics; 7 | import net.neoforged.neoforge.client.gui.GuiLayerManager; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 12 | 13 | /* DO NOT DO THIS. THIS IS LAST RESORT SINCE PRE EVENT CAN BE CANCELED */ 14 | @Mixin(GuiLayerManager.class) 15 | public class GuiLayerManagerMixin 16 | { 17 | @Inject(method = "renderInner", at = @At(value = "INVOKE", target = "Lnet/neoforged/neoforge/client/gui/GuiLayer;render(Lnet/minecraft/client/gui/GuiGraphics;Lnet/minecraft/client/DeltaTracker;)V")) 18 | private void controllableBeforeRenderLayer(GuiGraphics graphics, DeltaTracker tracker, CallbackInfo info, @Local(index = 4, ordinal = 0) GuiLayerManager.NamedLayer layer) 19 | { 20 | ClientEvents.beforeRenderLayer(graphics, layer); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /neoforge/src/main/java/com/mrcrayfish/controllable/mixin/client/NeoForgeKeyMappingMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client; 2 | 3 | import com.mojang.blaze3d.platform.InputConstants; 4 | import com.mrcrayfish.controllable.Controllable; 5 | import com.mrcrayfish.controllable.client.binding.BindingRegistry; 6 | import com.mrcrayfish.controllable.client.binding.KeyAdapterBinding; 7 | import net.minecraft.client.KeyMapping; 8 | import net.neoforged.neoforge.client.extensions.IKeyMappingExtension; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | 12 | /** 13 | * Author: MrCrayfish 14 | */ 15 | @Mixin(KeyMapping.class) 16 | public abstract class NeoForgeKeyMappingMixin implements IKeyMappingExtension 17 | { 18 | @Shadow 19 | public abstract String getCategory(); 20 | 21 | @Override 22 | public boolean isActiveAndMatches(InputConstants.Key keyCode) 23 | { 24 | String customKey = this.getCategory() + ".custom"; 25 | KeyAdapterBinding adapter = Controllable.getBindingRegistry().getKeyAdapters().get(customKey); 26 | if(adapter != null && adapter.isButtonDown()) 27 | { 28 | return true; 29 | } 30 | return keyCode != InputConstants.UNKNOWN && keyCode.equals(getKey()) && this.getKeyConflictContext().isActive() && this.getKeyModifier().isActive(this.getKeyConflictContext()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /neoforge/src/main/java/com/mrcrayfish/controllable/mixin/client/NeoForgeRecipeBookPageMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.mixin.client; 2 | 3 | import com.mrcrayfish.controllable.Config; 4 | import com.mrcrayfish.controllable.Controllable; 5 | import com.mrcrayfish.controllable.client.binding.ButtonBindings; 6 | import com.mrcrayfish.controllable.client.input.Controller; 7 | import com.mrcrayfish.controllable.client.util.ClientHelper; 8 | import net.minecraft.ChatFormatting; 9 | import net.minecraft.client.gui.screens.recipebook.RecipeBookPage; 10 | import net.minecraft.network.chat.Component; 11 | import net.minecraft.network.chat.contents.TranslatableContents; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.ModifyArg; 15 | 16 | import java.util.List; 17 | 18 | /** 19 | * Author: MrCrayfish 20 | */ 21 | @Mixin(RecipeBookPage.class) 22 | public class NeoForgeRecipeBookPageMixin 23 | { 24 | @ModifyArg(method = "renderTooltip", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphics;setComponentTooltipForNextFrame(Lnet/minecraft/client/gui/Font;Ljava/util/List;IILnet/minecraft/resources/ResourceLocation;)V"), index = 1) 25 | private List controllableModifyRenderToolTip(List components) 26 | { 27 | Controller controller = Controllable.getController(); 28 | if(controller != null && controller.isBeingUsed() && Config.CLIENT.options.quickCraft.get()) 29 | { 30 | if(components.removeIf(c -> c.getContents() instanceof TranslatableContents t && t.getKey().equals("gui.recipebook.moreRecipes"))) 31 | { 32 | components.add(Component.translatable("controllable.tooltip.more_recipes", ClientHelper.getButtonComponent(ButtonBindings.SPLIT_STACK.getButton())).withStyle(ChatFormatting.YELLOW)); 33 | } 34 | components.add(Component.translatable("controllable.tooltip.craft", ClientHelper.getButtonComponent(ButtonBindings.PICKUP_ITEM.getButton())).withStyle(ChatFormatting.YELLOW)); 35 | } 36 | return components; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /neoforge/src/main/java/com/mrcrayfish/controllable/platform/NeoForgePlatformHelper.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.controllable.platform; 2 | 3 | import com.mrcrayfish.controllable.platform.services.IPlatformHelper; 4 | 5 | /** 6 | * Author: MrCrayfish 7 | */ 8 | public class NeoForgePlatformHelper implements IPlatformHelper 9 | { 10 | @Override 11 | public boolean isNeoForge() 12 | { 13 | return true; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /neoforge/src/main/resources/META-INF/accesstransformer.cfg: -------------------------------------------------------------------------------- 1 | public net.minecraft.client.Minecraft startUseItem()V 2 | public net.minecraft.client.Minecraft startAttack()Z 3 | public net.minecraft.client.Minecraft pickBlock()V 4 | public net.minecraft.client.Minecraft rightClickDelay 5 | public net.minecraft.client.gui.components.ChatComponent trimmedMessages 6 | public net.minecraft.client.MouseHandler activeButton 7 | public net.minecraft.client.MouseHandler lastHandleMovementTime 8 | public net.minecraft.client.Minecraft getFramerateLimit()I 9 | public net.minecraft.client.OptionInstance caption 10 | public net.minecraft.client.OptionInstance tooltip 11 | public net.minecraft.client.gui.screens.inventory.LoomScreen startRow 12 | public net.minecraft.client.gui.screens.recipebook.OverlayRecipeComponent$OverlayRecipeButton 13 | public net.minecraft.client.gui.screens.recipebook.OverlayRecipeComponent$OverlayRecipeButton isCraftable # isCraftable 14 | public net.minecraft.client.gui.components.AbstractSelectionList$Entry 15 | public net.minecraft.client.Minecraft openChatScreen(Ljava/lang/String;)V 16 | public net.minecraft.client.gui.components.AbstractSelectionList getRowTop(I)I 17 | public net.minecraft.client.gui.components.AbstractSelectionList getRowBottom(I)I 18 | public net.minecraft.client.gui.components.AbstractSelectionList slotClicked(Lnet/minecraft/world/inventory/Slot;IILnet/minecraft/world/inventory/ClickType;)V 19 | public net.minecraft.client.player.LocalPlayer canStartSprinting()Z -------------------------------------------------------------------------------- /neoforge/src/main/resources/META-INF/neoforge.mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "${neoforge_loader_version_range}" 3 | license = "${mod_license}" 4 | issueTrackerURL = "${mod_issues}" 5 | 6 | [[mods]] 7 | modId = "${mod_id}" 8 | version = "${mod_version}" 9 | displayName = "${mod_name}" 10 | # updateJSONURL = "https://mrcrayfish.com/modupdatejson?id=${mod_id}" 11 | displayURL = "${mod_homepage}" 12 | logoFile = "assets/${mod_id}/icon.png" 13 | logoBlur = false 14 | authors = "${mod_author}" 15 | description = '''${mod_desc}''' 16 | 17 | [[mixins]] 18 | config = "${mod_id}.common.mixins.json" 19 | 20 | [[mixins]] 21 | config = "${mod_id}.neoforge.mixins.json" 22 | 23 | [[dependencies.${ mod_id }]] 24 | modId = "neoforge" 25 | type = "required" 26 | versionRange = "${neoforge_version_range}" 27 | ordering = "NONE" 28 | side = "BOTH" 29 | 30 | [[dependencies.${ mod_id }]] 31 | modId = "minecraft" 32 | type = "required" 33 | versionRange = "[${minecraft_version},)" 34 | ordering = "NONE" 35 | side = "BOTH" 36 | 37 | [[dependencies.${ mod_id }]] 38 | modId = "framework" 39 | type = "required" 40 | versionRange = "[${framework_version},)" 41 | ordering = "NONE" 42 | side = "BOTH" 43 | 44 | [[dependencies.${ mod_id }]] 45 | modId = "jei" 46 | type = "optional" 47 | versionRange = "[${jei_version},)" 48 | ordering = "NONE" 49 | side = "CLIENT" 50 | 51 | [[dependencies.${ mod_id }]] 52 | modId = "emi" 53 | type = "optional" 54 | versionRange = "[${emi_version},)" 55 | ordering = "NONE" 56 | side = "CLIENT" 57 | 58 | [[dependencies.${ mod_id }]] 59 | modId = "roughlyenoughitems" 60 | type = "optional" 61 | versionRange = "[${rei_version},)" 62 | ordering = "NONE" 63 | side = "CLIENT" 64 | 65 | [modproperties."${mod_id}"] 66 | catalogueImageIcon = "assets/${mod_id}/icon.png" 67 | catalogueBackground = "assets/${mod_id}/background.png" 68 | configuredBackground = "minecraft:textures/block/stone.png" 69 | -------------------------------------------------------------------------------- /neoforge/src/main/resources/META-INF/services/com.mrcrayfish.controllable.platform.services.IClientHelper: -------------------------------------------------------------------------------- 1 | com.mrcrayfish.controllable.platform.NeoForgeClientHelper -------------------------------------------------------------------------------- /neoforge/src/main/resources/META-INF/services/com.mrcrayfish.controllable.platform.services.IPlatformHelper: -------------------------------------------------------------------------------- 1 | com.mrcrayfish.controllable.platform.NeoForgePlatformHelper -------------------------------------------------------------------------------- /neoforge/src/main/resources/controllable.neoforge.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "com.mrcrayfish.controllable.mixin", 5 | "plugin": "com.mrcrayfish.controllable.mixin.ControllableMixinPlugin", 6 | "compatibilityLevel": "JAVA_21", 7 | "refmap": "controllable.refmap.json", 8 | "client": [ 9 | "client.GuiLayerManagerMixin", 10 | "client.NeoForgeKeyMappingMixin", 11 | "client.NeoForgeRecipeBookPageMixin" 12 | ], 13 | "injectors": { 14 | "defaultRequire": 1 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /release_build: -------------------------------------------------------------------------------- 1 | 4 -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | mavenCentral() 5 | exclusiveContent { 6 | forRepository { 7 | maven { 8 | name = 'Fabric' 9 | url = uri('https://maven.fabricmc.net') 10 | } 11 | } 12 | filter { 13 | includeGroup('net.fabricmc') 14 | includeGroup('fabric-loom') 15 | } 16 | } 17 | exclusiveContent { 18 | forRepository { 19 | maven { 20 | name = 'NeoForge' 21 | url = uri("https://maven.neoforged.net/releases") 22 | } 23 | } 24 | filter { 25 | includeGroupAndSubgroups("net.neoforged") 26 | includeGroup("codechicken") 27 | } 28 | } 29 | exclusiveContent { 30 | forRepository { 31 | maven { 32 | name = 'Sponge' 33 | url = uri('https://repo.spongepowered.org/repository/maven-public') 34 | } 35 | } 36 | filter { 37 | includeGroupAndSubgroups("org.spongepowered") 38 | } 39 | } 40 | exclusiveContent { 41 | forRepository { 42 | maven { 43 | name = 'Forge' 44 | url = uri('https://maven.minecraftforge.net') 45 | } 46 | } 47 | filter { 48 | includeGroupAndSubgroups('net.minecraftforge') 49 | } 50 | } 51 | } 52 | } 53 | 54 | plugins { 55 | id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' 56 | } 57 | 58 | def includeModloader(String name) { 59 | // If building with Github Actions, only include loader we want! 60 | String targetLoader = Boolean.getBoolean("CI") ?: System.getenv("TARGET_LOADER") 61 | if(targetLoader == null || targetLoader == name) { 62 | include(name) 63 | } 64 | } 65 | 66 | rootProject.name = 'Controllable' 67 | include("common") 68 | includeModloader("fabric") 69 | includeModloader("neoforge") 70 | --------------------------------------------------------------------------------