diff --git a/src/main/java/me/lambdaurora/lambdacontrols/ControlsMode.java b/src/main/java/me/lambdaurora/lambdacontrols/ControlsMode.java index fc8686d..6c96bf3 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/ControlsMode.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/ControlsMode.java @@ -13,12 +13,17 @@ import net.minecraft.client.resource.language.I18n; import org.aperlambda.lambdacommon.utils.Nameable; import org.jetbrains.annotations.NotNull; +import java.util.Arrays; +import java.util.Optional; + /** * Represents the controls mode. */ public enum ControlsMode implements Nameable { - ; + DEFAULT, + CONTROLLER, + TOUCHSCREEN; /** * Returns the next controls mode available. @@ -48,4 +53,15 @@ public enum ControlsMode implements Nameable { return this.name().toLowerCase(); } + + /** + * Gets the controls mode from its identifier. + * + * @param id The identifier of the controls mode. + * @return The controls mode if found, else empty. + */ + public static Optional by_id(@NotNull String id) + { + return Arrays.stream(values()).filter(mode -> mode.get_name().equalsIgnoreCase(id)).findFirst(); + } } diff --git a/src/main/java/me/lambdaurora/lambdacontrols/LambdaControls.java b/src/main/java/me/lambdaurora/lambdacontrols/LambdaControls.java index efd75ff..84b9b1a 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/LambdaControls.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/LambdaControls.java @@ -11,14 +11,17 @@ package me.lambdaurora.lambdacontrols; import me.lambdaurora.lambdacontrols.util.LambdaKeyBinding; import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.fabric.api.event.client.ClientTickCallback; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.options.KeyBinding; +import net.minecraft.client.options.GameOptions; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; import org.lwjgl.glfw.GLFW; import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.HashMap; +import java.util.Map; import static org.lwjgl.glfw.GLFW.GLFW_JOYSTICK_1; @@ -28,10 +31,11 @@ import static org.lwjgl.glfw.GLFW.GLFW_JOYSTICK_1; */ public class LambdaControls implements ClientModInitializer { - private static LambdaControls INSTANCE; - public final Logger logger = LogManager.getLogger("LambdaControls"); - public final LambdaControlsConfig config = new LambdaControlsConfig(this); - private int cid = GLFW_JOYSTICK_1; + private static LambdaControls INSTANCE; + public final Logger logger = LogManager.getLogger("LambdaControls"); + public final LambdaControlsConfig config = new LambdaControlsConfig(this); + private final Map BUTTON_STATES = new HashMap<>(); + private int cid = GLFW_JOYSTICK_1; @Override public void onInitializeClient() @@ -44,8 +48,9 @@ public class LambdaControls implements ClientModInitializer /** * This method is called when Minecraft is initializing. */ - public void on_mc_init() + public void on_mc_init(@NotNull MinecraftClient client) { + this.config.init_keybindings(client.options); GLFW.glfwSetJoystickCallback((jid, event) -> { if (event == GLFW.GLFW_CONNECTED) { this.log("CONNECTED " + jid); @@ -61,13 +66,79 @@ public class LambdaControls implements ClientModInitializer */ public void on_tick(MinecraftClient client) { - ByteBuffer buffer = GLFW.glfwGetJoystickButtons(GLFW.GLFW_JOYSTICK_3); - if (buffer == null) - return; - //this.log(String.valueOf(buffer.get())); - if (buffer.get() == (byte) 1) { - this.log("uwu"); - ((LambdaKeyBinding) client.options.keyJump).lambdacontrols_press(); + GameOptions options = client.options; + ByteBuffer btn_buffer = GLFW.glfwGetJoystickButtons(GLFW.GLFW_JOYSTICK_3); + if (btn_buffer != null) { + for (int i = 0; i < btn_buffer.limit(); i++) { + boolean btn_state = btn_buffer.get() == (byte) 1; + + int current_state = BUTTON_STATES.getOrDefault(i, 0); + if (current_state == 0 && btn_state) { + BUTTON_STATES.put(i, 1); + + int f_i = i; + this.config.get_keybind("button_" + i).ifPresent(key_binding -> { + ((LambdaKeyBinding) key_binding).lambdacontrols_press(); + if (key_binding == options.keyInventory && client.player != null && client.player.container != client.player.playerContainer) { + BUTTON_STATES.put(f_i, 2); + } + }); + if (this.config.is_hotbar_left_button(i)) { + client.player.inventory.selectedSlot = client.player.inventory.selectedSlot == 0 ? 8 : client.player.inventory.selectedSlot - 1; + } else if (this.config.is_hotbar_right_button(i)) { + client.player.inventory.selectedSlot = client.player.inventory.selectedSlot == 8 ? 0 : client.player.inventory.selectedSlot + 1; + } + } else if (current_state != 0 && !btn_state) { + this.config.get_keybind("button_" + i).ifPresent(key_binding -> { + if (key_binding == options.keyInventory && current_state == 2 && client.player != null && client.player.container != client.player.playerContainer) { + client.player.closeContainer(); + } + ((LambdaKeyBinding) key_binding).lambdacontrols_unpress(); + }); + + BUTTON_STATES.put(i, 0); + } + } + } + FloatBuffer axes_buffer = GLFW.glfwGetJoystickAxes(GLFW.GLFW_JOYSTICK_3); + if (axes_buffer != null) { + for (int i = 0; i < axes_buffer.limit(); i++) { + float value = axes_buffer.get(); + { + int state = value > 0.5F ? 1 : (value < -0.5F ? 2 : 0); + this.config.get_keybind("axe_" + i + "+").ifPresent(key_binding -> ((LambdaKeyBinding) key_binding).handle_press_state(state == 1)); + this.config.get_keybind("axe_" + i + "-").ifPresent(key_binding -> ((LambdaKeyBinding) key_binding).handle_press_state(state == 2)); + } + if (this.config.is_look_axis(i) && (value > 0.25F || value < -0.25F)) { + int state = value > 0.25F ? 1 : (value < -0.25F ? 2 : 0); + float multiplier = 50.f; + double x = 0.0D; + double y = 0.0D; + if (this.config.is_view_down_control(i, state)) { + if (this.config.get_view_down_control().endsWith("+")) + y = Math.abs(value * multiplier); + else + y = -Math.abs(value * multiplier); + } else if (this.config.is_view_up_control(i, state)) { + if (this.config.get_view_up_control().endsWith("+")) + y = Math.abs(value * multiplier); + else + y = -Math.abs(value * multiplier); + } + if (this.config.is_view_left_control(i, state)) { + if (this.config.get_view_left_control().endsWith("+")) + x = Math.abs(value * multiplier); + else + x = -Math.abs(value * multiplier); + } else if (this.config.is_view_right_control(i, state)) { + if (this.config.get_view_right_control().endsWith("+")) + x = Math.abs(value * multiplier); + else + x = -Math.abs(value * multiplier); + } + client.player.changeLookDirection(x, y); + } + } } } diff --git a/src/main/java/me/lambdaurora/lambdacontrols/LambdaControlsConfig.java b/src/main/java/me/lambdaurora/lambdacontrols/LambdaControlsConfig.java index 5ddc9c9..8941579 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/LambdaControlsConfig.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/LambdaControlsConfig.java @@ -10,15 +10,24 @@ package me.lambdaurora.lambdacontrols; import com.electronwill.nightconfig.core.file.FileConfig; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.options.GameOptions; +import net.minecraft.client.options.KeyBinding; import org.jetbrains.annotations.NotNull; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + /** * Represents LambdaControls configuration. */ public class LambdaControlsConfig { - private final FileConfig config = FileConfig.builder("config/lambdacontrols.toml").concurrent().defaultResource("/config.toml").build(); - private final LambdaControls mod; + private final FileConfig config = FileConfig.builder("config/lambdacontrols.toml").concurrent().defaultResource("/config.toml").build(); + private final Map keybinding_mappings = new HashMap<>(); + private final LambdaControls mod; + private ControlsMode controls_mode; public LambdaControlsConfig(@NotNull LambdaControls mod) { @@ -27,8 +36,47 @@ public class LambdaControlsConfig public void load() { + this.keybinding_mappings.clear(); this.config.load(); this.mod.log("Configuration loaded."); + this.controls_mode = ControlsMode.by_id(this.config.getOrElse("controls", "default")).orElse(ControlsMode.DEFAULT); + } + + public void init_keybindings(GameOptions options) + { + String str = this.config.getOrElse("controller.attack", "none").toLowerCase(); + if (!str.equals("none")) + this.keybinding_mappings.put(str, options.keyAttack); + str = this.config.getOrElse("controller.back", "none").toLowerCase(); + if (!str.equals("none")) + this.keybinding_mappings.put(str, options.keyBack); + str = this.config.getOrElse("controller.drop", "none").toLowerCase(); + if (!str.equals("none")) + this.keybinding_mappings.put(str, options.keyDrop); + str = this.config.getOrElse("controller.forward", "none").toLowerCase(); + if (!str.equals("none")) + this.keybinding_mappings.put(str, options.keyForward); + str = this.config.getOrElse("controller.inventory", "none").toLowerCase(); + if (!str.equals("none")) + this.keybinding_mappings.put(str, options.keyInventory); + str = this.config.getOrElse("controller.jump", "none").toLowerCase(); + if (!str.equals("none")) + this.keybinding_mappings.put(str, options.keyJump); + str = this.config.getOrElse("controller.left", "none").toLowerCase(); + if (!str.equals("none")) + this.keybinding_mappings.put(str, options.keyLeft); + str = this.config.getOrElse("controller.right", "none").toLowerCase(); + if (!str.equals("none")) + this.keybinding_mappings.put(str, options.keyRight); + str = this.config.getOrElse("controller.sneak", "none").toLowerCase(); + if (!str.equals("none")) + this.keybinding_mappings.put(str, options.keySneak); + str = this.config.getOrElse("controller.sprint", "none").toLowerCase(); + if (!str.equals("none")) + this.keybinding_mappings.put(str, options.keySprint); + str = this.config.getOrElse("controller.use", "none").toLowerCase(); + if (!str.equals("none")) + this.keybinding_mappings.put(str, options.keyUse); } public void save() @@ -36,4 +84,114 @@ public class LambdaControlsConfig this.config.save(); this.mod.log("Configuration saved."); } + + /** + * Returns the controls mode from the configuration. + * + * @return The controls mode. + */ + public @NotNull ControlsMode get_controls_mode() + { + return this.controls_mode; + } + + /** + * Sets the controls mode in the configuration. + * + * @param controls_mode The controls mode. + */ + public void set_controls_mode(@NotNull ControlsMode controls_mode) + { + this.controls_mode = controls_mode; + this.config.set("controls", controls_mode.get_name()); + } + + /** + * Returns the keybindings. + * + * @return The keybindings. + */ + public @NotNull Map get_keybindings() + { + return this.keybinding_mappings; + } + + public Optional get_keybind(@NotNull String id) + { + return Optional.ofNullable(this.keybinding_mappings.get(id)); + } + + public String get_hotbar_left_button() + { + return this.config.getOrElse("controller.hotbar_left", "none").toLowerCase(); + } + + public String get_hotbar_right_button() + { + return this.config.getOrElse("controller.hotbar_right", "none").toLowerCase(); + } + + public boolean is_hotbar_left_button(int button) + { + return this.get_hotbar_left_button().equals("button_" + button); + } + + public boolean is_hotbar_right_button(int button) + { + return this.get_hotbar_right_button().equals("button_" + button); + } + + public String get_view_down_control() + { + return this.config.getOrElse("controller.view_down", "none").toLowerCase(); + } + + public String get_view_left_control() + { + return this.config.getOrElse("controller.view_left", "none").toLowerCase(); + } + + public String get_view_right_control() + { + return this.config.getOrElse("controller.view_right", "none").toLowerCase(); + } + + public String get_view_up_control() + { + return this.config.getOrElse("controller.view_up", "none").toLowerCase(); + } + + public boolean is_view_down_control(int axe, int state) + { + if (state == 0) + return false; + return this.get_view_down_control().contains(axe + (state == 1 ? "+" : "-")); + } + + public boolean is_view_left_control(int axe, int state) + { + if (state == 0) + return false; + return this.get_view_left_control().endsWith(axe + (state == 1 ? "+" : "-")); + } + + public boolean is_view_right_control(int axe, int state) + { + if (state == 0) + return false; + return this.get_view_right_control().contains(axe + (state == 1 ? "+" : "-")); + } + + public boolean is_view_up_control(int axe, int state) + { + if (state == 0) + return false; + return this.get_view_up_control().contains(axe + (state == 1 ? "+" : "-")); + } + + public boolean is_look_axis(int i) + { + return this.get_view_down_control().startsWith("axe_" + i) || this.get_view_left_control().startsWith("axe_" + i) || this.get_view_right_control().startsWith("axe_" + i) + || this.get_view_up_control().startsWith("axe_" + i); + } } diff --git a/src/main/java/me/lambdaurora/lambdacontrols/mixin/KeyBindingMixin.java b/src/main/java/me/lambdaurora/lambdacontrols/mixin/KeyBindingMixin.java index 90fd318..9ba99f9 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/mixin/KeyBindingMixin.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/mixin/KeyBindingMixin.java @@ -19,9 +19,14 @@ import org.spongepowered.asm.mixin.Shadow; @Mixin(KeyBinding.class) public class KeyBindingMixin implements LambdaKeyBinding { - @Shadow private InputUtil.KeyCode keyCode; + @Shadow + private InputUtil.KeyCode keyCode; - @Shadow private int timesPressed; + @Shadow + private int timesPressed; + + @Shadow + private boolean pressed; @Override public @NotNull InputUtil.KeyCode get_key_code() @@ -30,8 +35,22 @@ public class KeyBindingMixin implements LambdaKeyBinding } @Override - public void lambdacontrols_press() + public boolean lambdacontrols_press() { + boolean old_pressed = this.pressed; + if (!this.pressed) + this.pressed = true; ++this.timesPressed; + return old_pressed != this.pressed; + } + + @Override + public boolean lambdacontrols_unpress() + { + if (this.pressed) { + this.pressed = false; + return true; + } + return false; } } diff --git a/src/main/java/me/lambdaurora/lambdacontrols/mixin/MinecraftClientMixin.java b/src/main/java/me/lambdaurora/lambdacontrols/mixin/MinecraftClientMixin.java index 17c8c25..2134920 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/mixin/MinecraftClientMixin.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/mixin/MinecraftClientMixin.java @@ -9,6 +9,7 @@ package me.lambdaurora.lambdacontrols.mixin; +import me.lambdaurora.lambdacontrols.ControlsMode; import me.lambdaurora.lambdacontrols.LambdaControls; import net.minecraft.client.MinecraftClient; import org.spongepowered.asm.mixin.Mixin; @@ -17,17 +18,18 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(MinecraftClient.class) -public class MinecraftClientMixin +public abstract class MinecraftClientMixin { @Inject(method = "init", at = @At("RETURN")) private void on_init(CallbackInfo ci) { - LambdaControls.get().on_mc_init(); + LambdaControls.get().on_mc_init((MinecraftClient) (Object) this); } - @Inject(method = "render", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/GLX;pollEvents()V")) - private void on_poll_events(boolean fullRender, CallbackInfo ci) + @Inject(method = "handleInputEvents", at = @At("HEAD")) + private void on_handle_input_events(CallbackInfo ci) { - LambdaControls.get().on_tick((MinecraftClient) (Object) this); + if (LambdaControls.get().config.get_controls_mode() == ControlsMode.CONTROLLER) + LambdaControls.get().on_tick((MinecraftClient) (Object) this); } } diff --git a/src/main/java/me/lambdaurora/lambdacontrols/util/LambdaKeyBinding.java b/src/main/java/me/lambdaurora/lambdacontrols/util/LambdaKeyBinding.java index dea0cee..7fecb1d 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/util/LambdaKeyBinding.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/util/LambdaKeyBinding.java @@ -19,5 +19,15 @@ public interface LambdaKeyBinding { @NotNull InputUtil.KeyCode get_key_code(); - void lambdacontrols_press(); + boolean lambdacontrols_press(); + + boolean lambdacontrols_unpress(); + + default boolean handle_press_state(boolean pressed) + { + if (pressed) + return this.lambdacontrols_press(); + else + return this.lambdacontrols_unpress(); + } } diff --git a/src/main/resources/config.toml b/src/main/resources/config.toml index d1059b2..5221aee 100644 --- a/src/main/resources/config.toml +++ b/src/main/resources/config.toml @@ -7,6 +7,27 @@ controls = "default" # Dertermines which side is used depending of the main hand. side = "right_handed" +# Controller settings +[controller] +attack = "button_7" +back = "axe_1+" +drop = "button_2" +forward = "axe_1-" +hotbar_left = "button_4" +hotbar_right = "button_5" +inventory = "button_3" +jump = "button_0" +left = "axe_0-" +right = "axe_0+" +sneak = "button_12" +sprint = "button_11" +start = "button_4" +use = "button_6" +view_down = "axe_3+" +view_left = "axe_2-" +view_right = "axe_2+" +view_up = "axe_3-" + # Colors [colors] normal = "#ffffffff"