From 557d7303c3ae811a7686c3035cb75d13f29f9eae Mon Sep 17 00:00:00 2001 From: Xander Date: Wed, 29 Jul 2026 01:30:05 +0100 Subject: [PATCH 01/15] port to 26.3 --- build.gradle.kts | 12 +- .../dev/isxander/controlify/Controlify.java | 15 +-- .../compatibility/ControlifyCompat.java | 4 +- .../fancymenu/FancyMenuCompat.java | 4 +- .../OpenControlifySettingsAction.java | 4 +- .../mixins/BaseOptionElementWidgetMixin.java | 4 +- .../BaseOptionElementComponentProcessor.java | 4 +- .../sodium/mixins/AbstractWidgetMixin.java | 4 +- .../mixins/CycleControlElementMixin.java | 4 +- .../mixins/FlatButtonWidgetAccessor.java | 4 +- .../sodium/mixins/FlatButtonWidgetMixin.java | 4 +- .../mixins/SliderControlElementMixin.java | 4 +- .../mixins/TickBoxControlElementMixin.java | 4 +- .../mixins/VideoSettingsScreenMixin.java | 8 +- .../screenop/SodiumGuiScreenProcessor.java | 4 +- .../screenop/SodiumScreenOperations.java | 4 +- .../driver/glfw/GLFWGamepadDriver.java | 119 ------------------ .../driver/glfw/GLFWJoystickDriver.java | 118 ----------------- .../driver/sdl/SDLNativesLoader.java | 4 +- .../gui/controllers/BindController.java | 4 +- .../controlify/mixins/core/GLXMixin.java | 25 ---- .../mixins/core/InputConstantsMixin.java | 23 ++++ .../mixins/core/KeyboardHandlerMixin.java | 11 +- .../mixins/core/MinecraftMixin.java | 2 +- .../mixins/core/MouseHandlerMixin.java | 25 ++-- .../mixins/core/SDLEventHandlerMixin.java | 54 ++++++++ .../JoinMultiplayerScreenAccessor.java | 4 +- .../impl/sign/SignEditScreenMixin.java | 14 +-- .../virtualmouse/InputConstantsMixin.java | 3 +- .../virtualmouse/MouseHandlerAccessor.java | 5 + .../virtualmouse/MouseHandlerMixin.java | 8 +- .../compat/vanilla/PauseScreenProcessor.java | 2 +- ...rSelectionListEntryComponentProcessor.java | 2 +- .../vanilla/SliderComponentProcessor.java | 6 +- .../compat/vanilla/TitleScreenProcessor.java | 2 +- .../screenop/keyboard/KeyWidget.java | 3 +- .../controlify/utils/CursorUtils.java | 42 +++++++ .../utils/render/BaseRenderState.java | 7 +- .../utils/render/CGuiElementRenderState.java | 7 +- .../virtualmouse/VirtualMouseHandler.java | 49 ++++---- src/main/resources/controlify.mixins.json | 3 +- versions/26.3/gradle.properties | 20 +++ versions/current | 2 +- versions/versions.json | 3 +- 44 files changed, 281 insertions(+), 373 deletions(-) delete mode 100644 src/main/java/dev/isxander/controlify/driver/glfw/GLFWGamepadDriver.java delete mode 100644 src/main/java/dev/isxander/controlify/driver/glfw/GLFWJoystickDriver.java delete mode 100644 src/main/java/dev/isxander/controlify/mixins/core/GLXMixin.java create mode 100644 src/main/java/dev/isxander/controlify/mixins/core/InputConstantsMixin.java create mode 100644 src/main/java/dev/isxander/controlify/mixins/core/SDLEventHandlerMixin.java create mode 100644 src/main/java/dev/isxander/controlify/utils/CursorUtils.java create mode 100644 versions/26.3/gradle.properties diff --git a/build.gradle.kts b/build.gradle.kts index 9613ce2f0..79d8e2f8f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,7 +19,11 @@ java.toolchain.languageVersion = JavaLanguageVersion.of(25) dependencies { minecraft("com.mojang:minecraft:$minecraftVersion") fabricLoader(libs.fabric.loader) - neoforgeImplementation("net.neoforged:neoforge:${property("dep.neoforge")}") + + ifPresent("dep.neoforge") { + neoforgeImplementation("net.neoforged:neoforge:${property("dep.neoforge")}") + } + implementation(platform("net.fabricmc.fabric-api:fabric-api-bom:${property("dep.fapi")}")) fabricImplementation(platform("net.fabricmc.fabric-api:fabric-api-bom:${property("dep.fapi")}")) @@ -112,8 +116,10 @@ stonecutter { /// Run configurations -runs.register("neoforgeClient") { - runType("client") +ifPresent("dep.neoforge") { + runs.register("neoforgeClient") { + runType("client") + } } /// Metadata file generation diff --git a/src/main/java/dev/isxander/controlify/Controlify.java b/src/main/java/dev/isxander/controlify/Controlify.java index 9a87ca18e..3495cf3d4 100644 --- a/src/main/java/dev/isxander/controlify/Controlify.java +++ b/src/main/java/dev/isxander/controlify/Controlify.java @@ -64,7 +64,6 @@ import net.minecraft.resources.Identifier; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.lwjgl.glfw.GLFW; import org.spongepowered.asm.mixin.MixinEnvironment; import java.io.IOException; @@ -705,21 +704,15 @@ public VirtualMouseHandler virtualMouseHandler() { } public void hideMouse(boolean hide, boolean moveMouse) { - long handle = minecraft.getWindow().handle(); - - GLFW.glfwSetInputMode( - handle, - GLFW.GLFW_CURSOR, - hide - ? GLFW.GLFW_CURSOR_HIDDEN - : GLFW.GLFW_CURSOR_NORMAL - ); + CursorUtils.setVisibility(minecraft.getWindow(), !hide); + if (MinecraftUtil.getScreen() != null) { var mouseHandlerAccessor = (MouseHandlerAccessor) minecraft.mouseHandler; if (hide && !virtualMouseHandler().isVirtualMouseEnabled() && moveMouse) { + long handle = minecraft.getWindow().handle(); // stop mouse hovering over last element before hiding cursor but don't actually move it // so when the user switches back to mouse it will be in the same place - mouseHandlerAccessor.controlify$invokeOnMove(handle, -50, -50); + mouseHandlerAccessor.controlify$invokeOnMove(handle, -50, -50 /*? if >=26.3 {*/, 0, 0/*?}*/); } } } diff --git a/src/main/java/dev/isxander/controlify/compatibility/ControlifyCompat.java b/src/main/java/dev/isxander/controlify/compatibility/ControlifyCompat.java index d50059884..ea3c9c2d4 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/ControlifyCompat.java +++ b/src/main/java/dev/isxander/controlify/compatibility/ControlifyCompat.java @@ -36,7 +36,7 @@ public static void init() { //?} //? if fancy_menu { - try { + /*try { wrapCompatCall( FANCY_MENU, dev.isxander.controlify.compatibility.fancymenu.FancyMenuCompat::registerActions @@ -44,7 +44,7 @@ public static void init() { } catch (NoClassDefFoundError e) { disabledMods.add(FANCY_MENU); } - //?} + *///?} } private static void wrapCompatCall(String modid, Runnable runnable) throws NoClassDefFoundError { diff --git a/src/main/java/dev/isxander/controlify/compatibility/fancymenu/FancyMenuCompat.java b/src/main/java/dev/isxander/controlify/compatibility/fancymenu/FancyMenuCompat.java index 9c4f78d56..52603a9e9 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/fancymenu/FancyMenuCompat.java +++ b/src/main/java/dev/isxander/controlify/compatibility/fancymenu/FancyMenuCompat.java @@ -8,11 +8,11 @@ //? if fancy_menu { -import de.keksuccino.fancymenu.customization.action.ActionRegistry; +/*import de.keksuccino.fancymenu.customization.action.ActionRegistry; public final class FancyMenuCompat { public static void registerActions() { ActionRegistry.register(new OpenControlifySettingsAction()); } } -//?} +*///?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/fancymenu/OpenControlifySettingsAction.java b/src/main/java/dev/isxander/controlify/compatibility/fancymenu/OpenControlifySettingsAction.java index c0d53df54..4b7649b70 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/fancymenu/OpenControlifySettingsAction.java +++ b/src/main/java/dev/isxander/controlify/compatibility/fancymenu/OpenControlifySettingsAction.java @@ -8,7 +8,7 @@ //? if fancy_menu { -import de.keksuccino.fancymenu.customization.action.Action; +/*import de.keksuccino.fancymenu.customization.action.Action; import dev.isxander.controlify.gui.screen.ControlifySettingsScreen; import dev.isxander.controlify.utils.MinecraftUtil; import net.minecraft.network.chat.Component; @@ -51,4 +51,4 @@ public void execute(@Nullable String s) { } } -//?} +*///?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/iris/mixins/BaseOptionElementWidgetMixin.java b/src/main/java/dev/isxander/controlify/compatibility/iris/mixins/BaseOptionElementWidgetMixin.java index 50d7ca7f6..7450bc940 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/iris/mixins/BaseOptionElementWidgetMixin.java +++ b/src/main/java/dev/isxander/controlify/compatibility/iris/mixins/BaseOptionElementWidgetMixin.java @@ -8,7 +8,7 @@ //? if iris { -import dev.isxander.controlify.compatibility.iris.screenop.BaseOptionElementComponentProcessor; +/*import dev.isxander.controlify.compatibility.iris.screenop.BaseOptionElementComponentProcessor; import dev.isxander.controlify.screenop.ComponentProcessor; import dev.isxander.controlify.screenop.ComponentProcessorProvider; import net.irisshaders.iris.gui.NavigationController; @@ -39,4 +39,4 @@ public ComponentProcessor componentProcessor() { } } -//?} +*///?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/iris/screenop/BaseOptionElementComponentProcessor.java b/src/main/java/dev/isxander/controlify/compatibility/iris/screenop/BaseOptionElementComponentProcessor.java index 138d6ee85..08342be26 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/iris/screenop/BaseOptionElementComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/compatibility/iris/screenop/BaseOptionElementComponentProcessor.java @@ -8,7 +8,7 @@ //? if iris { -import dev.isxander.controlify.screenop.compat.AbstractSliderComponentProcessor; +/*import dev.isxander.controlify.screenop.compat.AbstractSliderComponentProcessor; import java.util.function.Consumer; @@ -24,4 +24,4 @@ protected void incrementSlider(boolean reverse) { this.cycleMethod.accept(reverse); } } -//?} +*///?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/AbstractWidgetMixin.java b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/AbstractWidgetMixin.java index 91090a871..9f80ea2fa 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/AbstractWidgetMixin.java +++ b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/AbstractWidgetMixin.java @@ -8,7 +8,7 @@ //? if sodium { -import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +/*import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import dev.isxander.controlify.Controlify; import net.minecraft.client.InputType; import org.spongepowered.asm.mixin.Mixin; @@ -26,4 +26,4 @@ private InputType forceSetFocusedOnController(InputType type) { return type; } } -//?} +*///?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/CycleControlElementMixin.java b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/CycleControlElementMixin.java index 8c3aa78ba..3d56a62d0 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/CycleControlElementMixin.java +++ b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/CycleControlElementMixin.java @@ -8,7 +8,7 @@ //? if sodium { -import dev.isxander.controlify.compatibility.sodium.screenop.CycleControlProcessor; +/*import dev.isxander.controlify.compatibility.sodium.screenop.CycleControlProcessor; import dev.isxander.controlify.screenop.ComponentProcessor; import dev.isxander.controlify.screenop.ComponentProcessorProvider; import org.spongepowered.asm.mixin.Mixin; @@ -27,4 +27,4 @@ public ComponentProcessor componentProcessor() { return controlify$componentProcessor; } } -//?} +*///?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetAccessor.java b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetAccessor.java index 6cd2513d5..e41652c83 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetAccessor.java +++ b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetAccessor.java @@ -8,7 +8,7 @@ //? if sodium { -import org.spongepowered.asm.mixin.Mixin; +/*import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; import net.caffeinemc.mods.sodium.client.gui.widgets.FlatButtonWidget; @@ -18,4 +18,4 @@ public interface FlatButtonWidgetAccessor { @Invoker void invokeDoAction(); } -//?} +*///?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetMixin.java b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetMixin.java index c9e592836..70f9a5a9b 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetMixin.java +++ b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetMixin.java @@ -8,7 +8,7 @@ //? if sodium { -import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +/*import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import dev.isxander.controlify.api.bind.InputBinding; import dev.isxander.controlify.gui.ButtonGuideRenderer; import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; @@ -73,4 +73,4 @@ private Component modifyRenderedLabel(Component actualLabel) { this.controllerMessages.clear(); } } -//?} +*///?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/SliderControlElementMixin.java b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/SliderControlElementMixin.java index 2b8c05ed1..d6778ecff 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/SliderControlElementMixin.java +++ b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/SliderControlElementMixin.java @@ -8,7 +8,7 @@ //? if sodium { -import dev.isxander.controlify.compatibility.sodium.screenop.SliderControlProcessor; +/*import dev.isxander.controlify.compatibility.sodium.screenop.SliderControlProcessor; import dev.isxander.controlify.screenop.ComponentProcessor; import dev.isxander.controlify.screenop.ComponentProcessorProvider; import net.caffeinemc.mods.sodium.client.config.structure.IntegerOption; @@ -43,4 +43,4 @@ public ComponentProcessor componentProcessor() { )); } } -//?} +*///?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/TickBoxControlElementMixin.java b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/TickBoxControlElementMixin.java index 82efa42b0..93a959993 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/TickBoxControlElementMixin.java +++ b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/TickBoxControlElementMixin.java @@ -8,7 +8,7 @@ //? if sodium { -import dev.isxander.controlify.compatibility.sodium.screenop.TickBoxControlProcessor; +/*import dev.isxander.controlify.compatibility.sodium.screenop.TickBoxControlProcessor; import dev.isxander.controlify.screenop.ComponentProcessor; import dev.isxander.controlify.screenop.ComponentProcessorProvider; import org.spongepowered.asm.mixin.Mixin; @@ -28,4 +28,4 @@ public ComponentProcessor componentProcessor() { return controlify$componentProcessor; } } -//?} +*///?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/VideoSettingsScreenMixin.java b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/VideoSettingsScreenMixin.java index cf87cfae3..12ff3f0ca 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/VideoSettingsScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/VideoSettingsScreenMixin.java @@ -8,7 +8,7 @@ //? if sodium { -import dev.isxander.controlify.compatibility.sodium.screenop.SodiumGuiScreenProcessor; +/*import dev.isxander.controlify.compatibility.sodium.screenop.SodiumGuiScreenProcessor; import dev.isxander.controlify.compatibility.sodium.screenop.SodiumScreenOperations; import dev.isxander.controlify.screenop.ScreenProcessor; import dev.isxander.controlify.screenop.ScreenProcessorProvider; @@ -33,10 +33,10 @@ public abstract class VideoSettingsScreenMixin extends Screen implements ScreenP @Shadow private net.caffeinemc.mods.sodium.client.gui.widgets.KeyBoundButtonWidget closeButton; @Shadow private net.caffeinemc.mods.sodium.client.gui.widgets.KeyBoundButtonWidget undoButton; //?} else { - /*@Shadow private FlatButtonWidget applyButton; + /^@Shadow private FlatButtonWidget applyButton; @Shadow private FlatButtonWidget closeButton; @Shadow private FlatButtonWidget undoButton; - *///?} + ^///?} @Shadow private OptionListWidget optionList; @Unique private Page controlify$currentPage = null; @@ -106,4 +106,4 @@ public ScreenProcessor screenProcessor() { return undoButton; } } -//?} +*///?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java b/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java index 12667a3fb..7ac26febf 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java @@ -8,7 +8,7 @@ //? if sodium { -import dev.isxander.controlify.api.buttonguide.ButtonGuideApi; +/*import dev.isxander.controlify.api.buttonguide.ButtonGuideApi; import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate; import dev.isxander.controlify.bindings.ControlifyBindings; import dev.isxander.controlify.compatibility.sodium.mixins.FlatButtonWidgetAccessor; @@ -70,4 +70,4 @@ public void onRebuildGUI() { super.onWidgetRebuild(); } } -//?} +*///?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumScreenOperations.java b/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumScreenOperations.java index be61f804f..5f3286460 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumScreenOperations.java +++ b/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumScreenOperations.java @@ -8,7 +8,7 @@ //? if sodium { -import net.caffeinemc.mods.sodium.client.gui.widgets.FlatButtonWidget; +/*import net.caffeinemc.mods.sodium.client.gui.widgets.FlatButtonWidget; public interface SodiumScreenOperations { void controlify$nextPage(); @@ -21,4 +21,4 @@ public interface SodiumScreenOperations { FlatButtonWidget controlify$getUndoButton(); } -//?} +*///?} diff --git a/src/main/java/dev/isxander/controlify/driver/glfw/GLFWGamepadDriver.java b/src/main/java/dev/isxander/controlify/driver/glfw/GLFWGamepadDriver.java deleted file mode 100644 index b98684de6..000000000 --- a/src/main/java/dev/isxander/controlify/driver/glfw/GLFWGamepadDriver.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.driver.glfw; - -import dev.isxander.controlify.controller.info.DriverNameComponent; -import dev.isxander.controlify.controller.info.GUIDComponent; -import dev.isxander.controlify.controller.input.GamepadInputs; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.input.InputComponent; -import dev.isxander.controlify.controller.impl.ControllerStateImpl; -import dev.isxander.controlify.driver.Driver; -import net.minecraft.util.Mth; -import org.lwjgl.glfw.GLFW; -import org.lwjgl.glfw.GLFWGamepadState; - -import static org.lwjgl.glfw.GLFW.*; - -public class GLFWGamepadDriver implements Driver { - private final int jid; - private final String guid; - private final String name; - - private InputComponent inputComponent; - - public GLFWGamepadDriver(int jid) { - this.jid = jid; - this.guid = glfwGetJoystickGUID(jid); - this.name = glfwGetGamepadName(jid); - - this.getGamepadState(); // test input ability so the create catches it - } - - @Override - public void addComponents(ControllerEntity controller) { - controller.setComponent(new DriverNameComponent(this.name)); - controller.setComponent(new GUIDComponent(this.guid)); - - controller.setComponent(this.inputComponent = new InputComponent( - controller, 15, 10, 0, - true, - GamepadInputs.DEADZONE_GROUPS, - controller.info().type().mappingId() - )); - } - - @Override - public void update(ControllerEntity controller, boolean outOfFocus) { - this.updateInput(); - } - - @Override - public void close() { - - } - - - private void updateInput() { - GLFWGamepadState glfwState = this.getGamepadState(); - ControllerStateImpl state = new ControllerStateImpl(); - - state.setAxis(GamepadInputs.LEFT_STICK_AXIS_DOWN, positiveAxis(glfwState.axes(GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y))); - state.setAxis(GamepadInputs.LEFT_STICK_AXIS_RIGHT, positiveAxis(glfwState.axes(GLFW.GLFW_GAMEPAD_AXIS_LEFT_X))); - state.setAxis(GamepadInputs.LEFT_STICK_AXIS_UP, negativeAxis(glfwState.axes(GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y))); - state.setAxis(GamepadInputs.LEFT_STICK_AXIS_LEFT, negativeAxis(glfwState.axes(GLFW.GLFW_GAMEPAD_AXIS_LEFT_X))); - - state.setAxis(GamepadInputs.RIGHT_STICK_AXIS_UP, negativeAxis(glfwState.axes(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y))); - state.setAxis(GamepadInputs.RIGHT_STICK_AXIS_LEFT, negativeAxis(glfwState.axes(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X))); - state.setAxis(GamepadInputs.RIGHT_STICK_AXIS_DOWN, positiveAxis(glfwState.axes(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y))); - state.setAxis(GamepadInputs.RIGHT_STICK_AXIS_RIGHT, positiveAxis(glfwState.axes(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X))); - - state.setAxis(GamepadInputs.LEFT_TRIGGER_AXIS, (1f + glfwState.axes(GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER)) / 2f); - state.setAxis(GamepadInputs.RIGHT_TRIGGER_AXIS, (1f + glfwState.axes(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER)) / 2f); - - state.setButton(GamepadInputs.SOUTH_BUTTON, glfwState.buttons(GLFW.GLFW_GAMEPAD_BUTTON_A) == GLFW.GLFW_PRESS); - state.setButton(GamepadInputs.EAST_BUTTON, glfwState.buttons(GLFW.GLFW_GAMEPAD_BUTTON_B) == GLFW.GLFW_PRESS); - state.setButton(GamepadInputs.WEST_BUTTON, glfwState.buttons(GLFW.GLFW_GAMEPAD_BUTTON_X) == GLFW.GLFW_PRESS); - state.setButton(GamepadInputs.NORTH_BUTTON, glfwState.buttons(GLFW.GLFW_GAMEPAD_BUTTON_Y) == GLFW.GLFW_PRESS); - - state.setButton(GamepadInputs.LEFT_SHOULDER_BUTTON, glfwState.buttons(GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER) == GLFW.GLFW_PRESS); - state.setButton(GamepadInputs.RIGHT_SHOULDER_BUTTON, glfwState.buttons(GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER) == GLFW.GLFW_PRESS); - - state.setButton(GamepadInputs.LEFT_STICK_BUTTON, glfwState.buttons(GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB) == GLFW.GLFW_PRESS); - state.setButton(GamepadInputs.RIGHT_STICK_BUTTON, glfwState.buttons(GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB) == GLFW.GLFW_PRESS); - - state.setButton(GamepadInputs.BACK_BUTTON, glfwState.buttons(GLFW.GLFW_GAMEPAD_BUTTON_BACK) == GLFW.GLFW_PRESS); - state.setButton(GamepadInputs.START_BUTTON, glfwState.buttons(GLFW.GLFW_GAMEPAD_BUTTON_START) == GLFW.GLFW_PRESS); - state.setButton(GamepadInputs.GUIDE_BUTTON, glfwState.buttons(GLFW.GLFW_GAMEPAD_BUTTON_GUIDE) == GLFW.GLFW_PRESS); - - state.setButton(GamepadInputs.DPAD_UP_BUTTON, glfwState.buttons(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP) == GLFW.GLFW_PRESS); - state.setButton(GamepadInputs.DPAD_DOWN_BUTTON, glfwState.buttons(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN) == GLFW.GLFW_PRESS); - state.setButton(GamepadInputs.DPAD_LEFT_BUTTON, glfwState.buttons(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT) == GLFW.GLFW_PRESS); - state.setButton(GamepadInputs.DPAD_RIGHT_BUTTON, glfwState.buttons(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT) == GLFW.GLFW_PRESS); - - this.inputComponent.pushState(state); - } - - private GLFWGamepadState getGamepadState() { - GLFWGamepadState state = GLFWGamepadState.create(); - glfwGetGamepadState(jid, state); - return state; - } - - private float positiveAxis(float value) { - return value < 0 ? 0 : value; - } - - private float negativeAxis(float value) { - return value > 0 ? 0 : -value; - } - - private static float mapShortToFloat(short value) { - return Mth.clampedMap(value, Short.MIN_VALUE, 0, -1f, 0f) - + Mth.clampedMap(value, 0, Short.MAX_VALUE, 0f, 1f); - } -} diff --git a/src/main/java/dev/isxander/controlify/driver/glfw/GLFWJoystickDriver.java b/src/main/java/dev/isxander/controlify/driver/glfw/GLFWJoystickDriver.java deleted file mode 100644 index d389ba2a5..000000000 --- a/src/main/java/dev/isxander/controlify/driver/glfw/GLFWJoystickDriver.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.driver.glfw; - -import dev.isxander.controlify.controller.info.DriverNameComponent; -import dev.isxander.controlify.controller.info.GUIDComponent; -import dev.isxander.controlify.controller.input.HatState; -import dev.isxander.controlify.controller.input.JoystickInputs; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.input.InputComponent; -import dev.isxander.controlify.controller.impl.ControllerStateImpl; -import dev.isxander.controlify.driver.Driver; -import org.apache.commons.lang3.Validate; -import org.lwjgl.glfw.GLFW; - -import java.nio.ByteBuffer; -import java.nio.FloatBuffer; -import java.util.Set; - -import static org.lwjgl.glfw.GLFW.*; - -public class GLFWJoystickDriver implements Driver { - private final int jid; - private final String guid; - private final String name; - private final int numButtons, numAxes, numHats; - - private InputComponent inputComponent; - - public GLFWJoystickDriver(int jid) { - this.jid = jid; - this.guid = glfwGetJoystickGUID(jid); - this.name = glfwGetJoystickName(jid); - - GLFWJoystickState testState = this.getJoystickState(); - this.numButtons = testState.buttons().limit(); - this.numAxes = testState.axes().limit(); - this.numHats = testState.hats().limit(); - } - - @Override - public void addComponents(ControllerEntity controller) { - controller.setComponent(new DriverNameComponent(this.name)); - controller.setComponent(new GUIDComponent(this.guid)); - - controller.setComponent(this.inputComponent = new InputComponent( - controller, numButtons, numAxes * 2, numHats, - false, - Set.of(), - controller.info().type().mappingId() - )); - - } - - @Override - public void update(ControllerEntity controller, boolean outOfFocus) { - this.updateInput(); - } - - @Override - public void close() { - - } - - private void updateInput() { - GLFWJoystickState glfwState = this.getJoystickState(); - - ControllerStateImpl state = new ControllerStateImpl(); - - for (int i = 0; i < numAxes; i++) { - float axis = glfwState.axes().get(i); - state.setAxis(JoystickInputs.axis(i, true), Math.max(axis, 0)); - state.setAxis(JoystickInputs.axis(i, false), -Math.min(axis, 0)); - } - - for (int i = 0; i < numButtons; i++) { - state.setButton(JoystickInputs.button(i), glfwState.buttons().get(i) == org.lwjgl.glfw.GLFW.GLFW_PRESS); - } - - for (int i = 0; i < numHats; i++) { - HatState hatState = switch (glfwState.hats().get(i)) { - case GLFW.GLFW_HAT_CENTERED -> HatState.CENTERED; - case GLFW.GLFW_HAT_UP -> HatState.UP; - case GLFW.GLFW_HAT_RIGHT -> HatState.RIGHT; - case GLFW.GLFW_HAT_DOWN -> HatState.DOWN; - case GLFW.GLFW_HAT_LEFT -> HatState.LEFT; - case GLFW.GLFW_HAT_RIGHT_UP -> HatState.RIGHT_UP; - case GLFW.GLFW_HAT_RIGHT_DOWN -> HatState.RIGHT_DOWN; - case GLFW.GLFW_HAT_LEFT_UP -> HatState.LEFT_UP; - case GLFW.GLFW_HAT_LEFT_DOWN -> HatState.LEFT_DOWN; - default -> throw new IllegalStateException("Unexpected value: " + glfwState.hats().get(i)); - }; - - state.setHat(JoystickInputs.hat(i), hatState); - } - - this.inputComponent.pushState(state); - } - - private GLFWJoystickState getJoystickState() { - ByteBuffer buttonsBuf = glfwGetJoystickButtons(jid); - FloatBuffer axesBuf = glfwGetJoystickAxes(jid); - ByteBuffer hatsBuf = glfwGetJoystickHats(jid); - - Validate.notNull(buttonsBuf, "Could not fetch buttons state for joystick"); - Validate.notNull(axesBuf, "Could not fetch axes state for joystick"); - Validate.notNull(hatsBuf, "Could not fetch hat state for joystick"); - - return new GLFWJoystickState(buttonsBuf, axesBuf, hatsBuf); - } - - private record GLFWJoystickState(ByteBuffer buttons, FloatBuffer axes, ByteBuffer hats) { - } -} diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLNativesLoader.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDLNativesLoader.java index e305c7821..77c9c2788 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDLNativesLoader.java +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDLNativesLoader.java @@ -27,8 +27,8 @@ import static dev.isxander.sdl.SdlInit.*; public class SDLNativesLoader { - private static final boolean LWJGL_SDL_AVAILABLE = /*? if >=26.3 {*/ /*true *//*?} else {*/ false /*?}*/; - private static final boolean NATIVES_IN_JAR = /*? if natives_in_jar {*/ true /*?} else {*/ /*false *//*?}*/; + private static final boolean LWJGL_SDL_AVAILABLE = /*? if >=26.3 {*/ true /*?} else {*/ /*false *//*?}*/; + private static final boolean NATIVES_IN_JAR = /*? if natives_in_jar {*/ /*true *//*?} else {*/ false /*?}*/; private static final String NATIVE_SDL_NAME = System.mapLibraryName("SDL3"); private static final String NATIVE_SDL_PATH = Platform.getResourcePrefix() + "/" + NATIVE_SDL_NAME; diff --git a/src/main/java/dev/isxander/controlify/gui/controllers/BindController.java b/src/main/java/dev/isxander/controlify/gui/controllers/BindController.java index f1091c128..fe076895f 100644 --- a/src/main/java/dev/isxander/controlify/gui/controllers/BindController.java +++ b/src/main/java/dev/isxander/controlify/gui/controllers/BindController.java @@ -6,6 +6,7 @@ */ package dev.isxander.controlify.gui.controllers; +import com.mojang.blaze3d.platform.InputConstants; import dev.isxander.controlify.Controlify; import dev.isxander.controlify.bindings.ControlifyBindings; import dev.isxander.controlify.bindings.input.*; @@ -28,7 +29,6 @@ import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; -import org.lwjgl.glfw.GLFW; import java.util.Optional; @@ -91,7 +91,7 @@ protected void extractValueText(GuiGraphicsExtractor graphics, int mouseX, int m @Override public boolean keyPressed(KeyEvent keyEvent) { - if (isFocused() && keyEvent.key() == GLFW.GLFW_KEY_ENTER) { + if (isFocused() && keyEvent.key() == InputConstants.KEY_RETURN) { openConsumerScreen(); return true; } diff --git a/src/main/java/dev/isxander/controlify/mixins/core/GLXMixin.java b/src/main/java/dev/isxander/controlify/mixins/core/GLXMixin.java deleted file mode 100644 index 1df0f50a9..000000000 --- a/src/main/java/dev/isxander/controlify/mixins/core/GLXMixin.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.mixins.core; - -import com.mojang.blaze3d.platform.GLX; -import org.lwjgl.glfw.GLFW; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.util.function.LongSupplier; - -@Mixin(GLX.class) -public class GLXMixin { - @Inject(method = "_initGlfw", at = @At(value = "INVOKE", target = "Lorg/lwjgl/glfw/GLFW;glfwInit()Z")) - private static void addInitHints(CallbackInfoReturnable cir) { - // stops GLFW adding hats to button list (backward compat) - GLFW.glfwInitHint(GLFW.GLFW_JOYSTICK_HAT_BUTTONS, GLFW.GLFW_FALSE); - } -} diff --git a/src/main/java/dev/isxander/controlify/mixins/core/InputConstantsMixin.java b/src/main/java/dev/isxander/controlify/mixins/core/InputConstantsMixin.java new file mode 100644 index 000000000..6e16621ef --- /dev/null +++ b/src/main/java/dev/isxander/controlify/mixins/core/InputConstantsMixin.java @@ -0,0 +1,23 @@ +package dev.isxander.controlify.mixins.core; + +import org.spongepowered.asm.mixin.Mixin; + +//? if >=26.3 { +import com.mojang.blaze3d.platform.InputConstants; +import org.lwjgl.sdl.SDLMouse; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(InputConstants.class) +public class InputConstantsMixin { + @Inject(method = "releaseMouse", at = @At("HEAD")) + private static void ensureMouseVisible(CallbackInfo ci) { + SDLMouse.SDL_ShowCursor(); + } +} +//?} else { +/*@Mixin(dev.isxander.controlify.utils.DummyMixinTarget.class) +public class InputConstantsMixin { +} +*///?} diff --git a/src/main/java/dev/isxander/controlify/mixins/core/KeyboardHandlerMixin.java b/src/main/java/dev/isxander/controlify/mixins/core/KeyboardHandlerMixin.java index 394d73a49..638c93cbc 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/KeyboardHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/KeyboardHandlerMixin.java @@ -6,7 +6,10 @@ */ package dev.isxander.controlify.mixins.core; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import org.spongepowered.asm.mixin.Mixin; + +//? if <26.3 { +/*import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.mojang.blaze3d.platform.Window; import dev.isxander.controlify.Controlify; @@ -15,7 +18,6 @@ import net.minecraft.client.Minecraft; import org.lwjgl.glfw.*; import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; @@ -65,3 +67,8 @@ private void wrapKeyboardEvents( }); } } +*///?} else { +@Mixin(dev.isxander.controlify.utils.DummyMixinTarget.class) +public class KeyboardHandlerMixin { +} +//?} diff --git a/src/main/java/dev/isxander/controlify/mixins/core/MinecraftMixin.java b/src/main/java/dev/isxander/controlify/mixins/core/MinecraftMixin.java index 806ced381..3eba1f2a2 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/MinecraftMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/MinecraftMixin.java @@ -59,7 +59,7 @@ private void onMinecraftClose(CallbackInfo ci) { method = "renderFrame", at = @At( value = "INVOKE", - target = "Lnet/minecraft/client/renderer/GameRenderer;render(Lnet/minecraft/client/DeltaTracker;Z)V" + target = "Lnet/minecraft/client/renderer/GameRenderer;extract(Lnet/minecraft/client/DeltaTracker;Z)V" ) ) private void tickAnimator(CallbackInfo ci) { diff --git a/src/main/java/dev/isxander/controlify/mixins/core/MouseHandlerMixin.java b/src/main/java/dev/isxander/controlify/mixins/core/MouseHandlerMixin.java index 816c38dea..1ebe42f4b 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/MouseHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/MouseHandlerMixin.java @@ -16,10 +16,6 @@ import dev.isxander.controlify.utils.MouseMinecraftCallNotifier; import net.minecraft.client.Minecraft; import net.minecraft.client.MouseHandler; -import org.lwjgl.glfw.GLFWCursorPosCallbackI; -import org.lwjgl.glfw.GLFWDropCallbackI; -import org.lwjgl.glfw.GLFWMouseButtonCallbackI; -import org.lwjgl.glfw.GLFWScrollCallbackI; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -28,13 +24,21 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +//? if <26.3 { +/*import org.lwjgl.glfw.GLFWCursorPosCallbackI; +import org.lwjgl.glfw.GLFWDropCallbackI; +import org.lwjgl.glfw.GLFWMouseButtonCallbackI; +import org.lwjgl.glfw.GLFWScrollCallbackI; +*///?} + @Mixin(MouseHandler.class) public class MouseHandlerMixin implements MouseMinecraftCallNotifier { @Shadow @Final private Minecraft minecraft; @Unique private boolean controlify$calledFromMinecraftSetScreen = false; - @WrapOperation( + //? if <26.3 { + /*@WrapOperation( method = "setup", at = @At( value = "INVOKE", @@ -78,15 +82,18 @@ private void wrapMouseEvents( }); } } + *///?} - /** - * Without this, mouse is left in the center of the screen that conflicts with controller focus. - */ + /// Without this, mouse is left in the center of the screen that conflicts with controller focus. @Inject( method = "releaseMouse", at = @At( value = "INVOKE", - target = "Lcom/mojang/blaze3d/platform/InputConstants;grabOrReleaseMouse(Lcom/mojang/blaze3d/platform/Window;IDD)V" + //? if >=26.3 { + target = "Lcom/mojang/blaze3d/platform/InputConstants;releaseMouse(Lcom/mojang/blaze3d/platform/Window;DD)V" + //?} else { + /*target = "Lcom/mojang/blaze3d/platform/InputConstants;grabOrReleaseMouse(Lcom/mojang/blaze3d/platform/Window;IDD)V" + *///?} ) ) private void moveMouseIfNecessary(CallbackInfo ci) { diff --git a/src/main/java/dev/isxander/controlify/mixins/core/SDLEventHandlerMixin.java b/src/main/java/dev/isxander/controlify/mixins/core/SDLEventHandlerMixin.java new file mode 100644 index 000000000..898c17778 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/mixins/core/SDLEventHandlerMixin.java @@ -0,0 +1,54 @@ +package dev.isxander.controlify.mixins.core; + +import org.spongepowered.asm.mixin.Mixin; + +//? if >=26.3 { +import com.mojang.blaze3d.platform.SDLEventHandler; +import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.InputMode; +import net.minecraft.client.Minecraft; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(SDLEventHandler.class) +public class SDLEventHandlerMixin { + @Shadow + @Final + private Minecraft minecraft; + + @Inject(method = { + "handleKeyEvent", + "handleTextInputEvent", + "handleTextEditingEvent", + "handleTextEditingCandidatesEvent", + }, at = @At("HEAD")) + private void onKeyboardInput(CallbackInfo ci) { + minecraft.execute(() -> { + if (Controlify.instance().currentInputMode() != InputMode.MIXED) { + Controlify.instance().setInputMode(InputMode.KEYBOARD_MOUSE); + } + }); + } + + @Inject(method = { + "handleMouseMotionEvent", + "handleMouseButtonEvent", + "handleMouseWheelEvent", + }, at = @At("HEAD")) + private void onMouseInput(CallbackInfo ci) { + minecraft.execute(() -> { + if (Controlify.instance().currentInputMode() != InputMode.MIXED) { + Controlify.instance().setInputMode(InputMode.KEYBOARD_MOUSE); + } else { + Controlify.instance().showCursorTemporarily(); + } + }); + } +} +//?} else { +/*@Mixin(dev.isxander.controlify.utils.DummyMixinTarget.class) +public class SDLEventHandlerMixin { } +*///?} diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/JoinMultiplayerScreenAccessor.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/JoinMultiplayerScreenAccessor.java index 798be451b..30cc4748a 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/JoinMultiplayerScreenAccessor.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/JoinMultiplayerScreenAccessor.java @@ -13,6 +13,6 @@ @Mixin(JoinMultiplayerScreen.class) public interface JoinMultiplayerScreenAccessor { - @Accessor("selectButton") - Button controlify$getSelectButton(); + @Accessor(/*? if >=26.3 {*/ "joinButton" /*?} else {*/ /*"selectButton" *//*?}*/) + Button controlify$getJoinButton(); } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/sign/SignEditScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/sign/SignEditScreenMixin.java index 426c8ce1c..928e6dc38 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/sign/SignEditScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/sign/SignEditScreenMixin.java @@ -6,23 +6,15 @@ */ package dev.isxander.controlify.mixins.feature.screenop.impl.sign; -import com.llamalad7.mixinextras.expression.Definition; -import com.llamalad7.mixinextras.expression.Expression; -import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.ModifyReturnValue; -import net.minecraft.client.gui.screens.inventory.AbstractSignEditScreen; +import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.inventory.SignEditScreen; -import net.minecraft.world.level.block.entity.SignBlockEntity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; @Mixin(SignEditScreen.class) -public abstract class SignEditScreenMixin extends AbstractSignEditScreen { - - public SignEditScreenMixin(SignBlockEntity sign, boolean isFrontText, boolean isFiltered) { - super(sign, isFrontText, isFiltered); - } +public class SignEditScreenMixin { @ModifyReturnValue(method = "getSignYOffset", at = @At("RETURN")) private float modifySignY(float original) { @@ -43,7 +35,7 @@ private int modifySignRenderY(int original) { @Unique private float calculateOverlap() { float original = 90f; - float keyboardStart = this.height / 2f; + float keyboardStart = ((Screen) (Object) this).height / 2f; float signEnd = original + 90; return Math.max(0, signEnd - keyboardStart); } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/InputConstantsMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/InputConstantsMixin.java index 5667a5b82..474b93537 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/InputConstantsMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/InputConstantsMixin.java @@ -12,7 +12,6 @@ import dev.isxander.controlify.Controlify; import dev.isxander.controlify.bindings.ControlifyBindings; import dev.isxander.controlify.controller.ControllerEntity; -import org.lwjgl.glfw.GLFW; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -21,7 +20,7 @@ public class InputConstantsMixin { // must modify isKeyDown here because Screen.hasShiftDown has some instances that ask for this directly. @ModifyReturnValue(method = "isKeyDown", at = @At("RETURN")) private static boolean modifyIsKeyDown(boolean keyDown, @Local(argsOnly = true, name = "key") int key) { - if (key == GLFW.GLFW_KEY_LEFT_SHIFT) { + if (key == InputConstants.KEY_LSHIFT) { ControllerEntity controller = Controlify.instance().getCurrentController().orElse(null); if (controller == null) return keyDown; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/MouseHandlerAccessor.java b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/MouseHandlerAccessor.java index 8ebb3bed2..bf1d55839 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/MouseHandlerAccessor.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/MouseHandlerAccessor.java @@ -13,8 +13,13 @@ @Mixin(MouseHandler.class) public interface MouseHandlerAccessor { + //? if >=26.3 { @Invoker("onMove") + void controlify$invokeOnMove(long window, double xpos, double ypos, double xrel, double yrel); + //?} else { + /*@Invoker("onMove") void controlify$invokeOnMove(long window, double x, double y); + *///?} @Invoker("onButton") void controlify$invokeOnButton(long l, net.minecraft.client.input.MouseButtonInfo mouseButtonInfo, int i); diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/MouseHandlerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/MouseHandlerMixin.java index c67d8caf2..3104db56b 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/MouseHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/MouseHandlerMixin.java @@ -20,10 +20,14 @@ public class MouseHandlerMixin { method = "releaseMouse", at = @At( value = "INVOKE", - target = "Lcom/mojang/blaze3d/platform/InputConstants;grabOrReleaseMouse(Lcom/mojang/blaze3d/platform/Window;IDD)V" + //? if >=26.3 { + target = "Lcom/mojang/blaze3d/platform/InputConstants;releaseMouse(Lcom/mojang/blaze3d/platform/Window;DD)V" + //?} else { + /*target = "Lcom/mojang/blaze3d/platform/InputConstants;grabOrReleaseMouse(Lcom/mojang/blaze3d/platform/Window;IDD)V" + *///?} ) ) - private boolean shouldReleaseMouse(Window window, int cursorMode, double xpos, double ypos) { + private boolean shouldReleaseMouse(Window window, /*? if <26.3 {*//*int cursorMode,*//*?}*/ double xpos, double ypos) { // mouse cursor appears for a split second when going into guis on controller input return Controlify.instance().currentInputMode() != InputMode.CONTROLLER; } diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/PauseScreenProcessor.java b/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/PauseScreenProcessor.java index 13ad01b0e..5d99796d3 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/PauseScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/PauseScreenProcessor.java @@ -34,7 +34,7 @@ protected void handleButtons(ControllerEntity controller) { super.handleButtons(controller); if (ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).justPressed()) { - MinecraftUtil.setScreen(new OptionsScreen(screen, minecraft.options, true)); + MinecraftUtil.setScreen(new OptionsScreen(screen, minecraft.options /*? if <26.3 >>*//*,true*/ )); } if (ControlifyBindings.GUI_ABSTRACT_ACTION_2.on(controller).justPressed()) { screen.setFocused(disconnectButtonSupplier.get()); diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/ServerSelectionListEntryComponentProcessor.java b/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/ServerSelectionListEntryComponentProcessor.java index 789ebe291..46cfb2ea5 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/ServerSelectionListEntryComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/ServerSelectionListEntryComponentProcessor.java @@ -16,7 +16,7 @@ public class ServerSelectionListEntryComponentProcessor implements ComponentProc @Override public boolean overrideControllerButtons(ScreenProcessor screen, ControllerEntity controller) { if (ControlifyBindings.GUI_PRESS.on(controller).guiPressed().get()) { - screen.screen.setFocused(((JoinMultiplayerScreenAccessor) screen.screen).controlify$getSelectButton()); + screen.screen.setFocused(((JoinMultiplayerScreenAccessor) screen.screen).controlify$getJoinButton()); return true; } diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/SliderComponentProcessor.java b/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/SliderComponentProcessor.java index 471c53f0c..d23c8ec08 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/SliderComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/SliderComponentProcessor.java @@ -6,13 +6,13 @@ */ package dev.isxander.controlify.screenop.compat.vanilla; +import com.mojang.blaze3d.platform.InputConstants; import dev.isxander.controlify.bindings.ControlifyBindings; import dev.isxander.controlify.controller.ControllerEntity; import dev.isxander.controlify.screenop.ScreenProcessor; import dev.isxander.controlify.screenop.ComponentProcessor; import net.minecraft.client.gui.components.AbstractSliderButton; import net.minecraft.client.input.KeyEvent; -import org.lwjgl.glfw.GLFW; import java.util.function.Consumer; import java.util.function.Supplier; @@ -40,14 +40,14 @@ public boolean overrideControllerNavigation(ScreenProcessor screen, Controlle if (ControlifyBindings.GUI_NAVI_RIGHT.on(controller).digitalNow()) { if (canSliderChange) { - component.keyPressed(new KeyEvent(GLFW.GLFW_KEY_RIGHT, 0, 0)); + component.keyPressed(new KeyEvent(InputConstants.KEY_RIGHT, 0, 0)); lastSliderChange = 0; } return true; } else if (ControlifyBindings.GUI_NAVI_LEFT.on(controller).digitalNow()) { if (canSliderChange) { - component.keyPressed(new KeyEvent(GLFW.GLFW_KEY_LEFT, 0, 0)); + component.keyPressed(new KeyEvent(InputConstants.KEY_LEFT, 0, 0)); lastSliderChange = 0; } diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/TitleScreenProcessor.java b/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/TitleScreenProcessor.java index fc705908d..95715778b 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/TitleScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/TitleScreenProcessor.java @@ -34,7 +34,7 @@ protected void handleButtons(ControllerEntity controller) { if (ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).justPressed()) { if (getWidget("menu.options").isPresent()) { - MinecraftUtil.setScreen(new OptionsScreen(screen, minecraft.options, false)); + MinecraftUtil.setScreen(new OptionsScreen(screen, minecraft.options /*? if <26.3 >>*//*,false*/ )); playClackSound(); } } diff --git a/src/main/java/dev/isxander/controlify/screenop/keyboard/KeyWidget.java b/src/main/java/dev/isxander/controlify/screenop/keyboard/KeyWidget.java index d0a9d4c42..75ba270ee 100644 --- a/src/main/java/dev/isxander/controlify/screenop/keyboard/KeyWidget.java +++ b/src/main/java/dev/isxander/controlify/screenop/keyboard/KeyWidget.java @@ -26,7 +26,6 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; import org.jspecify.annotations.NonNull; -import org.lwjgl.glfw.GLFW; /** * Represents a single key widget within a {@link KeyboardWidget}. @@ -328,7 +327,7 @@ private static void insertText(String text, InputTarget inputConsumer) { // Code points are used to represent characters that *may* be represented by surrogate pairs. text.codePoints().forEach((codePoint) -> { // guess the modifier based on the nature of the character - int modCapital = Character.isUpperCase(codePoint) ? GLFW.GLFW_MOD_SHIFT : 0; + int modCapital = Character.isUpperCase(codePoint) ? InputConstants.MOD_SHIFT : 0; int modifiers = modCapital; if (Character.isBmpCodePoint(codePoint)) { diff --git a/src/main/java/dev/isxander/controlify/utils/CursorUtils.java b/src/main/java/dev/isxander/controlify/utils/CursorUtils.java new file mode 100644 index 000000000..59175326b --- /dev/null +++ b/src/main/java/dev/isxander/controlify/utils/CursorUtils.java @@ -0,0 +1,42 @@ +package dev.isxander.controlify.utils; + +import com.mojang.blaze3d.platform.Window; + +//? if >=26.3 { +import org.lwjgl.sdl.SDLMouse; +//?} else { +/*import org.lwjgl.glfw.GLFW; +*///?} + +public final class CursorUtils { + + private CursorUtils() { + } + + public static void setVisibility(Window window, boolean visible) { + //? if >=26.3 { + if (visible) { + SDLMouse.SDL_ShowCursor(); + SDLMouse.SDL_SetWindowRelativeMouseMode(window.handle(), false); + } else { + SDLMouse.SDL_HideCursor(); + } + //?} else { + /*GLFW.glfwSetInputMode( + window.handle(), + GLFW.GLFW_CURSOR, + visible + ? GLFW.GLFW_CURSOR_NORMAL + : GLFW.GLFW_CURSOR_HIDDEN + ); + *///?} + } + + public static void setPosition(Window window, float x, float y) { + //? if >=26.3 { + SDLMouse.SDL_WarpMouseInWindow(window.handle(), x, y); + //?} else { + /*GLFW.glfwSetCursorPos(window.handle(), x, y); + *///?} + } +} diff --git a/src/main/java/dev/isxander/controlify/utils/render/BaseRenderState.java b/src/main/java/dev/isxander/controlify/utils/render/BaseRenderState.java index 2964c7dc4..f61f4bc81 100644 --- a/src/main/java/dev/isxander/controlify/utils/render/BaseRenderState.java +++ b/src/main/java/dev/isxander/controlify/utils/render/BaseRenderState.java @@ -6,7 +6,6 @@ */ package dev.isxander.controlify.utils.render; -import com.mojang.blaze3d.pipeline.RenderPipeline; import dev.isxander.controlify.platform.client.PlatformClientUtil; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; @@ -17,6 +16,12 @@ import org.jetbrains.annotations.Nullable; import org.joml.Matrix3x2f; +//? if >=26.3 { +import com.mojang.renderpearl.api.pipeline.RenderPipeline; +//?} else { +/*import com.mojang.blaze3d.pipeline.RenderPipeline; +*///?} + public record BaseRenderState( RenderPipeline pipeline, TextureSetup textureSetup, diff --git a/src/main/java/dev/isxander/controlify/utils/render/CGuiElementRenderState.java b/src/main/java/dev/isxander/controlify/utils/render/CGuiElementRenderState.java index 528977c2e..f0a0de30b 100644 --- a/src/main/java/dev/isxander/controlify/utils/render/CGuiElementRenderState.java +++ b/src/main/java/dev/isxander/controlify/utils/render/CGuiElementRenderState.java @@ -9,12 +9,17 @@ import dev.isxander.controlify.platform.client.PlatformClientUtil; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.renderer.state.gui.GuiElementRenderState; -import com.mojang.blaze3d.pipeline.RenderPipeline; import net.minecraft.client.gui.render.TextureSetup; import net.minecraft.client.gui.navigation.ScreenRectangle; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +//? if >=26.3 { +import com.mojang.renderpearl.api.pipeline.RenderPipeline; +//?} else { +/*import com.mojang.blaze3d.pipeline.RenderPipeline; +*///?} + public interface CGuiElementRenderState extends GuiElementRenderState { BaseRenderState baseState(); diff --git a/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseHandler.java b/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseHandler.java index d998ac580..b870b31de 100644 --- a/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseHandler.java +++ b/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseHandler.java @@ -34,7 +34,6 @@ import net.minecraft.resources.Identifier; import net.minecraft.util.Mth; import org.joml.*; -import org.lwjgl.glfw.GLFW; import java.lang.Math; import java.util.*; @@ -202,8 +201,8 @@ public void handleCompatibilityBinds(ControllerEntity controller) { } if (ControlifyBindings.VMOUSE_SHIFT_CLICK.on(controller).justPressed()) { - this.simulateMousePress(InputConstants.MOUSE_BUTTON_LEFT, InputConstants.PRESS, GLFW.GLFW_MOD_SHIFT); - this.simulateMousePress(InputConstants.MOUSE_BUTTON_LEFT, InputConstants.RELEASE, GLFW.GLFW_MOD_SHIFT); + this.simulateMousePress(InputConstants.MOUSE_BUTTON_LEFT, InputConstants.PRESS, InputConstants.MOD_SHIFT); + this.simulateMousePress(InputConstants.MOUSE_BUTTON_LEFT, InputConstants.RELEASE, InputConstants.MOD_SHIFT); } } @@ -225,10 +224,15 @@ public void updateMouse() { var windowHandle = minecraft.getWindow().handle(); if (Math.round(targetX * 100) / 100.0 != Math.round(currentX * 100) / 100.0 || Math.round(targetY * 100) / 100.0 != Math.round(currentY * 100) / 100.0) { - currentX = Mth.lerp(delta, currentX, targetX); - currentY = Mth.lerp(delta, currentY, targetY); + double newX = Mth.lerp(delta, currentX, targetX); + double newY = Mth.lerp(delta, currentY, targetY); + double relX = newX - currentX; + double relY = newY - currentY; - ((MouseHandlerAccessor) minecraft.mouseHandler).controlify$invokeOnMove(windowHandle, currentX, currentY); + ((MouseHandlerAccessor) minecraft.mouseHandler).controlify$invokeOnMove(windowHandle, newX, newY /*? if >=26.3 {*/,relX, relY/*?}*/); + + currentX = newX; + currentY = newY; } else { currentX = targetX; currentY = targetY; @@ -332,15 +336,20 @@ private static Optional findOrthogonalSnapPoint(SnapPoint from, Scree public void snapToPoint(SnapPoint snapPoint, Vector2dc scaleFactor) { lastSnappedPoint = snapPoint; - targetX = currentX = snapPoint.position().x() / scaleFactor.x(); - targetY = currentY = snapPoint.position().y() / scaleFactor.y(); + double newX = snapPoint.position().x() / scaleFactor.x(); + double newY = snapPoint.position().y() / scaleFactor.y(); + double relX = newX - currentX; + double relY = newY - currentY; var windowHandle = minecraft.getWindow().handle(); - ((MouseHandlerAccessor) minecraft.mouseHandler).controlify$invokeOnMove(windowHandle, currentX, currentY); + ((MouseHandlerAccessor) minecraft.mouseHandler).controlify$invokeOnMove(windowHandle, newX, newY /*? if >=26.3 {*/,relX, relY/*?}*/); + + currentX = targetX = newX; + currentY = targetY = newY; } public void onScreenChanged() { - var windowHandle = minecraft.getWindow().handle(); + var window = minecraft.getWindow(); if (MinecraftUtil.getScreen() != null) { if (requiresVirtualMouse()) { @@ -348,8 +357,9 @@ public void onScreenChanged() { } else { disableVirtualMouse(); } - if (Controlify.instance().currentInputMode().isController()) - GLFW.glfwSetInputMode(windowHandle, GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_HIDDEN); + if (Controlify.instance().currentInputMode().isController()) { + CursorUtils.setVisibility(window, false); + } } else if (virtualMouseEnabled) { disableVirtualMouse(); @@ -400,9 +410,10 @@ public void renderVirtualMouse(GuiGraphicsExtractor graphics) { public void enableVirtualMouse() { if (virtualMouseEnabled) return; - var windowHandle = minecraft.getWindow().handle(); + var window = minecraft.getWindow(); - GLFW.glfwSetInputMode(windowHandle, GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED); + // TODO: verify that hiding the mouse is sufficient, pre-SDL this was GLFW_CURSOR_DISABLED + CursorUtils.setVisibility(window, false); virtualMouseEnabled = true; if (minecraft.mouseHandler.xpos() == -50 && minecraft.mouseHandler.ypos() == -50) { @@ -423,13 +434,13 @@ public void enableVirtualMouse() { public void disableVirtualMouse() { if (!virtualMouseEnabled) return; - var windowHandle = minecraft.getWindow().handle(); + var window = minecraft.getWindow(); // make sure minecraft doesn't think the mouse is grabbed when it isn't ((MouseHandlerAccessor) minecraft.mouseHandler).controlify$setMouseGrabbed(false); Controlify.instance().hideMouse(true, true); - GLFW.glfwSetInputMode(windowHandle, GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_NORMAL); + CursorUtils.setVisibility(window, true); setMousePosition(); virtualMouseEnabled = false; targetX = currentX = minecraft.mouseHandler.xpos(); @@ -442,11 +453,7 @@ public void disableVirtualMouse() { } private void setMousePosition() { - GLFW.glfwSetCursorPos( - minecraft.getWindow().handle(), - targetX, - targetY - ); + CursorUtils.setPosition(minecraft.getWindow(), (float) targetX, (float) targetY); } public boolean requiresVirtualMouse() { diff --git a/src/main/resources/controlify.mixins.json b/src/main/resources/controlify.mixins.json index f924794ca..2e5025c37 100644 --- a/src/main/resources/controlify.mixins.json +++ b/src/main/resources/controlify.mixins.json @@ -14,12 +14,13 @@ }, "client": [ "core.ClientPacketListenerMixin", - "core.GLXMixin", "core.GuiMixin", "core.HudMixin", + "core.InputConstantsMixin", "core.KeyboardHandlerMixin", "core.MinecraftMixin", "core.MouseHandlerMixin", + "core.SDLEventHandlerMixin", "feature.accessibility.LocalPlayerMixin", "feature.bind.GuiAccessor", "feature.bind.GuiMixin", diff --git a/versions/26.3/gradle.properties b/versions/26.3/gradle.properties new file mode 100644 index 000000000..7f8e50d44 --- /dev/null +++ b/versions/26.3/gradle.properties @@ -0,0 +1,20 @@ +dep.minecraft=26.3-snapshot-6 +meta.minecraft-range=(26.2,26.4) + +#dep.neoforge=26.2.0.28-beta + +dep.fapi=0.156.1+26.3 +meta.fapi-range=[0.156.1,) + +dep.yacl=3.9.6+26.3-fabric +dep.yacl-neoforge=3.9.6+26.2-neoforge +meta.yacl-range=[,) + +dep.mod-menu=21.0.0-alpha.1 +#dep.sodium=0.9.1+mc26.2 +#dep.iris=1.11.2+26.2-fabric +#dep.iris-neoforge=1.11.2+26.2-neoforge +dep.svc=fabric-2.6.21+26.3-snapshot-6 +#dep.svc-neoforge=neoforge-2.6.18+26.2 +#dep.fancy-menu=3.9.8-26.2-fabric +#dep.fancy-menu-neoforge=3.9.8-26.2-neoforge diff --git a/versions/current b/versions/current index f0dcf5d1b..3276f1d60 100644 --- a/versions/current +++ b/versions/current @@ -1 +1 @@ -26.2 \ No newline at end of file +26.3 \ No newline at end of file diff --git a/versions/versions.json b/versions/versions.json index aa35be4f5..30e774b10 100644 --- a/versions/versions.json +++ b/versions/versions.json @@ -3,7 +3,8 @@ "branches": { "": [ "26.1", - "26.2" + "26.2", + "26.3" ] } } From e62eff67b7884ba412c921097dd86e4d7339073a Mon Sep 17 00:00:00 2001 From: Xander Date: Tue, 4 Aug 2026 16:45:36 +0100 Subject: [PATCH 02/15] port to 26.3-snapshot-7 --- build.gradle.kts | 31 +++++++---- .../compatibility/ControlifyCompat.java | 4 +- .../SimpleVoiceChatCompat.java | 4 +- .../mixins/KeyEventsAccessor.java | 4 +- .../mixins/PTTKeyHandlerMixin.java | 4 +- .../ingame/DropWithResultInvoker.java | 11 ++++ .../controlify/ingame/InGameInputHandler.java | 12 +++-- .../mixins/core/InputConstantsMixin.java | 6 +++ .../mixins/core/SDLEventHandlerMixin.java | 6 +++ .../patches/dropresult/LocalPlayerMixin.java | 51 +++++++++++++++++++ .../controlify/utils/CursorUtils.java | 6 +++ src/main/resources/controlify.mixins.json | 1 + versions/26.3/gradle.properties | 8 +-- 13 files changed, 121 insertions(+), 27 deletions(-) create mode 100644 src/main/java/dev/isxander/controlify/ingame/DropWithResultInvoker.java create mode 100644 src/main/java/dev/isxander/controlify/mixins/feature/patches/dropresult/LocalPlayerMixin.java diff --git a/build.gradle.kts b/build.gradle.kts index 29e81f8ca..b01f54ab0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,5 @@ +@file:Suppress("UnstableApiUsage") + plugins { id("controlify-common") @@ -16,6 +18,14 @@ base.archivesName = "controlify" java.toolchain.languageVersion = JavaLanguageVersion.of(25) +val fabricModDependency = configurations.dependencyScope("fabricModDependency") +configurations.fabricCompileOnly { extendsFrom(fabricModDependency) } +configurations.fabricLocalRuntime { extendsFrom(fabricModDependency) } + +val neoforgeModDependency = configurations.dependencyScope("neoforgeModDependency") +configurations.neoforgeCompileOnly { extendsFrom(neoforgeModDependency) } +configurations.neoforgeLocalRuntime { extendsFrom(neoforgeModDependency) } + dependencies { minecraft("com.mojang:minecraft:$minecraftVersion") fabricLoader(libs.fabric.loader) @@ -24,7 +34,6 @@ dependencies { neoforgeImplementation("net.neoforged:neoforge:${property("dep.neoforge")}") } - implementation(platform("net.fabricmc.fabric-api:fabric-api-bom:${property("dep.fapi")}")) fabricImplementation(platform("net.fabricmc.fabric-api:fabric-api-bom:${property("dep.fapi")}")) fabricImplementation("net.fabricmc.fabric-api:fabric-resource-loader-v1") @@ -37,7 +46,7 @@ dependencies { fabricImplementation("net.fabricmc.fabric-api:fabric-key-mapping-api-v1") // this needs to be on main because fabric is shared with main jar implementation("net.fabricmc.fabric-api:fabric-transitive-access-wideners-v1") - fabricRuntimeOnly("net.fabricmc.fabric-api:fabric-api") + fabricLocalRuntime("net.fabricmc.fabric-api:fabric-api") commonApi(libs.sdl.java.api) commonInclude(libs.sdl.java.api) @@ -63,33 +72,33 @@ dependencies { compileOnly("net.caffeinemc:sodium-fabric:$it") { exclude(group = "net.fabricmc.fabric-api") } - fabricImplementation("net.caffeinemc:sodium-fabric:$it") - neoforgeImplementation("net.caffeinemc:sodium-neoforge:$it") - neoforgeImplementation("net.caffeinemc:sodium-neoforge-mod:$it") + fabricModDependency("net.caffeinemc:sodium-fabric:$it") + neoforgeModDependency("net.caffeinemc:sodium-neoforge:$it") + neoforgeModDependency("net.caffeinemc:sodium-neoforge-mod:$it") } ifPresent("dep.iris") { compileOnly("maven.modrinth:iris:$it") - fabricImplementation("maven.modrinth:iris:$it") + fabricModDependency("maven.modrinth:iris:$it") } ifPresent("dep.iris-neoforge") { - neoforgeImplementation("maven.modrinth:iris:$it") + neoforgeModDependency("maven.modrinth:iris:$it") } ifPresent("dep.rso") { compileOnly("maven.modrinth:reeses-sodium-options:$it") - fabricImplementation("maven.modrinth:reeses-sodium-options:$it") + fabricModDependency("maven.modrinth:reeses-sodium-options:$it") } ifPresent("dep.rso-neoforge") { - neoforgeImplementation("maven.modrinth:reeses-sodium-options:$it") + neoforgeModDependency("maven.modrinth:reeses-sodium-options:$it") } ifPresent("dep.svc") { compileOnly("maven.modrinth:simple-voice-chat:$it") - fabricImplementation("maven.modrinth:simple-voice-chat:$it") + fabricModDependency("maven.modrinth:simple-voice-chat:$it") } ifPresent("dep.svc-neoforge") { - neoforgeImplementation("maven.modrinth:simple-voice-chat:$it") + neoforgeModDependency("maven.modrinth:simple-voice-chat:$it") } ifPresent("dep.fancy-menu") { diff --git a/src/main/java/dev/isxander/controlify/compatibility/ControlifyCompat.java b/src/main/java/dev/isxander/controlify/compatibility/ControlifyCompat.java index ea3c9c2d4..0724f37f9 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/ControlifyCompat.java +++ b/src/main/java/dev/isxander/controlify/compatibility/ControlifyCompat.java @@ -25,7 +25,7 @@ public class ControlifyCompat { public static void init() { //? if simple_voice_chat { - try { + /*try { wrapCompatCall( SIMPLE_VOICE_CHAT, dev.isxander.controlify.compatibility.simplevoicechat.SimpleVoiceChatCompat::init @@ -33,7 +33,7 @@ public static void init() { } catch (NoClassDefFoundError e) { disabledMods.add(SIMPLE_VOICE_CHAT); } - //?} + *///?} //? if fancy_menu { /*try { diff --git a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/SimpleVoiceChatCompat.java b/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/SimpleVoiceChatCompat.java index 7dc2898df..14695f595 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/SimpleVoiceChatCompat.java +++ b/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/SimpleVoiceChatCompat.java @@ -8,7 +8,7 @@ //? if simple_voice_chat { -import de.maxhenkel.voicechat.voice.client.ClientManager; +/*import de.maxhenkel.voicechat.voice.client.ClientManager; import de.maxhenkel.voicechat.voice.client.KeyEvents; import dev.isxander.controlify.api.event.ControlifyEvents; import dev.isxander.controlify.api.bind.ControlifyBindApi; @@ -116,4 +116,4 @@ private static Identifier registerIcon16x(Identifier location) { return location; } } -//?} +*///?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/KeyEventsAccessor.java b/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/KeyEventsAccessor.java index ecc4a2f14..e53b11ac4 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/KeyEventsAccessor.java +++ b/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/KeyEventsAccessor.java @@ -8,7 +8,7 @@ //? if simple_voice_chat { -import de.maxhenkel.voicechat.voice.client.KeyEvents; +/*import de.maxhenkel.voicechat.voice.client.KeyEvents; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; @@ -17,4 +17,4 @@ public interface KeyEventsAccessor { @Invoker boolean invokeCheckConnected(); } -//?} +*///?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/PTTKeyHandlerMixin.java b/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/PTTKeyHandlerMixin.java index 2ce75f8c3..d49108ce2 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/PTTKeyHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/PTTKeyHandlerMixin.java @@ -8,7 +8,7 @@ //? if simple_voice_chat { -import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +/*import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import de.maxhenkel.voicechat.voice.client.PTTKeyHandler; import dev.isxander.controlify.compatibility.simplevoicechat.SimpleVoiceChatCompat; import org.objectweb.asm.Opcodes; @@ -47,4 +47,4 @@ private boolean isControllerWhisperDown(boolean keyDown) { return keyDown || SimpleVoiceChatCompat.isWhisperDown(); } } -//?} +*///?} diff --git a/src/main/java/dev/isxander/controlify/ingame/DropWithResultInvoker.java b/src/main/java/dev/isxander/controlify/ingame/DropWithResultInvoker.java new file mode 100644 index 000000000..8b2d804b0 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/ingame/DropWithResultInvoker.java @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.ingame; + +public interface DropWithResultInvoker { + boolean controlify$drop(final boolean all); +} diff --git a/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java b/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java index 2562d2ddd..65960f89d 100644 --- a/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java +++ b/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java @@ -113,8 +113,10 @@ protected void handleKeybinds() { if (!minecraft.player.isSpectator()) { if (ControlifyBindings.DROP_STACK.on(controller).justPressed()) { - if (minecraft.player.drop(true)) { - minecraft.player.swing(InteractionHand.MAIN_HAND); + if (((DropWithResultInvoker) minecraft.player).controlify$drop(true)) { + // >=26.3 swings within the drop method + //? if <26.3 + //minecraft.player.swing(InteractionHand.MAIN_HAND); } } else { if (ControlifyBindings.DROP_INGAME.on(controller).justPressed()) { @@ -124,9 +126,11 @@ protected void handleKeybinds() { } if (dropRepeating && dropRepeatHelper.shouldAction(ControlifyBindings.DROP_INGAME.on(controller))) { - if (minecraft.player.drop(false)) { + if (((DropWithResultInvoker) minecraft.player).controlify$drop(true)) { dropRepeatHelper.onNavigate(); - minecraft.player.swing(InteractionHand.MAIN_HAND); + // >=26.3 swings within the drop method + //? if <26.3 + //minecraft.player.swing(InteractionHand.MAIN_HAND); } } } diff --git a/src/main/java/dev/isxander/controlify/mixins/core/InputConstantsMixin.java b/src/main/java/dev/isxander/controlify/mixins/core/InputConstantsMixin.java index 6e16621ef..e3fdb464b 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/InputConstantsMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/InputConstantsMixin.java @@ -1,3 +1,9 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ package dev.isxander.controlify.mixins.core; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/core/SDLEventHandlerMixin.java b/src/main/java/dev/isxander/controlify/mixins/core/SDLEventHandlerMixin.java index 898c17778..c70a32a3a 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/SDLEventHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/SDLEventHandlerMixin.java @@ -1,3 +1,9 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ package dev.isxander.controlify.mixins.core; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/patches/dropresult/LocalPlayerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/patches/dropresult/LocalPlayerMixin.java new file mode 100644 index 000000000..f5224a05b --- /dev/null +++ b/src/main/java/dev/isxander/controlify/mixins/feature/patches/dropresult/LocalPlayerMixin.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.mixins.feature.patches.dropresult; + +import dev.isxander.controlify.ingame.DropWithResultInvoker; +import net.minecraft.client.player.LocalPlayer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +//? if >=26.3 { +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.world.item.ItemStack; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +//?} + +@Mixin(LocalPlayer.class) +public abstract class LocalPlayerMixin implements DropWithResultInvoker { + + //? if >=26.3 { + @Unique private final ThreadLocal controlify$dropResult = ThreadLocal.withInitial(() -> false); + + @Shadow + public abstract void drop(boolean all); + + @Inject(method = "drop", at = @At("RETURN")) + private void setResultField(boolean all, CallbackInfo ci, @Local(name = "prediction") ItemStack prediction) { + controlify$dropResult.set(!prediction.isEmpty()); + } + + @Override + public boolean controlify$drop(boolean all) { + this.drop(all); + return controlify$dropResult.get(); + } + //?} else { + /*@Shadow + public abstract boolean drop(boolean all); + + @Override + public boolean controlify$drop(boolean all) { + return this.drop(all); + } + *///?} +} diff --git a/src/main/java/dev/isxander/controlify/utils/CursorUtils.java b/src/main/java/dev/isxander/controlify/utils/CursorUtils.java index 59175326b..a99eddab9 100644 --- a/src/main/java/dev/isxander/controlify/utils/CursorUtils.java +++ b/src/main/java/dev/isxander/controlify/utils/CursorUtils.java @@ -1,3 +1,9 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ package dev.isxander.controlify.utils; import com.mojang.blaze3d.platform.Window; diff --git a/src/main/resources/controlify.mixins.json b/src/main/resources/controlify.mixins.json index 2e5025c37..a823d3505 100644 --- a/src/main/resources/controlify.mixins.json +++ b/src/main/resources/controlify.mixins.json @@ -45,6 +45,7 @@ "feature.oofinput.MinecraftMixin", "feature.patches.analogueboat.AbstractBoatMixin", "feature.patches.analogueboat.LocalPlayerMixin", + "feature.patches.dropresult.LocalPlayerMixin", "feature.patches.keymessages.ClientPacketListenerMixin", "feature.patches.keymessages.MovementTutorialStepInstanceMixin", "feature.patches.keymessages.OpenInventoryTutorialStepMixin", diff --git a/versions/26.3/gradle.properties b/versions/26.3/gradle.properties index 7f8e50d44..0616ac731 100644 --- a/versions/26.3/gradle.properties +++ b/versions/26.3/gradle.properties @@ -1,10 +1,10 @@ -dep.minecraft=26.3-snapshot-6 +dep.minecraft=26.3-snapshot-7 meta.minecraft-range=(26.2,26.4) #dep.neoforge=26.2.0.28-beta -dep.fapi=0.156.1+26.3 -meta.fapi-range=[0.156.1,) +dep.fapi=0.156.2+26.3 +meta.fapi-range=[0.156.2,) dep.yacl=3.9.6+26.3-fabric dep.yacl-neoforge=3.9.6+26.2-neoforge @@ -14,7 +14,7 @@ dep.mod-menu=21.0.0-alpha.1 #dep.sodium=0.9.1+mc26.2 #dep.iris=1.11.2+26.2-fabric #dep.iris-neoforge=1.11.2+26.2-neoforge -dep.svc=fabric-2.6.21+26.3-snapshot-6 +#dep.svc=fabric-2.6.21+26.3-snapshot-6 #dep.svc-neoforge=neoforge-2.6.18+26.2 #dep.fancy-menu=3.9.8-26.2-fabric #dep.fancy-menu-neoforge=3.9.8-26.2-neoforge From 06bdce18d64be2451968a9f5b35ed79ce09f6bf1 Mon Sep 17 00:00:00 2001 From: Xander Date: Tue, 4 Aug 2026 16:47:38 +0100 Subject: [PATCH 03/15] fix compile error on 26.1 --- .../feature/screenop/impl/sign/SignEditScreenMixin.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/sign/SignEditScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/sign/SignEditScreenMixin.java index 928e6dc38..cecc0db78 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/sign/SignEditScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/sign/SignEditScreenMixin.java @@ -13,6 +13,12 @@ import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; +//? if <26.2 { +/*import com.llamalad7.mixinextras.expression.Definition; +import com.llamalad7.mixinextras.expression.Expression; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +*///?} + @Mixin(SignEditScreen.class) public class SignEditScreenMixin { From 713982a68819f940b73e783c1ec6e15ac4181060 Mon Sep 17 00:00:00 2001 From: isxander Date: Mon, 21 Sep 2026 00:37:18 +0100 Subject: [PATCH 04/15] Port to 26.3 release --- gradle/libs.versions.toml | 2 +- .../compatibility/ControlifyCompat.java | 8 +-- .../fancymenu/FancyMenuCompat.java | 4 +- .../OpenControlifySettingsAction.java | 4 +- .../mixins/BaseOptionElementWidgetMixin.java | 4 +- .../BaseOptionElementComponentProcessor.java | 4 +- .../SimpleVoiceChatCompat.java | 4 +- .../mixins/KeyEventsAccessor.java | 4 +- .../mixins/PTTKeyHandlerMixin.java | 4 +- .../sodium/mixins/AbstractWidgetMixin.java | 4 +- .../mixins/CycleControlElementMixin.java | 4 +- .../mixins/FlatButtonWidgetAccessor.java | 4 +- .../sodium/mixins/FlatButtonWidgetMixin.java | 4 +- .../mixins/SliderControlElementMixin.java | 4 +- .../mixins/TickBoxControlElementMixin.java | 4 +- .../mixins/VideoSettingsScreenMixin.java | 8 +-- .../screenop/SodiumGuiScreenProcessor.java | 4 +- .../screenop/SodiumScreenOperations.java | 4 +- .../gui/screen/ControlifySettingsScreen.java | 8 +-- .../screen/GlobalSettingsScreenFactory.java | 2 +- .../ingame/DropWithResultInvoker.java | 11 ---- .../controlify/ingame/InGameInputHandler.java | 25 +++++---- .../patches/dropresult/LocalPlayerMixin.java | 51 ------------------ .../dev/isxander/controlify/utils/CUtil.java | 54 +++---------------- src/main/resources/controlify.mixins.json | 1 - versions/26.3/gradle.properties | 30 +++++------ 26 files changed, 81 insertions(+), 179 deletions(-) delete mode 100644 src/main/java/dev/isxander/controlify/ingame/DropWithResultInvoker.java delete mode 100644 src/main/java/dev/isxander/controlify/mixins/feature/patches/dropresult/LocalPlayerMixin.java diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1398811ac..bd9fc6637 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ modstitch_manifests = "0.1.5" modstitch_multiloader = "0.1.8" modstitch_modrepos = "0.1.2" fabric_loom = "1.17.16" -neoforged_gradle = "7.1.38" +neoforged_gradle = "7.1.39" spotless = "8.6.0" undercouch_download = "5.6.0" wiki_toolkit = "0.4.1" diff --git a/src/main/java/dev/isxander/controlify/compatibility/ControlifyCompat.java b/src/main/java/dev/isxander/controlify/compatibility/ControlifyCompat.java index 0724f37f9..d50059884 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/ControlifyCompat.java +++ b/src/main/java/dev/isxander/controlify/compatibility/ControlifyCompat.java @@ -25,7 +25,7 @@ public class ControlifyCompat { public static void init() { //? if simple_voice_chat { - /*try { + try { wrapCompatCall( SIMPLE_VOICE_CHAT, dev.isxander.controlify.compatibility.simplevoicechat.SimpleVoiceChatCompat::init @@ -33,10 +33,10 @@ public static void init() { } catch (NoClassDefFoundError e) { disabledMods.add(SIMPLE_VOICE_CHAT); } - *///?} + //?} //? if fancy_menu { - /*try { + try { wrapCompatCall( FANCY_MENU, dev.isxander.controlify.compatibility.fancymenu.FancyMenuCompat::registerActions @@ -44,7 +44,7 @@ public static void init() { } catch (NoClassDefFoundError e) { disabledMods.add(FANCY_MENU); } - *///?} + //?} } private static void wrapCompatCall(String modid, Runnable runnable) throws NoClassDefFoundError { diff --git a/src/main/java/dev/isxander/controlify/compatibility/fancymenu/FancyMenuCompat.java b/src/main/java/dev/isxander/controlify/compatibility/fancymenu/FancyMenuCompat.java index 52603a9e9..9c4f78d56 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/fancymenu/FancyMenuCompat.java +++ b/src/main/java/dev/isxander/controlify/compatibility/fancymenu/FancyMenuCompat.java @@ -8,11 +8,11 @@ //? if fancy_menu { -/*import de.keksuccino.fancymenu.customization.action.ActionRegistry; +import de.keksuccino.fancymenu.customization.action.ActionRegistry; public final class FancyMenuCompat { public static void registerActions() { ActionRegistry.register(new OpenControlifySettingsAction()); } } -*///?} +//?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/fancymenu/OpenControlifySettingsAction.java b/src/main/java/dev/isxander/controlify/compatibility/fancymenu/OpenControlifySettingsAction.java index 4b7649b70..c0d53df54 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/fancymenu/OpenControlifySettingsAction.java +++ b/src/main/java/dev/isxander/controlify/compatibility/fancymenu/OpenControlifySettingsAction.java @@ -8,7 +8,7 @@ //? if fancy_menu { -/*import de.keksuccino.fancymenu.customization.action.Action; +import de.keksuccino.fancymenu.customization.action.Action; import dev.isxander.controlify.gui.screen.ControlifySettingsScreen; import dev.isxander.controlify.utils.MinecraftUtil; import net.minecraft.network.chat.Component; @@ -51,4 +51,4 @@ public void execute(@Nullable String s) { } } -*///?} +//?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/iris/mixins/BaseOptionElementWidgetMixin.java b/src/main/java/dev/isxander/controlify/compatibility/iris/mixins/BaseOptionElementWidgetMixin.java index 7450bc940..50d7ca7f6 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/iris/mixins/BaseOptionElementWidgetMixin.java +++ b/src/main/java/dev/isxander/controlify/compatibility/iris/mixins/BaseOptionElementWidgetMixin.java @@ -8,7 +8,7 @@ //? if iris { -/*import dev.isxander.controlify.compatibility.iris.screenop.BaseOptionElementComponentProcessor; +import dev.isxander.controlify.compatibility.iris.screenop.BaseOptionElementComponentProcessor; import dev.isxander.controlify.screenop.ComponentProcessor; import dev.isxander.controlify.screenop.ComponentProcessorProvider; import net.irisshaders.iris.gui.NavigationController; @@ -39,4 +39,4 @@ public ComponentProcessor componentProcessor() { } } -*///?} +//?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/iris/screenop/BaseOptionElementComponentProcessor.java b/src/main/java/dev/isxander/controlify/compatibility/iris/screenop/BaseOptionElementComponentProcessor.java index 08342be26..138d6ee85 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/iris/screenop/BaseOptionElementComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/compatibility/iris/screenop/BaseOptionElementComponentProcessor.java @@ -8,7 +8,7 @@ //? if iris { -/*import dev.isxander.controlify.screenop.compat.AbstractSliderComponentProcessor; +import dev.isxander.controlify.screenop.compat.AbstractSliderComponentProcessor; import java.util.function.Consumer; @@ -24,4 +24,4 @@ protected void incrementSlider(boolean reverse) { this.cycleMethod.accept(reverse); } } -*///?} +//?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/SimpleVoiceChatCompat.java b/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/SimpleVoiceChatCompat.java index 14695f595..7dc2898df 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/SimpleVoiceChatCompat.java +++ b/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/SimpleVoiceChatCompat.java @@ -8,7 +8,7 @@ //? if simple_voice_chat { -/*import de.maxhenkel.voicechat.voice.client.ClientManager; +import de.maxhenkel.voicechat.voice.client.ClientManager; import de.maxhenkel.voicechat.voice.client.KeyEvents; import dev.isxander.controlify.api.event.ControlifyEvents; import dev.isxander.controlify.api.bind.ControlifyBindApi; @@ -116,4 +116,4 @@ private static Identifier registerIcon16x(Identifier location) { return location; } } -*///?} +//?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/KeyEventsAccessor.java b/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/KeyEventsAccessor.java index e53b11ac4..ecc4a2f14 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/KeyEventsAccessor.java +++ b/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/KeyEventsAccessor.java @@ -8,7 +8,7 @@ //? if simple_voice_chat { -/*import de.maxhenkel.voicechat.voice.client.KeyEvents; +import de.maxhenkel.voicechat.voice.client.KeyEvents; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; @@ -17,4 +17,4 @@ public interface KeyEventsAccessor { @Invoker boolean invokeCheckConnected(); } -*///?} +//?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/PTTKeyHandlerMixin.java b/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/PTTKeyHandlerMixin.java index d49108ce2..2ce75f8c3 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/PTTKeyHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/PTTKeyHandlerMixin.java @@ -8,7 +8,7 @@ //? if simple_voice_chat { -/*import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import de.maxhenkel.voicechat.voice.client.PTTKeyHandler; import dev.isxander.controlify.compatibility.simplevoicechat.SimpleVoiceChatCompat; import org.objectweb.asm.Opcodes; @@ -47,4 +47,4 @@ private boolean isControllerWhisperDown(boolean keyDown) { return keyDown || SimpleVoiceChatCompat.isWhisperDown(); } } -*///?} +//?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/AbstractWidgetMixin.java b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/AbstractWidgetMixin.java index 9f80ea2fa..91090a871 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/AbstractWidgetMixin.java +++ b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/AbstractWidgetMixin.java @@ -8,7 +8,7 @@ //? if sodium { -/*import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import dev.isxander.controlify.Controlify; import net.minecraft.client.InputType; import org.spongepowered.asm.mixin.Mixin; @@ -26,4 +26,4 @@ private InputType forceSetFocusedOnController(InputType type) { return type; } } -*///?} +//?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/CycleControlElementMixin.java b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/CycleControlElementMixin.java index 3d56a62d0..8c3aa78ba 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/CycleControlElementMixin.java +++ b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/CycleControlElementMixin.java @@ -8,7 +8,7 @@ //? if sodium { -/*import dev.isxander.controlify.compatibility.sodium.screenop.CycleControlProcessor; +import dev.isxander.controlify.compatibility.sodium.screenop.CycleControlProcessor; import dev.isxander.controlify.screenop.ComponentProcessor; import dev.isxander.controlify.screenop.ComponentProcessorProvider; import org.spongepowered.asm.mixin.Mixin; @@ -27,4 +27,4 @@ public ComponentProcessor componentProcessor() { return controlify$componentProcessor; } } -*///?} +//?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetAccessor.java b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetAccessor.java index e41652c83..6cd2513d5 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetAccessor.java +++ b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetAccessor.java @@ -8,7 +8,7 @@ //? if sodium { -/*import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; import net.caffeinemc.mods.sodium.client.gui.widgets.FlatButtonWidget; @@ -18,4 +18,4 @@ public interface FlatButtonWidgetAccessor { @Invoker void invokeDoAction(); } -*///?} +//?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetMixin.java b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetMixin.java index 70f9a5a9b..c9e592836 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetMixin.java +++ b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetMixin.java @@ -8,7 +8,7 @@ //? if sodium { -/*import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import dev.isxander.controlify.api.bind.InputBinding; import dev.isxander.controlify.gui.ButtonGuideRenderer; import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; @@ -73,4 +73,4 @@ private Component modifyRenderedLabel(Component actualLabel) { this.controllerMessages.clear(); } } -*///?} +//?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/SliderControlElementMixin.java b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/SliderControlElementMixin.java index d6778ecff..2b8c05ed1 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/SliderControlElementMixin.java +++ b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/SliderControlElementMixin.java @@ -8,7 +8,7 @@ //? if sodium { -/*import dev.isxander.controlify.compatibility.sodium.screenop.SliderControlProcessor; +import dev.isxander.controlify.compatibility.sodium.screenop.SliderControlProcessor; import dev.isxander.controlify.screenop.ComponentProcessor; import dev.isxander.controlify.screenop.ComponentProcessorProvider; import net.caffeinemc.mods.sodium.client.config.structure.IntegerOption; @@ -43,4 +43,4 @@ public ComponentProcessor componentProcessor() { )); } } -*///?} +//?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/TickBoxControlElementMixin.java b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/TickBoxControlElementMixin.java index 93a959993..82efa42b0 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/TickBoxControlElementMixin.java +++ b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/TickBoxControlElementMixin.java @@ -8,7 +8,7 @@ //? if sodium { -/*import dev.isxander.controlify.compatibility.sodium.screenop.TickBoxControlProcessor; +import dev.isxander.controlify.compatibility.sodium.screenop.TickBoxControlProcessor; import dev.isxander.controlify.screenop.ComponentProcessor; import dev.isxander.controlify.screenop.ComponentProcessorProvider; import org.spongepowered.asm.mixin.Mixin; @@ -28,4 +28,4 @@ public ComponentProcessor componentProcessor() { return controlify$componentProcessor; } } -*///?} +//?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/VideoSettingsScreenMixin.java b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/VideoSettingsScreenMixin.java index 12ff3f0ca..cf87cfae3 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/VideoSettingsScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/VideoSettingsScreenMixin.java @@ -8,7 +8,7 @@ //? if sodium { -/*import dev.isxander.controlify.compatibility.sodium.screenop.SodiumGuiScreenProcessor; +import dev.isxander.controlify.compatibility.sodium.screenop.SodiumGuiScreenProcessor; import dev.isxander.controlify.compatibility.sodium.screenop.SodiumScreenOperations; import dev.isxander.controlify.screenop.ScreenProcessor; import dev.isxander.controlify.screenop.ScreenProcessorProvider; @@ -33,10 +33,10 @@ public abstract class VideoSettingsScreenMixin extends Screen implements ScreenP @Shadow private net.caffeinemc.mods.sodium.client.gui.widgets.KeyBoundButtonWidget closeButton; @Shadow private net.caffeinemc.mods.sodium.client.gui.widgets.KeyBoundButtonWidget undoButton; //?} else { - /^@Shadow private FlatButtonWidget applyButton; + /*@Shadow private FlatButtonWidget applyButton; @Shadow private FlatButtonWidget closeButton; @Shadow private FlatButtonWidget undoButton; - ^///?} + *///?} @Shadow private OptionListWidget optionList; @Unique private Page controlify$currentPage = null; @@ -106,4 +106,4 @@ public ScreenProcessor screenProcessor() { return undoButton; } } -*///?} +//?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java b/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java index 7ac26febf..12667a3fb 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java @@ -8,7 +8,7 @@ //? if sodium { -/*import dev.isxander.controlify.api.buttonguide.ButtonGuideApi; +import dev.isxander.controlify.api.buttonguide.ButtonGuideApi; import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate; import dev.isxander.controlify.bindings.ControlifyBindings; import dev.isxander.controlify.compatibility.sodium.mixins.FlatButtonWidgetAccessor; @@ -70,4 +70,4 @@ public void onRebuildGUI() { super.onWidgetRebuild(); } } -*///?} +//?} diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumScreenOperations.java b/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumScreenOperations.java index 5f3286460..be61f804f 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumScreenOperations.java +++ b/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumScreenOperations.java @@ -8,7 +8,7 @@ //? if sodium { -/*import net.caffeinemc.mods.sodium.client.gui.widgets.FlatButtonWidget; +import net.caffeinemc.mods.sodium.client.gui.widgets.FlatButtonWidget; public interface SodiumScreenOperations { void controlify$nextPage(); @@ -21,4 +21,4 @@ public interface SodiumScreenOperations { FlatButtonWidget controlify$getUndoButton(); } -*///?} +//?} diff --git a/src/main/java/dev/isxander/controlify/gui/screen/ControlifySettingsScreen.java b/src/main/java/dev/isxander/controlify/gui/screen/ControlifySettingsScreen.java index c09b1ac6e..54057a692 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/ControlifySettingsScreen.java +++ b/src/main/java/dev/isxander/controlify/gui/screen/ControlifySettingsScreen.java @@ -67,7 +67,7 @@ protected void init() { Component donateText = Component.translatable("controlify.gui.carousel.donate") .withStyle(ChatFormatting.GOLD, ChatFormatting.BOLD); PlainTextButton donateBtn = this.addRenderableWidget(new PlainTextButton(3, 3, 100, 11, donateText, btn -> { - Util.getPlatform().openUri("https://patreon.com/isxander"); + CUtil.openUri("https://patreon.com/isxander"); }, font)); donateBtn.setTabOrderGroup(2); @@ -75,7 +75,7 @@ protected void init() { .withStyle(ChatFormatting.DARK_GRAY); int artCreditTextWidth = font.width(artCreditText); PlainTextButton artCreditBtn = this.addRenderableWidget(new PlainTextButton(width - artCreditTextWidth - 3, 3, artCreditTextWidth, 11, artCreditText, btn -> { - Util.getPlatform().openUri("https://github.com/Andrew6rant"); + CUtil.openUri("https://github.com/Andrew6rant"); }, font)); artCreditBtn.setTabOrderGroup(2); @@ -84,7 +84,7 @@ protected void init() { controllerNotDetectedButton = this.addRenderableWidget( Button.builder( Component.translatable("controlify.gui.carousel.controller_not_detected_btn"), - btn -> Util.getPlatform().openUri("https://docs.isxander.dev/controlify/users/controller-issues#my-controller-is-not-detected") + btn -> CUtil.openUri("https://docs.isxander.dev/controlify/users/controller-issues#my-controller-is-not-detected") ) .pos(width / 2 - 75, (this.height - 36) / 2 + 10) .tooltip(Tooltip.create(Component.translatable("controlify.gui.carousel.controller_not_detected_btn.tooltip"))) @@ -437,7 +437,7 @@ public SplitscreenAdvertisementSlotEntry( ); this.adButton = Button.builder( Component.literal("Donate on Patreon"), - btn -> Util.getPlatform().openUri("https://patreon.com/isxander") + btn -> CUtil.openUri("https://patreon.com/isxander") ).build(); Component disableAdText = Component.literal("Don't show this again") .withStyle(ChatFormatting.DARK_GRAY); diff --git a/src/main/java/dev/isxander/controlify/gui/screen/GlobalSettingsScreenFactory.java b/src/main/java/dev/isxander/controlify/gui/screen/GlobalSettingsScreenFactory.java index abb4a7da8..1e885fe5a 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/GlobalSettingsScreenFactory.java +++ b/src/main/java/dev/isxander/controlify/gui/screen/GlobalSettingsScreenFactory.java @@ -41,7 +41,7 @@ public static Screen createGlobalSettingsScreen(Screen parent) { .name(Component.translatable("controlify.gui.global_settings.title")) .option(ButtonOption.createBuilder() .name(Component.translatable("controlify.gui.open_issue_tracker")) - .action((screen, button) -> Util.getPlatform().openUri("https://github.com/isxander/controlify/issues")) + .action((screen, button) -> CUtil.openUri("https://github.com/isxander/controlify/issues")) .build()) .group(OptionGroup.createBuilder() .name(Component.translatable("controlify.gui.server_options")) diff --git a/src/main/java/dev/isxander/controlify/ingame/DropWithResultInvoker.java b/src/main/java/dev/isxander/controlify/ingame/DropWithResultInvoker.java deleted file mode 100644 index 8b2d804b0..000000000 --- a/src/main/java/dev/isxander/controlify/ingame/DropWithResultInvoker.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.ingame; - -public interface DropWithResultInvoker { - boolean controlify$drop(final boolean all); -} diff --git a/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java b/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java index 65960f89d..56bebc535 100644 --- a/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java +++ b/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java @@ -48,6 +48,7 @@ import org.joml.Vector2f; import java.io.File; +import java.util.Objects; public class InGameInputHandler { private final ControllerEntity controller; @@ -113,11 +114,7 @@ protected void handleKeybinds() { if (!minecraft.player.isSpectator()) { if (ControlifyBindings.DROP_STACK.on(controller).justPressed()) { - if (((DropWithResultInvoker) minecraft.player).controlify$drop(true)) { - // >=26.3 swings within the drop method - //? if <26.3 - //minecraft.player.swing(InteractionHand.MAIN_HAND); - } + dropItem(minecraft.player, true); } else { if (ControlifyBindings.DROP_INGAME.on(controller).justPressed()) { dropRepeating = true; @@ -126,11 +123,8 @@ protected void handleKeybinds() { } if (dropRepeating && dropRepeatHelper.shouldAction(ControlifyBindings.DROP_INGAME.on(controller))) { - if (((DropWithResultInvoker) minecraft.player).controlify$drop(true)) { + if (dropItem(minecraft.player, false)) { dropRepeatHelper.onNavigate(); - // >=26.3 swings within the drop method - //? if <26.3 - //minecraft.player.swing(InteractionHand.MAIN_HAND); } } } @@ -488,4 +482,17 @@ private boolean canProcessLookInput() { public static Vec2 getMoveVec(ClientInput input) { return input.getMoveVector(); } + + private static boolean dropItem(LocalPlayer player, boolean all) { + //? if >=26.3 { + Objects.requireNonNull(Minecraft.getInstance().gameMode).dropItem(player, all); + return !player.getInventory().getSelectedItem().isEmpty(); + //?} else { + /*if (player.drop(all)) { + player.swing(InteractionHand.MAIN_HAND); + return true; + } + return false; + *///?} + } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/patches/dropresult/LocalPlayerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/patches/dropresult/LocalPlayerMixin.java deleted file mode 100644 index f5224a05b..000000000 --- a/src/main/java/dev/isxander/controlify/mixins/feature/patches/dropresult/LocalPlayerMixin.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.mixins.feature.patches.dropresult; - -import dev.isxander.controlify.ingame.DropWithResultInvoker; -import net.minecraft.client.player.LocalPlayer; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; - -//? if >=26.3 { -import com.llamalad7.mixinextras.sugar.Local; -import net.minecraft.world.item.ItemStack; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -//?} - -@Mixin(LocalPlayer.class) -public abstract class LocalPlayerMixin implements DropWithResultInvoker { - - //? if >=26.3 { - @Unique private final ThreadLocal controlify$dropResult = ThreadLocal.withInitial(() -> false); - - @Shadow - public abstract void drop(boolean all); - - @Inject(method = "drop", at = @At("RETURN")) - private void setResultField(boolean all, CallbackInfo ci, @Local(name = "prediction") ItemStack prediction) { - controlify$dropResult.set(!prediction.isEmpty()); - } - - @Override - public boolean controlify$drop(boolean all) { - this.drop(all); - return controlify$dropResult.get(); - } - //?} else { - /*@Shadow - public abstract boolean drop(boolean all); - - @Override - public boolean controlify$drop(boolean all) { - return this.drop(all); - } - *///?} -} diff --git a/src/main/java/dev/isxander/controlify/utils/CUtil.java b/src/main/java/dev/isxander/controlify/utils/CUtil.java index 8f3d6cec0..07d8cee9b 100644 --- a/src/main/java/dev/isxander/controlify/utils/CUtil.java +++ b/src/main/java/dev/isxander/controlify/utils/CUtil.java @@ -6,6 +6,7 @@ */ package dev.isxander.controlify.utils; +import com.mojang.blaze3d.Blaze3D; import dev.isxander.controlify.utils.log.ControlifyLogger; import net.minecraft.util.Util; import net.minecraft.resources.Identifier; @@ -31,23 +32,12 @@ public static Identifier rl(String path) { return Identifier.fromNamespaceAndPath("controlify", path); } - /** - * Opens a URI using the system's default handler. - * Required because Minecraft's implementation converts all URIs to URLs, - * which prevents the use of custom protocols like steam:// - * @param uri the URI to open - */ public static void openUri(String uri) { - try { - String[] command = URIOpener.get().openArguments(URI.create(uri)); - - Process process = Runtime.getRuntime().exec(command); - process.getInputStream().close(); - process.getOutputStream().close(); - process.getErrorStream().close(); - } catch (IOException | SecurityException e) { - LOGGER.error("Failed to open URI: {}", uri, e); - } + //? if >=26.3 { + Blaze3D.openUri(URI.create(uri)); + //?} else { + /*Util.getPlatform().openUri(uri); + *///?} } public static Supplier lazyInit(Supplier supplier) { @@ -85,38 +75,6 @@ public static void sleepChecked(long millis) { } } - private enum URIOpener { - WINDOWS(Util.OS.WINDOWS), - OSX(Util.OS.OSX), - LINUX(Util.OS.LINUX), - SOLARIS(Util.OS.SOLARIS); - - private final Util.OS mcOS; - - URIOpener(Util.OS mcOS) { - this.mcOS = mcOS; - } - - public String[] openArguments(URI uri) { - return switch (this.mcOS) { - case WINDOWS -> new String[]{"rundll32", "url.dll,FileProtocolHandler", uri.toString()}; - case OSX -> new String[]{"open", uri.toString()}; - case LINUX, SOLARIS -> new String[]{"xdg-open", uri.toString()}; - default -> throw new UnsupportedOperationException("Unsupported OS: " + this.mcOS); - }; - } - - public static URIOpener get() { - return switch (Util.getPlatform()) { - case WINDOWS -> WINDOWS; - case OSX -> OSX; - case LINUX -> LINUX; - case SOLARIS -> SOLARIS; - default -> throw new UnsupportedOperationException("Unsupported OS: " + Util.getPlatform()); - }; - } - } - public static Function createNameLookup(T[] values, Function keyFunction) { Map map = Arrays.stream(values) .collect( diff --git a/src/main/resources/controlify.mixins.json b/src/main/resources/controlify.mixins.json index a823d3505..2e5025c37 100644 --- a/src/main/resources/controlify.mixins.json +++ b/src/main/resources/controlify.mixins.json @@ -45,7 +45,6 @@ "feature.oofinput.MinecraftMixin", "feature.patches.analogueboat.AbstractBoatMixin", "feature.patches.analogueboat.LocalPlayerMixin", - "feature.patches.dropresult.LocalPlayerMixin", "feature.patches.keymessages.ClientPacketListenerMixin", "feature.patches.keymessages.MovementTutorialStepInstanceMixin", "feature.patches.keymessages.OpenInventoryTutorialStepMixin", diff --git a/versions/26.3/gradle.properties b/versions/26.3/gradle.properties index 0616ac731..547eafd86 100644 --- a/versions/26.3/gradle.properties +++ b/versions/26.3/gradle.properties @@ -1,20 +1,20 @@ -dep.minecraft=26.3-snapshot-7 -meta.minecraft-range=(26.2,26.4) +dep.minecraft=26.3 +meta.minecraft-range=[26.3,26.4) -#dep.neoforge=26.2.0.28-beta +dep.neoforge=26.3.0.7-beta -dep.fapi=0.156.2+26.3 -meta.fapi-range=[0.156.2,) +dep.fapi=0.161.0+26.3 +meta.fapi-range=[0.161.0,) -dep.yacl=3.9.6+26.3-fabric -dep.yacl-neoforge=3.9.6+26.2-neoforge +dep.yacl=3.9.7+26.3-fabric +dep.yacl-neoforge=3.9.7+26.3-neoforge meta.yacl-range=[,) -dep.mod-menu=21.0.0-alpha.1 -#dep.sodium=0.9.1+mc26.2 -#dep.iris=1.11.2+26.2-fabric -#dep.iris-neoforge=1.11.2+26.2-neoforge -#dep.svc=fabric-2.6.21+26.3-snapshot-6 -#dep.svc-neoforge=neoforge-2.6.18+26.2 -#dep.fancy-menu=3.9.8-26.2-fabric -#dep.fancy-menu-neoforge=3.9.8-26.2-neoforge +dep.mod-menu=21.0.0-beta.1 +dep.sodium=0.9.2+mc26.3 +dep.iris=1.11.6+26.3-fabric +#dep.iris-neoforge=1.11.6+26.3-neoforge +dep.svc=fabric-2.6.24+26.3 +dep.svc-neoforge=neoforge-2.6.24+26.3 +dep.fancy-menu=3.9.13-26.3-fabric +dep.fancy-menu-neoforge=3.9.13-26.3-neoforge From 32896a2a9632e9fbe85e87d42c75d2649725b355 Mon Sep 17 00:00:00 2001 From: isxander Date: Mon, 21 Sep 2026 00:45:10 +0100 Subject: [PATCH 05/15] always include natives (for now) --- build.gradle.kts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index f356f6d53..39b8ebf0e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -211,7 +211,8 @@ tasks.withType().configureEach { /// Natives in the jar -val includeNatives = sc.current.parsed < "26.3" +//val includeNatives = sc.current.parsed < "26.3" +val includeNatives = true stonecutter.constants.put("natives_in_jar", includeNatives) From b158aec023ef0c6c54a27f417ea29f337d5fe24a Mon Sep 17 00:00:00 2001 From: isxander Date: Mon, 21 Sep 2026 00:50:13 +0100 Subject: [PATCH 06/15] do not have iris at runtime; forces GL despite graphics setting --- build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 39b8ebf0e..8ebf903a9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -83,10 +83,10 @@ dependencies { ifPresent("dep.iris") { compileOnly("maven.modrinth:iris:$it") - fabricModDependency("maven.modrinth:iris:$it") + fabricCompileOnly("maven.modrinth:iris:$it") } ifPresent("dep.iris-neoforge") { - neoforgeModDependency("maven.modrinth:iris:$it") + fabricCompileOnly("maven.modrinth:iris:$it") } ifPresent("dep.rso") { From 074d04b9830b643a3d47ef87175296cdf2317c4c Mon Sep 17 00:00:00 2001 From: isxander Date: Mon, 21 Sep 2026 00:55:10 +0100 Subject: [PATCH 07/15] remove references to GLFW --- .../controlify/gui/screen/RadialMenuEditScreen.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuEditScreen.java b/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuEditScreen.java index c05f80997..bca14447d 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuEditScreen.java +++ b/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuEditScreen.java @@ -6,6 +6,7 @@ */ package dev.isxander.controlify.gui.screen; +import com.mojang.blaze3d.platform.InputConstants; import dev.isxander.controlify.Controlify; import dev.isxander.controlify.api.bind.InputBinding; import dev.isxander.controlify.api.bind.RadialIcon; @@ -43,7 +44,6 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; import org.jspecify.annotations.NonNull; -import org.lwjgl.glfw.GLFW; import java.util.LinkedHashMap; import java.util.List; @@ -378,23 +378,23 @@ public void extractRenderState(@NonNull GuiGraphicsExtractor graphics, int mouse @Override public boolean keyPressed(@NonNull KeyEvent event) { if (model.isCarrying()) { - if (event.key() == GLFW.GLFW_KEY_ENTER || event.key() == GLFW.GLFW_KEY_SPACE) { + if (event.key() == InputConstants.KEY_RETURN || event.key() == InputConstants.KEY_SPACE) { dropCarried(); return true; } - if (event.key() == GLFW.GLFW_KEY_LEFT) { + if (event.key() == InputConstants.KEY_LEFT) { moveCarried(ScreenDirection.LEFT); return true; } - if (event.key() == GLFW.GLFW_KEY_RIGHT) { + if (event.key() == InputConstants.KEY_RIGHT) { moveCarried(ScreenDirection.RIGHT); return true; } - if (event.key() == GLFW.GLFW_KEY_UP) { + if (event.key() == InputConstants.KEY_UP) { moveCarried(ScreenDirection.UP); return true; } - if (event.key() == GLFW.GLFW_KEY_DOWN) { + if (event.key() == InputConstants.KEY_DOWN) { moveCarried(ScreenDirection.DOWN); return true; } From aba12eea167b1e25a34ec1865a998a59ceafbad2 Mon Sep 17 00:00:00 2001 From: isxander Date: Mon, 21 Sep 2026 00:57:23 +0100 Subject: [PATCH 08/15] fix neoforge iris dep --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 8ebf903a9..750b099ed 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -86,7 +86,7 @@ dependencies { fabricCompileOnly("maven.modrinth:iris:$it") } ifPresent("dep.iris-neoforge") { - fabricCompileOnly("maven.modrinth:iris:$it") + neoforgeCompileOnly("maven.modrinth:iris:$it") } ifPresent("dep.rso") { From 5148d0c16f6d0b8e3c690295eb8db471e4999d68 Mon Sep 17 00:00:00 2001 From: isxander Date: Mon, 21 Sep 2026 00:58:58 +0100 Subject: [PATCH 09/15] compile against iris neoforge despite it not being available 26.3 --- versions/26.3/gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions/26.3/gradle.properties b/versions/26.3/gradle.properties index 547eafd86..77966c612 100644 --- a/versions/26.3/gradle.properties +++ b/versions/26.3/gradle.properties @@ -13,7 +13,7 @@ meta.yacl-range=[,) dep.mod-menu=21.0.0-beta.1 dep.sodium=0.9.2+mc26.3 dep.iris=1.11.6+26.3-fabric -#dep.iris-neoforge=1.11.6+26.3-neoforge +dep.iris-neoforge=1.11.4+26.2-neoforge dep.svc=fabric-2.6.24+26.3 dep.svc-neoforge=neoforge-2.6.24+26.3 dep.fancy-menu=3.9.13-26.3-fabric From c44b68391edb02e069e9226f082e33438666c56d Mon Sep 17 00:00:00 2001 From: isxander Date: Mon, 21 Sep 2026 01:29:19 +0100 Subject: [PATCH 10/15] fix common neoforge check fail --- build.gradle.kts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 750b099ed..a66a404be 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -208,6 +208,14 @@ tasks.withType().configureEach { tasks.withType().configureEach { options.compilerArgs.add("-parameters") } +// Mixin 0.17.4 changed ModifyArg/ModifyVariable.at from At to At[]. Align the +// compile APIs so common annotation encoding matches in the NeoForge check. +// Existing Mixin runtimes accept both encodings; leave runtime dependencies unchanged. +if (minecraftVersion == "26.3") { + configurations.named("neoforgeCompileClasspath") { + resolutionStrategy.force("net.fabricmc:sponge-mixin:0.17.4+mixin.0.8.7") + } +} /// Natives in the jar From ad5a822606f2b092d2c1d6fd6477009daa0641a9 Mon Sep 17 00:00:00 2001 From: isxander Date: Mon, 21 Sep 2026 01:50:32 +0100 Subject: [PATCH 11/15] fix gametest not compiling with new fapi --- .../gametest/framework/TestUnitWorldContext.java | 12 ++++++++++-- .../gametest/tests/KeyboardLayoutTests.java | 6 +++++- .../gametest/tests/ResetControlifySettingsTests.java | 2 +- .../controlify/gametest/tests/RumbleTests.java | 6 +++++- .../controlify/gametest/tests/ServerPolicyTests.java | 6 +++++- .../controlify/driver/sdl/SDLNativesLoader.java | 2 +- versions/26.2/gradle.properties | 4 ++-- versions/versions.json | 2 +- 8 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/gametest/java/dev/isxander/controlify/gametest/framework/TestUnitWorldContext.java b/src/gametest/java/dev/isxander/controlify/gametest/framework/TestUnitWorldContext.java index bbbeecbf1..1f6a859d5 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/framework/TestUnitWorldContext.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/framework/TestUnitWorldContext.java @@ -43,7 +43,11 @@ public class TestUnitWorldContext implements ReversibleCallback { public TestUnitWorldContext(ClientGameTestContext context) { this.context = context; this.world = context.worldBuilder().setUseConsistentSettings(true).create(); - this.world.getClientLevel().waitForChunksDownload(); + //? if >=26.2 { + this.world.getConnection().waitForChunksDownload(); + //?} else { + /*this.world.getClientLevel().waitForChunksDownload(); + *///?} this.level = this.world.getServer().computeOnServer(server -> CTestUtil.getPrincipalPlayer(server).level()); this.floorY = this.world.getServer().computeOnServer(server -> { var player = CTestUtil.getPrincipalPlayer(server); @@ -67,7 +71,11 @@ public TestRegionContext allocateRegion(Vec3i size) { context.waitTick(); region.teleportPlayer(new BlockPos(1, 1, 1)); context.waitTick(); - getWorld().getClientLevel().waitForChunksRender(); + //? if >=26.2 { + getWorld().getConnection().waitForChunksRender(); + //?} else { + /*getWorld().getClientLevel().waitForChunksRender(); + *///?} return region; } diff --git a/src/gametest/java/dev/isxander/controlify/gametest/tests/KeyboardLayoutTests.java b/src/gametest/java/dev/isxander/controlify/gametest/tests/KeyboardLayoutTests.java index f0a6d7f2c..85b042376 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/KeyboardLayoutTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/KeyboardLayoutTests.java @@ -32,7 +32,11 @@ public void runTest(ClientGameTestContext context) { try (var world = context.worldBuilder() .create()) { - world.getClientLevel().waitForChunksRender(); + //? if >=26.2 { + world.getConnection().waitForChunksRender(); + //?} else { + /*world.getClientLevel().waitForChunksRender(); + *///?} runLocaleTest(context, controlify); } diff --git a/src/gametest/java/dev/isxander/controlify/gametest/tests/ResetControlifySettingsTests.java b/src/gametest/java/dev/isxander/controlify/gametest/tests/ResetControlifySettingsTests.java index 665173920..b5628ff39 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/ResetControlifySettingsTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/ResetControlifySettingsTests.java @@ -13,7 +13,7 @@ import net.fabricmc.fabric.api.client.gametest.v1.FabricClientGameTest; import net.fabricmc.fabric.api.client.gametest.v1.context.ClientGameTestContext; -/// Ensures that {@link CTestUtil#resetControlifySettings(ClientGameTestContext)} +/// Ensures that {@link ControlifyGameTestContext#resetSettings()} /// works appropriately, not leaving behind any stale references. @SuppressWarnings("UnstableApiUsage") public class ResetControlifySettingsTests implements FabricClientGameTest { diff --git a/src/gametest/java/dev/isxander/controlify/gametest/tests/RumbleTests.java b/src/gametest/java/dev/isxander/controlify/gametest/tests/RumbleTests.java index 1fac12fea..9b577ea55 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/RumbleTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/RumbleTests.java @@ -49,7 +49,11 @@ public void runTest(ClientGameTestContext context) { .withXbox() .attach()) { try (var world = new TestUnitWorldContext(context)) { - world.getWorld().getClientLevel().waitForChunksRender(); + //? if >=26.2 { + world.getWorld().getConnection().waitForChunksRender(); + //?} else { + /*world.getWorld().getClientLevel().waitForChunksRender(); + *///?} runTest(context, controlify, controller, world, "Lightning bolt", this::testLightningBoltRumble); runTest(context, controlify, controller, world, "Explosion", this::testExplosionRumble); diff --git a/src/gametest/java/dev/isxander/controlify/gametest/tests/ServerPolicyTests.java b/src/gametest/java/dev/isxander/controlify/gametest/tests/ServerPolicyTests.java index 2a5c5acb1..231263390 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/ServerPolicyTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/ServerPolicyTests.java @@ -33,7 +33,11 @@ public void runTest(ClientGameTestContext context) { try (var connection = world.connect()) { context.waitFor(minecraft -> CTestUtil.isToastPresent(minecraft, "controlify.toast.new_server.title")); - connection.getClientLevel().waitForChunksDownload(); + //? if >=26.2 { + connection.waitForChunksDownload(); + //?} else { + /*connection.getClientLevel().waitForChunksDownload(); + *///?} assertKeyboardMovement(context, true); } diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLNativesLoader.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDLNativesLoader.java index 048a57510..6524f9184 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDLNativesLoader.java +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDLNativesLoader.java @@ -28,7 +28,7 @@ public class SDLNativesLoader { private static final boolean LWJGL_SDL_AVAILABLE = /*? if >=26.3 {*/ true /*?} else {*/ /*false *//*?}*/; - private static final boolean NATIVES_IN_JAR = /*? if natives_in_jar {*/ /*true *//*?} else {*/ false /*?}*/; + private static final boolean NATIVES_IN_JAR = /*? if natives_in_jar {*/ true /*?} else {*/ /*false *//*?}*/; private static final String NATIVE_SDL_NAME = System.mapLibraryName("SDL3"); private static final String NATIVE_SDL_PATH = Platform.getResourcePrefix() + "/" + NATIVE_SDL_NAME; diff --git a/versions/26.2/gradle.properties b/versions/26.2/gradle.properties index 46b8c4085..7970596f5 100644 --- a/versions/26.2/gradle.properties +++ b/versions/26.2/gradle.properties @@ -3,8 +3,8 @@ meta.minecraft-range=[26.2,26.3) dep.neoforge=26.2.0.28-beta -dep.fapi=0.152.1+26.2 -meta.fapi-range=[0.152.1,) +dep.fapi=0.161.0+26.2 +meta.fapi-range=[0.161.0,) dep.yacl=3.9.6+26.2-fabric dep.yacl-neoforge=3.9.6+26.2-neoforge diff --git a/versions/versions.json b/versions/versions.json index 9876e335c..c8b58f710 100644 --- a/versions/versions.json +++ b/versions/versions.json @@ -1,6 +1,6 @@ { "$schema": "https://stonecutter.kikugie.dev/settings-schema/0.9.json", - "vcs": "26.2", + "vcs": "26.3", "branches": { "": [ "26.1", From 5fc0ad37ac6de87de4b59a86678fefe1abe5b57f Mon Sep 17 00:00:00 2001 From: isxander Date: Mon, 21 Sep 2026 01:56:01 +0100 Subject: [PATCH 12/15] fix gametest not compiling with new fapi x2 --- .../controlify/gametest/tests/AdaptiveTriggerTests.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/gametest/java/dev/isxander/controlify/gametest/tests/AdaptiveTriggerTests.java b/src/gametest/java/dev/isxander/controlify/gametest/tests/AdaptiveTriggerTests.java index 3aebb2090..6d1bbbb1e 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/AdaptiveTriggerTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/AdaptiveTriggerTests.java @@ -53,7 +53,11 @@ public void runTest(ClientGameTestContext context) { .withDualsense() .attachWithDriver(new DualsenseEffectDriver())) { try (var world = context.worldBuilder().create()) { - world.getClientLevel().waitForChunksRender(); + //? if >=26.2 { + world.getConnection().waitForChunksRender(); + //?} else { + /*world.getClientLevel().waitForChunksRender(); + *///?} setServerHands(world, new ItemStack(Items.DIAMOND_SWORD), new ItemStack(Items.SHIELD)); context.waitTick(); From da1346afecf1a0a4829a79c9cc6fca147dbd9dfd Mon Sep 17 00:00:00 2001 From: isxander Date: Mon, 21 Sep 2026 10:31:25 +0100 Subject: [PATCH 13/15] fix tests somehow? --- build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index a66a404be..c515be924 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -99,10 +99,10 @@ dependencies { ifPresent("dep.svc") { compileOnly("maven.modrinth:simple-voice-chat:$it") - fabricModDependency("maven.modrinth:simple-voice-chat:$it") + fabricCompileOnly("maven.modrinth:simple-voice-chat:$it") } ifPresent("dep.svc-neoforge") { - neoforgeModDependency("maven.modrinth:simple-voice-chat:$it") + neoforgeCompileOnly("maven.modrinth:simple-voice-chat:$it") } ifPresent("dep.fancy-menu") { From 1f33c56e7c24683820f6d62873005eb109d9662a Mon Sep 17 00:00:00 2001 From: isxander Date: Mon, 21 Sep 2026 10:46:24 +0100 Subject: [PATCH 14/15] use more reliable assertion for keyboard layout test --- .../gametest/tests/KeyboardLayoutTests.java | 36 ++++++++++++------ .../keyboard_layout/full/en_gb.json | 4 +- .../keyboard_layout/full/en_us.json | 4 +- .../templates/keyboard_layout_en_gb.png | Bin 12788 -> 0 bytes .../templates/keyboard_layout_en_us.png | Bin 15552 -> 0 bytes 5 files changed, 28 insertions(+), 16 deletions(-) delete mode 100644 src/gametest/resources/templates/keyboard_layout_en_gb.png delete mode 100644 src/gametest/resources/templates/keyboard_layout_en_us.png diff --git a/src/gametest/java/dev/isxander/controlify/gametest/tests/KeyboardLayoutTests.java b/src/gametest/java/dev/isxander/controlify/gametest/tests/KeyboardLayoutTests.java index 85b042376..66061ae4e 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/KeyboardLayoutTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/KeyboardLayoutTests.java @@ -8,6 +8,8 @@ import dev.isxander.controlify.gametest.framework.CTestUtil; import dev.isxander.controlify.gametest.framework.controller.ControlifyGameTestContext; +import dev.isxander.controlify.screenop.keyboard.KeyboardWidget; +import dev.isxander.controlify.utils.MinecraftUtil; import dev.isxander.sdl.SdlGamepad; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.gametest.v1.FabricClientGameTest; @@ -17,6 +19,9 @@ import net.minecraft.client.gui.screens.ChatScreen; import net.minecraft.resources.Identifier; +import java.util.Optional; +import java.util.stream.Stream; + @SuppressWarnings("UnstableApiUsage") public class KeyboardLayoutTests implements FabricClientGameTest, ClientModInitializer { private static final Identifier RESOURCE_PACK_ID = CTestUtil.id("keyboard_layout_test"); @@ -59,10 +64,7 @@ private void runLocaleTest(ClientGameTestContext context, ControlifyGameTestCont context.waitForScreen(ChatScreen.class); // check american keyboard layout - context.assertScreenshotContains(TestScreenshotComparisonOptions - .of("keyboard_layout_en_us") - .save() - .withAlgorithm(TestScreenshotComparisonAlgorithm.meanSquaredDifference(0.001f))); + assertKeyboard(context, "en_us"); // press back button to close on-screen keyboard controller.tapButton(SdlGamepad.SDL_GAMEPAD_BUTTON_EAST); @@ -75,16 +77,10 @@ private void runLocaleTest(ClientGameTestContext context, ControlifyGameTestCont context.waitForScreen(ChatScreen.class); // check british keyboard layout - context.assertScreenshotContains(TestScreenshotComparisonOptions - .of("keyboard_layout_en_gb") - .save() - .withAlgorithm(TestScreenshotComparisonAlgorithm.meanSquaredDifference(0.001f))); + assertKeyboard(context, "en_gb"); } // change language without closing chat screen and check en_us loaded - context.assertScreenshotContains(TestScreenshotComparisonOptions - .of("keyboard_layout_en_us") - .save() - .withAlgorithm(TestScreenshotComparisonAlgorithm.meanSquaredDifference(0.001f))); + assertKeyboard(context, "en_us"); // press back button to close on-screen keyboard controller.tapButton(SdlGamepad.SDL_GAMEPAD_BUTTON_EAST); @@ -93,4 +89,20 @@ private void runLocaleTest(ClientGameTestContext context, ControlifyGameTestCont } } + + private static void assertKeyboard(ClientGameTestContext context, String keyString) { + context.computeOnClient(_ -> { + var chatScreen = (ChatScreen) MinecraftUtil.getScreen(); + KeyboardWidget keyboard = chatScreen.children().stream() + .flatMap(child -> child instanceof KeyboardWidget k ? Stream.of(k) : Stream.empty()) + .findAny() + .orElse(null); + if (keyboard == null) { + return false; + } + + return keyboard.children().stream() + .anyMatch(key -> key.getKeyFunction().displayName().getString().equals(keyString)); + }); + } } diff --git a/src/gametest/resources/resourcepacks/keyboard_layout_test/assets/controlify/keyboard_layout/full/en_gb.json b/src/gametest/resources/resourcepacks/keyboard_layout_test/assets/controlify/keyboard_layout/full/en_gb.json index 81cee5708..d8403cbdf 100644 --- a/src/gametest/resources/resourcepacks/keyboard_layout_test/assets/controlify/keyboard_layout/full/en_gb.json +++ b/src/gametest/resources/resourcepacks/keyboard_layout_test/assets/controlify/keyboard_layout/full/en_gb.json @@ -1,6 +1,6 @@ { - "width": 7, + "width": 1, "keys": [ - ["b", "r", "i", "t", "i", "s", "h"] + ["en_gb"] ] } diff --git a/src/gametest/resources/resourcepacks/keyboard_layout_test/assets/controlify/keyboard_layout/full/en_us.json b/src/gametest/resources/resourcepacks/keyboard_layout_test/assets/controlify/keyboard_layout/full/en_us.json index 938424245..fcf805ca3 100644 --- a/src/gametest/resources/resourcepacks/keyboard_layout_test/assets/controlify/keyboard_layout/full/en_us.json +++ b/src/gametest/resources/resourcepacks/keyboard_layout_test/assets/controlify/keyboard_layout/full/en_us.json @@ -1,6 +1,6 @@ { - "width": 7, + "width": 1, "keys": [ - ["a", "m", "e", "r", "i", "c", "a"] + ["en_us"] ] } diff --git a/src/gametest/resources/templates/keyboard_layout_en_gb.png b/src/gametest/resources/templates/keyboard_layout_en_gb.png deleted file mode 100644 index f00da5946c10ec4599d6997762d11523d2271040..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12788 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%0D+T`*Y|@YUTIl) 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 F^?&3flv)4) From 68e4d3b7d86ac21056cc4e42d47cbe128b8d02be Mon Sep 17 00:00:00 2001 From: isxander Date: Mon, 21 Sep 2026 10:53:54 +0100 Subject: [PATCH 15/15] why are all the tests suddenly flaky? --- .../controlify/gametest/tests/VirtualControllerInputTests.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gametest/java/dev/isxander/controlify/gametest/tests/VirtualControllerInputTests.java b/src/gametest/java/dev/isxander/controlify/gametest/tests/VirtualControllerInputTests.java index 22160ed0c..f22a845b7 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/VirtualControllerInputTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/VirtualControllerInputTests.java @@ -51,6 +51,8 @@ public void runTest(ClientGameTestContext context) { var controllerEntity = controller.getControllerEntity(); var input = controllerEntity.input().orElseThrow(); + context.waitTick(); + for (var expected : XINPUT_BUTTONS.entrySet()) { controller.holdButton(expected.getKey()); context.waitFor(_ -> input.rawStateNow().isButtonDown(expected.getValue()), 10);