1
0
forked from Clones/Controlify

controllers!

This commit is contained in:
isXander
2023-01-31 18:39:30 +00:00
parent b090ccb5c1
commit 1ba701d2cd
27 changed files with 693 additions and 49 deletions

View File

@ -15,31 +15,11 @@ plugins {
group = "dev.isxander"
version = "1.0.0+1.19.3"
/* UNCOMMENT OR DELETE IF YOU WANT TESTMOD SOURCESET
val testmod by sourceSets.registering {
compileClasspath += sourceSets.main.get().compileClasspath
runtimeClasspath += sourceSets.main.get().runtimeClasspath
}
loom {
runs {
register("testmod") {
client()
ideConfigGenerated(true)
name("Test Mod")
source(testmod.get())
}
}
createRemapConfigurations(testmod.get())
}
*/
repositories {
mavenCentral()
maven("https://maven.terraformersmc.com")
maven("https://maven.isxander.dev/releases")
maven("https://maven.quiltmc.org/repository/release")
maven("https://api.modrinth.com/maven") {
name = "Modrinth"
content {
@ -52,13 +32,13 @@ val minecraftVersion = libs.versions.minecraft.get()
dependencies {
minecraft(libs.minecraft)
mappings("net.fabricmc:yarn:$minecraftVersion+build.${libs.versions.yarn.get()}:v2")
mappings(loom.layered {
mappings("org.quiltmc:quilt-mappings:$minecraftVersion+build.${libs.versions.quilt.mappings.get()}:intermediary-v2")
officialMojangMappings()
})
modImplementation(libs.fabric.loader)
// modImplementation(libs.fabric.api)
// modImplementation(fabricApi.module("fabric-resource-loader-v0", libs.versions.fabric.api.get()))
modRuntimeOnly("maven.modrinth:smoothboot-fabric:1.19-1.7.1") // improve system performance when booting dev env
modImplementation(libs.fabric.api)
}
tasks {
@ -180,7 +160,7 @@ publishing {
val password = "XANDER_MAVEN_PASS".let { System.getenv(it) ?: findProperty(it) }?.toString()
if (username != null && password != null) {
maven(url = "https://maven.isxander.dev/releases") {
name = "Xander Releases"
name = "XanderReleases"
credentials {
this.username = username
this.password = password

View File

@ -1,9 +1,9 @@
org.gradle.jvmargs=-Xmx4G
modId=fabric-mod-template
modName=Fabric Mod Template
modDescription=Template for Xander's mods with extra gradle goodies.
modId=controlify
modName=Controlify
modDescription=Another controller mod - for fabric!
curseforgeId=
modrinthId=
githubProject=isXander/FabricModTemplate
githubProject=isXander/Controlify

View File

@ -1,16 +1,16 @@
[versions]
loom = "1.0.+"
loom = "1.1.+"
loom_quiltflower = "1.8.+"
minotaur = "2.5.+"
minotaur = "2.6.+"
cursegradle = "2.+"
github_release = "2.+"
machete = "1.+"
grgit = "5.0.+"
minecraft = "1.19.3"
yarn = "4"
fabric_loader = "0.14.12"
fabric_api = "0.69.1+1.19.3"
minecraft = "23w04a"
quilt_mappings = "10"
fabric_loader = "0.14.13"
fabric_api = "0.73.1+1.19.4"
[libraries]
minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" }

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -13,5 +13,5 @@ dependencyResolutionManagement {
}
}
rootProject.name = "FabricModTemplate"
rootProject.name = "Controlify"

View File

@ -0,0 +1,74 @@
package dev.isxander.controlify;
import dev.isxander.controlify.compatibility.screen.ScreenProcessorProvider;
import dev.isxander.controlify.controller.AxesState;
import dev.isxander.controlify.controller.ButtonState;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.controller.ControllerState;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.minecraft.client.Minecraft;
import org.lwjgl.glfw.GLFW;
public class Controlify {
private static Controlify instance = null;
private Controller currentController;
private InputMode currentInputMode;
public void onInitializeInput() {
// find already connected controllers
for (int i = 0; i < GLFW.GLFW_JOYSTICK_LAST; i++) {
if (GLFW.glfwJoystickPresent(i)) {
currentController = Controller.byId(i);
System.out.println("Connected: " + currentController.name());
this.setCurrentInputMode(InputMode.CONTROLLER);
}
}
// listen for new controllers
GLFW.glfwSetJoystickCallback((jid, event) -> {
System.out.println("Event: " + event);
if (event == GLFW.GLFW_CONNECTED) {
currentController = Controller.byId(jid);
System.out.println("Connected: " + currentController.name());
this.setCurrentInputMode(InputMode.CONTROLLER);
} else if (event == GLFW.GLFW_DISCONNECTED) {
Controller.CONTROLLERS.remove(jid);
currentController = Controller.CONTROLLERS.values().stream().filter(Controller::connected).findFirst().orElse(null);
System.out.println("Disconnected: " + jid);
this.setCurrentInputMode(currentController == null ? InputMode.KEYBOARD_MOUSE : InputMode.CONTROLLER);
}
});
ClientTickEvents.START_CLIENT_TICK.register(client -> {
updateControllers();
});
}
public void updateControllers() {
for (Controller controller : Controller.CONTROLLERS.values()) {
controller.updateState();
}
ControllerState state = currentController == null ? ControllerState.EMPTY : currentController.state();
if (state.hasAnyInput())
this.setCurrentInputMode(InputMode.CONTROLLER);
Minecraft client = Minecraft.getInstance();
if (client.screen != null && currentController != null) ScreenProcessorProvider.provide(client.screen).onControllerUpdate(currentController);
}
public InputMode getCurrentInputMode() {
return currentInputMode;
}
public void setCurrentInputMode(InputMode currentInputMode) {
this.currentInputMode = currentInputMode;
}
public static Controlify getInstance() {
if (instance == null) instance = new Controlify();
return instance;
}
}

View File

@ -0,0 +1,6 @@
package dev.isxander.controlify;
public enum InputMode {
KEYBOARD_MOUSE,
CONTROLLER;
}

View File

@ -0,0 +1,100 @@
package dev.isxander.controlify.compatibility.screen;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.InputMode;
import dev.isxander.controlify.compatibility.screen.component.ComponentProcessorProvider;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.controller.ControllerState;
import dev.isxander.controlify.mixins.ScreenAccessor;
import net.minecraft.client.gui.ComponentPath;
import net.minecraft.client.gui.navigation.FocusNavigationEvent;
import net.minecraft.client.gui.navigation.ScreenDirection;
import net.minecraft.client.gui.screens.Screen;
import org.lwjgl.glfw.GLFW;
public class ScreenProcessor {
private static final int REPEAT_DELAY = 5;
private static final int INITIAL_REPEAT_DELAY = 20;
public final Screen screen;
private int lastMoved = 0;
public ScreenProcessor(Screen screen) {
this.screen = screen;
}
public void onControllerUpdate(Controller controller) {
handleComponentNavigation(controller);
handleButtons(controller);
}
private void handleComponentNavigation(Controller controller) {
if (screen.getFocused() != null) {
var focused = screen.getFocused();
var processor = ComponentProcessorProvider.provide(focused);
if (processor.overrideControllerNavigation(this, controller)) return;
}
var accessor = (ScreenAccessor) screen;
boolean repeatEventAvailable = ++lastMoved > INITIAL_REPEAT_DELAY;
var axes = controller.state().axes();
var prevAxes = controller.prevState().axes();
var buttons = controller.state().buttons();
var prevButtons = controller.prevState().buttons();
FocusNavigationEvent.ArrowNavigation event = null;
if (axes.leftStickX() > 0.5f && (repeatEventAvailable || prevAxes.leftStickX() <= 0.5f)) {
event = accessor.invokeCreateArrowEvent(ScreenDirection.RIGHT);
} else if (axes.leftStickX() < -0.5f && (repeatEventAvailable || prevAxes.leftStickX() >= -0.5f)) {
event = accessor.invokeCreateArrowEvent(ScreenDirection.LEFT);
} else if (axes.leftStickY() < -0.5f && (repeatEventAvailable || prevAxes.leftStickY() >= -0.5f)) {
event = accessor.invokeCreateArrowEvent(ScreenDirection.UP);
} else if (axes.leftStickY() > 0.5f && (repeatEventAvailable || prevAxes.leftStickY() <= 0.5f)) {
event = accessor.invokeCreateArrowEvent(ScreenDirection.DOWN);
} else if (buttons.dpadUp() && (repeatEventAvailable || !prevButtons.dpadUp())) {
event = accessor.invokeCreateArrowEvent(ScreenDirection.UP);
} else if (buttons.dpadDown() && (repeatEventAvailable || !prevButtons.dpadDown())) {
event = accessor.invokeCreateArrowEvent(ScreenDirection.DOWN);
} else if (buttons.dpadLeft() && (repeatEventAvailable || !prevButtons.dpadLeft())) {
event = accessor.invokeCreateArrowEvent(ScreenDirection.LEFT);
} else if (buttons.dpadRight() && (repeatEventAvailable || !prevButtons.dpadRight())) {
event = accessor.invokeCreateArrowEvent(ScreenDirection.RIGHT);
}
if (event != null) {
ComponentPath path = screen.nextFocusPath(event);
if (path != null) {
accessor.invokeChangeFocus(path);
lastMoved = repeatEventAvailable ? INITIAL_REPEAT_DELAY - REPEAT_DELAY : 0;
}
}
}
private void handleButtons(Controller controller) {
if (screen.getFocused() != null) {
var focused = screen.getFocused();
var processor = ComponentProcessorProvider.provide(focused);
if (processor.overrideControllerButtons(this, controller)) return;
}
var buttons = controller.state().buttons();
var prevButtons = controller.prevState().buttons();
if (buttons.a() && !prevButtons.a())
screen.keyPressed(GLFW.GLFW_KEY_ENTER, 0, 0);
if (buttons.b() && !prevButtons.b())
screen.onClose();
}
public void onWidgetRebuild() {
// initial focus
if (screen.getFocused() == null && Controlify.getInstance().getCurrentInputMode() == InputMode.CONTROLLER) {
var accessor = (ScreenAccessor) screen;
ComponentPath path = screen.nextFocusPath(accessor.invokeCreateArrowEvent(ScreenDirection.DOWN));
if (path != null)
accessor.invokeChangeFocus(path);
}
}
}

View File

@ -0,0 +1,11 @@
package dev.isxander.controlify.compatibility.screen;
import net.minecraft.client.gui.screens.Screen;
public interface ScreenProcessorProvider {
ScreenProcessor screenProcessor();
static ScreenProcessor provide(Screen screen) {
return ((ScreenProcessorProvider) screen).screenProcessor();
}
}

View File

@ -0,0 +1,26 @@
package dev.isxander.controlify.compatibility.screen.component;
import dev.isxander.controlify.compatibility.screen.ScreenProcessor;
import dev.isxander.controlify.controller.AxesState;
import dev.isxander.controlify.controller.ButtonState;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.controller.ControllerState;
import net.minecraft.client.gui.components.events.GuiEventListener;
public class ComponentProcessor<T extends GuiEventListener> {
static final ComponentProcessor<?> EMPTY = new ComponentProcessor<>(null);
protected final T component;
public ComponentProcessor(T component) {
this.component = component;
}
public boolean overrideControllerNavigation(ScreenProcessor screen, Controller controller) {
return false;
}
public boolean overrideControllerButtons(ScreenProcessor screen, Controller controller) {
return false;
}
}

View File

@ -0,0 +1,13 @@
package dev.isxander.controlify.compatibility.screen.component;
import net.minecraft.client.gui.components.events.GuiEventListener;
public interface ComponentProcessorProvider {
ComponentProcessor<?> componentProcessor();
static ComponentProcessor<?> provide(GuiEventListener component) {
if (component instanceof ComponentProcessorProvider provider)
return provider.componentProcessor();
return ComponentProcessor.EMPTY;
}
}

View File

@ -0,0 +1,65 @@
package dev.isxander.controlify.compatibility.screen.component;
import dev.isxander.controlify.compatibility.screen.ScreenProcessor;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.controller.ControllerState;
import net.minecraft.client.gui.components.AbstractSliderButton;
import org.lwjgl.glfw.GLFW;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class SliderComponentProcessor extends ComponentProcessor<AbstractSliderButton> {
private final Supplier<Boolean> canChangeValueGetter;
private final Consumer<Boolean> canChangeValueSetter;
private static final int SLIDER_CHANGE_DELAY = 1;
private int lastSliderChange = SLIDER_CHANGE_DELAY;
public SliderComponentProcessor(AbstractSliderButton component, Supplier<Boolean> canChangeValueGetter, Consumer<Boolean> canChangeValueSetter) {
super(component);
this.canChangeValueGetter = canChangeValueGetter;
this.canChangeValueSetter = canChangeValueSetter;
}
@Override
public boolean overrideControllerNavigation(ScreenProcessor screen, Controller controller) {
if (!this.canChangeValueGetter.get()) return false;
var canSliderChange = ++lastSliderChange > SLIDER_CHANGE_DELAY;
var axes = controller.state().axes();
var buttons = controller.state().buttons();
if (axes.leftStickX() > 0.5f || buttons.dpadRight()) {
if (canSliderChange) {
component.keyPressed(GLFW.GLFW_KEY_RIGHT, 0, 0);
lastSliderChange = 0;
}
return true;
} else if (axes.leftStickX() < -0.5f || buttons.dpadLeft()) {
if (canSliderChange) {
component.keyPressed(GLFW.GLFW_KEY_LEFT, 0, 0);
lastSliderChange = 0;
}
return true;
}
return false;
}
@Override
public boolean overrideControllerButtons(ScreenProcessor screen, Controller controller) {
if (!this.canChangeValueGetter.get()) return false;
var buttons = controller.state().buttons();
var prevButtons = controller.prevState().buttons();
if (buttons.b() && !prevButtons.b()) {
this.canChangeValueSetter.accept(false);
return true;
}
return false;
}
}

View File

@ -0,0 +1,61 @@
package dev.isxander.controlify.controller;
import org.lwjgl.glfw.GLFW;
public record AxesState(
float leftStickX, float leftStickY,
float rightStickX, float rightStickY,
float leftTrigger, float rightTrigger
) {
public static AxesState EMPTY = new AxesState(0, 0, 0, 0, 0, 0);
public AxesState leftJoystickDeadZone(float deadZoneX, float deadZoneY) {
return new AxesState(
Math.abs(leftStickX) < deadZoneX ? 0 : leftStickX,
Math.abs(leftStickY) < deadZoneY ? 0 : leftStickY,
rightStickX, rightStickY, leftTrigger, rightTrigger
);
}
public AxesState rightJoystickDeadZone(float deadZoneX, float deadZoneY) {
return new AxesState(
leftStickX, leftStickY,
Math.abs(rightStickX) < deadZoneX ? 0 : rightStickX,
Math.abs(rightStickY) < deadZoneY ? 0 : rightStickY,
leftTrigger, rightTrigger
);
}
public AxesState leftTriggerDeadZone(float deadZone) {
return new AxesState(
leftStickX, leftStickY, rightStickX, rightStickY,
Math.abs(leftTrigger) < deadZone ? 0 : leftTrigger,
rightTrigger
);
}
public AxesState rightTriggerDeadZone(float deadZone) {
return new AxesState(
leftStickX, leftStickY, rightStickX, rightStickY,
leftTrigger,
Math.abs(rightTrigger) < deadZone ? 0 : rightTrigger
);
}
public static AxesState fromController(Controller controller) {
if (controller == null || !controller.connected())
return EMPTY;
var state = controller.getGamepadState();
var axes = state.axes();
float leftX = axes.get(GLFW.GLFW_GAMEPAD_AXIS_LEFT_X);
float leftY = axes.get(GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y);
float rightX = axes.get(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X);
float rightY = axes.get(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y);
float leftTrigger = (axes.get(GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER) + 1f) / 2f;
float rightTrigger = (axes.get(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER) + 1f) / 2f;
return new AxesState(leftX, leftY, rightX, rightY, leftTrigger, rightTrigger);
}
}

View File

@ -0,0 +1,44 @@
package dev.isxander.controlify.controller;
import org.lwjgl.glfw.GLFW;
public record ButtonState(
boolean a, boolean b, boolean x, boolean y,
boolean leftBumper, boolean rightBumper,
boolean back, boolean start,
boolean dpadUp, boolean dpadDown, boolean dpadLeft, boolean dpadRight,
boolean leftStick, boolean rightStick
) {
public static ButtonState EMPTY = new ButtonState(
false, false, false, false,
false, false,
false, false,
false, false, false, false,
false, false
);
public static ButtonState fromController(Controller controller) {
if (controller == null || !controller.connected())
return EMPTY;
var state = controller.getGamepadState();
var buttons = state.buttons();
boolean a = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_A) == GLFW.GLFW_PRESS;
boolean b = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_B) == GLFW.GLFW_PRESS;
boolean x = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_X) == GLFW.GLFW_PRESS;
boolean y = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_Y) == GLFW.GLFW_PRESS;
boolean leftBumper = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER) == GLFW.GLFW_PRESS;
boolean rightBumper = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER) == GLFW.GLFW_PRESS;
boolean back = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_BACK) == GLFW.GLFW_PRESS;
boolean start = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_START) == GLFW.GLFW_PRESS;
boolean dpadUp = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP) == GLFW.GLFW_PRESS;
boolean dpadDown = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN) == GLFW.GLFW_PRESS;
boolean dpadLeft = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT) == GLFW.GLFW_PRESS;
boolean dpadRight = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT) == GLFW.GLFW_PRESS;
boolean leftStick = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB) == GLFW.GLFW_PRESS;
boolean rightStick = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB) == GLFW.GLFW_PRESS;
return new ButtonState(a, b, x, y, leftBumper, rightBumper, back, start, dpadUp, dpadDown, dpadLeft, dpadRight, leftStick, rightStick);
}
}

View File

@ -0,0 +1,119 @@
package dev.isxander.controlify.controller;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWGamepadState;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public final class Controller {
public static final Map<Integer, Controller> CONTROLLERS = new HashMap<>();
private final int id;
private final String guid;
private final String name;
private final boolean gamepad;
private ControllerState state = ControllerState.EMPTY;
private ControllerState prevState = ControllerState.EMPTY;
public Controller(int id, String guid, String name, boolean gamepad) {
this.id = id;
this.guid = guid;
this.name = name;
this.gamepad = gamepad;
}
public ControllerState state() {
return state;
}
public ControllerState prevState() {
return prevState;
}
public void updateState() {
if (!connected()) {
state = prevState = ControllerState.EMPTY;
return;
}
prevState = state;
AxesState axesState = AxesState.fromController(this)
.leftJoystickDeadZone(0.2f, 0.2f)
.rightJoystickDeadZone(0.2f, 0.2f)
.leftTriggerDeadZone(0.1f)
.rightTriggerDeadZone(0.1f);
ButtonState buttonState = ButtonState.fromController(this);
state = new ControllerState(axesState, buttonState);
}
public boolean connected() {
return GLFW.glfwJoystickPresent(id);
}
GLFWGamepadState getGamepadState() {
GLFWGamepadState state = GLFWGamepadState.create();
if (gamepad)
GLFW.glfwGetGamepadState(id, state);
return state;
}
public int id() {
return id;
}
public String guid() {
return guid;
}
public String name() {
return name;
}
public boolean gamepad() {
return gamepad;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (Controller) obj;
return this.id == that.id &&
Objects.equals(this.guid, that.guid) &&
Objects.equals(this.name, that.name) &&
this.gamepad == that.gamepad;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public String toString() {
return "Controller[" +
"id=" + id + ", " +
"name=" + name + ']';
}
public static Controller byId(int id) {
if (id > GLFW.GLFW_JOYSTICK_LAST)
throw new IllegalArgumentException("Invalid joystick id: " + id);
if (CONTROLLERS.containsKey(id))
return CONTROLLERS.get(id);
String guid = GLFW.glfwGetJoystickGUID(id);
boolean gamepad = GLFW.glfwJoystickIsGamepad(id);
String name = gamepad ? GLFW.glfwGetGamepadName(id) : GLFW.glfwGetJoystickName(id);
if (name == null) name = Integer.toString(id);
Controller controller = new Controller(id, guid, name, gamepad);
CONTROLLERS.put(id, controller);
return controller;
}
}

View File

@ -0,0 +1,9 @@
package dev.isxander.controlify.controller;
public record ControllerState(AxesState axes, ButtonState buttons) {
public static final ControllerState EMPTY = new ControllerState(AxesState.EMPTY, ButtonState.EMPTY);
public boolean hasAnyInput() {
return !this.equals(EMPTY);
}
}

View File

@ -0,0 +1,26 @@
package dev.isxander.controlify.mixins;
import dev.isxander.controlify.compatibility.screen.component.ComponentProcessor;
import dev.isxander.controlify.compatibility.screen.component.ComponentProcessorProvider;
import dev.isxander.controlify.compatibility.screen.component.SliderComponentProcessor;
import net.minecraft.client.gui.components.AbstractSliderButton;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
@Mixin(AbstractSliderButton.class)
public class AbstractSliderButtonMixin implements ComponentProcessorProvider {
@Shadow private boolean canChangeValue;
@Unique
private final SliderComponentProcessor controlify$processor = new SliderComponentProcessor(
(AbstractSliderButton) (Object) this,
() -> this.canChangeValue,
val -> this.canChangeValue = val
);
@Override
public ComponentProcessor<AbstractSliderButton> componentProcessor() {
return controlify$processor;
}
}

View File

@ -0,0 +1,17 @@
package dev.isxander.controlify.mixins;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.InputMode;
import net.minecraft.client.KeyboardHandler;
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.CallbackInfo;
@Mixin(KeyboardHandler.class)
public class KeyboardHandlerMixin {
@Inject(method = "keyPress", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;setLastInputType(Lnet/minecraft/client/InputType;)V"))
private void onKeyboardInput(long window, int key, int scancode, int action, int modifiers, CallbackInfo ci) {
Controlify.getInstance().setCurrentInputMode(InputMode.KEYBOARD_MOUSE);
}
}

View File

@ -0,0 +1,16 @@
package dev.isxander.controlify.mixins;
import dev.isxander.controlify.Controlify;
import net.minecraft.client.Minecraft;
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.CallbackInfo;
@Mixin(Minecraft.class)
public class MinecraftMixin {
@Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/KeyboardHandler;setup(J)V", shift = At.Shift.AFTER))
private void onInputInitialized(CallbackInfo ci) {
Controlify.getInstance().onInitializeInput();
}
}

View File

@ -0,0 +1,27 @@
package dev.isxander.controlify.mixins;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.InputMode;
import net.minecraft.client.MouseHandler;
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.CallbackInfo;
@Mixin(MouseHandler.class)
public class MouseHandlerMixin {
@Inject(method = "onPress", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;setLastInputType(Lnet/minecraft/client/InputType;)V"))
private void onMouseClickInput(long window, int button, int action, int modifiers, CallbackInfo ci) {
Controlify.getInstance().setCurrentInputMode(InputMode.KEYBOARD_MOUSE);
}
@Inject(method = "onMove", at = @At("RETURN"))
private void onMouseMoveInput(long window, double x, double y, CallbackInfo ci) {
Controlify.getInstance().setCurrentInputMode(InputMode.KEYBOARD_MOUSE);
}
@Inject(method = "onScroll", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;getOverlay()Lnet/minecraft/client/gui/screens/Overlay;"))
private void onMouseScrollInput(long window, double scrollDeltaX, double scrollDeltaY, CallbackInfo ci) {
Controlify.getInstance().setCurrentInputMode(InputMode.KEYBOARD_MOUSE);
}
}

View File

@ -0,0 +1,17 @@
package dev.isxander.controlify.mixins;
import net.minecraft.client.gui.ComponentPath;
import net.minecraft.client.gui.navigation.FocusNavigationEvent;
import net.minecraft.client.gui.navigation.ScreenDirection;
import net.minecraft.client.gui.screens.Screen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(Screen.class)
public interface ScreenAccessor {
@Invoker
FocusNavigationEvent.ArrowNavigation invokeCreateArrowEvent(ScreenDirection direction);
@Invoker
void invokeChangeFocus(ComponentPath path);
}

View File

@ -0,0 +1,26 @@
package dev.isxander.controlify.mixins;
import dev.isxander.controlify.compatibility.screen.ScreenProcessorProvider;
import dev.isxander.controlify.compatibility.screen.ScreenProcessor;
import net.minecraft.client.gui.screens.Screen;
import org.spongepowered.asm.mixin.Mixin;
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(Screen.class)
public class ScreenMixin implements ScreenProcessorProvider {
@Unique
private final ScreenProcessor controlify$processor = new ScreenProcessor((Screen) (Object) this);
@Override
public ScreenProcessor screenProcessor() {
return controlify$processor;
}
@Inject(method = "rebuildWidgets", at = @At("RETURN"))
private void onScreenInit(CallbackInfo ci) {
screenProcessor().onWidgetRebuild();
}
}

View File

@ -0,0 +1,16 @@
{
"package": "dev.isxander.controlify.mixins",
"required": true,
"minVersion": "0.8",
"compatibilityLevel": "JAVA_17",
"mixins": [
],
"client": [
"AbstractSliderButtonMixin",
"KeyboardHandlerMixin",
"MinecraftMixin",
"MouseHandlerMixin",
"ScreenAccessor",
"ScreenMixin"
]
}

View File

@ -1,9 +0,0 @@
{
"package": "dev.isxander.fabricmodtemplate.mixins",
"required": true,
"minVersion": "0.8",
"compatibilityLevel": "JAVA_17",
"mixins": [
]
}

View File

@ -18,7 +18,7 @@
},
"mixins": [
"fabric-mod-template.mixins.json"
"controlify.mixins.json"
],
"depends": {
"fabricloader": ">=0.14.0",