From e66a935b8eada3e6a36843d259a4ca44c35d20cb Mon Sep 17 00:00:00 2001 From: isxander Date: Fri, 9 Oct 2026 23:58:48 +0100 Subject: [PATCH 01/44] start on the new input pipeline --- .../isxander/controlify/input/SensorType.java | 23 ++++ .../controlify/input/action/Accumulator.java | 11 ++ .../controlify/input/action/Action.java | 24 ++++ .../controlify/input/action/ActionSpec.java | 15 ++ .../controlify/input/action/Activator.java | 18 +++ .../controlify/input/action/Channel.java | 37 +++++ .../controlify/input/action/ChannelKind.java | 7 + .../action/activator/ActivatorFactory.java | 8 ++ .../activator/ActivatorSerialization.java | 20 +++ .../activator/RepeatLatchPulseActivator.java | 96 +++++++++++++ .../action/activator/TapPulseActivator.java | 34 +++++ .../activator/ToggleLatchActivator.java | 59 ++++++++ .../input/logical/LogicalInput.java | 26 ++++ .../input/logical/LogicalSignal.java | 42 ++++++ .../input/logical/PhysicalToLogicalStage.java | 30 ++++ .../input/logical/SensorAccumulatorStage.java | 130 ++++++++++++++++++ .../input/physical/DeadzoneStage.java | 47 +++++++ .../physical/InputDomainExpansionStage.java | 47 +++++++ .../input/physical/PhysicalInputSignal.java | 20 +++ .../input/physical/PhysicalSensorSignal.java | 11 ++ .../input/physical/PhysicalSignal.java | 8 ++ .../controlify/utils/event/EventSink.java | 6 + .../controlify/utils/event/EventStage.java | 14 ++ .../utils/event/UnaryEventStage.java | 5 + 24 files changed, 738 insertions(+) create mode 100644 src/main/java/dev/isxander/controlify/input/SensorType.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/Accumulator.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/Action.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/ActionSpec.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/Activator.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/Channel.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/ChannelKind.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/ActivatorFactory.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/ActivatorSerialization.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/RepeatLatchPulseActivator.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/TapPulseActivator.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/ToggleLatchActivator.java create mode 100644 src/main/java/dev/isxander/controlify/input/logical/LogicalInput.java create mode 100644 src/main/java/dev/isxander/controlify/input/logical/LogicalSignal.java create mode 100644 src/main/java/dev/isxander/controlify/input/logical/PhysicalToLogicalStage.java create mode 100644 src/main/java/dev/isxander/controlify/input/logical/SensorAccumulatorStage.java create mode 100644 src/main/java/dev/isxander/controlify/input/physical/DeadzoneStage.java create mode 100644 src/main/java/dev/isxander/controlify/input/physical/InputDomainExpansionStage.java create mode 100644 src/main/java/dev/isxander/controlify/input/physical/PhysicalInputSignal.java create mode 100644 src/main/java/dev/isxander/controlify/input/physical/PhysicalSensorSignal.java create mode 100644 src/main/java/dev/isxander/controlify/input/physical/PhysicalSignal.java create mode 100644 src/main/java/dev/isxander/controlify/utils/event/EventSink.java create mode 100644 src/main/java/dev/isxander/controlify/utils/event/EventStage.java create mode 100644 src/main/java/dev/isxander/controlify/utils/event/UnaryEventStage.java diff --git a/src/main/java/dev/isxander/controlify/input/SensorType.java b/src/main/java/dev/isxander/controlify/input/SensorType.java new file mode 100644 index 000000000..220cba2cc --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/SensorType.java @@ -0,0 +1,23 @@ +package dev.isxander.controlify.input; + +public enum SensorType { + /// Sensor components are `xyz` and in radians. + GYROSCOPE, + /// Sensor components are `xyz` and in radians. + GYROSCOPE_L, + /// Sensor components are `xyz` and in radians. + GYROSCOPE_R, + /// Sensor components are `xyz` and in m/s^2. + ACCELEROMETER, + /// Sensor components are `xyz` and in m/s^2. + ACCELEROMETER_L, + /// Sensor components are `xyz` and in m/s^2. + ACCELEROMETER_R; + + public boolean isGyroscope() { + return this == GYROSCOPE || this == GYROSCOPE_L || this == GYROSCOPE_R; + } + public boolean isAccelerometer() { + return this == ACCELEROMETER || this == ACCELEROMETER_L || this == ACCELEROMETER_R; + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/Accumulator.java b/src/main/java/dev/isxander/controlify/input/action/Accumulator.java new file mode 100644 index 000000000..00470df98 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/Accumulator.java @@ -0,0 +1,11 @@ +package dev.isxander.controlify.input.action; + +/// Accepts action state. +public interface Accumulator { + default void firePulse() {} + + default void toggleLatch() {} + default void setLatch(boolean active) {} + + default void setContinuous(float value) {} +} diff --git a/src/main/java/dev/isxander/controlify/input/action/Action.java b/src/main/java/dev/isxander/controlify/input/action/Action.java new file mode 100644 index 000000000..833d2a6c0 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/Action.java @@ -0,0 +1,24 @@ +package dev.isxander.controlify.input.action; + +import dev.isxander.controlify.input.logical.LogicalInput; +import net.minecraft.network.chat.Component; +import org.jspecify.annotations.Nullable; + +/// An action is bound to a logical input and has one or more activators +/// that determine how its output channels respond to input signals. +public interface Action { + + ActionSpec spec(); + + Channel.Pulse pulse(); + Channel.Latch latch(); + Channel.Continuous continuous(); + + Component inputGlyph(); + + void setBoundInput(@Nullable LogicalInput input); + + @Nullable LogicalInput boundInput(); + + @Nullable LogicalInput defaultInput(); +} diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionSpec.java b/src/main/java/dev/isxander/controlify/input/action/ActionSpec.java new file mode 100644 index 000000000..bc9a895ff --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/ActionSpec.java @@ -0,0 +1,15 @@ +package dev.isxander.controlify.input.action; + +import net.minecraft.network.chat.Component; +import net.minecraft.resources.Identifier; + +import java.util.Set; + +public record ActionSpec( + Identifier id, + Component name, + Component description, + Component category, + Set contexts +) { +} diff --git a/src/main/java/dev/isxander/controlify/input/action/Activator.java b/src/main/java/dev/isxander/controlify/input/action/Activator.java new file mode 100644 index 000000000..8ccab37aa --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/Activator.java @@ -0,0 +1,18 @@ +package dev.isxander.controlify.input.action; + +import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.logical.LogicalSignal; + +/// Processes signals and activates an {@link Action}. +public interface Activator { + + /// Accepts a {@link LogicalSignal} from layer1 and potentially + /// submits state into the action accumulator. + void onSignal(LogicalSignal signal, Accumulator acc); + + /// The logical input this is bound to. + LogicalInput boundInput(); + + String describe(); + +} diff --git a/src/main/java/dev/isxander/controlify/input/action/Channel.java b/src/main/java/dev/isxander/controlify/input/action/Channel.java new file mode 100644 index 000000000..08f9f69ba --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/Channel.java @@ -0,0 +1,37 @@ +package dev.isxander.controlify.input.action; + +/// A channel is a way of interpreting the output state of a given {@link Action}. +/// An {@link Action action's} bindings sends output to any of these channels. +public sealed interface Channel { + ChannelKind kind(); + + /// A channel which gives a continuous float value, such as an axis/joystick. + non-sealed interface Continuous extends Channel { + float getContinuous(); + + @Override + default ChannelKind kind() { + return ChannelKind.CONTINUOUS; + } + } + + /// A channel which gives single pulses, for example a button press causing a jump. + non-sealed interface Pulse extends Channel { + boolean consumePulse(); + + @Override + default ChannelKind kind() { + return ChannelKind.PULSE; + } + } + + /// A channel which gives a stateful digital output of the action, for example a physical switch's state. + non-sealed interface Latch extends Channel { + boolean isLatchActive(); + + @Override + default ChannelKind kind() { + return ChannelKind.LATCH; + } + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/ChannelKind.java b/src/main/java/dev/isxander/controlify/input/action/ChannelKind.java new file mode 100644 index 000000000..d87739fb6 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/ChannelKind.java @@ -0,0 +1,7 @@ +package dev.isxander.controlify.input.action; + +public enum ChannelKind { + PULSE, + LATCH, + CONTINUOUS +} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorFactory.java b/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorFactory.java new file mode 100644 index 000000000..0c969448c --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorFactory.java @@ -0,0 +1,8 @@ +package dev.isxander.controlify.input.action.activator; + +import dev.isxander.controlify.input.action.Activator; +import dev.isxander.controlify.input.logical.LogicalInput; + +public interface ActivatorFactory { + T create(LogicalInput boundInput); +} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorSerialization.java b/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorSerialization.java new file mode 100644 index 000000000..bf6efd8e4 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorSerialization.java @@ -0,0 +1,20 @@ +package dev.isxander.controlify.input.action.activator; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import dev.isxander.controlify.input.action.Activator; +import net.minecraft.util.StringRepresentable; + +public final class ActivatorSerialization { + + private ActivatorSerialization() { + } + + public record Type>(String id, MapCodec mapCodec) implements StringRepresentable { + public static final Type TAP_PULSE = + new Type<>(TapPulseActivator.TapPulseActivatorConfig.TYPE_ID, TapPulseActivator.TapPulseActivatorConfig.MAP_CODEC); + + } + + public static final Codec> FACTORY_CODEC = null; +} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/RepeatLatchPulseActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/RepeatLatchPulseActivator.java new file mode 100644 index 000000000..bc054b43e --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/activator/RepeatLatchPulseActivator.java @@ -0,0 +1,96 @@ +package dev.isxander.controlify.input.action.activator; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import dev.isxander.controlify.input.action.Accumulator; +import dev.isxander.controlify.input.action.Activator; +import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.logical.LogicalSignal; + +/// Accepts another latch binding, and sends repeat pulses out while the latch is active. +public class RepeatLatchPulseActivator implements Activator { + + private final Activator latchActivator; + private final long initialDelayNs, repeatDelayNs; + private final HoldRepeatAccumulator holdRepeatAccumulator; + + private boolean down; + private long nextDue; + + public RepeatLatchPulseActivator( + Activator latchActivator, + long initialDelayNs, long repeatDelayNs + ) { + this.latchActivator = latchActivator; + this.initialDelayNs = initialDelayNs; + this.repeatDelayNs = repeatDelayNs; + this.holdRepeatAccumulator = new HoldRepeatAccumulator(); + } + + @Override + public void onSignal(LogicalSignal signal, Accumulator acc) { + this.holdRepeatAccumulator.target = acc; + this.holdRepeatAccumulator.time = signal.timeNanos(); + + this.latchActivator.onSignal(signal, this.holdRepeatAccumulator); + + if (signal instanceof LogicalSignal.Tick) { + while (down && signal.timeNanos() >= nextDue) { + acc.firePulse(); + nextDue += repeatDelayNs; + } + } + } + + @Override + public LogicalInput boundInput() { + return this.latchActivator.boundInput(); + } + + @Override + public String describe() { + return "HoldRepeat[" + latchActivator.describe() + ", initialDelay=" + initialDelayNs + "ns, repeatDelay=" + repeatDelayNs + "ns]"; + } + + private class HoldRepeatAccumulator implements Accumulator { + private Accumulator target; + private long time; + + @Override + public void setLatch(boolean active) { + if (active) { + down = true; + nextDue = time + initialDelayNs; + target.firePulse(); + } else { + down = false; + nextDue = Long.MAX_VALUE; + } + } + + @Override + public void toggleLatch() { + this.setLatch(!down); + } + } + + public record RepeatLatchPulseActivatorConfig( + ActivatorFactory latchBinding, + int initialDelayTicks, int repeatDelayTicks + ) implements ActivatorFactory { + public static final String TYPE_ID = "pulse_repeat_latch"; + public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + ActivatorSerialization.FACTORY_CODEC.fieldOf("source_latch").forGetter(RepeatLatchPulseActivatorConfig::latchBinding), + Codec.intRange(0, Integer.MAX_VALUE).optionalFieldOf("initial_delay_ticks", 5).forGetter(RepeatLatchPulseActivatorConfig::initialDelayTicks), + Codec.intRange(1, Integer.MAX_VALUE).optionalFieldOf("repeat_delay_ticks", 1).forGetter(RepeatLatchPulseActivatorConfig::repeatDelayTicks) + ).apply(instance, RepeatLatchPulseActivatorConfig::new)); + + @Override + public RepeatLatchPulseActivator create(LogicalInput boundInput) { + long initialDelayNs = this.initialDelayTicks * 50_000_000L; + long repeatDelayNs = this.repeatDelayTicks * 50_000_000L; + return new RepeatLatchPulseActivator(this.latchBinding.create(boundInput), initialDelayNs, repeatDelayNs); + } + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/TapPulseActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/TapPulseActivator.java new file mode 100644 index 000000000..78f407e5c --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/activator/TapPulseActivator.java @@ -0,0 +1,34 @@ +package dev.isxander.controlify.input.action.activator; + +import com.mojang.serialization.MapCodec; +import dev.isxander.controlify.input.action.Accumulator; +import dev.isxander.controlify.input.action.Activator; +import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.logical.LogicalSignal; + +/// Produces a pulse when the input is tapped. +public record TapPulseActivator(LogicalInput boundInput) implements Activator { + + @Override + public void onSignal(LogicalSignal signal, Accumulator acc) { + if (signal instanceof LogicalSignal.Tapped(_, LogicalInput input) && this.boundInput.equals(input)) { + acc.firePulse(); + } + } + + @Override + public String describe() { + return "Tap[" + boundInput.toString() + "]"; + } + + public record TapPulseActivatorConfig() implements ActivatorFactory { + public static final String TYPE_ID = "pulse_tap"; + public static final TapPulseActivatorConfig INSTANCE = new TapPulseActivatorConfig(); + public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); + + @Override + public TapPulseActivator create(LogicalInput boundInput) { + return new TapPulseActivator(boundInput); + } + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ToggleLatchActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/ToggleLatchActivator.java new file mode 100644 index 000000000..d045081f7 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/activator/ToggleLatchActivator.java @@ -0,0 +1,59 @@ +package dev.isxander.controlify.input.action.activator; + +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import dev.isxander.controlify.input.action.Accumulator; +import dev.isxander.controlify.input.action.Activator; +import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.logical.LogicalSignal; + +/// Accepts another pulse binding, and converts pulses into toggling a latch. +public class ToggleLatchActivator implements Activator { + + private final Activator pulseActivator; + private final PulseToToggleAcc pulseToToggleAcc; + + public ToggleLatchActivator(Activator pulseActivator) { + this.pulseActivator = pulseActivator; + this.pulseToToggleAcc = new PulseToToggleAcc(); + } + + @Override + public void onSignal(LogicalSignal signal, Accumulator acc) { + this.pulseToToggleAcc.target = acc; + this.pulseActivator.onSignal(signal, this.pulseToToggleAcc); + } + + @Override + public LogicalInput boundInput() { + return this.pulseActivator.boundInput(); + } + + @Override + public String describe() { + return "Toggle[" + this.pulseActivator.describe() + "]"; + } + + private static final class PulseToToggleAcc implements Accumulator { + private Accumulator target; + + @Override + public void firePulse() { + target.toggleLatch(); + } + } + + public record ToggleLatchActivatorConfig( + ActivatorFactory pulseBinding + ) implements ActivatorFactory { + public static final String TYPE_ID = "latch_toggle"; + public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + ActivatorSerialization.FACTORY_CODEC.fieldOf("source_pulse").forGetter(ToggleLatchActivatorConfig::pulseBinding) + ).apply(instance, ToggleLatchActivatorConfig::new)); + + @Override + public ToggleLatchActivator create(LogicalInput boundInput) { + return new ToggleLatchActivator(this.pulseBinding.create(boundInput)); + } + } +} diff --git a/src/main/java/dev/isxander/controlify/input/logical/LogicalInput.java b/src/main/java/dev/isxander/controlify/input/logical/LogicalInput.java new file mode 100644 index 000000000..ee6188fbc --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/logical/LogicalInput.java @@ -0,0 +1,26 @@ +package dev.isxander.controlify.input.logical; + +import net.minecraft.resources.Identifier; + +import java.util.Set; + +/// A reference to a logical button, axis, or sensor. +/// +/// A logical input's domain is the type of input, meaning that +/// a logical input that refers to the same physical inputs can be +/// safely duplicated between continuous and digital signals. +/// +/// Most of the time this will refer to a single physical button or axis, +/// like the `A` button or `Left Stick` left, but it may also refer to +/// a chord, like `A` + `B`. +public sealed interface LogicalInput { + /// A logical input with a direct relationship with a physical input. + record Single(Identifier physicalInput) implements LogicalInput { + + } + + /// A logical input that triggers only after all modifiers are already pressed. + record Modified(Set modifiers, Identifier trigger) implements LogicalInput { + + } +} diff --git a/src/main/java/dev/isxander/controlify/input/logical/LogicalSignal.java b/src/main/java/dev/isxander/controlify/input/logical/LogicalSignal.java new file mode 100644 index 000000000..f4041e2cd --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/logical/LogicalSignal.java @@ -0,0 +1,42 @@ +package dev.isxander.controlify.input.logical; + +import dev.isxander.controlify.input.SensorType; + +public sealed interface LogicalSignal { + /// The time this signal occurred. + /// Every signal will have the same epoch. + long timeNanos(); + + /// Submitted once per frame. + /// Allows bindings to accumulate state between ticks. + record Tick(long timeNanos, long deltaTimeNanos) implements LogicalSignal {} + + sealed interface InputSignal extends LogicalSignal { + LogicalInput input(); + } + /// When the button is pressed down + record ButtonDown(long timeNanos, LogicalInput input) implements InputSignal {} + + /// When the button is released + record ButtonUp(long timeNanos, LogicalInput input) implements InputSignal {} + + /// When button is pressed and released in quick succession + record Tapped(long timeNanos, LogicalInput input) implements InputSignal {} + + /// When two {@link Tapped taps} occur in quick succession + record DoubleTapped(long timeNanos, LogicalInput input) implements InputSignal {} + + /// When a button is pressed and released, where no GUI navigation happened in between + record GuiPress(long timeNanos, LogicalInput input) implements InputSignal {} + + /// When a button is held for a long period of time. Signaled whilst still held. + record Held(long timeNanos, LogicalInput input) implements InputSignal {} + + /// When an axis value changes + record AxisMoved(long timeNanos, LogicalInput input, float value, float delta) implements InputSignal {} + + /// Represents sensor data, which can be multi-dimensional. + /// The data is expected to be tick-integrated deltas. They are not individual IMU samples. + /// @see SensorAccumulatorStage + record SensorInterval(long timeNanos, long deltaTimeNanos, SensorType sensorType, float x, float y, float z) implements LogicalSignal {} +} diff --git a/src/main/java/dev/isxander/controlify/input/logical/PhysicalToLogicalStage.java b/src/main/java/dev/isxander/controlify/input/logical/PhysicalToLogicalStage.java new file mode 100644 index 000000000..df4db3c03 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/logical/PhysicalToLogicalStage.java @@ -0,0 +1,30 @@ +package dev.isxander.controlify.input.logical; + +import dev.isxander.controlify.input.physical.PhysicalSensorSignal; +import dev.isxander.controlify.input.physical.PhysicalSignal; +import dev.isxander.controlify.utils.event.EventSink; +import dev.isxander.controlify.utils.event.EventStage; + +/// The event stage that converts physical signals into logical ones. +/// This stage is responsible for recognizing chords, integrating sensor data, and emitting logical signals. +public class PhysicalToLogicalStage implements EventStage { + private final SensorAccumulatorStage sensorAccumulatorStage; + + public PhysicalToLogicalStage(SensorAccumulatorStage sensorAccumulatorStage) { + this.sensorAccumulatorStage = sensorAccumulatorStage; + } + + @Override + public void onEvent(PhysicalSignal event, EventSink downstream) { + switch (event) { + case PhysicalSignal.Tick(long timeNanos, long deltaTimeNanos) -> { + this.sensorAccumulatorStage.onEvent(event, downstream); + + downstream.accept(new LogicalSignal.Tick(timeNanos, deltaTimeNanos)); + } + + case PhysicalSensorSignal sensorSignal -> + this.sensorAccumulatorStage.onEvent(sensorSignal, downstream); + } + } +} diff --git a/src/main/java/dev/isxander/controlify/input/logical/SensorAccumulatorStage.java b/src/main/java/dev/isxander/controlify/input/logical/SensorAccumulatorStage.java new file mode 100644 index 000000000..279eb1d55 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/logical/SensorAccumulatorStage.java @@ -0,0 +1,130 @@ +package dev.isxander.controlify.input.logical; + +import dev.isxander.controlify.input.SensorType; +import dev.isxander.controlify.input.physical.PhysicalSensorSignal; +import dev.isxander.controlify.input.physical.PhysicalSignal; +import dev.isxander.controlify.utils.event.EventSink; +import dev.isxander.controlify.utils.event.EventStage; + +import java.util.EnumMap; +import java.util.Map; + +public class SensorAccumulatorStage implements EventStage { + // prevent stale readings from being continually accumulated + private static final long MAX_SAMPLE_AGE_NANOS = 50_000_000L; + + private final Map accSensorValues = new EnumMap<>(SensorType.class); + + @Override + public void onEvent( + PhysicalSignal event, + EventSink downstream + ) { + switch (event) { + case PhysicalSignal.Tick(long timeNanos, _) -> { + this.accSensorValues.forEach((sensorType, acc) -> { + if (acc.lastNs == -1) { + return; + } + + acc.integrateUntil(timeNanos); + + if (acc.accumulatedNanos > 0) { + float x = acc.x; + float y = acc.y; + float z = acc.z; + + if (sensorType.isAccelerometer()) { + float durationSeconds = acc.accumulatedNanos * 1e-9f; + + x /= durationSeconds; + y /= durationSeconds; + z /= durationSeconds; + } + + downstream.accept(new LogicalSignal.SensorInterval( + timeNanos, + acc.accumulatedNanos, + sensorType, + x, + y, + z + )); + } + + acc.resetAccumulation(); + }); + } + + case PhysicalSensorSignal(long timeNanos, SensorType sensorType, float x, float y, float z) -> { + Acc acc = this.accSensorValues.computeIfAbsent(sensorType, _ -> new Acc()); + + if (acc.lastNs != -1 && timeNanos <= acc.lastNs) { + return; + } + + acc.integrateUntil(timeNanos); + + acc.velocityX = x; + acc.velocityY = y; + acc.velocityZ = z; + acc.lastSampleNanos = timeNanos; + } + + default -> { + } + } + } + + private static final class Acc { + private long lastNs = -1; + private long lastSampleNanos = -1; + + private float velocityX; + private float velocityY; + private float velocityZ; + + private float x; + private float y; + private float z; + + private long accumulatedNanos; + + public void integrateUntil(long timeNanos) { + if (lastNs == -1) { + lastNs = timeNanos; + return; + } + + long deltaNanos = timeNanos - lastNs; + + if (deltaNanos <= 0) { + return; + } + + // Integrate only while the last sensor reading is considered valid. + long validUntilNanos = lastSampleNanos + MAX_SAMPLE_AGE_NANOS; + long integrationEndNanos = Math.min(timeNanos, validUntilNanos); + + if (integrationEndNanos > lastNs) { + long integrationNanos = integrationEndNanos - lastNs; + float deltaSeconds = integrationNanos * 1e-9f; + + x += velocityX * deltaSeconds; + y += velocityY * deltaSeconds; + z += velocityZ * deltaSeconds; + + accumulatedNanos += integrationNanos; + } + + lastNs = timeNanos; + } + + public void resetAccumulation() { + x = 0.0f; + y = 0.0f; + z = 0.0f; + accumulatedNanos = 0; + } + } +} diff --git a/src/main/java/dev/isxander/controlify/input/physical/DeadzoneStage.java b/src/main/java/dev/isxander/controlify/input/physical/DeadzoneStage.java new file mode 100644 index 000000000..26f8156f7 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/physical/DeadzoneStage.java @@ -0,0 +1,47 @@ +package dev.isxander.controlify.input.physical; + +import dev.isxander.controlify.utils.ControllerUtils; +import dev.isxander.controlify.utils.event.EventSink; +import dev.isxander.controlify.utils.event.UnaryEventStage; +import net.minecraft.resources.Identifier; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Function; + +/// Applies deadzones to physical axes. +// TODO: radial deadzones +public class DeadzoneStage implements UnaryEventStage { + private final Function deadzoneSupplier; + + private final Set axisWasZero; + + public DeadzoneStage(Function axisToDeadzoneSupplier) { + this.deadzoneSupplier = axisToDeadzoneSupplier; + this.axisWasZero = new HashSet<>(); + } + + @Override + public void onEvent(PhysicalSignal event, EventSink downstream) { + if (event instanceof PhysicalInputSignal.AxisMoved(long timeNanos, Identifier input, float value)) { + float deadzone = this.deadzoneSupplier.apply(input); + + if (deadzone > 0) { + // TODO: prevent duplicate zeros passing downstream + float newValue = ControllerUtils.deadzone(value, deadzone); + + if (newValue != 0 || this.axisWasZero.add(input)) { + downstream.accept(new PhysicalInputSignal.AxisMoved(timeNanos, input, newValue)); + if (newValue != 0) { + this.axisWasZero.remove(input); + } + } + return; + } else { + this.axisWasZero.remove(input); + } + } + + downstream.accept(event); + } +} diff --git a/src/main/java/dev/isxander/controlify/input/physical/InputDomainExpansionStage.java b/src/main/java/dev/isxander/controlify/input/physical/InputDomainExpansionStage.java new file mode 100644 index 000000000..f300ddb9a --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/physical/InputDomainExpansionStage.java @@ -0,0 +1,47 @@ +package dev.isxander.controlify.input.physical; + +import dev.isxander.controlify.utils.event.EventSink; +import dev.isxander.controlify.utils.event.UnaryEventStage; +import net.minecraft.resources.Identifier; + +import java.util.HashSet; +import java.util.Set; + +/// Expands the domain of physical inputs. +/// - Expands button presses and releases into axis moved events. +/// - Expands axis moved events into button presses and releases when passing threshold. +public class InputDomainExpansionStage implements UnaryEventStage { + + private final Set pressedButtons; + private final float actuation, release, pressedValue, releasedValue; + + public InputDomainExpansionStage(float actuation, float release, float pressedValue, float releasedValue) { + this.pressedButtons = new HashSet<>(); + this.actuation = actuation; + this.release = release; + this.pressedValue = pressedValue; + this.releasedValue = releasedValue; + } + + @Override + public void onEvent(PhysicalInputSignal event, EventSink downstream) { + downstream.accept(event); + + switch (event) { + case PhysicalInputSignal.AxisMoved(long timeNanos, Identifier input, float value) -> { + if (value >= actuation) { + if (!this.pressedButtons.add(input)) { + downstream.accept(new PhysicalInputSignal.ButtonChanged(timeNanos, input, true)); + } + } else if (value <= release) { + if (!this.pressedButtons.remove(input)) { + downstream.accept(new PhysicalInputSignal.ButtonChanged(timeNanos, input, false)); + } + } + } + case PhysicalInputSignal.ButtonChanged(long timeNanos, Identifier input, boolean state) -> { + downstream.accept(new PhysicalInputSignal.AxisMoved(timeNanos, input, state ? pressedValue : releasedValue)); + } + } + } +} diff --git a/src/main/java/dev/isxander/controlify/input/physical/PhysicalInputSignal.java b/src/main/java/dev/isxander/controlify/input/physical/PhysicalInputSignal.java new file mode 100644 index 000000000..aa6b8d064 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/physical/PhysicalInputSignal.java @@ -0,0 +1,20 @@ +package dev.isxander.controlify.input.physical; + +import net.minecraft.resources.Identifier; + +public sealed interface PhysicalInputSignal extends PhysicalSignal { + /// @return the identifier of the physical input + Identifier input(); + + /// Represents a button press/release event. + /// @param timeNanos the timestamp of the event in nanoseconds + /// @param input the identifier of the physical button + /// @param state the state of the button, where true indicates pressed and false indicates released + record ButtonChanged(long timeNanos, Identifier input, boolean state) implements PhysicalInputSignal {} + + /// Represents an axis movement event. + /// @param timeNanos the timestamp of the event in nanoseconds + /// @param input the identifier of the physical axis + /// @param value the position of the axis, *always* in the range `[0.0, 1.0]` + record AxisMoved(long timeNanos, Identifier input, float value) implements PhysicalInputSignal {} +} diff --git a/src/main/java/dev/isxander/controlify/input/physical/PhysicalSensorSignal.java b/src/main/java/dev/isxander/controlify/input/physical/PhysicalSensorSignal.java new file mode 100644 index 000000000..57ef156be --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/physical/PhysicalSensorSignal.java @@ -0,0 +1,11 @@ +package dev.isxander.controlify.input.physical; + +import dev.isxander.controlify.input.SensorType; + +/// Represents a single IMU sample +public record PhysicalSensorSignal( + long timeNanos, + SensorType sensorType, + float x, float y, float z +) implements PhysicalSignal { +} diff --git a/src/main/java/dev/isxander/controlify/input/physical/PhysicalSignal.java b/src/main/java/dev/isxander/controlify/input/physical/PhysicalSignal.java new file mode 100644 index 000000000..c92946936 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/physical/PhysicalSignal.java @@ -0,0 +1,8 @@ +package dev.isxander.controlify.input.physical; + +public sealed interface PhysicalSignal permits PhysicalInputSignal, PhysicalSensorSignal, PhysicalSignal.Tick { + long timeNanos(); + + /// Emitted at the end of each polling / every frame. + record Tick(long timeNanos, long deltaTimeNanos) implements PhysicalSignal {} +} diff --git a/src/main/java/dev/isxander/controlify/utils/event/EventSink.java b/src/main/java/dev/isxander/controlify/utils/event/EventSink.java new file mode 100644 index 000000000..ef93c6831 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/utils/event/EventSink.java @@ -0,0 +1,6 @@ +package dev.isxander.controlify.utils.event; + +@FunctionalInterface +public interface EventSink { + void accept(T event); +} diff --git a/src/main/java/dev/isxander/controlify/utils/event/EventStage.java b/src/main/java/dev/isxander/controlify/utils/event/EventStage.java new file mode 100644 index 000000000..086ba51ce --- /dev/null +++ b/src/main/java/dev/isxander/controlify/utils/event/EventStage.java @@ -0,0 +1,14 @@ +package dev.isxander.controlify.utils.event; + +@FunctionalInterface +public interface EventStage { + void onEvent(I event, EventSink downstream); + + default EventStage andThen(EventStage next) { + return (event, downstream) -> this.onEvent(event, x -> next.onEvent(x, downstream)); + } + + static UnaryEventStage identity() { + return (event, downstream) -> downstream.accept(event); + } +} diff --git a/src/main/java/dev/isxander/controlify/utils/event/UnaryEventStage.java b/src/main/java/dev/isxander/controlify/utils/event/UnaryEventStage.java new file mode 100644 index 000000000..cbf80fb8e --- /dev/null +++ b/src/main/java/dev/isxander/controlify/utils/event/UnaryEventStage.java @@ -0,0 +1,5 @@ +package dev.isxander.controlify.utils.event; + +@FunctionalInterface +public interface UnaryEventStage extends EventStage { +} From 560c6d0b2b83d7664c5349afbd6215dd30189842 Mon Sep 17 00:00:00 2001 From: isxander Date: Sat, 10 Oct 2026 18:17:59 +0100 Subject: [PATCH 02/44] activator system & more --- .../input/action/ActionAccessor.java | 27 ++++ .../controlify/input/action/ActionGraph.java | 56 +++++++ .../controlify/input/action/ActionImpl.java | 55 +++++++ .../controlify/input/action/ActionSpec.java | 9 +- .../input/action/ActionSpecBuilder.java | 20 +++ .../input/action/ActionSpecRegistry.java | 5 + .../controlify/input/action/ActionState.java | 46 ++++++ .../controlify/input/action/Activator.java | 3 +- .../controlify/input/action/Channel.java | 17 -- .../controlify/input/action/ChannelKind.java | 7 - .../action/activator/ActivatorConfig.java | 18 +++ .../action/activator/ActivatorConfigType.java | 55 +++++++ .../action/activator/ActivatorFactory.java | 8 - .../activator/ActivatorSerialization.java | 20 --- .../action/activator/CompoundActivator.java | 48 ++++++ .../action/activator/ContinuousActivator.java | 37 +++++ .../activator/DoubleTapPulseActivator.java | 38 +++++ .../activator/GuiPressPulseActivator.java | 38 +++++ .../action/activator/HoldLatchActivator.java | 48 ++++++ .../action/activator/HoldPulseActivator.java | 38 +++++ .../input/action/activator/NoopActivator.java | 23 +++ .../action/activator/PressPulseActivator.java | 38 +++++ .../activator/ReleasePulseActivator.java | 38 +++++ .../activator/RepeatLatchPulseActivator.java | 21 ++- .../action/activator/TapPulseActivator.java | 11 +- .../activator/ToggleLatchActivator.java | 17 +- .../input/logical/LogicalInput.java | 2 +- .../input/logical/LogicalSignal.java | 4 +- .../input/logical/PhysicalToLogicalStage.java | 153 +++++++++++++++++- .../input/physical/GestureInputs.java | 12 ++ .../physical/GestureRecogniserStage.java | 15 ++ 31 files changed, 848 insertions(+), 79 deletions(-) create mode 100644 src/main/java/dev/isxander/controlify/input/action/ActionAccessor.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/ActionGraph.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/ActionImpl.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/ActionSpecBuilder.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/ActionSpecRegistry.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/ActionState.java delete mode 100644 src/main/java/dev/isxander/controlify/input/action/ChannelKind.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfig.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfigType.java delete mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/ActivatorFactory.java delete mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/ActivatorSerialization.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/CompoundActivator.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/ContinuousActivator.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/DoubleTapPulseActivator.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/GuiPressPulseActivator.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/HoldLatchActivator.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/HoldPulseActivator.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/NoopActivator.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/PressPulseActivator.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/ReleasePulseActivator.java create mode 100644 src/main/java/dev/isxander/controlify/input/physical/GestureInputs.java create mode 100644 src/main/java/dev/isxander/controlify/input/physical/GestureRecogniserStage.java diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionAccessor.java b/src/main/java/dev/isxander/controlify/input/action/ActionAccessor.java new file mode 100644 index 000000000..040ba8eda --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/ActionAccessor.java @@ -0,0 +1,27 @@ +package dev.isxander.controlify.input.action; + +import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.controller.input.InputComponent; +import net.minecraft.resources.Identifier; +import org.jspecify.annotations.Nullable; + +/// An accessor to get an existing action from an input component +public interface ActionAccessor { + @Nullable Action onOrNull(InputComponent inputComponent); + + Identifier actionId(); + + default @Nullable Action onOrNull(ControllerEntity controller) { + return onOrNull(controller.input().orElseThrow()); + } + + default Action on(InputComponent inputComponent) { + Action action = onOrNull(inputComponent); + if (action == null) throw new NullPointerException("No action bound to input component " + actionId()); + return action; + } + + default Action on(ControllerEntity controller) { + return on(controller.input().orElseThrow()); + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionGraph.java b/src/main/java/dev/isxander/controlify/input/action/ActionGraph.java new file mode 100644 index 000000000..6afe890fd --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/ActionGraph.java @@ -0,0 +1,56 @@ +package dev.isxander.controlify.input.action; + +import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.logical.LogicalSignal; +import dev.isxander.controlify.utils.event.EventSink; +import net.minecraft.resources.Identifier; + +import java.util.*; + +public class ActionGraph implements EventSink { + private final Set allActions = new TreeSet<>(); + + // Context ID -> Logical Input -> set of ActionImpl + // the same compiled binding can be registered for multiple contexts and inputs + // this allows for more efficient lookup when processing signals + // by avoiding the need to iterate over all bindings for each signal + private final Map>> graph = new HashMap<>(); + + public void addAction(ActionImpl action) { + this.allActions.add(action); + for (var contextId : action.spec().contexts()) { + var byInputMap = this.graph.computeIfAbsent(contextId, _ -> new HashMap<>()); + byInputMap.computeIfAbsent(action.boundInput(), _ -> new TreeSet<>()) + .add(action); + } + } + + public void removeAction(ActionImpl action) { + for (var contextId : action.spec().contexts()) { + var byInputMap = this.graph.getOrDefault(contextId, Map.of()); + for (var set : byInputMap.values()) { + set.remove(action); + } + } + this.allActions.remove(action); + } + + @Override + public void accept(LogicalSignal event) { + switch (event) { + case LogicalSignal.Tick tick -> this.processTick(tick); + case LogicalSignal.InputSignal inputSignal -> this.processInput(inputSignal); + case LogicalSignal.SensorInterval _ -> {} // ignore sensor data + } + } + + private void processTick(LogicalSignal.Tick tick) { + for (ActionImpl action : this.allActions) { + action.activator().onSignal(tick, action.state()); + } + } + + private void processInput(LogicalSignal.InputSignal inputSignal) { + + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionImpl.java b/src/main/java/dev/isxander/controlify/input/action/ActionImpl.java new file mode 100644 index 000000000..eed9128a8 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/ActionImpl.java @@ -0,0 +1,55 @@ +package dev.isxander.controlify.input.action; + +import dev.isxander.controlify.input.action.activator.ActivatorConfig; +import dev.isxander.controlify.input.action.activator.NoopActivator; +import dev.isxander.controlify.input.logical.LogicalInput; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +public class ActionImpl implements Action, Comparable { + private final ActionSpec spec; + private final ActionState state; + private @Nullable LogicalInput boundInput; + private final @Nullable LogicalInput defaultInput; + private Activator activator; + private final ActivatorConfig activatorConfig; + + public ActionImpl( + ActionSpec spec, + @Nullable LogicalInput boundInput, + @Nullable LogicalInput defaultInput, + ActivatorConfig activator + ) { + this.spec = spec; + this.defaultInput = defaultInput; + this.activatorConfig = activator; + this.setBoundInput(boundInput); + this.state = new ActionState(); + } + + @Override + public @Nullable LogicalInput boundInput() { + return this.boundInput; + } + + @Override + public void setBoundInput(@Nullable LogicalInput boundInput) { + this.boundInput = boundInput; + this.activator = boundInput != null + ? this.activatorConfig.create(boundInput) + : new NoopActivator(); + } + + public ActionState state() { + return this.state; + } + + public Activator activator() { + return this.activator; + } + + @Override + public int compareTo(@NonNull ActionImpl o) { + return this.spec().compareTo(o.spec()); + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionSpec.java b/src/main/java/dev/isxander/controlify/input/action/ActionSpec.java index bc9a895ff..7da547c3f 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionSpec.java +++ b/src/main/java/dev/isxander/controlify/input/action/ActionSpec.java @@ -2,7 +2,9 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; +import java.util.Comparator; import java.util.Set; public record ActionSpec( @@ -11,5 +13,10 @@ public record ActionSpec( Component description, Component category, Set contexts -) { +) implements Comparable { + @Override + public int compareTo(@NonNull ActionSpec o) { + return Comparator.comparing(ActionSpec::id) + .compare(this, o); + } } diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionSpecBuilder.java b/src/main/java/dev/isxander/controlify/input/action/ActionSpecBuilder.java new file mode 100644 index 000000000..f343f82e8 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/ActionSpecBuilder.java @@ -0,0 +1,20 @@ +package dev.isxander.controlify.input.action; + +import net.minecraft.network.chat.Component; +import net.minecraft.resources.Identifier; + +public interface ActionSpecBuilder { + ActionSpecBuilder id(Identifier id); + + ActionSpecBuilder name(Component name); + + ActionSpecBuilder description(Component name); + + ActionSpecBuilder category(Component category); + + ActionSpecBuilder context(Identifier... contexts); + + ActionSpec build(); + + ActionAccessor buildAndRegister(ActionSpecRegistry registry); +} diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionSpecRegistry.java b/src/main/java/dev/isxander/controlify/input/action/ActionSpecRegistry.java new file mode 100644 index 000000000..aa1753630 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/ActionSpecRegistry.java @@ -0,0 +1,5 @@ +package dev.isxander.controlify.input.action; + +public interface ActionSpecRegistry { + void register(ActionSpec actionSpec); +} diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionState.java b/src/main/java/dev/isxander/controlify/input/action/ActionState.java new file mode 100644 index 000000000..1a248b5e5 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/ActionState.java @@ -0,0 +1,46 @@ +package dev.isxander.controlify.input.action; + +public final class ActionState implements Accumulator, Channel.Continuous, Channel.Latch, Channel.Pulse { + private int pulses = 0; + private boolean latchActive = false; + private float continuousValue = 0; + + @Override + public float getContinuous() { + return this.continuousValue; + } + + @Override + public boolean isLatchActive() { + return this.latchActive; + } + + @Override + public boolean consumePulse() { + if (this.pulses > 0) { + this.pulses--; + return true; + } + return false; + } + + @Override + public void toggleLatch() { + this.setLatch(!this.isLatchActive()); + } + + @Override + public void setLatch(boolean active) { + this.latchActive = active; + } + + @Override + public void firePulse() { + this.pulses++; + } + + @Override + public void setContinuous(float value) { + this.continuousValue = Math.clamp(value, 0f, 1f); + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/Activator.java b/src/main/java/dev/isxander/controlify/input/action/Activator.java index 8ccab37aa..7e5b0db02 100644 --- a/src/main/java/dev/isxander/controlify/input/action/Activator.java +++ b/src/main/java/dev/isxander/controlify/input/action/Activator.java @@ -6,13 +6,14 @@ /// Processes signals and activates an {@link Action}. public interface Activator { - /// Accepts a {@link LogicalSignal} from layer1 and potentially + /// Accepts a {@link LogicalSignal} and potentially /// submits state into the action accumulator. void onSignal(LogicalSignal signal, Accumulator acc); /// The logical input this is bound to. LogicalInput boundInput(); + /// gives a string representation of this activator String describe(); } diff --git a/src/main/java/dev/isxander/controlify/input/action/Channel.java b/src/main/java/dev/isxander/controlify/input/action/Channel.java index 08f9f69ba..064d29c4c 100644 --- a/src/main/java/dev/isxander/controlify/input/action/Channel.java +++ b/src/main/java/dev/isxander/controlify/input/action/Channel.java @@ -3,35 +3,18 @@ /// A channel is a way of interpreting the output state of a given {@link Action}. /// An {@link Action action's} bindings sends output to any of these channels. public sealed interface Channel { - ChannelKind kind(); - /// A channel which gives a continuous float value, such as an axis/joystick. non-sealed interface Continuous extends Channel { float getContinuous(); - - @Override - default ChannelKind kind() { - return ChannelKind.CONTINUOUS; - } } /// A channel which gives single pulses, for example a button press causing a jump. non-sealed interface Pulse extends Channel { boolean consumePulse(); - - @Override - default ChannelKind kind() { - return ChannelKind.PULSE; - } } /// A channel which gives a stateful digital output of the action, for example a physical switch's state. non-sealed interface Latch extends Channel { boolean isLatchActive(); - - @Override - default ChannelKind kind() { - return ChannelKind.LATCH; - } } } diff --git a/src/main/java/dev/isxander/controlify/input/action/ChannelKind.java b/src/main/java/dev/isxander/controlify/input/action/ChannelKind.java deleted file mode 100644 index d87739fb6..000000000 --- a/src/main/java/dev/isxander/controlify/input/action/ChannelKind.java +++ /dev/null @@ -1,7 +0,0 @@ -package dev.isxander.controlify.input.action; - -public enum ChannelKind { - PULSE, - LATCH, - CONTINUOUS -} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfig.java b/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfig.java new file mode 100644 index 000000000..cf4adc412 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfig.java @@ -0,0 +1,18 @@ +package dev.isxander.controlify.input.action.activator; + +import com.mojang.serialization.Codec; +import dev.isxander.controlify.input.action.Activator; +import dev.isxander.controlify.input.logical.LogicalInput; +import net.minecraft.util.StringRepresentable; + +/// An immutable structure typically containing the configuration +/// for an {@link Activator} type, allowing you to create instances +/// of the activator using the configuration and a given bound logical input. +public interface ActivatorConfig { + T create(LogicalInput boundInput); + + ActivatorConfigType type(); + + Codec> CODEC = StringRepresentable.fromValues(() -> ActivatorConfigType.TYPES) + .dispatch("type", ActivatorConfig::type, ActivatorConfigType::mapCodec); +} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfigType.java b/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfigType.java new file mode 100644 index 000000000..8cae0e58f --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfigType.java @@ -0,0 +1,55 @@ +package dev.isxander.controlify.input.action.activator; + +import com.mojang.serialization.MapCodec; +import dev.isxander.controlify.input.action.Activator; +import net.minecraft.util.StringRepresentable; +import org.jetbrains.annotations.NotNull; + +public record ActivatorConfigType>( + String id, + MapCodec mapCodec +) implements StringRepresentable { + public static final ActivatorConfigType COMPOUND = + new ActivatorConfigType<>(CompoundActivator.Config.TYPE_ID, CompoundActivator.Config.MAP_CODEC); + + public static final ActivatorConfigType CONTINUOUS = + new ActivatorConfigType<>(ContinuousActivator.Config.TYPE_ID, ContinuousActivator.Config.MAP_CODEC); + + public static final ActivatorConfigType HOLD_LATCH = + new ActivatorConfigType<>(HoldLatchActivator.Config.TYPE_ID, HoldLatchActivator.Config.MAP_CODEC); + + public static final ActivatorConfigType PRESS_PULSE = + new ActivatorConfigType<>(PressPulseActivator.Config.TYPE_ID, PressPulseActivator.Config.MAP_CODEC); + + public static final ActivatorConfigType RELEASE_PULSE = + new ActivatorConfigType<>(ReleasePulseActivator.Config.TYPE_ID, ReleasePulseActivator.Config.MAP_CODEC); + + public static final ActivatorConfigType REPEAT_LATCH_PULSE = + new ActivatorConfigType<>(RepeatLatchPulseActivator.Config.TYPE_ID, RepeatLatchPulseActivator.Config.MAP_CODEC); + + public static final ActivatorConfigType TAP_PULSE = + new ActivatorConfigType<>(TapPulseActivator.Config.TYPE_ID, TapPulseActivator.Config.MAP_CODEC); + + public static final ActivatorConfigType DOUBLE_TAP_PULSE = + new ActivatorConfigType<>(DoubleTapPulseActivator.Config.TYPE_ID, DoubleTapPulseActivator.Config.MAP_CODEC); + + public static final ActivatorConfigType GUI_PRESS_PULSE = + new ActivatorConfigType<>(GuiPressPulseActivator.Config.TYPE_ID, GuiPressPulseActivator.Config.MAP_CODEC); + + public static final ActivatorConfigType HOLD_PULSE = + new ActivatorConfigType<>(HoldPulseActivator.Config.TYPE_ID, HoldPulseActivator.Config.MAP_CODEC); + + public static final ActivatorConfigType TOGGLE_LATCH = + new ActivatorConfigType<>(ToggleLatchActivator.Config.TYPE_ID, ToggleLatchActivator.Config.MAP_CODEC); + + public static final ActivatorConfigType[] TYPES = new ActivatorConfigType[]{ + COMPOUND, CONTINUOUS, HOLD_LATCH, PRESS_PULSE, RELEASE_PULSE, + REPEAT_LATCH_PULSE, TAP_PULSE, DOUBLE_TAP_PULSE, GUI_PRESS_PULSE, + HOLD_PULSE, TOGGLE_LATCH + }; + + @Override + public @NotNull String getSerializedName() { + return this.id(); + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorFactory.java b/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorFactory.java deleted file mode 100644 index 0c969448c..000000000 --- a/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorFactory.java +++ /dev/null @@ -1,8 +0,0 @@ -package dev.isxander.controlify.input.action.activator; - -import dev.isxander.controlify.input.action.Activator; -import dev.isxander.controlify.input.logical.LogicalInput; - -public interface ActivatorFactory { - T create(LogicalInput boundInput); -} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorSerialization.java b/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorSerialization.java deleted file mode 100644 index bf6efd8e4..000000000 --- a/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorSerialization.java +++ /dev/null @@ -1,20 +0,0 @@ -package dev.isxander.controlify.input.action.activator; - -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; -import dev.isxander.controlify.input.action.Activator; -import net.minecraft.util.StringRepresentable; - -public final class ActivatorSerialization { - - private ActivatorSerialization() { - } - - public record Type>(String id, MapCodec mapCodec) implements StringRepresentable { - public static final Type TAP_PULSE = - new Type<>(TapPulseActivator.TapPulseActivatorConfig.TYPE_ID, TapPulseActivator.TapPulseActivatorConfig.MAP_CODEC); - - } - - public static final Codec> FACTORY_CODEC = null; -} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/CompoundActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/CompoundActivator.java new file mode 100644 index 000000000..916a1beb0 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/activator/CompoundActivator.java @@ -0,0 +1,48 @@ +package dev.isxander.controlify.input.action.activator; + +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import dev.isxander.controlify.input.action.Accumulator; +import dev.isxander.controlify.input.action.Activator; +import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.logical.LogicalSignal; + +import java.util.List; +import java.util.stream.Collectors; + +public record CompoundActivator(LogicalInput boundInput, List activators) implements Activator { + + @Override + public void onSignal(LogicalSignal signal, Accumulator acc) { + for (var activator : activators) { + activator.onSignal(signal, acc); + } + } + + @Override + public String describe() { + return activators().stream() + .map(Activator::describe) + .collect(Collectors.joining(",", "Compound[", "]")); + } + + public record Config(List> activators) implements ActivatorConfig { + public static final String TYPE_ID = "compound"; + public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + ActivatorConfig.CODEC.listOf().fieldOf("activators").forGetter(Config::activators) + ).apply(instance, Config::new)); + + @Override + public CompoundActivator create(LogicalInput boundInput) { + var activators = this.activators.stream() + .map(config -> config.create(boundInput)) + .toList(); + return new CompoundActivator(boundInput, activators); + } + + @Override + public ActivatorConfigType type() { + return ActivatorConfigType.COMPOUND; + } + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ContinuousActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/ContinuousActivator.java new file mode 100644 index 000000000..31d67366d --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/activator/ContinuousActivator.java @@ -0,0 +1,37 @@ +package dev.isxander.controlify.input.action.activator; + +import com.mojang.serialization.MapCodec; +import dev.isxander.controlify.input.action.Accumulator; +import dev.isxander.controlify.input.action.Activator; +import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.logical.LogicalSignal; + +public record ContinuousActivator(LogicalInput boundInput) implements Activator { + @Override + public void onSignal(LogicalSignal signal, Accumulator acc) { + if (signal instanceof LogicalSignal.AxisMoved s && s.input().equals(this.boundInput)) { + acc.setContinuous(s.value()); + } + } + + @Override + public String describe() { + return "Continuous[" + boundInput().toString() + "]"; + } + + public record Config() implements ActivatorConfig { + public static final String TYPE_ID = "continuous"; + public static final Config INSTANCE = new Config(); + public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); + + @Override + public ContinuousActivator create(LogicalInput boundInput) { + return new ContinuousActivator(boundInput); + } + + @Override + public ActivatorConfigType type() { + return ActivatorConfigType.CONTINUOUS; + } + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/DoubleTapPulseActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/DoubleTapPulseActivator.java new file mode 100644 index 000000000..8d271dad6 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/activator/DoubleTapPulseActivator.java @@ -0,0 +1,38 @@ +package dev.isxander.controlify.input.action.activator; + +import com.mojang.serialization.MapCodec; +import dev.isxander.controlify.input.action.Accumulator; +import dev.isxander.controlify.input.action.Activator; +import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.logical.LogicalSignal; + +/// Produces a pulse when the input is double tapped. +public record DoubleTapPulseActivator(LogicalInput boundInput) implements Activator { + @Override + public void onSignal(LogicalSignal signal, Accumulator acc) { + if (signal instanceof LogicalSignal.DoubleTapped(_, LogicalInput input) && this.boundInput.equals(input)) { + acc.firePulse(); + } + } + + @Override + public String describe() { + return "DoubleTap[" + boundInput.toString() + "]"; + } + + public record Config() implements ActivatorConfig { + public static final String TYPE_ID = "pulse_double_tap"; + public static final Config INSTANCE = new Config(); + public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); + + @Override + public DoubleTapPulseActivator create(LogicalInput boundInput) { + return new DoubleTapPulseActivator(boundInput); + } + + @Override + public ActivatorConfigType type() { + return ActivatorConfigType.DOUBLE_TAP_PULSE; + } + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/GuiPressPulseActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/GuiPressPulseActivator.java new file mode 100644 index 000000000..9d8fec1af --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/activator/GuiPressPulseActivator.java @@ -0,0 +1,38 @@ +package dev.isxander.controlify.input.action.activator; + +import com.mojang.serialization.MapCodec; +import dev.isxander.controlify.input.action.Accumulator; +import dev.isxander.controlify.input.action.Activator; +import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.logical.LogicalSignal; + +/// Produces a pulse on release when no GUI navigation consumed the press. +public record GuiPressPulseActivator(LogicalInput boundInput) implements Activator { + @Override + public void onSignal(LogicalSignal signal, Accumulator acc) { + if (signal instanceof LogicalSignal.GuiPress(_, LogicalInput input) && this.boundInput.equals(input)) { + acc.firePulse(); + } + } + + @Override + public String describe() { + return "GuiPress[" + boundInput.toString() + "]"; + } + + public record Config() implements ActivatorConfig { + public static final String TYPE_ID = "pulse_gui_press"; + public static final Config INSTANCE = new Config(); + public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); + + @Override + public GuiPressPulseActivator create(LogicalInput boundInput) { + return new GuiPressPulseActivator(boundInput); + } + + @Override + public ActivatorConfigType type() { + return ActivatorConfigType.GUI_PRESS_PULSE; + } + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/HoldLatchActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/HoldLatchActivator.java new file mode 100644 index 000000000..8d7921d7f --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/activator/HoldLatchActivator.java @@ -0,0 +1,48 @@ +package dev.isxander.controlify.input.action.activator; + +import com.mojang.serialization.MapCodec; +import dev.isxander.controlify.input.action.Accumulator; +import dev.isxander.controlify.input.action.Activator; +import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.logical.LogicalSignal; + +/// Keeps the latch active from button press until release, without a hold delay. +public record HoldLatchActivator(LogicalInput boundInput) implements Activator { + @Override + public void onSignal(LogicalSignal signal, Accumulator acc) { + switch (signal) { + case LogicalSignal.ButtonPress(_, LogicalInput input) -> { + if (this.boundInput.equals(input)) { + acc.setLatch(true); + } + } + case LogicalSignal.ButtonRelease(_, LogicalInput input) -> { + if (this.boundInput.equals(input)) { + acc.setLatch(false); + } + } + default -> {} + } + } + + @Override + public String describe() { + return "HoldLatch[" + boundInput.toString() + "]"; + } + + public record Config() implements ActivatorConfig { + public static final String TYPE_ID = "latch_hold"; + public static final Config INSTANCE = new Config(); + public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); + + @Override + public HoldLatchActivator create(LogicalInput boundInput) { + return new HoldLatchActivator(boundInput); + } + + @Override + public ActivatorConfigType type() { + return ActivatorConfigType.HOLD_LATCH; + } + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/HoldPulseActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/HoldPulseActivator.java new file mode 100644 index 000000000..d68f5da45 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/activator/HoldPulseActivator.java @@ -0,0 +1,38 @@ +package dev.isxander.controlify.input.action.activator; + +import com.mojang.serialization.MapCodec; +import dev.isxander.controlify.input.action.Accumulator; +import dev.isxander.controlify.input.action.Activator; +import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.logical.LogicalSignal; + +/// Produces a pulse once the input reaches the hold duration. +public record HoldPulseActivator(LogicalInput boundInput) implements Activator { + @Override + public void onSignal(LogicalSignal signal, Accumulator acc) { + if (signal instanceof LogicalSignal.Held(_, LogicalInput input) && this.boundInput.equals(input)) { + acc.firePulse(); + } + } + + @Override + public String describe() { + return "Hold[" + boundInput.toString() + "]"; + } + + public record Config() implements ActivatorConfig { + public static final String TYPE_ID = "pulse_hold"; + public static final Config INSTANCE = new Config(); + public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); + + @Override + public HoldPulseActivator create(LogicalInput boundInput) { + return new HoldPulseActivator(boundInput); + } + + @Override + public ActivatorConfigType type() { + return ActivatorConfigType.HOLD_PULSE; + } + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/NoopActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/NoopActivator.java new file mode 100644 index 000000000..dde0bf7e6 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/activator/NoopActivator.java @@ -0,0 +1,23 @@ +package dev.isxander.controlify.input.action.activator; + +import dev.isxander.controlify.input.action.Accumulator; +import dev.isxander.controlify.input.action.Activator; +import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.logical.LogicalSignal; + +public class NoopActivator implements Activator { + @Override + public void onSignal(LogicalSignal signal, Accumulator acc) { + + } + + @Override + public LogicalInput boundInput() { + return null; + } + + @Override + public String describe() { + return "Noop"; + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/PressPulseActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/PressPulseActivator.java new file mode 100644 index 000000000..58f38263c --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/activator/PressPulseActivator.java @@ -0,0 +1,38 @@ +package dev.isxander.controlify.input.action.activator; + +import com.mojang.serialization.MapCodec; +import dev.isxander.controlify.input.action.Accumulator; +import dev.isxander.controlify.input.action.Activator; +import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.logical.LogicalSignal; + +/// Produces a pulse when the input is pressed. +public record PressPulseActivator(LogicalInput boundInput) implements Activator { + @Override + public void onSignal(LogicalSignal signal, Accumulator acc) { + if (signal instanceof LogicalSignal.ButtonPress(_, LogicalInput input) && this.boundInput.equals(input)) { + acc.firePulse(); + } + } + + @Override + public String describe() { + return "Press[" + boundInput.toString() + "]"; + } + + public record Config() implements ActivatorConfig { + public static final String TYPE_ID = "pulse_press"; + public static final Config INSTANCE = new Config(); + public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); + + @Override + public PressPulseActivator create(LogicalInput boundInput) { + return new PressPulseActivator(boundInput); + } + + @Override + public ActivatorConfigType type() { + return ActivatorConfigType.PRESS_PULSE; + } + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ReleasePulseActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/ReleasePulseActivator.java new file mode 100644 index 000000000..3199708c0 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/activator/ReleasePulseActivator.java @@ -0,0 +1,38 @@ +package dev.isxander.controlify.input.action.activator; + +import com.mojang.serialization.MapCodec; +import dev.isxander.controlify.input.action.Accumulator; +import dev.isxander.controlify.input.action.Activator; +import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.logical.LogicalSignal; + +/// Produces a pulse when the input is released. +public record ReleasePulseActivator(LogicalInput boundInput) implements Activator { + @Override + public void onSignal(LogicalSignal signal, Accumulator acc) { + if (signal instanceof LogicalSignal.ButtonRelease(_, LogicalInput input) && this.boundInput.equals(input)) { + acc.firePulse(); + } + } + + @Override + public String describe() { + return "Release[" + boundInput.toString() + "]"; + } + + public record Config() implements ActivatorConfig { + public static final String TYPE_ID = "pulse_release"; + public static final Config INSTANCE = new Config(); + public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); + + @Override + public ReleasePulseActivator create(LogicalInput boundInput) { + return new ReleasePulseActivator(boundInput); + } + + @Override + public ActivatorConfigType type() { + return ActivatorConfigType.RELEASE_PULSE; + } + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/RepeatLatchPulseActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/RepeatLatchPulseActivator.java index bc054b43e..fd37620e8 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/RepeatLatchPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/input/action/activator/RepeatLatchPulseActivator.java @@ -75,16 +75,16 @@ public void toggleLatch() { } } - public record RepeatLatchPulseActivatorConfig( - ActivatorFactory latchBinding, + public record Config( + ActivatorConfig latchBinding, int initialDelayTicks, int repeatDelayTicks - ) implements ActivatorFactory { + ) implements ActivatorConfig { public static final String TYPE_ID = "pulse_repeat_latch"; - public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - ActivatorSerialization.FACTORY_CODEC.fieldOf("source_latch").forGetter(RepeatLatchPulseActivatorConfig::latchBinding), - Codec.intRange(0, Integer.MAX_VALUE).optionalFieldOf("initial_delay_ticks", 5).forGetter(RepeatLatchPulseActivatorConfig::initialDelayTicks), - Codec.intRange(1, Integer.MAX_VALUE).optionalFieldOf("repeat_delay_ticks", 1).forGetter(RepeatLatchPulseActivatorConfig::repeatDelayTicks) - ).apply(instance, RepeatLatchPulseActivatorConfig::new)); + public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + ActivatorConfig.CODEC.fieldOf("source_latch").forGetter(Config::latchBinding), + Codec.intRange(0, Integer.MAX_VALUE).optionalFieldOf("initial_delay_ticks", 5).forGetter(Config::initialDelayTicks), + Codec.intRange(1, Integer.MAX_VALUE).optionalFieldOf("repeat_delay_ticks", 1).forGetter(Config::repeatDelayTicks) + ).apply(instance, Config::new)); @Override public RepeatLatchPulseActivator create(LogicalInput boundInput) { @@ -92,5 +92,10 @@ public RepeatLatchPulseActivator create(LogicalInput boundInput) { long repeatDelayNs = this.repeatDelayTicks * 50_000_000L; return new RepeatLatchPulseActivator(this.latchBinding.create(boundInput), initialDelayNs, repeatDelayNs); } + + @Override + public ActivatorConfigType type() { + return ActivatorConfigType.REPEAT_LATCH_PULSE; + } } } diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/TapPulseActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/TapPulseActivator.java index 78f407e5c..11a227aac 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/TapPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/input/action/activator/TapPulseActivator.java @@ -21,14 +21,19 @@ public String describe() { return "Tap[" + boundInput.toString() + "]"; } - public record TapPulseActivatorConfig() implements ActivatorFactory { + public record Config() implements ActivatorConfig { public static final String TYPE_ID = "pulse_tap"; - public static final TapPulseActivatorConfig INSTANCE = new TapPulseActivatorConfig(); - public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); + public static final Config INSTANCE = new Config(); + public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); @Override public TapPulseActivator create(LogicalInput boundInput) { return new TapPulseActivator(boundInput); } + + @Override + public ActivatorConfigType type() { + return ActivatorConfigType.TAP_PULSE; + } } } diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ToggleLatchActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/ToggleLatchActivator.java index d045081f7..3c8df2b38 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/ToggleLatchActivator.java +++ b/src/main/java/dev/isxander/controlify/input/action/activator/ToggleLatchActivator.java @@ -43,17 +43,22 @@ public void firePulse() { } } - public record ToggleLatchActivatorConfig( - ActivatorFactory pulseBinding - ) implements ActivatorFactory { + public record Config( + ActivatorConfig pulseBinding + ) implements ActivatorConfig { public static final String TYPE_ID = "latch_toggle"; - public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - ActivatorSerialization.FACTORY_CODEC.fieldOf("source_pulse").forGetter(ToggleLatchActivatorConfig::pulseBinding) - ).apply(instance, ToggleLatchActivatorConfig::new)); + public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + ActivatorConfig.CODEC.fieldOf("source_pulse").forGetter(Config::pulseBinding) + ).apply(instance, Config::new)); @Override public ToggleLatchActivator create(LogicalInput boundInput) { return new ToggleLatchActivator(this.pulseBinding.create(boundInput)); } + + @Override + public ActivatorConfigType type() { + return ActivatorConfigType.TOGGLE_LATCH; + } } } diff --git a/src/main/java/dev/isxander/controlify/input/logical/LogicalInput.java b/src/main/java/dev/isxander/controlify/input/logical/LogicalInput.java index ee6188fbc..eeacbfeb7 100644 --- a/src/main/java/dev/isxander/controlify/input/logical/LogicalInput.java +++ b/src/main/java/dev/isxander/controlify/input/logical/LogicalInput.java @@ -20,7 +20,7 @@ record Single(Identifier physicalInput) implements LogicalInput { } /// A logical input that triggers only after all modifiers are already pressed. - record Modified(Set modifiers, Identifier trigger) implements LogicalInput { + record ModifiedChord(Set modifiers, Identifier trigger) implements LogicalInput { } } diff --git a/src/main/java/dev/isxander/controlify/input/logical/LogicalSignal.java b/src/main/java/dev/isxander/controlify/input/logical/LogicalSignal.java index f4041e2cd..8c3a4e871 100644 --- a/src/main/java/dev/isxander/controlify/input/logical/LogicalSignal.java +++ b/src/main/java/dev/isxander/controlify/input/logical/LogicalSignal.java @@ -15,10 +15,10 @@ sealed interface InputSignal extends LogicalSignal { LogicalInput input(); } /// When the button is pressed down - record ButtonDown(long timeNanos, LogicalInput input) implements InputSignal {} + record ButtonPress(long timeNanos, LogicalInput input) implements InputSignal {} /// When the button is released - record ButtonUp(long timeNanos, LogicalInput input) implements InputSignal {} + record ButtonRelease(long timeNanos, LogicalInput input) implements InputSignal {} /// When button is pressed and released in quick succession record Tapped(long timeNanos, LogicalInput input) implements InputSignal {} diff --git a/src/main/java/dev/isxander/controlify/input/logical/PhysicalToLogicalStage.java b/src/main/java/dev/isxander/controlify/input/logical/PhysicalToLogicalStage.java index df4db3c03..2694993d6 100644 --- a/src/main/java/dev/isxander/controlify/input/logical/PhysicalToLogicalStage.java +++ b/src/main/java/dev/isxander/controlify/input/logical/PhysicalToLogicalStage.java @@ -1,17 +1,62 @@ package dev.isxander.controlify.input.logical; +import dev.isxander.controlify.input.physical.PhysicalInputSignal; import dev.isxander.controlify.input.physical.PhysicalSensorSignal; import dev.isxander.controlify.input.physical.PhysicalSignal; import dev.isxander.controlify.utils.event.EventSink; import dev.isxander.controlify.utils.event.EventStage; +import net.minecraft.resources.Identifier; -/// The event stage that converts physical signals into logical ones. -/// This stage is responsible for recognizing chords, integrating sensor data, and emitting logical signals. +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/// Converts physical inputs into single inputs and arbitrary modifier chords. +/// Each trigger uses the most recently pressed button still held as its sole +/// modifier. The chord keeps that modifier until either button is released, +/// and follows the same gesture lifecycle as a single button. +/// +/// Button gestures use event timestamps, with holds emitted once on a tick. +/// Call {@link #onGuiNavigate()} when GUI navigation consumes a pending press. +/// Sensor integration is delegated to {@link SensorAccumulatorStage}. public class PhysicalToLogicalStage implements EventStage { + public static final long DEFAULT_TAP_MAX_NANOS = 200_000_000L; + public static final long DEFAULT_DOUBLE_TAP_MAX_NANOS = 300_000_000L; + public static final long DEFAULT_HOLD_NANOS = 500_000_000L; + private final SensorAccumulatorStage sensorAccumulatorStage; + private final long tapMaxNanos, doubleTapMaxNanos, holdNanos; + + // Press order lets an older held button become the latest modifier again when a newer one is released. + private final LinkedHashSet pressedButtons = new LinkedHashSet<>(); + private final Map activeButtons = new LinkedHashMap<>(); + private final Map lastTaps = new HashMap<>(); + private final Map axisValues = new HashMap<>(); public PhysicalToLogicalStage(SensorAccumulatorStage sensorAccumulatorStage) { - this.sensorAccumulatorStage = sensorAccumulatorStage; + this(sensorAccumulatorStage, DEFAULT_TAP_MAX_NANOS, + DEFAULT_DOUBLE_TAP_MAX_NANOS, DEFAULT_HOLD_NANOS); + } + + public PhysicalToLogicalStage( + SensorAccumulatorStage sensorAccumulatorStage, + long tapMaxNanos, long doubleTapMaxNanos, long holdNanos + ) { + this.sensorAccumulatorStage = Objects.requireNonNull(sensorAccumulatorStage); + if (tapMaxNanos < 0 || doubleTapMaxNanos < 0 || holdNanos <= tapMaxNanos) { + throw new IllegalArgumentException("Gesture durations must be nonnegative and hold must exceed tap"); + } + this.tapMaxNanos = tapMaxNanos; + this.doubleTapMaxNanos = doubleTapMaxNanos; + this.holdNanos = holdNanos; + } + + /// Cancels GUI presses for buttons already held; subsequent presses remain eligible. + public void onGuiNavigate() { + this.activeButtons.values().forEach(state -> state.guiPressEligible = false); } @Override @@ -19,12 +64,110 @@ public void onEvent(PhysicalSignal event, EventSink downs switch (event) { case PhysicalSignal.Tick(long timeNanos, long deltaTimeNanos) -> { this.sensorAccumulatorStage.onEvent(event, downstream); - + // Holds must fire even without new input events, before downstream actions process this frame's tick. + this.activeButtons.forEach((input, state) -> { + if (!state.held && timeNanos - state.pressedAt >= this.holdNanos) { + state.held = true; + downstream.accept(new LogicalSignal.Held(timeNanos, input)); + } + }); + this.lastTaps.values().removeIf(time -> timeNanos - time > this.doubleTapMaxNanos); downstream.accept(new LogicalSignal.Tick(timeNanos, deltaTimeNanos)); } - case PhysicalSensorSignal sensorSignal -> this.sensorAccumulatorStage.onEvent(sensorSignal, downstream); + case PhysicalInputSignal.ButtonChanged(long timeNanos, Identifier input, boolean state) -> + this.onButton(timeNanos, input, state, downstream); + case PhysicalInputSignal.AxisMoved(long timeNanos, Identifier input, float value) -> + this.onAxis(timeNanos, input, value, downstream); + } + } + + private void onButton(long time, Identifier input, boolean pressed, EventSink downstream) { + if (this.pressedButtons.contains(input) == pressed) { + // Duplicate edges must not restart gesture timers or change modifier priority. + return; + } + var single = new LogicalInput.Single(input); + if (pressed) { + // Only buttons held before this press can modify it; adding the trigger first would select itself. + LogicalInput.ModifiedChord chord = this.recogniseChord(input); + this.press(time, single, downstream); + if (chord != null) { + this.press(time, chord, downstream); + } + this.pressedButtons.add(input); + } else { + this.pressedButtons.remove(input); + this.release(time, single, downstream); + // Use the chords captured on press, so later presses cannot change which chord gets released. + // Snapshot because releasing a chord removes it from activeButtons. + for (var logicalInput : this.activeButtons.keySet().stream().toList()) { + if (logicalInput instanceof LogicalInput.ModifiedChord chord + && (chord.trigger().equals(input) || chord.modifiers().contains(input))) { + this.release(time, chord, downstream); + } + } + } + } + + private void press(long time, LogicalInput input, EventSink downstream) { + this.activeButtons.put(input, new ButtonState(time)); + downstream.accept(new LogicalSignal.ButtonPress(time, input)); + } + + private void release(long time, LogicalInput input, EventSink downstream) { + ButtonState state = this.activeButtons.remove(input); + if (state == null) { + return; + } + downstream.accept(new LogicalSignal.ButtonRelease(time, input)); + long duration = time - state.pressedAt; + if (!state.held && duration >= 0 && duration <= this.tapMaxNanos) { + downstream.accept(new LogicalSignal.Tapped(time, input)); + // Consume the first tap so three quick taps form one double tap, rather than overlapping pairs. + Long previousTap = this.lastTaps.remove(input); + if (previousTap != null && time >= previousTap && time - previousTap <= this.doubleTapMaxNanos) { + downstream.accept(new LogicalSignal.DoubleTapped(time, input)); + } else { + this.lastTaps.put(input, time); + } + } else { + this.lastTaps.remove(input); + } + if (state.guiPressEligible && duration >= 0) { + downstream.accept(new LogicalSignal.GuiPress(time, input)); + } + } + + private void onAxis(long time, Identifier input, float value, EventSink downstream) { + // Domain expansion supplies button edges for axis gestures; raw values only need continuous deltas here. + float previous = this.axisValues.getOrDefault(input, 0.0f); + if (value == previous) { + return; + } + if (value == 0.0f) { + this.axisValues.remove(input); + } else { + this.axisValues.put(input, value); + } + downstream.accept(new LogicalSignal.AxisMoved(time, new LogicalInput.Single(input), value, value - previous)); + } + + private LogicalInput.ModifiedChord recogniseChord(Identifier trigger) { + if (!this.pressedButtons.isEmpty()) { + return new LogicalInput.ModifiedChord(Set.of(this.pressedButtons.getLast()), trigger); + } + return null; + } + + private static final class ButtonState { + private final long pressedAt; + private boolean held; + private boolean guiPressEligible = true; + + private ButtonState(long pressedAt) { + this.pressedAt = pressedAt; } } } diff --git a/src/main/java/dev/isxander/controlify/input/physical/GestureInputs.java b/src/main/java/dev/isxander/controlify/input/physical/GestureInputs.java new file mode 100644 index 000000000..939cb4ba0 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/physical/GestureInputs.java @@ -0,0 +1,12 @@ +package dev.isxander.controlify.input.physical; + +import dev.isxander.controlify.utils.CUtil; +import net.minecraft.resources.Identifier; + +public final class GestureInputs { + + private GestureInputs() { + } + + public static final Identifier SHAKE = CUtil.rl("gesture/shake"); +} diff --git a/src/main/java/dev/isxander/controlify/input/physical/GestureRecogniserStage.java b/src/main/java/dev/isxander/controlify/input/physical/GestureRecogniserStage.java new file mode 100644 index 000000000..36b9c320f --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/physical/GestureRecogniserStage.java @@ -0,0 +1,15 @@ +package dev.isxander.controlify.input.physical; + +import dev.isxander.controlify.utils.event.EventSink; +import dev.isxander.controlify.utils.event.UnaryEventStage; + +/// Processes sensor data and recognizes distinct gestures such as shaking of the controller. +/// This stage then emits {@link dev.isxander.controlify.input.physical.PhysicalInputSignal.ButtonChanged} events +/// for when the gesture begins and ends using input identifiers from {@link GestureInputs}. +public class GestureRecogniserStage implements UnaryEventStage { + @Override + public void onEvent(PhysicalSignal event, EventSink downstream) { + // TODO + downstream.accept(event); + } +} From b1438518043c3fae4dac3d7f02af78f5f3a12c70 Mon Sep 17 00:00:00 2001 From: isxander Date: Sun, 11 Oct 2026 23:38:19 +0100 Subject: [PATCH 03/44] flesh out the pipeline and start wiring things up --- .../dev/isxander/controlify/Controlify.java | 40 ++-- .../bindings/InputBindingBuilderImpl.java | 2 +- .../bindings/RadialIconManager.java | 6 +- .../defaults/DefaultBindProvider.java | 4 +- .../defaults/LayeredDefaultBindProvider.java | 6 +- .../MapBackedDefaultBindProvider.java | 10 +- .../config/dto/profile/InputConfig.java | 6 +- .../settings/profile/InputSettings.java | 6 +- .../controlify/gui/screen/RadialItems.java | 6 +- .../gui/screen/RadialMenuEditScreen.java | 4 +- .../controlify/input/InputComponent.java | 173 ++++++++++++++++++ .../controlify/input/InputPipeline.java | 87 +++++++++ .../controlify/input/action/Action.java | 4 - .../input/action/ActionAccessor.java | 2 +- .../input/action/ActionAccessorImpl.java | 12 ++ .../input/action/ActionContext.java | 11 ++ .../controlify/input/action/ActionGraph.java | 58 +++++- .../controlify/input/action/ActionImpl.java | 43 ++++- .../controlify/input/action/ActionSpec.java | 17 +- .../input/action/ActionSpecBuilder.java | 9 +- .../input/action/ActionSpecBuilderImpl.java | 76 ++++++++ .../input/action/ActionSpecRegistry.java | 2 +- .../input/action/ActionSpecRegistryImpl.java | 42 +++++ .../controlify/input/action/ActionState.java | 6 + .../action/activator/ActivatorConfig.java | 17 +- .../action/activator/ActivatorConfigType.java | 29 ++- .../action/activator/ContinuousActivator.java | 1 - .../activator/DoubleTapPulseActivator.java | 1 - .../activator/GuiPressPulseActivator.java | 1 - .../action/activator/HoldLatchActivator.java | 1 - .../action/activator/HoldPulseActivator.java | 1 - .../action/activator/PressPulseActivator.java | 1 - .../activator/ReleasePulseActivator.java | 1 - .../activator/RepeatLatchPulseActivator.java | 10 +- .../action/activator/TapPulseActivator.java | 1 - .../input/logical/LogicalInput.java | 20 +- .../input/logical/PhysicalToLogicalStage.java | 2 +- .../input/logical/SensorAccumulatorStage.java | 2 +- .../input/physical/DeadzoneStage.java | 15 +- .../physical/InputDomainExpansionStage.java | 9 +- .../input/physical/PhysicalSignal.java | 4 +- .../input/physical/SensorProcessorStage.java | 95 ++++++++++ .../controlify/utils/codec/CExtraCodecs.java | 6 +- .../controlify/utils/event/EventSource.java | 11 ++ .../controlify/utils/event/EventStage.java | 24 ++- .../controlify/utils/event/EventTap.java | 44 +++++ 46 files changed, 832 insertions(+), 96 deletions(-) create mode 100644 src/main/java/dev/isxander/controlify/input/InputComponent.java create mode 100644 src/main/java/dev/isxander/controlify/input/InputPipeline.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/ActionAccessorImpl.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/ActionContext.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/ActionSpecBuilderImpl.java create mode 100644 src/main/java/dev/isxander/controlify/input/action/ActionSpecRegistryImpl.java create mode 100644 src/main/java/dev/isxander/controlify/input/physical/SensorProcessorStage.java create mode 100644 src/main/java/dev/isxander/controlify/utils/event/EventSource.java create mode 100644 src/main/java/dev/isxander/controlify/utils/event/EventTap.java diff --git a/src/main/java/dev/isxander/controlify/Controlify.java b/src/main/java/dev/isxander/controlify/Controlify.java index 7b6b59380..dfd09084c 100644 --- a/src/main/java/dev/isxander/controlify/Controlify.java +++ b/src/main/java/dev/isxander/controlify/Controlify.java @@ -40,6 +40,7 @@ import dev.isxander.controlify.gui.screen.*; import dev.isxander.controlify.debug.DebugProperties; import dev.isxander.controlify.ingame.ControllerPlayerMovement; +import dev.isxander.controlify.input.action.activator.ActivatorConfigManager; import dev.isxander.controlify.platform.client.PlatformClientUtil; import dev.isxander.controlify.platform.main.PlatformMainUtil; import dev.isxander.controlify.platform.network.SidedNetworkApi; @@ -93,6 +94,8 @@ public class Controlify implements ControlifyApi { private DefaultConfigManager defaultConfigManager; private ControllerTypeManager controllerTypeManager; private KeyboardLayoutManager keyboardLayoutManager; + private RadialIconManager radialIconManager; + private ActivatorConfigManager activatorConfigManager; private TriggerEffectManager triggerEffectManager; @@ -139,13 +142,16 @@ public void preInitialiseControlify() { this.defaultConfigManager = new DefaultConfigManager(); this.controllerTypeManager = new ControllerTypeManager(); this.keyboardLayoutManager = new KeyboardLayoutManager(); - - PlatformClientUtil.registerAssetReloadListener(inputFontMapper); - PlatformClientUtil.registerAssetReloadListener(defaultBindManager); - PlatformClientUtil.registerAssetReloadListener(defaultConfigManager); - PlatformClientUtil.registerAssetReloadListener(controllerTypeManager); - PlatformClientUtil.registerAssetReloadListener(keyboardLayoutManager); - PlatformClientUtil.registerAssetReloadListener(RadialIconManager.INSTANCE); + this.radialIconManager = new RadialIconManager(); + this.activatorConfigManager = new ActivatorConfigManager(); + + PlatformClientUtil.registerAssetReloadListener(this.inputFontMapper); + PlatformClientUtil.registerAssetReloadListener(this.defaultBindManager); + PlatformClientUtil.registerAssetReloadListener(this.defaultConfigManager); + PlatformClientUtil.registerAssetReloadListener(this.controllerTypeManager); + PlatformClientUtil.registerAssetReloadListener(this.keyboardLayoutManager); + PlatformClientUtil.registerAssetReloadListener(this.radialIconManager); + PlatformClientUtil.registerAssetReloadListener(this.activatorConfigManager); PlatformClientUtil.registerAssetReloadListener(RuleSetManager.GUIDE_RULES); PlatformClientUtil.registerAssetReloadListener(RuleSetManager.TRIGGER_EFFECT_RULES); ContextualDomains.touch(); @@ -756,27 +762,35 @@ private void restoreVirtualCursor() { } public InputFontMapper inputFontMapper() { - return inputFontMapper; + return this.inputFontMapper; } public DefaultBindManager defaultBindManager() { - return defaultBindManager; + return this.defaultBindManager; } public DefaultConfigManager defaultConfigManager() { - return defaultConfigManager; + return this.defaultConfigManager; } public ControllerTypeManager controllerTypeManager() { - return controllerTypeManager; + return this.controllerTypeManager; } public KeyboardLayoutManager keyboardLayoutManager() { - return keyboardLayoutManager; + return this.keyboardLayoutManager; + } + + public RadialIconManager radialIconManager() { + return this.radialIconManager; + } + + public ActivatorConfigManager activatorConfigManager() { + return this.activatorConfigManager; } public TriggerEffectManager triggerEffectManager() { - return triggerEffectManager; + return this.triggerEffectManager; } public Set thisTickBindContexts() { diff --git a/src/main/java/dev/isxander/controlify/bindings/InputBindingBuilderImpl.java b/src/main/java/dev/isxander/controlify/bindings/InputBindingBuilderImpl.java index e4f9c7634..063f68487 100644 --- a/src/main/java/dev/isxander/controlify/bindings/InputBindingBuilderImpl.java +++ b/src/main/java/dev/isxander/controlify/bindings/InputBindingBuilderImpl.java @@ -145,7 +145,7 @@ public InputBindingImpl build(@Nullable ControllerEntity controller) { controllerType ); - Input input = provider.getDefaultBind(id); + Input input = provider.getDefaultInput(id); if (input == null) input = defaultInput; if (input == null) input = EmptyInput.INSTANCE; diff --git a/src/main/java/dev/isxander/controlify/bindings/RadialIconManager.java b/src/main/java/dev/isxander/controlify/bindings/RadialIconManager.java index 62537bac9..d6e86dda1 100644 --- a/src/main/java/dev/isxander/controlify/bindings/RadialIconManager.java +++ b/src/main/java/dev/isxander/controlify/bindings/RadialIconManager.java @@ -25,8 +25,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; -public final class RadialIconManager implements SimpleControlifyReloadListener> { - public static final RadialIconManager INSTANCE = new RadialIconManager(); +public class RadialIconManager implements SimpleControlifyReloadListener> { public static final Identifier EMPTY = CUtil.rl("empty"); private static final Identifier DEFINITIONS = CUtil.rl("radial_icons.json"); @@ -82,7 +81,4 @@ public CompletableFuture apply(Map data, ResourceM public Identifier getReloadId() { return CUtil.rl("radial_icons"); } - - private RadialIconManager() { - } } diff --git a/src/main/java/dev/isxander/controlify/bindings/defaults/DefaultBindProvider.java b/src/main/java/dev/isxander/controlify/bindings/defaults/DefaultBindProvider.java index ff97f57a7..dfcb82a0a 100644 --- a/src/main/java/dev/isxander/controlify/bindings/defaults/DefaultBindProvider.java +++ b/src/main/java/dev/isxander/controlify/bindings/defaults/DefaultBindProvider.java @@ -6,12 +6,12 @@ */ package dev.isxander.controlify.bindings.defaults; -import dev.isxander.controlify.bindings.input.Input; +import dev.isxander.controlify.input.logical.LogicalInput; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; public interface DefaultBindProvider { - @Nullable Input getDefaultBind(Identifier bindId); + @Nullable LogicalInput getDefaultInput(Identifier actionId); DefaultBindProvider EMPTY = bind -> null; } diff --git a/src/main/java/dev/isxander/controlify/bindings/defaults/LayeredDefaultBindProvider.java b/src/main/java/dev/isxander/controlify/bindings/defaults/LayeredDefaultBindProvider.java index 9271dbd06..3ee8345f1 100644 --- a/src/main/java/dev/isxander/controlify/bindings/defaults/LayeredDefaultBindProvider.java +++ b/src/main/java/dev/isxander/controlify/bindings/defaults/LayeredDefaultBindProvider.java @@ -8,7 +8,7 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.bindings.input.Input; +import dev.isxander.controlify.input.logical.LogicalInput; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; @@ -24,9 +24,9 @@ public static DefaultBindProvider of(Layer... layers) { } @Override - @Nullable public Input getDefaultBind(Identifier bindId) { + @Nullable public LogicalInput getDefaultInput(Identifier actionId) { for (var layer : layers()) { - Input input = layer.provider().getDefaultBind(bindId); + var input = layer.provider().getDefaultInput(actionId); if (input != null) return input; if (layer.clearBelow()) return null; } diff --git a/src/main/java/dev/isxander/controlify/bindings/defaults/MapBackedDefaultBindProvider.java b/src/main/java/dev/isxander/controlify/bindings/defaults/MapBackedDefaultBindProvider.java index 229c59cde..238aff79d 100644 --- a/src/main/java/dev/isxander/controlify/bindings/defaults/MapBackedDefaultBindProvider.java +++ b/src/main/java/dev/isxander/controlify/bindings/defaults/MapBackedDefaultBindProvider.java @@ -10,20 +10,20 @@ import com.mojang.serialization.Keyable; import com.mojang.serialization.MapCodec; import dev.isxander.controlify.api.bind.ControlifyBindApi; -import dev.isxander.controlify.bindings.input.Input; +import dev.isxander.controlify.input.logical.LogicalInput; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; import java.util.Map; -public record MapBackedDefaultBindProvider(Map map) implements DefaultBindProvider { +public record MapBackedDefaultBindProvider(Map map) implements DefaultBindProvider { public static final MapCodec MAP_CODEC = Codec.simpleMap( - Identifier.CODEC, Input.CODEC, + Identifier.CODEC, LogicalInput.CODEC, Keyable.forStrings(() -> ControlifyBindApi.get().getAllBindIds().map(Identifier::toString)) ).xmap(MapBackedDefaultBindProvider::new, MapBackedDefaultBindProvider::map); @Override - public @Nullable Input getDefaultBind(Identifier bindId) { - return map.get(bindId); + public @Nullable LogicalInput getDefaultInput(Identifier actionId) { + return map.get(actionId); } } diff --git a/src/main/java/dev/isxander/controlify/config/dto/profile/InputConfig.java b/src/main/java/dev/isxander/controlify/config/dto/profile/InputConfig.java index dfab3b6ea..fb5fd7e32 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/profile/InputConfig.java +++ b/src/main/java/dev/isxander/controlify/config/dto/profile/InputConfig.java @@ -8,8 +8,8 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.bindings.input.Input; import dev.isxander.controlify.ingame.InputCurves; +import dev.isxander.controlify.input.logical.LogicalInput; import net.minecraft.resources.Identifier; import java.util.List; @@ -31,11 +31,11 @@ public record InputConfig( ).apply(instance, InputConfig::new)); public record BindingsConfig( - Map bindings + Map bindings ) { public static final BindingsConfig EMPTY = new BindingsConfig(Map.of()); - public static final Codec CODEC = Codec.unboundedMap(Identifier.CODEC, Input.CODEC) + public static final Codec CODEC = Codec.unboundedMap(Identifier.CODEC, LogicalInput.CODEC) .xmap(BindingsConfig::new, BindingsConfig::bindings); public BindingsConfig { diff --git a/src/main/java/dev/isxander/controlify/config/settings/profile/InputSettings.java b/src/main/java/dev/isxander/controlify/config/settings/profile/InputSettings.java index cf91cf920..c113f89ea 100644 --- a/src/main/java/dev/isxander/controlify/config/settings/profile/InputSettings.java +++ b/src/main/java/dev/isxander/controlify/config/settings/profile/InputSettings.java @@ -6,9 +6,9 @@ */ package dev.isxander.controlify.config.settings.profile; -import dev.isxander.controlify.bindings.input.Input; import dev.isxander.controlify.config.dto.profile.InputConfig; import dev.isxander.controlify.ingame.InputCurves; +import dev.isxander.controlify.input.logical.LogicalInput; import net.minecraft.resources.Identifier; import java.util.HashMap; @@ -59,9 +59,9 @@ public InputConfig toDTO() { } public static class BindingsSettings { - public Map bindings; + public Map bindings; - public BindingsSettings(Map bindings) { + public BindingsSettings(Map bindings) { this.bindings = new HashMap<>(bindings); } diff --git a/src/main/java/dev/isxander/controlify/gui/screen/RadialItems.java b/src/main/java/dev/isxander/controlify/gui/screen/RadialItems.java index 2436f1db5..477c5d82b 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/RadialItems.java +++ b/src/main/java/dev/isxander/controlify/gui/screen/RadialItems.java @@ -6,6 +6,7 @@ */ package dev.isxander.controlify.gui.screen; +import dev.isxander.controlify.Controlify; import dev.isxander.controlify.api.bind.InputBinding; import dev.isxander.controlify.api.bind.InputBindingSupplier; import dev.isxander.controlify.api.bind.RadialIcon; @@ -181,15 +182,16 @@ private static RadialIcon getIconForItem(ItemStack stack) { private static RadialMenuScreen.RadialItem getItemForBinding(Identifier id, ControllerEntity controller) { InputBinding binding = controller.input().orElseThrow().getBinding(id); + RadialIconManager radialIconManager = Controlify.instance().radialIconManager(); - if (binding == null || !RadialIconManager.INSTANCE.isRadialCandidate(binding)) { + if (binding == null || !radialIconManager.isRadialCandidate(binding)) { CUtil.LOGGER.warn("Binding {} does not exist or is not a radial candidate", binding); return EMPTY_ACTION; } return new RadialItemRecord( binding.name(), - RadialIconManager.INSTANCE.getIcon(binding), + radialIconManager.getIcon(binding), () -> { binding.fakePress(); return true; 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 bca14447d..ab306dfcc 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuEditScreen.java +++ b/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuEditScreen.java @@ -82,7 +82,7 @@ public RadialMenuEditScreen(Screen parent, ControllerEntity controller) { this.controller = controller; this.model = new RadialMenuEditModel(controller.settings().input.radialMenu.radialActions); this.candidates = controller.input().orElseThrow().getAllBindings().stream() - .filter(RadialIconManager.INSTANCE::isRadialCandidate) + .filter(Controlify.instance().radialIconManager()::isRadialCandidate) .toList(); this.candidatesById = candidates.stream().collect(Collectors.toMap( InputBinding::id, @@ -548,7 +548,7 @@ private ActionEntry(Identifier id, Component name, boolean equipped, int positio InputBinding binding = candidatesById.get(id); this.icon = binding == null ? RadialIcon.EMPTY - : RadialIconManager.INSTANCE.getIcon(binding); + : Controlify.instance().radialIconManager().getIcon(binding); } @Override diff --git a/src/main/java/dev/isxander/controlify/input/InputComponent.java b/src/main/java/dev/isxander/controlify/input/InputComponent.java new file mode 100644 index 000000000..9fe15db21 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/InputComponent.java @@ -0,0 +1,173 @@ +package dev.isxander.controlify.input; + +import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.bindings.defaults.DefaultBindProvider; +import dev.isxander.controlify.config.settings.device.DeviceSettings; +import dev.isxander.controlify.config.settings.profile.InputSettings; +import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.controller.impl.ECSComponentImpl; +import dev.isxander.controlify.controller.input.DeadzoneGroup; +import dev.isxander.controlify.input.action.ActionGraph; +import dev.isxander.controlify.input.action.ActionImpl; +import dev.isxander.controlify.input.action.ActionSpec; +import dev.isxander.controlify.input.action.ActionSpecRegistryImpl; +import dev.isxander.controlify.input.action.activator.ActivatorConfig; +import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.physical.SensorProcessorStage; +import dev.isxander.controlify.utils.CUtil; +import net.minecraft.resources.Identifier; +import net.minecraft.util.Util; +import org.jspecify.annotations.Nullable; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class InputComponent extends ECSComponentImpl { + public static final Identifier ID = CUtil.rl("input"); + + private final Identifier controllerType; + private DeviceSettings deviceSettings; + + private final InputPipeline inputPipeline; + private final ActionGraph actionGraph; + + private final Map actions; + + private final Set physicalButtons, physicalAxes; + private final Map deadzoneGroups; + private final boolean isGamepad; + private final Set supportedSensors; + + public InputComponent( + Identifier controllerType, + InputPipeline inputPipeline, + Set physicalButtons, + Set physicalAxes, + Set deadzoneGroups, + boolean isGamepad, + Set supportedSensors + ) { + this.controllerType = controllerType; + this.inputPipeline = inputPipeline; + + this.physicalButtons = Set.copyOf(physicalButtons); + this.physicalAxes = Set.copyOf(physicalAxes); + this.deadzoneGroups = deadzoneGroups.stream() + .flatMap(g -> g.axes().stream().map(a -> Map.entry(a, g))) + .collect(Util.toMap()); + this.isGamepad = isGamepad; + this.supportedSensors = Set.copyOf(supportedSensors); + + this.actionGraph = new ActionGraph(); + this.actions = new LinkedHashMap<>(); + } + + @Override + public void attach(ControllerEntity controller) { + super.attach(controller); + + this.deviceSettings = Controlify.instance().config().getSettings() + .getOrCreateDeviceSettings(controller.uid()); + + // we need to wait for settings to load before getting current binding + this.reloadActions(); + + this.inputPipeline.sourceLogicalSignals().subscribe(this.actionGraph); + this.inputPipeline.stageDeadzone().setDeadzoneSupplier(axis -> { + DeadzoneGroup group = this.deadzoneGroups.get(axis); + if (group == null) { + return 0f; + } + return this.controller().settings().input.sensitivity.getDeadzone(group.name()); + }); + this.inputPipeline.stageSensorProcessor().setProcessors(sensorType -> { + if (sensorType.isGyroscope()) { + return List.of( + new SensorProcessorStage.BiasCorrection(() -> this.deviceSettings.gyroCalibration.offset), + new SensorProcessorStage.LowPassFilter(() -> 15.0) // TODO: make this configurable + ); + } else { + return List.of(); + } + }); + } + + public @Nullable ActionImpl getAction(Identifier actionId) { + return this.actions.get(actionId); + } + + public void reloadActions() { + this.actions.clear(); + this.actionGraph.clearActions(); + + for (ActionSpec spec : ActionSpecRegistryImpl.INSTANCE.getActionSpecs()) { + ActionImpl action = this.createAction(spec); + this.actions.put(spec.id(), action); + this.actionGraph.addAction(action); + } + } + + public void onGuiNavigate() { + this.inputPipeline.stagePhysicalToLogical().onGuiNavigate(); + } + + @Override + public Identifier id() { + return ID; + } + + public InputSettings settings() { + return this.controller().settings().input; + } + + public InputSettings defaultSettings() { + return this.controller().defaultSettings().input; + } + + public boolean isGamepad() { + return this.isGamepad; + } + + public Set physicalButtons() { + return this.physicalButtons; + } + + public Set physicalAxes() { + return this.physicalAxes; + } + + public boolean supportsSensor(SensorType type) { + return this.supportedSensors.contains(type); + } + + public boolean supportsGyro() { + return this.supportedSensors.stream().anyMatch(SensorType::isGyroscope); + } + + public boolean supportsAccelerometer() { + return this.supportedSensors.stream().anyMatch(SensorType::isAccelerometer); + } + + private ActionImpl createAction(ActionSpec spec) { + DefaultBindProvider bindProvider = Controlify.instance().defaultBindManager() + .getDefaultBindProvider(this.controllerType); + LogicalInput defaultBoundInput = bindProvider.getDefaultInput(spec.id()); + + LogicalInput currentBoundInput = settings().bindings.bindings.get(spec.id()); + + ActivatorConfig activatorConfig = Controlify.instance().activatorConfigManager().getConfig(spec.id()); + + ActionImpl action = new ActionImpl( + spec, + currentBoundInput, + defaultBoundInput, + activatorConfig, + this.actionGraph + ); + action.reset(); + + return action; + } +} diff --git a/src/main/java/dev/isxander/controlify/input/InputPipeline.java b/src/main/java/dev/isxander/controlify/input/InputPipeline.java new file mode 100644 index 000000000..faf952ab7 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/InputPipeline.java @@ -0,0 +1,87 @@ +package dev.isxander.controlify.input; + +import dev.isxander.controlify.input.logical.LogicalSignal; +import dev.isxander.controlify.input.logical.PhysicalToLogicalStage; +import dev.isxander.controlify.input.logical.SensorAccumulatorStage; +import dev.isxander.controlify.input.physical.*; +import dev.isxander.controlify.utils.event.EventSink; +import dev.isxander.controlify.utils.event.EventSource; +import dev.isxander.controlify.utils.event.EventTap; + +public class InputPipeline implements EventSink { + private final EventTap tapRawPhysicalSignals; + private final SensorProcessorStage stageSensorProcessor; + private final DeadzoneStage stageDeadzone; + private final GestureRecogniserStage stageGestureRecogniser; + private final InputDomainExpansionStage stageInputDomainExpansion; + private final EventTap tapProcessedPhysicalSignals; + + private final SensorAccumulatorStage stageSensorAccumulator; + private final PhysicalToLogicalStage stagePhysicalToLogical; + private final EventTap tapLogicalSignals; + + private final EventSink fullPipeline; + + public InputPipeline() { + this.tapRawPhysicalSignals = new EventTap<>(); + this.stageSensorProcessor = new SensorProcessorStage(); + this.stageDeadzone = new DeadzoneStage(); + this.stageGestureRecogniser = new GestureRecogniserStage(); + this.stageInputDomainExpansion = new InputDomainExpansionStage(0.5f, 0.49f, 1f, 0f); + this.tapProcessedPhysicalSignals = new EventTap<>(); + this.stageSensorAccumulator = new SensorAccumulatorStage(); + this.stagePhysicalToLogical = new PhysicalToLogicalStage(this.stageSensorAccumulator); + this.tapLogicalSignals = new EventTap<>(); + + this.fullPipeline = this.tapRawPhysicalSignals + .andThen(this.stageSensorProcessor) + .andThen(this.stageDeadzone) + .andThen(this.stageGestureRecogniser) + .andThen(this.stageInputDomainExpansion) + .andThen(this.tapProcessedPhysicalSignals) + .andThen(this.stagePhysicalToLogical) + .andThen(this.tapLogicalSignals) + .discardDownstream(); + } + + @Override + public void accept(PhysicalSignal event) { + this.fullPipeline.accept(event); + } + + public EventSource sourceRawPhysicalSignals() { + return this.tapRawPhysicalSignals; + } + + public SensorProcessorStage stageSensorProcessor() { + return this.stageSensorProcessor; + } + + public DeadzoneStage stageDeadzone() { + return this.stageDeadzone; + } + + public GestureRecogniserStage stageGestureRecogniser() { + return this.stageGestureRecogniser; + } + + public InputDomainExpansionStage stageInputDomainExpansion() { + return this.stageInputDomainExpansion; + } + + public EventSource sourceProcessedPhysicalSignals() { + return this.tapProcessedPhysicalSignals; + } + + public SensorAccumulatorStage stageSensorAccumulator() { + return this.stageSensorAccumulator; + } + + public PhysicalToLogicalStage stagePhysicalToLogical() { + return this.stagePhysicalToLogical; + } + + public EventSource sourceLogicalSignals() { + return this.tapLogicalSignals; + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/Action.java b/src/main/java/dev/isxander/controlify/input/action/Action.java index 833d2a6c0..fb6ec58af 100644 --- a/src/main/java/dev/isxander/controlify/input/action/Action.java +++ b/src/main/java/dev/isxander/controlify/input/action/Action.java @@ -1,21 +1,17 @@ package dev.isxander.controlify.input.action; import dev.isxander.controlify.input.logical.LogicalInput; -import net.minecraft.network.chat.Component; import org.jspecify.annotations.Nullable; /// An action is bound to a logical input and has one or more activators /// that determine how its output channels respond to input signals. public interface Action { - ActionSpec spec(); Channel.Pulse pulse(); Channel.Latch latch(); Channel.Continuous continuous(); - Component inputGlyph(); - void setBoundInput(@Nullable LogicalInput input); @Nullable LogicalInput boundInput(); diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionAccessor.java b/src/main/java/dev/isxander/controlify/input/action/ActionAccessor.java index 040ba8eda..366a6575c 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionAccessor.java +++ b/src/main/java/dev/isxander/controlify/input/action/ActionAccessor.java @@ -1,7 +1,7 @@ package dev.isxander.controlify.input.action; import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.input.InputComponent; +import dev.isxander.controlify.input.InputComponent; import net.minecraft.resources.Identifier; import org.jspecify.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionAccessorImpl.java b/src/main/java/dev/isxander/controlify/input/action/ActionAccessorImpl.java new file mode 100644 index 000000000..db1419c9a --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/ActionAccessorImpl.java @@ -0,0 +1,12 @@ +package dev.isxander.controlify.input.action; + +import dev.isxander.controlify.input.InputComponent; +import net.minecraft.resources.Identifier; +import org.jspecify.annotations.Nullable; + +public record ActionAccessorImpl(Identifier actionId) implements ActionAccessor { + @Override + public @Nullable Action onOrNull(InputComponent inputComponent) { + return inputComponent.getAction(this.actionId); + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionContext.java b/src/main/java/dev/isxander/controlify/input/action/ActionContext.java new file mode 100644 index 000000000..477527a15 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/ActionContext.java @@ -0,0 +1,11 @@ +package dev.isxander.controlify.input.action; + +import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; + +public record ActionContext(Identifier id) implements Comparable { + @Override + public int compareTo(@NonNull ActionContext o) { + return this.id().compareTo(o.id()); + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionGraph.java b/src/main/java/dev/isxander/controlify/input/action/ActionGraph.java index 6afe890fd..4c76b1161 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionGraph.java +++ b/src/main/java/dev/isxander/controlify/input/action/ActionGraph.java @@ -8,6 +8,7 @@ import java.util.*; public class ActionGraph implements EventSink { + // use a sorted set to have deterministic ordering private final Set allActions = new TreeSet<>(); // Context ID -> Logical Input -> set of ActionImpl @@ -16,8 +17,14 @@ public class ActionGraph implements EventSink { // by avoiding the need to iterate over all bindings for each signal private final Map>> graph = new HashMap<>(); + private final Set currentContexts = new TreeSet<>(); + // mutable set to avoid set allocation on every signal + private final Set dispatchCandidates = new TreeSet<>(); + public void addAction(ActionImpl action) { - this.allActions.add(action); + if (!this.allActions.add(action)) { + throw new IllegalArgumentException("This action has already been added"); + } for (var contextId : action.spec().contexts()) { var byInputMap = this.graph.computeIfAbsent(contextId, _ -> new HashMap<>()); byInputMap.computeIfAbsent(action.boundInput(), _ -> new TreeSet<>()) @@ -35,6 +42,33 @@ public void removeAction(ActionImpl action) { this.allActions.remove(action); } + public void clearActions() { + this.allActions.clear(); + this.graph.clear(); + } + + public void onActionBindingChange(ActionImpl action) { + // re-indexes the action in the graph + this.removeAction(action); + this.addAction(action); + } + + public void setCurrentContexts(Set contexts) { + // Reset only actions that lose their last active context. + for (ActionImpl action : this.allActions) { + boolean wasActive = this.currentContexts.stream() + .anyMatch(context -> action.spec().contexts().contains(context.id())); + boolean isActive = contexts.stream() + .anyMatch(context -> action.spec().contexts().contains(context.id())); + if (wasActive && !isActive) { + action.reset(); + } + } + + this.currentContexts.clear(); + this.currentContexts.addAll(contexts); + } + @Override public void accept(LogicalSignal event) { switch (event) { @@ -51,6 +85,28 @@ private void processTick(LogicalSignal.Tick tick) { } private void processInput(LogicalSignal.InputSignal inputSignal) { + this.dispatchCandidates.clear(); + + // collect all actions monitoring this logical input in the current context + // actions in multiple contexts are automatically deduplicated by the set + for (ActionContext context : this.currentContexts) { + Map> byInput = this.graph.get(context.id()); + + if (byInput == null) { + continue; + } + + Set actionSet = byInput.get(inputSignal.input()); + if (actionSet == null) { + continue; + } + + this.dispatchCandidates.addAll(actionSet); + } + + for (ActionImpl action : this.dispatchCandidates) { + action.activator().onSignal(inputSignal, action.state()); + } } } diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionImpl.java b/src/main/java/dev/isxander/controlify/input/action/ActionImpl.java index eed9128a8..317369bc1 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionImpl.java +++ b/src/main/java/dev/isxander/controlify/input/action/ActionImpl.java @@ -14,17 +14,26 @@ public class ActionImpl implements Action, Comparable { private Activator activator; private final ActivatorConfig activatorConfig; + private final ActionGraph actionGraph; + public ActionImpl( ActionSpec spec, @Nullable LogicalInput boundInput, @Nullable LogicalInput defaultInput, - ActivatorConfig activator + ActivatorConfig activator, + ActionGraph actionGraph ) { this.spec = spec; this.defaultInput = defaultInput; this.activatorConfig = activator; - this.setBoundInput(boundInput); this.state = new ActionState(); + this.actionGraph = actionGraph; + this.boundInput = boundInput; + } + + @Override + public ActionSpec spec() { + return this.spec; } @Override @@ -35,9 +44,35 @@ public ActionImpl( @Override public void setBoundInput(@Nullable LogicalInput boundInput) { this.boundInput = boundInput; - this.activator = boundInput != null - ? this.activatorConfig.create(boundInput) + this.reset(); + this.actionGraph.onActionBindingChange(this); + } + + @Override + public @Nullable LogicalInput defaultInput() { + return this.defaultInput; + } + + @Override + public Channel.Pulse pulse() { + return this.state(); + } + + @Override + public Channel.Latch latch() { + return this.state(); + } + + @Override + public Channel.Continuous continuous() { + return this.state(); + } + + public void reset() { + this.activator = this.boundInput() != null + ? this.activatorConfig.create(this.boundInput()) : new NoopActivator(); + this.state.reset(); } public ActionState state() { diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionSpec.java b/src/main/java/dev/isxander/controlify/input/action/ActionSpec.java index 7da547c3f..44b31a138 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionSpec.java +++ b/src/main/java/dev/isxander/controlify/input/action/ActionSpec.java @@ -1,22 +1,37 @@ package dev.isxander.controlify.input.action; +import dev.isxander.controlify.controller.ControllerEntity; import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; import org.jspecify.annotations.NonNull; import java.util.Comparator; import java.util.Set; +import java.util.function.Predicate; +/// Immutable information about an {@link Action}. +/// @param id the unique identifier for the action +/// @param name the display name for the action +/// @param description the display description for the action +/// @param category the category for the action +/// @param contexts the set of context IDs that this action should work in +/// @param shouldExpose predicate deciding if the action should be visible in the settings and available +/// to bind for the current controller public record ActionSpec( Identifier id, Component name, Component description, Component category, - Set contexts + Set contexts, + Predicate shouldExpose ) implements Comparable { @Override public int compareTo(@NonNull ActionSpec o) { return Comparator.comparing(ActionSpec::id) .compare(this, o); } + + public static ActionSpecBuilder builder() { + return new ActionSpecBuilderImpl(); + } } diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionSpecBuilder.java b/src/main/java/dev/isxander/controlify/input/action/ActionSpecBuilder.java index f343f82e8..631bf5290 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionSpecBuilder.java +++ b/src/main/java/dev/isxander/controlify/input/action/ActionSpecBuilder.java @@ -1,20 +1,23 @@ package dev.isxander.controlify.input.action; +import dev.isxander.controlify.controller.ControllerEntity; import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; +import java.util.function.Predicate; + public interface ActionSpecBuilder { ActionSpecBuilder id(Identifier id); ActionSpecBuilder name(Component name); - ActionSpecBuilder description(Component name); + ActionSpecBuilder description(Component description); ActionSpecBuilder category(Component category); ActionSpecBuilder context(Identifier... contexts); - ActionSpec build(); + ActionSpecBuilder shouldExpose(Predicate predicate); - ActionAccessor buildAndRegister(ActionSpecRegistry registry); + ActionSpec build(); } diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionSpecBuilderImpl.java b/src/main/java/dev/isxander/controlify/input/action/ActionSpecBuilderImpl.java new file mode 100644 index 000000000..e653e1ecb --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/ActionSpecBuilderImpl.java @@ -0,0 +1,76 @@ +package dev.isxander.controlify.input.action; + +import dev.isxander.controlify.controller.ControllerEntity; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.Identifier; +import org.apache.commons.lang3.Validate; +import org.jspecify.annotations.Nullable; + +import java.util.*; +import java.util.function.Predicate; + +public class ActionSpecBuilderImpl implements ActionSpecBuilder { + private @Nullable Identifier id; + private @Nullable Component name; + private @Nullable Component description; + private @Nullable Component category; + private final Set contexts = new TreeSet<>(); + private Predicate shouldExpose = _ -> true; + + @Override + public ActionSpecBuilder id(Identifier id) { + this.id = id; + return this; + } + + @Override + public ActionSpecBuilder name(Component name) { + this.name = name; + return this; + } + + @Override + public ActionSpecBuilder description(Component description) { + this.description = description; + return this; + } + + @Override + public ActionSpecBuilder category(Component category) { + this.category = category; + return this; + } + + @Override + public ActionSpecBuilder context(Identifier... contexts) { + this.contexts.addAll(List.of(contexts)); + return this; + } + + @Override + public ActionSpecBuilder shouldExpose(Predicate predicate) { + this.shouldExpose = predicate; + return this; + } + + @Override + public ActionSpec build() { + Validate.notNull(this.id, "An action spec needs an id"); + Validate.notNull(this.category, "An action spec needs a category"); + Validate.isTrue(!this.contexts.isEmpty(), "An action spec needs at least one context"); + + Component name = Optional.ofNullable(this.name) + .orElseGet(() -> Component.translatable(this.id.toLanguageKey("controlify.action"))); + Component description = Optional.ofNullable(this.description) + .orElseGet(() -> Component.translatable(this.id.toLanguageKey("controlify.action", "desc"))); + + return new ActionSpec( + this.id, + name, + description, + this.category, + Collections.unmodifiableSet(this.contexts), + this.shouldExpose + ); + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionSpecRegistry.java b/src/main/java/dev/isxander/controlify/input/action/ActionSpecRegistry.java index aa1753630..26101ca60 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionSpecRegistry.java +++ b/src/main/java/dev/isxander/controlify/input/action/ActionSpecRegistry.java @@ -1,5 +1,5 @@ package dev.isxander.controlify.input.action; public interface ActionSpecRegistry { - void register(ActionSpec actionSpec); + ActionAccessor register(ActionSpec actionSpec); } diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionSpecRegistryImpl.java b/src/main/java/dev/isxander/controlify/input/action/ActionSpecRegistryImpl.java new file mode 100644 index 000000000..ec5104397 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/ActionSpecRegistryImpl.java @@ -0,0 +1,42 @@ +package dev.isxander.controlify.input.action; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ActionSpecRegistryImpl implements ActionSpecRegistry { + public static final ActionSpecRegistryImpl INSTANCE = new ActionSpecRegistryImpl(); + + private final List registeredSpecs = new ArrayList<>(); + + private boolean locked = false; + + @Override + public ActionAccessor register(ActionSpec actionSpec) { + this.checkLocked(false); + this.registeredSpecs.add(actionSpec); + + return new ActionAccessorImpl(actionSpec.id()); + } + + public List getActionSpecs() { + this.checkLocked(true); + return Collections.unmodifiableList(this.registeredSpecs); + } + + public void lock() { + this.locked = true; + } + + private void checkLocked(boolean required) { + if (required) { + if (!this.locked) { + throw new IllegalStateException("Registry must be locked to perform this operation."); + } + } else { + if (this.locked) { + throw new IllegalStateException("Registry must be unlocked to perform this operation."); + } + } + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionState.java b/src/main/java/dev/isxander/controlify/input/action/ActionState.java index 1a248b5e5..159457849 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionState.java +++ b/src/main/java/dev/isxander/controlify/input/action/ActionState.java @@ -43,4 +43,10 @@ public void firePulse() { public void setContinuous(float value) { this.continuousValue = Math.clamp(value, 0f, 1f); } + + public void reset() { + this.pulses = 0; + this.latchActive = false; + this.continuousValue = 0f; + } } diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfig.java b/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfig.java index cf4adc412..189aa34a8 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfig.java +++ b/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfig.java @@ -1,6 +1,7 @@ package dev.isxander.controlify.input.action.activator; import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; import dev.isxander.controlify.input.action.Activator; import dev.isxander.controlify.input.logical.LogicalInput; import net.minecraft.util.StringRepresentable; @@ -13,6 +14,18 @@ public interface ActivatorConfig { ActivatorConfigType type(); - Codec> CODEC = StringRepresentable.fromValues(() -> ActivatorConfigType.TYPES) - .dispatch("type", ActivatorConfig::type, ActivatorConfigType::mapCodec); + Codec> CODEC = Codec.recursive("ActivatorConfig", self -> { + Codec> typeCodec = StringRepresentable.fromValues(() -> ActivatorConfigType.TYPES); + Codec> fullCodec = typeCodec.dispatch("type", ActivatorConfig::type, ActivatorConfigType::mapCodec); + Codec> shorthandCodec = typeCodec.flatXmap( + type -> type.unitConfig() + .>>map(DataResult::success) + .orElseGet(() -> DataResult.error(() -> "Activator type '" + type.id() + "' requires an object configuration")), + config -> DataResult.success(config.type()) + ); + + // Alternatives are accepted when decoding; encoding always uses the full object form. + return fullCodec.withAlternative(shorthandCodec) + .withAlternative(self.listOf(), CompoundActivator.Config::new); + }); } diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfigType.java b/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfigType.java index 8cae0e58f..4f2bd4809 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfigType.java +++ b/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfigType.java @@ -5,39 +5,50 @@ import net.minecraft.util.StringRepresentable; import org.jetbrains.annotations.NotNull; +import java.util.Optional; + public record ActivatorConfigType>( String id, - MapCodec mapCodec + MapCodec mapCodec, + Optional unitConfig ) implements StringRepresentable { + public ActivatorConfigType(String id, MapCodec mapCodec) { + this(id, mapCodec, Optional.empty()); + } + + public ActivatorConfigType(String id, C unitConfig) { + this(id, MapCodec.unit(unitConfig), Optional.of(unitConfig)); + } + public static final ActivatorConfigType COMPOUND = new ActivatorConfigType<>(CompoundActivator.Config.TYPE_ID, CompoundActivator.Config.MAP_CODEC); public static final ActivatorConfigType CONTINUOUS = - new ActivatorConfigType<>(ContinuousActivator.Config.TYPE_ID, ContinuousActivator.Config.MAP_CODEC); + new ActivatorConfigType<>(ContinuousActivator.Config.TYPE_ID, ContinuousActivator.Config.INSTANCE); public static final ActivatorConfigType HOLD_LATCH = - new ActivatorConfigType<>(HoldLatchActivator.Config.TYPE_ID, HoldLatchActivator.Config.MAP_CODEC); + new ActivatorConfigType<>(HoldLatchActivator.Config.TYPE_ID, HoldLatchActivator.Config.INSTANCE); public static final ActivatorConfigType PRESS_PULSE = - new ActivatorConfigType<>(PressPulseActivator.Config.TYPE_ID, PressPulseActivator.Config.MAP_CODEC); + new ActivatorConfigType<>(PressPulseActivator.Config.TYPE_ID, PressPulseActivator.Config.INSTANCE); public static final ActivatorConfigType RELEASE_PULSE = - new ActivatorConfigType<>(ReleasePulseActivator.Config.TYPE_ID, ReleasePulseActivator.Config.MAP_CODEC); + new ActivatorConfigType<>(ReleasePulseActivator.Config.TYPE_ID, ReleasePulseActivator.Config.INSTANCE); public static final ActivatorConfigType REPEAT_LATCH_PULSE = new ActivatorConfigType<>(RepeatLatchPulseActivator.Config.TYPE_ID, RepeatLatchPulseActivator.Config.MAP_CODEC); public static final ActivatorConfigType TAP_PULSE = - new ActivatorConfigType<>(TapPulseActivator.Config.TYPE_ID, TapPulseActivator.Config.MAP_CODEC); + new ActivatorConfigType<>(TapPulseActivator.Config.TYPE_ID, TapPulseActivator.Config.INSTANCE); public static final ActivatorConfigType DOUBLE_TAP_PULSE = - new ActivatorConfigType<>(DoubleTapPulseActivator.Config.TYPE_ID, DoubleTapPulseActivator.Config.MAP_CODEC); + new ActivatorConfigType<>(DoubleTapPulseActivator.Config.TYPE_ID, DoubleTapPulseActivator.Config.INSTANCE); public static final ActivatorConfigType GUI_PRESS_PULSE = - new ActivatorConfigType<>(GuiPressPulseActivator.Config.TYPE_ID, GuiPressPulseActivator.Config.MAP_CODEC); + new ActivatorConfigType<>(GuiPressPulseActivator.Config.TYPE_ID, GuiPressPulseActivator.Config.INSTANCE); public static final ActivatorConfigType HOLD_PULSE = - new ActivatorConfigType<>(HoldPulseActivator.Config.TYPE_ID, HoldPulseActivator.Config.MAP_CODEC); + new ActivatorConfigType<>(HoldPulseActivator.Config.TYPE_ID, HoldPulseActivator.Config.INSTANCE); public static final ActivatorConfigType TOGGLE_LATCH = new ActivatorConfigType<>(ToggleLatchActivator.Config.TYPE_ID, ToggleLatchActivator.Config.MAP_CODEC); diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ContinuousActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/ContinuousActivator.java index 31d67366d..503e71b17 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/ContinuousActivator.java +++ b/src/main/java/dev/isxander/controlify/input/action/activator/ContinuousActivator.java @@ -22,7 +22,6 @@ public String describe() { public record Config() implements ActivatorConfig { public static final String TYPE_ID = "continuous"; public static final Config INSTANCE = new Config(); - public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); @Override public ContinuousActivator create(LogicalInput boundInput) { diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/DoubleTapPulseActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/DoubleTapPulseActivator.java index 8d271dad6..6ca8373c0 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/DoubleTapPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/input/action/activator/DoubleTapPulseActivator.java @@ -23,7 +23,6 @@ public String describe() { public record Config() implements ActivatorConfig { public static final String TYPE_ID = "pulse_double_tap"; public static final Config INSTANCE = new Config(); - public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); @Override public DoubleTapPulseActivator create(LogicalInput boundInput) { diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/GuiPressPulseActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/GuiPressPulseActivator.java index 9d8fec1af..2760ea5e4 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/GuiPressPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/input/action/activator/GuiPressPulseActivator.java @@ -23,7 +23,6 @@ public String describe() { public record Config() implements ActivatorConfig { public static final String TYPE_ID = "pulse_gui_press"; public static final Config INSTANCE = new Config(); - public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); @Override public GuiPressPulseActivator create(LogicalInput boundInput) { diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/HoldLatchActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/HoldLatchActivator.java index 8d7921d7f..3a0954e38 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/HoldLatchActivator.java +++ b/src/main/java/dev/isxander/controlify/input/action/activator/HoldLatchActivator.java @@ -33,7 +33,6 @@ public String describe() { public record Config() implements ActivatorConfig { public static final String TYPE_ID = "latch_hold"; public static final Config INSTANCE = new Config(); - public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); @Override public HoldLatchActivator create(LogicalInput boundInput) { diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/HoldPulseActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/HoldPulseActivator.java index d68f5da45..abeb8a984 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/HoldPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/input/action/activator/HoldPulseActivator.java @@ -23,7 +23,6 @@ public String describe() { public record Config() implements ActivatorConfig { public static final String TYPE_ID = "pulse_hold"; public static final Config INSTANCE = new Config(); - public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); @Override public HoldPulseActivator create(LogicalInput boundInput) { diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/PressPulseActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/PressPulseActivator.java index 58f38263c..4f7d6ad03 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/PressPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/input/action/activator/PressPulseActivator.java @@ -23,7 +23,6 @@ public String describe() { public record Config() implements ActivatorConfig { public static final String TYPE_ID = "pulse_press"; public static final Config INSTANCE = new Config(); - public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); @Override public PressPulseActivator create(LogicalInput boundInput) { diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ReleasePulseActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/ReleasePulseActivator.java index 3199708c0..9e065ab52 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/ReleasePulseActivator.java +++ b/src/main/java/dev/isxander/controlify/input/action/activator/ReleasePulseActivator.java @@ -23,7 +23,6 @@ public String describe() { public record Config() implements ActivatorConfig { public static final String TYPE_ID = "pulse_release"; public static final Config INSTANCE = new Config(); - public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); @Override public ReleasePulseActivator create(LogicalInput boundInput) { diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/RepeatLatchPulseActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/RepeatLatchPulseActivator.java index fd37620e8..35a6523c5 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/RepeatLatchPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/input/action/activator/RepeatLatchPulseActivator.java @@ -77,19 +77,19 @@ public void toggleLatch() { public record Config( ActivatorConfig latchBinding, - int initialDelayTicks, int repeatDelayTicks + int initialDelayMs, int repeatDelayMs ) implements ActivatorConfig { public static final String TYPE_ID = "pulse_repeat_latch"; public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( ActivatorConfig.CODEC.fieldOf("source_latch").forGetter(Config::latchBinding), - Codec.intRange(0, Integer.MAX_VALUE).optionalFieldOf("initial_delay_ticks", 5).forGetter(Config::initialDelayTicks), - Codec.intRange(1, Integer.MAX_VALUE).optionalFieldOf("repeat_delay_ticks", 1).forGetter(Config::repeatDelayTicks) + Codec.intRange(0, Integer.MAX_VALUE).optionalFieldOf("initial_delay_ms", 5).forGetter(Config::initialDelayMs), + Codec.intRange(1, Integer.MAX_VALUE).optionalFieldOf("repeat_delay_ms", 1).forGetter(Config::repeatDelayMs) ).apply(instance, Config::new)); @Override public RepeatLatchPulseActivator create(LogicalInput boundInput) { - long initialDelayNs = this.initialDelayTicks * 50_000_000L; - long repeatDelayNs = this.repeatDelayTicks * 50_000_000L; + long initialDelayNs = this.initialDelayMs * 1_000_000L; + long repeatDelayNs = this.repeatDelayMs * 1_000_000L; return new RepeatLatchPulseActivator(this.latchBinding.create(boundInput), initialDelayNs, repeatDelayNs); } diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/TapPulseActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/TapPulseActivator.java index 11a227aac..ba7c4d377 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/TapPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/input/action/activator/TapPulseActivator.java @@ -24,7 +24,6 @@ public String describe() { public record Config() implements ActivatorConfig { public static final String TYPE_ID = "pulse_tap"; public static final Config INSTANCE = new Config(); - public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); @Override public TapPulseActivator create(LogicalInput boundInput) { diff --git a/src/main/java/dev/isxander/controlify/input/logical/LogicalInput.java b/src/main/java/dev/isxander/controlify/input/logical/LogicalInput.java index eeacbfeb7..7b17ff27b 100644 --- a/src/main/java/dev/isxander/controlify/input/logical/LogicalInput.java +++ b/src/main/java/dev/isxander/controlify/input/logical/LogicalInput.java @@ -1,7 +1,11 @@ package dev.isxander.controlify.input.logical; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import dev.isxander.controlify.utils.codec.CExtraCodecs; import net.minecraft.resources.Identifier; +import java.util.List; import java.util.Set; /// A reference to a logical button, axis, or sensor. @@ -14,13 +18,25 @@ /// like the `A` button or `Left Stick` left, but it may also refer to /// a chord, like `A` + `B`. public sealed interface LogicalInput { + Codec CODEC = CExtraCodecs.fuzzy( + List.of(Single.CODEC, ModifiedChord.CODEC), + input -> switch (input) { + case Single _ -> Single.CODEC; + case ModifiedChord _ -> ModifiedChord.CODEC; + } + ); + /// A logical input with a direct relationship with a physical input. record Single(Identifier physicalInput) implements LogicalInput { - + public static final Codec CODEC = + Identifier.CODEC.xmap(Single::new, Single::physicalInput); } /// A logical input that triggers only after all modifiers are already pressed. record ModifiedChord(Set modifiers, Identifier trigger) implements LogicalInput { - + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + CExtraCodecs.set(Identifier.CODEC).fieldOf("modifiers").forGetter(ModifiedChord::modifiers), + Identifier.CODEC.fieldOf("trigger").forGetter(ModifiedChord::trigger) + ).apply(instance, ModifiedChord::new)); } } diff --git a/src/main/java/dev/isxander/controlify/input/logical/PhysicalToLogicalStage.java b/src/main/java/dev/isxander/controlify/input/logical/PhysicalToLogicalStage.java index 2694993d6..b83e7ee0f 100644 --- a/src/main/java/dev/isxander/controlify/input/logical/PhysicalToLogicalStage.java +++ b/src/main/java/dev/isxander/controlify/input/logical/PhysicalToLogicalStage.java @@ -62,7 +62,7 @@ public void onGuiNavigate() { @Override public void onEvent(PhysicalSignal event, EventSink downstream) { switch (event) { - case PhysicalSignal.Tick(long timeNanos, long deltaTimeNanos) -> { + case PhysicalSignal.Frame(long timeNanos, long deltaTimeNanos) -> { this.sensorAccumulatorStage.onEvent(event, downstream); // Holds must fire even without new input events, before downstream actions process this frame's tick. this.activeButtons.forEach((input, state) -> { diff --git a/src/main/java/dev/isxander/controlify/input/logical/SensorAccumulatorStage.java b/src/main/java/dev/isxander/controlify/input/logical/SensorAccumulatorStage.java index 279eb1d55..7f5505da5 100644 --- a/src/main/java/dev/isxander/controlify/input/logical/SensorAccumulatorStage.java +++ b/src/main/java/dev/isxander/controlify/input/logical/SensorAccumulatorStage.java @@ -21,7 +21,7 @@ public void onEvent( EventSink downstream ) { switch (event) { - case PhysicalSignal.Tick(long timeNanos, _) -> { + case PhysicalSignal.Frame(long timeNanos, _) -> { this.accSensorValues.forEach((sensorType, acc) -> { if (acc.lastNs == -1) { return; diff --git a/src/main/java/dev/isxander/controlify/input/physical/DeadzoneStage.java b/src/main/java/dev/isxander/controlify/input/physical/DeadzoneStage.java index 26f8156f7..a1ef784b5 100644 --- a/src/main/java/dev/isxander/controlify/input/physical/DeadzoneStage.java +++ b/src/main/java/dev/isxander/controlify/input/physical/DeadzoneStage.java @@ -12,22 +12,25 @@ /// Applies deadzones to physical axes. // TODO: radial deadzones public class DeadzoneStage implements UnaryEventStage { - private final Function deadzoneSupplier; + private Function deadzoneSupplier; private final Set axisWasZero; - public DeadzoneStage(Function axisToDeadzoneSupplier) { - this.deadzoneSupplier = axisToDeadzoneSupplier; + public DeadzoneStage(Function deadzoneSupplier) { + this.deadzoneSupplier = deadzoneSupplier; this.axisWasZero = new HashSet<>(); } + public DeadzoneStage() { + this(_ -> 0f); + } + @Override public void onEvent(PhysicalSignal event, EventSink downstream) { if (event instanceof PhysicalInputSignal.AxisMoved(long timeNanos, Identifier input, float value)) { float deadzone = this.deadzoneSupplier.apply(input); if (deadzone > 0) { - // TODO: prevent duplicate zeros passing downstream float newValue = ControllerUtils.deadzone(value, deadzone); if (newValue != 0 || this.axisWasZero.add(input)) { @@ -44,4 +47,8 @@ public void onEvent(PhysicalSignal event, EventSink down downstream.accept(event); } + + public void setDeadzoneSupplier(Function deadzoneSupplier) { + this.deadzoneSupplier = deadzoneSupplier; + } } diff --git a/src/main/java/dev/isxander/controlify/input/physical/InputDomainExpansionStage.java b/src/main/java/dev/isxander/controlify/input/physical/InputDomainExpansionStage.java index f300ddb9a..d41e478d9 100644 --- a/src/main/java/dev/isxander/controlify/input/physical/InputDomainExpansionStage.java +++ b/src/main/java/dev/isxander/controlify/input/physical/InputDomainExpansionStage.java @@ -10,7 +10,7 @@ /// Expands the domain of physical inputs. /// - Expands button presses and releases into axis moved events. /// - Expands axis moved events into button presses and releases when passing threshold. -public class InputDomainExpansionStage implements UnaryEventStage { +public class InputDomainExpansionStage implements UnaryEventStage { private final Set pressedButtons; private final float actuation, release, pressedValue, releasedValue; @@ -24,17 +24,17 @@ public InputDomainExpansionStage(float actuation, float release, float pressedVa } @Override - public void onEvent(PhysicalInputSignal event, EventSink downstream) { + public void onEvent(PhysicalSignal event, EventSink downstream) { downstream.accept(event); switch (event) { case PhysicalInputSignal.AxisMoved(long timeNanos, Identifier input, float value) -> { if (value >= actuation) { - if (!this.pressedButtons.add(input)) { + if (this.pressedButtons.add(input)) { downstream.accept(new PhysicalInputSignal.ButtonChanged(timeNanos, input, true)); } } else if (value <= release) { - if (!this.pressedButtons.remove(input)) { + if (this.pressedButtons.remove(input)) { downstream.accept(new PhysicalInputSignal.ButtonChanged(timeNanos, input, false)); } } @@ -42,6 +42,7 @@ public void onEvent(PhysicalInputSignal event, EventSink { downstream.accept(new PhysicalInputSignal.AxisMoved(timeNanos, input, state ? pressedValue : releasedValue)); } + default -> {} } } } diff --git a/src/main/java/dev/isxander/controlify/input/physical/PhysicalSignal.java b/src/main/java/dev/isxander/controlify/input/physical/PhysicalSignal.java index c92946936..f61d23a3c 100644 --- a/src/main/java/dev/isxander/controlify/input/physical/PhysicalSignal.java +++ b/src/main/java/dev/isxander/controlify/input/physical/PhysicalSignal.java @@ -1,8 +1,8 @@ package dev.isxander.controlify.input.physical; -public sealed interface PhysicalSignal permits PhysicalInputSignal, PhysicalSensorSignal, PhysicalSignal.Tick { +public sealed interface PhysicalSignal permits PhysicalInputSignal, PhysicalSensorSignal, PhysicalSignal.Frame { long timeNanos(); /// Emitted at the end of each polling / every frame. - record Tick(long timeNanos, long deltaTimeNanos) implements PhysicalSignal {} + record Frame(long timeNanos, long deltaTimeNanos) implements PhysicalSignal {} } diff --git a/src/main/java/dev/isxander/controlify/input/physical/SensorProcessorStage.java b/src/main/java/dev/isxander/controlify/input/physical/SensorProcessorStage.java new file mode 100644 index 000000000..2817f81b2 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/physical/SensorProcessorStage.java @@ -0,0 +1,95 @@ +package dev.isxander.controlify.input.physical; + +import dev.isxander.controlify.input.SensorType; +import dev.isxander.controlify.utils.event.EventSink; +import dev.isxander.controlify.utils.event.UnaryEventStage; +import org.joml.Vector3f; +import org.joml.Vector3fc; +import org.jspecify.annotations.Nullable; + +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.function.DoubleSupplier; +import java.util.function.Function; +import java.util.function.Supplier; + +public class SensorProcessorStage implements UnaryEventStage { + private final Map> processors; + + public SensorProcessorStage() { + this(_ -> List.of()); + } + + public SensorProcessorStage(Function> processorFactory) { + this.processors = new EnumMap<>(SensorType.class); + this.setProcessors(processorFactory); + } + + public void setProcessors(Function> processorFactory) { + this.processors.clear(); + for (SensorType sensorType : SensorType.values()) { + this.processors.put(sensorType, List.copyOf(processorFactory.apply(sensorType))); + } + } + + @Override + public void onEvent(PhysicalSignal event, EventSink downstream) { + switch (event) { + case PhysicalSensorSignal(long timeNanos, SensorType sensorType, float x, float y, float z) + when !this.processors.get(sensorType).isEmpty() -> { + var data = new Vector3f(x, y, z); + + for (SensorProcessor processor : this.processors.get(sensorType)) { + processor.process(data, timeNanos); + } + + // TODO: port GyroCalibrator here, and do other stuff like low pass + downstream.accept(new PhysicalSensorSignal(timeNanos, sensorType, data.x, data.y, data.z)); + } + default -> downstream.accept(event); + } + } + + public interface SensorProcessor { + void process(Vector3f data, long timeNanos); + } + + public record BiasCorrection(Supplier bias) implements SensorProcessor { + @Override + public void process(Vector3f data, long timeNanos) { + data.sub(this.bias.get()); + } + } + + /// A first-order low-pass filter with a finite, positive cutoff frequency in Hz. + /// Lower frequencies smooth more strongly. The first sample passes through unchanged. + public static class LowPassFilter implements SensorProcessor { + private final DoubleSupplier cutoffHz; + private @Nullable Vector3f previous; + private long previousTimeNanos; + + public LowPassFilter(DoubleSupplier cutoffHz) { + this.cutoffHz = cutoffHz; + } + + @Override + public void process(Vector3f data, long timeNanos) { + if (this.previous == null) { + this.previous = new Vector3f(data); + this.previousTimeNanos = timeNanos; + } else { + long elapsedNanos = timeNanos - this.previousTimeNanos; + if (elapsedNanos > 0) { + // Exponential decay keeps the response consistent across sample rates. + // expm1 preserves precision for very short sample intervals. + double decayPerNano = 2.0 * Math.PI * this.cutoffHz.getAsDouble() * 1e-9; + float blend = (float) -Math.expm1(-elapsedNanos * decayPerNano); + this.previous.lerp(data, blend); + this.previousTimeNanos = timeNanos; + } + data.set(this.previous); + } + } + } +} diff --git a/src/main/java/dev/isxander/controlify/utils/codec/CExtraCodecs.java b/src/main/java/dev/isxander/controlify/utils/codec/CExtraCodecs.java index 1dc3e2128..8b33daf6c 100644 --- a/src/main/java/dev/isxander/controlify/utils/codec/CExtraCodecs.java +++ b/src/main/java/dev/isxander/controlify/utils/codec/CExtraCodecs.java @@ -21,13 +21,15 @@ public final class CExtraCodecs { public static Codec fuzzy( List> codecs, - Function> encoderGetter) { + Function> encoderGetter + ) { return new FuzzyCodec<>(codecs, encoderGetter); } public static MapCodec fuzzyMap( List> codecs, - Function> encoderGetter) { + Function> encoderGetter + ) { return new FuzzyMapCodec<>(codecs, encoderGetter); } diff --git a/src/main/java/dev/isxander/controlify/utils/event/EventSource.java b/src/main/java/dev/isxander/controlify/utils/event/EventSource.java new file mode 100644 index 000000000..69a1d8416 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/utils/event/EventSource.java @@ -0,0 +1,11 @@ +package dev.isxander.controlify.utils.event; + +public interface EventSource { + Subscription subscribe(EventSink listener); + + @FunctionalInterface + interface Subscription extends AutoCloseable { + @Override + void close(); // unsubscribe; no checked exception + } +} diff --git a/src/main/java/dev/isxander/controlify/utils/event/EventStage.java b/src/main/java/dev/isxander/controlify/utils/event/EventStage.java index 086ba51ce..8d9de4c17 100644 --- a/src/main/java/dev/isxander/controlify/utils/event/EventStage.java +++ b/src/main/java/dev/isxander/controlify/utils/event/EventStage.java @@ -4,8 +4,28 @@ public interface EventStage { void onEvent(I event, EventSink downstream); - default EventStage andThen(EventStage next) { - return (event, downstream) -> this.onEvent(event, x -> next.onEvent(x, downstream)); + default EventStage andThen(EventStage next) { + EventStage first = this; + return new EventStage<>() { + @Override + public void onEvent(I event, EventSink downstream) { + first.onEvent(event, x -> next.onEvent(x, downstream)); + } + + @Override + public EventSink bind(EventSink downstream) { + return first.bind(next.bind(downstream)); + } + }; + } + + /// Connects this stage and any composed stages to a fixed downstream sink once. + default EventSink bind(EventSink downstream) { + return event -> this.onEvent(event, downstream); + } + + default EventSink discardDownstream() { + return this.bind(_ -> {}); } static UnaryEventStage identity() { diff --git a/src/main/java/dev/isxander/controlify/utils/event/EventTap.java b/src/main/java/dev/isxander/controlify/utils/event/EventTap.java new file mode 100644 index 000000000..b8658c026 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/utils/event/EventTap.java @@ -0,0 +1,44 @@ +package dev.isxander.controlify.utils.event; + +import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; + +/// Notifies listeners synchronously on the emitting thread before forwarding an event. +/// Each emission uses a snapshot of subscriptions; changes made during a callback +/// apply to subsequent emissions. Listener exceptions propagate to the caller. +public class EventTap implements EventSource, UnaryEventStage { + private final CopyOnWriteArrayList listeners = new CopyOnWriteArrayList<>(); + + @Override + public void onEvent(T event, EventSink downstream) { + this.emit(event); + downstream.accept(event); + } + + /// Notifies subscribers without forwarding to another stage. + public void emit(T event) { + for (Registration registration : this.listeners) { + registration.listener.accept(event); + } + } + + @Override + public Subscription subscribe(EventSink listener) { + Registration registration = new Registration(Objects.requireNonNull(listener, "listener")); + this.listeners.add(registration); + return registration; + } + + private final class Registration implements Subscription { + private final EventSink listener; + + private Registration(EventSink listener) { + this.listener = listener; + } + + @Override + public void close() { + EventTap.this.listeners.remove(this); + } + } +} From 006d8e4c4146eecd1166352be24ed52a6fd21f2e Mon Sep 17 00:00:00 2001 From: isxander Date: Mon, 12 Oct 2026 16:06:36 +0100 Subject: [PATCH 04/44] forgot to git add --- .../activator/ActivatorConfigManager.java | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfigManager.java diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfigManager.java b/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfigManager.java new file mode 100644 index 000000000..d19a8dee2 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfigManager.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.input.action.activator; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.mojang.serialization.Codec; +import com.mojang.serialization.JsonOps; +import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.input.InputComponent; +import dev.isxander.controlify.platform.client.resource.SimpleControlifyReloadListener; +import dev.isxander.controlify.utils.CUtil; +import net.minecraft.resources.Identifier; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.server.packs.resources.ResourceManager; + +import java.io.BufferedReader; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +public class ActivatorConfigManager implements SimpleControlifyReloadListener>> { + private static final Identifier DEFINITIONS = CUtil.rl("activators.json"); + private static final Codec>> CODEC = Codec.unboundedMap(Identifier.CODEC, ActivatorConfig.CODEC); + private static final ActivatorConfig FALLBACK = new CompoundActivator.Config(List.of()); + + private volatile Map> configsByAction = Map.of(); + + public ActivatorConfig getConfig(Identifier actionId) { + return configsByAction.getOrDefault(actionId, FALLBACK); + } + + @Override + public CompletableFuture>> load(ResourceManager manager, Executor executor) { + return CompletableFuture.supplyAsync(() -> { + Map> mappings = new HashMap<>(); + + // Resource stacks are returned from lowest to highest priority, so later + // packs overwrite individual actions from earlier packs. + for (Resource resource : manager.getResourceStack(DEFINITIONS)) { + try (BufferedReader reader = resource.openAsReader()) { + JsonElement json = JsonParser.parseReader(reader); + CODEC.parse(JsonOps.INSTANCE, json) + .resultOrPartial(error -> CUtil.LOGGER.error( + "Failed to parse activator configs from pack '{}': {}", + resource.sourcePackId(), + error + )) + .ifPresent(mappings::putAll); + } catch (Exception e) { + CUtil.LOGGER.error("Failed to load activator configs from pack '{}'", resource.sourcePackId(), e); + } + } + + return Map.copyOf(mappings); + }, executor); + } + + @Override + public CompletableFuture apply(Map> data, ResourceManager manager, Executor executor) { + return CompletableFuture.runAsync(() -> { + configsByAction = data; + Controlify.instance().getControllerManager().ifPresent(controllerManager -> { + for (var controller : controllerManager.getConnectedControllers()) { + // TODO + if (controller.getAllComponents().get(InputComponent.ID) instanceof InputComponent input) { + input.reloadActions(); + } + } + }); + }, executor); + } + + @Override + public Identifier getReloadId() { + return CUtil.rl("activators"); + } +} From d1286ce653db367ad4cc7be14ab06540a77947d8 Mon Sep 17 00:00:00 2001 From: isxander Date: Mon, 12 Oct 2026 16:39:35 +0100 Subject: [PATCH 05/44] allow gyro low pass cutoff hz to be configured --- .../config/dto/dfu/ControlifyDataFixer.java | 20 +++++----- .../config/dto/dfu/ControlifySchemas.java | 6 +++ .../config/dto/dfu/fixes/GyroLowPassFix.java | 37 +++++++++++++++++++ .../config/dto/profile/GyroConfig.java | 12 +++++- .../config/settings/profile/GyroSettings.java | 11 ++++-- .../controlify/input/InputComponent.java | 4 +- .../input/physical/SensorProcessorStage.java | 8 +++- 7 files changed, 79 insertions(+), 19 deletions(-) create mode 100644 src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/GyroLowPassFix.java diff --git a/src/main/java/dev/isxander/controlify/config/dto/dfu/ControlifyDataFixer.java b/src/main/java/dev/isxander/controlify/config/dto/dfu/ControlifyDataFixer.java index f67b2f46d..f10e97d35 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/dfu/ControlifyDataFixer.java +++ b/src/main/java/dev/isxander/controlify/config/dto/dfu/ControlifyDataFixer.java @@ -13,7 +13,7 @@ import dev.isxander.controlify.config.settings.profile.ProfileSettings; public final class ControlifyDataFixer { - public static final int CURRENT_VERSION = 8; + public static final int CURRENT_VERSION = 9; public static DataFixer getFixer() { return ProductionFixerHolder.FIXER; @@ -31,30 +31,30 @@ private static DataFixer createFixer(GlobalSettings globalDefaults, ProfileSetti var builder = new DataFixerBuilder(CURRENT_VERSION); var v0 = builder.addSchema(0, ControlifySchemas.V0::new); - var v1 = builder.addSchema(1, ControlifySchemas.V1::new); - var v2 = builder.addSchema(2, ControlifySchemas.V2::new); - var v3 = builder.addSchema(3, ControlifySchemas.V3::new); - var v6 = builder.addSchema(6, ControlifySchemas.V6::new); - var v7 = builder.addSchema(7, ControlifySchemas.V7::new); - var v8 = builder.addSchema(8, ControlifySchemas.V8::new); - // v1 + var v1 = builder.addSchema(1, ControlifySchemas.V1::new); builder.addFixer(new TheHolyMigrationFix(v1, globalDefaults, profileDefaults)); - // v2 + var v2 = builder.addSchema(2, ControlifySchemas.V2::new); builder.addFixer(new AnalogueMovementWhitelistFix(v2)); builder.addFixer(new HorizontalLookInvertFix(v2)); - // v6 + var v3 = builder.addSchema(3, ControlifySchemas.V3::new); + var v6 = builder.addSchema(6, ControlifySchemas.V6::new); builder.addFixer(new DualsenseConfigFix(v6, profileDefaults)); // v7 + var v7 = builder.addSchema(7, ControlifySchemas.V7::new); builder.addFixer(new GuideGuiScaleFix(v7, profileDefaults)); // v8 + var v8 = builder.addSchema(8, ControlifySchemas.V8::new); builder.addFixer(new GeneratedBindingIdsFix(v8)); builder.addFixer(new RadialMenuActionsFix(v8)); + var v9 = builder.addSchema(9, ControlifySchemas.V9::new); + builder.addFixer(new GyroLowPassFix(v9, profileDefaults)); + return builder.build().fixer(); } diff --git a/src/main/java/dev/isxander/controlify/config/dto/dfu/ControlifySchemas.java b/src/main/java/dev/isxander/controlify/config/dto/dfu/ControlifySchemas.java index 173c5b123..3c01b75f4 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/dfu/ControlifySchemas.java +++ b/src/main/java/dev/isxander/controlify/config/dto/dfu/ControlifySchemas.java @@ -88,4 +88,10 @@ public V8(int versionKey, Schema parent) { super(versionKey, parent); } } + + public static class V9 extends Schema { + public V9(int versionKey, Schema parent) { + super(versionKey, parent); + } + } } diff --git a/src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/GyroLowPassFix.java b/src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/GyroLowPassFix.java new file mode 100644 index 000000000..27c2fef8d --- /dev/null +++ b/src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/GyroLowPassFix.java @@ -0,0 +1,37 @@ +package dev.isxander.controlify.config.dto.dfu.fixes; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.DataFix; +import com.mojang.datafixers.TypeRewriteRule; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.serialization.Dynamic; +import dev.isxander.controlify.config.dto.dfu.ControlifyTypeReferences; +import dev.isxander.controlify.config.settings.profile.ProfileSettings; + +public class GyroLowPassFix extends DataFix { + private final ProfileSettings profileDefaults; + + public GyroLowPassFix(Schema outputSchema, ProfileSettings profileDefaults) { + super(outputSchema, true); + this.profileDefaults = profileDefaults; + } + + @Override + protected TypeRewriteRule makeRule() { + var profileType = getInputSchema().getType(ControlifyTypeReferences.PROFILE_CONFIG); + + return fixTypeEverywhereTyped( + "Controlify: add gyro low pass hz setting", + profileType, + typed -> typed.update(DSL.remainderFinder(), this::rewriteProfile) + ); + } + + private Dynamic rewriteProfile(Dynamic root) { + var gyro = root.get("gyro").orElseEmptyMap(); + + gyro = gyro.set("low_pass_cutoff_hz", root.createFloat(profileDefaults.gyro.lowPassCutoffHz)); + + return root.set("gyro", gyro); + } +} diff --git a/src/main/java/dev/isxander/controlify/config/dto/profile/GyroConfig.java b/src/main/java/dev/isxander/controlify/config/dto/profile/GyroConfig.java index 5c925796e..21682d7c8 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/profile/GyroConfig.java +++ b/src/main/java/dev/isxander/controlify/config/dto/profile/GyroConfig.java @@ -7,6 +7,7 @@ package dev.isxander.controlify.config.dto.profile; import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; import com.mojang.serialization.codecs.RecordCodecBuilder; import dev.isxander.controlify.controller.gyro.GyroButtonMode; import dev.isxander.controlify.controller.gyro.GyroYawMode; @@ -18,7 +19,8 @@ public record GyroConfig( boolean invertYaw, GyroButtonMode buttonMode, GyroYawMode yawMode, - boolean flickStick + boolean flickStick, + float lowPassCutoffHz ) { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( Codec.FLOAT.fieldOf("look_sensitivity").forGetter(GyroConfig::lookSensitivity), @@ -27,7 +29,13 @@ public record GyroConfig( Codec.BOOL.fieldOf("invert_yaw").forGetter(GyroConfig::invertYaw), GyroButtonMode.CODEC.fieldOf("button_mode").forGetter(GyroConfig::buttonMode), GyroYawMode.CODEC.fieldOf("yaw_mode").forGetter(GyroConfig::yawMode), - Codec.BOOL.fieldOf("flick_stick").forGetter(GyroConfig::flickStick) + Codec.BOOL.fieldOf("flick_stick").forGetter(GyroConfig::flickStick), + Codec.FLOAT + .validate(f -> Float.isFinite(f) && f >= 0 + ? DataResult.success(f) + : DataResult.error(() -> "low pass cutoff hz must be finite and positive")) + .fieldOf("low_pass_cutoff_hz") + .forGetter(GyroConfig::lowPassCutoffHz) ).apply(instance, GyroConfig::new)); } diff --git a/src/main/java/dev/isxander/controlify/config/settings/profile/GyroSettings.java b/src/main/java/dev/isxander/controlify/config/settings/profile/GyroSettings.java index 806fd23e0..1a3b669c1 100644 --- a/src/main/java/dev/isxander/controlify/config/settings/profile/GyroSettings.java +++ b/src/main/java/dev/isxander/controlify/config/settings/profile/GyroSettings.java @@ -18,6 +18,7 @@ public class GyroSettings { public GyroButtonMode buttonMode; public GyroYawMode yawMode; public boolean flickStick; + public float lowPassCutoffHz; public GyroSettings( float lookSensitivity, @@ -26,7 +27,8 @@ public GyroSettings( boolean invertYaw, GyroButtonMode buttonMode, GyroYawMode yawMode, - boolean flickStick + boolean flickStick, + float lowPassCutoffHz ) { this.lookSensitivity = lookSensitivity; this.relativeMode = relativeMode; @@ -35,6 +37,7 @@ public GyroSettings( this.buttonMode = buttonMode; this.yawMode = yawMode; this.flickStick = flickStick; + this.lowPassCutoffHz = lowPassCutoffHz; } public static GyroSettings fromDTO(GyroConfig dto) { @@ -45,7 +48,8 @@ public static GyroSettings fromDTO(GyroConfig dto) { dto.invertYaw(), dto.buttonMode(), dto.yawMode(), - dto.flickStick() + dto.flickStick(), + dto.lowPassCutoffHz() ); } @@ -57,7 +61,8 @@ public GyroConfig toDTO() { invertYaw, buttonMode, yawMode, - flickStick + flickStick, + lowPassCutoffHz ); } } diff --git a/src/main/java/dev/isxander/controlify/input/InputComponent.java b/src/main/java/dev/isxander/controlify/input/InputComponent.java index 9fe15db21..817a9885c 100644 --- a/src/main/java/dev/isxander/controlify/input/InputComponent.java +++ b/src/main/java/dev/isxander/controlify/input/InputComponent.java @@ -80,13 +80,13 @@ public void attach(ControllerEntity controller) { if (group == null) { return 0f; } - return this.controller().settings().input.sensitivity.getDeadzone(group.name()); + return settings().sensitivity.getDeadzone(group.name()); }); this.inputPipeline.stageSensorProcessor().setProcessors(sensorType -> { if (sensorType.isGyroscope()) { return List.of( new SensorProcessorStage.BiasCorrection(() -> this.deviceSettings.gyroCalibration.offset), - new SensorProcessorStage.LowPassFilter(() -> 15.0) // TODO: make this configurable + new SensorProcessorStage.LowPassFilter(() -> this.controller().settings().gyro.lowPassCutoffHz) ); } else { return List.of(); diff --git a/src/main/java/dev/isxander/controlify/input/physical/SensorProcessorStage.java b/src/main/java/dev/isxander/controlify/input/physical/SensorProcessorStage.java index 2817f81b2..6e3e41c4c 100644 --- a/src/main/java/dev/isxander/controlify/input/physical/SensorProcessorStage.java +++ b/src/main/java/dev/isxander/controlify/input/physical/SensorProcessorStage.java @@ -64,6 +64,8 @@ public void process(Vector3f data, long timeNanos) { /// A first-order low-pass filter with a finite, positive cutoff frequency in Hz. /// Lower frequencies smooth more strongly. The first sample passes through unchanged. + /// + /// If the frequency is set to 0, the filter is bypassed. public static class LowPassFilter implements SensorProcessor { private final DoubleSupplier cutoffHz; private @Nullable Vector3f previous; @@ -75,7 +77,9 @@ public LowPassFilter(DoubleSupplier cutoffHz) { @Override public void process(Vector3f data, long timeNanos) { - if (this.previous == null) { + double cutoffHz = this.cutoffHz.getAsDouble(); + + if (cutoffHz == 0 || this.previous == null) { this.previous = new Vector3f(data); this.previousTimeNanos = timeNanos; } else { @@ -83,7 +87,7 @@ public void process(Vector3f data, long timeNanos) { if (elapsedNanos > 0) { // Exponential decay keeps the response consistent across sample rates. // expm1 preserves precision for very short sample intervals. - double decayPerNano = 2.0 * Math.PI * this.cutoffHz.getAsDouble() * 1e-9; + double decayPerNano = 2.0 * Math.PI * cutoffHz * 1e-9; float blend = (float) -Math.expm1(-elapsedNanos * decayPerNano); this.previous.lerp(data, blend); this.previousTimeNanos = timeNanos; From b2df9759c68f4b6250b7ef022c0745f69a3e0e3a Mon Sep 17 00:00:00 2001 From: isxander Date: Mon, 12 Oct 2026 23:43:26 +0100 Subject: [PATCH 06/44] start to wire up the drivers to the new event system; start rewriting controller manager --- .../controller/TestControllerContext.java | 4 +- .../mixin/SDLControllerManagerMixin.java | 3 +- .../controller/ControllerEntity.java | 3 +- .../controlify/controller/ECSComponent.java | 4 + .../controller/info/ControllerInfo.java | 3 +- .../SDLControllerManager.java | 17 -- .../SDLUniqueControllerID.java | 23 ++ .../controllermanager2/ControllerManager.java | 22 ++ .../ControllerManagerBackend.java | 12 ++ .../SDLControllerManagerBackend.java | 167 ++++++++++++++ .../driver/sdl/SDL3GamepadDriver.java | 203 ++++++------------ .../driver/sdl/SDL3JoystickDriver.java | 81 +++---- .../driver/sdl/SDLCommonDriver.java | 115 ++++++---- .../driver/sdl/SDLControllerEvent.java | 192 +++++++++++++++++ .../driver/sdl/SDLControllerEventStage.java | 65 ++++++ .../driver/sdl/SDLInputConversions.java | 133 ++++++++++++ .../driver/sdl/SDLToPhysicalStage.java | 92 ++++++++ .../controlify/input/InputComponent.java | 16 +- .../input/logical/SensorAccumulatorStage.java | 2 +- .../input/physical/PhysicalSensorSignal.java | 10 + .../input/physical/PhysicalSignal.java | 1 + .../input/physical/SensorProcessorStage.java | 8 +- .../controlify/utils/event/EventSource.java | 14 ++ 23 files changed, 929 insertions(+), 261 deletions(-) create mode 100644 src/main/java/dev/isxander/controlify/controllermanager/SDLUniqueControllerID.java create mode 100644 src/main/java/dev/isxander/controlify/controllermanager2/ControllerManager.java create mode 100644 src/main/java/dev/isxander/controlify/controllermanager2/ControllerManagerBackend.java create mode 100644 src/main/java/dev/isxander/controlify/controllermanager2/SDLControllerManagerBackend.java create mode 100644 src/main/java/dev/isxander/controlify/driver/sdl/SDLControllerEvent.java create mode 100644 src/main/java/dev/isxander/controlify/driver/sdl/SDLControllerEventStage.java create mode 100644 src/main/java/dev/isxander/controlify/driver/sdl/SDLInputConversions.java create mode 100644 src/main/java/dev/isxander/controlify/driver/sdl/SDLToPhysicalStage.java diff --git a/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/TestControllerContext.java b/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/TestControllerContext.java index 6ebef7144..b4a958cfe 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/TestControllerContext.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/TestControllerContext.java @@ -8,7 +8,7 @@ import dev.isxander.controlify.Controlify; import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controllermanager.SDLControllerManager; +import dev.isxander.controlify.controllermanager.SDLUniqueControllerID; import dev.isxander.controlify.driver.sdl.SDLException; import dev.isxander.controlify.utils.CUtil; import dev.isxander.sdl.Sdl; @@ -48,7 +48,7 @@ public TestControllerContext( public ControllerEntity getControllerEntity() { return Controlify.instance().getControllerManager().orElseThrow() .getConnectedControllers().stream() - .filter(c -> c.info().ucid().equals(new SDLControllerManager.SDLUniqueControllerID(this.joystickId))) + .filter(c -> c.info().ucid().equals(new SDLUniqueControllerID(this.joystickId))) .findAny() .orElseThrow(); } diff --git a/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerManagerMixin.java b/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerManagerMixin.java index fa6ab7d8c..e06af92b4 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerManagerMixin.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerManagerMixin.java @@ -10,6 +10,7 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import dev.isxander.controlify.controller.ControllerEntity; import dev.isxander.controlify.controllermanager.SDLControllerManager; +import dev.isxander.controlify.controllermanager.SDLUniqueControllerID; import dev.isxander.controlify.controllermanager.UniqueControllerID; import dev.isxander.controlify.hid.ControllerHIDInfo; import dev.isxander.controlify.utils.log.ControlifyLogger; @@ -35,7 +36,7 @@ private Optional preventNonVirtual( ControlifyLogger controllerLogger, Operation> original ) { - SdlJoystickId jid = ((SDLControllerManager.SDLUniqueControllerID) ucid).jid(); + SdlJoystickId jid = ((SDLUniqueControllerID) ucid).jid(); if (!sdl.joystick().SDL_IsJoystickVirtual(jid)) { controllerLogger.warn("Suppressing creation of controller as the test environment permits only virtual controllers"); diff --git a/src/main/java/dev/isxander/controlify/controller/ControllerEntity.java b/src/main/java/dev/isxander/controlify/controller/ControllerEntity.java index 412aabcbe..6481592a2 100644 --- a/src/main/java/dev/isxander/controlify/controller/ControllerEntity.java +++ b/src/main/java/dev/isxander/controlify/controller/ControllerEntity.java @@ -17,12 +17,12 @@ import dev.isxander.controlify.controller.led.LEDComponent; import dev.isxander.controlify.controller.gyro.GyroComponent; import dev.isxander.controlify.controller.impl.ECSEntityImpl; -import dev.isxander.controlify.controller.input.InputComponent; import dev.isxander.controlify.controller.misc.BluetoothDeviceComponent; import dev.isxander.controlify.controller.rumble.RumbleComponent; import dev.isxander.controlify.controller.rumble.TriggerRumbleComponent; import dev.isxander.controlify.controller.touchpad.TouchpadComponent; import dev.isxander.controlify.driver.Driver; +import dev.isxander.controlify.input.InputComponent; import dev.isxander.controlify.utils.log.ControlifyLogger; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Contract; @@ -152,6 +152,7 @@ public void update(boolean outOfFocus) { } public void close() { + this.getAllComponents().values().forEach(ECSComponent::close); this.driver.close(); } diff --git a/src/main/java/dev/isxander/controlify/controller/ECSComponent.java b/src/main/java/dev/isxander/controlify/controller/ECSComponent.java index 38b5b29e7..dd0ee1a4c 100644 --- a/src/main/java/dev/isxander/controlify/controller/ECSComponent.java +++ b/src/main/java/dev/isxander/controlify/controller/ECSComponent.java @@ -14,4 +14,8 @@ public interface ECSComponent { default void attach(ControllerEntity controller) { } + + default void close() { + + } } diff --git a/src/main/java/dev/isxander/controlify/controller/info/ControllerInfo.java b/src/main/java/dev/isxander/controlify/controller/info/ControllerInfo.java index d1e3433a1..553dd853a 100644 --- a/src/main/java/dev/isxander/controlify/controller/info/ControllerInfo.java +++ b/src/main/java/dev/isxander/controlify/controller/info/ControllerInfo.java @@ -9,8 +9,9 @@ import dev.isxander.controlify.controller.id.ControllerType; import dev.isxander.controlify.controllermanager.UniqueControllerID; import dev.isxander.controlify.hid.HIDDevice; +import dev.isxander.controlify.hid.HIDID; import java.util.Optional; -public record ControllerInfo(UniqueControllerID ucid, ControllerType type, Optional hid) { +public record ControllerInfo(UniqueControllerID ucid, ControllerType type, HIDID hid) { } diff --git a/src/main/java/dev/isxander/controlify/controllermanager/SDLControllerManager.java b/src/main/java/dev/isxander/controlify/controllermanager/SDLControllerManager.java index 7e10dd3d3..280f6a224 100644 --- a/src/main/java/dev/isxander/controlify/controllermanager/SDLControllerManager.java +++ b/src/main/java/dev/isxander/controlify/controllermanager/SDLControllerManager.java @@ -28,7 +28,6 @@ import dev.isxander.sdl.*; import net.minecraft.server.packs.resources.Resource; import net.minecraft.server.packs.resources.ResourceProvider; -import org.jetbrains.annotations.NotNull; import java.io.InputStream; import java.nio.ByteBuffer; @@ -239,20 +238,4 @@ private static Optional fetchTypeFromSDL(Sdl sdl, SdlJoystick return Optional.empty(); } - public record SDLUniqueControllerID(@NotNull SdlJoystickId jid) implements UniqueControllerID { - @Override - public boolean equals(Object obj) { - return obj instanceof SDLUniqueControllerID && ((SDLUniqueControllerID) obj).jid.equals(jid); - } - - @Override - public String toString() { - return "SDL-" + jid.value(); - } - - @Override - public int hashCode() { - return Objects.hash(jid.value()); - } - } } diff --git a/src/main/java/dev/isxander/controlify/controllermanager/SDLUniqueControllerID.java b/src/main/java/dev/isxander/controlify/controllermanager/SDLUniqueControllerID.java new file mode 100644 index 000000000..f888c57ef --- /dev/null +++ b/src/main/java/dev/isxander/controlify/controllermanager/SDLUniqueControllerID.java @@ -0,0 +1,23 @@ +package dev.isxander.controlify.controllermanager; + +import dev.isxander.sdl.SdlJoystickId; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +public record SDLUniqueControllerID(@NotNull SdlJoystickId jid) implements UniqueControllerID { + @Override + public boolean equals(Object obj) { + return obj instanceof SDLUniqueControllerID && ((SDLUniqueControllerID) obj).jid.equals(jid); + } + + @Override + public String toString() { + return "SDL-" + jid.value(); + } + + @Override + public int hashCode() { + return Objects.hash(jid.value()); + } +} diff --git a/src/main/java/dev/isxander/controlify/controllermanager2/ControllerManager.java b/src/main/java/dev/isxander/controlify/controllermanager2/ControllerManager.java new file mode 100644 index 000000000..a74a58593 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/controllermanager2/ControllerManager.java @@ -0,0 +1,22 @@ +package dev.isxander.controlify.controllermanager2; + +import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.controller.id.ControllerType; +import dev.isxander.controlify.controllermanager.UniqueControllerID; +import dev.isxander.controlify.hid.HIDID; +import dev.isxander.controlify.utils.log.ControlifyLogger; + +public interface ControllerManager { + boolean canAddController( + UniqueControllerID ucid, + HIDID hidid, + ControllerType controllerType, + ControlifyLogger controllerLogger + ); + + void addController(ControllerEntity controller); + + void removeController(UniqueControllerID controller); + + ControllerType getControllerType(HIDID hidId); +} diff --git a/src/main/java/dev/isxander/controlify/controllermanager2/ControllerManagerBackend.java b/src/main/java/dev/isxander/controlify/controllermanager2/ControllerManagerBackend.java new file mode 100644 index 000000000..78a77c5be --- /dev/null +++ b/src/main/java/dev/isxander/controlify/controllermanager2/ControllerManagerBackend.java @@ -0,0 +1,12 @@ +package dev.isxander.controlify.controllermanager2; + +import dev.isxander.controlify.controller.ControllerEntity; + +public interface ControllerManagerBackend { + + void setup(); + + void pollEvents(); + + void closeController(ControllerEntity controller); +} diff --git a/src/main/java/dev/isxander/controlify/controllermanager2/SDLControllerManagerBackend.java b/src/main/java/dev/isxander/controlify/controllermanager2/SDLControllerManagerBackend.java new file mode 100644 index 000000000..4d3e8ade4 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/controllermanager2/SDLControllerManagerBackend.java @@ -0,0 +1,167 @@ +package dev.isxander.controlify.controllermanager2; + +import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.config.settings.profile.ProfileSettings; +import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.controller.id.ControllerType; +import dev.isxander.controlify.controller.info.ControllerInfo; +import dev.isxander.controlify.controllermanager.SDLUniqueControllerID; +import dev.isxander.controlify.debug.DebugProperties; +import dev.isxander.controlify.driver.Driver; +import dev.isxander.controlify.driver.sdl.*; +import dev.isxander.controlify.hid.HIDID; +import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.utils.event.EventSource; +import dev.isxander.controlify.utils.event.EventTap; +import dev.isxander.controlify.utils.log.ControlifyLogger; +import dev.isxander.sdl.*; +import net.minecraft.client.Minecraft; +import net.minecraft.server.packs.resources.Resource; + +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.Optional; + +public class SDLControllerManagerBackend implements ControllerManagerBackend { + private final Sdl sdl; + private final ControllerManager controllerManager; + private final ControlifyLogger logger; + private final SDLControllerEventStage controllerEventStage; + private final EventTap controllerEventTap; + + private SdlEvent event; + + public SDLControllerManagerBackend(Sdl sdl, ControllerManager controllerManager) { + this.sdl = sdl; + this.controllerManager = controllerManager; + this.logger = CUtil.LOGGER.createSubLogger("SDLControllerManagerBackend"); + this.controllerEventStage = new SDLControllerEventStage(); + this.controllerEventTap = new EventTap<>(); + this.event = new SdlEvent(); + } + + @Override + public void setup() { + this.logger.debugLog("Loading gamepad mappings..."); + + Optional resourceOpt = Minecraft.getInstance().getResourceManager() + .getResource(CUtil.rl("controllers/gamecontrollerdb-sdl3.txt")); + if (resourceOpt.isEmpty()) { + this.logger.error("Failed to find game controller database."); + return; + } + + try (InputStream is = resourceOpt.get().open()) { + byte[] bytes = is.readAllBytes(); + ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bytes.length); + byteBuffer.put(bytes); + byteBuffer.flip(); + + SdlIoStreamHandle stream = null; + try { + stream = this.sdl.ioStream().SDL_IOFromConstMem(byteBuffer); + if (stream.isNull()) throw new IllegalStateException("Failed to open stream"); + + int count = this.sdl.gamepad().SDL_AddGamepadMappingsFromIO(stream, true); + if (count < 0) { + this.logger.error("Failed to load gamepad mappings: {}", this.sdl.error().SDL_GetError()); + } else if (count == 0) { + this.logger.warn("Successfully applied gamepad mappings but none were applied. Unsupported OS?"); + } else { + this.logger.log("Successfully loaded {} gamepad mapping entries!", count); + } + } finally { + if (stream != null) { + this.sdl.ioStream().SDL_CloseIO(stream); + } + } + } catch (Throwable e) { + this.logger.error("Failed to load gamepad mappings", e); + } + } + + @Override + public void pollEvents() { + if (this.event == null) { + this.logger.warn("SDL_Event has somehow been set to null. Recreating..."); + this.event = new SdlEvent(); + } + + while (this.sdl.events().SDL_PollEvent(this.event)) { + this.controllerEventStage.onEvent(this.event, this::onControllerEvent); + } + } + + private void onControllerEvent(SDLControllerEvent event) { + this.controllerEventTap.emit(event); + + switch (event) { + case SDLControllerEvent.JoyDevice(SdlJoystickId which, _, SDLControllerEvent.DeviceEvent.Type eventType) + when eventType == SDLControllerEvent.DeviceEvent.Type.ADDED -> { + var ucid = new SDLUniqueControllerID(which); + + var logger = this.logger.createSubLogger("Controller #" + ucid); + logger.debugLog("New controller detected!"); + + HIDID hidId = this.getJoystickHIDID(which); + ControllerType type = this.controllerManager.getControllerType(hidId); + + if (this.controllerManager.canAddController(ucid, hidId, type, logger)) { + this.createAndAddController(ucid, hidId, type, logger); + } + } + + case SDLControllerEvent.JoyDevice(SdlJoystickId which, _, SDLControllerEvent.DeviceEvent.Type type) + when type == SDLControllerEvent.DeviceEvent.Type.REMOVED -> { + this.controllerManager.removeController(new SDLUniqueControllerID(which)); + } + + default -> {} + } + } + + private void createAndAddController(SDLUniqueControllerID ucid, HIDID hidId, ControllerType type, ControlifyLogger controllerLogger) { + SdlJoystickId jid = ucid.jid(); + controllerLogger.debugLog("Creating controller: {}", jid.value()); + + boolean isGamepad = this.sdl.gamepad().SDL_IsGamepad(jid) && !DebugProperties.FORCE_JOYSTICK; + controllerLogger.debugLog("Controller is gamepad: {}", isGamepad); + + EventSource controllerEventSource = this.controllerEventTap + .filter(event -> event.which().equals(jid)); + + Driver driver; + + if (isGamepad) { + SdlGamepadHandle gamepadHandle = SDLUtil.openGamepad(this.sdl, jid); + driver = new SDL3GamepadDriver(this.sdl, gamepadHandle, controllerEventSource, jid, type, controllerLogger); + } else { + SdlJoystickHandle joystickHandle = SDLUtil.openJoystick(this.sdl, jid); + driver = new SDL3JoystickDriver(this.sdl, joystickHandle, controllerEventSource, jid, type, controllerLogger); + } + + var controllerInfo = new ControllerInfo(ucid, type, hidId); + var controller = new ControllerEntity( + controllerInfo, + driver, + Controlify.instance().config().getActiveProfile(), + ProfileSettings.createDefault(), + controllerLogger + ); + + this.controllerManager.addController(controller); + + } + + @Override + public void closeController(ControllerEntity controller) { + + } + + private HIDID getJoystickHIDID(SdlJoystickId jid) { + int vid = this.sdl.joystick().SDL_GetJoystickVendorForID(jid); + int pid = this.sdl.joystick().SDL_GetJoystickProductForID(jid); + + return new HIDID(vid, pid); + } +} diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDL3GamepadDriver.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDL3GamepadDriver.java index d9f981cfb..f719323c8 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDL3GamepadDriver.java +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDL3GamepadDriver.java @@ -12,125 +12,98 @@ import dev.isxander.controlify.controller.input.GamepadInputs; import dev.isxander.controlify.controller.gyro.GyroState; import dev.isxander.controlify.controller.*; -import dev.isxander.controlify.controller.impl.ControllerStateImpl; -import dev.isxander.controlify.controller.input.InputComponent; import dev.isxander.controlify.controller.touchpad.Touchpads; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.input.InputComponent; +import dev.isxander.controlify.input.SensorType; +import dev.isxander.controlify.input.logical.LogicalSignal; +import dev.isxander.controlify.utils.event.EventSource; import dev.isxander.controlify.utils.log.ControlifyLogger; import dev.isxander.sdl.*; -import org.joml.Vector2f; +import net.minecraft.resources.Identifier; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; -import java.util.ArrayList; -import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.IntStream; +import java.util.stream.Stream; -import static dev.isxander.controlify.utils.CUtil.*; import static dev.isxander.sdl.SdlGamepad.*; public class SDL3GamepadDriver extends SDLCommonDriver { - private static final int GYRO_AXIS_COUNT = 3; - private static final List INPUT_MAPPINGS = List.of( - new SDLGamepadInputMapping.AxisMapping(GamepadInputs.LEFT_STICK_AXIS_RIGHT, SDL_GAMEPAD_AXIS_LEFTX, true), - new SDLGamepadInputMapping.AxisMapping(GamepadInputs.LEFT_STICK_AXIS_LEFT, SDL_GAMEPAD_AXIS_LEFTX, false), - new SDLGamepadInputMapping.AxisMapping(GamepadInputs.LEFT_STICK_AXIS_DOWN, SDL_GAMEPAD_AXIS_LEFTY, true), - new SDLGamepadInputMapping.AxisMapping(GamepadInputs.LEFT_STICK_AXIS_UP, SDL_GAMEPAD_AXIS_LEFTY, false), - new SDLGamepadInputMapping.AxisMapping(GamepadInputs.RIGHT_STICK_AXIS_RIGHT, SDL_GAMEPAD_AXIS_RIGHTX, true), - new SDLGamepadInputMapping.AxisMapping(GamepadInputs.RIGHT_STICK_AXIS_LEFT, SDL_GAMEPAD_AXIS_RIGHTX, false), - new SDLGamepadInputMapping.AxisMapping(GamepadInputs.RIGHT_STICK_AXIS_DOWN, SDL_GAMEPAD_AXIS_RIGHTY, true), - new SDLGamepadInputMapping.AxisMapping(GamepadInputs.RIGHT_STICK_AXIS_UP, SDL_GAMEPAD_AXIS_RIGHTY, false), - new SDLGamepadInputMapping.AxisMapping(GamepadInputs.LEFT_TRIGGER_AXIS, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, true), - new SDLGamepadInputMapping.AxisMapping(GamepadInputs.RIGHT_TRIGGER_AXIS, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, true), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.SOUTH_BUTTON, SDL_GAMEPAD_BUTTON_SOUTH), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.EAST_BUTTON, SDL_GAMEPAD_BUTTON_EAST), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.WEST_BUTTON, SDL_GAMEPAD_BUTTON_WEST), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.NORTH_BUTTON, SDL_GAMEPAD_BUTTON_NORTH), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.LEFT_SHOULDER_BUTTON, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.RIGHT_SHOULDER_BUTTON, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.BACK_BUTTON, SDL_GAMEPAD_BUTTON_BACK), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.START_BUTTON, SDL_GAMEPAD_BUTTON_START), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.GUIDE_BUTTON, SDL_GAMEPAD_BUTTON_GUIDE), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.DPAD_UP_BUTTON, SDL_GAMEPAD_BUTTON_DPAD_UP), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.DPAD_DOWN_BUTTON, SDL_GAMEPAD_BUTTON_DPAD_DOWN), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.DPAD_LEFT_BUTTON, SDL_GAMEPAD_BUTTON_DPAD_LEFT), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.DPAD_RIGHT_BUTTON, SDL_GAMEPAD_BUTTON_DPAD_RIGHT), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.LEFT_STICK_BUTTON, SDL_GAMEPAD_BUTTON_LEFT_STICK), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.RIGHT_STICK_BUTTON, SDL_GAMEPAD_BUTTON_RIGHT_STICK), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.MISC_1_BUTTON, SDL_GAMEPAD_BUTTON_MISC1), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.MISC_2_BUTTON, SDL_GAMEPAD_BUTTON_MISC2), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.MISC_3_BUTTON, SDL_GAMEPAD_BUTTON_MISC3), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.MISC_4_BUTTON, SDL_GAMEPAD_BUTTON_MISC4), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.MISC_5_BUTTON, SDL_GAMEPAD_BUTTON_MISC5), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.MISC_6_BUTTON, SDL_GAMEPAD_BUTTON_MISC6), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.LEFT_PADDLE_1_BUTTON, SDL_GAMEPAD_BUTTON_LEFT_PADDLE1), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.LEFT_PADDLE_2_BUTTON, SDL_GAMEPAD_BUTTON_LEFT_PADDLE2), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.RIGHT_PADDLE_1_BUTTON, SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.RIGHT_PADDLE_2_BUTTON, SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2), - new SDLGamepadInputMapping.ButtonMapping(GamepadInputs.TOUCHPAD_1_BUTTON, SDL_GAMEPAD_BUTTON_TOUCHPAD) - ); - private InputComponent inputComponent; private GyroComponent gyroComponent; private TouchpadComponent touchpadComponent; + private final Set supportedButtons; + private final Set supportedAxes; + private final Set supportedSensors; + private final boolean isGryoSupported; private final int numTouchpads; - private final MemorySegment gyroMemory; - private final FloatBuffer gyroBuffer; - - public SDL3GamepadDriver(Sdl sdl, SdlGamepadHandle ptrController, SdlJoystickId jid, ControllerType type, ControlifyLogger logger) { - super(sdl, ptrController, jid, type, logger); - - this.isGryoSupported = sdl.gamepad().SDL_GamepadHasSensor(ptrController, SDL_SENSOR_GYRO); - this.numTouchpads = sdl.gamepad().SDL_GetNumGamepadTouchpads(ptrController); - + public SDL3GamepadDriver(Sdl sdl, SdlGamepadHandle ptrController, EventSource eventSource, SdlJoystickId jid, ControllerType type, ControlifyLogger logger) { + super(sdl, ptrController, jid, eventSource, type, logger); + + this.supportedButtons = IntStream.range(0, SDL_GAMEPAD_BUTTON_COUNT) + .filter(i -> this.sdl.gamepad().SDL_GamepadHasButton(this.ptrController, i)) + .mapToObj(SDLInputConversions::getGamepadButton) + .collect(Collectors.toSet()); + this.supportedAxes = IntStream.range(0, SDL_GAMEPAD_AXIS_COUNT) + .filter(i -> this.sdl.gamepad().SDL_GamepadHasAxis(this.ptrController, i)) + .boxed() + .flatMap(i -> Stream.of(SDLInputConversions.getGamepadAxis(i, true), SDLInputConversions.getGamepadAxis(i, false))) + .collect(Collectors.toSet()); + this.supportedSensors = IntStream.range(0, SDL_SENSOR_COUNT) + .filter(i -> this.sdl.gamepad().SDL_GamepadHasSensor(this.ptrController, i)) + .mapToObj(SDLInputConversions::getSensorType) + .collect(Collectors.toSet()); + + this.isGryoSupported = this.supportedSensors.contains(SensorType.GYROSCOPE); if (this.isGryoSupported) { sdl.gamepad().SDL_SetGamepadSensorEnabled(ptrController, SDL_SENSOR_GYRO, true); - - this.gyroMemory = this.arena.allocate(ValueLayout.JAVA_FLOAT, GYRO_AXIS_COUNT); - } else { - this.gyroMemory = MemorySegment.NULL; } - this.gyroBuffer = this.gyroMemory.asByteBuffer() - .order(ByteOrder.nativeOrder()) - .asFloatBuffer(); + this.numTouchpads = sdl.gamepad().SDL_GetNumGamepadTouchpads(ptrController); } @Override public void addComponents(ControllerEntity controller) { super.addComponents(controller); - int buttonCount = (int) INPUT_MAPPINGS.stream() - .filter(m -> m instanceof SDLGamepadInputMapping.ButtonMapping) - .filter(m -> m.hasInput(sdl.gamepad(), ptrController)) - .count(); - int axisCount = (int) INPUT_MAPPINGS.stream() - .filter(m -> m instanceof SDLGamepadInputMapping.AxisMapping) - .filter(m -> m.hasInput(sdl.gamepad(), ptrController)) - .count(); - controller.setComponent( this.inputComponent = new InputComponent( - controller, - buttonCount, - axisCount, - 0, - true, + this.controllerType.namespace(), + this.inputPipeline, + this.supportedButtons, + this.supportedAxes, GamepadInputs.DEADZONE_GROUPS, - controller.info().type().mappingId() + true, + this.supportedSensors ) ); if (this.isGryoSupported) { controller.setComponent(this.gyroComponent = new GyroComponent()); + + // TODO: this is not the final way of doing this + // but this is an idea of how gyro would be given to the GyroComponent instead of having everything + // through InputComponent, potentially GyroComponent should further accumulate this until handled + // although most of the time it would only have one sample, so maybe accumulation isn't necessary + // this would be queried at the same time MouseHandler.handleAccumulatedMouseMovement + // this subscription is also never closed atm, nor is deltaTime handled (does it need to be?) + // EDIT: no, this is a bad idea, remember we are in SDL3GamepadDriver but we are purposefully listening + // to the generic processed signals, this should probably be done elsewhere outside of the driver + // EDIT2: we could give the same InputPipeline to GyroComponent similarly to how we give it to InputComponent + // InputComponent subscribes the ActionGraph, while GyroComponent subscribes itself + this.inputPipeline.sourceLogicalSignals().subscribe(sig -> { + if (sig instanceof LogicalSignal.SensorInterval(long timeNanos, long deltaTimeNanos, SensorType sensorType, float x, float y, float z)) { + if (sensorType == SensorType.GYROSCOPE) { + this.gyroComponent.setState(new GyroState(x, y, z)); + } + } + }); } if (this.numTouchpads > 0) { @@ -148,65 +121,27 @@ public void addComponents(ControllerEntity controller) { } @Override - public void update(ControllerEntity controller, boolean outOfFocus) { - super.update(controller, outOfFocus); - - this.updateInput(); - this.updateGyro(); - this.updateTouchpad(); - } + protected void onEvent(SDLControllerEvent event) { + super.onEvent(event); - private void updateInput() { - ControllerStateImpl state = new ControllerStateImpl(); + switch (event) { + case SDLControllerEvent.GamepadTouchpad touchpad -> + this.onTouchpadEvent(touchpad); - for (SDLGamepadInputMapping m : INPUT_MAPPINGS) { - m.applyState(sdl.gamepad(), ptrController, state); + default -> {} } - - this.inputComponent.pushState(state); } - private void updateGyro() { - if (!isGryoSupported) return; - - if (sdl.gamepad().SDL_GetGamepadSensorData(ptrController, SDL_SENSOR_GYRO, gyroBuffer)) { - this.gyroComponent.setState( - new GyroState(gyroBuffer.get(0), gyroBuffer.get(1), gyroBuffer.get(2)) - ); - } else { - CUtil.LOGGER.error("Could not get gyro data: {}", sdl.error().SDL_GetError()); - } + @Override + public void update(ControllerEntity controller, boolean outOfFocus) { + super.update(controller, outOfFocus); } - private void updateTouchpad() { - if (numTouchpads < 1) return; - - for (int touchpadIdx = 0; touchpadIdx < numTouchpads; touchpadIdx++) { - Touchpads.Touchpad touchpad = this.touchpadComponent.touchpads()[touchpadIdx]; - - List fingers = new ArrayList<>(); - for (int fingerIdx = 0; fingerIdx < touchpad.maxFingers(); fingerIdx++) { - var fingerState = new SdlRefs.ByteRef(); - var x = new SdlRefs.FloatRef(); - var y = new SdlRefs.FloatRef(); - var pressure = new SdlRefs.FloatRef(); - - if (!sdl.gamepad().SDL_GetGamepadTouchpadFinger(ptrController, touchpadIdx, fingerIdx, fingerState, x, y, pressure)) { - CUtil.LOGGER.error("Failed to fetch touchpad finger: {}", sdl.error().SDL_GetError()); - } else if (fingerState.value == 1) { - fingers.add( - new Touchpads.Finger( - fingerIdx, - // SDL already returns the correct range for touchpad position and pressure - new Vector2f(x.value, y.value), - pressure.value - ) - ); - } - } - - touchpad.pushFingers(fingers); - } + private void onTouchpadEvent(SDLControllerEvent.GamepadTouchpad event) { + // TODO: touchpad component probably needs redoing, it was designed to receive + // complete snapshots of touchpad data, not continuous updates + // TODO: touchpad events should probably be fed down the physical input pipeline + // so we can recognise gestures from finger swipes } @Override diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDL3JoystickDriver.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDL3JoystickDriver.java index c9fad7e39..8165911a3 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDL3JoystickDriver.java +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDL3JoystickDriver.java @@ -7,84 +7,61 @@ package dev.isxander.controlify.driver.sdl; import dev.isxander.controlify.controller.id.ControllerType; -import dev.isxander.controlify.controller.input.HatState; -import dev.isxander.controlify.controller.input.InputComponent; import dev.isxander.controlify.controller.input.JoystickInputs; import dev.isxander.controlify.controller.*; -import dev.isxander.controlify.controller.impl.ControllerStateImpl; +import dev.isxander.controlify.input.InputComponent; +import dev.isxander.controlify.utils.event.EventSource; import dev.isxander.controlify.utils.log.ControlifyLogger; import dev.isxander.sdl.*; -import net.minecraft.util.Mth; +import net.minecraft.resources.Identifier; import java.nio.ByteBuffer; import java.util.Set; - -import static dev.isxander.sdl.SdlJoystick.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; public class SDL3JoystickDriver extends SDLCommonDriver { private InputComponent inputComponent; - private final int numAxes, numButtons, numHats; - public SDL3JoystickDriver(Sdl sdl, SdlJoystickHandle ptrJoystick, SdlJoystickId jid, ControllerType type, ControlifyLogger logger) { - super(sdl, ptrJoystick, jid, type, logger); + private final int numAxes, numButtons; + private final Set supportedAxes; + private final Set supportedButtons; + + public SDL3JoystickDriver(Sdl sdl, SdlJoystickHandle ptrJoystick, EventSource eventSource, SdlJoystickId jid, ControllerType type, ControlifyLogger logger) { + super(sdl, ptrJoystick, jid, eventSource, type, logger); this.numAxes = sdl.joystick().SDL_GetNumJoystickAxes(ptrJoystick); this.numButtons = sdl.joystick().SDL_GetNumJoystickButtons(ptrJoystick); - this.numHats = sdl.joystick().SDL_GetNumJoystickHats(ptrJoystick); + + this.supportedAxes = IntStream.range(0, this.numAxes) + .boxed() + .flatMap(i -> Stream.of(JoystickInputs.axis(i, false), JoystickInputs.axis(i, true))) + .collect(Collectors.toSet()); + this.supportedButtons = IntStream.range(0, this.numButtons) + .mapToObj(JoystickInputs::button) + .collect(Collectors.toSet()); } @Override public void addComponents(ControllerEntity controller) { super.addComponents(controller); - controller.setComponent(this.inputComponent = new InputComponent(controller, numButtons, numAxes * 2, numHats, false, Set.of(), controller.info().type().mappingId())); + controller.setComponent(this.inputComponent = new InputComponent( + this.controllerType.namespace(), + this.inputPipeline, + this.supportedButtons, + this.supportedAxes, + Set.of(), + false, + Set.of() + )); } @Override public void update(ControllerEntity controller, boolean outOfFocus) { super.update(controller, outOfFocus); - - this.updateInput(); - } - - private void updateInput() { - ControllerStateImpl state = new ControllerStateImpl(); - - for (int i = 0; i < numAxes; i++) { - float axis = mapShortToFloat(sdl.joystick().SDL_GetJoystickAxis(ptrController, 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), sdl.joystick().SDL_GetJoystickButton(ptrController, i)); - } - - for (int i = 0; i < numHats; i++) { - HatState hatState = switch (sdl.joystick().SDL_GetJoystickHat(ptrController, i)) { - case SDL_HAT_CENTERED -> HatState.CENTERED; - case SDL_HAT_UP -> HatState.UP; - case SDL_HAT_RIGHT -> HatState.RIGHT; - case SDL_HAT_DOWN -> HatState.DOWN; - case SDL_HAT_LEFT -> HatState.LEFT; - case SDL_HAT_RIGHTUP -> HatState.RIGHT_UP; - case SDL_HAT_RIGHTDOWN -> HatState.RIGHT_DOWN; - case SDL_HAT_LEFTUP -> HatState.LEFT_UP; - case SDL_HAT_LEFTDOWN -> HatState.LEFT_DOWN; - default -> throw new IllegalStateException("Unexpected value: " + sdl.joystick().SDL_GetJoystickHat(ptrController, i)); - }; - - state.setHat(JoystickInputs.hat(i), hatState); - } - - this.inputComponent.pushState(state); - } - - 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); } @Override diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLCommonDriver.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDLCommonDriver.java index 1e75f2c6c..a4b48168c 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDLCommonDriver.java +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDLCommonDriver.java @@ -24,9 +24,11 @@ import dev.isxander.controlify.driver.Driver; import dev.isxander.controlify.driver.dualsense.DualsenseEffectsState; import dev.isxander.controlify.driver.dualsense.DualsenseTriggerEffect; +import dev.isxander.controlify.input.InputPipeline; import dev.isxander.controlify.rumble.RumbleState; import dev.isxander.controlify.rumble.TriggerRumbleState; import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.utils.event.EventSource; import dev.isxander.controlify.utils.log.ControlifyLogger; import dev.isxander.sdl.*; import net.minecraft.util.Util; @@ -43,7 +45,6 @@ import java.util.Optional; import static dev.isxander.sdl.SdlAudio.*; -import static dev.isxander.sdl.SdlGamepad.*; import static dev.isxander.sdl.SdlInit.*; import static dev.isxander.sdl.SdlProperties.*; @@ -57,6 +58,11 @@ public abstract class SDLCommonDriver implements Driver { protected SdlController ptrController; + protected final EventSource sdlEventSource; + protected final InputPipeline inputPipeline; + private final SDLToPhysicalStage sdlToPhysicalStage; + private EventSource.Subscription eventSubscription; + protected BatteryLevelComponent batteryLevelComponent; protected RumbleComponent rumbleComponent; protected TriggerRumbleComponent triggerRumbleComponent; @@ -75,19 +81,22 @@ public abstract class SDLCommonDriver implements Driver { protected final SdlPropertiesId props; protected final short vendorId, productId; protected final SDLJoystickConnectionState connectionState; + protected final ControllerType controllerType; @Nullable protected SdlAudioDeviceId dualsenseAudioDev; @Nullable protected SdlAudioSpec dualsenseAudioSpec; protected final List dualsenseAudioHandles; - public SDLCommonDriver(Sdl sdl, SdlController ptrController, SdlJoystickId jid, ControllerType type, ControlifyLogger logger) { + public SDLCommonDriver(Sdl sdl, SdlController ptrController, SdlJoystickId jid, EventSource eventSource, ControllerType type, ControlifyLogger logger) { this.sdl = sdl; this.arena = Arena.ofConfined(); this.ptrController = ptrController; + this.sdlEventSource = eventSource; this.logger = logger; this.props = SDL_GetControllerProperties(ptrController); + this.controllerType = type; this.name = Optional.ofNullable(SDL_GetControllerName(ptrController)) .orElse("SDL Controller"); @@ -113,6 +122,9 @@ public SDLCommonDriver(Sdl sdl, SdlController ptrController, SdlJoystickId jid, DecodedGUID decodedGuid = DecodedGUID.fromGUID(this.guid); logger.log("SDL GUID driver signature: {}", decodedGuid.getDriverHint()); + this.inputPipeline = new InputPipeline(); + this.sdlToPhysicalStage = new SDLToPhysicalStage(); + // open audio device for dualsense hd haptics this.dualsenseAudioHandles = new ArrayList<>(); @@ -176,6 +188,29 @@ public void addComponents(ControllerEntity controller) { } } + protected void onEvent(SDLControllerEvent event) { + this.sdlToPhysicalStage.onEvent(event, this.inputPipeline); + + switch (event) { + case SDLControllerEvent.Battery battery -> + this.onBatteryEvent(battery); + + default -> {} + } + } + + private void onBatteryEvent(SDLControllerEvent.Battery event) { + PowerState level = switch (event.state()) { + case ERROR, UNKNOWN -> new PowerState.Unknown(); + case ON_BATTERY -> new PowerState.Depleting(event.percent()); + case NO_BATTERY -> new PowerState.WiredOnly(); + case CHARGING -> new PowerState.Charging(event.percent()); + case CHARGED -> new PowerState.Full(); + }; + + this.batteryLevelComponent.setBatteryLevel(level); + } + @Override public void update(ControllerEntity controller, boolean outOfFocus) { if (ptrController == null) { @@ -183,39 +218,11 @@ public void update(ControllerEntity controller, boolean outOfFocus) { } updateRumble(); - updateBatteryLevel(); updateLED(); updateDualSense(); updateHDHaptic(); } - @Override - public void close() { - if (ptrController == null) { - throw new IllegalStateException("Tried to close controller when it's already closed."); - } - - if (dualSenseComponent != null) { - dualSenseComponent.setLeftTriggerEffect(DualsenseTriggerEffect.Off.INSTANCE); - dualSenseComponent.setRightTriggerEffect(DualsenseTriggerEffect.Off.INSTANCE); - dualSenseComponent.setMuteLight(false); - updateDualSense(); - } - - SDL_CloseController(ptrController); - ptrController = null; - - if (dualsenseAudioDev != null) { - sdl.audio().SDL_CloseAudioDevice(dualsenseAudioDev); - dualsenseAudioDev = null; - for (AudioStreamHandle handle : dualsenseAudioHandles) { - handle.close(); - } - } - - arena.close(); - } - protected void updateRumble() { if (isRumbleSupported) { Optional stateOpt = this.rumbleComponent.consumeRumble(); @@ -238,22 +245,6 @@ protected void updateRumble() { } } - private void updateBatteryLevel() { - SdlRefs.IntRef percent = new SdlRefs.IntRef(); - int powerState = SDL_GetControllerPowerInfo(ptrController, percent); - - PowerState level = switch (powerState) { - case SDL_POWERSTATE_ERROR, SDL_POWERSTATE_UNKNOWN -> new PowerState.Unknown(); - case SDL_POWERSTATE_ON_BATTERY -> new PowerState.Depleting(percent.value); - case SDL_POWERSTATE_NO_BATTERY -> new PowerState.WiredOnly(); - case SDL_POWERSTATE_CHARGING -> new PowerState.Charging(percent.value); - case SDL_POWERSTATE_CHARGED -> new PowerState.Full(); - default -> throw new IllegalStateException("Unexpected value"); - }; - - this.batteryLevelComponent.setBatteryLevel(level); - } - private void updateLED() { if (ledComponent == null) return; @@ -314,6 +305,38 @@ private void updateHDHaptic() { } } + @Override + public void close() { + if (ptrController == null) { + throw new IllegalStateException("Tried to close controller when it's already closed."); + } + + if (this.eventSubscription != null) { + this.eventSubscription.close(); + this.eventSubscription = null; + } + + if (dualSenseComponent != null) { + dualSenseComponent.setLeftTriggerEffect(DualsenseTriggerEffect.Off.INSTANCE); + dualSenseComponent.setRightTriggerEffect(DualsenseTriggerEffect.Off.INSTANCE); + dualSenseComponent.setMuteLight(false); + updateDualSense(); + } + + SDL_CloseController(ptrController); + ptrController = null; + + if (dualsenseAudioDev != null) { + sdl.audio().SDL_CloseAudioDevice(dualsenseAudioDev); + dualsenseAudioDev = null; + for (AudioStreamHandle handle : dualsenseAudioHandles) { + handle.close(); + } + } + + arena.close(); + } + private void playHaptic(CompleteSoundData sound) { if (ptrController == null || dualsenseAudioDev == null || dualsenseAudioSpec == null) { return; diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLControllerEvent.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDLControllerEvent.java new file mode 100644 index 000000000..60a8ba551 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDLControllerEvent.java @@ -0,0 +1,192 @@ +package dev.isxander.controlify.driver.sdl; + +import dev.isxander.sdl.SdlJoystickId; + +public sealed interface SDLControllerEvent { + + long timeNanos(); + + SdlJoystickId which(); + + sealed interface JoystickEvent extends SDLControllerEvent {} + sealed interface GamepadEvent extends SDLControllerEvent {} + + sealed interface DeviceEvent extends SDLControllerEvent { + Type type(); + + enum Type { + ADDED, + REMOVED, + UPDATE_COMPLETE, + /// Gamepad only + REMAPPED, + /// Gamepad only + STEAM_HANDLE_UPDATED, + } + } + + sealed interface AxisEvent extends SDLControllerEvent { + /// The axis. It may be an index or a constant, depending on the event type. + byte axis(); + + /// The axis value, in the range [-32768, 32767] + short value(); + } + + sealed interface ButtonEvent extends SDLControllerEvent { + /// The button. It may be an index or a constant, depending on the event type. + byte button(); + + /// True if the button is pressed, false if released. + boolean down(); + } + + /// A joystick device event. + /// @param which the joystick ID + /// @param timeNanos the event timestamp in nanoseconds + /// @param type the type of device event + record JoyDevice( + SdlJoystickId which, + long timeNanos, + Type type + ) implements JoystickEvent, DeviceEvent {} + + /// A joystick axis motion event. + /// @param which the joystick ID + /// @param timeNanos the event timestamp in nanoseconds + /// @param axis the axis index + /// @param value the axis value, in the range [-32768, 32767] + record JoyAxis( + SdlJoystickId which, + long timeNanos, + byte axis, + short value + ) implements JoystickEvent, AxisEvent {} + + /// A joystick button event. + /// @param which the joystick ID + /// @param timeNanos the event timestamp in nanoseconds + /// @param button the button index + /// @param down true if the button is pressed, false if released + record JoyButton( + SdlJoystickId which, + long timeNanos, + byte button, + boolean down + ) implements JoystickEvent, ButtonEvent {} + + /// A controller battery level change event. + /// @param which the joystick ID + /// @param timeNanos the event timestamp in nanoseconds + /// @param state the power state + /// @param percent the battery percentage, in the range [0, 100], or -1 if unknown + record Battery( + SdlJoystickId which, + long timeNanos, + PowerState state, + int percent + ) implements SDLControllerEvent { + public enum PowerState { + ERROR, + UNKNOWN, + ON_BATTERY, + NO_BATTERY, + CHARGING, + CHARGED, + } + } + + /// A gamepad device event. + /// @param which the joystick ID + /// @param timeNanos the event timestamp in nanoseconds + /// @param type the type of device event + record GamepadDevice( + SdlJoystickId which, + long timeNanos, + Type type + ) implements GamepadEvent, DeviceEvent {} + + /// A gamepad axis motion event. + /// @param which the joystick ID + /// @param timeNanos the event timestamp in nanoseconds + /// @param axis the axis + /// @param value the axis value, in the range [-32768, 32767] + record GamepadAxis( + SdlJoystickId which, + long timeNanos, + byte axis, + short value + ) implements GamepadEvent, AxisEvent {} + + /// A gamepad button event. + /// @param which the joystick ID + /// @param timeNanos the event timestamp in nanoseconds + /// @param button the button + /// @param down true if the button is pressed, false if released + record GamepadButton( + SdlJoystickId which, + long timeNanos, + byte button, + boolean down + ) implements GamepadEvent, ButtonEvent {} + + /// A gamepad touchpad event. + /// @param which the joystick ID + /// @param timeNanos the event timestamp in nanoseconds + /// @param action the action this event represents + /// @param touchpad the index of the touchpad + /// @param finger the index of the finger on the touchpad + /// @param x normalized in the range [0, 1] with 0 being on the left + /// @param y normalized in the range [0, 1] with 0 being at the top + /// @param pressure normalized in the range [0, 1] + record GamepadTouchpad( + SdlJoystickId which, + long timeNanos, + Action action, + int touchpad, + int finger, + float x, + float y, + float pressure + ) implements GamepadEvent { + public enum Action { + DOWN, + MOTION, + UP, + } + } + + /// A gamepad capsense event. + /// Monitors capacitive touch sensors on controllers. + /// @param which the joystick ID + /// @param timeNanos the event timestamp in nanoseconds + /// @param capsense the capsense type + /// @param down true if the capsense is touched + record GamepadCapSense( + SdlJoystickId which, + long timeNanos, + byte capsense, + boolean down + ) implements GamepadEvent {} + + /// A gamepad sensor event + /// @param which the joystick ID + /// @param timeNanos the event timestamp in nanoseconds + /// @param sensor the sensor type + /// @param x the first component of the sensor data + /// @param y the second component of the sensor data, + /// may be 0 if the sensor type is 1-dimensional + /// @param z the third component of the sensor data, + /// may be 0 if the sensor type is not 3-dimensional + /// @param sensorTimestamp the timestamp of the sensor reading in nanoseconds, + /// not necessarily synchronized with the system clock + record GamepadSensor( + SdlJoystickId which, + long timeNanos, + int sensor, + float x, + float y, + float z, + long sensorTimestamp + ) implements GamepadEvent {} +} diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLControllerEventStage.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDLControllerEventStage.java new file mode 100644 index 000000000..dc862b7a9 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDLControllerEventStage.java @@ -0,0 +1,65 @@ +package dev.isxander.controlify.driver.sdl; + +import dev.isxander.controlify.utils.event.EventSink; +import dev.isxander.controlify.utils.event.EventStage; +import dev.isxander.sdl.*; + +public class SDLControllerEventStage implements EventStage { + @Override + public void onEvent(SdlEvent event, EventSink downstream) { + long timeNanos = event.timestamp(); + + switch (event.data()) { + case SdlEvent.JoyAxis(SdlJoystickId which, byte axis, short value) -> + downstream.accept(new SDLControllerEvent.JoyAxis(which, timeNanos, axis, value)); + + case SdlEvent.JoyButton(SdlJoystickId which, byte button, boolean down) -> + downstream.accept(new SDLControllerEvent.JoyButton(which, timeNanos, button, down)); + + case SdlEvent.JoyDevice(SdlJoystickId which) -> { + var type = SDLInputConversions.getDeviceEventType(event.type()); + if (type != null) { + downstream.accept(new SDLControllerEvent.JoyDevice(which, timeNanos, type)); + } + } + + case SdlEvent.JoyBattery(SdlJoystickId which, int state, int percent) -> { + var stateEnum = SDLInputConversions.getPowerState(state); + if (stateEnum != null) { + downstream.accept(new SDLControllerEvent.Battery(which, timeNanos, stateEnum, percent)); + } + } + + case SdlEvent.GamepadAxis(SdlJoystickId which, byte axis, short value) -> + downstream.accept(new SDLControllerEvent.GamepadAxis(which, timeNanos, axis, value)); + + case SdlEvent.GamepadButton(SdlJoystickId which, byte button, boolean down) -> + downstream.accept(new SDLControllerEvent.GamepadButton(which, timeNanos, button, down)); + + case SdlEvent.GamepadDevice(SdlJoystickId which) -> { + var type = SDLInputConversions.getDeviceEventType(event.type()); + if (type != null) { + downstream.accept(new SDLControllerEvent.GamepadDevice(which, timeNanos, type)); + } + } + + case SdlEvent.GamepadTouchpad(SdlJoystickId which, int touchpad, int finger, float x, float y, float pressure) -> { + var action = SDLInputConversions.getTouchpadAction(event.type()); + if (action != null) { + downstream.accept(new SDLControllerEvent.GamepadTouchpad(which, timeNanos, action, touchpad, finger, x, y, pressure)); + } + } + + case SdlEvent.GamepadSensor(SdlJoystickId which, int sensor, float[] values, long sensorTimestamp) -> { + float x = values.length >= 1 ? values[0] : 0f; + float y = values.length >= 2 ? values[1] : 0f; + float z = values.length >= 3 ? values[2] : 0f; + downstream.accept(new SDLControllerEvent.GamepadSensor(which, timeNanos, sensor, x, y, z, sensorTimestamp)); + } + + default -> {} + } + } + + +} diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLInputConversions.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDLInputConversions.java new file mode 100644 index 000000000..92b36ae82 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDLInputConversions.java @@ -0,0 +1,133 @@ +package dev.isxander.controlify.driver.sdl; + +import dev.isxander.controlify.controller.input.GamepadInputs; +import dev.isxander.controlify.input.SensorType; +import dev.isxander.sdl.SdlEvents; +import dev.isxander.sdl.SdlGamepad; +import net.minecraft.resources.Identifier; +import org.jspecify.annotations.Nullable; + +import java.util.stream.IntStream; + +import static dev.isxander.sdl.SdlGamepad.*; + +public final class SDLInputConversions { + private static final Identifier[] GAMEPAD_AXES = IntStream.range(0, SDL_GAMEPAD_AXIS_COUNT << 1) + .mapToObj(i -> switch (i) { + case (SDL_GAMEPAD_AXIS_LEFTX << 1) -> GamepadInputs.LEFT_STICK_AXIS_RIGHT; + case (SDL_GAMEPAD_AXIS_LEFTX << 1) | 1 -> GamepadInputs.LEFT_STICK_AXIS_LEFT; + case (SDL_GAMEPAD_AXIS_LEFTY << 1) -> GamepadInputs.LEFT_STICK_AXIS_DOWN; + case (SDL_GAMEPAD_AXIS_LEFTY << 1) | 1 -> GamepadInputs.LEFT_STICK_AXIS_UP; + case (SDL_GAMEPAD_AXIS_RIGHTX << 1) -> GamepadInputs.RIGHT_STICK_AXIS_RIGHT; + case (SDL_GAMEPAD_AXIS_RIGHTX << 1) | 1 -> GamepadInputs.RIGHT_STICK_AXIS_LEFT; + case (SDL_GAMEPAD_AXIS_RIGHTY << 1) -> GamepadInputs.RIGHT_STICK_AXIS_DOWN; + case (SDL_GAMEPAD_AXIS_RIGHTY << 1) | 1 -> GamepadInputs.RIGHT_STICK_AXIS_UP; + case (SDL_GAMEPAD_AXIS_LEFT_TRIGGER << 1), + (SDL_GAMEPAD_AXIS_LEFT_TRIGGER << 1) | 1 -> GamepadInputs.LEFT_TRIGGER_AXIS; + case (SDL_GAMEPAD_AXIS_RIGHT_TRIGGER << 1), + (SDL_GAMEPAD_AXIS_RIGHT_TRIGGER << 1) | 1 -> GamepadInputs.RIGHT_TRIGGER_AXIS; + default -> throw new IllegalArgumentException("Unknown axis"); + }) + .toArray(Identifier[]::new); + private static final Identifier[] GAMEPAD_BUTTONS = IntStream.range(0, SDL_GAMEPAD_BUTTON_COUNT) + .mapToObj(i -> switch (i) { + case SDL_GAMEPAD_BUTTON_SOUTH -> GamepadInputs.SOUTH_BUTTON; + case SDL_GAMEPAD_BUTTON_EAST -> GamepadInputs.EAST_BUTTON; + case SDL_GAMEPAD_BUTTON_WEST -> GamepadInputs.WEST_BUTTON; + case SDL_GAMEPAD_BUTTON_NORTH -> GamepadInputs.NORTH_BUTTON; + case SDL_GAMEPAD_BUTTON_BACK -> GamepadInputs.BACK_BUTTON; + case SDL_GAMEPAD_BUTTON_GUIDE -> GamepadInputs.GUIDE_BUTTON; + case SDL_GAMEPAD_BUTTON_START -> GamepadInputs.START_BUTTON; + case SDL_GAMEPAD_BUTTON_LEFT_STICK -> GamepadInputs.LEFT_STICK_BUTTON; + case SDL_GAMEPAD_BUTTON_RIGHT_STICK -> GamepadInputs.RIGHT_STICK_BUTTON; + case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER -> GamepadInputs.LEFT_SHOULDER_BUTTON; + case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER -> GamepadInputs.RIGHT_SHOULDER_BUTTON; + case SDL_GAMEPAD_BUTTON_DPAD_UP -> GamepadInputs.DPAD_UP_BUTTON; + case SDL_GAMEPAD_BUTTON_DPAD_DOWN -> GamepadInputs.DPAD_DOWN_BUTTON; + case SDL_GAMEPAD_BUTTON_DPAD_LEFT -> GamepadInputs.DPAD_LEFT_BUTTON; + case SDL_GAMEPAD_BUTTON_DPAD_RIGHT -> GamepadInputs.DPAD_RIGHT_BUTTON; + case SDL_GAMEPAD_BUTTON_MISC1 -> GamepadInputs.MISC_1_BUTTON; + case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1 -> GamepadInputs.RIGHT_PADDLE_1_BUTTON; + case SDL_GAMEPAD_BUTTON_LEFT_PADDLE1 -> GamepadInputs.LEFT_PADDLE_1_BUTTON; + case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2 -> GamepadInputs.RIGHT_PADDLE_2_BUTTON; + case SDL_GAMEPAD_BUTTON_LEFT_PADDLE2 -> GamepadInputs.LEFT_PADDLE_2_BUTTON; + case SDL_GAMEPAD_BUTTON_TOUCHPAD -> GamepadInputs.TOUCHPAD_1_BUTTON; + case SDL_GAMEPAD_BUTTON_MISC2 -> GamepadInputs.MISC_2_BUTTON; + case SDL_GAMEPAD_BUTTON_MISC3 -> GamepadInputs.MISC_3_BUTTON; + case SDL_GAMEPAD_BUTTON_MISC4 -> GamepadInputs.MISC_4_BUTTON; + case SDL_GAMEPAD_BUTTON_MISC5 -> GamepadInputs.MISC_5_BUTTON; + case SDL_GAMEPAD_BUTTON_MISC6 -> GamepadInputs.MISC_6_BUTTON; + default -> throw new IllegalArgumentException("Unknown button"); + + }) + .toArray(Identifier[]::new); + + private static final SensorType[] GAMEPAD_SENSOR_TYPES = IntStream.range(0, SDL_SENSOR_COUNT) + .mapToObj(i -> switch (i) { + case SDL_SENSOR_GYRO -> SensorType.GYROSCOPE; + case SDL_SENSOR_GYRO_L -> SensorType.GYROSCOPE_L; + case SDL_SENSOR_GYRO_R -> SensorType.GYROSCOPE_R; + case SDL_SENSOR_ACCEL -> SensorType.ACCELEROMETER; + case SDL_SENSOR_ACCEL_L -> SensorType.ACCELEROMETER_L; + case SDL_SENSOR_ACCEL_R -> SensorType.ACCELEROMETER_R; + default -> null; + }) + .toArray(SensorType[]::new); + + public static @Nullable Identifier getGamepadButton(int button) { + if (button < 0 || button >= GAMEPAD_BUTTONS.length) { + return null; + } + return GAMEPAD_BUTTONS[button]; + } + + public static @Nullable Identifier getGamepadAxis(int axis, boolean positive) { + int index = (axis << 1) | (positive ? 0 : 1); + if (index < 0 || index >= GAMEPAD_AXES.length) { + return null; + } + return GAMEPAD_AXES[index]; + } + + public static @Nullable SensorType getSensorType(int sensor) { + if (sensor < 0 || sensor >= GAMEPAD_SENSOR_TYPES.length) { + return null; + } + return GAMEPAD_SENSOR_TYPES[sensor]; + } + + public static SDLControllerEvent.DeviceEvent.@Nullable Type getDeviceEventType(int event) { + return switch (event) { + case SdlEvents.SDL_EVENT_JOYSTICK_ADDED -> SDLControllerEvent.DeviceEvent.Type.ADDED; + case SdlEvents.SDL_EVENT_JOYSTICK_REMOVED -> SDLControllerEvent.DeviceEvent.Type.REMOVED; + case SdlEvents.SDL_EVENT_JOYSTICK_UPDATE_COMPLETE -> SDLControllerEvent.DeviceEvent.Type.UPDATE_COMPLETE; + case SdlEvents.SDL_EVENT_GAMEPAD_REMAPPED -> SDLControllerEvent.DeviceEvent.Type.REMAPPED; + case SdlEvents.SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED -> SDLControllerEvent.DeviceEvent.Type.STEAM_HANDLE_UPDATED; + default -> null; + }; + } + + public static SDLControllerEvent.Battery.@Nullable PowerState getPowerState(int powerState) { + return switch (powerState) { + case SdlGamepad.SDL_POWERSTATE_ERROR -> SDLControllerEvent.Battery.PowerState.ERROR; + case SdlGamepad.SDL_POWERSTATE_UNKNOWN -> SDLControllerEvent.Battery.PowerState.UNKNOWN; + case SdlGamepad.SDL_POWERSTATE_ON_BATTERY -> SDLControllerEvent.Battery.PowerState.ON_BATTERY; + case SdlGamepad.SDL_POWERSTATE_NO_BATTERY -> SDLControllerEvent.Battery.PowerState.NO_BATTERY; + case SdlGamepad.SDL_POWERSTATE_CHARGING -> SDLControllerEvent.Battery.PowerState.CHARGING; + case SdlGamepad.SDL_POWERSTATE_CHARGED -> SDLControllerEvent.Battery.PowerState.CHARGED; + default -> null; + }; + } + + public static SDLControllerEvent.GamepadTouchpad.@Nullable Action getTouchpadAction(int event) { + return switch (event) { + case SdlEvents.SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN -> SDLControllerEvent.GamepadTouchpad.Action.DOWN; + case SdlEvents.SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION -> SDLControllerEvent.GamepadTouchpad.Action.MOTION; + case SdlEvents.SDL_EVENT_GAMEPAD_TOUCHPAD_UP -> SDLControllerEvent.GamepadTouchpad.Action.UP; + default -> null; + }; + } + + private SDLInputConversions() { + } +} diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLToPhysicalStage.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDLToPhysicalStage.java new file mode 100644 index 000000000..e43cfa0a0 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDLToPhysicalStage.java @@ -0,0 +1,92 @@ +package dev.isxander.controlify.driver.sdl; + +import dev.isxander.controlify.controller.input.JoystickInputs; +import dev.isxander.controlify.input.SensorType; +import dev.isxander.controlify.input.physical.PhysicalInputSignal; +import dev.isxander.controlify.input.physical.PhysicalSensorSignal; +import dev.isxander.controlify.input.physical.PhysicalSignal; +import dev.isxander.controlify.utils.event.EventSink; +import dev.isxander.controlify.utils.event.EventStage; +import net.minecraft.resources.Identifier; + +import java.util.Arrays; + +public class SDLToPhysicalStage implements EventStage { + private final float[] prevGamepadAxisValues = new float[64]; + private final float[] prevJoystickAxisValues = new float[64]; + + public SDLToPhysicalStage() { + Arrays.fill(this.prevGamepadAxisValues, 0f); + Arrays.fill(this.prevJoystickAxisValues, 0f); + } + + @Override + public void onEvent(SDLControllerEvent event, EventSink downstream) { + switch (event) { + case SDLControllerEvent.JoyAxis(_, long timeNanos, byte axis, short value) -> { + float normalisedValue = normaliseAxis(value); + + float prevNormalisedValue = this.prevJoystickAxisValues[axis]; + this.prevJoystickAxisValues[axis] = normalisedValue; + + boolean positive = normalisedValue > 0; + boolean prevPositive = prevNormalisedValue > 0; + + if (prevPositive != positive) { + // sign changed + Identifier prevInput = JoystickInputs.axis(axis, prevPositive); + downstream.accept(new PhysicalInputSignal.AxisMoved(timeNanos, prevInput, 0f)); + } + + Identifier input = JoystickInputs.axis(axis, positive); + downstream.accept(new PhysicalInputSignal.AxisMoved(timeNanos, input, Math.abs(normalisedValue))); + } + + case SDLControllerEvent.JoyButton(_, long timeNanos, byte button, boolean down) -> + downstream.accept(new PhysicalInputSignal.ButtonChanged(timeNanos, JoystickInputs.button(button), down)); + + case SDLControllerEvent.GamepadAxis(_, long timeNanos, byte axis, short value) -> { + float normalisedValue = normaliseAxis(value); + + float prevNormalisedValue = this.prevGamepadAxisValues[axis]; + this.prevGamepadAxisValues[axis] = normalisedValue; + + boolean positive = normalisedValue > 0; + boolean prevPositive = prevNormalisedValue > 0; + + if (prevPositive != positive) { + // sign changed + Identifier prevInput = SDLInputConversions.getGamepadAxis(axis, prevPositive); + if (prevInput != null) { + downstream.accept(new PhysicalInputSignal.AxisMoved(timeNanos, prevInput, 0f)); + } + } + + Identifier input = SDLInputConversions.getGamepadAxis(axis, positive); + if (input != null) { + downstream.accept(new PhysicalInputSignal.AxisMoved(timeNanos, input, Math.abs(normalisedValue))); + } + } + + case SDLControllerEvent.GamepadButton(_, long timeNanos, byte button, boolean down) -> { + Identifier input = SDLInputConversions.getGamepadButton(button); + if (input != null) { + downstream.accept(new PhysicalInputSignal.ButtonChanged(timeNanos, input, down)); + } + } + + case SDLControllerEvent.GamepadSensor(_, long timeNanos, int sensor, float x, float y, float z, long sensorTimestamp) -> { + SensorType sensorType = SDLInputConversions.getSensorType(sensor); + if (sensorType != null) { + downstream.accept(new PhysicalSensorSignal(timeNanos, sensorTimestamp, sensorType, x, y, z)); + } + } + + default -> {} + } + } + + private static float normaliseAxis(short v) { + return v < 0 ? (v / 32768f) : (v / 32767f); + } +} diff --git a/src/main/java/dev/isxander/controlify/input/InputComponent.java b/src/main/java/dev/isxander/controlify/input/InputComponent.java index 817a9885c..d95475312 100644 --- a/src/main/java/dev/isxander/controlify/input/InputComponent.java +++ b/src/main/java/dev/isxander/controlify/input/InputComponent.java @@ -15,6 +15,7 @@ import dev.isxander.controlify.input.logical.LogicalInput; import dev.isxander.controlify.input.physical.SensorProcessorStage; import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.utils.event.EventSource; import net.minecraft.resources.Identifier; import net.minecraft.util.Util; import org.jspecify.annotations.Nullable; @@ -40,6 +41,8 @@ public class InputComponent extends ECSComponentImpl { private final boolean isGamepad; private final Set supportedSensors; + private EventSource.Subscription actionGraphSub; + public InputComponent( Identifier controllerType, InputPipeline inputPipeline, @@ -74,7 +77,7 @@ public void attach(ControllerEntity controller) { // we need to wait for settings to load before getting current binding this.reloadActions(); - this.inputPipeline.sourceLogicalSignals().subscribe(this.actionGraph); + this.actionGraphSub = this.inputPipeline.sourceLogicalSignals().subscribe(this.actionGraph); this.inputPipeline.stageDeadzone().setDeadzoneSupplier(axis -> { DeadzoneGroup group = this.deadzoneGroups.get(axis); if (group == null) { @@ -150,6 +153,14 @@ public boolean supportsAccelerometer() { return this.supportedSensors.stream().anyMatch(SensorType::isAccelerometer); } + @Override + public void close() { + super.close(); + + this.actionGraphSub.close(); + this.actionGraphSub = null; + } + private ActionImpl createAction(ActionSpec spec) { DefaultBindProvider bindProvider = Controlify.instance().defaultBindManager() .getDefaultBindProvider(this.controllerType); @@ -157,7 +168,8 @@ private ActionImpl createAction(ActionSpec spec) { LogicalInput currentBoundInput = settings().bindings.bindings.get(spec.id()); - ActivatorConfig activatorConfig = Controlify.instance().activatorConfigManager().getConfig(spec.id()); + ActivatorConfig activatorConfig = Controlify.instance().activatorConfigManager() + .getConfig(spec.id()); ActionImpl action = new ActionImpl( spec, diff --git a/src/main/java/dev/isxander/controlify/input/logical/SensorAccumulatorStage.java b/src/main/java/dev/isxander/controlify/input/logical/SensorAccumulatorStage.java index 7f5505da5..587d3418d 100644 --- a/src/main/java/dev/isxander/controlify/input/logical/SensorAccumulatorStage.java +++ b/src/main/java/dev/isxander/controlify/input/logical/SensorAccumulatorStage.java @@ -56,7 +56,7 @@ public void onEvent( }); } - case PhysicalSensorSignal(long timeNanos, SensorType sensorType, float x, float y, float z) -> { + case PhysicalSensorSignal(long timeNanos, _, SensorType sensorType, float x, float y, float z) -> { Acc acc = this.accSensorValues.computeIfAbsent(sensorType, _ -> new Acc()); if (acc.lastNs != -1 && timeNanos <= acc.lastNs) { diff --git a/src/main/java/dev/isxander/controlify/input/physical/PhysicalSensorSignal.java b/src/main/java/dev/isxander/controlify/input/physical/PhysicalSensorSignal.java index 57ef156be..f4af8d8d8 100644 --- a/src/main/java/dev/isxander/controlify/input/physical/PhysicalSensorSignal.java +++ b/src/main/java/dev/isxander/controlify/input/physical/PhysicalSensorSignal.java @@ -3,8 +3,18 @@ import dev.isxander.controlify.input.SensorType; /// Represents a single IMU sample +/// @param timeNanos timestamp of the event in nanoseconds +/// @param sensorTimeNanos timestamp provided by the sensor firmware in nanoseconds. +/// this may be out of sync with the system clock and therefore {@link timeNanos} +/// @param sensorType the type of sensor this signal is for +/// @param x the first component of the sensor data +/// @param y the second component of the sensor data; +/// may be zero if the sensor data is not at least 2-dimensional +/// @param z the third component of the sensor data; +/// may be zero if the sensor data is not at least 3-dimensional public record PhysicalSensorSignal( long timeNanos, + long sensorTimeNanos, SensorType sensorType, float x, float y, float z ) implements PhysicalSignal { diff --git a/src/main/java/dev/isxander/controlify/input/physical/PhysicalSignal.java b/src/main/java/dev/isxander/controlify/input/physical/PhysicalSignal.java index f61d23a3c..5657f8dc5 100644 --- a/src/main/java/dev/isxander/controlify/input/physical/PhysicalSignal.java +++ b/src/main/java/dev/isxander/controlify/input/physical/PhysicalSignal.java @@ -1,6 +1,7 @@ package dev.isxander.controlify.input.physical; public sealed interface PhysicalSignal permits PhysicalInputSignal, PhysicalSensorSignal, PhysicalSignal.Frame { + /// timestamp of the signal in nanoseconds long timeNanos(); /// Emitted at the end of each polling / every frame. diff --git a/src/main/java/dev/isxander/controlify/input/physical/SensorProcessorStage.java b/src/main/java/dev/isxander/controlify/input/physical/SensorProcessorStage.java index 6e3e41c4c..2e007424c 100644 --- a/src/main/java/dev/isxander/controlify/input/physical/SensorProcessorStage.java +++ b/src/main/java/dev/isxander/controlify/input/physical/SensorProcessorStage.java @@ -36,16 +36,16 @@ public void setProcessors(Function> processorF @Override public void onEvent(PhysicalSignal event, EventSink downstream) { switch (event) { - case PhysicalSensorSignal(long timeNanos, SensorType sensorType, float x, float y, float z) + case PhysicalSensorSignal(long timeNanos, long sensorTimeNanos, SensorType sensorType, float x, float y, float z) when !this.processors.get(sensorType).isEmpty() -> { var data = new Vector3f(x, y, z); for (SensorProcessor processor : this.processors.get(sensorType)) { - processor.process(data, timeNanos); + processor.process(data, sensorTimeNanos); } - // TODO: port GyroCalibrator here, and do other stuff like low pass - downstream.accept(new PhysicalSensorSignal(timeNanos, sensorType, data.x, data.y, data.z)); + // TODO: port GyroCalibrator here + downstream.accept(new PhysicalSensorSignal(timeNanos, sensorTimeNanos, sensorType, data.x, data.y, data.z)); } default -> downstream.accept(event); } diff --git a/src/main/java/dev/isxander/controlify/utils/event/EventSource.java b/src/main/java/dev/isxander/controlify/utils/event/EventSource.java index 69a1d8416..d96dbfec8 100644 --- a/src/main/java/dev/isxander/controlify/utils/event/EventSource.java +++ b/src/main/java/dev/isxander/controlify/utils/event/EventSource.java @@ -1,8 +1,22 @@ package dev.isxander.controlify.utils.event; +import java.util.function.Predicate; + public interface EventSource { Subscription subscribe(EventSink listener); + default EventSource via(EventStage stage) { + return sink -> this.subscribe(stage.bind(sink)); + } + + default EventSource filter(Predicate predicate) { + return sink -> this.subscribe(event -> { + if (predicate.test(event)) { + sink.accept(event); + } + }); + } + @FunctionalInterface interface Subscription extends AutoCloseable { @Override From dbebcc3a0287db033bcab37f58533109475e924c Mon Sep 17 00:00:00 2001 From: isxander Date: Wed, 14 Oct 2026 13:26:32 +0100 Subject: [PATCH 07/44] rewrite controller manager and start to wire up fixedupdate/update system --- .../dev/isxander/controlify/Controlify.java | 164 +++++------- .../api/event/ControlifyEvents.java | 2 +- .../controller/info/ControllerInfo.java | 3 - .../AbstractControllerManager.java | 157 ------------ .../controllermanager/ControllerBackend.java | 33 +++ .../controllermanager/ControllerManager.java | 100 ++++++-- .../SDLControllerBackend.java} | 63 +++-- .../SDLControllerManager.java | 241 ------------------ .../controllermanager2/ControllerManager.java | 22 -- .../ControllerManagerBackend.java | 12 - .../gui/screen/BindConsumerScreen.java | 2 +- .../screen/ControllerMappingMakerScreen.java | 2 +- .../controlify/ingame/InGameInputHandler.java | 3 +- .../input/action/ActionContext.java | 5 +- .../mixins/core/MinecraftMixin.java | 7 +- .../controlify/screenop/ScreenProcessor.java | 2 +- .../controlify/utils/ControllerUtils.java | 2 +- ...ollerSetupWizard.java => SetupWizard.java} | 2 +- .../controlify/utils/SetupWizardManager.java | 28 ++ .../wireless/LowBatteryNotifier.java | 6 +- 20 files changed, 265 insertions(+), 591 deletions(-) delete mode 100644 src/main/java/dev/isxander/controlify/controllermanager/AbstractControllerManager.java create mode 100644 src/main/java/dev/isxander/controlify/controllermanager/ControllerBackend.java rename src/main/java/dev/isxander/controlify/{controllermanager2/SDLControllerManagerBackend.java => controllermanager/SDLControllerBackend.java} (79%) delete mode 100644 src/main/java/dev/isxander/controlify/controllermanager/SDLControllerManager.java delete mode 100644 src/main/java/dev/isxander/controlify/controllermanager2/ControllerManager.java delete mode 100644 src/main/java/dev/isxander/controlify/controllermanager2/ControllerManagerBackend.java rename src/main/java/dev/isxander/controlify/utils/{ControllerSetupWizard.java => SetupWizard.java} (97%) create mode 100644 src/main/java/dev/isxander/controlify/utils/SetupWizardManager.java diff --git a/src/main/java/dev/isxander/controlify/Controlify.java b/src/main/java/dev/isxander/controlify/Controlify.java index dfd09084c..ad4df0da7 100644 --- a/src/main/java/dev/isxander/controlify/Controlify.java +++ b/src/main/java/dev/isxander/controlify/Controlify.java @@ -27,12 +27,11 @@ import dev.isxander.controlify.controller.*; import dev.isxander.controlify.controller.dualsense.TriggerEffectManager; import dev.isxander.controlify.controller.id.ControllerTypeManager; -import dev.isxander.controlify.controller.input.ControllerState; -import dev.isxander.controlify.controller.input.ControllerStateView; import dev.isxander.controlify.controller.input.InputComponent; import dev.isxander.controlify.controller.rumble.RumbleComponent; +import dev.isxander.controlify.controllermanager.ControllerBackend; import dev.isxander.controlify.controllermanager.ControllerManager; -import dev.isxander.controlify.controllermanager.SDLControllerManager; +import dev.isxander.controlify.controllermanager.SDLControllerBackend; import dev.isxander.controlify.driver.sdl.SDLNativesLoader; import dev.isxander.controlify.driver.steamdeck.SteamDeckMode; import dev.isxander.controlify.driver.steamdeck.SteamDeckUtil; @@ -70,7 +69,6 @@ import java.io.IOException; import java.util.*; -import java.util.stream.Collectors; import static dev.isxander.controlify.utils.ControllerUtils.wrapControllerError; @@ -80,10 +78,13 @@ public class Controlify implements ControlifyApi { private Minecraft minecraft = null; private ControllerManager controllerManager; + private ControllerBackend controllerBackend; private ControllerEntity currentController = null; private InputMode currentInputMode = InputMode.KEYBOARD_MOUSE; + private ConfigManager config; + private @Nullable InGameInputHandler inGameInputHandler; public @Nullable InGameButtonGuide inGameButtonGuide; private VirtualMouseHandler virtualMouseHandler; @@ -99,12 +100,8 @@ public class Controlify implements ControlifyApi { private TriggerEffectManager triggerEffectManager; - private Set thisTickContexts; - - private ConfigManager config; + private SetupWizardManager setupWizardManager; - private final Queue setupWizards = new ArrayDeque<>(); - private ControllerSetupWizard currentSetupWizard = null; private boolean hasDiscoveredControllers = false; private int consecutiveInputSwitches = 0; @@ -137,6 +134,8 @@ public void preInitialiseControlify() { this.config.close(); }); + this.setupWizardManager = new SetupWizardManager(); + this.inputFontMapper = new InputFontMapper(); this.defaultBindManager = new DefaultBindManager(); this.defaultConfigManager = new DefaultConfigManager(); @@ -258,15 +257,17 @@ public void initializeControlify() { ScreenProcessorProvider.provide(screen).render(controller, graphics, tickDelta); })); + this.controllerManager = new ControllerManager(); try { Sdl sdl = SDLNativesLoader.load(); - controllerManager = new SDLControllerManager(sdl, CUtil.LOGGER); + this.controllerBackend = new SDLControllerBackend(sdl, this, this.controllerManager); } catch (Throwable throwable) { - CUtil.LOGGER.error("Failed to initialize controller manager", throwable); + CUtil.LOGGER.error("Failed to initialize controller backend", throwable); return; } + this.controllerBackend.setup(); - PlatformClientUtil.registerClientTickStarted(this::tick); + PlatformClientUtil.registerClientTickStarted(this::fixedUpdate); // initialise and compatability modules that controlify implements itself // this does NOT invoke any entrypoints. this is done in the pre-initialisation phase @@ -337,9 +338,9 @@ public void discoverControllers() { DebugLog.log("Discovering and initializing controllers..."); - controllerManager.discoverControllers(); + this.controllerBackend.discoverControllers(); - if (controllerManager.getConnectedControllers().isEmpty()) { + if (this.controllerManager.getAllControllers().isEmpty()) { CUtil.LOGGER.log("No controllers found."); } @@ -364,7 +365,7 @@ public void discoverControllers() { * @param hotplugged if this was a result of hotplugging */ private void onControllerAdded(ControllerEntity controller, boolean hotplugged) { - ControllerSetupWizard wizard = new ControllerSetupWizard(); + SetupWizard wizard = new SetupWizard(); DeviceSettings rememberedDevice = config().getSettings().getOrCreateDeviceSettings(controller.uid()); rememberedDevice.name = controller.name(); rememberedDevice.lastSeen = System.currentTimeMillis(); @@ -378,8 +379,6 @@ private void onControllerAdded(ControllerEntity controller, boolean hotplugged) // wizard.addStage(() -> SubmitUnknownControllerScreen.canSubmit(controller), nextScreen -> new SubmitUnknownControllerScreen(controller, nextScreen)); - // Calibration screen removed - gyro calibration is now automatic via rolling calibration - wizard.addStage( () -> { Optional inputOpt = controller.input(); @@ -410,7 +409,7 @@ private void onControllerAdded(ControllerEntity controller, boolean hotplugged) config().saveIfDirty(); } - setupWizards.add(wizard); + this.setupWizardManager.push(wizard); } /** @@ -434,39 +433,20 @@ private void onControllerRemoved(ControllerEntity controller) { } private void selectFirstConnectedController() { - Optional firstController = controllerManager.getConnectedControllers() + Optional firstController = this.controllerManager.getAllControllers() .stream() .findFirst(); this.setCurrentController(firstController.orElse(null), true); } - /** - * The main loop of Controlify. - * Only the current controller ticks. - */ - public void tick(Minecraft client) { - if (MinecraftUtil.getOverlay() == null) { - if (currentSetupWizard != null && currentSetupWizard.isDone()) { - currentSetupWizard = null; - } - - if (!setupWizards.isEmpty() && !(MinecraftUtil.getScreen() instanceof DontInterruptScreen)) { - currentSetupWizard = setupWizards.poll(); - MinecraftUtil.setScreen(currentSetupWizard.start(MinecraftUtil.getScreen())); - } - } - - boolean outOfFocus = !config().getSettings().globalSettings().outOfFocusInput && !client.isWindowActive(); - - this.thisTickContexts = BindContext.CONTEXTS.values().stream() - .filter(ctx -> ctx.isApplicable().apply(minecraft)) - .collect(Collectors.toUnmodifiableSet()); + /// Called once per frame, just after Minecraft has done its own SDL event polling. + public void update(float tickDelta) { + this.controllerBackend.pollEvents(); - // handles updating state of all controllers - controllerManager.tick(outOfFocus); + this.setupWizardManager.tick(); // handle showing/hiding mouse whilst in mixed input mode - if (minecraft.mouseHandler.isMouseGrabbed()) + if (this.minecraft.mouseHandler.isMouseGrabbed()) showMouseTicks = 0; if (currentInputMode() == InputMode.MIXED && showMouseTicks > 0) { showMouseTicks--; @@ -475,53 +455,42 @@ public void tick(Minecraft client) { } } - LowBatteryNotifier.tick(); + boolean outOfFocus = !config().getSettings().globalSettings().outOfFocusInput + && !this.minecraft.isWindowActive(); - getCurrentController().ifPresent(currentController -> { + this.getCurrentController().ifPresent(currentController -> { wrapControllerError( - () -> tickActiveController(currentController, outOfFocus), - "Ticking current controller", + () -> updateActiveController(currentController, outOfFocus), + "Updating current controller", currentController ); }); - for (ControllerEntity controller : controllerManager.getConnectedControllers()) { - if (controller.equals(getCurrentController().orElse(null))) continue; - - wrapControllerError( - () -> tickInactiveController(controller), - "Ticking inactive controller", - controller - ); - } mouseUsedThisTick = false; } - /** - * Ticks a specific controller. - * - * @param controller controller to tick - * @param outOfFocus if the window is out of focus - */ - private void tickActiveController(ControllerEntity controller, boolean outOfFocus) { - InputComponent input = controller.input().orElseThrow(); - ControllerStateView state = input.stateNow(); - Optional rumbleManager = controller.rumble().map(RumbleComponent::rumbleManager); + /// Called at 20tps + public void fixedUpdate() { + LowBatteryNotifier.tick(); - boolean isPaused = minecraft.isPaused() || MinecraftUtil.getScreen() instanceof PauseScreen; - boolean isConfigScreen = MinecraftUtil.getScreen() instanceof YACLScreen; + boolean outOfFocus = !config().getSettings().globalSettings().outOfFocusInput + && !this.minecraft.isWindowActive(); - rumbleManager.ifPresent(rumble -> rumble.setSilent(outOfFocus || (isPaused && !isConfigScreen) || currentInputMode() == InputMode.KEYBOARD_MOUSE)); - if (outOfFocus) { - state = ControllerState.EMPTY; - } else { - rumbleManager.ifPresent(RumbleManager::tick); - } + this.getCurrentController().ifPresent(currentController -> { + wrapControllerError( + () -> fixedUpdateActiveController(currentController, outOfFocus), + "Fixed-updating current controller", + currentController + ); + }); + } + private void updateActiveController(ControllerEntity controller, boolean outOfFocus) { boolean controllerInputSuppressed = outOfFocus || currentInputMode() == InputMode.KEYBOARD_MOUSE; this.triggerEffectManager().tick(this.minecraft, controller); this.triggerEffectManager().applyTriggerEffects(controller, controllerInputSuppressed); + // TODO: move this to listener based InputPipeline signal approach if (state.isGivingInput()) { minecraft.getFramerateLimitTracker().onInputReceived(); @@ -532,10 +501,6 @@ private void tickActiveController(ControllerEntity controller, boolean outOfFocu } } - if (showMouseTicks > 0 && !mouseUsedThisTick && state.isGivingInput()) { - this.endTemporaryCursor(); - } - if (consecutiveInputSwitches > 100) { CUtil.LOGGER.warn("Controlify detected current controller to be constantly giving input and has been disabled."); MinecraftUtil.sendToast( @@ -548,32 +513,35 @@ private void tickActiveController(ControllerEntity controller, boolean outOfFocu return; } - if (minecraft.level != null) { - this.inGameInputHandler().ifPresent(InGameInputHandler::inputTick); - } - if (this.currentInputMode().isController()) { - if (MinecraftUtil.getScreen() != null) { - ScreenProcessorProvider.provide(MinecraftUtil.getScreen()).onControllerUpdate(controller); - } - ControlifyEvents.ACTIVE_CONTROLLER_TICKED.invoke(new ControlifyEvents.ControllerStateUpdate(controller)); } } - private void tickInactiveController(ControllerEntity controller) { - if (config().getActiveProfile().controllerUid != null) { - return; + private void fixedUpdateActiveController(ControllerEntity controller, boolean outOfFocus) { + Optional rumbleManager = controller.rumble().map(RumbleComponent::rumbleManager); + + boolean isPaused = minecraft.isPaused() || MinecraftUtil.getScreen() instanceof PauseScreen; + boolean isConfigScreen = MinecraftUtil.getScreen() instanceof YACLScreen; + + rumbleManager.ifPresent(rumble -> rumble.setSilent(outOfFocus || (isPaused && !isConfigScreen) || currentInputMode() == InputMode.KEYBOARD_MOUSE)); + if (!outOfFocus) { + rumbleManager.ifPresent(RumbleManager::tick); } - InputComponent input = controller.input().orElseThrow(); - ControllerStateView state = input.stateNow(); + if (showMouseTicks > 0 && !mouseUsedThisTick && state.isGivingInput()) { + // TODO: move this to a InputPipeline listener-based approach + this.endTemporaryCursor(); + } - boolean thisControllerGivingInput = state.isGivingInput(); - boolean activeControllerGivingInput = getCurrentController().map(c -> c.input().orElseThrow().stateNow().isGivingInput()).orElse(false); + if (minecraft.level != null) { + this.inGameInputHandler().ifPresent(InGameInputHandler::fixedUpdate); + } - if (thisControllerGivingInput && !activeControllerGivingInput) { - this.setCurrentController(controller, true); + if (this.currentInputMode().isController()) { + if (MinecraftUtil.getScreen() != null) { + ScreenProcessorProvider.provide(MinecraftUtil.getScreen()).onControllerFixedUpdate(controller); + } } } @@ -581,7 +549,7 @@ public void applyControllerSelection(boolean changeInputMode) { String selectedUid = config().getActiveProfile().controllerUid; Optional selected = controllerManager == null ? Optional.empty() - : controllerManager.getConnectedControllers().stream() + : this.controllerManager.getAllControllers().stream() .filter(controller -> selectedUid == null || selectedUid.equals(controller.uid())) .findFirst(); this.setCurrentController(selected.orElse(null), changeInputMode); @@ -593,8 +561,8 @@ public boolean switchProfile(int profileIndex, boolean remember) throws IOExcept } ProfileSettings profile = config().getActiveProfile(); - if (controllerManager != null) { - controllerManager.getConnectedControllers().forEach(controller -> controller.setSettings(profile)); + if (this.controllerManager != null) { + this.controllerManager.getAllControllers().forEach(controller -> controller.setSettings(profile)); } this.applyControllerSelection(true); this.setupForController(this.currentInputMode.isController() ? this.currentController : null); diff --git a/src/main/java/dev/isxander/controlify/api/event/ControlifyEvents.java b/src/main/java/dev/isxander/controlify/api/event/ControlifyEvents.java index 183c6d320..e96f10ff1 100644 --- a/src/main/java/dev/isxander/controlify/api/event/ControlifyEvents.java +++ b/src/main/java/dev/isxander/controlify/api/event/ControlifyEvents.java @@ -48,7 +48,7 @@ public final class ControlifyEvents { */ public static final EventHandler LOOK_INPUT_MODIFIER = EventHandler.createPlatformBackedEvent(); - public record ControllerConnected(ControllerEntity controller, boolean hotplugged, @Deprecated boolean newController) { + public record ControllerConnected(ControllerEntity controller, boolean hotplugged) { } public record ControllerDisconnected(ControllerEntity controller) { diff --git a/src/main/java/dev/isxander/controlify/controller/info/ControllerInfo.java b/src/main/java/dev/isxander/controlify/controller/info/ControllerInfo.java index 553dd853a..1fc56b074 100644 --- a/src/main/java/dev/isxander/controlify/controller/info/ControllerInfo.java +++ b/src/main/java/dev/isxander/controlify/controller/info/ControllerInfo.java @@ -8,10 +8,7 @@ import dev.isxander.controlify.controller.id.ControllerType; import dev.isxander.controlify.controllermanager.UniqueControllerID; -import dev.isxander.controlify.hid.HIDDevice; import dev.isxander.controlify.hid.HIDID; -import java.util.Optional; - public record ControllerInfo(UniqueControllerID ucid, ControllerType type, HIDID hid) { } diff --git a/src/main/java/dev/isxander/controlify/controllermanager/AbstractControllerManager.java b/src/main/java/dev/isxander/controlify/controllermanager/AbstractControllerManager.java deleted file mode 100644 index 3f9c26ffe..000000000 --- a/src/main/java/dev/isxander/controlify/controllermanager/AbstractControllerManager.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controllermanager; - -import com.google.common.collect.ImmutableList; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.api.event.ControlifyEvents; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.driver.steamdeck.SteamDeckUtil; -import dev.isxander.controlify.hid.ControllerHIDInfo; -import dev.isxander.controlify.hid.HIDDevice; -import dev.isxander.controlify.hid.HIDID; -import dev.isxander.controlify.utils.ControllerUtils; -import dev.isxander.controlify.utils.log.ControlifyLogger; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.minecraft.CrashReport; -import net.minecraft.CrashReportCategory; -import net.minecraft.client.Minecraft; -import net.minecraft.server.packs.resources.ResourceProvider; - -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public abstract class AbstractControllerManager implements ControllerManager { - protected final Controlify controlify; - protected final Minecraft minecraft; - - protected final Map controllersByJid = new Object2ObjectOpenHashMap<>(); - protected final Map controllersByUid = new Object2ObjectOpenHashMap<>(); - protected final Map hidInfoByUid = new Object2ObjectOpenHashMap<>(); - - protected final ControlifyLogger logger; - - public AbstractControllerManager(ControlifyLogger logger) { - this.controlify = Controlify.instance(); - this.minecraft = Minecraft.getInstance(); - this.logger = logger.createSubLogger("ControllerManager"); - } - - public Optional tryCreate(UniqueControllerID ucid, ControllerHIDInfo hidInfo) { - ControlifyLogger controllerLogger = logger.createSubLogger("Controller #" + ucid); - - try { - if (controllersByJid.containsKey(ucid)) { - controllerLogger.warn("Tried to create controller that already is initialised: {}.", ucid); - return Optional.empty(); - } - - if (hidInfo.type().dontLoad()) { - controllerLogger.debugLog("Preventing load of controller #" + ucid + " because its type prevents loading."); - return Optional.empty(); - } - - if (hidInfo.type().isSteamDeck() - && SteamDeckUtil.DECK_MODE.isDesktopMode() - ) { - controllerLogger.log("Preventing load of controller #{} because Steam Deck is in desktop mode.", ucid); - return Optional.empty(); - } - - return createController(ucid, hidInfo, controllerLogger); - } catch (Throwable e) { - controllerLogger.error("Failed to create controller #{}!", e, ucid); - CrashReport crashReport = CrashReport.forThrowable(e, "Creating controller #" + ucid); - CrashReportCategory category = crashReport.addCategory("Controller Info"); - category.setDetail("Unique controller ID", ucid); - category.setDetail("Controller identification", hidInfo.type()); - category.setDetail("HID path", hidInfo.hidDevice().map(HIDDevice::path).orElse("N/A")); - category.setDetail("System name", Optional.ofNullable(getControllerSystemName(ucid)).orElse("N/A")); - controllerLogger.crashReport(crashReport); - return Optional.empty(); - //throw new ReportedException(crashReport); - } - } - - protected abstract Optional createController(UniqueControllerID ucid, ControllerHIDInfo hidInfo, ControlifyLogger controllerLogger); - - @Override - public void tick(boolean outOfFocus) { - for (ControllerEntity controller : controllersByUid.values()) { - controller.update(outOfFocus); - ControlifyEvents.CONTROLLER_STATE_UPDATE.invoke(new ControlifyEvents.ControllerStateUpdate(controller)); - } - } - - protected void onControllerConnected(ControllerEntity controller, boolean hotplug) { - logger.log("Controller connected: {}", ControllerUtils.createControllerString(controller)); - - ControlifyEvents.CONTROLLER_CONNECTED.invoke(new ControlifyEvents.ControllerConnected(controller, hotplug, false)); - } - - protected void onControllerRemoved(ControllerEntity controller) { - logger.log("Controller disconnected: {}", ControllerUtils.createControllerString(controller)); - - closeController(controller.uid()); - - ControlifyEvents.CONTROLLER_DISCONNECTED.invoke(new ControlifyEvents.ControllerDisconnected(controller)); - } - - @Override - public Optional reinitController(ControllerEntity controller, ControllerHIDInfo hidInfo) { - onControllerRemoved(controller); - - Optional newController = tryCreate(controller.info().ucid(), hidInfo); - newController.ifPresent(c -> { - ControllerUtils.wrapControllerError(() -> onControllerConnected(c, true), "Connecting controller", c); - }); - return newController; - } - - protected void addController(UniqueControllerID ucid, ControllerEntity controller) { - controllersByUid.put(controller.uid(), controller); - controllersByJid.put(ucid, controller); - } - - @Override - public void closeController(String uid) { - ControllerEntity controller = controllersByUid.remove(uid); - - if (controller == null) return; - - controller.close(); - - controllersByJid.remove(controller.info().ucid()); - hidInfoByUid.remove(uid); - } - - @Override - public List getConnectedControllers() { - return ImmutableList.copyOf(controllersByUid.values()); - } - - @Override - public boolean isControllerConnected(String uid) { - return controllersByUid.containsKey(uid); - } - - protected int getControllerCountWithMatchingHID(HIDID hid) { - return (int) controllersByJid.values().stream() - .filter(c -> c.info().hid().isPresent() && c.info().hid().get().hidid().equals(hid)) - .count(); - } - - @Override - public void close() { - controllersByUid.values().forEach(ControllerEntity::close); - } - - protected abstract void loadGamepadMappings(ResourceProvider resourceProvider); - - protected abstract String getControllerSystemName(UniqueControllerID ucid); -} diff --git a/src/main/java/dev/isxander/controlify/controllermanager/ControllerBackend.java b/src/main/java/dev/isxander/controlify/controllermanager/ControllerBackend.java new file mode 100644 index 000000000..75317ffdb --- /dev/null +++ b/src/main/java/dev/isxander/controlify/controllermanager/ControllerBackend.java @@ -0,0 +1,33 @@ +package dev.isxander.controlify.controllermanager; + +import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.controller.id.ControllerType; +import dev.isxander.controlify.hid.HIDID; + +/// A controller backend is responsible for listening to controller events +/// and creating controllers using the referenced {@link ControllerManager controller manager}. +public abstract class ControllerBackend { + protected final Controlify controlify; + protected final ControllerManager controllerManager; + + public ControllerBackend(Controlify controlify, ControllerManager controllerManager) { + this.controlify = controlify; + this.controllerManager = controllerManager; + } + + /// Called once and allows the backend to do any first-time setup + /// before beginning to poll events and discover controllers. + public abstract void setup(); + + /// Called once after {@link setup}, discovers already-connected + /// controllers before ever polling events. + public abstract void discoverControllers(); + + /// Called once per frame, polls events from the backend and processes + /// them, potentially creating controllers and adding them to {@link ControllerManager}. + public abstract void pollEvents(); + + protected ControllerType getControllerType(HIDID hidId) { + return this.controlify.controllerTypeManager().getControllerType(hidId); + } +} diff --git a/src/main/java/dev/isxander/controlify/controllermanager/ControllerManager.java b/src/main/java/dev/isxander/controlify/controllermanager/ControllerManager.java index 9f7cb531a..e8f0811cb 100644 --- a/src/main/java/dev/isxander/controlify/controllermanager/ControllerManager.java +++ b/src/main/java/dev/isxander/controlify/controllermanager/ControllerManager.java @@ -1,33 +1,95 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ package dev.isxander.controlify.controllermanager; +import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.api.event.ControlifyEvents; import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.hid.ControllerHIDInfo; +import dev.isxander.controlify.controller.id.ControllerType; +import dev.isxander.controlify.driver.steamdeck.SteamDeckUtil; +import dev.isxander.controlify.hid.HIDID; +import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.utils.ControllerUtils; +import dev.isxander.controlify.utils.log.ControlifyLogger; -import java.util.List; -import java.util.Optional; +import java.io.Closeable; +import java.util.*; -public interface ControllerManager { - void discoverControllers(); +public class ControllerManager implements Closeable { + private final Map controllersByUid; + private final Map controllersByUcid; - void tick(boolean outOfFocus); + public ControllerManager() { + this.controllersByUid = new HashMap<>(); + this.controllersByUcid = new HashMap<>(); + } - boolean probeConnectedControllers(); + public boolean canAddController( + UniqueControllerID ucid, + HIDID hidid, + ControllerType controllerType, + ControlifyLogger controllerLogger + ) { + if (this.controllersByUcid.containsKey(ucid)) { + controllerLogger.warn("Tried to create controller that already is initialised: {}.", ucid); + return false; + } - List getConnectedControllers(); + if (controllerType.dontLoad()) { + controllerLogger.debugLog("Preventing load of controller #" + ucid + " because its type prevents loading."); + return false; + } - boolean isControllerConnected(String uid); + if (controllerType.isSteamDeck() + && SteamDeckUtil.DECK_MODE.isDesktopMode() + ) { + controllerLogger.log("Preventing load of controller #{} because Steam Deck is in desktop mode.", ucid); + return false; + } - boolean isControllerGamepad(UniqueControllerID ucid); + return true; + } - Optional reinitController(ControllerEntity controller, ControllerHIDInfo hidInfo); + public void addController(ControllerEntity controller, boolean hotplugged) { + this.controllersByUcid.put(controller.info().ucid(), controller); + this.controllersByUid.put(controller.uid(), controller); - void closeController(String uid); + CUtil.LOGGER.log("Controller connected: {}", ControllerUtils.createControllerString(controller)); - void close(); + ControllerUtils.wrapControllerError( + () -> ControlifyEvents.CONTROLLER_CONNECTED.invoke(new ControlifyEvents.ControllerConnected(controller, hotplugged)), + "Connecting controller", + controller + ); + } + + public void removeController(UniqueControllerID controllerUcid) { + ControllerEntity controller = this.controllersByUcid.get(controllerUcid); + + ControlifyEvents.CONTROLLER_DISCONNECTED.invoke(new ControlifyEvents.ControllerDisconnected(controller)); + + if (controller == null) { + throw new IllegalArgumentException("The given ucid is not associated with any connected controller"); + } + + this.controllersByUcid.remove(controllerUcid, controller); + this.controllersByUid.remove(controller.uid(), controller); + + controller.close(); + } + + public Optional getControllerByUcid(UniqueControllerID ucid) { + return Optional.ofNullable(this.controllersByUcid.get(ucid)); + } + + public Optional getControllerByUid(String uid) { + return Optional.ofNullable(this.controllersByUid.get(uid)); + } + + public Collection getAllControllers() { + return Collections.unmodifiableCollection(this.controllersByUcid.values()); + } + + @Override + public void close() { + this.controllersByUcid.values().forEach(ControllerEntity::close); + } } diff --git a/src/main/java/dev/isxander/controlify/controllermanager2/SDLControllerManagerBackend.java b/src/main/java/dev/isxander/controlify/controllermanager/SDLControllerBackend.java similarity index 79% rename from src/main/java/dev/isxander/controlify/controllermanager2/SDLControllerManagerBackend.java rename to src/main/java/dev/isxander/controlify/controllermanager/SDLControllerBackend.java index 4d3e8ade4..2b437f4ac 100644 --- a/src/main/java/dev/isxander/controlify/controllermanager2/SDLControllerManagerBackend.java +++ b/src/main/java/dev/isxander/controlify/controllermanager/SDLControllerBackend.java @@ -1,11 +1,10 @@ -package dev.isxander.controlify.controllermanager2; +package dev.isxander.controlify.controllermanager; import dev.isxander.controlify.Controlify; import dev.isxander.controlify.config.settings.profile.ProfileSettings; import dev.isxander.controlify.controller.ControllerEntity; import dev.isxander.controlify.controller.id.ControllerType; import dev.isxander.controlify.controller.info.ControllerInfo; -import dev.isxander.controlify.controllermanager.SDLUniqueControllerID; import dev.isxander.controlify.debug.DebugProperties; import dev.isxander.controlify.driver.Driver; import dev.isxander.controlify.driver.sdl.*; @@ -22,18 +21,17 @@ import java.nio.ByteBuffer; import java.util.Optional; -public class SDLControllerManagerBackend implements ControllerManagerBackend { +public class SDLControllerBackend extends ControllerBackend { private final Sdl sdl; - private final ControllerManager controllerManager; private final ControlifyLogger logger; private final SDLControllerEventStage controllerEventStage; private final EventTap controllerEventTap; private SdlEvent event; - public SDLControllerManagerBackend(Sdl sdl, ControllerManager controllerManager) { + public SDLControllerBackend(Sdl sdl, Controlify controlify, ControllerManager controllerManager) { + super(controlify, controllerManager); this.sdl = sdl; - this.controllerManager = controllerManager; this.logger = CUtil.LOGGER.createSubLogger("SDLControllerManagerBackend"); this.controllerEventStage = new SDLControllerEventStage(); this.controllerEventTap = new EventTap<>(); @@ -80,6 +78,16 @@ public void setup() { } } + @Override + public void discoverControllers() { + this.logger.debugLog("Discovering controllers..."); + + SdlJoystickId[] joysticks = this.sdl.joystick().SDL_GetJoysticks(); + for (SdlJoystickId jid : joysticks) { + this.tryCreateAndAddController(jid, false); + } + } + @Override public void pollEvents() { if (this.event == null) { @@ -98,17 +106,7 @@ private void onControllerEvent(SDLControllerEvent event) { switch (event) { case SDLControllerEvent.JoyDevice(SdlJoystickId which, _, SDLControllerEvent.DeviceEvent.Type eventType) when eventType == SDLControllerEvent.DeviceEvent.Type.ADDED -> { - var ucid = new SDLUniqueControllerID(which); - - var logger = this.logger.createSubLogger("Controller #" + ucid); - logger.debugLog("New controller detected!"); - - HIDID hidId = this.getJoystickHIDID(which); - ControllerType type = this.controllerManager.getControllerType(hidId); - - if (this.controllerManager.canAddController(ucid, hidId, type, logger)) { - this.createAndAddController(ucid, hidId, type, logger); - } + this.tryCreateAndAddController(which, true); } case SDLControllerEvent.JoyDevice(SdlJoystickId which, _, SDLControllerEvent.DeviceEvent.Type type) @@ -120,7 +118,27 @@ private void onControllerEvent(SDLControllerEvent event) { } } - private void createAndAddController(SDLUniqueControllerID ucid, HIDID hidId, ControllerType type, ControlifyLogger controllerLogger) { + private void tryCreateAndAddController(SdlJoystickId jid, boolean hotplugged) { + var ucid = new SDLUniqueControllerID(jid); + + var logger = this.logger.createSubLogger("Controller #" + ucid); + logger.debugLog("New controller detected!"); + + HIDID hidId = this.getJoystickHIDID(jid); + ControllerType type = this.getControllerType(hidId); + + if (this.controllerManager.canAddController(ucid, hidId, type, logger)) { + this.createAndAddController(ucid, hidId, type, logger, hotplugged); + } + } + + private void createAndAddController( + SDLUniqueControllerID ucid, + HIDID hidId, + ControllerType type, + ControlifyLogger controllerLogger, + boolean hotplugged + ) { SdlJoystickId jid = ucid.jid(); controllerLogger.debugLog("Creating controller: {}", jid.value()); @@ -131,7 +149,6 @@ private void createAndAddController(SDLUniqueControllerID ucid, HIDID hidId, Con .filter(event -> event.which().equals(jid)); Driver driver; - if (isGamepad) { SdlGamepadHandle gamepadHandle = SDLUtil.openGamepad(this.sdl, jid); driver = new SDL3GamepadDriver(this.sdl, gamepadHandle, controllerEventSource, jid, type, controllerLogger); @@ -149,13 +166,7 @@ private void createAndAddController(SDLUniqueControllerID ucid, HIDID hidId, Con controllerLogger ); - this.controllerManager.addController(controller); - - } - - @Override - public void closeController(ControllerEntity controller) { - + this.controllerManager.addController(controller, hotplugged); } private HIDID getJoystickHIDID(SdlJoystickId jid) { diff --git a/src/main/java/dev/isxander/controlify/controllermanager/SDLControllerManager.java b/src/main/java/dev/isxander/controlify/controllermanager/SDLControllerManager.java deleted file mode 100644 index 280f6a224..000000000 --- a/src/main/java/dev/isxander/controlify/controllermanager/SDLControllerManager.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controllermanager; - -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.config.settings.profile.ProfileSettings; -import dev.isxander.controlify.controller.info.ControllerInfo; -import dev.isxander.controlify.controller.id.ControllerType; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.debug.DebugProperties; -import dev.isxander.controlify.driver.CompoundDriver; -import dev.isxander.controlify.driver.Driver; -import dev.isxander.controlify.driver.sdl.SDL3GamepadDriver; -import dev.isxander.controlify.driver.sdl.SDL3JoystickDriver; -import dev.isxander.controlify.driver.sdl.SDLUtil; -import dev.isxander.controlify.driver.steamdeck.SteamDeckDriver; -import dev.isxander.controlify.driver.steamdeck.SteamDeckUtil; -import dev.isxander.controlify.hid.ControllerHIDInfo; -import dev.isxander.controlify.hid.HIDDevice; -import dev.isxander.controlify.hid.HIDID; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.ControllerUtils; -import dev.isxander.controlify.utils.log.ControlifyLogger; -import dev.isxander.sdl.*; -import net.minecraft.server.packs.resources.Resource; -import net.minecraft.server.packs.resources.ResourceProvider; - -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.util.*; -import java.util.stream.Collectors; - -import static dev.isxander.sdl.SdlEvents.*; - -public class SDLControllerManager extends AbstractControllerManager { - - private final Sdl sdl; - - private SdlEvent event = new SdlEvent(); - - private boolean steamDeckConsumed = false; - - public SDLControllerManager(Sdl sdl, ControlifyLogger logger) { - super(logger); - this.sdl = sdl; - logger.debugLog("Controller manager using SDL3"); - - this.loadGamepadMappings(minecraft.getResourceManager()); - } - - @Override - public void tick(boolean outOfFocus) { - if (event == null) { - logger.warn("SDL_Event has somehow been set to null. Recreating..."); - event = new SdlEvent(); - } - - while (sdl.events().SDL_PollEvent(event)) { - switch (event.type()) { - // On added, `which` refers to the device index - case SDL_EVENT_JOYSTICK_ADDED -> { - var jdevice = (SdlEvent.JoyDevice) event.data(); - SdlJoystickId jid = jdevice.which(); - logger.validateIsTrue(jid != null, "event.jdevice.which was null during SDL_EVENT_JOYSTICK_ADDED event"); - - logger.debugLog("SDL event: Joystick added: {}", jid.value()); - - UniqueControllerID ucid = new SDLUniqueControllerID(jid); - - Optional controllerOpt = tryCreate( - ucid, - fetchTypeFromSDL(sdl, jid) - .orElse(new ControllerHIDInfo(ControllerType.DEFAULT, Optional.empty())) - ); - controllerOpt.ifPresent(controller -> { - ControllerUtils.wrapControllerError(() -> onControllerConnected(controller, true), "Connecting controller", controller); - }); - } - - // On removed, `which` refers to the device instance ID - case SDL_EVENT_JOYSTICK_REMOVED -> { - var jdevice = (SdlEvent.JoyDevice) event.data(); - SdlJoystickId jid = jdevice.which(); - logger.validateIsTrue(jid != null, "event.jdevice.which was null during SDL_EVENT_JOYSTICK_REMOVED event"); - - logger.debugLog("SDL event: Joystick removed: {}", jid.value()); - - getController(new SDLUniqueControllerID(jid)) - .ifPresentOrElse( - this::onControllerRemoved, - () -> CUtil.LOGGER.warn("Controller removed but not found: {}", jid.value()) - ); - } - } - } - - super.tick(outOfFocus); - } - - @Override - public void discoverControllers() { - logger.debugLog("Discovering controllers..."); - - SdlJoystickId[] joysticks = sdl.joystick().SDL_GetJoysticks(); - for (SdlJoystickId jid : joysticks) { - Optional controllerOpt = tryCreate( - new SDLUniqueControllerID(jid), - fetchTypeFromSDL(sdl, jid) - .orElse(new ControllerHIDInfo(ControllerType.DEFAULT, Optional.empty())) - ); - controllerOpt.ifPresent(controller -> onControllerConnected(controller, false)); - } - } - - @Override - protected Optional createController(UniqueControllerID ucid, ControllerHIDInfo hidInfo, ControlifyLogger controllerLogger) { - SdlJoystickId jid = ((SDLUniqueControllerID) ucid).jid(); - controllerLogger.debugLog("Creating controller: {}", jid.value()); - - boolean isGamepad = isControllerGamepad(ucid) && !DebugProperties.FORCE_JOYSTICK; - controllerLogger.debugLog("Controller is gamepad: {}", isGamepad); - - List drivers = new ArrayList<>(); - if ((SteamDeckUtil.DECK_MODE.isGamingMode() || DebugProperties.STEAM_DECK_CUSTOM_CEF_URL != null) - && !steamDeckConsumed - && hidInfo.type().namespace().equals(SteamDeckUtil.STEAM_DECK_NAMESPACE) - ) { - controllerLogger.debugLog("Controller is steam deck candidate"); - Optional steamDeckDriver = SteamDeckDriver.create(controllerLogger); - if (steamDeckDriver.isPresent()) { - drivers.add(steamDeckDriver.get()); - steamDeckConsumed = true; - controllerLogger.debugLog("Adding SteamDeckDriver - this controller has been reserved for Steam Deck"); - } - } - - if (isGamepad) { - SdlGamepadHandle ptrGamepad = SDLUtil.openGamepad(sdl, jid); - drivers.add(new SDL3GamepadDriver(sdl, ptrGamepad, jid, hidInfo.type(), controllerLogger)); - } else { - SdlJoystickHandle ptrJoystick = SDLUtil.openJoystick(sdl, jid); - drivers.add(new SDL3JoystickDriver(sdl, ptrJoystick, jid, hidInfo.type(), controllerLogger)); - } - - controllerLogger.debugLog("Drivers: {}", drivers.stream().map(driver -> driver.getClass().getSimpleName()).collect(Collectors.joining(", "))); - - CompoundDriver compoundDriver = new CompoundDriver(drivers); - - ControllerInfo info = new ControllerInfo(ucid, hidInfo.type(), hidInfo.hidDevice()); - ControllerEntity controller = new ControllerEntity( - info, - compoundDriver, - this.controlify.config().getActiveProfile(), - ProfileSettings.createDefault(), - controllerLogger - ); - - controllerLogger.debugLog("Unique Controller ID: {}", info.ucid()); - - this.addController(ucid, controller); - return Optional.of(controller); - } - - @Override - public boolean probeConnectedControllers() { - return sdl.joystick().SDL_HasJoystick() || sdl.gamepad().SDL_HasGamepad(); - } - - @Override - public boolean isControllerGamepad(UniqueControllerID ucid) { - SdlJoystickId jid = ((SDLUniqueControllerID) ucid).jid; - return sdl.gamepad().SDL_IsGamepad(jid); - } - - @Override - protected String getControllerSystemName(UniqueControllerID ucid) { - SdlJoystickId jid = ((SDLUniqueControllerID) ucid).jid; - return isControllerGamepad(ucid) - ? sdl.gamepad().SDL_GetGamepadNameForID(jid) - : sdl.joystick().SDL_GetJoystickNameForID(jid); - } - - private Optional getController(UniqueControllerID ucid) { - return Optional.ofNullable(controllersByJid.getOrDefault(ucid, null)); - } - - @Override - protected void loadGamepadMappings(ResourceProvider resourceProvider) { - CUtil.LOGGER.debugLog("Loading gamepad mappings..."); - - Optional resourceOpt = resourceProvider - .getResource(CUtil.rl("controllers/gamecontrollerdb-sdl3.txt")); - if (resourceOpt.isEmpty()) { - CUtil.LOGGER.error("Failed to find game controller database."); - return; - } - - try (InputStream is = resourceOpt.get().open()) { - byte[] bytes = is.readAllBytes(); - ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bytes.length); - byteBuffer.put(bytes); - byteBuffer.flip(); - - SdlIoStreamHandle stream = sdl.ioStream().SDL_IOFromConstMem(byteBuffer); - if (stream == null) throw new IllegalStateException("Failed to open stream"); - - int count = sdl.gamepad().SDL_AddGamepadMappingsFromIO(stream, true); - if (count < 0) { - CUtil.LOGGER.error("Failed to load gamepad mappings: {}", sdl.error().SDL_GetError()); - } else if (count == 0) { - CUtil.LOGGER.warn("Successfully applied gamepad mappings but none were found for this OS. Unsupported OS?"); - } else { - CUtil.LOGGER.log("Successfully loaded {} gamepad mapping entries!", count); - } - } catch (Throwable e) { - CUtil.LOGGER.error("Failed to load gamepad mappings", e); - } - } - - private static Optional fetchTypeFromSDL(Sdl sdl, SdlJoystickId jid) { - int vid = sdl.joystick().SDL_GetJoystickVendorForID(jid); - int pid = sdl.joystick().SDL_GetJoystickProductForID(jid); - SdlGuid guid = sdl.joystick().SDL_GetJoystickGUIDForID(jid); - String guidStr = guid.toString(); - - if (vid != 0 && pid != 0) { - CUtil.LOGGER.log("Using SDL to identify controller type."); - return Optional.of(new ControllerHIDInfo( - Controlify.instance().controllerTypeManager().getControllerType(new HIDID(vid, pid)), - Optional.of(new HIDDevice(new HIDID(vid, pid), guidStr)) - )); - } - - return Optional.empty(); - } - -} diff --git a/src/main/java/dev/isxander/controlify/controllermanager2/ControllerManager.java b/src/main/java/dev/isxander/controlify/controllermanager2/ControllerManager.java deleted file mode 100644 index a74a58593..000000000 --- a/src/main/java/dev/isxander/controlify/controllermanager2/ControllerManager.java +++ /dev/null @@ -1,22 +0,0 @@ -package dev.isxander.controlify.controllermanager2; - -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.id.ControllerType; -import dev.isxander.controlify.controllermanager.UniqueControllerID; -import dev.isxander.controlify.hid.HIDID; -import dev.isxander.controlify.utils.log.ControlifyLogger; - -public interface ControllerManager { - boolean canAddController( - UniqueControllerID ucid, - HIDID hidid, - ControllerType controllerType, - ControlifyLogger controllerLogger - ); - - void addController(ControllerEntity controller); - - void removeController(UniqueControllerID controller); - - ControllerType getControllerType(HIDID hidId); -} diff --git a/src/main/java/dev/isxander/controlify/controllermanager2/ControllerManagerBackend.java b/src/main/java/dev/isxander/controlify/controllermanager2/ControllerManagerBackend.java deleted file mode 100644 index 78a77c5be..000000000 --- a/src/main/java/dev/isxander/controlify/controllermanager2/ControllerManagerBackend.java +++ /dev/null @@ -1,12 +0,0 @@ -package dev.isxander.controlify.controllermanager2; - -import dev.isxander.controlify.controller.ControllerEntity; - -public interface ControllerManagerBackend { - - void setup(); - - void pollEvents(); - - void closeController(ControllerEntity controller); -} diff --git a/src/main/java/dev/isxander/controlify/gui/screen/BindConsumerScreen.java b/src/main/java/dev/isxander/controlify/gui/screen/BindConsumerScreen.java index b0b2700a5..764eb1ebf 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/BindConsumerScreen.java +++ b/src/main/java/dev/isxander/controlify/gui/screen/BindConsumerScreen.java @@ -150,7 +150,7 @@ public ScreenProcessorImpl(BindConsumerScreen screen) { } @Override - public void onControllerUpdate(ControllerEntity controller) { + public void onControllerFixedUpdate(ControllerEntity controller) { // prevent all other controller input logic } } diff --git a/src/main/java/dev/isxander/controlify/gui/screen/ControllerMappingMakerScreen.java b/src/main/java/dev/isxander/controlify/gui/screen/ControllerMappingMakerScreen.java index 42c5c4b7d..01dd89c0a 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/ControllerMappingMakerScreen.java +++ b/src/main/java/dev/isxander/controlify/gui/screen/ControllerMappingMakerScreen.java @@ -359,7 +359,7 @@ public ScreenProcessorImpl(ControllerMappingMakerScreen screen) { } @Override - public void onControllerUpdate(ControllerEntity controller) { + public void onControllerFixedUpdate(ControllerEntity controller) { } } diff --git a/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java b/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java index f62ceecd2..4f8a86587 100644 --- a/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java +++ b/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java @@ -41,7 +41,6 @@ import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; import net.minecraft.server.commands.GameModeCommand; import net.minecraft.util.Mth; -import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.Vec2; @@ -83,7 +82,7 @@ public InGameInputHandler(ControllerEntity controller) { this.gyroToggledOn = false; } - public void inputTick() { + public void fixedUpdate() { boolean isController = ControllerPlayerMovement.shouldBeControllerInput(); handlePlayerLookInput(isController); diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionContext.java b/src/main/java/dev/isxander/controlify/input/action/ActionContext.java index 477527a15..6a93fc9d9 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionContext.java +++ b/src/main/java/dev/isxander/controlify/input/action/ActionContext.java @@ -1,9 +1,12 @@ package dev.isxander.controlify.input.action; +import net.minecraft.client.Minecraft; import net.minecraft.resources.Identifier; import org.jspecify.annotations.NonNull; -public record ActionContext(Identifier id) implements Comparable { +import java.util.function.Function; + +public record ActionContext(Identifier id, Function isApplicable) implements Comparable { @Override public int compareTo(@NonNull ActionContext o) { return this.id().compareTo(o.id()); 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 3eba1f2a2..2a25963d8 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/MinecraftMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/MinecraftMixin.java @@ -39,8 +39,13 @@ private void initControlifyNow(CallbackInfo ci) { } } + @Inject(method = "runTick", at = @At("HEAD")) + private void doPolling(CallbackInfo ci) { + Controlify.instance().update(getTickDelta()); + } + @Inject(method = "runTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MouseHandler;handleAccumulatedMovement()V")) - private void doPlayerLook(boolean advanceGameTime, CallbackInfo ci) { + private void doPlayerLook(CallbackInfo ci) { Controlify.instance().inGameInputHandler().ifPresent(ih -> ih.processPlayerLook(getTickDelta())); } diff --git a/src/main/java/dev/isxander/controlify/screenop/ScreenProcessor.java b/src/main/java/dev/isxander/controlify/screenop/ScreenProcessor.java index 64eeb95dd..ac04de7df 100644 --- a/src/main/java/dev/isxander/controlify/screenop/ScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/screenop/ScreenProcessor.java @@ -58,7 +58,7 @@ public ScreenProcessor(T screen) { this.holdRepeatHelper = createHoldRepeatHelper(); } - public void onControllerUpdate(ControllerEntity controller) { + public void onControllerFixedUpdate(ControllerEntity controller) { Controlify.instance().virtualMouseHandler().handleControllerInput(controller); if (!Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled()) { diff --git a/src/main/java/dev/isxander/controlify/utils/ControllerUtils.java b/src/main/java/dev/isxander/controlify/utils/ControllerUtils.java index a43df5f04..3c9b55f70 100644 --- a/src/main/java/dev/isxander/controlify/utils/ControllerUtils.java +++ b/src/main/java/dev/isxander/controlify/utils/ControllerUtils.java @@ -24,7 +24,7 @@ public static String createControllerString(ControllerEntity controller) { return String.format("'%s'#%s-%s (%s)", controller.name(), controller.info().ucid().toString(), - controller.info().hid().map(hid -> hid.hidid().toString()).orElse("hid"), + controller.info().hid().toString(), controller.info().type().friendlyName() ); } diff --git a/src/main/java/dev/isxander/controlify/utils/ControllerSetupWizard.java b/src/main/java/dev/isxander/controlify/utils/SetupWizard.java similarity index 97% rename from src/main/java/dev/isxander/controlify/utils/ControllerSetupWizard.java rename to src/main/java/dev/isxander/controlify/utils/SetupWizard.java index 66c592ea1..224ef4934 100644 --- a/src/main/java/dev/isxander/controlify/utils/ControllerSetupWizard.java +++ b/src/main/java/dev/isxander/controlify/utils/SetupWizard.java @@ -14,7 +14,7 @@ /** * Collection of screens that are in a specific order to set up various aspects of a controller. */ -public class ControllerSetupWizard { +public class SetupWizard { private final Deque stages = new ArrayDeque<>(); private final List screens = new ArrayList<>(); diff --git a/src/main/java/dev/isxander/controlify/utils/SetupWizardManager.java b/src/main/java/dev/isxander/controlify/utils/SetupWizardManager.java new file mode 100644 index 000000000..ca601f2ca --- /dev/null +++ b/src/main/java/dev/isxander/controlify/utils/SetupWizardManager.java @@ -0,0 +1,28 @@ +package dev.isxander.controlify.utils; + +import dev.isxander.controlify.gui.screen.DontInterruptScreen; + +import java.util.ArrayDeque; +import java.util.Queue; + +public class SetupWizardManager { + private final Queue setupWizards = new ArrayDeque<>(); + private SetupWizard currentSetupWizard = null; + + public void push(SetupWizard setupWizard) { + this.setupWizards.add(setupWizard); + } + + public void tick() { + if (MinecraftUtil.getOverlay() == null) { + if (this.currentSetupWizard != null && this.currentSetupWizard.isDone()) { + this.currentSetupWizard = null; + } + + if (!this.setupWizards.isEmpty() && !(MinecraftUtil.getScreen() instanceof DontInterruptScreen)) { + this.currentSetupWizard = this.setupWizards.poll(); + MinecraftUtil.setScreen(this.currentSetupWizard.start(MinecraftUtil.getScreen())); + } + } + } +} diff --git a/src/main/java/dev/isxander/controlify/wireless/LowBatteryNotifier.java b/src/main/java/dev/isxander/controlify/wireless/LowBatteryNotifier.java index 1c89d4ba3..c06014598 100644 --- a/src/main/java/dev/isxander/controlify/wireless/LowBatteryNotifier.java +++ b/src/main/java/dev/isxander/controlify/wireless/LowBatteryNotifier.java @@ -35,18 +35,18 @@ public static void tick() { if (controllerManager == null) return; - for (ControllerEntity controller : controllerManager.getConnectedControllers()) { + for (ControllerEntity controller : controllerManager.getAllControllers()) { PowerState batteryLevel = controller.batteryLevel() .map(BatteryLevelComponent::getBatteryLevel) .orElse(new PowerState.Unknown()); String uid = controller.uid(); - if (batteryLevel instanceof PowerState.Depleting depleting && depleting.percent() <= 10) { + if (batteryLevel instanceof PowerState.Depleting(int percent) && percent <= 10) { if (!notifiedControllers.contains(uid)) { MinecraftUtil.sendToast( Component.translatable("controlify.toast.low_battery.title"), - Component.translatable("controlify.toast.low_battery.message", controller.name(), depleting.percent() + "%"), + Component.translatable("controlify.toast.low_battery.message", controller.name(), percent + "%"), true ); From a1ed3ce68d0f36ab7b890283c2a60f77e1102e1e Mon Sep 17 00:00:00 2001 From: isxander Date: Fri, 16 Oct 2026 12:42:10 +0100 Subject: [PATCH 08/44] extract some of Controlify into ControllerCoordinator; start to reimplement ControllerMapping for pipeline; implement radial deadzones --- gradle/libs.versions.toml | 2 +- .../dev/isxander/controlify/Controlify.java | 245 ++++-------------- .../api/event/ControlifyEvents.java | 10 + .../controller/ControllerEntity.java | 14 + .../controller/gyro/GyroCalibrator.java | 63 ++--- .../controller/gyro/GyroComponent.java | 106 +++++--- .../controller/input/GamepadInputs.java | 1 - .../input/mapping/ControllerMapping.java | 50 ++-- .../controller/input/mapping/MapType.java | 4 +- .../input/mapping/MappingEntry.java | 160 +++--------- .../input/mapping/MappingEntryCodecs.java | 130 ++-------- .../controller/input/mapping/StateMapper.java | 13 - .../ControllerCoordinator.java | 241 +++++++++++++++++ .../SDLControllerBackend.java | 9 +- .../controlify/driver/CompoundDriver.java | 7 + .../isxander/controlify/driver/Driver.java | 4 + .../driver/sdl/SDL3GamepadDriver.java | 58 +++-- .../driver/sdl/SDLCommonDriver.java | 6 +- .../driver/sdl/SDLControllerEvent.java | 20 +- .../driver/sdl/SDLToPhysicalStage.java | 3 + .../screen/ControllerMappingMakerScreen.java | 31 --- .../controlify/input/InputComponent.java | 16 +- .../input/logical/SensorAccumulatorStage.java | 26 +- .../input/physical/DeadzoneStage.java | 105 ++++++-- .../input/physical/SensorProcessorStage.java | 8 +- .../mixins/core/MinecraftMixin.java | 4 +- 26 files changed, 687 insertions(+), 649 deletions(-) delete mode 100644 src/main/java/dev/isxander/controlify/controller/input/mapping/StateMapper.java create mode 100644 src/main/java/dev/isxander/controlify/controllermanager/ControllerCoordinator.java diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 56ccb5774..be2e9fc64 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] fabric_loader = "0.19.3" sdl_natives = "release-3.4.12" -sdl_java = "release-3.4.14-7" +sdl_java = "release-3.4.14-8" steamdeck4j = "1.1.7" quilt_parsers = "0.3.0" diff --git a/src/main/java/dev/isxander/controlify/Controlify.java b/src/main/java/dev/isxander/controlify/Controlify.java index ad4df0da7..662f62848 100644 --- a/src/main/java/dev/isxander/controlify/Controlify.java +++ b/src/main/java/dev/isxander/controlify/Controlify.java @@ -20,25 +20,21 @@ import dev.isxander.controlify.compatibility.ControlifyCompat; import dev.isxander.controlify.config.ConfigManager; import dev.isxander.controlify.config.dto.profile.defaults.DefaultConfigManager; -import dev.isxander.controlify.config.settings.device.DeviceSettings; -import dev.isxander.controlify.config.settings.profile.ProfileSettings; import dev.isxander.controlify.contextual.ContextualDomains; import dev.isxander.controlify.contextual.RuleSetManager; import dev.isxander.controlify.controller.*; import dev.isxander.controlify.controller.dualsense.TriggerEffectManager; import dev.isxander.controlify.controller.id.ControllerTypeManager; -import dev.isxander.controlify.controller.input.InputComponent; import dev.isxander.controlify.controller.rumble.RumbleComponent; -import dev.isxander.controlify.controllermanager.ControllerBackend; +import dev.isxander.controlify.controllermanager.ControllerCoordinator; import dev.isxander.controlify.controllermanager.ControllerManager; -import dev.isxander.controlify.controllermanager.SDLControllerBackend; -import dev.isxander.controlify.driver.sdl.SDLNativesLoader; import dev.isxander.controlify.driver.steamdeck.SteamDeckMode; import dev.isxander.controlify.driver.steamdeck.SteamDeckUtil; import dev.isxander.controlify.font.InputFontMapper; import dev.isxander.controlify.gui.screen.*; import dev.isxander.controlify.debug.DebugProperties; import dev.isxander.controlify.ingame.ControllerPlayerMovement; +import dev.isxander.controlify.input.InputComponent; import dev.isxander.controlify.input.action.activator.ActivatorConfigManager; import dev.isxander.controlify.platform.client.PlatformClientUtil; import dev.isxander.controlify.platform.main.PlatformMainUtil; @@ -56,7 +52,6 @@ import dev.isxander.controlify.utils.*; import dev.isxander.controlify.virtualmouse.VirtualMouseHandler; import dev.isxander.controlify.wireless.LowBatteryNotifier; -import dev.isxander.sdl.Sdl; import dev.isxander.yacl3.gui.YACLScreen; import net.minecraft.client.KeyMapping; import net.minecraft.client.Minecraft; @@ -77,10 +72,7 @@ public class Controlify implements ControlifyApi { private Minecraft minecraft = null; - private ControllerManager controllerManager; - private ControllerBackend controllerBackend; - - private ControllerEntity currentController = null; + private ControllerCoordinator controllerCoordinator; private InputMode currentInputMode = InputMode.KEYBOARD_MOUSE; private ConfigManager config; @@ -102,13 +94,11 @@ public class Controlify implements ControlifyApi { private SetupWizardManager setupWizardManager; - private boolean hasDiscoveredControllers = false; - private int consecutiveInputSwitches = 0; private double lastInputSwitchTime = 0; private int showMouseTicks = 0; - private boolean mouseUsedThisTick = false; + private boolean mouseUsedThisFrame = false; /** * Called at usual fabric client entrypoint and in NeoForge mod constructor @@ -131,6 +121,7 @@ public void preInitialiseControlify() { PlatformMainUtil.getConfigDir() ); PlatformClientUtil.registerClientStopping(_ -> { + this.getControllerCoordinator().ifPresent(ControllerCoordinator::close); this.config.close(); }); @@ -245,10 +236,6 @@ public void initializeControlify() { this.virtualMouseHandler = new VirtualMouseHandler(); this.triggerEffectManager = new TriggerEffectManager(); - ControlifyEvents.CONTROLLER_CONNECTED.register(event -> this.onControllerAdded( - event.controller(), event.hotplugged())); - ControlifyEvents.CONTROLLER_DISCONNECTED.register(event -> this.onControllerRemoved(event.controller())); - ControlifyBindings.registerModdedBindings(); PlatformClientUtil.registerPostScreenRender((screen, graphics, mouseX, mouseY, tickDelta) -> @@ -257,15 +244,12 @@ public void initializeControlify() { ScreenProcessorProvider.provide(screen).render(controller, graphics, tickDelta); })); - this.controllerManager = new ControllerManager(); - try { - Sdl sdl = SDLNativesLoader.load(); - this.controllerBackend = new SDLControllerBackend(sdl, this, this.controllerManager); - } catch (Throwable throwable) { - CUtil.LOGGER.error("Failed to initialize controller backend", throwable); + ControlifyEvents.CURRENT_CONTROLLER_CHANGED.register(event -> + this.onCurrentControllerChanged(event.controller(), event.changeInputMode())); + this.controllerCoordinator = new ControllerCoordinator(this, this.setupWizardManager); + if (!this.controllerCoordinator.initialize()) { return; } - this.controllerBackend.setup(); PlatformClientUtil.registerClientTickStarted(this::fixedUpdate); @@ -324,155 +308,49 @@ private void doSteamDeckChecks() { } } - /** - * Loops through every controller slot and initialises it if it is connected. - * This is guaranteed to be called at most once. If no controllers are connected - * in the whole game lifecycle, this is never ran. - */ + /// Discovers already-connected controllers once the backend is initialized. public void discoverControllers() { - if (hasDiscoveredControllers) { - CUtil.LOGGER.warn("Attempted to discover controllers twice!"); - return; - } - hasDiscoveredControllers = true; - - DebugLog.log("Discovering and initializing controllers..."); - - this.controllerBackend.discoverControllers(); - - if (this.controllerManager.getAllControllers().isEmpty()) { - CUtil.LOGGER.log("No controllers found."); - } - - this.applyControllerSelection(false); - - config().saveIfDirty(); - - PlatformMainUtil.applyToControlifyEntrypoint(entrypoint -> { - try { - entrypoint.onControllersDiscovered(this); - } catch (Throwable e) { - CUtil.LOGGER.error("Failed to run `onControllersDiscovered` on Controlify entrypoint: {}", entrypoint.getClass().getName(), e); - } - }); - } - - /** - * Called when a controller is connected. Either from controller - * discovery or hotplugging. - * - * @param controller the new controller - * @param hotplugged if this was a result of hotplugging - */ - private void onControllerAdded(ControllerEntity controller, boolean hotplugged) { - SetupWizard wizard = new SetupWizard(); - DeviceSettings rememberedDevice = config().getSettings().getOrCreateDeviceSettings(controller.uid()); - rememberedDevice.name = controller.name(); - rememberedDevice.lastSeen = System.currentTimeMillis(); - rememberedDevice.controllerType = controller.info().type().namespace(); - config().markDirty(); - - String selectedUid = config().getActiveProfile().controllerUid; - if (selectedUid == null || selectedUid.equals(controller.uid())) { - this.setCurrentController(controller, true); - } - - // wizard.addStage(() -> SubmitUnknownControllerScreen.canSubmit(controller), nextScreen -> new SubmitUnknownControllerScreen(controller, nextScreen)); - - wizard.addStage( - () -> { - Optional inputOpt = controller.input(); - if (inputOpt.isPresent()) { - InputComponent input = inputOpt.get(); - DeviceSettings deviceSettings = config().getSettings().getOrCreateDeviceSettings(controller.uid()); - return !input.isDefinitelyGamepad() && deviceSettings.mapping == null; - } - return false; - }, - nextScreen -> new AskToMapControllerScreen(controller, nextScreen) - ); - wizard.addStage( - () -> controller.dualSense().isPresent() && controller.bluetooth().map(bt -> !bt.settings().dontShowWarning).orElse(false), - nextScreen -> new BluetoothWarningScreen(controller.bluetooth().orElseThrow(), nextScreen) - ); - - if (hotplugged) { - MinecraftUtil.sendToast( - Component.translatable("controlify.toast.controller_connected.title"), - Component.translatable("controlify.toast.controller_connected.description", controller.name()), - false - ); - } - - // saved after discovery - if (hotplugged) { - config().saveIfDirty(); - } - - this.setupWizardManager.push(wizard); - } - - /** - * Called when a controller is disconnected. - * @param controller controller that has been disconnected - */ - private void onControllerRemoved(ControllerEntity controller) { - if (this.getCurrentController().isPresent() && getCurrentController().get().equals(controller)) { - if (config().getActiveProfile().controllerUid == null) { - this.selectFirstConnectedController(); - } else { - this.setCurrentController(null, true); - } - } - - MinecraftUtil.sendToast( - Component.translatable("controlify.toast.controller_disconnected.title"), - Component.translatable("controlify.toast.controller_disconnected.description", controller.name()), - false - ); - } - - private void selectFirstConnectedController() { - Optional firstController = this.controllerManager.getAllControllers() - .stream() - .findFirst(); - this.setCurrentController(firstController.orElse(null), true); + this.getControllerCoordinator().ifPresent(ControllerCoordinator::discoverControllers); } /// Called once per frame, just after Minecraft has done its own SDL event polling. public void update(float tickDelta) { - this.controllerBackend.pollEvents(); + if (this.minecraft == null || this.controllerCoordinator == null || !this.controllerCoordinator.isReady()) { + return; + } + + boolean outOfFocus = !config().getSettings().globalSettings().outOfFocusInput + && !this.minecraft.isWindowActive(); + boolean givingInput = this.controllerCoordinator.update(outOfFocus); this.setupWizardManager.tick(); // handle showing/hiding mouse whilst in mixed input mode - if (this.minecraft.mouseHandler.isMouseGrabbed()) + if (this.minecraft.mouseHandler.isMouseGrabbed()) { showMouseTicks = 0; - if (currentInputMode() == InputMode.MIXED && showMouseTicks > 0) { - showMouseTicks--; - if (showMouseTicks == 0) { - restoreVirtualCursor(); - } } - - boolean outOfFocus = !config().getSettings().globalSettings().outOfFocusInput - && !this.minecraft.isWindowActive(); - this.getCurrentController().ifPresent(currentController -> { wrapControllerError( - () -> updateActiveController(currentController, outOfFocus), + () -> updateActiveController(currentController, givingInput), "Updating current controller", currentController ); }); - mouseUsedThisTick = false; + this.mouseUsedThisFrame = false; } /// Called at 20tps public void fixedUpdate() { LowBatteryNotifier.tick(); + if (currentInputMode() == InputMode.MIXED && showMouseTicks > 0) { + showMouseTicks--; + if (showMouseTicks == 0) { + restoreVirtualCursor(); + } + } + boolean outOfFocus = !config().getSettings().globalSettings().outOfFocusInput && !this.minecraft.isWindowActive(); @@ -485,15 +363,14 @@ public void fixedUpdate() { }); } - private void updateActiveController(ControllerEntity controller, boolean outOfFocus) { - boolean controllerInputSuppressed = outOfFocus || currentInputMode() == InputMode.KEYBOARD_MOUSE; - this.triggerEffectManager().tick(this.minecraft, controller); - this.triggerEffectManager().applyTriggerEffects(controller, controllerInputSuppressed); - - // TODO: move this to listener based InputPipeline signal approach - if (state.isGivingInput()) { + private void updateActiveController(ControllerEntity controller, boolean givingInput) { + if (givingInput) { minecraft.getFramerateLimitTracker().onInputReceived(); + if (showMouseTicks > 0 && !mouseUsedThisFrame) { + this.endTemporaryCursor(); + } + if (!this.currentInputMode().isController()) { this.setInputMode(config().getSettings().globalSettings().mixedInput ? InputMode.MIXED : InputMode.CONTROLLER); @@ -519,21 +396,19 @@ private void updateActiveController(ControllerEntity controller, boolean outOfFo } private void fixedUpdateActiveController(ControllerEntity controller, boolean outOfFocus) { - Optional rumbleManager = controller.rumble().map(RumbleComponent::rumbleManager); + boolean controllerInputSuppressed = outOfFocus || currentInputMode() == InputMode.KEYBOARD_MOUSE; + this.triggerEffectManager().tick(this.minecraft, controller); + this.triggerEffectManager().applyTriggerEffects(controller, controllerInputSuppressed); boolean isPaused = minecraft.isPaused() || MinecraftUtil.getScreen() instanceof PauseScreen; boolean isConfigScreen = MinecraftUtil.getScreen() instanceof YACLScreen; + Optional rumbleManager = controller.rumble().map(RumbleComponent::rumbleManager); rumbleManager.ifPresent(rumble -> rumble.setSilent(outOfFocus || (isPaused && !isConfigScreen) || currentInputMode() == InputMode.KEYBOARD_MOUSE)); if (!outOfFocus) { rumbleManager.ifPresent(RumbleManager::tick); } - if (showMouseTicks > 0 && !mouseUsedThisTick && state.isGivingInput()) { - // TODO: move this to a InputPipeline listener-based approach - this.endTemporaryCursor(); - } - if (minecraft.level != null) { this.inGameInputHandler().ifPresent(InGameInputHandler::fixedUpdate); } @@ -546,13 +421,7 @@ private void fixedUpdateActiveController(ControllerEntity controller, boolean ou } public void applyControllerSelection(boolean changeInputMode) { - String selectedUid = config().getActiveProfile().controllerUid; - Optional selected = controllerManager == null - ? Optional.empty() - : this.controllerManager.getAllControllers().stream() - .filter(controller -> selectedUid == null || selectedUid.equals(controller.uid())) - .findFirst(); - this.setCurrentController(selected.orElse(null), changeInputMode); + this.getControllerCoordinator().ifPresent(coordinator -> coordinator.applyControllerSelection(changeInputMode)); } public boolean switchProfile(int profileIndex, boolean remember) throws IOException { @@ -560,12 +429,8 @@ public boolean switchProfile(int profileIndex, boolean remember) throws IOExcept return false; } - ProfileSettings profile = config().getActiveProfile(); - if (this.controllerManager != null) { - this.controllerManager.getAllControllers().forEach(controller -> controller.setSettings(profile)); - } - this.applyControllerSelection(true); - this.setupForController(this.currentInputMode.isController() ? this.currentController : null); + this.getControllerCoordinator().ifPresent(ControllerCoordinator::applyActiveProfile); + this.setupForController(this.currentInputMode.isController() ? this.getCurrentController().orElse(null) : null); return true; } @@ -575,23 +440,22 @@ public ConfigManager config() { @Override public @NotNull Optional getCurrentController() { - return Optional.ofNullable(currentController); + return this.getControllerCoordinator().flatMap(ControllerCoordinator::currentController); } public void setCurrentController(@Nullable ControllerEntity controller, boolean changeInputMode) { - if (this.currentController == controller) return; - this.currentController = controller; + this.getControllerCoordinator().ifPresent(coordinator -> coordinator.setCurrentController(controller, changeInputMode)); + } + private void onCurrentControllerChanged(@Nullable ControllerEntity controller, boolean changeInputMode) { boolean changedInputMode = false; if (controller == null) { changedInputMode = this.setInputMode(InputMode.KEYBOARD_MOUSE); - DebugLog.log("Cleared current controller."); - } else { + } else if (changeInputMode) { changedInputMode = this.setInputMode(config().getSettings().globalSettings().mixedInput ? InputMode.MIXED : InputMode.CONTROLLER); - DebugLog.log("Updated current controller to {}({})", controller.name(), controller.uid()); } if (!changedInputMode) { - this.setupForController(controller); + this.setupForController(this.currentInputMode.isController() ? controller : null); } } @@ -620,7 +484,7 @@ public boolean setInputMode(@NotNull InputMode newInputMode) { hideMouse(newInputMode.isController(), true); } - this.setupForController(this.currentInputMode.isController() ? this.currentController : null); + this.setupForController(this.currentInputMode.isController() ? this.getCurrentController().orElse(null) : null); KeyMapping.resetToggleKeys(); @@ -655,14 +519,15 @@ private void setupForController(@Nullable ControllerEntity controller) { this.inGameInputHandler = new InGameInputHandler(controller); this.inGameButtonGuide = new InGameButtonGuide(controller, this.minecraft); - controller.input().ifPresent(input -> { - input.rawStateNow().clearState(); - input.rawStateThen().clearState(); - }); + controller.input().ifPresent(InputComponent::resetActions); } public Optional getControllerManager() { - return Optional.ofNullable(controllerManager); + return this.getControllerCoordinator().map(ControllerCoordinator::controllerManager); + } + + public Optional getControllerCoordinator() { + return Optional.ofNullable(this.controllerCoordinator); } public Optional inGameInputHandler() { @@ -697,7 +562,7 @@ public void hideMouse(boolean hide, boolean moveMouse) { public void showCursorTemporarily() { if (currentInputMode() == InputMode.MIXED && !minecraft.mouseHandler.isMouseGrabbed()) { - mouseUsedThisTick = true; + mouseUsedThisFrame = true; if (virtualMouseHandler().isVirtualMouseEnabled()) { moveCursorToVirtualMouse(); virtualMouseHandler().disableVirtualMouse(); diff --git a/src/main/java/dev/isxander/controlify/api/event/ControlifyEvents.java b/src/main/java/dev/isxander/controlify/api/event/ControlifyEvents.java index e96f10ff1..34c721b70 100644 --- a/src/main/java/dev/isxander/controlify/api/event/ControlifyEvents.java +++ b/src/main/java/dev/isxander/controlify/api/event/ControlifyEvents.java @@ -10,12 +10,19 @@ import dev.isxander.controlify.api.ingameinput.LookInputModifier; import dev.isxander.controlify.controller.ControllerEntity; import dev.isxander.controlify.platform.EventHandler; +import org.jetbrains.annotations.Nullable; public final class ControlifyEvents { public static final EventHandler CONTROLLER_CONNECTED = EventHandler.createPlatformBackedEvent(); public static final EventHandler CONTROLLER_DISCONNECTED = EventHandler.createPlatformBackedEvent(); + /** + * Triggers after the current controller changes and its activation has been updated. + * The controller is null when the current selection is cleared. + */ + public static final EventHandler CURRENT_CONTROLLER_CHANGED = EventHandler.createPlatformBackedEvent(); + /** * Triggers when the input mode is changed from keyboard to controller or vice versa. */ @@ -54,6 +61,9 @@ public record ControllerConnected(ControllerEntity controller, boolean hotplugge public record ControllerDisconnected(ControllerEntity controller) { } + public record CurrentControllerChanged(@Nullable ControllerEntity controller, boolean changeInputMode) { + } + public record InputModeChanged(InputMode mode) { } diff --git a/src/main/java/dev/isxander/controlify/controller/ControllerEntity.java b/src/main/java/dev/isxander/controlify/controller/ControllerEntity.java index 6481592a2..037edbec7 100644 --- a/src/main/java/dev/isxander/controlify/controller/ControllerEntity.java +++ b/src/main/java/dev/isxander/controlify/controller/ControllerEntity.java @@ -37,6 +37,7 @@ public class ControllerEntity extends ECSEntityImpl { private ProfileSettings settings; private final ProfileSettings defaultSettings; private final ControlifyLogger logger; + private boolean active; public ControllerEntity( ControllerInfo info, @@ -151,7 +152,20 @@ public void update(boolean outOfFocus) { this.driver.update(this, outOfFocus); } + public boolean isActive() { + return this.active; + } + + public void setActive(boolean active) { + if (this.active == active) { + return; + } + this.active = active; + this.driver.setActive(active); + } + public void close() { + this.setActive(false); this.getAllComponents().values().forEach(ECSComponent::close); this.driver.close(); } diff --git a/src/main/java/dev/isxander/controlify/controller/gyro/GyroCalibrator.java b/src/main/java/dev/isxander/controlify/controller/gyro/GyroCalibrator.java index 7b7bf3ebc..a626e4fe7 100644 --- a/src/main/java/dev/isxander/controlify/controller/gyro/GyroCalibrator.java +++ b/src/main/java/dev/isxander/controlify/controller/gyro/GyroCalibrator.java @@ -8,9 +8,8 @@ /** * Implements rolling gyro calibration using exponential moving average - * for background bias estimation. The calibrator continuously updates - * during stationary periods (low angular velocity) to track and correct - * for gyro drift over time. + * for background bias estimation during stationary periods (low angular velocity). + * Bias application is handled by the input pipeline using the device settings. */ public class GyroCalibrator { // Exponential moving average (EMA) alpha value - lower = slower adaptation, more stable @@ -30,54 +29,34 @@ public class GyroCalibrator { // Counter for consecutive stationary samples private int stationarySampleCount = 0; - /** - * Update the calibration with a new gyro sample. - * This should be called every time new gyro data arrives. - * - * @param rawGyroState The raw gyro reading from SDL3 - */ - public void update(GyroStateC rawGyroState) { + public void update(float x, float y, float z) { // Check if the gyro appears to be stationary - boolean isStationary = isStationary(rawGyroState); + boolean isStationary = isStationary(x, y, z); if (isStationary) { - stationarySampleCount++; + this.stationarySampleCount++; // Only update bias estimate if we've been stationary long enough - if (stationarySampleCount >= MIN_STATIONARY_SAMPLES) { + if (this.stationarySampleCount >= MIN_STATIONARY_SAMPLES) { // Update bias estimate using exponential moving average // New bias = (1 - alpha) * old_bias + alpha * current_reading - biasEstimate.x = (1 - EMA_ALPHA) * biasEstimate.x + EMA_ALPHA * rawGyroState.pitch(); - biasEstimate.y = (1 - EMA_ALPHA) * biasEstimate.y + EMA_ALPHA * rawGyroState.yaw(); - biasEstimate.z = (1 - EMA_ALPHA) * biasEstimate.z + EMA_ALPHA * rawGyroState.roll(); + this.biasEstimate.x = (1 - EMA_ALPHA) * this.biasEstimate.x + EMA_ALPHA * x; + this.biasEstimate.y = (1 - EMA_ALPHA) * this.biasEstimate.y + EMA_ALPHA * y; + this.biasEstimate.z = (1 - EMA_ALPHA) * this.biasEstimate.z + EMA_ALPHA * z; } } else { // Reset stationary counter when motion is detected - stationarySampleCount = 0; + this.stationarySampleCount = 0; } } - /** - * Apply the current bias correction to a raw gyro reading. - * - * @param rawGyroState The raw gyro reading from SDL3 - * @return Calibrated gyro state with bias removed - */ - public GyroState applyCalibration(GyroStateC rawGyroState) { - return new GyroState( - rawGyroState.pitch() - biasEstimate.pitch(), - rawGyroState.yaw() - biasEstimate.yaw(), - rawGyroState.roll() - biasEstimate.roll() - ); - } - /** * Check if the gyro appears to be stationary based on angular velocity. */ - private boolean isStationary(GyroStateC state) { - return Math.abs(state.pitch()) < STATIONARY_THRESHOLD - && Math.abs(state.yaw()) < STATIONARY_THRESHOLD - && Math.abs(state.roll()) < STATIONARY_THRESHOLD; + private boolean isStationary(float x, float y, float z) { + return Math.abs(x) < STATIONARY_THRESHOLD + && Math.abs(y) < STATIONARY_THRESHOLD + && Math.abs(z) < STATIONARY_THRESHOLD; } /** @@ -87,19 +66,13 @@ public GyroStateC getCurrentBias() { return new GyroState(this.biasEstimate); } - /** - * Set the bias estimate (for loading from config). - */ public void setBias(GyroStateC bias) { - biasEstimate.set(bias.pitch(), bias.yaw(), bias.roll()); - stationarySampleCount = 0; + this.biasEstimate.set(bias.x(), bias.y(), bias.z()); + this.stationarySampleCount = 0; } - /** - * Reset the calibrator to initial state. - */ public void reset() { - biasEstimate.set(0, 0, 0); - stationarySampleCount = 0; + this.biasEstimate.set(0, 0, 0); + this.stationarySampleCount = 0; } } diff --git a/src/main/java/dev/isxander/controlify/controller/gyro/GyroComponent.java b/src/main/java/dev/isxander/controlify/controller/gyro/GyroComponent.java index 3a49a211b..8bfdfb87d 100644 --- a/src/main/java/dev/isxander/controlify/controller/gyro/GyroComponent.java +++ b/src/main/java/dev/isxander/controlify/controller/gyro/GyroComponent.java @@ -11,54 +11,93 @@ import dev.isxander.controlify.config.settings.profile.GyroSettings; import dev.isxander.controlify.controller.ControllerEntity; import dev.isxander.controlify.controller.impl.ECSComponentImpl; +import dev.isxander.controlify.input.InputPipeline; +import dev.isxander.controlify.input.SensorType; +import dev.isxander.controlify.input.logical.LogicalSignal; +import dev.isxander.controlify.input.physical.PhysicalSensorSignal; +import dev.isxander.controlify.input.physical.PhysicalSignal; import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.utils.event.EventSource; import net.minecraft.resources.Identifier; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; public class GyroComponent extends ECSComponentImpl { public static final Identifier ID = CUtil.rl("gyro"); + private final InputPipeline inputPipeline; + private @Nullable EventSource.Subscription calibrationSubscription; + private @Nullable EventSource.Subscription sensorSubscription; + private DeviceSettings deviceSettings; private GyroStateC gyroState = GyroStateC.ZERO; + private static final long MAX_SAMPLE_AGE_NANOS = 50_000_000L; + private long lastStateTimeNanos; private final GyroCalibrator calibrator = new GyroCalibrator(); - // Save calibration every 5 seconds (100 ticks) - private static final int CALIBRATION_SAVE_INTERVAL = 100; - private int ticksSinceLastSave = 0; + private static final long CALIBRATION_SAVE_INTERVAL_NANOS = 5_000_000_000L; + private long lastCalibrationSaveTimeNanos; private GyroStateC lastSavedBias = GyroStateC.ZERO; + public GyroComponent(InputPipeline inputPipeline) { + this.inputPipeline = Objects.requireNonNull(inputPipeline); + } + @Override public void attach(ControllerEntity controller) { super.attach(controller); // Load the calibration from device settings - DeviceSettings deviceSettings = Controlify.instance().config().getSettings() + this.deviceSettings = Controlify.instance().config().getSettings() .getOrCreateDeviceSettings(controller.uid()); - GyroStateC savedOffset = deviceSettings.gyroCalibration.offset; + GyroStateC savedOffset = this.deviceSettings.gyroCalibration.offset; // Initialize the calibrator with the saved offset calibrator.setBias(savedOffset); - lastSavedBias = savedOffset; + lastSavedBias = new GyroState(savedOffset); + this.lastCalibrationSaveTimeNanos = System.nanoTime(); + + this.calibrationSubscription = this.inputPipeline.sourceRawPhysicalSignals().subscribe(this::onRawSignal); + this.sensorSubscription = this.inputPipeline.sourceLogicalSignals().subscribe(this::onSignal); } - public GyroStateC getState() { - return this.gyroState; + private void onRawSignal(PhysicalSignal signal) { + if (signal instanceof PhysicalSensorSignal(_, _, SensorType sensorType, float x, float y, float z) + && sensorType == SensorType.GYROSCOPE) { + this.updateCalibration(x, y, z); + } + } + + private void updateCalibration(float x, float y, float z) { + this.calibrator.update(x, y, z); + // The raw tap runs before SensorProcessorStage, so its bias supplier sees this update immediately. + this.deviceSettings.gyroCalibration.offset = this.calibrator.getCurrentBias(); + + long now = System.nanoTime(); + if (now - this.lastCalibrationSaveTimeNanos >= CALIBRATION_SAVE_INTERVAL_NANOS) { + this.lastCalibrationSaveTimeNanos = now; + this.saveCalibrationIfChanged(); + } } - public void setState(GyroStateC state) { - // Update the calibrator with the raw reading - calibrator.update(state); - // Store the calibrated state - this.gyroState = calibrator.applyCalibration(state); - - // Periodically save calibration if it has changed significantly - ticksSinceLastSave++; - if (ticksSinceLastSave >= CALIBRATION_SAVE_INTERVAL) { - ticksSinceLastSave = 0; - saveCalibrationIfChanged(); + private void onSignal(LogicalSignal signal) { + if (signal instanceof LogicalSignal.SensorInterval(_, _, SensorType sensorType, float x, float y, float z) + && sensorType == SensorType.GYROSCOPE) { + // The pipeline has already calibrated, filtered and integrated these values into radians. + this.gyroState = new GyroState(x, y, z); + this.lastStateTimeNanos = System.nanoTime(); } } + /// Returns the latest integrated gyro interval in radians. + /// Missing samples expire naturally, independently of controller selection. + public GyroStateC getState() { + return System.nanoTime() - this.lastStateTimeNanos > MAX_SAMPLE_AGE_NANOS + ? GyroStateC.ZERO : this.gyroState; + } + private void saveCalibrationIfChanged() { - GyroStateC currentBias = calibrator.getCurrentBias(); + GyroStateC currentBias = this.deviceSettings.gyroCalibration.offset; // Check if bias has changed significantly (more than 0.001 rad/s on any axis) float threshold = 0.001f; @@ -66,17 +105,10 @@ private void saveCalibrationIfChanged() { Math.abs(currentBias.yaw() - lastSavedBias.yaw()) > threshold || Math.abs(currentBias.roll() - lastSavedBias.roll()) > threshold) { - // Save to device settings - DeviceSettings deviceSettings = Controlify.instance().config().getSettings() - .getOrCreateDeviceSettings(controller().uid()); - deviceSettings.gyroCalibration.offset = new GyroState(currentBias); - - // Mark config as dirty so it gets saved at some point - // We don't really care if it never gets saved if the user never changes any other config - // that triggers a save, we just recalibrate from last saved on next launch + // Settings update on every sample; persistence is checked at a fixed time interval. Controlify.instance().config().markDirty(); - lastSavedBias = currentBias; + lastSavedBias = new GyroState(currentBias); } } @@ -97,4 +129,20 @@ public Identifier id() { return ID; } + @Override + public void close() { + if (this.calibrationSubscription != null) { + this.calibrationSubscription.close(); + this.calibrationSubscription = null; + } + if (this.sensorSubscription != null) { + this.sensorSubscription.close(); + this.sensorSubscription = null; + } + if (this.deviceSettings != null) { + this.saveCalibrationIfChanged(); + } + super.close(); + } + } diff --git a/src/main/java/dev/isxander/controlify/controller/input/GamepadInputs.java b/src/main/java/dev/isxander/controlify/controller/input/GamepadInputs.java index b663983f6..71bf9c65b 100644 --- a/src/main/java/dev/isxander/controlify/controller/input/GamepadInputs.java +++ b/src/main/java/dev/isxander/controlify/controller/input/GamepadInputs.java @@ -61,7 +61,6 @@ private GamepadInputs() { public static final Identifier MISC_5_BUTTON = button("misc_5"); public static final Identifier MISC_6_BUTTON = button("misc_6"); - public static final Identifier RIGHT_PADDLE_1_BUTTON = button("right_paddle_1"); public static final Identifier RIGHT_PADDLE_2_BUTTON = button("right_paddle_2"); public static final Identifier LEFT_PADDLE_1_BUTTON = button("left_paddle_1"); diff --git a/src/main/java/dev/isxander/controlify/controller/input/mapping/ControllerMapping.java b/src/main/java/dev/isxander/controlify/controller/input/mapping/ControllerMapping.java index bdf7bac7f..4da4e14bf 100644 --- a/src/main/java/dev/isxander/controlify/controller/input/mapping/ControllerMapping.java +++ b/src/main/java/dev/isxander/controlify/controller/input/mapping/ControllerMapping.java @@ -7,22 +7,29 @@ package dev.isxander.controlify.controller.input.mapping; import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.controller.input.ControllerState; import dev.isxander.controlify.controller.input.DeadzoneGroup; -import dev.isxander.controlify.controller.input.ModifiableControllerState; -import dev.isxander.controlify.controller.impl.ControllerStateImpl; +import dev.isxander.controlify.controller.input.GamepadInputs; import net.minecraft.resources.Identifier; import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; public record ControllerMapping( List mappings, LinkedHashMap deadzones -) implements StateMapper { +) { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( MappingEntry.codec() .listOf() + .validate(entries -> { + if (!validateDistinctMappingSources(entries)) { + return DataResult.error(() -> "Every mapping entry must have a distinct `from` source input"); + } + return DataResult.success(entries); + }) .fieldOf("mappings") .forGetter(ControllerMapping::mappings), Codec.unboundedMap(Identifier.CODEC, DeadzoneGroup.CODEC) @@ -31,22 +38,33 @@ public record ControllerMapping( .forGetter(ControllerMapping::deadzones) ).apply(instance, ControllerMapping::new)); - @Override - public ControllerState mapState(ControllerState state) { - if (mappings.isEmpty()) { - return state; - } - - ModifiableControllerState newState = new ControllerStateImpl(); - - for (MappingEntry mapping : mappings) { - mapping.apply(state, newState); + public ControllerMapping { + if (!validateDistinctMappingSources(mappings)) { + throw new IllegalArgumentException("Every mapping entry must have a distinct source input"); } + } - return newState; + public Map createMappingEntryMap() { + return this.mappings.stream() + .collect(Collectors.toUnmodifiableMap( + MappingEntry::sourceInput, + Function.identity() + )); } public static final ControllerMapping NO_MAPPING = new Builder().build(); + public static final ControllerMapping GAMEPAD_DEADZONES_ONLY_MAPPING = new Builder() + .putDeadzoneGroups(GamepadInputs.DEADZONE_GROUPS) + .build(); + + private static boolean validateDistinctMappingSources(Collection entries) { + long entryCount = entries.size(); + long distinctSourceCount = entries.stream() + .map(MappingEntry::sourceInput) + .distinct() + .count(); + return entryCount == distinctSourceCount; + } public static class Builder { private final List mappings = new ArrayList<>(); @@ -56,7 +74,7 @@ public Builder putMapping(MappingEntry mapping) { if (mapping == null) return this; - mappings.add(mapping); + this.mappings.add(mapping); return this; } diff --git a/src/main/java/dev/isxander/controlify/controller/input/mapping/MapType.java b/src/main/java/dev/isxander/controlify/controller/input/mapping/MapType.java index 35e83f294..17e82057e 100644 --- a/src/main/java/dev/isxander/controlify/controller/input/mapping/MapType.java +++ b/src/main/java/dev/isxander/controlify/controller/input/mapping/MapType.java @@ -12,9 +12,7 @@ public enum MapType implements StringRepresentable { BUTTON, - AXIS, - HAT, - NOTHING; + AXIS; public static final Codec CODEC = StringRepresentable.fromEnum(MapType::values); diff --git a/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntry.java b/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntry.java index 696f62c2f..b834bbebd 100644 --- a/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntry.java +++ b/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntry.java @@ -10,11 +10,20 @@ import dev.isxander.controlify.controller.input.ControllerState; import dev.isxander.controlify.controller.input.HatState; import dev.isxander.controlify.controller.input.ModifiableControllerState; +import dev.isxander.controlify.input.physical.PhysicalInputSignal; import dev.isxander.controlify.utils.MthExt; import net.minecraft.resources.Identifier; +import org.jspecify.annotations.Nullable; public sealed interface MappingEntry { - void apply(ControllerState oldState, ModifiableControllerState newState); + /// Transforms an input signal + /// A mapping entry may transform a signal to one with a differing input identifier. + /// This function will only be called when the signal matches {@link sourceInput}. + PhysicalInputSignal apply(PhysicalInputSignal signal); + + /// The physical input identifier this mapping entry transforms. + /// The {@link apply} function will only receive signals whose {@link PhysicalInputSignal#input()} matches. + Identifier sourceInput(); MapType inputType(); @@ -25,146 +34,61 @@ static Codec codec() { } sealed interface FromButton extends MappingEntry { - record ToButton(Identifier from, Identifier to, boolean invert, MapType inputType, MapType outputType) implements FromButton { - public ToButton(Identifier from, Identifier to, boolean invert) { - this(from, to, invert, MapType.BUTTON, MapType.BUTTON); - } - - @Override - public void apply(ControllerState oldState, ModifiableControllerState newState) { - boolean fromState = oldState.isButtonDown(from); - if (invert()) fromState = !fromState; - newState.setButton(to, fromState); - } - } - - record ToAxis(Identifier from, Identifier to, float offState, float onState, MapType inputType, MapType outputType) implements FromButton { - public ToAxis(Identifier from, Identifier to, float offState, float onState) { - this(from, to, offState, onState, MapType.BUTTON, MapType.AXIS); + record ToButton(Identifier sourceInput, Identifier to, boolean invert, MapType inputType, MapType outputType) implements FromButton { + public ToButton(Identifier sourceInput, Identifier to, boolean invert) { + this(sourceInput, to, invert, MapType.BUTTON, MapType.BUTTON); } @Override - public void apply(ControllerState oldState, ModifiableControllerState newState) { - newState.setAxis(to, oldState.isButtonDown(from) ? onState : offState); + public PhysicalInputSignal apply(PhysicalInputSignal signal) { + if (signal instanceof PhysicalInputSignal.ButtonChanged(long timeNanos, _, boolean state)) { + return new PhysicalInputSignal.ButtonChanged(timeNanos, to, invert != state); + } + return signal; } } - record ToHat(Identifier from, Identifier to, HatState offState, HatState onState, MapType inputType, MapType outputType) implements FromButton { - public ToHat(Identifier from, Identifier to, HatState offState, HatState onState) { - this(from, to, offState, onState, MapType.BUTTON, MapType.HAT); + record ToAxis(Identifier sourceInput, Identifier to, float offState, float onState, MapType inputType, MapType outputType) implements FromButton { + public ToAxis(Identifier sourceInput, Identifier to, float offState, float onState) { + this(sourceInput, to, offState, onState, MapType.BUTTON, MapType.AXIS); } @Override - public void apply(ControllerState oldState, ModifiableControllerState newState) { - newState.setHat(to, oldState.isButtonDown(from) ? onState : offState); + public PhysicalInputSignal apply(PhysicalInputSignal signal) { + if (signal instanceof PhysicalInputSignal.ButtonChanged(long timeNanos, _, boolean state)) { + return new PhysicalInputSignal.AxisMoved(timeNanos, this.to, state ? onState : offState); + } + return signal; } } } sealed interface FromAxis extends MappingEntry { - record ToButton(Identifier from, Identifier to, float threshold, MapType inputType, MapType outputType) implements FromAxis { - public ToButton(Identifier from, Identifier to, float threshold) { - this(from, to, threshold, MapType.AXIS, MapType.BUTTON); - } - - @Override - public void apply(ControllerState oldState, ModifiableControllerState newState) { - newState.setButton(to, oldState.getAxisState(from) >= threshold); - } - } - - record ToAxis(Identifier from, Identifier to, float minIn, float minOut, float maxIn, float maxOut, MapType inputType, MapType outputType) implements FromAxis { - public ToAxis(Identifier from, Identifier to, float minIn, float minOut, float maxIn, float maxOut) { - this(from, to, minIn, minOut, maxIn, maxOut, MapType.AXIS, MapType.AXIS); - } - - @Override - public void apply(ControllerState oldState, ModifiableControllerState newState) { - float oldVal = oldState.getAxisState(from); - float newVal = MthExt.remap(oldVal, minIn, maxIn, minOut, maxOut); - newState.setAxis(to, newVal); - } - } - - record ToHat(Identifier from, Identifier to, float threshold, HatState targetState, MapType inputType, MapType outputType) implements FromAxis { - public ToHat(Identifier from, Identifier to, float threshold, HatState targetState) { - this(from, to, threshold, targetState, MapType.AXIS, MapType.HAT); - } - - @Override - public void apply(ControllerState oldState, ModifiableControllerState newState) { - float oldVal = oldState.getAxisState(from); - newState.setHat(to, oldVal >= threshold ? targetState : HatState.CENTERED); - } - } - } - - sealed interface FromHat extends MappingEntry { - record ToButton(Identifier from, Identifier to, HatState targetState, MapType inputType, MapType outputType) implements FromHat { - public ToButton(Identifier from, Identifier to, HatState targetState) { - this(from, to, targetState, MapType.HAT, MapType.BUTTON); - } - - @Override - public void apply(ControllerState oldState, ModifiableControllerState newState) { - newState.setButton(to, oldState.getHatState(from) == targetState); - } - } - - record ToAxis(Identifier from, Identifier to, HatState targetState, float onState, float offState, MapType inputType, MapType outputType) implements FromHat { - public ToAxis(Identifier from, Identifier to, HatState targetState, float onState, float offState) { - this(from, to, targetState, onState, offState, MapType.HAT, MapType.AXIS); - } - - @Override - public void apply(ControllerState oldState, ModifiableControllerState newState) { - newState.setAxis(to, oldState.getHatState(from) == targetState ? onState : offState); - } - } - - record ToHat(Identifier from, Identifier to, MapType inputType, MapType outputType) implements FromHat { - public ToHat(Identifier from, Identifier to) { - this(from, to, MapType.HAT, MapType.HAT); - } - - @Override - public void apply(ControllerState oldState, ModifiableControllerState newState) { - newState.setHat(to, oldState.getHatState(from)); - } - } - } - - sealed interface FromNothing extends MappingEntry { - record ToButton(Identifier to, boolean state, MapType inputType, MapType outputType) implements FromNothing { - public ToButton(Identifier to, boolean state) { - this(to, state, MapType.NOTHING, MapType.BUTTON); - } - - @Override - public void apply(ControllerState oldState, ModifiableControllerState newState) { - newState.setButton(to, state); - } - } - - record ToAxis(Identifier to, float state, MapType inputType, MapType outputType) implements FromNothing { - public ToAxis(Identifier to, float state) { - this(to, state, MapType.NOTHING, MapType.AXIS); + record ToButton(Identifier sourceInput, Identifier to, float threshold, MapType inputType, MapType outputType) implements FromAxis { + public ToButton(Identifier sourceInput, Identifier to, float threshold) { + this(sourceInput, to, threshold, MapType.AXIS, MapType.BUTTON); } @Override - public void apply(ControllerState oldState, ModifiableControllerState newState) { - newState.setAxis(to, state); + public PhysicalInputSignal apply(PhysicalInputSignal signal) { + if (signal instanceof PhysicalInputSignal.AxisMoved(long timeNanos, _, float value)) { + return new PhysicalInputSignal.ButtonChanged(timeNanos, to, value >= threshold); + } + return signal; } } - record ToHat(Identifier to, MapType inputType, MapType outputType) implements FromNothing { - public ToHat(Identifier to) { - this(to, MapType.NOTHING, MapType.HAT); + record ToAxis(Identifier sourceInput, Identifier to, float minIn, float minOut, float maxIn, float maxOut, MapType inputType, MapType outputType) implements FromAxis { + public ToAxis(Identifier sourceInput, Identifier to, float minIn, float minOut, float maxIn, float maxOut) { + this(sourceInput, to, minIn, minOut, maxIn, maxOut, MapType.AXIS, MapType.AXIS); } @Override - public void apply(ControllerState oldState, ModifiableControllerState newState) { - newState.setHat(to, HatState.CENTERED); + public PhysicalInputSignal apply(PhysicalInputSignal signal) { + if (signal instanceof PhysicalInputSignal.AxisMoved(long timeNanos, _, float value)) { + return new PhysicalInputSignal.AxisMoved(timeNanos, to, MthExt.remap(value, minIn, maxIn, minOut, maxOut)); + } + return signal; } } } diff --git a/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntryCodecs.java b/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntryCodecs.java index 2f3c09253..03dced81b 100644 --- a/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntryCodecs.java +++ b/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntryCodecs.java @@ -22,7 +22,7 @@ final class MappingEntryCodecs { ).apply(instance, MappingEntryStub::new)); public static final Codec CODEC_BUTTON_TO_BUTTON = RecordCodecBuilder.create(instance -> instance.group( - Identifier.CODEC.fieldOf("from").forGetter(MappingEntry.FromButton.ToButton::from), + Identifier.CODEC.fieldOf("from").forGetter(MappingEntry.FromButton.ToButton::sourceInput), Identifier.CODEC.fieldOf("to").forGetter(MappingEntry.FromButton.ToButton::to), Codec.BOOL.fieldOf("invert").forGetter(MappingEntry.FromButton.ToButton::invert), MapType.CODEC.fieldOf("input_type").forGetter(MappingEntry.FromButton.ToButton::inputType), @@ -30,7 +30,7 @@ final class MappingEntryCodecs { ).apply(instance, MappingEntry.FromButton.ToButton::new)); public static final Codec CODEC_BUTTON_TO_AXIS = RecordCodecBuilder.create(instance -> instance.group( - Identifier.CODEC.fieldOf("from").forGetter(MappingEntry.FromButton.ToAxis::from), + Identifier.CODEC.fieldOf("from").forGetter(MappingEntry.FromButton.ToAxis::sourceInput), Identifier.CODEC.fieldOf("to").forGetter(MappingEntry.FromButton.ToAxis::to), Codec.FLOAT.fieldOf("off_state").forGetter(MappingEntry.FromButton.ToAxis::offState), Codec.FLOAT.fieldOf("on_state").forGetter(MappingEntry.FromButton.ToAxis::onState), @@ -38,17 +38,8 @@ final class MappingEntryCodecs { MapType.CODEC.fieldOf("output_type").forGetter(MappingEntry.FromButton.ToAxis::outputType) ).apply(instance, MappingEntry.FromButton.ToAxis::new)); - public static final Codec CODEC_BUTTON_TO_HAT = RecordCodecBuilder.create(instance -> instance.group( - Identifier.CODEC.fieldOf("from").forGetter(MappingEntry.FromButton.ToHat::from), - Identifier.CODEC.fieldOf("to").forGetter(MappingEntry.FromButton.ToHat::to), - HatState.CODEC.fieldOf("off_state").forGetter(MappingEntry.FromButton.ToHat::offState), - HatState.CODEC.fieldOf("on_state").forGetter(MappingEntry.FromButton.ToHat::onState), - MapType.CODEC.fieldOf("input_type").forGetter(MappingEntry.FromButton.ToHat::inputType), - MapType.CODEC.fieldOf("output_type").forGetter(MappingEntry.FromButton.ToHat::outputType) - ).apply(instance, MappingEntry.FromButton.ToHat::new)); - public static final Codec CODEC_AXIS_TO_BUTTON = RecordCodecBuilder.create(instance -> instance.group( - Identifier.CODEC.fieldOf("from").forGetter(MappingEntry.FromAxis.ToButton::from), + Identifier.CODEC.fieldOf("from").forGetter(MappingEntry.FromAxis.ToButton::sourceInput), Identifier.CODEC.fieldOf("to").forGetter(MappingEntry.FromAxis.ToButton::to), Codec.FLOAT.fieldOf("threshold").forGetter(MappingEntry.FromAxis.ToButton::threshold), MapType.CODEC.fieldOf("input_type").forGetter(MappingEntry.FromAxis.ToButton::inputType), @@ -56,7 +47,7 @@ final class MappingEntryCodecs { ).apply(instance, MappingEntry.FromAxis.ToButton::new)); public static final Codec CODEC_AXIS_TO_AXIS = RecordCodecBuilder.create(instance -> instance.group( - Identifier.CODEC.fieldOf("from").forGetter(MappingEntry.FromAxis.ToAxis::from), + Identifier.CODEC.fieldOf("from").forGetter(MappingEntry.FromAxis.ToAxis::sourceInput), Identifier.CODEC.fieldOf("to").forGetter(MappingEntry.FromAxis.ToAxis::to), Codec.FLOAT.fieldOf("min_in").forGetter(MappingEntry.FromAxis.ToAxis::minIn), Codec.FLOAT.fieldOf("min_out").forGetter(MappingEntry.FromAxis.ToAxis::minOut), @@ -66,120 +57,33 @@ final class MappingEntryCodecs { MapType.CODEC.fieldOf("output_type").forGetter(MappingEntry.FromAxis.ToAxis::outputType) ).apply(instance, MappingEntry.FromAxis.ToAxis::new)); - public static final Codec CODEC_AXIS_TO_HAT = RecordCodecBuilder.create(instance -> instance.group( - Identifier.CODEC.fieldOf("from").forGetter(MappingEntry.FromAxis.ToHat::from), - Identifier.CODEC.fieldOf("to").forGetter(MappingEntry.FromAxis.ToHat::to), - Codec.FLOAT.fieldOf("threshold").forGetter(MappingEntry.FromAxis.ToHat::threshold), - HatState.CODEC.fieldOf("target_state").forGetter(MappingEntry.FromAxis.ToHat::targetState), - MapType.CODEC.fieldOf("input_type").forGetter(MappingEntry.FromAxis.ToHat::inputType), - MapType.CODEC.fieldOf("output_type").forGetter(MappingEntry.FromAxis.ToHat::outputType) - ).apply(instance, MappingEntry.FromAxis.ToHat::new)); - - public static final Codec CODEC_HAT_TO_BUTTON = RecordCodecBuilder.create(instance -> instance.group( - Identifier.CODEC.fieldOf("from").forGetter(MappingEntry.FromHat.ToButton::from), - Identifier.CODEC.fieldOf("to").forGetter(MappingEntry.FromHat.ToButton::to), - HatState.CODEC.fieldOf("target_state").forGetter(MappingEntry.FromHat.ToButton::targetState), - MapType.CODEC.fieldOf("input_type").forGetter(MappingEntry.FromHat.ToButton::inputType), - MapType.CODEC.fieldOf("output_type").forGetter(MappingEntry.FromHat.ToButton::outputType) - ).apply(instance, MappingEntry.FromHat.ToButton::new)); - - public static final Codec CODEC_HAT_TO_AXIS = RecordCodecBuilder.create(instance -> instance.group( - Identifier.CODEC.fieldOf("from").forGetter(MappingEntry.FromHat.ToAxis::from), - Identifier.CODEC.fieldOf("to").forGetter(MappingEntry.FromHat.ToAxis::to), - HatState.CODEC.fieldOf("target_state").forGetter(MappingEntry.FromHat.ToAxis::targetState), - Codec.FLOAT.fieldOf("on_state").forGetter(MappingEntry.FromHat.ToAxis::onState), - Codec.FLOAT.fieldOf("off_state").forGetter(MappingEntry.FromHat.ToAxis::offState), - MapType.CODEC.fieldOf("input_type").forGetter(MappingEntry.FromHat.ToAxis::inputType), - MapType.CODEC.fieldOf("output_type").forGetter(MappingEntry.FromHat.ToAxis::outputType) - ).apply(instance, MappingEntry.FromHat.ToAxis::new)); - - public static final Codec CODEC_HAT_TO_HAT = RecordCodecBuilder.create(instance -> instance.group( - Identifier.CODEC.fieldOf("from").forGetter(MappingEntry.FromHat.ToHat::from), - Identifier.CODEC.fieldOf("to").forGetter(MappingEntry.FromHat.ToHat::to), - MapType.CODEC.fieldOf("input_type").forGetter(MappingEntry.FromHat.ToHat::inputType), - MapType.CODEC.fieldOf("output_type").forGetter(MappingEntry.FromHat.ToHat::outputType) - ).apply(instance, MappingEntry.FromHat.ToHat::new)); - - public static final Codec CODEC_NOTHING_TO_BUTTON = RecordCodecBuilder.create(instance -> instance.group( - Identifier.CODEC.fieldOf("to").forGetter(MappingEntry.FromNothing.ToButton::to), - Codec.BOOL.fieldOf("state").forGetter(MappingEntry.FromNothing.ToButton::state), - MapType.CODEC.fieldOf("input_type").forGetter(MappingEntry.FromNothing.ToButton::inputType), - MapType.CODEC.fieldOf("output_type").forGetter(MappingEntry.FromNothing.ToButton::outputType) - ).apply(instance, MappingEntry.FromNothing.ToButton::new)); - - public static final Codec CODEC_NOTHING_TO_AXIS = RecordCodecBuilder.create(instance -> instance.group( - Identifier.CODEC.fieldOf("to").forGetter(MappingEntry.FromNothing.ToAxis::to), - Codec.FLOAT.fieldOf("state").forGetter(MappingEntry.FromNothing.ToAxis::state), - MapType.CODEC.fieldOf("input_type").forGetter(MappingEntry.FromNothing.ToAxis::inputType), - MapType.CODEC.fieldOf("output_type").forGetter(MappingEntry.FromNothing.ToAxis::outputType) - ).apply(instance, MappingEntry.FromNothing.ToAxis::new)); - - public static final Codec CODEC_NOTHING_TO_HAT = RecordCodecBuilder.create(instance -> instance.group( - Identifier.CODEC.fieldOf("to").forGetter(MappingEntry.FromNothing.ToHat::to), - MapType.CODEC.fieldOf("input_type").forGetter(MappingEntry.FromNothing.ToHat::inputType), - MapType.CODEC.fieldOf("output_type").forGetter(MappingEntry.FromNothing.ToHat::outputType) - ).apply(instance, MappingEntry.FromNothing.ToHat::new)); - public static final Codec CODEC = new Codec<>() { @Override public DataResult encode(MappingEntry input, DynamicOps ops, T prefix) { return switch (input) { case MappingEntry.FromButton.ToButton e -> CODEC_BUTTON_TO_BUTTON.encode(e, ops, prefix); case MappingEntry.FromButton.ToAxis e -> CODEC_BUTTON_TO_AXIS.encode(e, ops, prefix); - case MappingEntry.FromButton.ToHat e -> CODEC_BUTTON_TO_HAT.encode(e, ops, prefix); case MappingEntry.FromAxis.ToButton e -> CODEC_AXIS_TO_BUTTON.encode(e, ops, prefix); case MappingEntry.FromAxis.ToAxis e -> CODEC_AXIS_TO_AXIS.encode(e, ops, prefix); - case MappingEntry.FromAxis.ToHat e -> CODEC_AXIS_TO_HAT.encode(e, ops, prefix); - case MappingEntry.FromHat.ToButton e -> CODEC_HAT_TO_BUTTON.encode(e, ops, prefix); - case MappingEntry.FromHat.ToAxis e -> CODEC_HAT_TO_AXIS.encode(e, ops, prefix); - case MappingEntry.FromHat.ToHat e -> CODEC_HAT_TO_HAT.encode(e, ops, prefix); - case MappingEntry.FromNothing.ToButton e -> CODEC_NOTHING_TO_BUTTON.encode(e, ops, prefix); - case MappingEntry.FromNothing.ToAxis e -> CODEC_NOTHING_TO_AXIS.encode(e, ops, prefix); - case MappingEntry.FromNothing.ToHat e -> CODEC_NOTHING_TO_HAT.encode(e, ops, prefix); }; } @Override public DataResult> decode(DynamicOps ops, T input) { // First decode the stub to determine types - return CODEC_STUB.parse(ops, input).flatMap(stub -> switch (stub) { - case MappingEntryStub s when s.inputType() == MapType.BUTTON && s.outputType() == MapType.BUTTON -> - CODEC_BUTTON_TO_BUTTON.decode(ops, input) - .map(pair -> pair.mapFirst(e -> (MappingEntry) e)); - case MappingEntryStub s when s.inputType() == MapType.BUTTON && s.outputType() == MapType.AXIS -> - CODEC_BUTTON_TO_AXIS.decode(ops, input) - .map(pair -> pair.mapFirst(e -> (MappingEntry) e)); - case MappingEntryStub s when s.inputType() == MapType.BUTTON && s.outputType() == MapType.HAT -> - CODEC_BUTTON_TO_HAT.decode(ops, input) - .map(pair -> pair.mapFirst(e -> (MappingEntry) e)); - case MappingEntryStub s when s.inputType() == MapType.AXIS && s.outputType() == MapType.BUTTON -> - CODEC_AXIS_TO_BUTTON.decode(ops, input) - .map(pair -> pair.mapFirst(e -> (MappingEntry) e)); - case MappingEntryStub s when s.inputType() == MapType.AXIS && s.outputType() == MapType.AXIS -> - CODEC_AXIS_TO_AXIS.decode(ops, input) - .map(pair -> pair.mapFirst(e -> (MappingEntry) e)); - case MappingEntryStub s when s.inputType() == MapType.AXIS && s.outputType() == MapType.HAT -> - CODEC_AXIS_TO_HAT.decode(ops, input) - .map(pair -> pair.mapFirst(e -> (MappingEntry) e)); - case MappingEntryStub s when s.inputType() == MapType.HAT && s.outputType() == MapType.BUTTON -> - CODEC_HAT_TO_BUTTON.decode(ops, input) - .map(pair -> pair.mapFirst(e -> (MappingEntry) e)); - case MappingEntryStub s when s.inputType() == MapType.HAT && s.outputType() == MapType.AXIS -> - CODEC_HAT_TO_AXIS.decode(ops, input) - .map(pair -> pair.mapFirst(e -> (MappingEntry) e)); - case MappingEntryStub s when s.inputType() == MapType.HAT && s.outputType() == MapType.HAT -> - CODEC_HAT_TO_HAT.decode(ops, input) - .map(pair -> pair.mapFirst(e -> (MappingEntry) e)); - case MappingEntryStub s when s.inputType() == MapType.NOTHING && s.outputType() == MapType.BUTTON -> - CODEC_NOTHING_TO_BUTTON.decode(ops, input) - .map(pair -> pair.mapFirst(e -> (MappingEntry) e)); - case MappingEntryStub s when s.inputType() == MapType.NOTHING && s.outputType() == MapType.AXIS -> - CODEC_NOTHING_TO_AXIS.decode(ops, input) - .map(pair -> pair.mapFirst(e -> (MappingEntry) e)); - case MappingEntryStub s when s.inputType() == MapType.NOTHING && s.outputType() == MapType.HAT -> - CODEC_NOTHING_TO_HAT.decode(ops, input) - .map(pair -> pair.mapFirst(e -> (MappingEntry) e)); - default -> DataResult.error(() -> "Unknown MappingEntry type for input type " + stub.inputType() + " and output type " + stub.outputType()); + return CODEC_STUB.parse(ops, input).flatMap(stub -> { + Codec codec = switch (stub.inputType()) { + case BUTTON -> switch (stub.outputType()) { + case BUTTON -> CODEC_BUTTON_TO_BUTTON; + case AXIS -> CODEC_BUTTON_TO_AXIS; + }; + case AXIS -> switch (stub.outputType()) { + case BUTTON -> CODEC_AXIS_TO_BUTTON; + case AXIS -> CODEC_AXIS_TO_AXIS; + }; + }; + return codec.decode(ops, input) + .map(pair -> pair.mapFirst(entry -> entry)); }); } }; diff --git a/src/main/java/dev/isxander/controlify/controller/input/mapping/StateMapper.java b/src/main/java/dev/isxander/controlify/controller/input/mapping/StateMapper.java deleted file mode 100644 index 0ac303ff5..000000000 --- a/src/main/java/dev/isxander/controlify/controller/input/mapping/StateMapper.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller.input.mapping; - -import dev.isxander.controlify.controller.input.ControllerState; - -public interface StateMapper { -ControllerState mapState(ControllerState state); -} diff --git a/src/main/java/dev/isxander/controlify/controllermanager/ControllerCoordinator.java b/src/main/java/dev/isxander/controlify/controllermanager/ControllerCoordinator.java new file mode 100644 index 000000000..360e7e03c --- /dev/null +++ b/src/main/java/dev/isxander/controlify/controllermanager/ControllerCoordinator.java @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.controllermanager; + +import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.api.event.ControlifyEvents; +import dev.isxander.controlify.config.ConfigManager; +import dev.isxander.controlify.config.settings.device.DeviceSettings; +import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.driver.sdl.SDLNativesLoader; +import dev.isxander.controlify.gui.screen.AskToMapControllerScreen; +import dev.isxander.controlify.gui.screen.BluetoothWarningScreen; +import dev.isxander.controlify.input.physical.PhysicalInputSignal; +import dev.isxander.controlify.platform.main.PlatformMainUtil; +import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.utils.DebugLog; +import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.utils.SetupWizard; +import dev.isxander.controlify.utils.SetupWizardManager; +import dev.isxander.controlify.utils.event.EventSource; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import static dev.isxander.controlify.utils.ControllerUtils.wrapControllerError; + +/// Owns controller discovery, selection, activation and activity subscriptions. +/// Publishes selection changes through {@link ControlifyEvents#CURRENT_CONTROLLER_CHANGED}. +public class ControllerCoordinator implements AutoCloseable { + private final Controlify controlify; + private final ConfigManager config; + private final SetupWizardManager setupWizardManager; + private final ControllerManager controllerManager = new ControllerManager(); + private final Map inputSubscriptions = new HashMap<>(); + private final Set controllersWithInput = new LinkedHashSet<>(); + + private @Nullable ControllerBackend backend; + private @Nullable ControllerEntity currentController; + private boolean hasDiscoveredControllers; + private boolean closed; + + public ControllerCoordinator(Controlify controlify, SetupWizardManager setupWizardManager) { + this.controlify = controlify; + this.config = controlify.config(); + this.setupWizardManager = setupWizardManager; + + ControlifyEvents.CONTROLLER_CONNECTED.register(event -> + this.onControllerAdded(event.controller(), event.hotplugged())); + ControlifyEvents.CONTROLLER_DISCONNECTED.register(event -> + this.onControllerRemoved(event.controller())); + } + + public boolean initialize() { + try { + this.backend = new SDLControllerBackend(SDLNativesLoader.load(), this.controlify, this.controllerManager); + } catch (Throwable throwable) { + CUtil.LOGGER.error("Failed to initialize controller backend", throwable); + return false; + } + this.backend.setup(); + return true; + } + + public boolean isReady() { + return this.backend != null && !this.closed; + } + + public ControllerManager controllerManager() { + return this.controllerManager; + } + + public Optional currentController() { + return Optional.ofNullable(this.currentController); + } + + public void discoverControllers() { + if (!this.isReady()) { + return; + } + if (this.hasDiscoveredControllers) { + CUtil.LOGGER.warn("Attempted to discover controllers twice!"); + return; + } + this.hasDiscoveredControllers = true; + DebugLog.log("Discovering and initializing controllers..."); + this.backend.discoverControllers(); + if (this.controllerManager.getAllControllers().isEmpty()) { + CUtil.LOGGER.log("No controllers found."); + } + this.applyControllerSelection(false); + this.config.saveIfDirty(); + + PlatformMainUtil.applyToControlifyEntrypoint(entrypoint -> { + try { + entrypoint.onControllersDiscovered(this.controlify); + } catch (Throwable e) { + CUtil.LOGGER.error("Failed to run `onControllersDiscovered` on Controlify entrypoint: {}", entrypoint.getClass().getName(), e); + } + }); + } + + /// Polls every controller and resolves selection after all events have been received. + /// @return whether the selected controller received non-sensor input during this poll + public boolean update(boolean outOfFocus) { + this.controllersWithInput.clear(); + + if (!this.isReady()) { + return false; + } + + this.backend.pollEvents(); + + if (!outOfFocus && this.config.getActiveProfile().controllerUid == null + && !this.controllersWithInput.contains(this.currentController)) { + // The active controller wins regardless of the order of events within the poll. + this.controllersWithInput.stream().findFirst().ifPresent(controller -> wrapControllerError( + () -> this.setCurrentController(controller, true), + "Selecting controller from input", controller)); + } + return !outOfFocus && this.controllersWithInput.contains(this.currentController); + } + + public void setCurrentController(@Nullable ControllerEntity controller, boolean changeInputMode) { + if (this.closed || this.currentController == controller) { + return; + } + if (this.currentController != null) { + this.currentController.setActive(false); + } + this.currentController = controller; + if (controller != null) { + controller.setActive(true); + DebugLog.log("Updated current controller to {}({})", controller.name(), controller.uid()); + } else { + DebugLog.log("Cleared current controller."); + } + ControlifyEvents.CURRENT_CONTROLLER_CHANGED.invoke( + new ControlifyEvents.CurrentControllerChanged(controller, changeInputMode)); + } + + public void applyControllerSelection(boolean changeInputMode) { + String selectedUid = this.config.getActiveProfile().controllerUid; + ControllerEntity selected = this.controllerManager.getAllControllers().stream() + .filter(controller -> selectedUid == null || selectedUid.equals(controller.uid())) + .findFirst().orElse(null); + this.setCurrentController(selected, changeInputMode); + } + + public void applyActiveProfile() { + this.controllerManager.getAllControllers().forEach(controller -> + controller.setSettings(this.config.getActiveProfile())); + this.applyControllerSelection(true); + } + + private void onControllerAdded(ControllerEntity controller, boolean hotplugged) { + if (this.closed) { + return; + } + controller.input().ifPresent(input -> { + EventSource.Subscription previous = this.inputSubscriptions.remove(controller); + if (previous != null) { + previous.close(); + } + this.inputSubscriptions.put(controller, input.inputPipeline().sourceProcessedPhysicalSignals() + .filter(signal -> signal instanceof PhysicalInputSignal) + .subscribe(_ -> this.controllersWithInput.add(controller))); + }); + + DeviceSettings rememberedDevice = this.config.getSettings().getOrCreateDeviceSettings(controller.uid()); + rememberedDevice.name = controller.name(); + rememberedDevice.lastSeen = System.currentTimeMillis(); + rememberedDevice.controllerType = controller.info().type().namespace(); + this.config.markDirty(); + + String selectedUid = this.config.getActiveProfile().controllerUid; + if (selectedUid == null || selectedUid.equals(controller.uid())) { + this.setCurrentController(controller, true); + } + + SetupWizard wizard = new SetupWizard(); + wizard.addStage( + () -> controller.input().map(input -> !input.isGamepad() + && this.config.getSettings().getOrCreateDeviceSettings(controller.uid()).mapping == null) + .orElse(false), + nextScreen -> new AskToMapControllerScreen(controller, nextScreen)); + wizard.addStage( + () -> controller.dualSense().isPresent() + && controller.bluetooth().map(bt -> !bt.settings().dontShowWarning).orElse(false), + nextScreen -> new BluetoothWarningScreen(controller.bluetooth().orElseThrow(), nextScreen)); + if (hotplugged) { + MinecraftUtil.sendToast( + Component.translatable("controlify.toast.controller_connected.title"), + Component.translatable("controlify.toast.controller_connected.description", controller.name()), false); + this.config.saveIfDirty(); + } + this.setupWizardManager.push(wizard); + } + + private void onControllerRemoved(ControllerEntity controller) { + if (this.closed) { + return; + } + EventSource.Subscription subscription = this.inputSubscriptions.remove(controller); + if (subscription != null) { + subscription.close(); + } + this.controllersWithInput.remove(controller); + if (this.currentController == controller) { + ControllerEntity fallback = this.config.getActiveProfile().controllerUid == null + ? this.controllerManager.getAllControllers().stream() + .filter(candidate -> candidate != controller).findFirst().orElse(null) + : null; + this.setCurrentController(fallback, true); + } + MinecraftUtil.sendToast( + Component.translatable("controlify.toast.controller_disconnected.title"), + Component.translatable("controlify.toast.controller_disconnected.description", controller.name()), false); + } + + @Override + public void close() { + if (this.closed) { + return; + } + this.closed = true; + this.inputSubscriptions.values().forEach(EventSource.Subscription::close); + this.inputSubscriptions.clear(); + this.controllersWithInput.clear(); + this.currentController = null; + this.controllerManager.close(); + } +} diff --git a/src/main/java/dev/isxander/controlify/controllermanager/SDLControllerBackend.java b/src/main/java/dev/isxander/controlify/controllermanager/SDLControllerBackend.java index 2b437f4ac..fc0d497a1 100644 --- a/src/main/java/dev/isxander/controlify/controllermanager/SDLControllerBackend.java +++ b/src/main/java/dev/isxander/controlify/controllermanager/SDLControllerBackend.java @@ -28,6 +28,7 @@ public class SDLControllerBackend extends ControllerBackend { private final EventTap controllerEventTap; private SdlEvent event; + private long lastFrameTimeNanos = -1; public SDLControllerBackend(Sdl sdl, Controlify controlify, ControllerManager controllerManager) { super(controlify, controllerManager); @@ -98,6 +99,11 @@ public void pollEvents() { while (this.sdl.events().SDL_PollEvent(this.event)) { this.controllerEventStage.onEvent(this.event, this::onControllerEvent); } + + long timeNanos = this.sdl.timer().SDL_GetTicksNS(); + long deltaTimeNanos = this.lastFrameTimeNanos == -1 ? 0 : timeNanos - this.lastFrameTimeNanos; + this.lastFrameTimeNanos = timeNanos; + this.onControllerEvent(new SDLControllerEvent.Frame(timeNanos, deltaTimeNanos)); } private void onControllerEvent(SDLControllerEvent event) { @@ -146,7 +152,8 @@ private void createAndAddController( controllerLogger.debugLog("Controller is gamepad: {}", isGamepad); EventSource controllerEventSource = this.controllerEventTap - .filter(event -> event.which().equals(jid)); + .filter(event -> event instanceof SDLControllerEvent.Frame + || event instanceof SDLControllerEvent.DeviceScopedEvent scoped && scoped.which().equals(jid)); Driver driver; if (isGamepad) { diff --git a/src/main/java/dev/isxander/controlify/driver/CompoundDriver.java b/src/main/java/dev/isxander/controlify/driver/CompoundDriver.java index fa157e05f..5175847a9 100644 --- a/src/main/java/dev/isxander/controlify/driver/CompoundDriver.java +++ b/src/main/java/dev/isxander/controlify/driver/CompoundDriver.java @@ -33,6 +33,13 @@ public void update(ControllerEntity controller, boolean outOfFocus) { } } + @Override + public void setActive(boolean active) { + for (Driver driver : drivers) { + driver.setActive(active); + } + } + @Override public void close() { for (Driver driver : drivers) { diff --git a/src/main/java/dev/isxander/controlify/driver/Driver.java b/src/main/java/dev/isxander/controlify/driver/Driver.java index e258310f3..3f4e0c43f 100644 --- a/src/main/java/dev/isxander/controlify/driver/Driver.java +++ b/src/main/java/dev/isxander/controlify/driver/Driver.java @@ -13,5 +13,9 @@ default void addComponents(ControllerEntity controller) {} default void update(ControllerEntity controller, boolean outOfFocus) {} + /// Enables resources needed only by the active controller, such as sensor sampling. + /// Buttons and axes must remain available while inactive for controller selection. + default void setActive(boolean active) {} + default void close() {} } diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDL3GamepadDriver.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDL3GamepadDriver.java index f719323c8..48dc387ff 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDL3GamepadDriver.java +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDL3GamepadDriver.java @@ -10,12 +10,10 @@ import dev.isxander.controlify.controller.touchpad.TouchpadComponent; import dev.isxander.controlify.controller.gyro.GyroComponent; import dev.isxander.controlify.controller.input.GamepadInputs; -import dev.isxander.controlify.controller.gyro.GyroState; import dev.isxander.controlify.controller.*; import dev.isxander.controlify.controller.touchpad.Touchpads; import dev.isxander.controlify.input.InputComponent; import dev.isxander.controlify.input.SensorType; -import dev.isxander.controlify.input.logical.LogicalSignal; import dev.isxander.controlify.utils.event.EventSource; import dev.isxander.controlify.utils.log.ControlifyLogger; import dev.isxander.sdl.*; @@ -32,14 +30,14 @@ public class SDL3GamepadDriver extends SDLCommonDriver { private InputComponent inputComponent; - private GyroComponent gyroComponent; private TouchpadComponent touchpadComponent; private final Set supportedButtons; private final Set supportedAxes; private final Set supportedSensors; - private final boolean isGryoSupported; + private final boolean isGyroSupported; + private boolean active; private final int numTouchpads; @@ -60,10 +58,7 @@ public SDL3GamepadDriver(Sdl sdl, SdlGamepadHandle ptrController, EventSource { - if (sig instanceof LogicalSignal.SensorInterval(long timeNanos, long deltaTimeNanos, SensorType sensorType, float x, float y, float z)) { - if (sensorType == SensorType.GYROSCOPE) { - this.gyroComponent.setState(new GyroState(x, y, z)); - } - } - }); + if (this.isGyroSupported) { + controller.setComponent(new GyroComponent(this.inputPipeline)); } if (this.numTouchpads > 0) { @@ -120,8 +97,27 @@ public void addComponents(ControllerEntity controller) { } } + @Override + public void setActive(boolean active) { + if (this.ptrController == null) { + throw new IllegalStateException("Tried to activate or deactivate a closed controller."); + } + if (this.active == active) { + return; + } + this.active = active; + if (this.isGyroSupported + && !this.sdl.gamepad().SDL_SetGamepadSensorEnabled(this.ptrController, SDL_SENSOR_GYRO, active)) { + this.logger.warn("Failed to {} gyro sensor: {}", active ? "enable" : "disable", this.sdl.error().SDL_GetError()); + } + } + @Override protected void onEvent(SDLControllerEvent event) { + // SDL may still have queued samples after the sensor has been disabled. + if (!this.active && event instanceof SDLControllerEvent.GamepadSensor) { + return; + } super.onEvent(event); switch (event) { @@ -137,6 +133,12 @@ public void update(ControllerEntity controller, boolean outOfFocus) { super.update(controller, outOfFocus); } + @Override + public void close() { + this.setActive(false); + super.close(); + } + private void onTouchpadEvent(SDLControllerEvent.GamepadTouchpad event) { // TODO: touchpad component probably needs redoing, it was designed to receive // complete snapshots of touchpad data, not continuous updates diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLCommonDriver.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDLCommonDriver.java index a4b48168c..0ef26ce5a 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDLCommonDriver.java +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDLCommonDriver.java @@ -52,7 +52,7 @@ public abstract class SDLCommonDriver implements Driver { private static final int AUDIO_STREAM_TIMEOUT_TICKS = 5 * 60 * 60 * 20; // 5 minutes protected final Sdl sdl; - private final ControlifyLogger logger; + protected final ControlifyLogger logger; protected final Arena arena; @@ -186,6 +186,8 @@ public void addComponents(ControllerEntity controller) { if (isBluetooth()) { controller.setComponent(new BluetoothDeviceComponent()); } + + this.eventSubscription = this.sdlEventSource.subscribe(this::onEvent); } protected void onEvent(SDLControllerEvent event) { @@ -426,7 +428,7 @@ protected String createUid() { String nonDuplicateUid = uid; int duplicateCount = (int) Controlify.instance().getControllerManager().orElseThrow() - .getConnectedControllers() + .getAllControllers() .stream() .filter(controller -> controller.uid().startsWith(nonDuplicateUid)) .count(); diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLControllerEvent.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDLControllerEvent.java index 60a8ba551..b1d763393 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDLControllerEvent.java +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDLControllerEvent.java @@ -6,12 +6,18 @@ public sealed interface SDLControllerEvent { long timeNanos(); - SdlJoystickId which(); + /// Emitted once after each backend poll and delivered to every controller. + /// Uses the same SDL clock as native event timestamps; the first frame has a zero delta. + record Frame(long timeNanos, long deltaTimeNanos) implements SDLControllerEvent {} - sealed interface JoystickEvent extends SDLControllerEvent {} - sealed interface GamepadEvent extends SDLControllerEvent {} + sealed interface DeviceScopedEvent extends SDLControllerEvent { + SdlJoystickId which(); + } + + sealed interface JoystickEvent extends DeviceScopedEvent {} + sealed interface GamepadEvent extends DeviceScopedEvent {} - sealed interface DeviceEvent extends SDLControllerEvent { + sealed interface DeviceEvent extends DeviceScopedEvent { Type type(); enum Type { @@ -25,7 +31,7 @@ enum Type { } } - sealed interface AxisEvent extends SDLControllerEvent { + sealed interface AxisEvent extends DeviceScopedEvent { /// The axis. It may be an index or a constant, depending on the event type. byte axis(); @@ -33,7 +39,7 @@ sealed interface AxisEvent extends SDLControllerEvent { short value(); } - sealed interface ButtonEvent extends SDLControllerEvent { + sealed interface ButtonEvent extends DeviceScopedEvent { /// The button. It may be an index or a constant, depending on the event type. byte button(); @@ -85,7 +91,7 @@ record Battery( long timeNanos, PowerState state, int percent - ) implements SDLControllerEvent { + ) implements DeviceScopedEvent { public enum PowerState { ERROR, UNKNOWN, diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLToPhysicalStage.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDLToPhysicalStage.java index e43cfa0a0..0b0554298 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDLToPhysicalStage.java +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDLToPhysicalStage.java @@ -23,6 +23,9 @@ public SDLToPhysicalStage() { @Override public void onEvent(SDLControllerEvent event, EventSink downstream) { switch (event) { + case SDLControllerEvent.Frame(long timeNanos, long deltaTimeNanos) -> + downstream.accept(new PhysicalSignal.Frame(timeNanos, deltaTimeNanos)); + case SDLControllerEvent.JoyAxis(_, long timeNanos, byte axis, short value) -> { float normalisedValue = normaliseAxis(value); diff --git a/src/main/java/dev/isxander/controlify/gui/screen/ControllerMappingMakerScreen.java b/src/main/java/dev/isxander/controlify/gui/screen/ControllerMappingMakerScreen.java index 01dd89c0a..facfb4f6f 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/ControllerMappingMakerScreen.java +++ b/src/main/java/dev/isxander/controlify/gui/screen/ControllerMappingMakerScreen.java @@ -154,11 +154,6 @@ private void processStage(MappingStage stage, ControllerStateView stateNow, Cont MappingEntry mapping = switch (stage.outputType()) { case BUTTON -> new MappingEntry.FromButton.ToButton(button, stage.originInput(), !now); case AXIS -> new MappingEntry.FromButton.ToAxis(button, stage.originInput(), prev ? 1 : 0, now ? 1 : 0); - case HAT -> { - HatState state = now ? HatState.DOWN : HatState.UP; - yield new MappingEntry.FromButton.ToHat(button, stage.originInput(), HatState.CENTERED, state); - } - case NOTHING -> null; }; mappingBuilder.putMapping(mapping); stage.setSatisfied(true); @@ -174,24 +169,6 @@ private void processStage(MappingStage stage, ControllerStateView stateNow, Cont MappingEntry mapping = switch (stage.outputType()) { case BUTTON -> new MappingEntry.FromAxis.ToButton(axis, stage.originInput(), 0.5f); case AXIS -> new MappingEntry.FromAxis.ToAxis(axis, stage.originInput(), 0, 0, 1, 1); - case HAT -> new MappingEntry.FromAxis.ToHat(axis, stage.originInput(), 0.5f, diff > 0 ? HatState.UP : HatState.DOWN); - case NOTHING -> null; - }; - mappingBuilder.putMapping(mapping); - stage.setSatisfied(true); - return; - } - } - - for (Identifier hat : stateNow.getHats()) { - HatState now = stateNow.getHatState(hat); - HatState prev = stateThen.getHatState(hat); - if (now != prev) { - MappingEntry mapping = switch (stage.outputType()) { - case BUTTON -> new MappingEntry.FromHat.ToButton(hat, stage.originInput(), now); - case AXIS -> new MappingEntry.FromHat.ToAxis(hat, stage.originInput(), now, 0, 1); - case HAT -> new MappingEntry.FromHat.ToHat(hat, stage.originInput()); - case NOTHING -> null; }; mappingBuilder.putMapping(mapping); stage.setSatisfied(true); @@ -205,14 +182,6 @@ private void setStage(MappingStage stage) { } private void mapAsNone() { - MappingEntry mapping = switch (currentStage.outputType()) { - case BUTTON -> new MappingEntry.FromNothing.ToButton(currentStage.originInput(), false); - case AXIS -> new MappingEntry.FromNothing.ToAxis(currentStage.originInput(), 0f); - case HAT -> new MappingEntry.FromNothing.ToHat(currentStage.originInput()); - case NOTHING -> null; - }; - mappingBuilder.putMapping(mapping); - delayTillNextStage = 0; } diff --git a/src/main/java/dev/isxander/controlify/input/InputComponent.java b/src/main/java/dev/isxander/controlify/input/InputComponent.java index d95475312..8f2017480 100644 --- a/src/main/java/dev/isxander/controlify/input/InputComponent.java +++ b/src/main/java/dev/isxander/controlify/input/InputComponent.java @@ -78,13 +78,7 @@ public void attach(ControllerEntity controller) { this.reloadActions(); this.actionGraphSub = this.inputPipeline.sourceLogicalSignals().subscribe(this.actionGraph); - this.inputPipeline.stageDeadzone().setDeadzoneSupplier(axis -> { - DeadzoneGroup group = this.deadzoneGroups.get(axis); - if (group == null) { - return 0f; - } - return settings().sensitivity.getDeadzone(group.name()); - }); + this.inputPipeline.stageDeadzone().setDeadzoneSupplier(axis -> settings().sensitivity.getDeadzone((Identifier) axis)); this.inputPipeline.stageSensorProcessor().setProcessors(sensorType -> { if (sensorType.isGyroscope()) { return List.of( @@ -101,6 +95,14 @@ public void attach(ControllerEntity controller) { return this.actions.get(actionId); } + public InputPipeline inputPipeline() { + return this.inputPipeline; + } + + public void resetActions() { + this.actions.values().forEach(ActionImpl::reset); + } + public void reloadActions() { this.actions.clear(); this.actionGraph.clearActions(); diff --git a/src/main/java/dev/isxander/controlify/input/logical/SensorAccumulatorStage.java b/src/main/java/dev/isxander/controlify/input/logical/SensorAccumulatorStage.java index 587d3418d..6d5ca6083 100644 --- a/src/main/java/dev/isxander/controlify/input/logical/SensorAccumulatorStage.java +++ b/src/main/java/dev/isxander/controlify/input/logical/SensorAccumulatorStage.java @@ -22,6 +22,8 @@ public void onEvent( ) { switch (event) { case PhysicalSignal.Frame(long timeNanos, _) -> { + this.accSensorValues.values().removeIf(acc -> + acc.lastSampleNanos != -1 && timeNanos - acc.lastSampleNanos > MAX_SAMPLE_AGE_NANOS); this.accSensorValues.forEach((sensorType, acc) -> { if (acc.lastNs == -1) { return; @@ -102,21 +104,19 @@ public void integrateUntil(long timeNanos) { return; } - // Integrate only while the last sensor reading is considered valid. - long validUntilNanos = lastSampleNanos + MAX_SAMPLE_AGE_NANOS; - long integrationEndNanos = Math.min(timeNanos, validUntilNanos); - - if (integrationEndNanos > lastNs) { - long integrationNanos = integrationEndNanos - lastNs; - float deltaSeconds = integrationNanos * 1e-9f; - - x += velocityX * deltaSeconds; - y += velocityY * deltaSeconds; - z += velocityZ * deltaSeconds; - - accumulatedNanos += integrationNanos; + if (lastSampleNanos == -1 || timeNanos - lastSampleNanos > MAX_SAMPLE_AGE_NANOS) { + // A gap invalidates both the held sample and any motion waiting for a frame. + resetAccumulation(); + lastSampleNanos = -1; + lastNs = timeNanos; + return; } + float deltaSeconds = deltaNanos * 1e-9f; + x += velocityX * deltaSeconds; + y += velocityY * deltaSeconds; + z += velocityZ * deltaSeconds; + accumulatedNanos += deltaNanos; lastNs = timeNanos; } diff --git a/src/main/java/dev/isxander/controlify/input/physical/DeadzoneStage.java b/src/main/java/dev/isxander/controlify/input/physical/DeadzoneStage.java index a1ef784b5..5349465ab 100644 --- a/src/main/java/dev/isxander/controlify/input/physical/DeadzoneStage.java +++ b/src/main/java/dev/isxander/controlify/input/physical/DeadzoneStage.java @@ -1,54 +1,107 @@ package dev.isxander.controlify.input.physical; +import dev.isxander.controlify.controller.input.DeadzoneGroup; import dev.isxander.controlify.utils.ControllerUtils; import dev.isxander.controlify.utils.event.EventSink; import dev.isxander.controlify.utils.event.UnaryEventStage; +import it.unimi.dsi.fastutil.objects.*; import net.minecraft.resources.Identifier; +import net.minecraft.util.Mth; import java.util.HashSet; +import java.util.Map; import java.util.Set; -import java.util.function.Function; +import java.util.stream.Collectors; -/// Applies deadzones to physical axes. -// TODO: radial deadzones +/// Applies radial deadzones to physical axes. public class DeadzoneStage implements UnaryEventStage { - private Function deadzoneSupplier; - private final Set axisWasZero; + private final Map groupByAxis; + private final Map stateByGroup; - public DeadzoneStage(Function deadzoneSupplier) { - this.deadzoneSupplier = deadzoneSupplier; - this.axisWasZero = new HashSet<>(); - } + private Object2FloatFunction deadzoneByGroupName; - public DeadzoneStage() { - this(_ -> 0f); + public DeadzoneStage(Set deadzoneGroups) { + this.groupByAxis = deadzoneGroups.stream() + .flatMap(g -> g.axes().stream().map(a -> Map.entry(a, g))) + .collect(Collectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue, + (_, _) -> { + throw new IllegalArgumentException("Axis belongs to multiple deadzone groups"); + }, + Object2ObjectOpenHashMap::new + )); + this.stateByGroup = new Object2ObjectArrayMap<>(); + this.deadzoneByGroupName = _ -> 0f; } @Override public void onEvent(PhysicalSignal event, EventSink downstream) { - if (event instanceof PhysicalInputSignal.AxisMoved(long timeNanos, Identifier input, float value)) { - float deadzone = this.deadzoneSupplier.apply(input); - - if (deadzone > 0) { - float newValue = ControllerUtils.deadzone(value, deadzone); + if (event instanceof PhysicalInputSignal.AxisMoved axisMoved) { + DeadzoneGroup deadzoneGroup = this.groupByAxis.get(axisMoved.input()); - if (newValue != 0 || this.axisWasZero.add(input)) { - downstream.accept(new PhysicalInputSignal.AxisMoved(timeNanos, input, newValue)); - if (newValue != 0) { - this.axisWasZero.remove(input); - } - } + if (deadzoneGroup != null) { + RadialDeadzoneState state = this.stateByGroup.computeIfAbsent(deadzoneGroup, RadialDeadzoneState::new); + state.onEvent(axisMoved, downstream); return; - } else { - this.axisWasZero.remove(input); } } downstream.accept(event); } - public void setDeadzoneSupplier(Function deadzoneSupplier) { - this.deadzoneSupplier = deadzoneSupplier; + public void setDeadzoneSupplier(Object2FloatFunction deadzoneByGroupName) { + this.deadzoneByGroupName = deadzoneByGroupName; + } + + private class RadialDeadzoneState implements UnaryEventStage { + private final DeadzoneGroup deadzoneGroup; + private final Object2FloatMap rawAxes = new Object2FloatArrayMap<>(4); + private final Object2FloatMap outputAxes = new Object2FloatArrayMap<>(4); + + public RadialDeadzoneState(DeadzoneGroup deadzoneGroup) { + this.deadzoneGroup = deadzoneGroup; + + for (Identifier axis : deadzoneGroup.axes()) { + this.rawAxes.put(axis, 0.0f); + this.outputAxes.put(axis, 0.0f); + } + } + + @Override + public void onEvent(PhysicalInputSignal.AxisMoved event, EventSink downstream) { + this.rawAxes.put(event.input(), event.value()); + + float deadzone = DeadzoneStage.this.deadzoneByGroupName.getFloat(this.deadzoneGroup.name()); + + float magnitudeSqr = 0.0f; + for (float value : this.rawAxes.values()) { + magnitudeSqr += value * value; + } + float magnitude = Mth.sqrt(magnitudeSqr); + + float scale; + if (magnitude <= deadzone) { + scale = 0.0f; + } else { + float outputMagnitude = Math.min( + (magnitude - deadzone) / (1.0f - deadzone), + 1.0f + ); + scale = outputMagnitude / magnitude; + } + + for (var entry : Object2FloatMaps.fastIterable(this.rawAxes)) { + Identifier axis = entry.getKey(); + + float newValue = entry.getFloatValue() * scale; + float oldValue = this.outputAxes.put(axis, newValue); + + if (newValue != oldValue) { + downstream.accept(new PhysicalInputSignal.AxisMoved(event.timeNanos(), axis, newValue)); + } + } + } } } diff --git a/src/main/java/dev/isxander/controlify/input/physical/SensorProcessorStage.java b/src/main/java/dev/isxander/controlify/input/physical/SensorProcessorStage.java index 2e007424c..c90e7c733 100644 --- a/src/main/java/dev/isxander/controlify/input/physical/SensorProcessorStage.java +++ b/src/main/java/dev/isxander/controlify/input/physical/SensorProcessorStage.java @@ -44,7 +44,6 @@ case PhysicalSensorSignal(long timeNanos, long sensorTimeNanos, SensorType senso processor.process(data, sensorTimeNanos); } - // TODO: port GyroCalibrator here downstream.accept(new PhysicalSensorSignal(timeNanos, sensorTimeNanos, sensorType, data.x, data.y, data.z)); } default -> downstream.accept(event); @@ -64,9 +63,11 @@ public void process(Vector3f data, long timeNanos) { /// A first-order low-pass filter with a finite, positive cutoff frequency in Hz. /// Lower frequencies smooth more strongly. The first sample passes through unchanged. + /// History expires after a gap of 50ms or when the sensor clock moves backwards. /// /// If the frequency is set to 0, the filter is bypassed. public static class LowPassFilter implements SensorProcessor { + private static final long MAX_SAMPLE_AGE_NANOS = 50_000_000L; private final DoubleSupplier cutoffHz; private @Nullable Vector3f previous; private long previousTimeNanos; @@ -79,11 +80,12 @@ public LowPassFilter(DoubleSupplier cutoffHz) { public void process(Vector3f data, long timeNanos) { double cutoffHz = this.cutoffHz.getAsDouble(); - if (cutoffHz == 0 || this.previous == null) { + long elapsedNanos = timeNanos - this.previousTimeNanos; + if (cutoffHz == 0 || this.previous == null + || elapsedNanos > MAX_SAMPLE_AGE_NANOS || elapsedNanos < 0) { this.previous = new Vector3f(data); this.previousTimeNanos = timeNanos; } else { - long elapsedNanos = timeNanos - this.previousTimeNanos; if (elapsedNanos > 0) { // Exponential decay keeps the response consistent across sample rates. // expm1 preserves precision for very short sample intervals. 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 2a25963d8..736a94226 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/MinecraftMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/MinecraftMixin.java @@ -7,7 +7,7 @@ package dev.isxander.controlify.mixins.core; import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.controllermanager.ControllerManager; +import dev.isxander.controlify.controllermanager.ControllerCoordinator; import dev.isxander.controlify.utils.animation.impl.Animator; import net.minecraft.CrashReport; import net.minecraft.client.DeltaTracker; @@ -57,7 +57,7 @@ private void doPlayerLook(CallbackInfo ci) { ) ) private void onMinecraftClose(CallbackInfo ci) { - Controlify.instance().getControllerManager().ifPresent(ControllerManager::close); + Controlify.instance().getControllerCoordinator().ifPresent(ControllerCoordinator::close); } @Inject( From 7683b39ee9f0c9f133bd6f39a44a030bbe80121b Mon Sep 17 00:00:00 2001 From: isxander Date: Fri, 16 Oct 2026 16:00:28 +0100 Subject: [PATCH 09/44] wire up controller mapping to pipeline --- .../settings/device/DeviceSettings.java | 1 + .../controller/impl/ControllerStateImpl.java | 106 ---------- .../controller/input/ControllerState.java | 17 -- .../controller/input/ControllerStateView.java | 31 --- .../input/DeadzoneControllerStateView.java | 62 ------ .../controlify/controller/input/HatState.java | 57 ------ .../controller/input/InputComponent.java | 187 ------------------ .../input/ModifiableControllerState.java | 17 -- .../input/mapping/ControllerMapping.java | 10 - .../mapping/ControllerMappingStorage.java | 47 ----- .../input/mapping/MappingEntry.java | 4 - .../input/mapping/MappingEntryCodecs.java | 1 - .../mapping/MappingEntryTypeAdapter.java | 53 ----- .../driver/sdl/SDL3GamepadDriver.java | 10 +- .../driver/sdl/SDL3JoystickDriver.java | 3 +- .../controlify/input/InputComponent.java | 27 +-- .../controlify/input/InputPipeline.java | 8 + .../input/physical/DeadzoneStage.java | 41 ++-- .../input/physical/MappingStage.java | 40 ++++ 19 files changed, 90 insertions(+), 632 deletions(-) delete mode 100644 src/main/java/dev/isxander/controlify/controller/impl/ControllerStateImpl.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/input/ControllerState.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/input/ControllerStateView.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/input/DeadzoneControllerStateView.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/input/HatState.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/input/InputComponent.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/input/ModifiableControllerState.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/input/mapping/ControllerMappingStorage.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntryTypeAdapter.java create mode 100644 src/main/java/dev/isxander/controlify/input/physical/MappingStage.java diff --git a/src/main/java/dev/isxander/controlify/config/settings/device/DeviceSettings.java b/src/main/java/dev/isxander/controlify/config/settings/device/DeviceSettings.java index 4bd8a88af..c255e2036 100644 --- a/src/main/java/dev/isxander/controlify/config/settings/device/DeviceSettings.java +++ b/src/main/java/dev/isxander/controlify/config/settings/device/DeviceSettings.java @@ -19,6 +19,7 @@ public class DeviceSettings { public long lastSeen; public Identifier controllerType; public final GyroCalibrationSettings gyroCalibration; + /// Controller must be re-initialized for a change in mapping to take effect. public @Nullable ControllerMapping mapping; private DeviceSettings(String uid) { diff --git a/src/main/java/dev/isxander/controlify/controller/impl/ControllerStateImpl.java b/src/main/java/dev/isxander/controlify/controller/impl/ControllerStateImpl.java deleted file mode 100644 index c7f7abdad..000000000 --- a/src/main/java/dev/isxander/controlify/controller/impl/ControllerStateImpl.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller.impl; - -import com.google.common.base.Joiner; -import dev.isxander.controlify.controller.input.HatState; -import dev.isxander.controlify.controller.input.ModifiableControllerState; -import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap; -import it.unimi.dsi.fastutil.objects.Object2FloatArrayMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; -import net.minecraft.resources.Identifier; - -import java.util.Map; -import java.util.Set; - -public class ControllerStateImpl implements ModifiableControllerState { - private final Map buttons; - private final Map axes; - private final Map restingAxes; - private final Map hats; - - public ControllerStateImpl() { - this.buttons = new Object2BooleanArrayMap<>(); - this.axes = new Object2FloatArrayMap<>(); - this.restingAxes = new Object2FloatArrayMap<>(); - this.hats = new Object2ObjectArrayMap<>(); - } - - @Override - public boolean isButtonDown(Identifier button) { - return buttons.getOrDefault(button, false); - } - - @Override - public Set getButtons() { - return buttons.keySet(); - } - - @Override - public float getAxisState(Identifier axis) { - return axes.getOrDefault(axis, 0f); - } - - @Override - public Set getAxes() { - return axes.keySet(); - } - - @Override - public float getAxisResting(Identifier axis) { - return restingAxes.getOrDefault(axis, 0f); - } - - @Override - public HatState getHatState(Identifier hat) { - return hats.getOrDefault(hat, HatState.CENTERED); - } - - @Override - public Set getHats() { - return hats.keySet(); - } - - @Override - public void setButton(Identifier button, boolean value) { - buttons.put(button, value); - } - - @Override - public void setAxis(Identifier axis, float value) { - axes.put(axis, value); - } - - public void setRestingAxis(Identifier axis, float value) { - if (!axes.containsKey(axis)) - throw new IllegalArgumentException("Cannot set resting axis for axis that doesn't exist"); - - restingAxes.put(axis, value); - } - - @Override - public void setHat(Identifier hat, HatState value) { - hats.put(hat, value); - } - - @Override - public void clearState() { - this.buttons.clear(); - this.axes.clear(); - this.restingAxes.clear(); - this.hats.clear(); - } - - public String toDebugString() { - Joiner.MapJoiner joiner = Joiner.on(",").withKeyValueSeparator("="); - return "ControllerState{axes:%s,buttons:%s,hats:%s}".formatted( - joiner.join(axes), - joiner.join(buttons), - joiner.join(hats) - ); - } -} diff --git a/src/main/java/dev/isxander/controlify/controller/input/ControllerState.java b/src/main/java/dev/isxander/controlify/controller/input/ControllerState.java deleted file mode 100644 index fbabdfc10..000000000 --- a/src/main/java/dev/isxander/controlify/controller/input/ControllerState.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller.input; - -import dev.isxander.controlify.controller.impl.ControllerStateImpl; - -public interface ControllerState extends ControllerStateView { - ControllerState EMPTY = new ControllerStateImpl(); - - void clearState(); - - default void close() {} -} diff --git a/src/main/java/dev/isxander/controlify/controller/input/ControllerStateView.java b/src/main/java/dev/isxander/controlify/controller/input/ControllerStateView.java deleted file mode 100644 index 46141b247..000000000 --- a/src/main/java/dev/isxander/controlify/controller/input/ControllerStateView.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller.input; - -import net.minecraft.resources.Identifier; - -import java.util.Set; - -public interface ControllerStateView { - boolean isButtonDown(Identifier button); - - Set getButtons(); - - float getAxisState(Identifier axis); - Set getAxes(); - - float getAxisResting(Identifier axis); - - HatState getHatState(Identifier hat); - Set getHats(); - - default boolean isGivingInput() { - return getButtons().stream().anyMatch(this::isButtonDown) - || getAxes().stream().map(this::getAxisState).anyMatch(axis -> Math.abs(axis) > 0.5f) - || getHats().stream().map(this::getHatState).anyMatch(hat -> hat != HatState.CENTERED); - } -} diff --git a/src/main/java/dev/isxander/controlify/controller/input/DeadzoneControllerStateView.java b/src/main/java/dev/isxander/controlify/controller/input/DeadzoneControllerStateView.java deleted file mode 100644 index 2a1f62e41..000000000 --- a/src/main/java/dev/isxander/controlify/controller/input/DeadzoneControllerStateView.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller.input; - -import dev.isxander.controlify.utils.ControllerUtils; -import net.minecraft.resources.Identifier; - -import java.util.Optional; -import java.util.Set; - -public class DeadzoneControllerStateView implements ControllerStateView { - private final ControllerStateView view; - private final InputComponent input; - - public DeadzoneControllerStateView(ControllerStateView view, InputComponent input) { - this.view = view; - this.input = input; - } - - @Override - public boolean isButtonDown(Identifier button) { - return view.isButtonDown(button); - } - - @Override - public Set getButtons() { - return view.getButtons(); - } - - @Override - public float getAxisState(Identifier axis) { - float rawAxis = view.getAxisState(axis); - Optional deadzoneId = input.getDeadzoneForAxis(axis); - float deadzone = deadzoneId.map(id -> input.settings().sensitivity.getDeadzone(id)).orElse(0f); - - return ControllerUtils.deadzone(rawAxis, deadzone); - } - - @Override - public Set getAxes() { - return view.getAxes(); - } - - @Override - public float getAxisResting(Identifier axis) { - return view.getAxisResting(axis); - } - - @Override - public HatState getHatState(Identifier hat) { - return view.getHatState(hat); - } - - @Override - public Set getHats() { - return view.getHats(); - } -} diff --git a/src/main/java/dev/isxander/controlify/controller/input/HatState.java b/src/main/java/dev/isxander/controlify/controller/input/HatState.java deleted file mode 100644 index bd1068f69..000000000 --- a/src/main/java/dev/isxander/controlify/controller/input/HatState.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller.input; - -import com.mojang.serialization.Codec; -import dev.isxander.yacl3.api.NameableEnum; -import net.minecraft.network.chat.Component; -import net.minecraft.util.StringRepresentable; -import org.jetbrains.annotations.NotNull; - -public enum HatState implements NameableEnum, StringRepresentable { - CENTERED, - UP, - RIGHT, - DOWN, - LEFT, - RIGHT_UP, - RIGHT_DOWN, - LEFT_UP, - LEFT_DOWN; - - public static final Codec CODEC = StringRepresentable.fromEnum(HatState::values); - - public boolean isCentered() { - return this == CENTERED; - } - - public boolean isRight() { - return this == RIGHT || this == RIGHT_UP || this == RIGHT_DOWN; - } - - public boolean isUp() { - return this == UP || this == RIGHT_UP || this == LEFT_UP; - } - - public boolean isLeft() { - return this == LEFT || this == LEFT_UP || this == LEFT_DOWN; - } - - public boolean isDown() { - return this == DOWN || this == RIGHT_DOWN || this == LEFT_DOWN; - } - - @Override - public Component getDisplayName() { - return Component.translatable("controlify.hat_state." + this.name().toLowerCase()); - } - - @Override - public @NotNull String getSerializedName() { - return this.name(); - } -} diff --git a/src/main/java/dev/isxander/controlify/controller/input/InputComponent.java b/src/main/java/dev/isxander/controlify/controller/input/InputComponent.java deleted file mode 100644 index 61ae21e19..000000000 --- a/src/main/java/dev/isxander/controlify/controller/input/InputComponent.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller.input; - -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.bindings.ControlifyBindApiImpl; -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.config.settings.device.DeviceSettings; -import dev.isxander.controlify.config.settings.profile.InputSettings; -import dev.isxander.controlify.controller.*; -import dev.isxander.controlify.controller.impl.ECSComponentImpl; -import dev.isxander.controlify.controller.input.mapping.ControllerMapping; -import dev.isxander.controlify.utils.CUtil; -import net.minecraft.resources.Identifier; -import org.jetbrains.annotations.Nullable; - -import java.util.*; -import java.util.function.Function; -import java.util.stream.Collectors; - -public class InputComponent extends ECSComponentImpl { - public static final Identifier ID = CUtil.rl("input"); - - private final ControllerEntity controller; - private DeviceSettings deviceSettings; // Cached reference - - private ControllerState - stateNow = ControllerState.EMPTY, - stateThen = ControllerState.EMPTY; - private DeadzoneControllerStateView - deadzoneStateNow, - deadzoneStateThen; - - private final int buttonCount, axisCount, hatCount; - private final Map deadzoneAxes; - private final boolean definitelyGamepad; - - private final Map inputBindings; - - public InputComponent( - ControllerEntity controller, - int buttonCount, int axisCount, int hatCount, - boolean definitelyGamepad, - Set deadzoneAxes, - String mappingId - ) { - this.controller = controller; - this.buttonCount = buttonCount; - this.axisCount = axisCount; - this.hatCount = hatCount; - this.definitelyGamepad = definitelyGamepad; - this.deadzoneAxes = deadzoneAxes.stream() - .collect(Collectors.toMap( - DeadzoneGroup::name, - Function.identity(), - (x, y) -> y, - LinkedHashMap::new - )); - this.inputBindings = new LinkedHashMap<>(); - - this.updateDeadzoneView(); - } - - public ControllerStateView stateNow() { - return this.deadzoneStateNow; - } - public ControllerStateView stateThen() { - return this.deadzoneStateThen; - } - - public ControllerState rawStateNow() { - return this.stateNow; - } - - public ControllerState rawStateThen() { - return this.stateThen; - } - - public void pushState(ControllerState state) { - ControllerMapping mapping = getDeviceMapping(); - if (mapping != null) { - state = mapping.mapState(state); - } - - this.stateThen = this.stateNow; - this.stateNow = state; - this.updateDeadzoneView(); - - for (InputBinding binding : this.inputBindings.values()) { - binding.pushState(this.deadzoneStateNow); - } - } - - public @Nullable InputBinding getBinding(Identifier id) { - return this.inputBindings.get(id); - } - - public Collection getAllBindings() { - return this.inputBindings.values(); - } - - public void notifyGuiPressOutputsOfNavigate() { - for (InputBinding binding : this.inputBindings.values()) { - binding.guiPressed().onNavigate(); - } - } - - @Override - public void attach(ControllerEntity controller) { - super.attach(controller); - - // Cache device settings reference - this.deviceSettings = Controlify.instance().config().getSettings() - .getOrCreateDeviceSettings(controller.uid()); - - // Populate bindings - for (InputBinding binding : ControlifyBindApiImpl.INSTANCE.provideBindsForController(controller)) { - this.inputBindings.put(binding.id(), binding); - } - - // Note: No need to copy bound inputs from config here since InputBindingImpl.boundInput() - // now reads directly from the config (via inputComponent.settings().bindings.bindings) - } - - public int buttonCount() { - return this.buttonCount; - } - public int axisCount() { - return this.axisCount; - } - public int hatCount() { - return this.hatCount; - } - - public boolean isDefinitelyGamepad() { - return this.definitelyGamepad; - } - - public Map getDeadzoneGroups() { - ControllerMapping mapping = getDeviceMapping(); - if (mapping != null) { - return mapping.deadzones(); - } else { - return this.deadzoneAxes; - } - } - - private @Nullable ControllerMapping getDeviceMapping() { - // Use cached device settings to avoid repeated lookups - return deviceSettings.mapping; - } - - public ControllerEntity getController() { - return controller(); - } - - public InputSettings settings() { - return this.controller().settings().input; - } - - public InputSettings defaultSettings() { - return this.controller().defaultSettings().input; - } - - @Override - public Identifier id() { - return ID; - } - - private void updateDeadzoneView() { - this.deadzoneStateNow = new DeadzoneControllerStateView(this.stateNow, this); - this.deadzoneStateThen = new DeadzoneControllerStateView(this.stateThen, this); - } - - Optional getDeadzoneForAxis(Identifier axis) { - for (DeadzoneGroup group : this.getDeadzoneGroups().values()) { - if (group.axes().contains(axis)) { - return Optional.of(group.name()); - } - } - return Optional.empty(); - } -} diff --git a/src/main/java/dev/isxander/controlify/controller/input/ModifiableControllerState.java b/src/main/java/dev/isxander/controlify/controller/input/ModifiableControllerState.java deleted file mode 100644 index 2f7b7ee64..000000000 --- a/src/main/java/dev/isxander/controlify/controller/input/ModifiableControllerState.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller.input; - -import net.minecraft.resources.Identifier; - -public interface ModifiableControllerState extends ControllerState { - void setButton(Identifier button, boolean pressed); - - void setAxis(Identifier axis, float value); - - void setHat(Identifier hat, HatState state); -} diff --git a/src/main/java/dev/isxander/controlify/controller/input/mapping/ControllerMapping.java b/src/main/java/dev/isxander/controlify/controller/input/mapping/ControllerMapping.java index 4da4e14bf..f5ecff6cd 100644 --- a/src/main/java/dev/isxander/controlify/controller/input/mapping/ControllerMapping.java +++ b/src/main/java/dev/isxander/controlify/controller/input/mapping/ControllerMapping.java @@ -14,8 +14,6 @@ import net.minecraft.resources.Identifier; import java.util.*; -import java.util.function.Function; -import java.util.stream.Collectors; public record ControllerMapping( List mappings, @@ -44,14 +42,6 @@ public record ControllerMapping( } } - public Map createMappingEntryMap() { - return this.mappings.stream() - .collect(Collectors.toUnmodifiableMap( - MappingEntry::sourceInput, - Function.identity() - )); - } - public static final ControllerMapping NO_MAPPING = new Builder().build(); public static final ControllerMapping GAMEPAD_DEADZONES_ONLY_MAPPING = new Builder() .putDeadzoneGroups(GamepadInputs.DEADZONE_GROUPS) diff --git a/src/main/java/dev/isxander/controlify/controller/input/mapping/ControllerMappingStorage.java b/src/main/java/dev/isxander/controlify/controller/input/mapping/ControllerMappingStorage.java deleted file mode 100644 index 144aa49a7..000000000 --- a/src/main/java/dev/isxander/controlify/controller/input/mapping/ControllerMappingStorage.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller.input.mapping; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.serialization.DataResult; -import com.mojang.serialization.JsonOps; -import dev.isxander.controlify.utils.CUtil; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.minecraft.client.Minecraft; -import net.minecraft.server.packs.resources.Resource; -import net.minecraft.server.packs.resources.ResourceManager; -import org.jetbrains.annotations.Nullable; - -import java.io.BufferedReader; -import java.io.IOException; -import java.util.Map; - -public class ControllerMappingStorage { - private static final Map MAPPINGS = new Object2ObjectOpenHashMap<>(); - - public static @Nullable ControllerMapping get(String id) { - return MAPPINGS.computeIfAbsent(id, ControllerMappingStorage::resolve); - } - - private static @Nullable ControllerMapping resolve(String id) { - ResourceManager resourceManager = Minecraft.getInstance().getResourceManager(); - Resource resource = resourceManager - .getResource(CUtil.rl("mappings/" + id + ".json")) - .orElse(null); - if (resource == null) - return null; - - try (BufferedReader reader = resource.openAsReader()) { - JsonElement jsonElement = JsonParser.parseReader(reader); - DataResult result = ControllerMapping.CODEC.parse(JsonOps.INSTANCE, jsonElement); - return result.getOrThrow(); - } catch (IOException e) { - throw new IllegalStateException("Failed to load controller mapping!", e); - } - } -} diff --git a/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntry.java b/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntry.java index b834bbebd..2338302fe 100644 --- a/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntry.java +++ b/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntry.java @@ -7,13 +7,9 @@ package dev.isxander.controlify.controller.input.mapping; import com.mojang.serialization.Codec; -import dev.isxander.controlify.controller.input.ControllerState; -import dev.isxander.controlify.controller.input.HatState; -import dev.isxander.controlify.controller.input.ModifiableControllerState; import dev.isxander.controlify.input.physical.PhysicalInputSignal; import dev.isxander.controlify.utils.MthExt; import net.minecraft.resources.Identifier; -import org.jspecify.annotations.Nullable; public sealed interface MappingEntry { /// Transforms an input signal diff --git a/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntryCodecs.java b/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntryCodecs.java index 03dced81b..804633966 100644 --- a/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntryCodecs.java +++ b/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntryCodecs.java @@ -11,7 +11,6 @@ import com.mojang.serialization.DataResult; import com.mojang.serialization.DynamicOps; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.controller.input.HatState; import net.minecraft.resources.Identifier; final class MappingEntryCodecs { diff --git a/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntryTypeAdapter.java b/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntryTypeAdapter.java deleted file mode 100644 index 92f0f3a0e..000000000 --- a/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntryTypeAdapter.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller.input.mapping; - -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonParseException; - -import java.lang.reflect.Type; - -public class MappingEntryTypeAdapter implements JsonDeserializer { - @Override - public MappingEntry deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - InOutRecord inOut = context.deserialize(json, InOutRecord.class); - - Type type = switch (inOut.inputType()) { - case BUTTON -> switch (inOut.outputType()) { - case BUTTON -> MappingEntry.FromButton.ToButton.class; - case AXIS -> MappingEntry.FromButton.ToAxis.class; - case HAT -> MappingEntry.FromButton.ToHat.class; - case NOTHING -> throw new IllegalStateException(); - }; - case AXIS -> switch (inOut.outputType()) { - case BUTTON -> MappingEntry.FromAxis.ToButton.class; - case AXIS -> MappingEntry.FromAxis.ToAxis.class; - case HAT -> MappingEntry.FromAxis.ToHat.class; - case NOTHING -> throw new IllegalStateException(); - }; - case HAT -> switch (inOut.outputType()) { - case BUTTON -> MappingEntry.FromHat.ToButton.class; - case AXIS -> MappingEntry.FromHat.ToAxis.class; - case HAT -> MappingEntry.FromHat.ToHat.class; - case NOTHING -> throw new IllegalStateException(); - }; - case NOTHING -> switch (inOut.outputType()) { - case BUTTON -> MappingEntry.FromNothing.ToButton.class; - case AXIS -> MappingEntry.FromNothing.ToAxis.class; - case HAT -> MappingEntry.FromNothing.ToHat.class; - case NOTHING -> throw new IllegalStateException(); - }; - }; - - return context.deserialize(json, type); - } - - private record InOutRecord(MapType inputType, MapType outputType) { - } -} diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDL3GamepadDriver.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDL3GamepadDriver.java index 48dc387ff..f9b1f2f44 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDL3GamepadDriver.java +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDL3GamepadDriver.java @@ -9,7 +9,6 @@ import dev.isxander.controlify.controller.id.ControllerType; import dev.isxander.controlify.controller.touchpad.TouchpadComponent; import dev.isxander.controlify.controller.gyro.GyroComponent; -import dev.isxander.controlify.controller.input.GamepadInputs; import dev.isxander.controlify.controller.*; import dev.isxander.controlify.controller.touchpad.Touchpads; import dev.isxander.controlify.input.InputComponent; @@ -73,9 +72,8 @@ public void addComponents(ControllerEntity controller) { this.inputPipeline, this.supportedButtons, this.supportedAxes, - GamepadInputs.DEADZONE_GROUPS, - true, - this.supportedSensors + this.supportedSensors, + true ) ); @@ -114,10 +112,6 @@ public void setActive(boolean active) { @Override protected void onEvent(SDLControllerEvent event) { - // SDL may still have queued samples after the sensor has been disabled. - if (!this.active && event instanceof SDLControllerEvent.GamepadSensor) { - return; - } super.onEvent(event); switch (event) { diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDL3JoystickDriver.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDL3JoystickDriver.java index 8165911a3..4e77ffd9d 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDL3JoystickDriver.java +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDL3JoystickDriver.java @@ -54,8 +54,7 @@ public void addComponents(ControllerEntity controller) { this.supportedButtons, this.supportedAxes, Set.of(), - false, - Set.of() + false )); } diff --git a/src/main/java/dev/isxander/controlify/input/InputComponent.java b/src/main/java/dev/isxander/controlify/input/InputComponent.java index 8f2017480..6ec17e595 100644 --- a/src/main/java/dev/isxander/controlify/input/InputComponent.java +++ b/src/main/java/dev/isxander/controlify/input/InputComponent.java @@ -6,7 +6,7 @@ import dev.isxander.controlify.config.settings.profile.InputSettings; import dev.isxander.controlify.controller.ControllerEntity; import dev.isxander.controlify.controller.impl.ECSComponentImpl; -import dev.isxander.controlify.controller.input.DeadzoneGroup; +import dev.isxander.controlify.controller.input.mapping.ControllerMapping; import dev.isxander.controlify.input.action.ActionGraph; import dev.isxander.controlify.input.action.ActionImpl; import dev.isxander.controlify.input.action.ActionSpec; @@ -37,7 +37,6 @@ public class InputComponent extends ECSComponentImpl { private final Map actions; private final Set physicalButtons, physicalAxes; - private final Map deadzoneGroups; private final boolean isGamepad; private final Set supportedSensors; @@ -48,20 +47,16 @@ public InputComponent( InputPipeline inputPipeline, Set physicalButtons, Set physicalAxes, - Set deadzoneGroups, - boolean isGamepad, - Set supportedSensors + Set supportedSensors, + boolean isGamepad ) { this.controllerType = controllerType; this.inputPipeline = inputPipeline; this.physicalButtons = Set.copyOf(physicalButtons); this.physicalAxes = Set.copyOf(physicalAxes); - this.deadzoneGroups = deadzoneGroups.stream() - .flatMap(g -> g.axes().stream().map(a -> Map.entry(a, g))) - .collect(Util.toMap()); - this.isGamepad = isGamepad; this.supportedSensors = Set.copyOf(supportedSensors); + this.isGamepad = isGamepad; this.actionGraph = new ActionGraph(); this.actions = new LinkedHashMap<>(); @@ -74,10 +69,15 @@ public void attach(ControllerEntity controller) { this.deviceSettings = Controlify.instance().config().getSettings() .getOrCreateDeviceSettings(controller.uid()); - // we need to wait for settings to load before getting current binding - this.reloadActions(); + ControllerMapping controllerMapping = this.deviceSettings.mapping; + if (controllerMapping == null) { + controllerMapping = this.isGamepad + ? ControllerMapping.GAMEPAD_DEADZONES_ONLY_MAPPING + : ControllerMapping.NO_MAPPING; + } - this.actionGraphSub = this.inputPipeline.sourceLogicalSignals().subscribe(this.actionGraph); + this.inputPipeline.stageMapping().setControllerMapping(controllerMapping); + this.inputPipeline.stageDeadzone().setDeadzoneGroups(controllerMapping.deadzones().values()); this.inputPipeline.stageDeadzone().setDeadzoneSupplier(axis -> settings().sensitivity.getDeadzone((Identifier) axis)); this.inputPipeline.stageSensorProcessor().setProcessors(sensorType -> { if (sensorType.isGyroscope()) { @@ -89,6 +89,9 @@ public void attach(ControllerEntity controller) { return List.of(); } }); + + this.reloadActions(); + this.actionGraphSub = this.inputPipeline.sourceLogicalSignals().subscribe(this.actionGraph); } public @Nullable ActionImpl getAction(Identifier actionId) { diff --git a/src/main/java/dev/isxander/controlify/input/InputPipeline.java b/src/main/java/dev/isxander/controlify/input/InputPipeline.java index faf952ab7..252f1ed78 100644 --- a/src/main/java/dev/isxander/controlify/input/InputPipeline.java +++ b/src/main/java/dev/isxander/controlify/input/InputPipeline.java @@ -1,5 +1,6 @@ package dev.isxander.controlify.input; +import dev.isxander.controlify.controller.input.mapping.ControllerMapping; import dev.isxander.controlify.input.logical.LogicalSignal; import dev.isxander.controlify.input.logical.PhysicalToLogicalStage; import dev.isxander.controlify.input.logical.SensorAccumulatorStage; @@ -11,6 +12,7 @@ public class InputPipeline implements EventSink { private final EventTap tapRawPhysicalSignals; private final SensorProcessorStage stageSensorProcessor; + private final MappingStage stageMapping; private final DeadzoneStage stageDeadzone; private final GestureRecogniserStage stageGestureRecogniser; private final InputDomainExpansionStage stageInputDomainExpansion; @@ -25,6 +27,7 @@ public class InputPipeline implements EventSink { public InputPipeline() { this.tapRawPhysicalSignals = new EventTap<>(); this.stageSensorProcessor = new SensorProcessorStage(); + this.stageMapping = new MappingStage(ControllerMapping.NO_MAPPING); this.stageDeadzone = new DeadzoneStage(); this.stageGestureRecogniser = new GestureRecogniserStage(); this.stageInputDomainExpansion = new InputDomainExpansionStage(0.5f, 0.49f, 1f, 0f); @@ -35,6 +38,7 @@ public InputPipeline() { this.fullPipeline = this.tapRawPhysicalSignals .andThen(this.stageSensorProcessor) + .andThen(this.stageMapping) .andThen(this.stageDeadzone) .andThen(this.stageGestureRecogniser) .andThen(this.stageInputDomainExpansion) @@ -57,6 +61,10 @@ public SensorProcessorStage stageSensorProcessor() { return this.stageSensorProcessor; } + public MappingStage stageMapping() { + return this.stageMapping; + } + public DeadzoneStage stageDeadzone() { return this.stageDeadzone; } diff --git a/src/main/java/dev/isxander/controlify/input/physical/DeadzoneStage.java b/src/main/java/dev/isxander/controlify/input/physical/DeadzoneStage.java index 5349465ab..23bce96cb 100644 --- a/src/main/java/dev/isxander/controlify/input/physical/DeadzoneStage.java +++ b/src/main/java/dev/isxander/controlify/input/physical/DeadzoneStage.java @@ -8,7 +8,7 @@ import net.minecraft.resources.Identifier; import net.minecraft.util.Mth; -import java.util.HashSet; +import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -16,33 +16,24 @@ /// Applies radial deadzones to physical axes. public class DeadzoneStage implements UnaryEventStage { - private final Map groupByAxis; - private final Map stateByGroup; - + private Map stateByAxis; private Object2FloatFunction deadzoneByGroupName; public DeadzoneStage(Set deadzoneGroups) { - this.groupByAxis = deadzoneGroups.stream() - .flatMap(g -> g.axes().stream().map(a -> Map.entry(a, g))) - .collect(Collectors.toMap( - Map.Entry::getKey, - Map.Entry::getValue, - (_, _) -> { - throw new IllegalArgumentException("Axis belongs to multiple deadzone groups"); - }, - Object2ObjectOpenHashMap::new - )); - this.stateByGroup = new Object2ObjectArrayMap<>(); + this.setDeadzoneGroups(deadzoneGroups); this.deadzoneByGroupName = _ -> 0f; } + public DeadzoneStage() { + this(Set.of()); + } + @Override public void onEvent(PhysicalSignal event, EventSink downstream) { if (event instanceof PhysicalInputSignal.AxisMoved axisMoved) { - DeadzoneGroup deadzoneGroup = this.groupByAxis.get(axisMoved.input()); + RadialDeadzoneState state = this.stateByAxis.get(axisMoved.input()); - if (deadzoneGroup != null) { - RadialDeadzoneState state = this.stateByGroup.computeIfAbsent(deadzoneGroup, RadialDeadzoneState::new); + if (state != null) { state.onEvent(axisMoved, downstream); return; } @@ -51,6 +42,20 @@ public void onEvent(PhysicalSignal event, EventSink down downstream.accept(event); } + public void setDeadzoneGroups(Collection deadzoneGroups) { + this.stateByAxis = deadzoneGroups.stream() + .map(RadialDeadzoneState::new) + .flatMap(state -> state.deadzoneGroup.axes().stream().map(axis -> Map.entry(axis, state))) + .collect(Collectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue, + (_, _) -> { + throw new IllegalArgumentException("Axis belongs to multiple deadzone groups"); + }, + Object2ObjectOpenHashMap::new + )); + } + public void setDeadzoneSupplier(Object2FloatFunction deadzoneByGroupName) { this.deadzoneByGroupName = deadzoneByGroupName; } diff --git a/src/main/java/dev/isxander/controlify/input/physical/MappingStage.java b/src/main/java/dev/isxander/controlify/input/physical/MappingStage.java new file mode 100644 index 000000000..b929ec49e --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/physical/MappingStage.java @@ -0,0 +1,40 @@ +package dev.isxander.controlify.input.physical; + +import dev.isxander.controlify.controller.input.mapping.ControllerMapping; +import dev.isxander.controlify.controller.input.mapping.MappingEntry; +import dev.isxander.controlify.utils.event.EventSink; +import dev.isxander.controlify.utils.event.UnaryEventStage; +import net.minecraft.resources.Identifier; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class MappingStage implements UnaryEventStage { + private Map mappingByInput; + + public MappingStage(ControllerMapping mapping) { + this.setControllerMapping(mapping); + } + + @Override + public void onEvent(PhysicalSignal event, EventSink downstream) { + if (event instanceof PhysicalInputSignal inputSignal) { + MappingEntry mapping = this.mappingByInput.get(inputSignal.input()); + + if (mapping != null) { + downstream.accept(mapping.apply(inputSignal)); + return; + } + } + downstream.accept(event); + } + + public void setControllerMapping(ControllerMapping mapping) { + this.mappingByInput = mapping.mappings().stream() + .collect(Collectors.toUnmodifiableMap( + MappingEntry::sourceInput, + Function.identity() + )); + } +} From b7813fb2a176d8eb7c65fcb4d34ec141b74afacd Mon Sep 17 00:00:00 2001 From: isxander Date: Fri, 16 Oct 2026 17:18:24 +0100 Subject: [PATCH 10/44] implement data-driven controller mappings; bit of reorganisation --- .../tests/VirtualControllerInputTests.java | 2 +- .../dev/isxander/controlify/Controlify.java | 21 +- .../{input => }/action/Accumulator.java | 2 +- .../controlify/{input => }/action/Action.java | 4 +- .../{input => }/action/ActionAccessor.java | 4 +- .../action/ActionAccessorImpl.java | 4 +- .../{input => }/action/ActionContext.java | 2 +- .../{input => }/action/ActionGraph.java | 6 +- .../{input => }/action/ActionImpl.java | 8 +- .../{input => }/action/ActionSpec.java | 2 +- .../{input => }/action/ActionSpecBuilder.java | 2 +- .../action/ActionSpecBuilderImpl.java | 2 +- .../action/ActionSpecRegistry.java | 2 +- .../action/ActionSpecRegistryImpl.java | 2 +- .../{input => }/action/ActionState.java | 2 +- .../{input => }/action/Activator.java | 6 +- .../{input => }/action/Channel.java | 2 +- .../action/activator/ActivatorConfig.java | 6 +- .../activator/ActivatorConfigManager.java | 4 +- .../action/activator/ActivatorConfigType.java | 4 +- .../action/activator/CompoundActivator.java | 10 +- .../action/activator/ContinuousActivator.java | 11 +- .../activator/DoubleTapPulseActivator.java | 11 +- .../activator/GuiPressPulseActivator.java | 11 +- .../action/activator/HoldLatchActivator.java | 11 +- .../action/activator/HoldPulseActivator.java | 11 +- .../action/activator/NoopActivator.java | 23 ++ .../action/activator/PressPulseActivator.java | 11 +- .../activator/ReleasePulseActivator.java | 11 +- .../activator/RepeatLatchPulseActivator.java | 10 +- .../action/activator/TapPulseActivator.java | 11 +- .../activator/ToggleLatchActivator.java | 10 +- .../defaults/DefaultBindManager.java | 2 +- .../defaults/DefaultBindProvider.java | 4 +- .../defaults/LayeredDefaultBindProvider.java | 4 +- .../MapBackedDefaultBindProvider.java | 4 +- .../controlify/action/radial/RadialIcon.java | 102 +++++ .../radial}/RadialIconExtractor.java | 18 +- .../radial}/RadialIconManager.java | 20 +- .../api/bind/ControlifyBindApi.java | 83 ----- .../controlify/api/bind/InputBinding.java | 243 ------------ .../api/bind/InputBindingBuilder.java | 155 -------- .../api/bind/InputBindingSupplier.java | 65 ---- .../controlify/api/bind/RadialIcon.java | 74 ---- .../api/buttonguide/ButtonGuideApi.java | 11 +- .../bindings/ControlifyBindApiImpl.java | 126 ------- .../bindings/InputBindingBuilderImpl.java | 211 ----------- .../controlify/bindings/InputBindingImpl.java | 351 ------------------ .../bindings/InputBindingSupplierImpl.java | 27 -- .../controlify/bindings/RadialIcons.java | 32 -- .../controlify/bindings/StateAccess.java | 19 - .../controlify/bindings/input/AxisInput.java | 37 -- .../bindings/input/ButtonInput.java | 37 -- .../controlify/bindings/input/EmptyInput.java | 39 -- .../controlify/bindings/input/HatInput.java | 39 -- .../controlify/bindings/input/Input.java | 25 -- .../controlify/bindings/input/InputType.java | 64 ---- .../bindings/output/AnalogueOutput.java | 11 - .../bindings/output/BindingOutput.java | 10 - .../bindings/output/DigitalOutput.java | 15 - .../bindings/output/GuiPressOutput.java | 67 ---- .../bindings/output/JustPressedOutput.java | 26 -- .../bindings/output/JustReleasedOutput.java | 26 -- .../bindings/output/JustTappedOutput.java | 46 --- .../output/KeyMappingEmulationOutput.java | 56 --- .../bindings/output/SimpleAnalogueOutput.java | 28 -- .../bindings/output/SimpleDigitalOutput.java | 28 -- .../config/dto/device/DeviceConfig.java | 2 +- .../config/dto/profile/InputConfig.java | 2 +- .../settings/device/DeviceSettings.java | 2 +- .../settings/profile/InputSettings.java | 2 +- .../controller/ControllerEntity.java | 2 +- .../dualsense/TriggerEffectInstanceImpl.java | 2 +- .../controller/gyro/GyroComponent.java | 8 +- .../controller/id/ControllerType.java | 17 +- .../input/InputComponent.java | 37 +- .../ControllerCoordinator.java | 2 +- .../controllermanager/ControllerManager.java | 3 +- .../driver/sdl/SDL3GamepadDriver.java | 2 +- .../driver/sdl/SDL3JoystickDriver.java | 4 +- .../driver/sdl/SDLCommonDriver.java | 2 +- .../driver/sdl/SDLInputConversions.java | 2 +- .../driver/sdl/SDLToPhysicalStage.java | 8 +- .../driver/steamdeck/SteamDeckDriver.java | 2 +- .../controlify/gui/ButtonGuideRenderer.java | 24 +- .../controllers/Deadzone2DImageRenderer.java | 2 +- .../screen/ControllerConfigScreenFactory.java | 4 +- .../screen/ControllerMappingMakerScreen.java | 6 +- .../controlify/gui/screen/RadialItems.java | 4 +- .../gui/screen/RadialMenuEditScreen.java | 5 +- .../gui/screen/RadialMenuScreen.java | 4 +- .../input/{logical => }/LogicalInput.java | 4 +- .../input/action/activator/NoopActivator.java | 23 -- .../input/mapping/ControllerMapping.java | 4 +- .../mapping/ControllerMappingManager.java | 70 ++++ .../mapping}/DeadzoneGroup.java | 2 +- .../mapping}/GamepadInputs.java | 14 +- .../{physical => mapping}/GestureInputs.java | 2 +- .../input => input/mapping}/Inputs.java | 2 +- .../mapping}/JoystickInputs.java | 2 +- .../input/mapping/MapType.java | 2 +- .../input/mapping/MappingEntry.java | 4 +- .../input/mapping/MappingEntryCodecs.java | 2 +- .../{physical => pipeline}/DeadzoneStage.java | 7 +- .../GestureRecogniserStage.java | 7 +- .../InputDomainExpansionStage.java | 4 +- .../input/{ => pipeline}/InputPipeline.java | 10 +- .../{physical => pipeline}/MappingStage.java | 8 +- .../PhysicalToLogicalStage.java | 10 +- .../SensorAccumulatorStage.java | 7 +- .../SensorProcessorStage.java | 4 +- .../{logical => signal}/LogicalSignal.java | 4 +- .../PhysicalInputSignal.java | 2 +- .../PhysicalSensorSignal.java | 2 +- .../{physical => signal}/PhysicalSignal.java | 2 +- .../controlify/screenop/ScreenProcessor.java | 2 +- .../virtualmouse/VirtualMouseHandler.java | 2 +- 117 files changed, 458 insertions(+), 2225 deletions(-) rename src/main/java/dev/isxander/controlify/{input => }/action/Accumulator.java (81%) rename src/main/java/dev/isxander/controlify/{input => }/action/Action.java (81%) rename src/main/java/dev/isxander/controlify/{input => }/action/ActionAccessor.java (88%) rename src/main/java/dev/isxander/controlify/{input => }/action/ActionAccessorImpl.java (74%) rename src/main/java/dev/isxander/controlify/{input => }/action/ActionContext.java (89%) rename src/main/java/dev/isxander/controlify/{input => }/action/ActionGraph.java (95%) rename src/main/java/dev/isxander/controlify/{input => }/action/ActionImpl.java (88%) rename src/main/java/dev/isxander/controlify/{input => }/action/ActionSpec.java (96%) rename src/main/java/dev/isxander/controlify/{input => }/action/ActionSpecBuilder.java (92%) rename src/main/java/dev/isxander/controlify/{input => }/action/ActionSpecBuilderImpl.java (97%) rename src/main/java/dev/isxander/controlify/{input => }/action/ActionSpecRegistry.java (66%) rename src/main/java/dev/isxander/controlify/{input => }/action/ActionSpecRegistryImpl.java (95%) rename src/main/java/dev/isxander/controlify/{input => }/action/ActionState.java (95%) rename src/main/java/dev/isxander/controlify/{input => }/action/Activator.java (70%) rename src/main/java/dev/isxander/controlify/{input => }/action/Channel.java (93%) rename src/main/java/dev/isxander/controlify/{input => }/action/activator/ActivatorConfig.java (88%) rename src/main/java/dev/isxander/controlify/{input => }/action/activator/ActivatorConfigManager.java (96%) rename src/main/java/dev/isxander/controlify/{input => }/action/activator/ActivatorConfigType.java (96%) rename src/main/java/dev/isxander/controlify/{input => }/action/activator/CompoundActivator.java (82%) rename src/main/java/dev/isxander/controlify/{input => }/action/activator/ContinuousActivator.java (71%) rename src/main/java/dev/isxander/controlify/{input => }/action/activator/DoubleTapPulseActivator.java (73%) rename src/main/java/dev/isxander/controlify/{input => }/action/activator/GuiPressPulseActivator.java (73%) rename src/main/java/dev/isxander/controlify/{input => }/action/activator/HoldLatchActivator.java (76%) rename src/main/java/dev/isxander/controlify/{input => }/action/activator/HoldPulseActivator.java (72%) create mode 100644 src/main/java/dev/isxander/controlify/action/activator/NoopActivator.java rename src/main/java/dev/isxander/controlify/{input => }/action/activator/PressPulseActivator.java (72%) rename src/main/java/dev/isxander/controlify/{input => }/action/activator/ReleasePulseActivator.java (73%) rename src/main/java/dev/isxander/controlify/{input => }/action/activator/RepeatLatchPulseActivator.java (91%) rename src/main/java/dev/isxander/controlify/{input => }/action/activator/TapPulseActivator.java (72%) rename src/main/java/dev/isxander/controlify/{input => }/action/activator/ToggleLatchActivator.java (85%) rename src/main/java/dev/isxander/controlify/{bindings => action}/defaults/DefaultBindManager.java (99%) rename src/main/java/dev/isxander/controlify/{bindings => action}/defaults/DefaultBindProvider.java (76%) rename src/main/java/dev/isxander/controlify/{bindings => action}/defaults/LayeredDefaultBindProvider.java (92%) rename src/main/java/dev/isxander/controlify/{bindings => action}/defaults/MapBackedDefaultBindProvider.java (89%) create mode 100644 src/main/java/dev/isxander/controlify/action/radial/RadialIcon.java rename src/main/java/dev/isxander/controlify/{bindings => action/radial}/RadialIconExtractor.java (75%) rename src/main/java/dev/isxander/controlify/{bindings => action/radial}/RadialIconManager.java (79%) delete mode 100644 src/main/java/dev/isxander/controlify/api/bind/ControlifyBindApi.java delete mode 100644 src/main/java/dev/isxander/controlify/api/bind/InputBinding.java delete mode 100644 src/main/java/dev/isxander/controlify/api/bind/InputBindingBuilder.java delete mode 100644 src/main/java/dev/isxander/controlify/api/bind/InputBindingSupplier.java delete mode 100644 src/main/java/dev/isxander/controlify/api/bind/RadialIcon.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/ControlifyBindApiImpl.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/InputBindingBuilderImpl.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/InputBindingImpl.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/InputBindingSupplierImpl.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/RadialIcons.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/StateAccess.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/input/AxisInput.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/input/ButtonInput.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/input/EmptyInput.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/input/HatInput.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/input/Input.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/input/InputType.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/output/AnalogueOutput.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/output/BindingOutput.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/output/DigitalOutput.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/output/GuiPressOutput.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/output/JustPressedOutput.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/output/JustReleasedOutput.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/output/JustTappedOutput.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/output/KeyMappingEmulationOutput.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/output/SimpleAnalogueOutput.java delete mode 100644 src/main/java/dev/isxander/controlify/bindings/output/SimpleDigitalOutput.java rename src/main/java/dev/isxander/controlify/{ => controller}/input/InputComponent.java (82%) rename src/main/java/dev/isxander/controlify/input/{logical => }/LogicalInput.java (94%) delete mode 100644 src/main/java/dev/isxander/controlify/input/action/activator/NoopActivator.java rename src/main/java/dev/isxander/controlify/{controller => }/input/mapping/ControllerMapping.java (93%) create mode 100644 src/main/java/dev/isxander/controlify/input/mapping/ControllerMappingManager.java rename src/main/java/dev/isxander/controlify/{controller/input => input/mapping}/DeadzoneGroup.java (92%) rename src/main/java/dev/isxander/controlify/{controller/input => input/mapping}/GamepadInputs.java (87%) rename src/main/java/dev/isxander/controlify/input/{physical => mapping}/GestureInputs.java (82%) rename src/main/java/dev/isxander/controlify/{controller/input => input/mapping}/Inputs.java (94%) rename src/main/java/dev/isxander/controlify/{controller/input => input/mapping}/JoystickInputs.java (95%) rename src/main/java/dev/isxander/controlify/{controller => }/input/mapping/MapType.java (89%) rename src/main/java/dev/isxander/controlify/{controller => }/input/mapping/MappingEntry.java (96%) rename src/main/java/dev/isxander/controlify/{controller => }/input/mapping/MappingEntryCodecs.java (98%) rename src/main/java/dev/isxander/controlify/input/{physical => pipeline}/DeadzoneStage.java (93%) rename src/main/java/dev/isxander/controlify/input/{physical => pipeline}/GestureRecogniserStage.java (63%) rename src/main/java/dev/isxander/controlify/input/{physical => pipeline}/InputDomainExpansionStage.java (90%) rename src/main/java/dev/isxander/controlify/input/{ => pipeline}/InputPipeline.java (89%) rename src/main/java/dev/isxander/controlify/input/{physical => pipeline}/MappingStage.java (78%) rename src/main/java/dev/isxander/controlify/input/{logical => pipeline}/PhysicalToLogicalStage.java (95%) rename src/main/java/dev/isxander/controlify/input/{logical => pipeline}/SensorAccumulatorStage.java (92%) rename src/main/java/dev/isxander/controlify/input/{physical => pipeline}/SensorProcessorStage.java (95%) rename src/main/java/dev/isxander/controlify/input/{logical => signal}/LogicalSignal.java (91%) rename src/main/java/dev/isxander/controlify/input/{physical => signal}/PhysicalInputSignal.java (94%) rename src/main/java/dev/isxander/controlify/input/{physical => signal}/PhysicalSensorSignal.java (94%) rename src/main/java/dev/isxander/controlify/input/{physical => signal}/PhysicalSignal.java (86%) 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 044a1655e..d66c54502 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/VirtualControllerInputTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/VirtualControllerInputTests.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.gametest.tests; -import dev.isxander.controlify.controller.input.GamepadInputs; +import dev.isxander.controlify.input.mapping.GamepadInputs; import dev.isxander.controlify.controller.input.InputComponent; import dev.isxander.controlify.gametest.framework.CTestUtil; import dev.isxander.controlify.gametest.framework.controller.ControlifyGameTestContext; diff --git a/src/main/java/dev/isxander/controlify/Controlify.java b/src/main/java/dev/isxander/controlify/Controlify.java index 662f62848..0be77c533 100644 --- a/src/main/java/dev/isxander/controlify/Controlify.java +++ b/src/main/java/dev/isxander/controlify/Controlify.java @@ -12,11 +12,10 @@ import dev.isxander.controlify.api.contextual.ContextualDomainRegistry; import dev.isxander.controlify.api.entrypoint.InitContext; import dev.isxander.controlify.api.entrypoint.PreInitContext; -import dev.isxander.controlify.bindings.BindContext; import dev.isxander.controlify.bindings.ControlifyBindApiImpl; import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.bindings.RadialIconManager; -import dev.isxander.controlify.bindings.defaults.DefaultBindManager; +import dev.isxander.controlify.action.radial.RadialIconManager; +import dev.isxander.controlify.action.defaults.DefaultBindManager; import dev.isxander.controlify.compatibility.ControlifyCompat; import dev.isxander.controlify.config.ConfigManager; import dev.isxander.controlify.config.dto.profile.defaults.DefaultConfigManager; @@ -25,6 +24,7 @@ import dev.isxander.controlify.controller.*; import dev.isxander.controlify.controller.dualsense.TriggerEffectManager; import dev.isxander.controlify.controller.id.ControllerTypeManager; +import dev.isxander.controlify.input.mapping.ControllerMappingManager; import dev.isxander.controlify.controller.rumble.RumbleComponent; import dev.isxander.controlify.controllermanager.ControllerCoordinator; import dev.isxander.controlify.controllermanager.ControllerManager; @@ -34,8 +34,8 @@ import dev.isxander.controlify.gui.screen.*; import dev.isxander.controlify.debug.DebugProperties; import dev.isxander.controlify.ingame.ControllerPlayerMovement; -import dev.isxander.controlify.input.InputComponent; -import dev.isxander.controlify.input.action.activator.ActivatorConfigManager; +import dev.isxander.controlify.controller.input.InputComponent; +import dev.isxander.controlify.action.activator.ActivatorConfigManager; import dev.isxander.controlify.platform.client.PlatformClientUtil; import dev.isxander.controlify.platform.main.PlatformMainUtil; import dev.isxander.controlify.platform.network.SidedNetworkApi; @@ -89,6 +89,7 @@ public class Controlify implements ControlifyApi { private KeyboardLayoutManager keyboardLayoutManager; private RadialIconManager radialIconManager; private ActivatorConfigManager activatorConfigManager; + private ControllerMappingManager controllerMappingManager; private TriggerEffectManager triggerEffectManager; @@ -134,6 +135,7 @@ public void preInitialiseControlify() { this.keyboardLayoutManager = new KeyboardLayoutManager(); this.radialIconManager = new RadialIconManager(); this.activatorConfigManager = new ActivatorConfigManager(); + this.controllerMappingManager = new ControllerMappingManager(); PlatformClientUtil.registerAssetReloadListener(this.inputFontMapper); PlatformClientUtil.registerAssetReloadListener(this.defaultBindManager); @@ -142,6 +144,7 @@ public void preInitialiseControlify() { PlatformClientUtil.registerAssetReloadListener(this.keyboardLayoutManager); PlatformClientUtil.registerAssetReloadListener(this.radialIconManager); PlatformClientUtil.registerAssetReloadListener(this.activatorConfigManager); + PlatformClientUtil.registerAssetReloadListener(this.controllerMappingManager); PlatformClientUtil.registerAssetReloadListener(RuleSetManager.GUIDE_RULES); PlatformClientUtil.registerAssetReloadListener(RuleSetManager.TRIGGER_EFFECT_RULES); ContextualDomains.touch(); @@ -622,12 +625,12 @@ public ActivatorConfigManager activatorConfigManager() { return this.activatorConfigManager; } - public TriggerEffectManager triggerEffectManager() { - return this.triggerEffectManager; + public ControllerMappingManager controllerMappingManager() { + return this.controllerMappingManager; } - public Set thisTickBindContexts() { - return this.thisTickContexts; + public TriggerEffectManager triggerEffectManager() { + return this.triggerEffectManager; } public void notifyNewServer(ServerData data) { diff --git a/src/main/java/dev/isxander/controlify/input/action/Accumulator.java b/src/main/java/dev/isxander/controlify/action/Accumulator.java similarity index 81% rename from src/main/java/dev/isxander/controlify/input/action/Accumulator.java rename to src/main/java/dev/isxander/controlify/action/Accumulator.java index 00470df98..fb9e37d17 100644 --- a/src/main/java/dev/isxander/controlify/input/action/Accumulator.java +++ b/src/main/java/dev/isxander/controlify/action/Accumulator.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.input.action; +package dev.isxander.controlify.action; /// Accepts action state. public interface Accumulator { diff --git a/src/main/java/dev/isxander/controlify/input/action/Action.java b/src/main/java/dev/isxander/controlify/action/Action.java similarity index 81% rename from src/main/java/dev/isxander/controlify/input/action/Action.java rename to src/main/java/dev/isxander/controlify/action/Action.java index fb6ec58af..39de6c9fe 100644 --- a/src/main/java/dev/isxander/controlify/input/action/Action.java +++ b/src/main/java/dev/isxander/controlify/action/Action.java @@ -1,6 +1,6 @@ -package dev.isxander.controlify.input.action; +package dev.isxander.controlify.action; -import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.LogicalInput; import org.jspecify.annotations.Nullable; /// An action is bound to a logical input and has one or more activators diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionAccessor.java b/src/main/java/dev/isxander/controlify/action/ActionAccessor.java similarity index 88% rename from src/main/java/dev/isxander/controlify/input/action/ActionAccessor.java rename to src/main/java/dev/isxander/controlify/action/ActionAccessor.java index 366a6575c..7fd0ed51a 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionAccessor.java +++ b/src/main/java/dev/isxander/controlify/action/ActionAccessor.java @@ -1,7 +1,7 @@ -package dev.isxander.controlify.input.action; +package dev.isxander.controlify.action; import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.input.InputComponent; +import dev.isxander.controlify.controller.input.InputComponent; import net.minecraft.resources.Identifier; import org.jspecify.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionAccessorImpl.java b/src/main/java/dev/isxander/controlify/action/ActionAccessorImpl.java similarity index 74% rename from src/main/java/dev/isxander/controlify/input/action/ActionAccessorImpl.java rename to src/main/java/dev/isxander/controlify/action/ActionAccessorImpl.java index db1419c9a..6ee6f2a3f 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionAccessorImpl.java +++ b/src/main/java/dev/isxander/controlify/action/ActionAccessorImpl.java @@ -1,6 +1,6 @@ -package dev.isxander.controlify.input.action; +package dev.isxander.controlify.action; -import dev.isxander.controlify.input.InputComponent; +import dev.isxander.controlify.controller.input.InputComponent; import net.minecraft.resources.Identifier; import org.jspecify.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionContext.java b/src/main/java/dev/isxander/controlify/action/ActionContext.java similarity index 89% rename from src/main/java/dev/isxander/controlify/input/action/ActionContext.java rename to src/main/java/dev/isxander/controlify/action/ActionContext.java index 6a93fc9d9..ccaaf4763 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionContext.java +++ b/src/main/java/dev/isxander/controlify/action/ActionContext.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.input.action; +package dev.isxander.controlify.action; import net.minecraft.client.Minecraft; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionGraph.java b/src/main/java/dev/isxander/controlify/action/ActionGraph.java similarity index 95% rename from src/main/java/dev/isxander/controlify/input/action/ActionGraph.java rename to src/main/java/dev/isxander/controlify/action/ActionGraph.java index 4c76b1161..71be9fc45 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionGraph.java +++ b/src/main/java/dev/isxander/controlify/action/ActionGraph.java @@ -1,7 +1,7 @@ -package dev.isxander.controlify.input.action; +package dev.isxander.controlify.action; -import dev.isxander.controlify.input.logical.LogicalInput; -import dev.isxander.controlify.input.logical.LogicalSignal; +import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.input.signal.LogicalSignal; import dev.isxander.controlify.utils.event.EventSink; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionImpl.java b/src/main/java/dev/isxander/controlify/action/ActionImpl.java similarity index 88% rename from src/main/java/dev/isxander/controlify/input/action/ActionImpl.java rename to src/main/java/dev/isxander/controlify/action/ActionImpl.java index 317369bc1..6e2847cf3 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionImpl.java +++ b/src/main/java/dev/isxander/controlify/action/ActionImpl.java @@ -1,8 +1,8 @@ -package dev.isxander.controlify.input.action; +package dev.isxander.controlify.action; -import dev.isxander.controlify.input.action.activator.ActivatorConfig; -import dev.isxander.controlify.input.action.activator.NoopActivator; -import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.action.activator.ActivatorConfig; +import dev.isxander.controlify.action.activator.NoopActivator; +import dev.isxander.controlify.input.LogicalInput; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionSpec.java b/src/main/java/dev/isxander/controlify/action/ActionSpec.java similarity index 96% rename from src/main/java/dev/isxander/controlify/input/action/ActionSpec.java rename to src/main/java/dev/isxander/controlify/action/ActionSpec.java index 44b31a138..c39361cb5 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionSpec.java +++ b/src/main/java/dev/isxander/controlify/action/ActionSpec.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.input.action; +package dev.isxander.controlify.action; import dev.isxander.controlify.controller.ControllerEntity; import net.minecraft.network.chat.Component; diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionSpecBuilder.java b/src/main/java/dev/isxander/controlify/action/ActionSpecBuilder.java similarity index 92% rename from src/main/java/dev/isxander/controlify/input/action/ActionSpecBuilder.java rename to src/main/java/dev/isxander/controlify/action/ActionSpecBuilder.java index 631bf5290..d30c52e96 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionSpecBuilder.java +++ b/src/main/java/dev/isxander/controlify/action/ActionSpecBuilder.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.input.action; +package dev.isxander.controlify.action; import dev.isxander.controlify.controller.ControllerEntity; import net.minecraft.network.chat.Component; diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionSpecBuilderImpl.java b/src/main/java/dev/isxander/controlify/action/ActionSpecBuilderImpl.java similarity index 97% rename from src/main/java/dev/isxander/controlify/input/action/ActionSpecBuilderImpl.java rename to src/main/java/dev/isxander/controlify/action/ActionSpecBuilderImpl.java index e653e1ecb..c6a3d10d7 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionSpecBuilderImpl.java +++ b/src/main/java/dev/isxander/controlify/action/ActionSpecBuilderImpl.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.input.action; +package dev.isxander.controlify.action; import dev.isxander.controlify.controller.ControllerEntity; import net.minecraft.network.chat.Component; diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionSpecRegistry.java b/src/main/java/dev/isxander/controlify/action/ActionSpecRegistry.java similarity index 66% rename from src/main/java/dev/isxander/controlify/input/action/ActionSpecRegistry.java rename to src/main/java/dev/isxander/controlify/action/ActionSpecRegistry.java index 26101ca60..1fc2a2ca5 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionSpecRegistry.java +++ b/src/main/java/dev/isxander/controlify/action/ActionSpecRegistry.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.input.action; +package dev.isxander.controlify.action; public interface ActionSpecRegistry { ActionAccessor register(ActionSpec actionSpec); diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionSpecRegistryImpl.java b/src/main/java/dev/isxander/controlify/action/ActionSpecRegistryImpl.java similarity index 95% rename from src/main/java/dev/isxander/controlify/input/action/ActionSpecRegistryImpl.java rename to src/main/java/dev/isxander/controlify/action/ActionSpecRegistryImpl.java index ec5104397..ae391579f 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionSpecRegistryImpl.java +++ b/src/main/java/dev/isxander/controlify/action/ActionSpecRegistryImpl.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.input.action; +package dev.isxander.controlify.action; import java.util.ArrayList; import java.util.Collections; diff --git a/src/main/java/dev/isxander/controlify/input/action/ActionState.java b/src/main/java/dev/isxander/controlify/action/ActionState.java similarity index 95% rename from src/main/java/dev/isxander/controlify/input/action/ActionState.java rename to src/main/java/dev/isxander/controlify/action/ActionState.java index 159457849..17838159e 100644 --- a/src/main/java/dev/isxander/controlify/input/action/ActionState.java +++ b/src/main/java/dev/isxander/controlify/action/ActionState.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.input.action; +package dev.isxander.controlify.action; public final class ActionState implements Accumulator, Channel.Continuous, Channel.Latch, Channel.Pulse { private int pulses = 0; diff --git a/src/main/java/dev/isxander/controlify/input/action/Activator.java b/src/main/java/dev/isxander/controlify/action/Activator.java similarity index 70% rename from src/main/java/dev/isxander/controlify/input/action/Activator.java rename to src/main/java/dev/isxander/controlify/action/Activator.java index 7e5b0db02..4e87f8f5a 100644 --- a/src/main/java/dev/isxander/controlify/input/action/Activator.java +++ b/src/main/java/dev/isxander/controlify/action/Activator.java @@ -1,7 +1,7 @@ -package dev.isxander.controlify.input.action; +package dev.isxander.controlify.action; -import dev.isxander.controlify.input.logical.LogicalInput; -import dev.isxander.controlify.input.logical.LogicalSignal; +import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.input.signal.LogicalSignal; /// Processes signals and activates an {@link Action}. public interface Activator { diff --git a/src/main/java/dev/isxander/controlify/input/action/Channel.java b/src/main/java/dev/isxander/controlify/action/Channel.java similarity index 93% rename from src/main/java/dev/isxander/controlify/input/action/Channel.java rename to src/main/java/dev/isxander/controlify/action/Channel.java index 064d29c4c..dfa89cfbc 100644 --- a/src/main/java/dev/isxander/controlify/input/action/Channel.java +++ b/src/main/java/dev/isxander/controlify/action/Channel.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.input.action; +package dev.isxander.controlify.action; /// A channel is a way of interpreting the output state of a given {@link Action}. /// An {@link Action action's} bindings sends output to any of these channels. diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfig.java b/src/main/java/dev/isxander/controlify/action/activator/ActivatorConfig.java similarity index 88% rename from src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfig.java rename to src/main/java/dev/isxander/controlify/action/activator/ActivatorConfig.java index 189aa34a8..908c6556c 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfig.java +++ b/src/main/java/dev/isxander/controlify/action/activator/ActivatorConfig.java @@ -1,9 +1,9 @@ -package dev.isxander.controlify.input.action.activator; +package dev.isxander.controlify.action.activator; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; -import dev.isxander.controlify.input.action.Activator; -import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.action.Activator; +import dev.isxander.controlify.input.LogicalInput; import net.minecraft.util.StringRepresentable; /// An immutable structure typically containing the configuration diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfigManager.java b/src/main/java/dev/isxander/controlify/action/activator/ActivatorConfigManager.java similarity index 96% rename from src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfigManager.java rename to src/main/java/dev/isxander/controlify/action/activator/ActivatorConfigManager.java index d19a8dee2..34aeecf18 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfigManager.java +++ b/src/main/java/dev/isxander/controlify/action/activator/ActivatorConfigManager.java @@ -4,14 +4,14 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.input.action.activator; +package dev.isxander.controlify.action.activator; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.input.InputComponent; +import dev.isxander.controlify.controller.input.InputComponent; import dev.isxander.controlify.platform.client.resource.SimpleControlifyReloadListener; import dev.isxander.controlify.utils.CUtil; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfigType.java b/src/main/java/dev/isxander/controlify/action/activator/ActivatorConfigType.java similarity index 96% rename from src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfigType.java rename to src/main/java/dev/isxander/controlify/action/activator/ActivatorConfigType.java index 4f2bd4809..5c8f2cb97 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/ActivatorConfigType.java +++ b/src/main/java/dev/isxander/controlify/action/activator/ActivatorConfigType.java @@ -1,7 +1,7 @@ -package dev.isxander.controlify.input.action.activator; +package dev.isxander.controlify.action.activator; import com.mojang.serialization.MapCodec; -import dev.isxander.controlify.input.action.Activator; +import dev.isxander.controlify.action.Activator; import net.minecraft.util.StringRepresentable; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/CompoundActivator.java b/src/main/java/dev/isxander/controlify/action/activator/CompoundActivator.java similarity index 82% rename from src/main/java/dev/isxander/controlify/input/action/activator/CompoundActivator.java rename to src/main/java/dev/isxander/controlify/action/activator/CompoundActivator.java index 916a1beb0..b39c7b18e 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/CompoundActivator.java +++ b/src/main/java/dev/isxander/controlify/action/activator/CompoundActivator.java @@ -1,11 +1,11 @@ -package dev.isxander.controlify.input.action.activator; +package dev.isxander.controlify.action.activator; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.input.action.Accumulator; -import dev.isxander.controlify.input.action.Activator; -import dev.isxander.controlify.input.logical.LogicalInput; -import dev.isxander.controlify.input.logical.LogicalSignal; +import dev.isxander.controlify.action.Accumulator; +import dev.isxander.controlify.action.Activator; +import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.input.signal.LogicalSignal; import java.util.List; import java.util.stream.Collectors; diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ContinuousActivator.java b/src/main/java/dev/isxander/controlify/action/activator/ContinuousActivator.java similarity index 71% rename from src/main/java/dev/isxander/controlify/input/action/activator/ContinuousActivator.java rename to src/main/java/dev/isxander/controlify/action/activator/ContinuousActivator.java index 503e71b17..552d00840 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/ContinuousActivator.java +++ b/src/main/java/dev/isxander/controlify/action/activator/ContinuousActivator.java @@ -1,10 +1,9 @@ -package dev.isxander.controlify.input.action.activator; +package dev.isxander.controlify.action.activator; -import com.mojang.serialization.MapCodec; -import dev.isxander.controlify.input.action.Accumulator; -import dev.isxander.controlify.input.action.Activator; -import dev.isxander.controlify.input.logical.LogicalInput; -import dev.isxander.controlify.input.logical.LogicalSignal; +import dev.isxander.controlify.action.Accumulator; +import dev.isxander.controlify.action.Activator; +import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.input.signal.LogicalSignal; public record ContinuousActivator(LogicalInput boundInput) implements Activator { @Override diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/DoubleTapPulseActivator.java b/src/main/java/dev/isxander/controlify/action/activator/DoubleTapPulseActivator.java similarity index 73% rename from src/main/java/dev/isxander/controlify/input/action/activator/DoubleTapPulseActivator.java rename to src/main/java/dev/isxander/controlify/action/activator/DoubleTapPulseActivator.java index 6ca8373c0..dfd6a6c84 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/DoubleTapPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/action/activator/DoubleTapPulseActivator.java @@ -1,10 +1,9 @@ -package dev.isxander.controlify.input.action.activator; +package dev.isxander.controlify.action.activator; -import com.mojang.serialization.MapCodec; -import dev.isxander.controlify.input.action.Accumulator; -import dev.isxander.controlify.input.action.Activator; -import dev.isxander.controlify.input.logical.LogicalInput; -import dev.isxander.controlify.input.logical.LogicalSignal; +import dev.isxander.controlify.action.Accumulator; +import dev.isxander.controlify.action.Activator; +import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.input.signal.LogicalSignal; /// Produces a pulse when the input is double tapped. public record DoubleTapPulseActivator(LogicalInput boundInput) implements Activator { diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/GuiPressPulseActivator.java b/src/main/java/dev/isxander/controlify/action/activator/GuiPressPulseActivator.java similarity index 73% rename from src/main/java/dev/isxander/controlify/input/action/activator/GuiPressPulseActivator.java rename to src/main/java/dev/isxander/controlify/action/activator/GuiPressPulseActivator.java index 2760ea5e4..d66ec387e 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/GuiPressPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/action/activator/GuiPressPulseActivator.java @@ -1,10 +1,9 @@ -package dev.isxander.controlify.input.action.activator; +package dev.isxander.controlify.action.activator; -import com.mojang.serialization.MapCodec; -import dev.isxander.controlify.input.action.Accumulator; -import dev.isxander.controlify.input.action.Activator; -import dev.isxander.controlify.input.logical.LogicalInput; -import dev.isxander.controlify.input.logical.LogicalSignal; +import dev.isxander.controlify.action.Accumulator; +import dev.isxander.controlify.action.Activator; +import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.input.signal.LogicalSignal; /// Produces a pulse on release when no GUI navigation consumed the press. public record GuiPressPulseActivator(LogicalInput boundInput) implements Activator { diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/HoldLatchActivator.java b/src/main/java/dev/isxander/controlify/action/activator/HoldLatchActivator.java similarity index 76% rename from src/main/java/dev/isxander/controlify/input/action/activator/HoldLatchActivator.java rename to src/main/java/dev/isxander/controlify/action/activator/HoldLatchActivator.java index 3a0954e38..35eb00978 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/HoldLatchActivator.java +++ b/src/main/java/dev/isxander/controlify/action/activator/HoldLatchActivator.java @@ -1,10 +1,9 @@ -package dev.isxander.controlify.input.action.activator; +package dev.isxander.controlify.action.activator; -import com.mojang.serialization.MapCodec; -import dev.isxander.controlify.input.action.Accumulator; -import dev.isxander.controlify.input.action.Activator; -import dev.isxander.controlify.input.logical.LogicalInput; -import dev.isxander.controlify.input.logical.LogicalSignal; +import dev.isxander.controlify.action.Accumulator; +import dev.isxander.controlify.action.Activator; +import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.input.signal.LogicalSignal; /// Keeps the latch active from button press until release, without a hold delay. public record HoldLatchActivator(LogicalInput boundInput) implements Activator { diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/HoldPulseActivator.java b/src/main/java/dev/isxander/controlify/action/activator/HoldPulseActivator.java similarity index 72% rename from src/main/java/dev/isxander/controlify/input/action/activator/HoldPulseActivator.java rename to src/main/java/dev/isxander/controlify/action/activator/HoldPulseActivator.java index abeb8a984..004e83593 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/HoldPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/action/activator/HoldPulseActivator.java @@ -1,10 +1,9 @@ -package dev.isxander.controlify.input.action.activator; +package dev.isxander.controlify.action.activator; -import com.mojang.serialization.MapCodec; -import dev.isxander.controlify.input.action.Accumulator; -import dev.isxander.controlify.input.action.Activator; -import dev.isxander.controlify.input.logical.LogicalInput; -import dev.isxander.controlify.input.logical.LogicalSignal; +import dev.isxander.controlify.action.Accumulator; +import dev.isxander.controlify.action.Activator; +import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.input.signal.LogicalSignal; /// Produces a pulse once the input reaches the hold duration. public record HoldPulseActivator(LogicalInput boundInput) implements Activator { diff --git a/src/main/java/dev/isxander/controlify/action/activator/NoopActivator.java b/src/main/java/dev/isxander/controlify/action/activator/NoopActivator.java new file mode 100644 index 000000000..d4d4e3632 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/action/activator/NoopActivator.java @@ -0,0 +1,23 @@ +package dev.isxander.controlify.action.activator; + +import dev.isxander.controlify.action.Accumulator; +import dev.isxander.controlify.action.Activator; +import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.input.signal.LogicalSignal; + +public class NoopActivator implements Activator { + @Override + public void onSignal(LogicalSignal signal, Accumulator acc) { + + } + + @Override + public LogicalInput boundInput() { + return null; + } + + @Override + public String describe() { + return "Noop"; + } +} diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/PressPulseActivator.java b/src/main/java/dev/isxander/controlify/action/activator/PressPulseActivator.java similarity index 72% rename from src/main/java/dev/isxander/controlify/input/action/activator/PressPulseActivator.java rename to src/main/java/dev/isxander/controlify/action/activator/PressPulseActivator.java index 4f7d6ad03..812a5616b 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/PressPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/action/activator/PressPulseActivator.java @@ -1,10 +1,9 @@ -package dev.isxander.controlify.input.action.activator; +package dev.isxander.controlify.action.activator; -import com.mojang.serialization.MapCodec; -import dev.isxander.controlify.input.action.Accumulator; -import dev.isxander.controlify.input.action.Activator; -import dev.isxander.controlify.input.logical.LogicalInput; -import dev.isxander.controlify.input.logical.LogicalSignal; +import dev.isxander.controlify.action.Accumulator; +import dev.isxander.controlify.action.Activator; +import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.input.signal.LogicalSignal; /// Produces a pulse when the input is pressed. public record PressPulseActivator(LogicalInput boundInput) implements Activator { diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ReleasePulseActivator.java b/src/main/java/dev/isxander/controlify/action/activator/ReleasePulseActivator.java similarity index 73% rename from src/main/java/dev/isxander/controlify/input/action/activator/ReleasePulseActivator.java rename to src/main/java/dev/isxander/controlify/action/activator/ReleasePulseActivator.java index 9e065ab52..c74e9bae4 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/ReleasePulseActivator.java +++ b/src/main/java/dev/isxander/controlify/action/activator/ReleasePulseActivator.java @@ -1,10 +1,9 @@ -package dev.isxander.controlify.input.action.activator; +package dev.isxander.controlify.action.activator; -import com.mojang.serialization.MapCodec; -import dev.isxander.controlify.input.action.Accumulator; -import dev.isxander.controlify.input.action.Activator; -import dev.isxander.controlify.input.logical.LogicalInput; -import dev.isxander.controlify.input.logical.LogicalSignal; +import dev.isxander.controlify.action.Accumulator; +import dev.isxander.controlify.action.Activator; +import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.input.signal.LogicalSignal; /// Produces a pulse when the input is released. public record ReleasePulseActivator(LogicalInput boundInput) implements Activator { diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/RepeatLatchPulseActivator.java b/src/main/java/dev/isxander/controlify/action/activator/RepeatLatchPulseActivator.java similarity index 91% rename from src/main/java/dev/isxander/controlify/input/action/activator/RepeatLatchPulseActivator.java rename to src/main/java/dev/isxander/controlify/action/activator/RepeatLatchPulseActivator.java index 35a6523c5..b87ab051e 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/RepeatLatchPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/action/activator/RepeatLatchPulseActivator.java @@ -1,12 +1,12 @@ -package dev.isxander.controlify.input.action.activator; +package dev.isxander.controlify.action.activator; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.input.action.Accumulator; -import dev.isxander.controlify.input.action.Activator; -import dev.isxander.controlify.input.logical.LogicalInput; -import dev.isxander.controlify.input.logical.LogicalSignal; +import dev.isxander.controlify.action.Accumulator; +import dev.isxander.controlify.action.Activator; +import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.input.signal.LogicalSignal; /// Accepts another latch binding, and sends repeat pulses out while the latch is active. public class RepeatLatchPulseActivator implements Activator { diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/TapPulseActivator.java b/src/main/java/dev/isxander/controlify/action/activator/TapPulseActivator.java similarity index 72% rename from src/main/java/dev/isxander/controlify/input/action/activator/TapPulseActivator.java rename to src/main/java/dev/isxander/controlify/action/activator/TapPulseActivator.java index ba7c4d377..fc8f5dd63 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/TapPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/action/activator/TapPulseActivator.java @@ -1,10 +1,9 @@ -package dev.isxander.controlify.input.action.activator; +package dev.isxander.controlify.action.activator; -import com.mojang.serialization.MapCodec; -import dev.isxander.controlify.input.action.Accumulator; -import dev.isxander.controlify.input.action.Activator; -import dev.isxander.controlify.input.logical.LogicalInput; -import dev.isxander.controlify.input.logical.LogicalSignal; +import dev.isxander.controlify.action.Accumulator; +import dev.isxander.controlify.action.Activator; +import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.input.signal.LogicalSignal; /// Produces a pulse when the input is tapped. public record TapPulseActivator(LogicalInput boundInput) implements Activator { diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/ToggleLatchActivator.java b/src/main/java/dev/isxander/controlify/action/activator/ToggleLatchActivator.java similarity index 85% rename from src/main/java/dev/isxander/controlify/input/action/activator/ToggleLatchActivator.java rename to src/main/java/dev/isxander/controlify/action/activator/ToggleLatchActivator.java index 3c8df2b38..691ea2282 100644 --- a/src/main/java/dev/isxander/controlify/input/action/activator/ToggleLatchActivator.java +++ b/src/main/java/dev/isxander/controlify/action/activator/ToggleLatchActivator.java @@ -1,11 +1,11 @@ -package dev.isxander.controlify.input.action.activator; +package dev.isxander.controlify.action.activator; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.input.action.Accumulator; -import dev.isxander.controlify.input.action.Activator; -import dev.isxander.controlify.input.logical.LogicalInput; -import dev.isxander.controlify.input.logical.LogicalSignal; +import dev.isxander.controlify.action.Accumulator; +import dev.isxander.controlify.action.Activator; +import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.input.signal.LogicalSignal; /// Accepts another pulse binding, and converts pulses into toggling a latch. public class ToggleLatchActivator implements Activator { diff --git a/src/main/java/dev/isxander/controlify/bindings/defaults/DefaultBindManager.java b/src/main/java/dev/isxander/controlify/action/defaults/DefaultBindManager.java similarity index 99% rename from src/main/java/dev/isxander/controlify/bindings/defaults/DefaultBindManager.java rename to src/main/java/dev/isxander/controlify/action/defaults/DefaultBindManager.java index 38af1cf98..bc7e14d9f 100644 --- a/src/main/java/dev/isxander/controlify/bindings/defaults/DefaultBindManager.java +++ b/src/main/java/dev/isxander/controlify/action/defaults/DefaultBindManager.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.bindings.defaults; +package dev.isxander.controlify.action.defaults; import com.google.gson.JsonElement; import com.google.gson.JsonParser; diff --git a/src/main/java/dev/isxander/controlify/bindings/defaults/DefaultBindProvider.java b/src/main/java/dev/isxander/controlify/action/defaults/DefaultBindProvider.java similarity index 76% rename from src/main/java/dev/isxander/controlify/bindings/defaults/DefaultBindProvider.java rename to src/main/java/dev/isxander/controlify/action/defaults/DefaultBindProvider.java index dfcb82a0a..f18f820be 100644 --- a/src/main/java/dev/isxander/controlify/bindings/defaults/DefaultBindProvider.java +++ b/src/main/java/dev/isxander/controlify/action/defaults/DefaultBindProvider.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.bindings.defaults; +package dev.isxander.controlify.action.defaults; -import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.LogicalInput; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/bindings/defaults/LayeredDefaultBindProvider.java b/src/main/java/dev/isxander/controlify/action/defaults/LayeredDefaultBindProvider.java similarity index 92% rename from src/main/java/dev/isxander/controlify/bindings/defaults/LayeredDefaultBindProvider.java rename to src/main/java/dev/isxander/controlify/action/defaults/LayeredDefaultBindProvider.java index 3ee8345f1..e469b92ca 100644 --- a/src/main/java/dev/isxander/controlify/bindings/defaults/LayeredDefaultBindProvider.java +++ b/src/main/java/dev/isxander/controlify/action/defaults/LayeredDefaultBindProvider.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.bindings.defaults; +package dev.isxander.controlify.action.defaults; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.LogicalInput; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/bindings/defaults/MapBackedDefaultBindProvider.java b/src/main/java/dev/isxander/controlify/action/defaults/MapBackedDefaultBindProvider.java similarity index 89% rename from src/main/java/dev/isxander/controlify/bindings/defaults/MapBackedDefaultBindProvider.java rename to src/main/java/dev/isxander/controlify/action/defaults/MapBackedDefaultBindProvider.java index 238aff79d..46d4c1824 100644 --- a/src/main/java/dev/isxander/controlify/bindings/defaults/MapBackedDefaultBindProvider.java +++ b/src/main/java/dev/isxander/controlify/action/defaults/MapBackedDefaultBindProvider.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.bindings.defaults; +package dev.isxander.controlify.action.defaults; import com.mojang.serialization.Codec; import com.mojang.serialization.Keyable; import com.mojang.serialization.MapCodec; import dev.isxander.controlify.api.bind.ControlifyBindApi; -import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.LogicalInput; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/action/radial/RadialIcon.java b/src/main/java/dev/isxander/controlify/action/radial/RadialIcon.java new file mode 100644 index 000000000..21112c3ff --- /dev/null +++ b/src/main/java/dev/isxander/controlify/action/radial/RadialIcon.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.action.radial; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.ComponentSerialization; +import net.minecraft.resources.Identifier; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public record RadialIcon(Content content, @Nullable Component overlay) { + public static final RadialIcon EMPTY = new RadialIcon(new Empty(), null); + + public static final Codec CODEC = Definition.CODEC.flatXmap( + Definition::toRadialIcon, + RadialIcon::toDefinition + ); + + public static RadialIcon model(Identifier model, boolean enchantGlint) { + return new RadialIcon(new Model(model, enchantGlint), null); + } + + public static RadialIcon model(Identifier model) { + return model(model, false); + } + + public static RadialIcon texture(Identifier texture) { + return new RadialIcon(new Texture(texture), null); + } + + public RadialIcon withOverlay(Component overlay) { + return new RadialIcon(content, overlay); + } + + private static DataResult toDefinition(RadialIcon icon) { + return switch (icon.content()) { + case Model model -> DataResult.success(new Definition( + Optional.of(model), + Optional.empty(), + Optional.ofNullable(icon.overlay()) + )); + case Texture texture -> DataResult.success(new Definition( + Optional.empty(), + Optional.of(texture), + Optional.ofNullable(icon.overlay()) + )); + default -> DataResult.error(() -> "Only model and texture radial icons can be serialized"); + }; + } + + public sealed interface Content permits Empty, Model, Texture { + } + + public record Empty() implements Content { + } + + public record Model(Identifier id, boolean enchantGlint) implements Content { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Identifier.CODEC.fieldOf("id").forGetter(Model::id), + Codec.BOOL.optionalFieldOf("enchant_glint", false).forGetter(Model::enchantGlint) + ).apply(instance, Model::new)) + .withAlternative(Identifier.CODEC.xmap(id -> new Model(id, false), Model::id)); + } + + public record Texture(Identifier id) implements Content { + public static final Codec CODEC = Identifier.CODEC.xmap(Texture::new, Texture::id); + } + + private record Definition(Optional model, Optional texture, Optional overlay) { + private static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Model.CODEC.optionalFieldOf("model").forGetter(Definition::model), + Texture.CODEC.optionalFieldOf("texture").forGetter(Definition::texture), + ComponentSerialization.CODEC.optionalFieldOf("overlay").forGetter(Definition::overlay) + ).apply(instance, Definition::new)) + .validate(def -> { + if (def.model.isPresent() == def.texture.isPresent()) { + return DataResult.error(() -> "Radial icon must define exactly one of 'model' or 'texture'"); + } + return DataResult.success(def); + }); + + private DataResult toRadialIcon() { + if (this.model.isPresent() == this.texture.isPresent()) { + return DataResult.error(() -> "Radial icon must define exactly one of 'model' or 'texture'"); + } + + Content content = this.model.isPresent() + ? this.model.get() + : this.texture.get(); + + return DataResult.success(new RadialIcon(content, this.overlay.orElse(null))); + } + } +} diff --git a/src/main/java/dev/isxander/controlify/bindings/RadialIconExtractor.java b/src/main/java/dev/isxander/controlify/action/radial/RadialIconExtractor.java similarity index 75% rename from src/main/java/dev/isxander/controlify/bindings/RadialIconExtractor.java rename to src/main/java/dev/isxander/controlify/action/radial/RadialIconExtractor.java index 5c47b22bb..d2714c87a 100644 --- a/src/main/java/dev/isxander/controlify/bindings/RadialIconExtractor.java +++ b/src/main/java/dev/isxander/controlify/action/radial/RadialIconExtractor.java @@ -4,9 +4,8 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.bindings; +package dev.isxander.controlify.action.radial; -import dev.isxander.controlify.api.bind.RadialIcon; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.renderer.RenderPipelines; @@ -22,7 +21,7 @@ public final class RadialIconExtractor { public static void extract(GuiGraphicsExtractor graphics, RadialIcon icon, int x, int y) { switch (icon.content()) { case RadialIcon.Empty _ -> {} - case RadialIcon.Model(Identifier model) -> extractModel(graphics, model, x, y); + case RadialIcon.Model(Identifier model, boolean enchantGlint) -> extractModel(graphics, model, enchantGlint, x, y); case RadialIcon.Texture(Identifier texture) -> extractTexture(graphics, texture, x, y); } @@ -31,15 +30,16 @@ public static void extract(GuiGraphicsExtractor graphics, RadialIcon icon, int x } } - private static void extractModel(GuiGraphicsExtractor graphics, Identifier model, int x, int y) { + private static void extractModel(GuiGraphicsExtractor graphics, Identifier model, boolean enchantGlint, int x, int y) { ItemStack stack = new ItemStack(Holder.direct( Items.STICK, DataComponentMap.builder() - .set(DataComponents.MAX_STACK_SIZE, 1) - .set(DataComponents.MAX_DAMAGE, 2) - .set(DataComponents.DAMAGE, 0) - .set(DataComponents.ITEM_MODEL, model) - .build() + .set(DataComponents.MAX_STACK_SIZE, 1) + .set(DataComponents.MAX_DAMAGE, 2) + .set(DataComponents.DAMAGE, 0) + .set(DataComponents.ITEM_MODEL, model) + .set(DataComponents.ENCHANTMENT_GLINT_OVERRIDE, enchantGlint) + .build() )); graphics.item(stack, x, y); diff --git a/src/main/java/dev/isxander/controlify/bindings/RadialIconManager.java b/src/main/java/dev/isxander/controlify/action/radial/RadialIconManager.java similarity index 79% rename from src/main/java/dev/isxander/controlify/bindings/RadialIconManager.java rename to src/main/java/dev/isxander/controlify/action/radial/RadialIconManager.java index d6e86dda1..ea7dbd269 100644 --- a/src/main/java/dev/isxander/controlify/bindings/RadialIconManager.java +++ b/src/main/java/dev/isxander/controlify/action/radial/RadialIconManager.java @@ -4,14 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.bindings; +package dev.isxander.controlify.action.radial; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.api.bind.RadialIcon; +import dev.isxander.controlify.bindings.ControlifyBindings; import dev.isxander.controlify.platform.client.resource.SimpleControlifyReloadListener; import dev.isxander.controlify.utils.CUtil; import net.minecraft.resources.Identifier; @@ -32,17 +31,16 @@ public class RadialIconManager implements SimpleControlifyReloadListener> CODEC = Codec.unboundedMap(Identifier.CODEC, RadialIcon.CODEC); private static final RadialIcon FALLBACK = RadialIcon.model(Identifier.withDefaultNamespace("book")); - private volatile Map iconsByBinding = Map.of(); + private volatile Map iconsByActionId = Map.of(); - public boolean isRadialCandidate(InputBinding binding) { + public boolean isRadialCandidate(Identifier actionId) { // noinspection removal - return iconsByBinding.containsKey(binding.id()) - || binding.id().getNamespace().equals(ControlifyBindings.MODDED_BIND_NAMESPACE) - || binding.isRadialCandidate(); + return this.iconsByActionId.containsKey(actionId) + || actionId.getNamespace().equals(ControlifyBindings.MODDED_BIND_NAMESPACE); } - public RadialIcon getIcon(InputBinding binding) { - return iconsByBinding.getOrDefault(binding.id(), FALLBACK); + public RadialIcon getIcon(Identifier actionId) { + return iconsByActionId.getOrDefault(actionId, FALLBACK); } @Override @@ -74,7 +72,7 @@ public CompletableFuture> load(ResourceManager manag @Override public CompletableFuture apply(Map data, ResourceManager manager, Executor executor) { - return CompletableFuture.runAsync(() -> iconsByBinding = data, executor); + return CompletableFuture.runAsync(() -> iconsByActionId = data, executor); } @Override diff --git a/src/main/java/dev/isxander/controlify/api/bind/ControlifyBindApi.java b/src/main/java/dev/isxander/controlify/api/bind/ControlifyBindApi.java deleted file mode 100644 index 5fc639f63..000000000 --- a/src/main/java/dev/isxander/controlify/api/bind/ControlifyBindApi.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.api.bind; - -import dev.isxander.controlify.bindings.BindContext; -import dev.isxander.controlify.bindings.ControlifyBindApiImpl; -import dev.isxander.controlify.controller.ControllerEntity; -import net.minecraft.client.KeyMapping; -import net.minecraft.resources.Identifier; - -import java.util.List; -import java.util.function.Predicate; -import java.util.function.UnaryOperator; -import java.util.stream.Stream; - -/** - * The API for registering and managing bindings. - */ -public interface ControlifyBindApi { - static ControlifyBindApi get() { - return ControlifyBindApiImpl.INSTANCE; - } - - /** - * Registers a new binding that will be created on all controllers. - * - * @param callback the callback that populates the binding builder. will only be called once - * @return the supplier for the binding - */ - InputBindingSupplier registerBinding(RegistryCallback callback); - - /** - * Registers a new binding that will be created on only the controllers that pass the filter. - * - * @param callback the callback that populates the binding builder. will only be called once - * @param filter the filter that determines if the binding should be created for the controller - * @return the supplier for the binding - */ - InputBindingSupplier registerBinding(RegistryCallback callback, Predicate filter); - - /** - * Creates a supplier for an input binding with the given bind ID. - * Useful when fetching bindings by ID, rather than by java reference, such is needed - * in cases of data-driven systems. - * {@link InputBindingSupplier#on(ControllerEntity)} may throw an exception if the binding does not exist, - * when it is attempted to be resolved. - * @param bindId the ID of the binding to create a supplier for - * @return the supplier for the binding - */ - InputBindingSupplier createSupplier(Identifier bindId); - - /** - * Get all the controller bindings for a vanilla key mapping. - * This is populated using {@link InputBindingBuilder#addKeyCorrelation(KeyMapping)} - * or {@link InputBindingBuilder#keyEmulation(KeyMapping)}. - * - * @param key the key mapping to get the correlations for - * @return the list of the suppliers for the binding - */ - List getKeyCorrelation(KeyMapping key); - - /** - * Registers a new bind context that can be used to determine when a binding is active. - * - * @param context the bind context - */ - void registerBindContext(BindContext context); - - /** - * Gets all registered bind contexts. - * - * @return the stream of bind contexts - */ - Stream getAllBindIds(); - - @FunctionalInterface - interface RegistryCallback extends UnaryOperator { - } -} diff --git a/src/main/java/dev/isxander/controlify/api/bind/InputBinding.java b/src/main/java/dev/isxander/controlify/api/bind/InputBinding.java deleted file mode 100644 index 1d86013dd..000000000 --- a/src/main/java/dev/isxander/controlify/api/bind/InputBinding.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.api.bind; - -import dev.isxander.controlify.bindings.BindContext; -import dev.isxander.controlify.bindings.StateAccess; -import dev.isxander.controlify.bindings.input.EmptyInput; -import dev.isxander.controlify.bindings.input.Input; -import dev.isxander.controlify.bindings.output.AnalogueOutput; -import dev.isxander.controlify.bindings.output.DigitalOutput; -import dev.isxander.controlify.bindings.output.GuiPressOutput; -import dev.isxander.controlify.controller.input.ControllerStateView; -import dev.isxander.controlify.utils.CUtil; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; -import org.jetbrains.annotations.ApiStatus; - -import java.util.Set; -import java.util.function.Consumer; - -/** - * An input binding is a single action that can be performed in Minecraft, e.g. punching. - * It is bound to a controller {@link Input}, e.g. the A button. - * Every tick, the input binding will update its state based on the controller input and then propagate to its outputs, - * e.g. {@link #justTapped()}. The outputs are then used to perform the action in the game. - */ -public interface InputBinding { - /** - * The ID of the binding, set from {@link InputBindingBuilder#id(Identifier)}. - * @return the ID - */ - Identifier id(); - - /** - * The name of the binding. - * @return the name - */ - Component name(); - - /** - * The description of the binding. - * @return the description - */ - Component description(); - - /** - * The category of the binding. - * @return the category - */ - Component category(); - - /** - * The icon glyph of the input - * @return the icon component - */ - Component inputGlyph(); - - /** - * The icon glyph of the input - * @return the icon component - */ - @Deprecated - default Component inputIcon() { - return this.inputGlyph(); - } - - @Deprecated(forRemoval = true) - boolean isRadialCandidate(); - - /** - * Create a new state access with the given history required. - * Please use the minimum amount of history required. - * - * @param historyRequired the amount of ticks of history required. - * @return the state access - */ - StateAccess createStateAccess(int historyRequired); - - /** - * Create a new state access with the given history required and push event. - * Please use the minimum amount of history required. - * - * @param historyRequired the amount of ticks of history required. - * @param pushEvent the event to call when a new state is pushed. - * @return the state access - */ - StateAccess createStateAccess(int historyRequired, Consumer pushEvent); - - /** - * Returns the state access created with {@link #createStateAccess(int)}. - * This renders it unusable forever. - * This frees up history if possible. - * - * @param stateAccess the state access to return - */ - void returnStateAccess(StateAccess stateAccess); - - @ApiStatus.Internal - void pushState(ControllerStateView state); - - /** - * Emulates a tap of this binding. - * This is used by the radial menu. - */ - void fakePress(); - - /** - * Set the input that this binding is bound to. - * - * @param input input to bind - */ - void setBoundInput(Input input); - - /** - * Get the input that this binding is bound to. - * - * @return currently bound input - */ - Input boundInput(); - - /** - * Get the default input for this binding. - * This can change if the user reloads resources and the default changes - * (since it's data-driven). - * - * @return default input - */ - Input defaultInput(); - - /** - * @return true if the binding is equal to {@link EmptyInput} - */ - default boolean isUnbound() { - return EmptyInput.equals(this.boundInput()); - } - - /** - * Gets all contexts associated with this binding, - * not just the currently applicable ones. - * This is set from {@link InputBindingBuilder#allowedContexts(BindContext...)}. - * - * @return the contexts - */ - Set contexts(); - - /** - * Equivalent to calling - *

-	 *     StateAccess stateAccess = binding.createStateAccess(0);
-	 *     float analogueNow = stateAccess.analogue(0);
-	 * 
- * - * @return the current analogue state (0-1, never negative), - * this tick. - */ - float analogueNow(); - - /** - * Equivalent to calling - *

-	 *     StateAccess stateAccess = binding.createStateAccess(1);
-	 *     float analoguePrev = stateAccess.analogue(1);
-	 * 
- * - * @return the previous analogue state (0-1, never negative), 1 tick ago - */ - float analoguePrev(); - - /** - * Equivalent to calling - *

-	 *     StateAccess stateAccess = binding.createStateAccess(0);
-	 *     boolean digitalNow = stateAccess.digital(0);
-	 * 
- * - * @return the current digital state (true or false), this tick - */ - boolean digitalNow(); - - /** - * Equivalent to calling - *

-	 *     StateAccess stateAccess = binding.createStateAccess(1);
-	 *     boolean digitalPrev = stateAccess.digital(1);
-	 * 
- * - * @return the previous digital state (true or false), 1 tick ago - */ - boolean digitalPrev(); - - /** - * Equivalent to calling - *

-	 *     binding.digitalNow() && !binding.digitalPrev()
-	 * 
- * - * @return true if the binding is pressed this tick and not pressed the previous tick - */ - boolean justPressed(); - - /** - * Equivalent to calling - *

-	 *     !binding.digitalNow() && binding.digitalPrev()
-	 * 
- * - * @return true if the binding is not pressed this tick and pressed the previous tick - */ - boolean justReleased(); - - /** - * @return true if the binding got pressed, and then released - */ - boolean justTapped(); - - /** - * A more advanced output that returns true after the input was released, - * but only if the player has not navigated away from the button where the input was initially pressed. - * - * @return the output - */ - GuiPressOutput guiPressed(); - - Identifier ANALOGUE_NOW = CUtil.rl("analogue_now"); - Identifier ANALOGUE_PREV = CUtil.rl("analogue_prev"); - Identifier DIGITAL_NOW = CUtil.rl("digital_now"); - Identifier DIGITAL_PREV = CUtil.rl("digital_prev"); - Identifier JUST_PRESSED = CUtil.rl("just_pressed"); - Identifier JUST_RELEASED = CUtil.rl("just_released"); - Identifier JUST_TAPPED = CUtil.rl("just_tapped"); - Identifier GUI_PRESSED = CUtil.rl("gui_pressed"); - Identifier KEY_EMULATION = CUtil.rl("key_emulation"); - - T getDigitalOutput(Identifier id); - T addDigitalOutput(Identifier id, T output); - - T getAnalogueOutput(Identifier id); - T addAnalogueOutput(Identifier id, T output); -} diff --git a/src/main/java/dev/isxander/controlify/api/bind/InputBindingBuilder.java b/src/main/java/dev/isxander/controlify/api/bind/InputBindingBuilder.java deleted file mode 100644 index e0671897d..000000000 --- a/src/main/java/dev/isxander/controlify/api/bind/InputBindingBuilder.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.api.bind; - -import dev.isxander.controlify.bindings.BindContext; -import dev.isxander.controlify.bindings.input.Input; -import dev.isxander.controlify.controller.ControllerEntity; -import net.minecraft.client.KeyMapping; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.function.Function; - -/** - * A builder for creating an {@link InputBinding}. - * This is used to populate the fields of the binding. - * The builder is provided through a callback in - * {@link ControlifyBindApi#registerBinding(ControlifyBindApi.RegistryCallback)} - * and should not be created or built manually. - */ -public interface InputBindingBuilder { - /** - * Sets the unique identifier for the binding. - * This is a required field. The namespace should be your mod's ID. - *

- * The id is used to automatically generate the translation keys for the name and description. - *

    - *
  • Name: controlify.binding.(namespace).(path)
  • - *
  • Desc: controlify.binding.(namespace).(path).desc, optional
  • - *
- * This is the ID referred to in {@link InputBindingSupplier#bindId()}. - * - * @param rl the binding's id - * @return this builder - */ - InputBindingBuilder id(@NotNull Identifier rl); - - /** - * Sets the unique identifier for the binding. - * This is a required field. The namespace should be your mod's ID. - *

- * The id is used to automatically generate the translation keys for the name and description. - *

    - *
  • Name: controlify.binding.(namespace).(path)
  • - *
  • Desc: controlify.binding.(namespace).(path).desc, optional
  • - *
- * This is the ID referred to in {@link InputBindingSupplier#bindId()}. - * - * @param namespace namespace of {@link Identifier} - * @param path path of {@link Identifier} - * @return this builder - */ - InputBindingBuilder id(@NotNull String namespace, @NotNull String path); - - /** - * Sets a custom name component for the binding. Optional field. - * If not set, the name will be automatically generated from {@link #id(Identifier)}, - * controlify.binding.(namespace).(path) - *

- * Useful for re-using language keys from regular key mappings. - * - * @param text the name component - * @return this builder - */ - InputBindingBuilder name(@NotNull Component text); - - /** - * Sets a custom description component for the binding. Optional field. - * If not set, the description will be automatically generated from {@link #id(Identifier)}, - * controlify.binding.(namespace).(path).desc - * The description will be empty if the translation key does not exist. - *

- * Useful for re-using language keys from regular key mappings. - * - * @param text the name component - * @return this builder - */ - InputBindingBuilder description(@NotNull Component text); - - /** - * Sets the category for the binding. Required field. - * This is used to group the binding in the option menu. - * It's recommended that you use a single category for all of your mod's bindings. - * - * @param text the category component - * @return this builder - */ - InputBindingBuilder category(@NotNull Component text); - - /** - * Sets the contexts that the binding is allowed to be active in. - * If the binding's predicate {@link BindContext#isApplicable()} returns false, all outputs will be disabled. - * - * @param contexts all applicable contexts - * @return this builder - */ - InputBindingBuilder allowedContexts(@NotNull BindContext @Nullable... contexts); - - /** - * Adds a correlation between a vanilla key mapping and this binding. - * A correlation means that an auto-generated binding for this vanilla key mapping will not be created. - * - * @param keyMapping the key mapping to correlate with - * @return this builder - */ - InputBindingBuilder addKeyCorrelation(@NotNull KeyMapping keyMapping); - - /** - * Makes the binding emulate a vanilla key mapping. - * If the bound input is pressed, so will the key mapping. - * - * @param keyMapping the key mapping to emulate - * @return this builder - */ - InputBindingBuilder keyEmulation(@NotNull KeyMapping keyMapping); - - InputBindingBuilder keyEmulation(@NotNull KeyMapping keyMapping, @Nullable Function toggleCondition); - - /** - * Sets the default input for the binding. - * It is not recommended to use this method. Default inputs should be data-driven. - * If specified, the default will be bottom-most layered with data-driven defaults. - * To set the default input from data, specify it in: - *

assets/controlify/controllers/default_bind/default.json
- *

-	 *     {
-	 *         "defaults": {
-	 *             "your_binding_id": {
-	 *                 "button": "controlify:button/south"
-	 *             }
-	 *         }
-	 *     }
-	 * 
- * @param input the default input - * @return this builder - */ - @Deprecated - InputBindingBuilder defaultInput(@Nullable Input input); - - /// @deprecated Radial Menu candidates are now data driven. - /// [Check out the wiki](https://moddedmc.wiki/en/project/controlify/latest/docs/resource-packs/radial-icons) - @Deprecated(forRemoval = true) - default InputBindingBuilder radialCandidate(@Nullable Identifier icon) { - return radialCandidate(icon != null); - } - - @Deprecated(forRemoval = true) - InputBindingBuilder radialCandidate(boolean isCandidate); -} diff --git a/src/main/java/dev/isxander/controlify/api/bind/InputBindingSupplier.java b/src/main/java/dev/isxander/controlify/api/bind/InputBindingSupplier.java deleted file mode 100644 index 50cc4fdd5..000000000 --- a/src/main/java/dev/isxander/controlify/api/bind/InputBindingSupplier.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.api.bind; - -import com.mojang.serialization.Codec; -import dev.isxander.controlify.controller.ControllerEntity; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Objects; - -/** - * A supplier that retrieves an {@link InputBinding} for a controller. - */ -public interface InputBindingSupplier { - /** - * Retrieves the input binding for the controller, throwing an exception if it does not exist. - * - * @param controller the controller to fetch the binding from - * @throws NullPointerException if the binding does not exist - * @return the input binding - */ - default InputBinding on(@NotNull ControllerEntity controller) { - return Objects.requireNonNull( - onOrNull(controller), - () -> "Attempted to fetch " + bindId() + " for controller " + controller.uid() + " but it did not exist." + - "The binding registry callback may have a filter that did not pass for this controller."); - } - - /** - * Retrieves the input binding for the controller, or null if it does not exist. - * - * @param controller controller to fetch binding from - * @return the input binding, or null if it does not exist - */ - @Nullable InputBinding onOrNull(ControllerEntity controller); - - /** - * @return id of the binding. - */ - Identifier bindId(); - - /** - * Returns a text component with the currently bound input glyph - * for the currently selected controller. - *

- * Be careful, as this component will be rendered differently - * depending on the active controller, so make sure to deal with - * width caching and other issues that may arise. - * @see InputBinding#inputGlyph() for a deterministic version on a specific controller - * @return a component representing the input glyph - */ - Component inputGlyph(); - - Codec CODEC = Identifier.CODEC.xmap( - bindId -> ControlifyBindApi.get().createSupplier(bindId), - InputBindingSupplier::bindId - ); -} diff --git a/src/main/java/dev/isxander/controlify/api/bind/RadialIcon.java b/src/main/java/dev/isxander/controlify/api/bind/RadialIcon.java deleted file mode 100644 index 101c41f9a..000000000 --- a/src/main/java/dev/isxander/controlify/api/bind/RadialIcon.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.api.bind; - -import com.mojang.serialization.Codec; -import com.mojang.serialization.DataResult; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; -import org.jetbrains.annotations.Nullable; - -import java.util.Optional; - -public record RadialIcon(Content content, @Nullable Component overlay) { - public static final RadialIcon EMPTY = new RadialIcon(new Empty(), null); - - public static final Codec CODEC = Definition.CODEC.flatXmap( - Definition::toRadialIcon, - RadialIcon::toDefinition - ); - - public static RadialIcon model(Identifier model) { - return new RadialIcon(new Model(model), null); - } - - public static RadialIcon texture(Identifier texture) { - return new RadialIcon(new Texture(texture), null); - } - - public RadialIcon withOverlay(Component overlay) { - return new RadialIcon(content, overlay); - } - - private static DataResult toDefinition(RadialIcon icon) { - return switch (icon.content()) { - case Model(Identifier model) -> DataResult.success(new Definition(Optional.of(model), Optional.empty())); - case Texture(Identifier texture) -> DataResult.success(new Definition(Optional.empty(), Optional.of(texture))); - default -> DataResult.error(() -> "Only model and texture radial icons can be serialized"); - }; - } - - public sealed interface Content permits Empty, Model, Texture { - } - - public record Empty() implements Content { - } - - public record Model(Identifier id) implements Content { - } - - public record Texture(Identifier id) implements Content { - } - - private record Definition(Optional model, Optional texture) { - private static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - Identifier.CODEC.optionalFieldOf("model").forGetter(Definition::model), - Identifier.CODEC.optionalFieldOf("texture").forGetter(Definition::texture) - ).apply(instance, Definition::new)); - - private DataResult toRadialIcon() { - if (model.isPresent() == texture.isPresent()) { - return DataResult.error(() -> "Radial icon must define exactly one of 'model' or 'texture'"); - } - - return DataResult.success(model - .map(RadialIcon::model) - .orElseGet(() -> RadialIcon.texture(texture.orElseThrow()))); - } - } -} diff --git a/src/main/java/dev/isxander/controlify/api/buttonguide/ButtonGuideApi.java b/src/main/java/dev/isxander/controlify/api/buttonguide/ButtonGuideApi.java index ab2bca95c..bf64bc3d3 100644 --- a/src/main/java/dev/isxander/controlify/api/buttonguide/ButtonGuideApi.java +++ b/src/main/java/dev/isxander/controlify/api/buttonguide/ButtonGuideApi.java @@ -6,6 +6,7 @@ */ package dev.isxander.controlify.api.buttonguide; +import dev.isxander.controlify.action.ActionAccessor; import dev.isxander.controlify.api.bind.InputBindingSupplier; import dev.isxander.controlify.gui.ButtonGuideRenderer; import net.minecraft.client.gui.screens.Screen; @@ -28,20 +29,20 @@ private ButtonGuideApi() { * Custom behaviour should be handled inside a {@link dev.isxander.controlify.screenop.ScreenProcessor} or {@link dev.isxander.controlify.screenop.ComponentProcessor} * * @param button button to render the guide for - * @param binding the custom binding to render + * @param action the action binding to render * @param renderPredicate whether the guide should be rendered */ public static void addGuideToButton( @NotNull T button, - @NotNull InputBindingSupplier binding, + @NotNull ActionAccessor action, @NotNull ButtonGuidePredicate renderPredicate) { - ButtonGuideRenderer.registerBindingForButton(button, () -> binding, renderPredicate); + ButtonGuideRenderer.registerBindingForButton(button, () -> action, renderPredicate); } public static void addGuideToButton( @NotNull T button, - @NotNull Supplier binding, + @NotNull Supplier action, @NotNull ButtonGuidePredicate renderPredicate) { - ButtonGuideRenderer.registerBindingForButton(button, binding, renderPredicate); + ButtonGuideRenderer.registerBindingForButton(button, action, renderPredicate); } } diff --git a/src/main/java/dev/isxander/controlify/bindings/ControlifyBindApiImpl.java b/src/main/java/dev/isxander/controlify/bindings/ControlifyBindApiImpl.java deleted file mode 100644 index 111686670..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/ControlifyBindApiImpl.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings; - -import dev.isxander.controlify.api.bind.ControlifyBindApi; -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.api.bind.InputBindingSupplier; -import dev.isxander.controlify.bindings.output.KeyMappingEmulationOutput; -import dev.isxander.controlify.controller.ControllerEntity; -import net.minecraft.client.KeyMapping; -import net.minecraft.resources.Identifier; -import org.jetbrains.annotations.Nullable; - -import java.util.*; -import java.util.function.BooleanSupplier; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Stream; - -public class ControlifyBindApiImpl implements ControlifyBindApi { - public static final ControlifyBindApiImpl INSTANCE = new ControlifyBindApiImpl(); - - private final List bindEntries = new ArrayList<>(); - private final Map> keyMappingCorrelations = new HashMap<>(); - - private boolean locked; - - @Override - public InputBindingSupplier registerBinding(RegistryCallback callback) { - return registerBinding(callback, c -> true); - } - - @Override - public InputBindingSupplier registerBinding(RegistryCallback callback, Predicate filter) { - checkLocked(); - - var builder = new InputBindingBuilderImpl(); - - callback.apply(builder); - - Function finaliser = builder::build; - - Identifier bindId = builder.getIdAndLock(); - - this.bindEntries.add(new RegistryEntry(filter, finaliser, builder.getKeyEmulation(), builder.getKeyEmulationToggle(), bindId)); - - for (KeyMapping key : builder.getKeyCorrelations()) { - keyMappingCorrelations.computeIfAbsent(key, k -> new ArrayList<>()).add(createSupplier(bindId)); - } - - return createSupplier(bindId); - } - - @Override - public List getKeyCorrelation(KeyMapping key) { - return Optional.ofNullable(this.keyMappingCorrelations.get(key)).orElse(List.of()); - } - - @Override - public void registerBindContext(BindContext context) { - BindContext.CONTEXTS.put(context.id(), context); - } - - private void checkLocked() { - if (locked) - throw new IllegalStateException("Registry is locked. Cannot add bind now."); - } - - /** - * Provides all bindings for a given controller. - * @param controller The controller to provide bindings for. Can be null to get a list of all bindings. - * @return A list of input bindings. - */ - public List provideBindsForController(@Nullable ControllerEntity controller) { - List bindings = new ArrayList<>(); - - for (RegistryEntry entry : bindEntries) { - if (controller != null && !entry.filter().test(controller)) - continue; - - InputBindingImpl binding = entry.builder().apply(controller); - - if (controller != null && entry.emulation() != null) { - BooleanSupplier emulationToggle = null; - if (entry.emulationToggle() != null) { - emulationToggle = () -> entry.emulationToggle().apply(controller); - } - - binding.addDigitalOutput( - InputBinding.KEY_EMULATION, - new KeyMappingEmulationOutput(controller, binding, entry.emulation(), emulationToggle) - ); - } - - bindings.add(binding); - } - - return bindings; - } - - @Override - public InputBindingSupplier createSupplier(Identifier bindingId) { - return new InputBindingSupplierImpl(bindingId); - } - - public void lock() { - this.locked = true; - } - - @Override - public Stream getAllBindIds() { - return this.bindEntries.stream().map(RegistryEntry::id); - } - - private record RegistryEntry( - Predicate filter, - Function<@Nullable ControllerEntity, InputBindingImpl> builder, - KeyMapping emulation, - Function emulationToggle, - Identifier id - ) {} -} diff --git a/src/main/java/dev/isxander/controlify/bindings/InputBindingBuilderImpl.java b/src/main/java/dev/isxander/controlify/bindings/InputBindingBuilderImpl.java deleted file mode 100644 index 063f68487..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/InputBindingBuilderImpl.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings; - -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.api.bind.InputBindingBuilder; -import dev.isxander.controlify.bindings.defaults.DefaultBindProvider; -import dev.isxander.controlify.bindings.input.EmptyInput; -import dev.isxander.controlify.bindings.input.Input; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.id.ControllerType; -import net.minecraft.client.KeyMapping; -import net.minecraft.locale.Language; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; -import org.apache.commons.lang3.Validate; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; -import java.util.function.Function; -import java.util.function.Supplier; - -public class InputBindingBuilderImpl implements InputBindingBuilder { - private @Nullable Identifier id; - private @Nullable Component category; - private @Nullable Component customName, customDescription; - private @Nullable Input defaultInput; - private final Set allowedContexts = new HashSet<>(); - private final Set keyCorrelations = new HashSet<>(); - private KeyMapping keyEmulation = null; - private Function keyEmulationToggle = null; - - @Deprecated(forRemoval = true) - private boolean isRadialCandidate = false; - - private boolean locked; - - @Override - public InputBindingBuilder id(@NotNull Identifier rl) { - checkLocked(); - - this.id = rl; - return this; - } - - @Override - public InputBindingBuilder id(@NotNull String namespace, @NotNull String path) { - return this.id(Identifier.fromNamespaceAndPath(namespace, path)); - } - - @Override - public InputBindingBuilder category(@NotNull Component text) { - checkLocked(); - - this.category = text; - return this; - } - - @Override - public InputBindingBuilder name(@NotNull Component text) { - checkLocked(); - - this.customName = text; - return this; - } - - @Override - public InputBindingBuilder description(@NotNull Component text) { - checkLocked(); - - this.customDescription = text; - return this; - } - - @Override - public InputBindingBuilder defaultInput(@Nullable Input input) { - checkLocked(); - - this.defaultInput = input; - return this; - } - - @Override - public InputBindingBuilder allowedContexts(@NotNull BindContext @Nullable ... contexts) { - checkLocked(); - - if (contexts != null) - this.allowedContexts.addAll(List.of(contexts)); - return this; - } - - @Override - public InputBindingBuilder addKeyCorrelation(@NotNull KeyMapping keyMapping) { - checkLocked(); - - this.keyCorrelations.add(keyMapping); - return this; - } - - @Override - public InputBindingBuilder keyEmulation(@NotNull KeyMapping keyMapping, @Nullable Function toggleCondition) { - checkLocked(); - - this.keyEmulation = keyMapping; - this.keyEmulationToggle = toggleCondition; - this.addKeyCorrelation(keyMapping); - return this; - } - - @Override - public InputBindingBuilder keyEmulation(@NotNull KeyMapping keyMapping) { - return keyEmulation(keyMapping, null); - } - - @Override - @Deprecated(forRemoval = true) - @SuppressWarnings("removal") - public InputBindingBuilder radialCandidate(boolean isCandidate) { - this.isRadialCandidate = isCandidate; - return this; - } - - public InputBindingImpl build(@Nullable ControllerEntity controller) { - Validate.isTrue(locked, "Tried to build builder before it was locked."); - - Identifier controllerType = controller != null - ? controller.info().type().namespace() - : ControllerType.DEFAULT.namespace(); - - Component name = createDefaultString(controllerType, null, false); - if (customName != null) name = customName; - - Component description = createDefaultString(controllerType, "desc", true); - if (customDescription != null) description = customDescription; - if (description == null) description = Component.empty(); - - Supplier defaultSupplier = () -> { - // retrieve every tick so the bind provider isn't cached after a resource reload - DefaultBindProvider provider = Controlify.instance().defaultBindManager().getDefaultBindProvider( - controllerType - ); - - Input input = provider.getDefaultInput(id); - if (input == null) input = defaultInput; - if (input == null) input = EmptyInput.INSTANCE; - - return input; - }; - - return new InputBindingImpl( - controller != null ? controller.input().orElse(null) : null, - controllerType, - id, - name, - description, - category, - defaultSupplier, - allowedContexts, - isRadialCandidate - ); - } - - @NotNull public Identifier getIdAndLock() { - checkLocked(); - - locked = true; - Validate.notNull(this.id, "Must call `.id(Identifier)` on builder!"); - Validate.notNull(this.category, "Must call `.category(Component)` on builder %s!".formatted(this.id)); - - return this.id; - } - - public Set getKeyCorrelations() { - return this.keyCorrelations; - } - - public @Nullable KeyMapping getKeyEmulation() { - return this.keyEmulation; - } - - public @Nullable Function getKeyEmulationToggle() { - return this.keyEmulationToggle; - } - - private void checkLocked() { - Validate.isTrue(!locked, "Tried to modify binding builder after is has been locked!"); - } - - private Component createDefaultString(Identifier controllerType, @Nullable String suffix, boolean notExistToNull) { - Objects.requireNonNull(id); - - String typeSpecificKey = controllerType.toLanguageKey("controlify.binding", id.toLanguageKey()); - if (suffix != null) typeSpecificKey += "." + suffix; - if (Language.getInstance().has(typeSpecificKey)) { - return Component.translatable(typeSpecificKey); - } - - String genericKey = id.toLanguageKey("controlify.binding"); - if (suffix != null) genericKey += "." + suffix; - - if (notExistToNull && !Language.getInstance().has(genericKey)) - return null; - - return Component.translatable(genericKey); - } -} diff --git a/src/main/java/dev/isxander/controlify/bindings/InputBindingImpl.java b/src/main/java/dev/isxander/controlify/bindings/InputBindingImpl.java deleted file mode 100644 index 58b9be40e..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/InputBindingImpl.java +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings; - -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.bindings.input.Input; -import dev.isxander.controlify.bindings.output.*; -import dev.isxander.controlify.controller.input.ControllerStateView; -import dev.isxander.controlify.controller.input.InputComponent; -import dev.isxander.controlify.utils.ResizableRingBuffer; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; -import org.jetbrains.annotations.Nullable; - -import java.util.*; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class InputBindingImpl implements InputBinding { - private final Identifier id; - private final Component name, description, category; - private final boolean isRadialCandidate; - /** - * Fallback bound input used only when inputComponent is null (i.e., bindings not attached to a controller). - * When inputComponent is present, the bound input is read/written directly from the config. - */ - private Input fallbackBoundInput; - private final Supplier defaultBindSupplier; - private final Set contexts; - private final @Nullable InputComponent inputComponent; - private final Identifier controllerType; - - private final ResizableRingBuffer stateHistory; - private final Set borrowedAccesses; - - private boolean suppressed; - - private final AnalogueOutput analogueNow; - private final AnalogueOutput analoguePrev; - private final DigitalOutput digitalNow; - private final DigitalOutput digitalThen; - private final DigitalOutput justPressed; - private final DigitalOutput justReleased; - private final DigitalOutput justTapped; - private final GuiPressOutput guiPressOutput; - - private final Map digitalOutputs; - private final Map analogueOutputs; - - private int fakePressState = -1; - - public InputBindingImpl( - @Nullable InputComponent inputComponent, - Identifier controllerType, - Identifier id, - Component name, - Component description, - Component category, - Supplier defaultBindSupplier, - Set contexts, - boolean isRadialCandidate - ) { - this.inputComponent = inputComponent; - this.controllerType = controllerType; - this.id = id; - this.name = name; - this.description = description; - this.category = category; - this.isRadialCandidate = isRadialCandidate; - this.stateHistory = new ResizableRingBuffer<>(2, () -> 0f); - this.fallbackBoundInput = defaultBindSupplier.get(); - this.defaultBindSupplier = defaultBindSupplier; - this.contexts = contexts; - this.borrowedAccesses = new HashSet<>(); - - this.digitalOutputs = new HashMap<>(); - this.analogueOutputs = new HashMap<>(); - - this.analogueNow = addAnalogueOutput(ANALOGUE_NOW, new SimpleAnalogueOutput(this, 0)); - this.analoguePrev = addAnalogueOutput(ANALOGUE_PREV, new SimpleAnalogueOutput(this, 1)); - this.digitalNow = addDigitalOutput(DIGITAL_NOW, new SimpleDigitalOutput(this, 0)); - this.digitalThen = addDigitalOutput(DIGITAL_PREV, new SimpleDigitalOutput(this, 1)); - this.justPressed = addDigitalOutput(JUST_PRESSED, new JustPressedOutput(this)); - this.justReleased = addDigitalOutput(JUST_RELEASED, new JustReleasedOutput(this)); - this.justTapped = addDigitalOutput(JUST_TAPPED, new JustTappedOutput(this)); - this.guiPressOutput = addDigitalOutput(GUI_PRESSED, new GuiPressOutput(this)); - } - - @Override - public Identifier id() { - return this.id; - } - - @Override - public Component name() { - return this.name; - } - - @Override - public Component description() { - return this.description; - } - - @Override - public Component category() { - return this.category; - } - - @Override - public Component inputGlyph() { - return Controlify.instance().inputFontMapper().getComponentFromInputs( - controllerType, - boundInput().getRelevantInputs() - ); - } - - @Deprecated(forRemoval = true) - @SuppressWarnings("removal") - @Override - public boolean isRadialCandidate() { - return isRadialCandidate; - } - - @Override - public StateAccess createStateAccess(int historyRequired) { - return createStateAccess(historyRequired, null); - } - - @Override - public StateAccess createStateAccess(int historyRequired, Consumer pushEvent) { - if (historyRequired > this.stateHistory.size()) { - this.stateHistory.setSize(historyRequired); - } - - StateAccessImpl access = new StateAccessImpl(historyRequired, pushEvent); - borrowedAccesses.add(access); - - return access; - } - - @Override - public void returnStateAccess(StateAccess stateAccess) { - if (stateAccess instanceof StateAccessImpl accessImpl) { - boolean removed = this.borrowedAccesses.remove(accessImpl); - accessImpl.retire(); - - if (removed) { - OptionalInt newMaxSize = this.borrowedAccesses.stream().mapToInt(StateAccessImpl::maxHistory).max(); - newMaxSize.ifPresent(this.stateHistory::setSize); - } - } else { - throw new IllegalStateException("Unknown implementation of state access"); - } - } - - @Override - public void pushState(ControllerStateView state) { - if (!this.contexts.isEmpty()) { - Set thisTickContexts = Controlify.instance().thisTickBindContexts(); - this.suppressed = this.contexts.stream().noneMatch(thisTickContexts::contains); - } else { - this.suppressed = false; - } - - float analogue = this.boundInput().state(state); - - switch (fakePressState) { - case 0 -> analogue = 0; - case 1,2 -> analogue = 1; - case 3 -> analogue = 0; - } - if (fakePressState >= 0) { - if (fakePressState == 3) - fakePressState = -1; - else - fakePressState++; - } - - this.stateHistory.push(analogue); - borrowedAccesses.forEach(StateAccessImpl::onPush); - } - - @Override - public void fakePress() { - fakePressState = 0; - } - - @Override - public void setBoundInput(Input input) { - if (inputComponent != null) { - var settings = inputComponent.settings(); - // If keepDefaultBindings is false and the input equals the default, - // remove from config so it won't be serialized (allowing future default changes to take effect) - if (!settings.keepDefaultBindings && input.equals(defaultInput())) { - settings.bindings.bindings.remove(id); - } else { - // Write directly to the config - all bindings sharing the same config will see this change - settings.bindings.bindings.put(id, input); - } - } else { - // No inputComponent means this binding isn't attached to a controller, use fallback - this.fallbackBoundInput = input; - } - Controlify.instance().config().markDirty(); - } - - @Override - public Input boundInput() { - if (inputComponent != null) { - // Read directly from the config - ensures all bindings sharing the same config see the same value - Input configInput = inputComponent.settings().bindings.bindings.get(id); - if (configInput != null) { - return configInput; - } - // Not in config means it's at the default (require keepDefaultBindings is false) - // Return the dynamic default so all controllers stay in sync - return defaultInput(); - } - // Fallback: use local value for bindings without inputComponent - return this.fallbackBoundInput; - } - - @Override - public Input defaultInput() { - return this.defaultBindSupplier.get(); - } - - @Override - public Set contexts() { - return this.contexts; - } - - @Override - public float analogueNow() { - return this.analogueNow.get(); - } - - @Override - public float analoguePrev() { - return this.analoguePrev.get(); - } - - @Override - public boolean digitalNow() { - return this.digitalNow.get(); - } - - @Override - public boolean digitalPrev() { - return this.digitalThen.get(); - } - - @Override - public boolean justPressed() { - return this.justPressed.get(); - } - - @Override - public boolean justReleased() { - return this.justReleased.get(); - } - - @Override - public boolean justTapped() { - return this.justTapped.get(); - } - - @Override - public GuiPressOutput guiPressed() { - return this.guiPressOutput; - } - - @Override - public T addDigitalOutput(Identifier id, T output) { - this.digitalOutputs.put(id, output); - return output; - } - - @Override - public T getDigitalOutput(Identifier id) { - return (T) this.digitalOutputs.get(id); - } - - @Override - public T addAnalogueOutput(Identifier id, T output) { - this.analogueOutputs.put(id, output); - return output; - } - - @Override - public T getAnalogueOutput(Identifier id) { - return (T) this.analogueOutputs.get(id); - } - - private class StateAccessImpl implements StateAccess { - private final int maxHistory; - private boolean valid; - private final @Nullable Consumer pushListener; - - public StateAccessImpl(int maxHistory, @Nullable Consumer pushListener) { - this.maxHistory = maxHistory; - this.valid = true; - this.pushListener = pushListener; - } - - @Override - public float analogue(int history) { - if (!valid) throw new IllegalStateException("Tried to access state from returned access!"); - if (history > maxHistory) throw new IllegalStateException("Overflowing history!"); - - return InputBindingImpl.this.stateHistory.tail(history); - } - - @Override - public boolean digital(int history) { - float threshold = inputComponent != null - ? inputComponent.settings().buttonActivationThreshold - : 0.5f; - return analogue(history) > threshold; - } - - @Override - public boolean isSuppressed() { - return suppressed; - } - - @Override - public boolean isValid() { - return valid; - } - - @Override - public int maxHistory() { - return this.maxHistory; - } - - public void onPush() { - if (this.pushListener != null) - this.pushListener.accept(this); - } - - public void retire() { - this.valid = false; - } - } -} diff --git a/src/main/java/dev/isxander/controlify/bindings/InputBindingSupplierImpl.java b/src/main/java/dev/isxander/controlify/bindings/InputBindingSupplierImpl.java deleted file mode 100644 index baa7d638c..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/InputBindingSupplierImpl.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings; - -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.api.bind.InputBindingSupplier; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.font.BindingFontHelper; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; -import org.jetbrains.annotations.Nullable; - -public record InputBindingSupplierImpl(Identifier bindId) implements InputBindingSupplier { - @Override - public @Nullable InputBinding onOrNull(ControllerEntity controller) { - return controller.input().map(c -> c.getBinding(bindId)).orElse(null); - } - - @Override - public Component inputGlyph() { - return BindingFontHelper.binding(this.bindId()); - } -} diff --git a/src/main/java/dev/isxander/controlify/bindings/RadialIcons.java b/src/main/java/dev/isxander/controlify/bindings/RadialIcons.java deleted file mode 100644 index 3124ff4d5..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/RadialIcons.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings; - -import dev.isxander.controlify.utils.CUtil; -import net.minecraft.core.Holder; -import net.minecraft.resources.Identifier; -import net.minecraft.world.effect.MobEffect; -import net.minecraft.world.item.Item; - -/// @deprecated Radial icons are now data driven. -/// [Check out the wiki](https://moddedmc.wiki/en/project/controlify/latest/docs/resource-packs/radial-icons) -@Deprecated(forRemoval = true) -public final class RadialIcons { - - private RadialIcons() { - } - - @Deprecated(forRemoval = true) - public static Identifier getItem(Item item) { - return CUtil.rl("radial_icons_are_data_driven"); - } - - @Deprecated(forRemoval = true) - public static Identifier getEffect(Holder effect) { - return CUtil.rl("radial_icons_are_data_driven"); - } -} diff --git a/src/main/java/dev/isxander/controlify/bindings/StateAccess.java b/src/main/java/dev/isxander/controlify/bindings/StateAccess.java deleted file mode 100644 index df2b7bfc6..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/StateAccess.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings; - -public interface StateAccess { - float analogue(int history); - - boolean digital(int history); - - boolean isSuppressed(); - - boolean isValid(); - - int maxHistory(); -} diff --git a/src/main/java/dev/isxander/controlify/bindings/input/AxisInput.java b/src/main/java/dev/isxander/controlify/bindings/input/AxisInput.java deleted file mode 100644 index 3045090e1..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/input/AxisInput.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings.input; - -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.controller.input.ControllerStateView; -import net.minecraft.resources.Identifier; - -import java.util.List; - -public record AxisInput(Identifier axis) implements Input { - public static final String INPUT_ID = "axis"; - - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - Identifier.CODEC.fieldOf(INPUT_ID).forGetter(AxisInput::axis) - ).apply(instance, AxisInput::new)); - - @Override - public float state(ControllerStateView state) { - return state.getAxisState(axis); - } - - @Override - public List getRelevantInputs() { - return List.of(axis); - } - - @Override - public InputType type() { - return InputType.AXIS; - } -} diff --git a/src/main/java/dev/isxander/controlify/bindings/input/ButtonInput.java b/src/main/java/dev/isxander/controlify/bindings/input/ButtonInput.java deleted file mode 100644 index eac6b4434..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/input/ButtonInput.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings.input; - -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.controller.input.ControllerStateView; -import net.minecraft.resources.Identifier; - -import java.util.List; - -public record ButtonInput(Identifier button) implements Input { - public static final String INPUT_ID = "button"; - - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - Identifier.CODEC.fieldOf(INPUT_ID).forGetter(ButtonInput::button) - ).apply(instance, ButtonInput::new)); - - @Override - public float state(ControllerStateView state) { - return state.isButtonDown(button) ? 1 : 0; - } - - @Override - public List getRelevantInputs() { - return List.of(button); - } - - @Override - public InputType type() { - return InputType.BUTTON; - } -} diff --git a/src/main/java/dev/isxander/controlify/bindings/input/EmptyInput.java b/src/main/java/dev/isxander/controlify/bindings/input/EmptyInput.java deleted file mode 100644 index 619cd46df..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/input/EmptyInput.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings.input; - -import com.mojang.serialization.MapCodec; -import dev.isxander.controlify.controller.input.ControllerStateView; -import net.minecraft.resources.Identifier; - -import java.util.List; - -public record EmptyInput() implements Input { - public static final EmptyInput INSTANCE = new EmptyInput(); - - public static final String INPUT_ID = "empty"; - public static final MapCodec CODEC = MapCodec.unit(() -> INSTANCE); - - @Override - public float state(ControllerStateView state) { - return 0; - } - - @Override - public List getRelevantInputs() { - return List.of(); - } - - @Override - public InputType type() { - return InputType.EMPTY; - } - - public static boolean equals(Input input) { - return input instanceof EmptyInput; - } -} diff --git a/src/main/java/dev/isxander/controlify/bindings/input/HatInput.java b/src/main/java/dev/isxander/controlify/bindings/input/HatInput.java deleted file mode 100644 index 7d221d6c2..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/input/HatInput.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings.input; - -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.controller.input.ControllerStateView; -import dev.isxander.controlify.controller.input.HatState; -import net.minecraft.resources.Identifier; - -import java.util.List; - -public record HatInput(Identifier hat, HatState targetState) implements Input { - public static final String INPUT_ID = "hat"; - - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - Identifier.CODEC.fieldOf(INPUT_ID).forGetter(HatInput::hat), - HatState.CODEC.fieldOf("target_state").forGetter(HatInput::targetState) - ).apply(instance, HatInput::new)); - - @Override - public float state(ControllerStateView state) { - return state.getHatState(hat) == targetState ? 1 : 0; - } - - @Override - public List getRelevantInputs() { - return List.of(hat); - } - - @Override - public InputType type() { - return InputType.HAT; - } -} diff --git a/src/main/java/dev/isxander/controlify/bindings/input/Input.java b/src/main/java/dev/isxander/controlify/bindings/input/Input.java deleted file mode 100644 index a86b55f14..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/input/Input.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.bindings.input; - -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; -import dev.isxander.controlify.controller.input.ControllerStateView; -import net.minecraft.resources.Identifier; - -import java.util.List; - -public interface Input { - MapCodec MAP_CODEC = InputType.createCodec(InputType.TYPES, InputType::codec, Input::type, "type"); - Codec CODEC = MAP_CODEC.codec(); - - float state(ControllerStateView state); - - List getRelevantInputs(); - - InputType type(); -} diff --git a/src/main/java/dev/isxander/controlify/bindings/input/InputType.java b/src/main/java/dev/isxander/controlify/bindings/input/InputType.java deleted file mode 100644 index 5a0b56851..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/input/InputType.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings.input; - -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.codec.CExtraCodecs; -import dev.isxander.controlify.utils.codec.StrictEitherMapCodec; -import net.minecraft.util.Util; -import net.minecraft.util.ExtraCodecs; -import net.minecraft.util.StringRepresentable; -import org.jetbrains.annotations.NotNull; - -import java.util.Arrays; -import java.util.function.Function; -import java.util.stream.Stream; - -public record InputType(String id, MapCodec codec) implements StringRepresentable { - public static final InputType BUTTON = new InputType<>(ButtonInput.INPUT_ID, ButtonInput.CODEC); - public static final InputType AXIS = new InputType<>(AxisInput.INPUT_ID, AxisInput.CODEC); - public static final InputType HAT = new InputType<>(HatInput.INPUT_ID, HatInput.CODEC); - public static final InputType EMPTY = new InputType<>(EmptyInput.INPUT_ID, EmptyInput.CODEC); - - public static final InputType[] TYPES = { - InputType.BUTTON, InputType.AXIS, InputType.HAT, InputType.EMPTY - }; - - public static MapCodec createCodec( - T[] types, Function> codecGetter, Function typeGetter, String typeFieldName - ) { - MapCodec fuzzyCodec = CExtraCodecs.fuzzyMap( - Stream.of(types).map(codecGetter).toList(), - obj -> codecGetter.apply(typeGetter.apply(obj)) - ); - - - Codec typeCodec = ExtraCodecs.orCompressed( - CExtraCodecs.stringResolver( - StringRepresentable::getSerializedName, - CUtil.createNameLookup(types, Function.identity()) - ), - ExtraCodecs.idResolverCodec( - Util.createIndexLookup(Arrays.asList(types)), - i -> i >= 0 && i < types.length ? types[i] : null, - -1 - ) - ); - - MapCodec typedCodec = typeCodec.dispatchMap(typeFieldName, typeGetter, codecGetter); - MapCodec eitherCodec = new StrictEitherMapCodec<>(typeFieldName, typedCodec, fuzzyCodec, false); - - return CExtraCodecs.orCompressed(eitherCodec, typedCodec); - } - - @Override - public @NotNull String getSerializedName() { - return this.id(); - } -} diff --git a/src/main/java/dev/isxander/controlify/bindings/output/AnalogueOutput.java b/src/main/java/dev/isxander/controlify/bindings/output/AnalogueOutput.java deleted file mode 100644 index fc796b3a2..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/output/AnalogueOutput.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.bindings.output; - -public interface AnalogueOutput extends BindingOutput { - float get(); -} diff --git a/src/main/java/dev/isxander/controlify/bindings/output/BindingOutput.java b/src/main/java/dev/isxander/controlify/bindings/output/BindingOutput.java deleted file mode 100644 index 86ef48c1f..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/output/BindingOutput.java +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings.output; - -public interface BindingOutput { -} diff --git a/src/main/java/dev/isxander/controlify/bindings/output/DigitalOutput.java b/src/main/java/dev/isxander/controlify/bindings/output/DigitalOutput.java deleted file mode 100644 index 62aa2607c..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/output/DigitalOutput.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings.output; - -/** - * An interface that is responsible for consuming a binding's raw - * state and outputting a relevant value from that. - */ -public interface DigitalOutput extends BindingOutput { - boolean get(); -} diff --git a/src/main/java/dev/isxander/controlify/bindings/output/GuiPressOutput.java b/src/main/java/dev/isxander/controlify/bindings/output/GuiPressOutput.java deleted file mode 100644 index f1d4b523f..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/output/GuiPressOutput.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings.output; - -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.bindings.StateAccess; - -public class GuiPressOutput implements DigitalOutput { - private final StateAccess stateAccess; - - private PressState pressState = PressState.OFF; - - public GuiPressOutput(InputBinding binding) { - this.stateAccess = binding.createStateAccess(2, state -> push()); - } - - @Override - public boolean get() { - return pressState == PressState.JUST_RELEASED; - } - - public PressState getPressState() { - return this.pressState; - } - - private void push() { - boolean held = stateAccess.digital(0); - boolean prevHeld = stateAccess.digital(1); - - // if just pressed, set state to could press in future - // if just released: - // if it could press in future, set to just released - this is the state where get() == true - // if the state is anything else, set the state to off - - if (held) { - if (!prevHeld) { // just started pressing - pressState = PressState.COULD_PRESS_IN_FUTURE; - } - } else { - if (prevHeld && pressState == PressState.COULD_PRESS_IN_FUTURE) { // just released - pressState = PressState.JUST_RELEASED; - } else { - pressState = PressState.OFF; - } - } - - if (stateAccess.isSuppressed()) - pressState = PressState.OFF; - } - - public void onNavigate() { - this.pressState = PressState.OFF; - } - - public enum PressState { - /** Not pressed or navigated since held */ - OFF, - /** This is the state where the action takes place */ - JUST_RELEASED, - /** Currently held down with no navigations since it was pressed */ - COULD_PRESS_IN_FUTURE, - } -} diff --git a/src/main/java/dev/isxander/controlify/bindings/output/JustPressedOutput.java b/src/main/java/dev/isxander/controlify/bindings/output/JustPressedOutput.java deleted file mode 100644 index 852d7e5e2..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/output/JustPressedOutput.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings.output; - -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.bindings.StateAccess; - -public class JustPressedOutput implements DigitalOutput { - private final StateAccess stateAccess; - - public JustPressedOutput(InputBinding binding) { - this.stateAccess = binding.createStateAccess(2); - } - - @Override - public boolean get() { - if (stateAccess.isSuppressed()) - return false; - - return stateAccess.digital(0) && !stateAccess.digital(1); - } -} diff --git a/src/main/java/dev/isxander/controlify/bindings/output/JustReleasedOutput.java b/src/main/java/dev/isxander/controlify/bindings/output/JustReleasedOutput.java deleted file mode 100644 index 18545b432..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/output/JustReleasedOutput.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings.output; - -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.bindings.StateAccess; - -public class JustReleasedOutput implements DigitalOutput { - private final StateAccess stateAccess; - - public JustReleasedOutput(InputBinding binding) { - this.stateAccess = binding.createStateAccess(2); - } - - @Override - public boolean get() { - if (stateAccess.isSuppressed()) - return false; - - return !stateAccess.digital(0) && stateAccess.digital(1); - } -} diff --git a/src/main/java/dev/isxander/controlify/bindings/output/JustTappedOutput.java b/src/main/java/dev/isxander/controlify/bindings/output/JustTappedOutput.java deleted file mode 100644 index 060c25276..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/output/JustTappedOutput.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings.output; - -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.bindings.StateAccess; - -public class JustTappedOutput implements DigitalOutput { - public static final int DEFAULT_MAX_HOLD_TIME_TICKS = 2; - - private final StateAccess stateAccess; - private final int maxHoldTime; - - public JustTappedOutput(InputBinding binding, int maxHoldTimeTicks) { - this.stateAccess = binding.createStateAccess(maxHoldTimeTicks + 2); - this.maxHoldTime = maxHoldTimeTicks; - } - - public JustTappedOutput(InputBinding binding) { - this(binding, DEFAULT_MAX_HOLD_TIME_TICKS); - } - - @Override - public boolean get() { - if (stateAccess.isSuppressed()) - return false; - - boolean justReleased = stateAccess.digital(0); - if (!justReleased) return false; // still holding or just started to hold, not just tapped - - for (int i = 1; i < maxHoldTime + 2; i++) { // loop through remaining history - boolean state = stateAccess.digital(i); - if (!state) { // if we find one that isn't pressed, we know immediately a tap has taken place - return true; - } - } - - // if we reached the end of the loop without return, - // we know that the maxHoldTime has been exceeded and we did not just tap - return false; - } -} diff --git a/src/main/java/dev/isxander/controlify/bindings/output/KeyMappingEmulationOutput.java b/src/main/java/dev/isxander/controlify/bindings/output/KeyMappingEmulationOutput.java deleted file mode 100644 index c8e6b3c84..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/output/KeyMappingEmulationOutput.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings.output; - -import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.bindings.KeyMappingHandle; -import dev.isxander.controlify.bindings.StateAccess; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.utils.MinecraftUtil; -import net.minecraft.client.KeyMapping; - -import java.util.function.BooleanSupplier; - -public class KeyMappingEmulationOutput implements DigitalOutput { - private final ControllerEntity controller; - private final StateAccess stateAccess; - private final KeyMapping keyMapping; - - public KeyMappingEmulationOutput(ControllerEntity controller, InputBinding binding, KeyMapping keyMapping, BooleanSupplier toggleCondition) { - this.controller = controller; - this.stateAccess = binding.createStateAccess(2, state -> push()); - this.keyMapping = keyMapping; - - if (toggleCondition != null) { - ((KeyMappingHandle) keyMapping).controlify$addToggleCondition(controller, toggleCondition); - } - } - - @Override - public boolean get() { - throw new IllegalStateException("Should never retrieve output of key mapping emulation!"); - } - - private void push() { - boolean now = stateAccess.digital(0); - boolean prev = stateAccess.digital(1); - - if (ControlifyApi.get().getCurrentController().orElse(null) != controller) - return; // only emulate current controller - - if (MinecraftUtil.getScreen() != null) - return; // minecraft keybinds don't work in gui screens it conflicts - - KeyMappingHandle handle = (KeyMappingHandle) keyMapping; - if (now && !prev) { - handle.controlify$setPressed(true); - } else if (prev && !now) { - handle.controlify$setPressed(false); - } - } -} diff --git a/src/main/java/dev/isxander/controlify/bindings/output/SimpleAnalogueOutput.java b/src/main/java/dev/isxander/controlify/bindings/output/SimpleAnalogueOutput.java deleted file mode 100644 index 9a2d409ad..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/output/SimpleAnalogueOutput.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings.output; - -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.bindings.StateAccess; - -public class SimpleAnalogueOutput implements AnalogueOutput { - private final StateAccess stateAccess; - private final int history; - - public SimpleAnalogueOutput(InputBinding binding, int history) { - this.stateAccess = binding.createStateAccess(history + 1); - this.history = history; - } - - @Override - public float get() { - if (stateAccess.isSuppressed()) - return 0f; - - return stateAccess.analogue(history); - } -} diff --git a/src/main/java/dev/isxander/controlify/bindings/output/SimpleDigitalOutput.java b/src/main/java/dev/isxander/controlify/bindings/output/SimpleDigitalOutput.java deleted file mode 100644 index 1eafa83df..000000000 --- a/src/main/java/dev/isxander/controlify/bindings/output/SimpleDigitalOutput.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.bindings.output; - -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.bindings.StateAccess; - -public class SimpleDigitalOutput implements DigitalOutput { - private final StateAccess stateAccess; - private final int history; - - public SimpleDigitalOutput(InputBinding binding, int history) { - this.stateAccess = binding.createStateAccess(history + 1); - this.history = history; - } - - @Override - public boolean get() { - if (stateAccess.isSuppressed()) - return false; - - return stateAccess.digital(history); - } -} diff --git a/src/main/java/dev/isxander/controlify/config/dto/device/DeviceConfig.java b/src/main/java/dev/isxander/controlify/config/dto/device/DeviceConfig.java index f0c7bb87f..56b92a71f 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/device/DeviceConfig.java +++ b/src/main/java/dev/isxander/controlify/config/dto/device/DeviceConfig.java @@ -9,7 +9,7 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import dev.isxander.controlify.controller.id.ControllerType; -import dev.isxander.controlify.controller.input.mapping.ControllerMapping; +import dev.isxander.controlify.input.mapping.ControllerMapping; import net.minecraft.resources.Identifier; import java.util.Optional; diff --git a/src/main/java/dev/isxander/controlify/config/dto/profile/InputConfig.java b/src/main/java/dev/isxander/controlify/config/dto/profile/InputConfig.java index fb5fd7e32..9ec34705a 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/profile/InputConfig.java +++ b/src/main/java/dev/isxander/controlify/config/dto/profile/InputConfig.java @@ -9,7 +9,7 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import dev.isxander.controlify.ingame.InputCurves; -import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.LogicalInput; import net.minecraft.resources.Identifier; import java.util.List; diff --git a/src/main/java/dev/isxander/controlify/config/settings/device/DeviceSettings.java b/src/main/java/dev/isxander/controlify/config/settings/device/DeviceSettings.java index c255e2036..130f749d6 100644 --- a/src/main/java/dev/isxander/controlify/config/settings/device/DeviceSettings.java +++ b/src/main/java/dev/isxander/controlify/config/settings/device/DeviceSettings.java @@ -8,7 +8,7 @@ import dev.isxander.controlify.config.dto.device.DeviceConfig; import dev.isxander.controlify.controller.id.ControllerType; -import dev.isxander.controlify.controller.input.mapping.ControllerMapping; +import dev.isxander.controlify.input.mapping.ControllerMapping; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/config/settings/profile/InputSettings.java b/src/main/java/dev/isxander/controlify/config/settings/profile/InputSettings.java index c113f89ea..359ada37d 100644 --- a/src/main/java/dev/isxander/controlify/config/settings/profile/InputSettings.java +++ b/src/main/java/dev/isxander/controlify/config/settings/profile/InputSettings.java @@ -8,7 +8,7 @@ import dev.isxander.controlify.config.dto.profile.InputConfig; import dev.isxander.controlify.ingame.InputCurves; -import dev.isxander.controlify.input.logical.LogicalInput; +import dev.isxander.controlify.input.LogicalInput; import net.minecraft.resources.Identifier; import java.util.HashMap; diff --git a/src/main/java/dev/isxander/controlify/controller/ControllerEntity.java b/src/main/java/dev/isxander/controlify/controller/ControllerEntity.java index 037edbec7..becb50736 100644 --- a/src/main/java/dev/isxander/controlify/controller/ControllerEntity.java +++ b/src/main/java/dev/isxander/controlify/controller/ControllerEntity.java @@ -22,7 +22,7 @@ import dev.isxander.controlify.controller.rumble.TriggerRumbleComponent; import dev.isxander.controlify.controller.touchpad.TouchpadComponent; import dev.isxander.controlify.driver.Driver; -import dev.isxander.controlify.input.InputComponent; +import dev.isxander.controlify.controller.input.InputComponent; import dev.isxander.controlify.utils.log.ControlifyLogger; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Contract; diff --git a/src/main/java/dev/isxander/controlify/controller/dualsense/TriggerEffectInstanceImpl.java b/src/main/java/dev/isxander/controlify/controller/dualsense/TriggerEffectInstanceImpl.java index fe6bdbbef..735a5f09e 100644 --- a/src/main/java/dev/isxander/controlify/controller/dualsense/TriggerEffectInstanceImpl.java +++ b/src/main/java/dev/isxander/controlify/controller/dualsense/TriggerEffectInstanceImpl.java @@ -14,7 +14,7 @@ import dev.isxander.controlify.contextual.RuleEngine; import dev.isxander.controlify.contextual.TriggerEffectRule; import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.input.GamepadInputs; +import dev.isxander.controlify.input.mapping.GamepadInputs; import dev.isxander.controlify.driver.dualsense.DualsenseTriggerEffect; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/controller/gyro/GyroComponent.java b/src/main/java/dev/isxander/controlify/controller/gyro/GyroComponent.java index 8bfdfb87d..c8409deb9 100644 --- a/src/main/java/dev/isxander/controlify/controller/gyro/GyroComponent.java +++ b/src/main/java/dev/isxander/controlify/controller/gyro/GyroComponent.java @@ -11,11 +11,11 @@ import dev.isxander.controlify.config.settings.profile.GyroSettings; import dev.isxander.controlify.controller.ControllerEntity; import dev.isxander.controlify.controller.impl.ECSComponentImpl; -import dev.isxander.controlify.input.InputPipeline; +import dev.isxander.controlify.input.pipeline.InputPipeline; import dev.isxander.controlify.input.SensorType; -import dev.isxander.controlify.input.logical.LogicalSignal; -import dev.isxander.controlify.input.physical.PhysicalSensorSignal; -import dev.isxander.controlify.input.physical.PhysicalSignal; +import dev.isxander.controlify.input.signal.LogicalSignal; +import dev.isxander.controlify.input.signal.PhysicalSensorSignal; +import dev.isxander.controlify.input.signal.PhysicalSignal; import dev.isxander.controlify.utils.CUtil; import dev.isxander.controlify.utils.event.EventSource; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/controller/id/ControllerType.java b/src/main/java/dev/isxander/controlify/controller/id/ControllerType.java index 13ee74c1d..4b0d01146 100644 --- a/src/main/java/dev/isxander/controlify/controller/id/ControllerType.java +++ b/src/main/java/dev/isxander/controlify/controller/id/ControllerType.java @@ -11,18 +11,25 @@ import com.mojang.serialization.codecs.RecordCodecBuilder; import dev.isxander.controlify.driver.steamdeck.SteamDeckUtil; import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.utils.codec.CExtraCodecs; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; -public record ControllerType(@Nullable String friendlyName, String mappingId, Identifier namespace, boolean forceJoystick, boolean dontLoad) { - public static final ControllerType DEFAULT = new ControllerType(null, "default", CUtil.rl("default"), false, false); +public record ControllerType( + Identifier namespace, + @Nullable String friendlyName, + @Nullable Identifier mappingId, + boolean forceJoystick, + boolean preventLoading +) { + public static final ControllerType DEFAULT = new ControllerType(CUtil.rl("default"), null, null, false, false); public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - Codec.STRING.optionalFieldOf("name", null).forGetter(ControllerType::friendlyName), - Codec.STRING.optionalFieldOf("mapping", DEFAULT.mappingId()).forGetter(ControllerType::mappingId), Identifier.CODEC.optionalFieldOf("namespace", DEFAULT.namespace()).forGetter(ControllerType::namespace), + CExtraCodecs.nullableField(Codec.STRING, "name").forGetter(ControllerType::friendlyName), + CExtraCodecs.nullableField(Identifier.CODEC, "mapping").forGetter(ControllerType::mappingId), Codec.BOOL.optionalFieldOf("force_joystick", false).forGetter(ControllerType::forceJoystick), - Codec.BOOL.optionalFieldOf("dont_load", false).forGetter(ControllerType::dontLoad) + Codec.BOOL.optionalFieldOf("prevent_loading", false).forGetter(ControllerType::preventLoading) ).apply(instance, ControllerType::new)); public Identifier getIconSprite() { diff --git a/src/main/java/dev/isxander/controlify/input/InputComponent.java b/src/main/java/dev/isxander/controlify/controller/input/InputComponent.java similarity index 82% rename from src/main/java/dev/isxander/controlify/input/InputComponent.java rename to src/main/java/dev/isxander/controlify/controller/input/InputComponent.java index 6ec17e595..2a93f7b71 100644 --- a/src/main/java/dev/isxander/controlify/input/InputComponent.java +++ b/src/main/java/dev/isxander/controlify/controller/input/InputComponent.java @@ -1,29 +1,28 @@ -package dev.isxander.controlify.input; +package dev.isxander.controlify.controller.input; import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.bindings.defaults.DefaultBindProvider; +import dev.isxander.controlify.action.defaults.DefaultBindProvider; import dev.isxander.controlify.config.settings.device.DeviceSettings; import dev.isxander.controlify.config.settings.profile.InputSettings; import dev.isxander.controlify.controller.ControllerEntity; import dev.isxander.controlify.controller.impl.ECSComponentImpl; -import dev.isxander.controlify.controller.input.mapping.ControllerMapping; -import dev.isxander.controlify.input.action.ActionGraph; -import dev.isxander.controlify.input.action.ActionImpl; -import dev.isxander.controlify.input.action.ActionSpec; -import dev.isxander.controlify.input.action.ActionSpecRegistryImpl; -import dev.isxander.controlify.input.action.activator.ActivatorConfig; -import dev.isxander.controlify.input.logical.LogicalInput; -import dev.isxander.controlify.input.physical.SensorProcessorStage; +import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.input.SensorType; +import dev.isxander.controlify.input.mapping.ControllerMapping; +import dev.isxander.controlify.input.mapping.ControllerMappingManager; +import dev.isxander.controlify.action.ActionGraph; +import dev.isxander.controlify.action.ActionImpl; +import dev.isxander.controlify.action.ActionSpec; +import dev.isxander.controlify.action.ActionSpecRegistryImpl; +import dev.isxander.controlify.action.activator.ActivatorConfig; +import dev.isxander.controlify.input.pipeline.InputPipeline; +import dev.isxander.controlify.input.pipeline.SensorProcessorStage; import dev.isxander.controlify.utils.CUtil; import dev.isxander.controlify.utils.event.EventSource; import net.minecraft.resources.Identifier; -import net.minecraft.util.Util; import org.jspecify.annotations.Nullable; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; public class InputComponent extends ECSComponentImpl { public static final Identifier ID = CUtil.rl("input"); @@ -70,6 +69,12 @@ public void attach(ControllerEntity controller) { .getOrCreateDeviceSettings(controller.uid()); ControllerMapping controllerMapping = this.deviceSettings.mapping; + if (controllerMapping == null) { + ControllerMappingManager controllerMappingManager = Controlify.instance().controllerMappingManager(); + controllerMapping = controllerMappingManager + .getMappingById(controller.info().type().mappingId()) + .orElse(null); + } if (controllerMapping == null) { controllerMapping = this.isGamepad ? ControllerMapping.GAMEPAD_DEADZONES_ONLY_MAPPING @@ -80,7 +85,7 @@ public void attach(ControllerEntity controller) { this.inputPipeline.stageDeadzone().setDeadzoneGroups(controllerMapping.deadzones().values()); this.inputPipeline.stageDeadzone().setDeadzoneSupplier(axis -> settings().sensitivity.getDeadzone((Identifier) axis)); this.inputPipeline.stageSensorProcessor().setProcessors(sensorType -> { - if (sensorType.isGyroscope()) { + if (sensorType == SensorType.GYROSCOPE) { return List.of( new SensorProcessorStage.BiasCorrection(() -> this.deviceSettings.gyroCalibration.offset), new SensorProcessorStage.LowPassFilter(() -> this.controller().settings().gyro.lowPassCutoffHz) diff --git a/src/main/java/dev/isxander/controlify/controllermanager/ControllerCoordinator.java b/src/main/java/dev/isxander/controlify/controllermanager/ControllerCoordinator.java index 360e7e03c..54c2fe21e 100644 --- a/src/main/java/dev/isxander/controlify/controllermanager/ControllerCoordinator.java +++ b/src/main/java/dev/isxander/controlify/controllermanager/ControllerCoordinator.java @@ -14,7 +14,7 @@ import dev.isxander.controlify.driver.sdl.SDLNativesLoader; import dev.isxander.controlify.gui.screen.AskToMapControllerScreen; import dev.isxander.controlify.gui.screen.BluetoothWarningScreen; -import dev.isxander.controlify.input.physical.PhysicalInputSignal; +import dev.isxander.controlify.input.signal.PhysicalInputSignal; import dev.isxander.controlify.platform.main.PlatformMainUtil; import dev.isxander.controlify.utils.CUtil; import dev.isxander.controlify.utils.DebugLog; diff --git a/src/main/java/dev/isxander/controlify/controllermanager/ControllerManager.java b/src/main/java/dev/isxander/controlify/controllermanager/ControllerManager.java index e8f0811cb..46b6e8161 100644 --- a/src/main/java/dev/isxander/controlify/controllermanager/ControllerManager.java +++ b/src/main/java/dev/isxander/controlify/controllermanager/ControllerManager.java @@ -1,6 +1,5 @@ package dev.isxander.controlify.controllermanager; -import dev.isxander.controlify.Controlify; import dev.isxander.controlify.api.event.ControlifyEvents; import dev.isxander.controlify.controller.ControllerEntity; import dev.isxander.controlify.controller.id.ControllerType; @@ -33,7 +32,7 @@ public boolean canAddController( return false; } - if (controllerType.dontLoad()) { + if (controllerType.preventLoading()) { controllerLogger.debugLog("Preventing load of controller #" + ucid + " because its type prevents loading."); return false; } diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDL3GamepadDriver.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDL3GamepadDriver.java index f9b1f2f44..7f2ae3f30 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDL3GamepadDriver.java +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDL3GamepadDriver.java @@ -11,7 +11,7 @@ import dev.isxander.controlify.controller.gyro.GyroComponent; import dev.isxander.controlify.controller.*; import dev.isxander.controlify.controller.touchpad.Touchpads; -import dev.isxander.controlify.input.InputComponent; +import dev.isxander.controlify.controller.input.InputComponent; import dev.isxander.controlify.input.SensorType; import dev.isxander.controlify.utils.event.EventSource; import dev.isxander.controlify.utils.log.ControlifyLogger; diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDL3JoystickDriver.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDL3JoystickDriver.java index 4e77ffd9d..b508cac32 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDL3JoystickDriver.java +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDL3JoystickDriver.java @@ -7,9 +7,9 @@ package dev.isxander.controlify.driver.sdl; import dev.isxander.controlify.controller.id.ControllerType; -import dev.isxander.controlify.controller.input.JoystickInputs; +import dev.isxander.controlify.input.mapping.JoystickInputs; import dev.isxander.controlify.controller.*; -import dev.isxander.controlify.input.InputComponent; +import dev.isxander.controlify.controller.input.InputComponent; import dev.isxander.controlify.utils.event.EventSource; import dev.isxander.controlify.utils.log.ControlifyLogger; import dev.isxander.sdl.*; diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLCommonDriver.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDLCommonDriver.java index 0ef26ce5a..5c0120d5d 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDLCommonDriver.java +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDLCommonDriver.java @@ -24,7 +24,7 @@ import dev.isxander.controlify.driver.Driver; import dev.isxander.controlify.driver.dualsense.DualsenseEffectsState; import dev.isxander.controlify.driver.dualsense.DualsenseTriggerEffect; -import dev.isxander.controlify.input.InputPipeline; +import dev.isxander.controlify.input.pipeline.InputPipeline; import dev.isxander.controlify.rumble.RumbleState; import dev.isxander.controlify.rumble.TriggerRumbleState; import dev.isxander.controlify.utils.CUtil; diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLInputConversions.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDLInputConversions.java index 92b36ae82..dda85452f 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDLInputConversions.java +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDLInputConversions.java @@ -1,6 +1,6 @@ package dev.isxander.controlify.driver.sdl; -import dev.isxander.controlify.controller.input.GamepadInputs; +import dev.isxander.controlify.input.mapping.GamepadInputs; import dev.isxander.controlify.input.SensorType; import dev.isxander.sdl.SdlEvents; import dev.isxander.sdl.SdlGamepad; diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLToPhysicalStage.java b/src/main/java/dev/isxander/controlify/driver/sdl/SDLToPhysicalStage.java index 0b0554298..bf22dcbe5 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDLToPhysicalStage.java +++ b/src/main/java/dev/isxander/controlify/driver/sdl/SDLToPhysicalStage.java @@ -1,10 +1,10 @@ package dev.isxander.controlify.driver.sdl; -import dev.isxander.controlify.controller.input.JoystickInputs; +import dev.isxander.controlify.input.mapping.JoystickInputs; import dev.isxander.controlify.input.SensorType; -import dev.isxander.controlify.input.physical.PhysicalInputSignal; -import dev.isxander.controlify.input.physical.PhysicalSensorSignal; -import dev.isxander.controlify.input.physical.PhysicalSignal; +import dev.isxander.controlify.input.signal.PhysicalInputSignal; +import dev.isxander.controlify.input.signal.PhysicalSensorSignal; +import dev.isxander.controlify.input.signal.PhysicalSignal; import dev.isxander.controlify.utils.event.EventSink; import dev.isxander.controlify.utils.event.EventStage; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/driver/steamdeck/SteamDeckDriver.java b/src/main/java/dev/isxander/controlify/driver/steamdeck/SteamDeckDriver.java index 2c11f8cf6..021e6fc3b 100644 --- a/src/main/java/dev/isxander/controlify/driver/steamdeck/SteamDeckDriver.java +++ b/src/main/java/dev/isxander/controlify/driver/steamdeck/SteamDeckDriver.java @@ -16,7 +16,7 @@ import dev.isxander.controlify.controller.info.DriverNameComponent; import dev.isxander.controlify.controller.info.GUIDComponent; import dev.isxander.controlify.controller.info.UIDComponent; -import dev.isxander.controlify.controller.input.GamepadInputs; +import dev.isxander.controlify.input.mapping.GamepadInputs; import dev.isxander.controlify.controller.input.InputComponent; import dev.isxander.controlify.controller.steamdeck.SteamDeckComponent; import dev.isxander.controlify.controller.touchpad.TouchpadComponent; diff --git a/src/main/java/dev/isxander/controlify/gui/ButtonGuideRenderer.java b/src/main/java/dev/isxander/controlify/gui/ButtonGuideRenderer.java index 071db5fad..ac55002a1 100644 --- a/src/main/java/dev/isxander/controlify/gui/ButtonGuideRenderer.java +++ b/src/main/java/dev/isxander/controlify/gui/ButtonGuideRenderer.java @@ -7,9 +7,9 @@ package dev.isxander.controlify.gui; import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.api.bind.InputBinding; +import dev.isxander.controlify.action.Action; +import dev.isxander.controlify.action.ActionAccessor; import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate; -import dev.isxander.controlify.api.bind.InputBindingSupplier; import net.minecraft.client.Minecraft; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; @@ -23,36 +23,36 @@ public interface ButtonGuideRenderer { void controlify$setButtonGuide(RenderData renderData); - static void registerBindingForButton(T button, Supplier binding, ButtonGuidePredicate renderPredicate) { - ((ButtonGuideRenderer) button).controlify$setButtonGuide(new RenderData<>(binding, renderPredicate)); + static void registerBindingForButton(T button, Supplier action, ButtonGuidePredicate renderPredicate) { + ((ButtonGuideRenderer) button).controlify$setButtonGuide(new RenderData<>(action, renderPredicate)); } - record RenderData(Supplier binding, ButtonGuidePredicate renderPredicate) { - public Component getControllerMessage(InputBinding bind, Component actualLabel) { + record RenderData(Supplier action, ButtonGuidePredicate renderPredicate) { + public Component getControllerMessage(Action action, Component actualLabel) { var component = Component.empty(); if (!Minecraft.getInstance().font.isBidirectional()) { - component.append(bind.inputGlyph()); + component.append(action.inputGlyph()); component.append(CommonComponents.SPACE); } component.append(actualLabel); if (Minecraft.getInstance().font.isBidirectional()) { component.append(CommonComponents.SPACE); - component.append(bind.inputGlyph()); + component.append(action.inputGlyph()); } return component; } public boolean shouldRender(T renderable) { - Optional binding = getBind(); + Optional binding = getAction(); return binding.isPresent() && Controlify.instance().currentInputMode().isController() && Controlify.instance().getCurrentController().map(c -> c.settings().generic.guide.showScreenGuides).orElse(false) - && !binding.get().isUnbound() + && binding.get().boundInput() != null && renderPredicate().shouldDisplay(renderable); } - public Optional getBind() { - return Controlify.instance().getCurrentController().map(c -> binding().get().on(c)); + public Optional getAction() { + return Controlify.instance().getCurrentController().map(c -> action().get().on(c)); } } } diff --git a/src/main/java/dev/isxander/controlify/gui/controllers/Deadzone2DImageRenderer.java b/src/main/java/dev/isxander/controlify/gui/controllers/Deadzone2DImageRenderer.java index 450b28ae8..30c7eace6 100644 --- a/src/main/java/dev/isxander/controlify/gui/controllers/Deadzone2DImageRenderer.java +++ b/src/main/java/dev/isxander/controlify/gui/controllers/Deadzone2DImageRenderer.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.gui.controllers; -import dev.isxander.controlify.controller.input.DeadzoneGroup; +import dev.isxander.controlify.input.mapping.DeadzoneGroup; import dev.isxander.controlify.controller.input.InputComponent; import dev.isxander.controlify.utils.render.elements.CircleElementRenderState; import dev.isxander.yacl3.api.Option; diff --git a/src/main/java/dev/isxander/controlify/gui/screen/ControllerConfigScreenFactory.java b/src/main/java/dev/isxander/controlify/gui/screen/ControllerConfigScreenFactory.java index 26a31aaff..3473aa7d5 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/ControllerConfigScreenFactory.java +++ b/src/main/java/dev/isxander/controlify/gui/screen/ControllerConfigScreenFactory.java @@ -21,9 +21,9 @@ import dev.isxander.controlify.controller.*; import dev.isxander.controlify.controller.gyro.GyroButtonMode; import dev.isxander.controlify.controller.gyro.GyroYawMode; -import dev.isxander.controlify.controller.input.DeadzoneGroup; +import dev.isxander.controlify.input.mapping.DeadzoneGroup; import dev.isxander.controlify.controller.input.InputComponent; -import dev.isxander.controlify.controller.input.Inputs; +import dev.isxander.controlify.input.mapping.Inputs; import dev.isxander.controlify.controller.rumble.RumbleComponent; import dev.isxander.controlify.gui.controllers.BindController; import dev.isxander.controlify.gui.controllers.Deadzone2DImageRenderer; diff --git a/src/main/java/dev/isxander/controlify/gui/screen/ControllerMappingMakerScreen.java b/src/main/java/dev/isxander/controlify/gui/screen/ControllerMappingMakerScreen.java index facfb4f6f..8e080af83 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/ControllerMappingMakerScreen.java +++ b/src/main/java/dev/isxander/controlify/gui/screen/ControllerMappingMakerScreen.java @@ -9,10 +9,8 @@ import dev.isxander.controlify.Controlify; import dev.isxander.controlify.config.settings.device.DeviceSettings; import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.input.*; -import dev.isxander.controlify.controller.input.mapping.MapType; -import dev.isxander.controlify.controller.input.mapping.MappingEntry; -import dev.isxander.controlify.controller.input.mapping.ControllerMapping; +import dev.isxander.controlify.controller.input.mapping.*; +import dev.isxander.controlify.input.mapping.*; import dev.isxander.controlify.screenop.ScreenControllerEventListener; import dev.isxander.controlify.screenop.ScreenProcessor; import dev.isxander.controlify.screenop.ScreenProcessorProvider; diff --git a/src/main/java/dev/isxander/controlify/gui/screen/RadialItems.java b/src/main/java/dev/isxander/controlify/gui/screen/RadialItems.java index 477c5d82b..f910ed596 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/RadialItems.java +++ b/src/main/java/dev/isxander/controlify/gui/screen/RadialItems.java @@ -9,9 +9,9 @@ import dev.isxander.controlify.Controlify; import dev.isxander.controlify.api.bind.InputBinding; import dev.isxander.controlify.api.bind.InputBindingSupplier; -import dev.isxander.controlify.api.bind.RadialIcon; +import dev.isxander.controlify.action.radial.RadialIcon; import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.bindings.RadialIconManager; +import dev.isxander.controlify.action.radial.RadialIconManager; import dev.isxander.controlify.controller.ControllerEntity; import dev.isxander.controlify.utils.CUtil; import net.minecraft.client.Minecraft; 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 ab306dfcc..1ce96d25a 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuEditScreen.java +++ b/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuEditScreen.java @@ -9,12 +9,11 @@ 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; +import dev.isxander.controlify.action.radial.RadialIcon; import dev.isxander.controlify.api.buttonguide.ButtonGuideApi; import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate; import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.bindings.RadialIconExtractor; -import dev.isxander.controlify.bindings.RadialIconManager; +import dev.isxander.controlify.action.radial.RadialIconExtractor; import dev.isxander.controlify.controller.ControllerEntity; import dev.isxander.controlify.controller.haptic.HapticEffects; import dev.isxander.controlify.screenop.ScreenProcessor; diff --git a/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuScreen.java b/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuScreen.java index 838ce9814..0e07e4b47 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuScreen.java +++ b/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuScreen.java @@ -7,9 +7,9 @@ package dev.isxander.controlify.gui.screen; import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.api.bind.RadialIcon; +import dev.isxander.controlify.action.radial.RadialIcon; import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.bindings.RadialIconExtractor; +import dev.isxander.controlify.action.radial.RadialIconExtractor; import dev.isxander.controlify.controller.ControllerEntity; import dev.isxander.controlify.controller.haptic.HapticEffects; import dev.isxander.controlify.gui.screen.RadialItems.RadialPage; diff --git a/src/main/java/dev/isxander/controlify/input/logical/LogicalInput.java b/src/main/java/dev/isxander/controlify/input/LogicalInput.java similarity index 94% rename from src/main/java/dev/isxander/controlify/input/logical/LogicalInput.java rename to src/main/java/dev/isxander/controlify/input/LogicalInput.java index 7b17ff27b..bdc48e56a 100644 --- a/src/main/java/dev/isxander/controlify/input/logical/LogicalInput.java +++ b/src/main/java/dev/isxander/controlify/input/LogicalInput.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.input.logical; +package dev.isxander.controlify.input; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; @@ -8,7 +8,7 @@ import java.util.List; import java.util.Set; -/// A reference to a logical button, axis, or sensor. +/// A reference to a logical button or axis. /// /// A logical input's domain is the type of input, meaning that /// a logical input that refers to the same physical inputs can be diff --git a/src/main/java/dev/isxander/controlify/input/action/activator/NoopActivator.java b/src/main/java/dev/isxander/controlify/input/action/activator/NoopActivator.java deleted file mode 100644 index dde0bf7e6..000000000 --- a/src/main/java/dev/isxander/controlify/input/action/activator/NoopActivator.java +++ /dev/null @@ -1,23 +0,0 @@ -package dev.isxander.controlify.input.action.activator; - -import dev.isxander.controlify.input.action.Accumulator; -import dev.isxander.controlify.input.action.Activator; -import dev.isxander.controlify.input.logical.LogicalInput; -import dev.isxander.controlify.input.logical.LogicalSignal; - -public class NoopActivator implements Activator { - @Override - public void onSignal(LogicalSignal signal, Accumulator acc) { - - } - - @Override - public LogicalInput boundInput() { - return null; - } - - @Override - public String describe() { - return "Noop"; - } -} diff --git a/src/main/java/dev/isxander/controlify/controller/input/mapping/ControllerMapping.java b/src/main/java/dev/isxander/controlify/input/mapping/ControllerMapping.java similarity index 93% rename from src/main/java/dev/isxander/controlify/controller/input/mapping/ControllerMapping.java rename to src/main/java/dev/isxander/controlify/input/mapping/ControllerMapping.java index f5ecff6cd..8559df0c9 100644 --- a/src/main/java/dev/isxander/controlify/controller/input/mapping/ControllerMapping.java +++ b/src/main/java/dev/isxander/controlify/input/mapping/ControllerMapping.java @@ -4,13 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.input.mapping; +package dev.isxander.controlify.input.mapping; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.controller.input.DeadzoneGroup; -import dev.isxander.controlify.controller.input.GamepadInputs; import net.minecraft.resources.Identifier; import java.util.*; diff --git a/src/main/java/dev/isxander/controlify/input/mapping/ControllerMappingManager.java b/src/main/java/dev/isxander/controlify/input/mapping/ControllerMappingManager.java new file mode 100644 index 000000000..6224bdd7b --- /dev/null +++ b/src/main/java/dev/isxander/controlify/input/mapping/ControllerMappingManager.java @@ -0,0 +1,70 @@ +package dev.isxander.controlify.input.mapping; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.JsonOps; +import dev.isxander.controlify.platform.client.resource.SimpleControlifyReloadListener; +import dev.isxander.controlify.utils.CUtil; +import net.minecraft.resources.FileToIdConverter; +import net.minecraft.resources.Identifier; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.util.Util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +public class ControllerMappingManager implements SimpleControlifyReloadListener { + + private static final FileToIdConverter converter = FileToIdConverter.json("controller/mapping"); + + private final Map mappingById = new HashMap<>(); + + public Optional getMappingById(Identifier id) { + return Optional.ofNullable(this.mappingById.get(id)); + } + + @Override + public CompletableFuture load(ResourceManager manager, Executor executor) { + return CompletableFuture.supplyAsync(() -> { + Map resources = converter.listMatchingResources(manager); + Map mappingById = resources.entrySet().stream() + .map(entry -> Map.entry(entry.getKey(), parseFile(entry.getValue()))) + .collect(Util.toMap()); + + return new Preparations(mappingById); + }, executor); + } + + private static ControllerMapping parseFile(Resource resource) { + try (BufferedReader reader = resource.openAsReader()) { + JsonElement json = JsonParser.parseReader(reader); + DataResult mappingResult = ControllerMapping.CODEC.parse(JsonOps.INSTANCE, json); + return mappingResult.getOrThrow(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture apply(Preparations data, ResourceManager manager, Executor executor) { + return CompletableFuture.runAsync(() -> { + this.mappingById.clear(); + this.mappingById.putAll(data.mappingById); + }, executor); + } + + @Override + public Identifier getReloadId() { + return CUtil.rl("controller_mapping"); + } + + public record Preparations(Map mappingById) { + } +} diff --git a/src/main/java/dev/isxander/controlify/controller/input/DeadzoneGroup.java b/src/main/java/dev/isxander/controlify/input/mapping/DeadzoneGroup.java similarity index 92% rename from src/main/java/dev/isxander/controlify/controller/input/DeadzoneGroup.java rename to src/main/java/dev/isxander/controlify/input/mapping/DeadzoneGroup.java index 773aef561..7cbbb3cb1 100644 --- a/src/main/java/dev/isxander/controlify/controller/input/DeadzoneGroup.java +++ b/src/main/java/dev/isxander/controlify/input/mapping/DeadzoneGroup.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.input; +package dev.isxander.controlify.input.mapping; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; diff --git a/src/main/java/dev/isxander/controlify/controller/input/GamepadInputs.java b/src/main/java/dev/isxander/controlify/input/mapping/GamepadInputs.java similarity index 87% rename from src/main/java/dev/isxander/controlify/controller/input/GamepadInputs.java rename to src/main/java/dev/isxander/controlify/input/mapping/GamepadInputs.java index 71bf9c65b..bd654e24d 100644 --- a/src/main/java/dev/isxander/controlify/controller/input/GamepadInputs.java +++ b/src/main/java/dev/isxander/controlify/input/mapping/GamepadInputs.java @@ -4,12 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.input; +package dev.isxander.controlify.input.mapping; import com.google.common.collect.Sets; -import dev.isxander.controlify.bindings.input.AxisInput; -import dev.isxander.controlify.bindings.input.ButtonInput; -import dev.isxander.controlify.bindings.input.Input; import dev.isxander.controlify.utils.CUtil; import net.minecraft.resources.Identifier; @@ -84,15 +81,6 @@ private GamepadInputs() { )) )); - public static Input getBind(Identifier id) { - return switch (id.getPath().split("/")[0]) { - case "button" -> new ButtonInput(id); - case "axis" -> new AxisInput(id); - case "hat" -> throw new IllegalArgumentException("Gamepad does not have hat inputs."); - default -> throw new IllegalArgumentException("Unknown bind type: " + id); - }; - } - private static Identifier button(String id) { return CUtil.rl("button/" + id); } diff --git a/src/main/java/dev/isxander/controlify/input/physical/GestureInputs.java b/src/main/java/dev/isxander/controlify/input/mapping/GestureInputs.java similarity index 82% rename from src/main/java/dev/isxander/controlify/input/physical/GestureInputs.java rename to src/main/java/dev/isxander/controlify/input/mapping/GestureInputs.java index 939cb4ba0..66fcfb7cf 100644 --- a/src/main/java/dev/isxander/controlify/input/physical/GestureInputs.java +++ b/src/main/java/dev/isxander/controlify/input/mapping/GestureInputs.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.input.physical; +package dev.isxander.controlify.input.mapping; import dev.isxander.controlify.utils.CUtil; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/controller/input/Inputs.java b/src/main/java/dev/isxander/controlify/input/mapping/Inputs.java similarity index 94% rename from src/main/java/dev/isxander/controlify/controller/input/Inputs.java rename to src/main/java/dev/isxander/controlify/input/mapping/Inputs.java index 344672c9b..beccfc754 100644 --- a/src/main/java/dev/isxander/controlify/controller/input/Inputs.java +++ b/src/main/java/dev/isxander/controlify/input/mapping/Inputs.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.input; +package dev.isxander.controlify.input.mapping; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; diff --git a/src/main/java/dev/isxander/controlify/controller/input/JoystickInputs.java b/src/main/java/dev/isxander/controlify/input/mapping/JoystickInputs.java similarity index 95% rename from src/main/java/dev/isxander/controlify/controller/input/JoystickInputs.java rename to src/main/java/dev/isxander/controlify/input/mapping/JoystickInputs.java index a39990162..8fbf51cb8 100644 --- a/src/main/java/dev/isxander/controlify/controller/input/JoystickInputs.java +++ b/src/main/java/dev/isxander/controlify/input/mapping/JoystickInputs.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.input; +package dev.isxander.controlify.input.mapping; import dev.isxander.controlify.utils.CUtil; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/controller/input/mapping/MapType.java b/src/main/java/dev/isxander/controlify/input/mapping/MapType.java similarity index 89% rename from src/main/java/dev/isxander/controlify/controller/input/mapping/MapType.java rename to src/main/java/dev/isxander/controlify/input/mapping/MapType.java index 17e82057e..f5fbbddcd 100644 --- a/src/main/java/dev/isxander/controlify/controller/input/mapping/MapType.java +++ b/src/main/java/dev/isxander/controlify/input/mapping/MapType.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.input.mapping; +package dev.isxander.controlify.input.mapping; import com.mojang.serialization.Codec; import net.minecraft.util.StringRepresentable; diff --git a/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntry.java b/src/main/java/dev/isxander/controlify/input/mapping/MappingEntry.java similarity index 96% rename from src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntry.java rename to src/main/java/dev/isxander/controlify/input/mapping/MappingEntry.java index 2338302fe..845b81f6b 100644 --- a/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntry.java +++ b/src/main/java/dev/isxander/controlify/input/mapping/MappingEntry.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.input.mapping; +package dev.isxander.controlify.input.mapping; import com.mojang.serialization.Codec; -import dev.isxander.controlify.input.physical.PhysicalInputSignal; +import dev.isxander.controlify.input.signal.PhysicalInputSignal; import dev.isxander.controlify.utils.MthExt; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntryCodecs.java b/src/main/java/dev/isxander/controlify/input/mapping/MappingEntryCodecs.java similarity index 98% rename from src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntryCodecs.java rename to src/main/java/dev/isxander/controlify/input/mapping/MappingEntryCodecs.java index 804633966..94c6ca3f2 100644 --- a/src/main/java/dev/isxander/controlify/controller/input/mapping/MappingEntryCodecs.java +++ b/src/main/java/dev/isxander/controlify/input/mapping/MappingEntryCodecs.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.input.mapping; +package dev.isxander.controlify.input.mapping; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; diff --git a/src/main/java/dev/isxander/controlify/input/physical/DeadzoneStage.java b/src/main/java/dev/isxander/controlify/input/pipeline/DeadzoneStage.java similarity index 93% rename from src/main/java/dev/isxander/controlify/input/physical/DeadzoneStage.java rename to src/main/java/dev/isxander/controlify/input/pipeline/DeadzoneStage.java index 23bce96cb..26020acb4 100644 --- a/src/main/java/dev/isxander/controlify/input/physical/DeadzoneStage.java +++ b/src/main/java/dev/isxander/controlify/input/pipeline/DeadzoneStage.java @@ -1,7 +1,8 @@ -package dev.isxander.controlify.input.physical; +package dev.isxander.controlify.input.pipeline; -import dev.isxander.controlify.controller.input.DeadzoneGroup; -import dev.isxander.controlify.utils.ControllerUtils; +import dev.isxander.controlify.input.mapping.DeadzoneGroup; +import dev.isxander.controlify.input.signal.PhysicalInputSignal; +import dev.isxander.controlify.input.signal.PhysicalSignal; import dev.isxander.controlify.utils.event.EventSink; import dev.isxander.controlify.utils.event.UnaryEventStage; import it.unimi.dsi.fastutil.objects.*; diff --git a/src/main/java/dev/isxander/controlify/input/physical/GestureRecogniserStage.java b/src/main/java/dev/isxander/controlify/input/pipeline/GestureRecogniserStage.java similarity index 63% rename from src/main/java/dev/isxander/controlify/input/physical/GestureRecogniserStage.java rename to src/main/java/dev/isxander/controlify/input/pipeline/GestureRecogniserStage.java index 36b9c320f..ec478d8f7 100644 --- a/src/main/java/dev/isxander/controlify/input/physical/GestureRecogniserStage.java +++ b/src/main/java/dev/isxander/controlify/input/pipeline/GestureRecogniserStage.java @@ -1,10 +1,13 @@ -package dev.isxander.controlify.input.physical; +package dev.isxander.controlify.input.pipeline; +import dev.isxander.controlify.input.mapping.GestureInputs; +import dev.isxander.controlify.input.signal.PhysicalSignal; +import dev.isxander.controlify.input.signal.PhysicalInputSignal; import dev.isxander.controlify.utils.event.EventSink; import dev.isxander.controlify.utils.event.UnaryEventStage; /// Processes sensor data and recognizes distinct gestures such as shaking of the controller. -/// This stage then emits {@link dev.isxander.controlify.input.physical.PhysicalInputSignal.ButtonChanged} events +/// This stage then emits {@link PhysicalInputSignal.ButtonChanged} events /// for when the gesture begins and ends using input identifiers from {@link GestureInputs}. public class GestureRecogniserStage implements UnaryEventStage { @Override diff --git a/src/main/java/dev/isxander/controlify/input/physical/InputDomainExpansionStage.java b/src/main/java/dev/isxander/controlify/input/pipeline/InputDomainExpansionStage.java similarity index 90% rename from src/main/java/dev/isxander/controlify/input/physical/InputDomainExpansionStage.java rename to src/main/java/dev/isxander/controlify/input/pipeline/InputDomainExpansionStage.java index d41e478d9..4b94ca556 100644 --- a/src/main/java/dev/isxander/controlify/input/physical/InputDomainExpansionStage.java +++ b/src/main/java/dev/isxander/controlify/input/pipeline/InputDomainExpansionStage.java @@ -1,5 +1,7 @@ -package dev.isxander.controlify.input.physical; +package dev.isxander.controlify.input.pipeline; +import dev.isxander.controlify.input.signal.PhysicalInputSignal; +import dev.isxander.controlify.input.signal.PhysicalSignal; import dev.isxander.controlify.utils.event.EventSink; import dev.isxander.controlify.utils.event.UnaryEventStage; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/input/InputPipeline.java b/src/main/java/dev/isxander/controlify/input/pipeline/InputPipeline.java similarity index 89% rename from src/main/java/dev/isxander/controlify/input/InputPipeline.java rename to src/main/java/dev/isxander/controlify/input/pipeline/InputPipeline.java index 252f1ed78..764b924d3 100644 --- a/src/main/java/dev/isxander/controlify/input/InputPipeline.java +++ b/src/main/java/dev/isxander/controlify/input/pipeline/InputPipeline.java @@ -1,10 +1,8 @@ -package dev.isxander.controlify.input; +package dev.isxander.controlify.input.pipeline; -import dev.isxander.controlify.controller.input.mapping.ControllerMapping; -import dev.isxander.controlify.input.logical.LogicalSignal; -import dev.isxander.controlify.input.logical.PhysicalToLogicalStage; -import dev.isxander.controlify.input.logical.SensorAccumulatorStage; -import dev.isxander.controlify.input.physical.*; +import dev.isxander.controlify.input.mapping.ControllerMapping; +import dev.isxander.controlify.input.signal.LogicalSignal; +import dev.isxander.controlify.input.signal.PhysicalSignal; import dev.isxander.controlify.utils.event.EventSink; import dev.isxander.controlify.utils.event.EventSource; import dev.isxander.controlify.utils.event.EventTap; diff --git a/src/main/java/dev/isxander/controlify/input/physical/MappingStage.java b/src/main/java/dev/isxander/controlify/input/pipeline/MappingStage.java similarity index 78% rename from src/main/java/dev/isxander/controlify/input/physical/MappingStage.java rename to src/main/java/dev/isxander/controlify/input/pipeline/MappingStage.java index b929ec49e..38d83fcba 100644 --- a/src/main/java/dev/isxander/controlify/input/physical/MappingStage.java +++ b/src/main/java/dev/isxander/controlify/input/pipeline/MappingStage.java @@ -1,7 +1,9 @@ -package dev.isxander.controlify.input.physical; +package dev.isxander.controlify.input.pipeline; -import dev.isxander.controlify.controller.input.mapping.ControllerMapping; -import dev.isxander.controlify.controller.input.mapping.MappingEntry; +import dev.isxander.controlify.input.mapping.ControllerMapping; +import dev.isxander.controlify.input.mapping.MappingEntry; +import dev.isxander.controlify.input.signal.PhysicalInputSignal; +import dev.isxander.controlify.input.signal.PhysicalSignal; import dev.isxander.controlify.utils.event.EventSink; import dev.isxander.controlify.utils.event.UnaryEventStage; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/input/logical/PhysicalToLogicalStage.java b/src/main/java/dev/isxander/controlify/input/pipeline/PhysicalToLogicalStage.java similarity index 95% rename from src/main/java/dev/isxander/controlify/input/logical/PhysicalToLogicalStage.java rename to src/main/java/dev/isxander/controlify/input/pipeline/PhysicalToLogicalStage.java index b83e7ee0f..385d2d0bd 100644 --- a/src/main/java/dev/isxander/controlify/input/logical/PhysicalToLogicalStage.java +++ b/src/main/java/dev/isxander/controlify/input/pipeline/PhysicalToLogicalStage.java @@ -1,8 +1,10 @@ -package dev.isxander.controlify.input.logical; +package dev.isxander.controlify.input.pipeline; -import dev.isxander.controlify.input.physical.PhysicalInputSignal; -import dev.isxander.controlify.input.physical.PhysicalSensorSignal; -import dev.isxander.controlify.input.physical.PhysicalSignal; +import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.input.signal.LogicalSignal; +import dev.isxander.controlify.input.signal.PhysicalInputSignal; +import dev.isxander.controlify.input.signal.PhysicalSensorSignal; +import dev.isxander.controlify.input.signal.PhysicalSignal; import dev.isxander.controlify.utils.event.EventSink; import dev.isxander.controlify.utils.event.EventStage; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/input/logical/SensorAccumulatorStage.java b/src/main/java/dev/isxander/controlify/input/pipeline/SensorAccumulatorStage.java similarity index 92% rename from src/main/java/dev/isxander/controlify/input/logical/SensorAccumulatorStage.java rename to src/main/java/dev/isxander/controlify/input/pipeline/SensorAccumulatorStage.java index 6d5ca6083..24fb9310f 100644 --- a/src/main/java/dev/isxander/controlify/input/logical/SensorAccumulatorStage.java +++ b/src/main/java/dev/isxander/controlify/input/pipeline/SensorAccumulatorStage.java @@ -1,8 +1,9 @@ -package dev.isxander.controlify.input.logical; +package dev.isxander.controlify.input.pipeline; import dev.isxander.controlify.input.SensorType; -import dev.isxander.controlify.input.physical.PhysicalSensorSignal; -import dev.isxander.controlify.input.physical.PhysicalSignal; +import dev.isxander.controlify.input.signal.LogicalSignal; +import dev.isxander.controlify.input.signal.PhysicalSensorSignal; +import dev.isxander.controlify.input.signal.PhysicalSignal; import dev.isxander.controlify.utils.event.EventSink; import dev.isxander.controlify.utils.event.EventStage; diff --git a/src/main/java/dev/isxander/controlify/input/physical/SensorProcessorStage.java b/src/main/java/dev/isxander/controlify/input/pipeline/SensorProcessorStage.java similarity index 95% rename from src/main/java/dev/isxander/controlify/input/physical/SensorProcessorStage.java rename to src/main/java/dev/isxander/controlify/input/pipeline/SensorProcessorStage.java index c90e7c733..4ec21c7d5 100644 --- a/src/main/java/dev/isxander/controlify/input/physical/SensorProcessorStage.java +++ b/src/main/java/dev/isxander/controlify/input/pipeline/SensorProcessorStage.java @@ -1,6 +1,8 @@ -package dev.isxander.controlify.input.physical; +package dev.isxander.controlify.input.pipeline; import dev.isxander.controlify.input.SensorType; +import dev.isxander.controlify.input.signal.PhysicalSensorSignal; +import dev.isxander.controlify.input.signal.PhysicalSignal; import dev.isxander.controlify.utils.event.EventSink; import dev.isxander.controlify.utils.event.UnaryEventStage; import org.joml.Vector3f; diff --git a/src/main/java/dev/isxander/controlify/input/logical/LogicalSignal.java b/src/main/java/dev/isxander/controlify/input/signal/LogicalSignal.java similarity index 91% rename from src/main/java/dev/isxander/controlify/input/logical/LogicalSignal.java rename to src/main/java/dev/isxander/controlify/input/signal/LogicalSignal.java index 8c3a4e871..233d4c1a4 100644 --- a/src/main/java/dev/isxander/controlify/input/logical/LogicalSignal.java +++ b/src/main/java/dev/isxander/controlify/input/signal/LogicalSignal.java @@ -1,6 +1,8 @@ -package dev.isxander.controlify.input.logical; +package dev.isxander.controlify.input.signal; +import dev.isxander.controlify.input.LogicalInput; import dev.isxander.controlify.input.SensorType; +import dev.isxander.controlify.input.pipeline.SensorAccumulatorStage; public sealed interface LogicalSignal { /// The time this signal occurred. diff --git a/src/main/java/dev/isxander/controlify/input/physical/PhysicalInputSignal.java b/src/main/java/dev/isxander/controlify/input/signal/PhysicalInputSignal.java similarity index 94% rename from src/main/java/dev/isxander/controlify/input/physical/PhysicalInputSignal.java rename to src/main/java/dev/isxander/controlify/input/signal/PhysicalInputSignal.java index aa6b8d064..aaf72386d 100644 --- a/src/main/java/dev/isxander/controlify/input/physical/PhysicalInputSignal.java +++ b/src/main/java/dev/isxander/controlify/input/signal/PhysicalInputSignal.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.input.physical; +package dev.isxander.controlify.input.signal; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/input/physical/PhysicalSensorSignal.java b/src/main/java/dev/isxander/controlify/input/signal/PhysicalSensorSignal.java similarity index 94% rename from src/main/java/dev/isxander/controlify/input/physical/PhysicalSensorSignal.java rename to src/main/java/dev/isxander/controlify/input/signal/PhysicalSensorSignal.java index f4af8d8d8..69367f56f 100644 --- a/src/main/java/dev/isxander/controlify/input/physical/PhysicalSensorSignal.java +++ b/src/main/java/dev/isxander/controlify/input/signal/PhysicalSensorSignal.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.input.physical; +package dev.isxander.controlify.input.signal; import dev.isxander.controlify.input.SensorType; diff --git a/src/main/java/dev/isxander/controlify/input/physical/PhysicalSignal.java b/src/main/java/dev/isxander/controlify/input/signal/PhysicalSignal.java similarity index 86% rename from src/main/java/dev/isxander/controlify/input/physical/PhysicalSignal.java rename to src/main/java/dev/isxander/controlify/input/signal/PhysicalSignal.java index 5657f8dc5..10ec1a5cc 100644 --- a/src/main/java/dev/isxander/controlify/input/physical/PhysicalSignal.java +++ b/src/main/java/dev/isxander/controlify/input/signal/PhysicalSignal.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.input.physical; +package dev.isxander.controlify.input.signal; public sealed interface PhysicalSignal permits PhysicalInputSignal, PhysicalSensorSignal, PhysicalSignal.Frame { /// timestamp of the signal in nanoseconds diff --git a/src/main/java/dev/isxander/controlify/screenop/ScreenProcessor.java b/src/main/java/dev/isxander/controlify/screenop/ScreenProcessor.java index ac04de7df..330fc61e0 100644 --- a/src/main/java/dev/isxander/controlify/screenop/ScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/screenop/ScreenProcessor.java @@ -14,7 +14,7 @@ import dev.isxander.controlify.controller.*; import dev.isxander.controlify.controller.haptic.HapticEffects; import dev.isxander.controlify.controller.input.ControllerStateView; -import dev.isxander.controlify.controller.input.GamepadInputs; +import dev.isxander.controlify.input.mapping.GamepadInputs; import dev.isxander.controlify.controller.input.InputComponent; import dev.isxander.controlify.mixins.feature.screenop.ScreenAccessor; import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.TabNavigationBarAccessor; diff --git a/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseHandler.java b/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseHandler.java index 65b58dc37..b5409039c 100644 --- a/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseHandler.java +++ b/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseHandler.java @@ -15,7 +15,7 @@ import dev.isxander.controlify.bindings.ControlifyBindings; import dev.isxander.controlify.api.bind.InputBinding; import dev.isxander.controlify.controller.*; -import dev.isxander.controlify.controller.input.GamepadInputs; +import dev.isxander.controlify.input.mapping.GamepadInputs; import dev.isxander.controlify.controller.input.InputComponent; import dev.isxander.controlify.controller.touchpad.TouchpadComponent; import dev.isxander.controlify.debug.DebugProperties; From 92928d660a2b41b14e8a4a1a87c478a1dc9a0657 Mon Sep 17 00:00:00 2001 From: isxander Date: Mon, 19 Oct 2026 16:38:26 +0100 Subject: [PATCH 11/44] repackage everything into api and impl packages; rewrite rumble api --- .../fabric/ControlifyBootstrap.java | 8 +- .../FabricCompatMixinPlatform.java | 2 +- .../compatibility/ModMenuIntegration.java | 2 +- .../platform/FabricBackedEventHandler.java | 2 +- .../platform/FabricPlatformMainImpl.java | 16 +- .../client/FAPIApiCreativeTabHelper.java | 2 +- .../client/FabricPlatformClientImpl.java | 16 +- .../platform/network/C2SNetworkApiFabric.java | 2 +- .../platform/network/S2CNetworkApiFabric.java | 2 +- .../gametest/framework/CTestUtil.java | 11 +- .../controller/ControlifyGameTestContext.java | 23 +- .../controller/DualsenseEffectDriver.java | 2 +- .../controller/TestControllerContext.java | 12 +- .../controller/VirtualControllerBuilder.java | 2 +- .../mixin/ControlifySettingsAccessor.java | 8 +- .../mixin/SDLControllerManagerAccessor.java | 2 +- .../mixin/SDLControllerManagerMixin.java | 16 +- .../gametest/tests/AdaptiveTriggerTests.java | 10 +- .../gametest/tests/ButtonGuideTests.java | 10 +- .../gametest/tests/KeyboardLayoutTests.java | 4 +- .../gametest/tests/ManyHotplugTests.java | 2 +- .../tests/RecipeBookNavigationTests.java | 10 +- .../tests/ResetControlifySettingsTests.java | 16 +- .../gametest/tests/ServerPolicyTests.java | 7 +- .../tests/VirtualControllerInputTests.java | 12 +- .../controlify/action/ActionSpecRegistry.java | 5 - .../action/activator/NoopActivator.java | 23 - .../controlify/api/ControlifyApi.java | 27 +- .../api/{event => }/ControlifyEvents.java | 17 +- .../controlify/{ => api}/InputMode.java | 2 +- .../controlify/{ => api}/action/Action.java | 7 +- .../{ => api}/action/ActionSpec.java | 11 +- .../controlify/{ => api}/action/Channel.java | 2 +- .../controlify/api/action/package-info.java | 4 + .../api/contextual/ContainerContext.java | 4 +- .../controlify/api/contextual/Context.java | 4 +- .../ContextualStateContributor.java | 2 +- .../api/contextual/InGameContext.java | 9 +- .../api/contextual/TriggerEffectInstance.java | 2 +- .../api/contextual/package-info.java | 4 + .../controlify/api/controller/Controller.java | 40 + .../api/controller/package-info.java | 4 + .../rumble/ContinuousRumbleEffect.java | 34 + .../controller/rumble/FiniteRumbleEffect.java | 27 + .../controller/rumble/RumbleComponent.java | 11 + .../api/controller/rumble/RumbleEffect.java | 28 + .../rumble/RumbleEffectInstance.java | 26 + .../controller}/rumble/RumbleState.java | 2 +- .../rumble/TriggerRumbleState.java | 2 +- .../api/controller/rumble/package-info.java | 4 + .../{buttonguide => gui}/ButtonGuideApi.java | 22 +- .../ButtonGuidePredicate.java | 11 +- .../controlify/api/gui/package-info.java | 4 + .../{ => api}/input/LogicalInput.java | 4 +- .../controlify/api/input/package-info.java | 4 + .../isxander/controlify/api/package-info.java | 4 + .../controller/ControllerEntity.java | 176 --- .../controlify/controller/ECSComponent.java | 21 - .../controlify/controller/ECSEntity.java | 22 - .../controller/SingleValueComponent.java | 28 - .../controlify/controller/gyro/GyroState.java | 59 - .../controller/gyro/GyroStateC.java | 36 - .../controller/impl/ECSComponentImpl.java | 23 - .../controller/impl/ECSEntityImpl.java | 45 - .../controller/info/ControllerInfo.java | 14 - .../controller/info/DriverNameComponent.java | 19 - .../controller/info/UIDComponent.java | 19 - .../controller/rumble/RumbleComponent.java | 57 - .../steamdeck/SteamDeckComponent.java | 20 - .../controlify/{ => impl}/Controlify.java | 124 +- .../{ => impl}/action/Accumulator.java | 2 +- .../{ => impl}/action/ActionAccessor.java | 11 +- .../{ => impl}/action/ActionAccessorImpl.java | 5 +- .../{ => impl}/action/ActionContext.java | 2 +- .../{ => impl}/action/ActionGraph.java | 8 +- .../{ => impl}/action/ActionImpl.java | 11 +- .../{ => impl}/action/ActionSpecBuilder.java | 7 +- .../action/ActionSpecBuilderImpl.java | 9 +- .../impl/action/ActionSpecRegistry.java | 7 + .../action/ActionSpecRegistryImpl.java | 4 +- .../{ => impl}/action/ActionState.java | 4 +- .../{ => impl}/action/Activator.java | 7 +- .../action/activator/ActivatorConfig.java | 6 +- .../activator/ActivatorConfigManager.java | 12 +- .../action/activator/ActivatorConfigType.java | 4 +- .../action/activator/CompoundActivator.java | 10 +- .../action/activator/ContinuousActivator.java | 10 +- .../activator/DoubleTapPulseActivator.java | 10 +- .../activator/GuiPressPulseActivator.java | 10 +- .../action/activator/HoldLatchActivator.java | 10 +- .../action/activator/HoldPulseActivator.java | 10 +- .../impl/action/activator/NoopActivator.java | 23 + .../action/activator/PressPulseActivator.java | 10 +- .../activator/ReleasePulseActivator.java | 10 +- .../activator/RepeatLatchPulseActivator.java | 10 +- .../action/activator/TapPulseActivator.java | 10 +- .../activator/ToggleLatchActivator.java | 10 +- .../action/defaults/DefaultBindManager.java | 20 +- .../action/defaults/DefaultBindProvider.java | 4 +- .../defaults/LayeredDefaultBindProvider.java | 4 +- .../MapBackedDefaultBindProvider.java | 6 +- .../{ => impl}/action/radial/RadialIcon.java | 2 +- .../action/radial/RadialIconExtractor.java | 2 +- .../action/radial/RadialIconManager.java | 10 +- .../api/entrypoint/ControlifyEntrypoint.java | 2 +- .../api/entrypoint/InitContext.java | 2 +- .../api/entrypoint/PreInitContext.java | 4 +- .../api/ingameinput/LookInputModifier.java | 12 +- .../api/vmousesnapping/ISnapBehaviour.java | 2 +- .../api/vmousesnapping/SnapPoint.java | 2 +- .../{ => impl}/bindings/BindContext.java | 14 +- .../bindings/ControlifyBindings.java | 10 +- .../{ => impl}/bindings/KeyMappingHandle.java | 6 +- .../boatfix/AnalogBoatInput.java | 2 +- .../compatibility/CompatMixinPlatform.java | 2 +- .../compatibility/CompatMixinPlugin.java | 2 +- .../compatibility/ControlifyCompat.java | 10 +- .../fancymenu/FancyMenuCompat.java | 2 +- .../OpenControlifySettingsAction.java | 6 +- .../compatibility/iris/IrisMixinPlugin.java | 4 +- .../mixins/BaseOptionElementWidgetMixin.java | 8 +- .../BaseOptionElementComponentProcessor.java | 4 +- .../compatibility/rrls/RrlsMixinPlugin.java | 4 +- .../rrls/mixins/HideTypeMixin.java | 4 +- .../compatibility/rso/RSOMixinPlugin.java | 4 +- .../rso/mixins/AbstractFrameAccessor.java | 2 +- .../mixins/SodiumVideoOptionsScreenMixin.java | 2 +- .../rso/mixins/TabFrameAccessor.java | 2 +- .../simplevoicechat/SVCMixinPlugin.java | 4 +- .../SimpleVoiceChatCompat.java | 16 +- .../mixins/KeyEventsAccessor.java | 2 +- .../mixins/PTTKeyHandlerMixin.java | 4 +- .../sodium/SodiumMixinPlugin.java | 4 +- .../sodium/mixins/AbstractWidgetMixin.java | 4 +- .../mixins/CycleControlElementMixin.java | 8 +- .../mixins/FlatButtonWidgetAccessor.java | 2 +- .../sodium/mixins/FlatButtonWidgetMixin.java | 6 +- .../mixins/SliderControlElementMixin.java | 8 +- .../mixins/TickBoxControlElementMixin.java | 8 +- .../mixins/VideoSettingsScreenMixin.java | 10 +- .../screenop/CycleControlProcessor.java | 12 +- .../screenop/SliderControlProcessor.java | 4 +- .../screenop/SodiumGuiScreenProcessor.java | 18 +- .../screenop/SodiumScreenOperations.java | 2 +- .../screenop/TickBoxControlProcessor.java | 12 +- .../compatibility/yacl/YACLMixinPlugin.java | 4 +- .../yacl/mixins/ControllerWidgetAccessor.java | 2 +- .../mixins/CyclingControllerElementMixin.java | 8 +- .../mixins/GroupSeparatorEntryAccessor.java | 2 +- .../yacl/mixins/OptionListWidgetMixin.java | 8 +- .../mixins/SliderControllerElementMixin.java | 8 +- .../StringControllerElementAccessor.java | 2 +- .../mixins/StringControllerElementMixin.java | 8 +- .../mixins/YACLScreenCategoryTabAccessor.java | 2 +- .../mixins/YACLScreenCategoryTabMixin.java | 8 +- .../yacl/mixins/YACLScreenMixin.java | 8 +- ...ngControllerElementComponentProcessor.java | 14 +- .../OptionListWidgetComponentProcessor.java | 14 +- ...erControllerElementComponentProcessor.java | 18 +- ...ngControllerElementComponentProcessor.java | 20 +- .../yacl/screenop/YACLScreenProcessor.java | 14 +- .../{ => impl}/config/ConfigManager.java | 14 +- .../{ => impl}/config/ConfigMigrator.java | 16 +- .../{ => impl}/config/dto/GlobalConfig.java | 4 +- .../{ => impl}/config/dto/SharedConfig.java | 4 +- .../config/dto/device/DeviceConfig.java | 8 +- .../dto/device/GyroCalibrationConfig.java | 10 +- .../config/dto/dfu/ControlifyDataFixer.java | 8 +- .../config/dto/dfu/ControlifySchemas.java | 2 +- .../dto/dfu/ControlifyTypeReferences.java | 2 +- .../fixes/AnalogueMovementWhitelistFix.java | 4 +- .../dto/dfu/fixes/DualsenseConfigFix.java | 6 +- .../dto/dfu/fixes/GeneratedBindingIdsFix.java | 4 +- .../dto/dfu/fixes/GuideGuiScaleFix.java | 6 +- .../config/dto/dfu/fixes/GyroLowPassFix.java | 6 +- .../dfu/fixes/HorizontalLookInvertFix.java | 4 +- .../dto/dfu/fixes/RadialMenuActionsFix.java | 4 +- .../dto/dfu/fixes/TheHolyMigrationFix.java | 8 +- .../dto/profile/BluetoothDeviceConfig.java | 2 +- .../config/dto/profile/DualsenseConfig.java | 2 +- .../dto/profile/GenericControllerConfig.java | 2 +- .../config/dto/profile/GyroConfig.java | 6 +- .../config/dto/profile/HDHapticConfig.java | 2 +- .../config/dto/profile/InputConfig.java | 6 +- .../config/dto/profile/ProfileConfig.java | 2 +- .../config/dto/profile/RumbleConfig.java | 2 +- .../defaults/DefaultConfigManager.java | 8 +- .../defaults/DefaultConfigProvider.java | 4 +- .../config/settings/ControlifySettings.java | 8 +- .../config/settings/GlobalSettings.java | 8 +- .../settings/device/DeviceSettings.java | 10 +- .../device/GyroCalibrationSettings.java | 14 +- .../profile/BluetoothDeviceSettings.java | 4 +- .../settings/profile/DualsenseSettings.java | 4 +- .../profile/GenericControllerSettings.java | 4 +- .../config/settings/profile/GyroSettings.java | 8 +- .../settings/profile/HDHapticSettings.java | 4 +- .../settings/profile/InputSettings.java | 8 +- .../settings/profile/ProfileSettings.java | 6 +- .../settings/profile/RumbleSettings.java | 8 +- .../contextual/ContextualDebug.java | 6 +- .../contextual/ContextualDomainImpl.java | 15 +- .../contextual/ContextualDomains.java | 6 +- .../contextual/ContextualPredicate.java | 8 +- .../contextual/ContextualState.java | 2 +- .../ContextualStateAccumulator.java | 12 +- .../ContextualStateContributors.java | 9 +- .../CyclicFactDependencyException.java | 2 +- .../{ => impl}/contextual/FactDefinition.java | 2 +- .../contextual/FactDependencyGraph.java | 2 +- .../contextual/GuideLocation.java | 2 +- .../{ => impl}/contextual/GuideRule.java | 5 +- .../{ => impl}/contextual/Rule.java | 2 +- .../{ => impl}/contextual/RuleEngine.java | 2 +- .../{ => impl}/contextual/RuleSet.java | 2 +- .../{ => impl}/contextual/RuleSetManager.java | 8 +- .../contextual/TriggerEffectRule.java | 8 +- .../impl/controller/ControllerComponent.java | 27 + .../impl/controller/ControllerImpl.java | 194 +++ .../impl/controller/ControllerInfo.java | 14 + .../SingleValueControllerComponent.java | 16 + .../battery/BatteryLevelComponent.java | 17 +- .../controller/battery/PowerState.java | 2 +- .../dualsense/DualsenseComponent.java | 21 +- .../dualsense/TriggerEffectCodecs.java | 6 +- .../dualsense/TriggerEffectInstanceImpl.java | 20 +- .../dualsense/TriggerEffectManager.java | 20 +- .../controller/gyro/GyroButtonMode.java | 2 +- .../controller/gyro/GyroCalibrator.java | 13 +- .../controller/gyro/GyroComponent.java | 64 +- .../controller/gyro/GyroYawMode.java | 2 +- .../controller/haptic/CompleteSoundData.java | 2 +- .../controller/haptic/HDHapticComponent.java | 16 +- .../controller/haptic/HapticEffects.java | 4 +- .../controller/input/InputComponent.java | 54 +- .../controller/led/LEDComponent.java | 16 +- .../misc/BluetoothDeviceComponent.java | 20 +- .../controller/misc/DriverNameComponent.java | 19 + .../controller/misc}/GUIDComponent.java | 6 +- .../impl/controller/misc/UIDComponent.java | 19 + .../rumble/ContinuousRumbleEffectImpl.java | 122 ++ .../rumble/FiniteRumbleEffectImpl.java | 97 ++ .../rumble/RumbleComponentImpl.java | 61 + .../rumble/RumbleEffectInstanceImpl.java | 98 ++ .../impl/controller/rumble/RumbleManager.java | 97 ++ .../impl/controller/rumble/RumbleSource.java | 27 + .../rumble/RumbleSourceManager.java | 82 ++ .../rumble/TriggerRumbleComponent.java | 19 +- .../rumble/effects/UseItemEffectHolder.java | 6 +- .../steamdeck/SteamDeckComponent.java | 19 + .../touchpad/TouchpadComponent.java | 14 +- .../controller/touchpad/Touchpads.java | 2 +- .../controllermanager/ControllerBackend.java | 7 +- .../ControllerCoordinator.java | 58 +- .../controllermanager/ControllerManager.java | 33 +- .../controllermanager}/ControllerType.java | 16 +- .../ControllerTypeManager.java | 23 +- .../SDLControllerBackend.java | 31 +- .../SDLUniqueControllerID.java | 2 +- .../controllermanager/UniqueControllerID.java | 2 +- .../{ => impl}/debug/DebugProperties.java | 6 +- .../driver/ComponentAdderDriver.java | 10 +- .../{ => impl}/driver/CompoundDriver.java | 10 +- .../controlify/{ => impl}/driver/Driver.java | 8 +- .../dualsense/DualsenseEffectsState.java | 2 +- .../dualsense/DualsenseTriggerEffect.java | 2 +- .../DualsenseTriggerEffectTypes.java | 2 +- .../{ => impl}/driver/sdl/DecodedGUID.java | 2 +- .../driver/sdl/SDL3GamepadDriver.java | 27 +- .../driver/sdl/SDL3JoystickDriver.java | 21 +- .../driver/sdl/SDLCommonDriver.java | 62 +- .../driver/sdl/SDLControllerEvent.java | 2 +- .../driver/sdl/SDLControllerEventStage.java | 6 +- .../{ => impl}/driver/sdl/SDLException.java | 2 +- .../driver/sdl/SDLGamepadInputMapping.java | 6 +- .../driver/sdl/SDLInputConversions.java | 6 +- .../sdl/SDLJoystickConnectionState.java | 2 +- .../driver/sdl/SDLNativesLoader.java | 10 +- .../driver/sdl/SDLToPhysicalStage.java | 18 +- .../{ => impl}/driver/sdl/SDLUtil.java | 2 +- .../steamdeck/CEFDebuggerFileResult.java | 2 +- .../driver/steamdeck/SteamDeckDriver.java | 46 +- .../driver/steamdeck/SteamDeckMode.java | 2 +- .../driver/steamdeck/SteamDeckUtil.java | 8 +- .../{ => impl}/font/BindingFontHelper.java | 6 +- .../{ => impl}/font/InputFontMapper.java | 18 +- .../{ => impl}/gui/ButtonGuideRenderer.java | 10 +- .../FakePositionPlainTextButton.java | 2 +- .../gui/components/PlainTextWidget.java | 2 +- .../gui/controllers/BindController.java | 33 +- .../controllers/Deadzone2DImageRenderer.java | 8 +- .../gui/guide/GuideInstanceImpl.java | 23 +- .../{ => impl}/gui/guide/GuideRenderer.java | 2 +- .../gui/guide/InGameButtonGuide.java | 15 +- .../gui/guide/PrecomputedLines.java | 2 +- .../gui/layout/AbstractLayoutComponent.java | 2 +- .../{ => impl}/gui/layout/AnchorPoint.java | 2 +- .../gui/layout/ColumnLayoutComponent.java | 2 +- .../gui/layout/PositionedComponent.java | 2 +- .../gui/layout/RenderComponent.java | 2 +- .../gui/layout/RowLayoutComponent.java | 2 +- .../gui/screen/AskToMapControllerScreen.java | 8 +- .../gui/screen/BindConsumerScreen.java | 18 +- .../gui/screen/BluetoothWarningScreen.java | 8 +- .../gui/screen/ControlifySettingsScreen.java | 35 +- .../screen/ControllerConfigScreenFactory.java | 126 +- .../screen/ControllerMappingMakerScreen.java | 30 +- .../gui/screen/DontInterruptScreen.java | 2 +- .../screen/GlobalSettingsScreenFactory.java | 22 +- .../gui/screen/ProfileSelectionScreen.java | 30 +- .../{ => impl}/gui/screen/RadialItems.java | 28 +- .../gui/screen/RadialMenuEditModel.java | 2 +- .../gui/screen/RadialMenuEditScreen.java | 36 +- .../gui/screen/RadialMenuPages.java | 2 +- .../gui/screen/RadialMenuScreen.java | 48 +- .../gui/screen/SteamDeckAlerts.java | 2 +- .../{ => impl}/hid/ControllerHIDInfo.java | 10 +- .../controlify/{ => impl}/hid/HIDDevice.java | 2 +- .../controlify/{ => impl}/hid/HIDID.java | 4 +- .../ingame/ControllerPlayerMovement.java | 16 +- .../{ => impl}/ingame/DualInput.java | 2 +- .../{ => impl}/ingame/InGameInputHandler.java | 52 +- .../{ => impl}/ingame/InputCurve.java | 2 +- .../{ => impl}/ingame/InputCurves.java | 2 +- .../{ => impl}/ingame/PickBlockAccessor.java | 2 +- .../{ => impl}/input/SensorType.java | 2 +- .../input/mapping/ControllerMapping.java | 2 +- .../mapping/ControllerMappingManager.java | 6 +- .../input/mapping/DeadzoneGroup.java | 2 +- .../input/mapping/GamepadInputs.java | 4 +- .../input/mapping/GestureInputs.java | 4 +- .../{ => impl}/input/mapping/Inputs.java | 2 +- .../input/mapping/JoystickInputs.java | 4 +- .../{ => impl}/input/mapping/MapType.java | 2 +- .../input/mapping/MappingEntry.java | 6 +- .../input/mapping/MappingEntryCodecs.java | 2 +- .../input/pipeline/DeadzoneStage.java | 12 +- .../pipeline/GestureRecogniserStage.java | 12 +- .../pipeline/InputDomainExpansionStage.java | 10 +- .../input/pipeline/InputPipeline.java | 16 +- .../input/pipeline/MappingStage.java | 14 +- .../pipeline/PhysicalToLogicalStage.java | 18 +- .../pipeline/SensorAccumulatorStage.java | 16 +- .../input/pipeline/SensorProcessorStage.java | 12 +- .../input/signal/LogicalSignal.java | 8 +- .../input/signal/PhysicalInputSignal.java | 2 +- .../input/signal/PhysicalSensorSignal.java | 4 +- .../input/signal/PhysicalSignal.java | 2 +- .../platform/ArrayBackedEventHandler.java | 2 +- .../{ => impl}/platform/Environment.java | 2 +- .../controlify/{ => impl}/platform/Event.java | 2 +- .../{ => impl}/platform/EventHandler.java | 2 +- .../platform/client/CreativeTabHelper.java | 2 +- .../platform/client/HudRenderLayer.java | 2 +- .../platform/client/PlatformClientUtil.java | 12 +- .../client/PlatformClientUtilImpl.java | 6 +- .../client/events/DisconnectedEvent.java | 2 +- .../client/events/LifecycleEvent.java | 2 +- .../client/events/ScreenRenderEvent.java | 2 +- .../platform/client/events/TickEvent.java | 2 +- .../resource/ControlifyReloadListener.java | 2 +- .../SimpleControlifyReloadListener.java | 2 +- .../platform/main/PlatformMainUtil.java | 18 +- .../platform/main/PlatformMainUtilImpl.java | 18 +- .../CommandRegistrationCallbackEvent.java | 2 +- .../main/events/HandshakeCompletionEvent.java | 2 +- .../main/events/PlayerJoinedEvent.java | 2 +- .../platform/network/C2SNetworkApi.java | 2 +- .../platform/network/S2CNetworkApi.java | 2 +- .../platform/network/SidedNetworkApi.java | 4 +- .../reacharound/ReachAroundHandler.java | 6 +- .../reacharound/ReachAroundMode.java | 4 +- .../screenop/ComponentProcessor.java | 14 +- .../screenop/ComponentProcessorProvider.java | 2 +- .../{ => impl}/screenop/CustomFocus.java | 2 +- .../{ => impl}/screenop/Registry.java | 2 +- .../ScreenControllerEventListener.java | 6 +- .../{ => impl}/screenop/ScreenProcessor.java | 53 +- .../screenop/ScreenProcessorFactory.java | 2 +- .../screenop/ScreenProcessorProvider.java | 2 +- .../AbstractSliderComponentProcessor.java | 16 +- .../AbstractButtonComponentProcessor.java | 14 +- .../AbstractContainerScreenProcessor.java | 28 +- .../AbstractSignEditScreenProcessor.java | 32 +- .../vanilla/AddServerLikeScreenProcessor.java | 18 +- .../BundleItemSlotControllerAction.java | 10 +- .../compat/vanilla/ChatScreenProcessor.java | 32 +- .../vanilla/CreateWorldScreenProcessor.java | 10 +- .../CreativeModeInventoryScreenProcessor.java | 18 +- .../vanilla/EditBoxComponentProcessor.java | 12 +- .../vanilla/ItemSlotControllerAction.java | 6 +- .../JoinMultiplayerScreenProcessor.java | 14 +- ...nguageSelectionListComponentProcessor.java | 14 +- .../vanilla/OptionsScreenProcessor.java | 10 +- .../vanilla/OptionsSubScreenProcessor.java | 10 +- .../compat/vanilla/PauseScreenProcessor.java | 16 +- .../vanilla/RecipeBookScreenProcessor.java | 12 +- .../vanilla/SelectWorldScreenProcessor.java | 12 +- ...rSelectionListEntryComponentProcessor.java | 12 +- .../vanilla/SliderComponentProcessor.java | 16 +- .../compat/vanilla/TitleScreenProcessor.java | 16 +- .../WorldListEntryComponentProcessor.java | 12 +- .../screenop/keyboard/ChatKeyboardDucky.java | 2 +- .../keyboard/CommonKeyboardHints.java | 6 +- .../keyboard/ComponentKeyboardBehaviour.java | 4 +- .../keyboard/FallbackKeyboardLayout.java | 12 +- .../screenop/keyboard/InputTarget.java | 2 +- .../screenop/keyboard/KeyWidget.java | 26 +- .../screenop/keyboard/KeyboardLayout.java | 6 +- .../keyboard/KeyboardLayoutManager.java | 10 +- .../keyboard/KeyboardLayoutWithId.java | 2 +- .../screenop/keyboard/KeyboardLayouts.java | 6 +- .../keyboard/KeyboardOverlayScreen.java | 6 +- .../screenop/keyboard/KeyboardWidget.java | 16 +- .../screenop/keyboard/MixinInputTarget.java | 2 +- .../server/ControlifyHandshake.java | 8 +- .../{ => impl}/server/ControlifyServer.java | 10 +- .../server/ControlifyServerConfig.java | 6 +- .../{ => impl}/server/ServerPolicies.java | 2 +- .../{ => impl}/server/ServerPolicy.java | 2 +- .../{ => impl}/server/VibrateCommand.java | 28 +- .../server/packets/EntityVibrationPacket.java | 22 +- .../server/packets/OriginVibrationPacket.java | 22 +- .../server/packets/ServerPolicyPacket.java | 4 +- .../server/packets/VibrationPacket.java | 15 +- .../sound/ControlifyClientSounds.java | 4 +- .../controlify/{ => impl}/utils/CUtil.java | 7 +- .../{ => impl}/utils/ColorUtils.java | 2 +- .../{ => impl}/utils/ControllerUtils.java | 10 +- .../{ => impl}/utils/CursorUtils.java | 2 +- .../{ => impl}/utils/DebugDump.java | 14 +- .../controlify/{ => impl}/utils/DebugLog.java | 4 +- .../{ => impl}/utils/DebugOverlayHelper.java | 2 +- .../{ => impl}/utils/DummyMixinTarget.java | 2 +- .../controlify/{ => impl}/utils/Easings.java | 2 +- .../{ => impl}/utils/HoldRepeatHelper.java | 4 +- .../utils/InitialScreenRegistryDuck.java | 2 +- .../{ => impl}/utils/JsonTreeParser.java | 2 +- .../{ => impl}/utils/LazyComponentDims.java | 12 +- .../{ => impl}/utils/MinecraftUtil.java | 2 +- .../utils/MouseMinecraftCallNotifier.java | 2 +- .../controlify/{ => impl}/utils/MthExt.java | 2 +- .../controlify/{ => impl}/utils/Platform.java | 2 +- .../utils/PrecomputedComponentDims.java | 4 +- .../{ => impl}/utils/ResizableRingBuffer.java | 2 +- .../{ => impl}/utils/SetupWizard.java | 2 +- .../{ => impl}/utils/SetupWizardManager.java | 4 +- .../utils/TrackingBodySubscriber.java | 2 +- .../{ => impl}/utils/TrackingConsumer.java | 2 +- .../utils/UnhandledCompletableFutures.java | 2 +- .../utils/animation/api/Animatable.java | 2 +- .../utils/animation/api/Animation.java | 4 +- .../utils/animation/api/AnimationGroup.java | 4 +- .../animation/api/AnimationSequence.java | 4 +- .../utils/animation/api/EasingFunction.java | 2 +- .../animation/impl/AnimationGroupImpl.java | 6 +- .../utils/animation/impl/AnimationImpl.java | 6 +- .../animation/impl/AnimationSequenceImpl.java | 6 +- .../utils/animation/impl/Animator.java | 4 +- .../{ => impl}/utils/codec/CExtraCodecs.java | 2 +- .../{ => impl}/utils/codec/FuzzyCodec.java | 2 +- .../{ => impl}/utils/codec/FuzzyMapCodec.java | 2 +- .../{ => impl}/utils/codec/SetCodec.java | 2 +- .../utils/codec/StrictEitherMapCodec.java | 2 +- .../{ => impl}/utils/event/EventSink.java | 2 +- .../{ => impl}/utils/event/EventSource.java | 2 +- .../{ => impl}/utils/event/EventStage.java | 2 +- .../{ => impl}/utils/event/EventTap.java | 2 +- .../utils/event/UnaryEventStage.java | 2 +- .../utils/log/AbstractControlifyLogger.java | 2 +- .../utils/log/ControlifyLogger.java | 2 +- .../utils/log/ControlifyMasterLogger.java | 4 +- .../utils/log/ControlifySubLogger.java | 2 +- .../utils/log/ControlifySubLoggerImpl.java | 2 +- .../{ => impl}/utils/log/LogLevel.java | 2 +- .../{ => impl}/utils/log/LogMessage.java | 2 +- .../predicates/ClientEntityPredicate.java | 2 +- .../predicates/ClientPredicateUtils.java | 4 +- .../predicates/EntityEquipmentPredicate.java | 2 +- .../utils/render/BaseRenderState.java | 4 +- .../utils/render/CGuiElementRenderState.java | 4 +- .../{ => impl}/utils/render/RenderUtils.java | 2 +- .../elements/CircleElementRenderState.java | 6 +- .../{ => impl}/virtualmouse/SnapUtils.java | 4 +- .../virtualmouse/VirtualMouseBehaviour.java | 2 +- .../virtualmouse/VirtualMouseHandler.java | 37 +- .../wireless/LowBatteryNotifier.java | 18 +- .../core/ClientPacketListenerMixin.java | 2 +- .../controlify/mixins/core/GuiMixin.java | 8 +- .../controlify/mixins/core/HudMixin.java | 4 +- .../mixins/core/KeyboardHandlerMixin.java | 2 +- .../mixins/core/MinecraftMixin.java | 6 +- .../mixins/core/MouseHandlerMixin.java | 8 +- .../mixins/core/SDLEventHandlerMixin.java | 4 +- .../mixins/feature/bind/GuiMixin.java | 6 +- .../mixins/feature/bind/HudMixin.java | 4 +- .../mixins/feature/bind/KeyMappingMixin.java | 6 +- .../feature/bind/ToggleKeyMappingMixin.java | 8 +- .../feature/font/KeybindContentsMixin.java | 6 +- .../font/TranslatableContentsMixin.java | 2 +- .../ingame/ClientPacketListenerMixin.java | 4 +- .../mixins/feature/guide/ingame/HudMixin.java | 4 +- .../guide/screen/AbstractButtonMixin.java | 4 +- .../guide/screen/TabNavigationBarMixin.java | 8 +- .../mixins/feature/input/MinecraftMixin.java | 2 +- .../feature/oofinput/MinecraftMixin.java | 2 +- .../analogueboat/AbstractBoatMixin.java | 2 +- .../analogueboat/LocalPlayerMixin.java | 8 +- .../ClientPacketListenerMixin.java | 6 +- .../MovementTutorialStepInstanceMixin.java | 4 +- .../OpenInventoryTutorialStepMixin.java | 4 +- .../PunchTreeTutorialStepInstanceMixin.java | 4 +- .../patches/sprintfix/LocalPlayerMixin.java | 2 +- .../feature/reacharound/MinecraftMixin.java | 2 +- .../mixins/feature/rumble/MinecraftMixin.java | 4 +- .../blockbreak/MultiPlayerGameModeMixin.java | 25 +- .../damage/ClientPacketListenerMixin.java | 16 +- .../explosion/ClientPacketListenerMixin.java | 16 +- .../rumble/explosion/LightningBoltMixin.java | 14 +- .../rumble/fishing/FishingHookMixin.java | 14 +- .../rumble/itembreak/LocalPlayerMixin.java | 10 +- .../levelevents/LevelEventHandlerMixin.java | 38 +- .../rumble/slowblock/LocalPlayerMixin.java | 22 +- .../useitem/ClientPacketListenerMixin.java | 6 +- .../rumble/useitem/LocalPlayerMixin.java | 47 +- .../rumble/waterland/LocalPlayerMixin.java | 19 +- .../screenop/ContainerEventHandlerMixin.java | 2 +- .../mixins/feature/screenop/GuiMixin.java | 4 +- .../mixins/feature/screenop/ScreenMixin.java | 8 +- .../impl/bundle/BundleMouseActionsMixin.java | 8 +- .../impl/chat/ChatComponentMixin.java | 4 +- .../screenop/impl/chat/ChatScreenMixin.java | 6 +- .../impl/chat/CommandSuggestionsMixin.java | 6 +- .../AbstractContainerScreenMixin.java | 14 +- .../AbstractRecipeBookScreenMixin.java | 6 +- .../CreativeModeInventoryScreenMixin.java | 12 +- .../impl/elements/AbstractButtonMixin.java | 6 +- .../AbstractContainerEventHandlerMixin.java | 2 +- .../elements/AbstractSliderButtonMixin.java | 8 +- .../impl/elements/AbstractWidgetMixin.java | 10 +- ...ontainerObjectSelectionListEntryMixin.java | 2 +- .../screenop/impl/elements/EditBoxMixin.java | 16 +- .../outofgame/CreateWorldScreenMixin.java | 12 +- .../DirectJoinServerScreenMixin.java | 6 +- .../outofgame/JoinMultiplayerScreenMixin.java | 6 +- .../LanguageSelectionListEntryMixin.java | 6 +- .../outofgame/ManageServerScreenMixin.java | 6 +- .../impl/outofgame/OptionsScreenMixin.java | 6 +- .../impl/outofgame/OptionsSubScreenMixin.java | 6 +- .../impl/outofgame/PauseScreenMixin.java | 6 +- .../outofgame/SelectWorldScreenMixin.java | 12 +- .../ServerSelectionListEntryMixin.java | 6 +- .../impl/outofgame/TitleScreenMixin.java | 6 +- .../WorldSelectionListEntryMixin.java | 6 +- .../sign/AbstractSignEditScreenMixin.java | 12 +- .../settingsbutton/ControlsScreenMixin.java | 4 +- .../feature/util/ConnectScreenMixin.java | 2 +- .../mixins/feature/virtualmouse/GuiMixin.java | 4 +- .../virtualmouse/InputConstantsMixin.java | 8 +- .../feature/virtualmouse/MinecraftMixin.java | 4 +- .../virtualmouse/MouseHandlerMixin.java | 4 +- .../AbstractContainerScreenMixin.java | 4 +- .../AbstractRecipeBookScreenMixin.java | 4 +- .../CreativeModeInventoryScreenMixin.java | 2 +- .../snapping/EnchantmentScreenMixin.java | 4 +- .../snapping/LoomScreenMixin.java | 4 +- .../virtualmouse/snapping/ScreenMixin.java | 4 +- .../snapping/StonecutterScreenMixin.java | 4 +- .../controlify/rumble/BasicRumbleEffect.java | 162 --- .../rumble/ContinuousRumbleEffect.java | 161 --- .../controlify/rumble/RumbleCapable.java | 15 - .../controlify/rumble/RumbleEffect.java | 24 - .../controlify/rumble/RumbleManager.java | 92 -- .../controlify/rumble/RumbleSource.java | 74 - .../controlify/controller/activators.json | 3 + .../contextual/facts/container.json | 0 .../contextual/facts/in_game.json | 0 .../contextual/guide/container.json | 0 .../contextual/guide/in_game.json | 0 .../contextual/trigger_effect/in_game.json | 0 .../controller_identification.json5 | 0 .../default_bind/default.json | 0 .../default_bind/dualsense.json | 0 .../default_bind/switch.json | 0 .../default_config.json | 0 .../font_mapping}/default.json | 0 .../font_mapping}/dualsense.json | 0 .../font_mapping}/dualshock3.json | 0 .../font_mapping}/dualshock4.json | 0 .../font_mapping}/steam_controller.json | 0 .../font_mapping}/steam_deck.json | 0 .../font_mapping}/switch.json | 0 .../font_mapping}/xbox_one.json | 0 .../gamecontrollerdb-sdl2.txt | 0 .../gamecontrollerdb-sdl3.txt | 0 .../{ => controller}/haptics/navigate.ogg | Bin .../keyboard_layout/full/en_us.json | 0 .../keyboard_layout/server_ip/en_us.json | 0 .../keyboard_layout/simple/en_us.json | 0 .../{ => controller}/radial_icons.json | 0 .../controlify/controller/rumble_sources.json | 16 + .../controlify-compat.iris.mixins.json | 4 +- ...y-compat.reeses-sodium-options.mixins.json | 4 +- .../controlify-compat.rrls.mixins.json | 4 +- ...olify-compat.simple-voice-chat.mixins.json | 4 +- .../controlify-compat.sodium.mixins.json | 4 +- .../controlify-compat.yacl.mixins.json | 4 +- .../controlify/contextual/guide/in_game.json | 1194 ----------------- .../neoforge/ControlifyBootstrap.java | 12 +- .../NeoforgeCompatMixinPlatform.java | 2 +- .../platform/NeoforgePlatformMainImpl.java | 16 +- .../client/NeoforgeCreativeTabHelper.java | 2 +- .../client/NeoforgePlatformClientImpl.java | 16 +- .../network/C2SNetworkApiNeoforge.java | 2 +- .../network/S2CNetworkApiNeoforge.java | 7 +- .../config/ConfigMigrationTestHelper.java | 4 +- .../contextual/FactDependencyGraphTest.java | 2 +- 617 files changed, 3428 insertions(+), 4644 deletions(-) delete mode 100644 src/main/java/dev/isxander/controlify/action/ActionSpecRegistry.java delete mode 100644 src/main/java/dev/isxander/controlify/action/activator/NoopActivator.java rename src/main/java/dev/isxander/controlify/api/{event => }/ControlifyEvents.java (79%) rename src/main/java/dev/isxander/controlify/{ => api}/InputMode.java (89%) rename src/main/java/dev/isxander/controlify/{ => api}/action/Action.java (65%) rename src/main/java/dev/isxander/controlify/{ => api}/action/ActionSpec.java (76%) rename src/main/java/dev/isxander/controlify/{ => api}/action/Channel.java (94%) create mode 100644 src/main/java/dev/isxander/controlify/api/action/package-info.java create mode 100644 src/main/java/dev/isxander/controlify/api/contextual/package-info.java create mode 100644 src/main/java/dev/isxander/controlify/api/controller/Controller.java create mode 100644 src/main/java/dev/isxander/controlify/api/controller/package-info.java create mode 100644 src/main/java/dev/isxander/controlify/api/controller/rumble/ContinuousRumbleEffect.java create mode 100644 src/main/java/dev/isxander/controlify/api/controller/rumble/FiniteRumbleEffect.java create mode 100644 src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleComponent.java create mode 100644 src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleEffect.java create mode 100644 src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleEffectInstance.java rename src/main/java/dev/isxander/controlify/{ => api/controller}/rumble/RumbleState.java (94%) rename src/main/java/dev/isxander/controlify/{ => api/controller}/rumble/TriggerRumbleState.java (89%) create mode 100644 src/main/java/dev/isxander/controlify/api/controller/rumble/package-info.java rename src/main/java/dev/isxander/controlify/api/{buttonguide => gui}/ButtonGuideApi.java (65%) rename src/main/java/dev/isxander/controlify/api/{buttonguide => gui}/ButtonGuidePredicate.java (57%) create mode 100644 src/main/java/dev/isxander/controlify/api/gui/package-info.java rename src/main/java/dev/isxander/controlify/{ => api}/input/LogicalInput.java (93%) create mode 100644 src/main/java/dev/isxander/controlify/api/input/package-info.java create mode 100644 src/main/java/dev/isxander/controlify/api/package-info.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/ControllerEntity.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/ECSComponent.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/ECSEntity.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/SingleValueComponent.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/gyro/GyroState.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/gyro/GyroStateC.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/impl/ECSComponentImpl.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/impl/ECSEntityImpl.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/info/ControllerInfo.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/info/DriverNameComponent.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/info/UIDComponent.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/rumble/RumbleComponent.java delete mode 100644 src/main/java/dev/isxander/controlify/controller/steamdeck/SteamDeckComponent.java rename src/main/java/dev/isxander/controlify/{ => impl}/Controlify.java (82%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/Accumulator.java (82%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/ActionAccessor.java (64%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/ActionAccessorImpl.java (64%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/ActionContext.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/ActionGraph.java (93%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/ActionImpl.java (82%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/ActionSpecBuilder.java (64%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/ActionSpecBuilderImpl.java (86%) create mode 100644 src/main/java/dev/isxander/controlify/impl/action/ActionSpecRegistry.java rename src/main/java/dev/isxander/controlify/{ => impl}/action/ActionSpecRegistryImpl.java (91%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/ActionState.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/Activator.java (64%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/activator/ActivatorConfig.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/activator/ActivatorConfigManager.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/activator/ActivatorConfigType.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/activator/CompoundActivator.java (82%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/activator/ContinuousActivator.java (74%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/activator/DoubleTapPulseActivator.java (76%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/activator/GuiPressPulseActivator.java (76%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/activator/HoldLatchActivator.java (79%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/activator/HoldPulseActivator.java (75%) create mode 100644 src/main/java/dev/isxander/controlify/impl/action/activator/NoopActivator.java rename src/main/java/dev/isxander/controlify/{ => impl}/action/activator/PressPulseActivator.java (75%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/activator/ReleasePulseActivator.java (75%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/activator/RepeatLatchPulseActivator.java (91%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/activator/TapPulseActivator.java (75%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/activator/ToggleLatchActivator.java (85%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/defaults/DefaultBindManager.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/defaults/DefaultBindProvider.java (76%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/defaults/LayeredDefaultBindProvider.java (92%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/defaults/MapBackedDefaultBindProvider.java (83%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/radial/RadialIcon.java (98%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/radial/RadialIconExtractor.java (97%) rename src/main/java/dev/isxander/controlify/{ => impl}/action/radial/RadialIconManager.java (88%) rename src/main/java/dev/isxander/controlify/{ => impl}/api/entrypoint/ControlifyEntrypoint.java (94%) rename src/main/java/dev/isxander/controlify/{ => impl}/api/entrypoint/InitContext.java (81%) rename src/main/java/dev/isxander/controlify/{ => impl}/api/entrypoint/PreInitContext.java (72%) rename src/main/java/dev/isxander/controlify/{ => impl}/api/ingameinput/LookInputModifier.java (76%) rename src/main/java/dev/isxander/controlify/{ => impl}/api/vmousesnapping/ISnapBehaviour.java (91%) rename src/main/java/dev/isxander/controlify/{ => impl}/api/vmousesnapping/SnapPoint.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/bindings/BindContext.java (84%) rename src/main/java/dev/isxander/controlify/{ => impl}/bindings/ControlifyBindings.java (98%) rename src/main/java/dev/isxander/controlify/{ => impl}/bindings/KeyMappingHandle.java (55%) rename src/main/java/dev/isxander/controlify/{fixes => impl}/boatfix/AnalogBoatInput.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/CompatMixinPlatform.java (81%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/CompatMixinPlugin.java (98%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/ControlifyCompat.java (80%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/fancymenu/FancyMenuCompat.java (85%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/fancymenu/OpenControlifySettingsAction.java (84%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/iris/IrisMixinPlugin.java (66%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/iris/mixins/BaseOptionElementWidgetMixin.java (78%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/iris/screenop/BaseOptionElementComponentProcessor.java (78%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/rrls/RrlsMixinPlugin.java (66%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/rrls/mixins/HideTypeMixin.java (92%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/rso/RSOMixinPlugin.java (67%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/rso/mixins/AbstractFrameAccessor.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/rso/mixins/SodiumVideoOptionsScreenMixin.java (98%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/rso/mixins/TabFrameAccessor.java (92%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/simplevoicechat/SVCMixinPlugin.java (64%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/simplevoicechat/SimpleVoiceChatCompat.java (86%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/simplevoicechat/mixins/KeyEventsAccessor.java (84%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/simplevoicechat/mixins/PTTKeyHandlerMixin.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/sodium/SodiumMixinPlugin.java (66%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/sodium/mixins/AbstractWidgetMixin.java (88%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/sodium/mixins/CycleControlElementMixin.java (72%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/sodium/mixins/FlatButtonWidgetAccessor.java (86%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/sodium/mixins/FlatButtonWidgetMixin.java (91%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/sodium/mixins/SliderControlElementMixin.java (83%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/sodium/mixins/TickBoxControlElementMixin.java (72%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/sodium/mixins/VideoSettingsScreenMixin.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/sodium/screenop/CycleControlProcessor.java (69%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/sodium/screenop/SliderControlProcessor.java (76%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java (73%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/sodium/screenop/SodiumScreenOperations.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/sodium/screenop/TickBoxControlProcessor.java (67%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/yacl/YACLMixinPlugin.java (67%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/yacl/mixins/ControllerWidgetAccessor.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/yacl/mixins/CyclingControllerElementMixin.java (68%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/yacl/mixins/GroupSeparatorEntryAccessor.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/yacl/mixins/OptionListWidgetMixin.java (67%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/yacl/mixins/SliderControllerElementMixin.java (68%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/yacl/mixins/StringControllerElementAccessor.java (86%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/yacl/mixins/StringControllerElementMixin.java (68%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/yacl/mixins/YACLScreenCategoryTabAccessor.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/yacl/mixins/YACLScreenCategoryTabMixin.java (78%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/yacl/mixins/YACLScreenMixin.java (66%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/yacl/screenop/CyclingControllerElementComponentProcessor.java (73%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/yacl/screenop/OptionListWidgetComponentProcessor.java (82%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/yacl/screenop/SliderControllerElementComponentProcessor.java (74%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/yacl/screenop/StringControllerElementComponentProcessor.java (74%) rename src/main/java/dev/isxander/controlify/{ => impl}/compatibility/yacl/screenop/YACLScreenProcessor.java (75%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/ConfigManager.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/ConfigMigrator.java (92%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/GlobalConfig.java (94%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/SharedConfig.java (85%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/device/DeviceConfig.java (79%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/device/GyroCalibrationConfig.java (60%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/dfu/ControlifyDataFixer.java (88%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/dfu/ControlifySchemas.java (97%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/dfu/ControlifyTypeReferences.java (93%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/dfu/fixes/AnalogueMovementWhitelistFix.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/dfu/fixes/DualsenseConfigFix.java (85%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/dfu/fixes/GeneratedBindingIdsFix.java (94%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/dfu/fixes/GuideGuiScaleFix.java (86%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/dfu/fixes/GyroLowPassFix.java (82%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/dfu/fixes/HorizontalLookInvertFix.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/dfu/fixes/RadialMenuActionsFix.java (93%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/dfu/fixes/TheHolyMigrationFix.java (97%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/profile/BluetoothDeviceConfig.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/profile/DualsenseConfig.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/profile/GenericControllerConfig.java (97%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/profile/GyroConfig.java (88%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/profile/HDHapticConfig.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/profile/InputConfig.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/profile/ProfileConfig.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/profile/RumbleConfig.java (92%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/profile/defaults/DefaultConfigManager.java (91%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/dto/profile/defaults/DefaultConfigProvider.java (61%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/settings/ControlifySettings.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/settings/GlobalSettings.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/settings/device/DeviceSettings.java (82%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/settings/device/GyroCalibrationSettings.java (64%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/settings/profile/BluetoothDeviceSettings.java (79%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/settings/profile/DualsenseSettings.java (78%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/settings/profile/GenericControllerSettings.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/settings/profile/GyroSettings.java (83%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/settings/profile/HDHapticSettings.java (76%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/settings/profile/InputSettings.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/settings/profile/ProfileSettings.java (92%) rename src/main/java/dev/isxander/controlify/{ => impl}/config/settings/profile/RumbleSettings.java (83%) rename src/main/java/dev/isxander/controlify/{ => impl}/contextual/ContextualDebug.java (97%) rename src/main/java/dev/isxander/controlify/{ => impl}/contextual/ContextualDomainImpl.java (93%) rename src/main/java/dev/isxander/controlify/{ => impl}/contextual/ContextualDomains.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/contextual/ContextualPredicate.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/contextual/ContextualState.java (92%) rename src/main/java/dev/isxander/controlify/{ => impl}/contextual/ContextualStateAccumulator.java (83%) rename src/main/java/dev/isxander/controlify/{ => impl}/contextual/ContextualStateContributors.java (94%) rename src/main/java/dev/isxander/controlify/{ => impl}/contextual/CyclicFactDependencyException.java (93%) rename src/main/java/dev/isxander/controlify/{ => impl}/contextual/FactDefinition.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/contextual/FactDependencyGraph.java (99%) rename src/main/java/dev/isxander/controlify/{api => impl}/contextual/GuideLocation.java (93%) rename src/main/java/dev/isxander/controlify/{ => impl}/contextual/GuideRule.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/contextual/Rule.java (91%) rename src/main/java/dev/isxander/controlify/{ => impl}/contextual/RuleEngine.java (98%) rename src/main/java/dev/isxander/controlify/{ => impl}/contextual/RuleSet.java (93%) rename src/main/java/dev/isxander/controlify/{ => impl}/contextual/RuleSetManager.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/contextual/TriggerEffectRule.java (79%) create mode 100644 src/main/java/dev/isxander/controlify/impl/controller/ControllerComponent.java create mode 100644 src/main/java/dev/isxander/controlify/impl/controller/ControllerImpl.java create mode 100644 src/main/java/dev/isxander/controlify/impl/controller/ControllerInfo.java create mode 100644 src/main/java/dev/isxander/controlify/impl/controller/SingleValueControllerComponent.java rename src/main/java/dev/isxander/controlify/{ => impl}/controller/battery/BatteryLevelComponent.java (62%) rename src/main/java/dev/isxander/controlify/{ => impl}/controller/battery/PowerState.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/controller/dualsense/DualsenseComponent.java (77%) rename src/main/java/dev/isxander/controlify/{ => impl}/controller/dualsense/TriggerEffectCodecs.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/controller/dualsense/TriggerEffectInstanceImpl.java (81%) rename src/main/java/dev/isxander/controlify/{ => impl}/controller/dualsense/TriggerEffectManager.java (73%) rename src/main/java/dev/isxander/controlify/{ => impl}/controller/gyro/GyroButtonMode.java (91%) rename src/main/java/dev/isxander/controlify/{ => impl}/controller/gyro/GyroCalibrator.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/controller/gyro/GyroComponent.java (69%) rename src/main/java/dev/isxander/controlify/{ => impl}/controller/gyro/GyroYawMode.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/controller/haptic/CompleteSoundData.java (80%) rename src/main/java/dev/isxander/controlify/{ => impl}/controller/haptic/HDHapticComponent.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/controller/haptic/HapticEffects.java (72%) rename src/main/java/dev/isxander/controlify/{ => impl}/controller/input/InputComponent.java (75%) rename src/main/java/dev/isxander/controlify/{ => impl}/controller/led/LEDComponent.java (86%) rename src/main/java/dev/isxander/controlify/{ => impl}/controller/misc/BluetoothDeviceComponent.java (50%) create mode 100644 src/main/java/dev/isxander/controlify/impl/controller/misc/DriverNameComponent.java rename src/main/java/dev/isxander/controlify/{controller/info => impl/controller/misc}/GUIDComponent.java (67%) create mode 100644 src/main/java/dev/isxander/controlify/impl/controller/misc/UIDComponent.java create mode 100644 src/main/java/dev/isxander/controlify/impl/controller/rumble/ContinuousRumbleEffectImpl.java create mode 100644 src/main/java/dev/isxander/controlify/impl/controller/rumble/FiniteRumbleEffectImpl.java create mode 100644 src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleComponentImpl.java create mode 100644 src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleEffectInstanceImpl.java create mode 100644 src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleManager.java create mode 100644 src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleSource.java create mode 100644 src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleSourceManager.java rename src/main/java/dev/isxander/controlify/{ => impl}/controller/rumble/TriggerRumbleComponent.java (62%) rename src/main/java/dev/isxander/controlify/{ => impl/controller}/rumble/effects/UseItemEffectHolder.java (50%) create mode 100644 src/main/java/dev/isxander/controlify/impl/controller/steamdeck/SteamDeckComponent.java rename src/main/java/dev/isxander/controlify/{ => impl}/controller/touchpad/TouchpadComponent.java (60%) rename src/main/java/dev/isxander/controlify/{ => impl}/controller/touchpad/Touchpads.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/controllermanager/ControllerBackend.java (85%) rename src/main/java/dev/isxander/controlify/{ => impl}/controllermanager/ControllerCoordinator.java (79%) rename src/main/java/dev/isxander/controlify/{ => impl}/controllermanager/ControllerManager.java (66%) rename src/main/java/dev/isxander/controlify/{controller/id => impl/controllermanager}/ControllerType.java (73%) rename src/main/java/dev/isxander/controlify/{controller/id => impl/controllermanager}/ControllerTypeManager.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/controllermanager/SDLControllerBackend.java (88%) rename src/main/java/dev/isxander/controlify/{ => impl}/controllermanager/SDLUniqueControllerID.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/controllermanager/UniqueControllerID.java (88%) rename src/main/java/dev/isxander/controlify/{ => impl}/debug/DebugProperties.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/ComponentAdderDriver.java (51%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/CompoundDriver.java (77%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/Driver.java (62%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/dualsense/DualsenseEffectsState.java (99%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/dualsense/DualsenseTriggerEffect.java (99%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/dualsense/DualsenseTriggerEffectTypes.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/sdl/DecodedGUID.java (98%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/sdl/SDL3GamepadDriver.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/sdl/SDL3JoystickDriver.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/sdl/SDLCommonDriver.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/sdl/SDLControllerEvent.java (99%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/sdl/SDLControllerEventStage.java (93%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/sdl/SDLException.java (91%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/sdl/SDLGamepadInputMapping.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/sdl/SDLInputConversions.java (97%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/sdl/SDLJoystickConnectionState.java (92%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/sdl/SDLNativesLoader.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/sdl/SDLToPhysicalStage.java (86%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/sdl/SDLUtil.java (94%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/steamdeck/CEFDebuggerFileResult.java (93%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/steamdeck/SteamDeckDriver.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/steamdeck/SteamDeckMode.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/driver/steamdeck/SteamDeckUtil.java (94%) rename src/main/java/dev/isxander/controlify/{ => impl}/font/BindingFontHelper.java (94%) rename src/main/java/dev/isxander/controlify/{ => impl}/font/InputFontMapper.java (91%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/ButtonGuideRenderer.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/components/FakePositionPlainTextButton.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/components/PlainTextWidget.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/controllers/BindController.java (82%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/controllers/Deadzone2DImageRenderer.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/guide/GuideInstanceImpl.java (81%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/guide/GuideRenderer.java (98%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/guide/InGameButtonGuide.java (75%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/guide/PrecomputedLines.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/layout/AbstractLayoutComponent.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/layout/AnchorPoint.java (92%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/layout/ColumnLayoutComponent.java (98%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/layout/PositionedComponent.java (97%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/layout/RenderComponent.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/layout/RowLayoutComponent.java (98%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/screen/AskToMapControllerScreen.java (77%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/screen/BindConsumerScreen.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/screen/BluetoothWarningScreen.java (79%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/screen/ControlifySettingsScreen.java (94%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/screen/ControllerConfigScreenFactory.java (92%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/screen/ControllerMappingMakerScreen.java (93%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/screen/DontInterruptScreen.java (77%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/screen/GlobalSettingsScreenFactory.java (94%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/screen/ProfileSelectionScreen.java (94%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/screen/RadialItems.java (92%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/screen/RadialMenuEditModel.java (97%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/screen/RadialMenuEditScreen.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/screen/RadialMenuPages.java (93%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/screen/RadialMenuScreen.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/gui/screen/SteamDeckAlerts.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/hid/ControllerHIDInfo.java (78%) rename src/main/java/dev/isxander/controlify/{ => impl}/hid/HIDDevice.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/hid/HIDID.java (85%) rename src/main/java/dev/isxander/controlify/{ => impl}/ingame/ControllerPlayerMovement.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/ingame/DualInput.java (97%) rename src/main/java/dev/isxander/controlify/{ => impl}/ingame/InGameInputHandler.java (92%) rename src/main/java/dev/isxander/controlify/{ => impl}/ingame/InputCurve.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/ingame/InputCurves.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/ingame/PickBlockAccessor.java (83%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/SensorType.java (93%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/mapping/ControllerMapping.java (97%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/mapping/ControllerMappingManager.java (92%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/mapping/DeadzoneGroup.java (92%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/mapping/GamepadInputs.java (97%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/mapping/GestureInputs.java (64%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/mapping/Inputs.java (94%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/mapping/JoystickInputs.java (91%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/mapping/MapType.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/mapping/MappingEntry.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/mapping/MappingEntryCodecs.java (98%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/pipeline/DeadzoneStage.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/pipeline/GestureRecogniserStage.java (56%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/pipeline/InputDomainExpansionStage.java (83%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/pipeline/InputPipeline.java (86%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/pipeline/MappingStage.java (68%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/pipeline/PhysicalToLogicalStage.java (93%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/pipeline/SensorAccumulatorStage.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/pipeline/SensorProcessorStage.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/signal/LogicalSignal.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/signal/PhysicalInputSignal.java (94%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/signal/PhysicalSensorSignal.java (88%) rename src/main/java/dev/isxander/controlify/{ => impl}/input/signal/PhysicalSignal.java (86%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/ArrayBackedEventHandler.java (91%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/Environment.java (78%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/Event.java (76%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/EventHandler.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/client/CreativeTabHelper.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/client/HudRenderLayer.java (86%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/client/PlatformClientUtil.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/client/PlatformClientUtilImpl.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/client/events/DisconnectedEvent.java (81%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/client/events/LifecycleEvent.java (81%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/client/events/ScreenRenderEvent.java (86%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/client/events/TickEvent.java (93%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/client/resource/ControlifyReloadListener.java (84%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/client/resource/SimpleControlifyReloadListener.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/main/PlatformMainUtil.java (80%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/main/PlatformMainUtilImpl.java (71%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/main/events/CommandRegistrationCallbackEvent.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/main/events/HandshakeCompletionEvent.java (86%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/main/events/PlayerJoinedEvent.java (81%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/network/C2SNetworkApi.java (92%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/network/S2CNetworkApi.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/platform/network/SidedNetworkApi.java (81%) rename src/main/java/dev/isxander/controlify/{ => impl}/reacharound/ReachAroundHandler.java (93%) rename src/main/java/dev/isxander/controlify/{ => impl}/reacharound/ReachAroundMode.java (94%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/ComponentProcessor.java (70%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/ComponentProcessorProvider.java (93%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/CustomFocus.java (84%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/Registry.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/ScreenControllerEventListener.java (50%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/ScreenProcessor.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/ScreenProcessorFactory.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/ScreenProcessorProvider.java (93%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/AbstractSliderComponentProcessor.java (67%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/AbstractButtonComponentProcessor.java (68%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/AbstractContainerScreenProcessor.java (83%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/AbstractSignEditScreenProcessor.java (80%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/AddServerLikeScreenProcessor.java (77%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/BundleItemSlotControllerAction.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/ChatScreenProcessor.java (81%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/CreateWorldScreenProcessor.java (67%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/CreativeModeInventoryScreenProcessor.java (77%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/EditBoxComponentProcessor.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/ItemSlotControllerAction.java (71%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/JoinMultiplayerScreenProcessor.java (86%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/LanguageSelectionListComponentProcessor.java (72%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/OptionsScreenProcessor.java (73%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/OptionsSubScreenProcessor.java (73%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/PauseScreenProcessor.java (81%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/RecipeBookScreenProcessor.java (88%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/SelectWorldScreenProcessor.java (76%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/ServerSelectionListEntryComponentProcessor.java (63%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/SliderComponentProcessor.java (83%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/TitleScreenProcessor.java (75%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/compat/vanilla/WorldListEntryComponentProcessor.java (67%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/keyboard/ChatKeyboardDucky.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/keyboard/CommonKeyboardHints.java (79%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/keyboard/ComponentKeyboardBehaviour.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/keyboard/FallbackKeyboardLayout.java (83%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/keyboard/InputTarget.java (97%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/keyboard/KeyWidget.java (93%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/keyboard/KeyboardLayout.java (98%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/keyboard/KeyboardLayoutManager.java (93%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/keyboard/KeyboardLayoutWithId.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/keyboard/KeyboardLayouts.java (88%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/keyboard/KeyboardOverlayScreen.java (97%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/keyboard/KeyboardWidget.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/screenop/keyboard/MixinInputTarget.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/server/ControlifyHandshake.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/server/ControlifyServer.java (91%) rename src/main/java/dev/isxander/controlify/{ => impl}/server/ControlifyServerConfig.java (84%) rename src/main/java/dev/isxander/controlify/{ => impl}/server/ServerPolicies.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/server/ServerPolicy.java (88%) rename src/main/java/dev/isxander/controlify/{ => impl}/server/VibrateCommand.java (87%) rename src/main/java/dev/isxander/controlify/{ => impl}/server/packets/EntityVibrationPacket.java (70%) rename src/main/java/dev/isxander/controlify/{ => impl}/server/packets/OriginVibrationPacket.java (70%) rename src/main/java/dev/isxander/controlify/{ => impl}/server/packets/ServerPolicyPacket.java (86%) rename src/main/java/dev/isxander/controlify/{ => impl}/server/packets/VibrationPacket.java (67%) rename src/main/java/dev/isxander/controlify/{ => impl}/sound/ControlifyClientSounds.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/CUtil.java (94%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/ColorUtils.java (91%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/ControllerUtils.java (92%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/CursorUtils.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/DebugDump.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/DebugLog.java (74%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/DebugOverlayHelper.java (98%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/DummyMixinTarget.java (88%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/Easings.java (94%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/HoldRepeatHelper.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/InitialScreenRegistryDuck.java (94%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/JsonTreeParser.java (97%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/LazyComponentDims.java (83%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/MinecraftUtil.java (97%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/MouseMinecraftCallNotifier.java (83%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/MthExt.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/Platform.java (98%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/PrecomputedComponentDims.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/ResizableRingBuffer.java (97%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/SetupWizard.java (97%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/SetupWizardManager.java (86%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/TrackingBodySubscriber.java (97%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/TrackingConsumer.java (89%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/UnhandledCompletableFutures.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/animation/api/Animatable.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/animation/api/Animation.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/animation/api/AnimationGroup.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/animation/api/AnimationSequence.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/animation/api/EasingFunction.java (98%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/animation/impl/AnimationGroupImpl.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/animation/impl/AnimationImpl.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/animation/impl/AnimationSequenceImpl.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/animation/impl/Animator.java (83%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/codec/CExtraCodecs.java (98%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/codec/FuzzyCodec.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/codec/FuzzyMapCodec.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/codec/SetCodec.java (98%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/codec/StrictEitherMapCodec.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/event/EventSink.java (61%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/event/EventSource.java (91%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/event/EventStage.java (94%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/event/EventTap.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/event/UnaryEventStage.java (63%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/log/AbstractControlifyLogger.java (98%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/log/ControlifyLogger.java (97%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/log/ControlifyMasterLogger.java (91%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/log/ControlifySubLogger.java (81%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/log/ControlifySubLoggerImpl.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/log/LogLevel.java (78%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/log/LogMessage.java (96%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/predicates/ClientEntityPredicate.java (98%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/predicates/ClientPredicateUtils.java (94%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/predicates/EntityEquipmentPredicate.java (98%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/render/BaseRenderState.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/render/CGuiElementRenderState.java (90%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/render/RenderUtils.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/utils/render/elements/CircleElementRenderState.java (92%) rename src/main/java/dev/isxander/controlify/{ => impl}/virtualmouse/SnapUtils.java (95%) rename src/main/java/dev/isxander/controlify/{ => impl}/virtualmouse/VirtualMouseBehaviour.java (88%) rename src/main/java/dev/isxander/controlify/{ => impl}/virtualmouse/VirtualMouseHandler.java (94%) rename src/main/java/dev/isxander/controlify/{ => impl}/wireless/LowBatteryNotifier.java (72%) delete mode 100644 src/main/java/dev/isxander/controlify/rumble/BasicRumbleEffect.java delete mode 100644 src/main/java/dev/isxander/controlify/rumble/ContinuousRumbleEffect.java delete mode 100644 src/main/java/dev/isxander/controlify/rumble/RumbleCapable.java delete mode 100644 src/main/java/dev/isxander/controlify/rumble/RumbleEffect.java delete mode 100644 src/main/java/dev/isxander/controlify/rumble/RumbleManager.java delete mode 100644 src/main/java/dev/isxander/controlify/rumble/RumbleSource.java create mode 100644 src/main/resources/assets/controlify/controller/activators.json rename src/main/resources/assets/controlify/{ => controller}/contextual/facts/container.json (100%) rename src/main/resources/assets/controlify/{ => controller}/contextual/facts/in_game.json (100%) rename src/main/resources/assets/controlify/{ => controller}/contextual/guide/container.json (100%) rename src/main/resources/assets/controlify/{ => controller}/contextual/guide/in_game.json (100%) rename src/main/resources/assets/controlify/{ => controller}/contextual/trigger_effect/in_game.json (100%) rename src/main/resources/assets/controlify/{controllers => controller}/controller_identification.json5 (100%) rename src/main/resources/assets/controlify/{controllers => controller}/default_bind/default.json (100%) rename src/main/resources/assets/controlify/{controllers => controller}/default_bind/dualsense.json (100%) rename src/main/resources/assets/controlify/{controllers => controller}/default_bind/switch.json (100%) rename src/main/resources/assets/controlify/{controllers => controller}/default_config.json (100%) rename src/main/resources/assets/controlify/{controllers/font_mappings => controller/font_mapping}/default.json (100%) rename src/main/resources/assets/controlify/{controllers/font_mappings => controller/font_mapping}/dualsense.json (100%) rename src/main/resources/assets/controlify/{controllers/font_mappings => controller/font_mapping}/dualshock3.json (100%) rename src/main/resources/assets/controlify/{controllers/font_mappings => controller/font_mapping}/dualshock4.json (100%) rename src/main/resources/assets/controlify/{controllers/font_mappings => controller/font_mapping}/steam_controller.json (100%) rename src/main/resources/assets/controlify/{controllers/font_mappings => controller/font_mapping}/steam_deck.json (100%) rename src/main/resources/assets/controlify/{controllers/font_mappings => controller/font_mapping}/switch.json (100%) rename src/main/resources/assets/controlify/{controllers/font_mappings => controller/font_mapping}/xbox_one.json (100%) rename src/main/resources/assets/controlify/{controllers => controller}/gamecontrollerdb-sdl2.txt (100%) rename src/main/resources/assets/controlify/{controllers => controller}/gamecontrollerdb-sdl3.txt (100%) rename src/main/resources/assets/controlify/{ => controller}/haptics/navigate.ogg (100%) rename src/main/resources/assets/controlify/{ => controller}/keyboard_layout/full/en_us.json (100%) rename src/main/resources/assets/controlify/{ => controller}/keyboard_layout/server_ip/en_us.json (100%) rename src/main/resources/assets/controlify/{ => controller}/keyboard_layout/simple/en_us.json (100%) rename src/main/resources/assets/controlify/{ => controller}/radial_icons.json (100%) create mode 100644 src/main/resources/assets/controlify/controller/rumble_sources.json delete mode 100644 src/main/resources/resourcepacks/legacy_console/assets/controlify/contextual/guide/in_game.json rename src/test/java/dev/isxander/controlify/{ => impl}/config/ConfigMigrationTestHelper.java (95%) rename src/test/java/dev/isxander/controlify/{ => impl}/contextual/FactDependencyGraphTest.java (98%) diff --git a/src/fabric/java/dev/isxander/controlify/fabric/ControlifyBootstrap.java b/src/fabric/java/dev/isxander/controlify/fabric/ControlifyBootstrap.java index 896effd42..e719426ad 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/ControlifyBootstrap.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/ControlifyBootstrap.java @@ -6,12 +6,12 @@ */ package dev.isxander.controlify.fabric; -import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.fabric.platform.client.FabricPlatformClientImpl; import dev.isxander.controlify.fabric.platform.FabricPlatformMainImpl; -import dev.isxander.controlify.platform.client.PlatformClientUtil; -import dev.isxander.controlify.platform.main.PlatformMainUtil; -import dev.isxander.controlify.server.ControlifyServer; +import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; +import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; +import dev.isxander.controlify.impl.server.ControlifyServer; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.DedicatedServerModInitializer; import net.fabricmc.api.ModInitializer; diff --git a/src/fabric/java/dev/isxander/controlify/fabric/compatibility/FabricCompatMixinPlatform.java b/src/fabric/java/dev/isxander/controlify/fabric/compatibility/FabricCompatMixinPlatform.java index 9a7096abf..b025c8736 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/compatibility/FabricCompatMixinPlatform.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/compatibility/FabricCompatMixinPlatform.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.fabric.compatibility; -import dev.isxander.controlify.compatibility.CompatMixinPlatform; +import dev.isxander.controlify.impl.compatibility.CompatMixinPlatform; import net.fabricmc.loader.api.FabricLoader; public final class FabricCompatMixinPlatform implements CompatMixinPlatform { diff --git a/src/fabric/java/dev/isxander/controlify/fabric/compatibility/ModMenuIntegration.java b/src/fabric/java/dev/isxander/controlify/fabric/compatibility/ModMenuIntegration.java index 634aba6e3..28cb8a801 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/compatibility/ModMenuIntegration.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/compatibility/ModMenuIntegration.java @@ -10,7 +10,7 @@ import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; -import dev.isxander.controlify.gui.screen.ControlifySettingsScreen; +import dev.isxander.controlify.impl.gui.screen.ControlifySettingsScreen; public class ModMenuIntegration implements ModMenuApi { @Override diff --git a/src/fabric/java/dev/isxander/controlify/fabric/platform/FabricBackedEventHandler.java b/src/fabric/java/dev/isxander/controlify/fabric/platform/FabricBackedEventHandler.java index a90f48a7e..eeb2d14ad 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/platform/FabricBackedEventHandler.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/platform/FabricBackedEventHandler.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.fabric.platform; -import dev.isxander.controlify.platform.EventHandler; +import dev.isxander.controlify.impl.platform.EventHandler; import net.fabricmc.fabric.api.event.EventFactory; public class FabricBackedEventHandler implements EventHandler { diff --git a/src/fabric/java/dev/isxander/controlify/fabric/platform/FabricPlatformMainImpl.java b/src/fabric/java/dev/isxander/controlify/fabric/platform/FabricPlatformMainImpl.java index 3b20c45cc..7f650640d 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/platform/FabricPlatformMainImpl.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/platform/FabricPlatformMainImpl.java @@ -6,16 +6,16 @@ */ package dev.isxander.controlify.fabric.platform; -import dev.isxander.controlify.api.entrypoint.ControlifyEntrypoint; +import dev.isxander.controlify.impl.api.entrypoint.ControlifyEntrypoint; import dev.isxander.controlify.fabric.platform.network.C2SNetworkApiFabric; import dev.isxander.controlify.fabric.platform.network.S2CNetworkApiFabric; -import dev.isxander.controlify.platform.Environment; -import dev.isxander.controlify.platform.main.PlatformMainUtilImpl; -import dev.isxander.controlify.platform.main.events.CommandRegistrationCallbackEvent; -import dev.isxander.controlify.platform.main.events.HandshakeCompletionEvent; -import dev.isxander.controlify.platform.main.events.PlayerJoinedEvent; -import dev.isxander.controlify.platform.network.C2SNetworkApi; -import dev.isxander.controlify.platform.network.S2CNetworkApi; +import dev.isxander.controlify.impl.platform.Environment; +import dev.isxander.controlify.impl.platform.main.PlatformMainUtilImpl; +import dev.isxander.controlify.impl.platform.main.events.CommandRegistrationCallbackEvent; +import dev.isxander.controlify.impl.platform.main.events.HandshakeCompletionEvent; +import dev.isxander.controlify.impl.platform.main.events.PlayerJoinedEvent; +import dev.isxander.controlify.impl.platform.network.C2SNetworkApi; +import dev.isxander.controlify.impl.platform.network.S2CNetworkApi; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.fabricmc.fabric.api.networking.v1.FriendlyByteBufs; import net.fabricmc.fabric.api.networking.v1.ServerLoginConnectionEvents; diff --git a/src/fabric/java/dev/isxander/controlify/fabric/platform/client/FAPIApiCreativeTabHelper.java b/src/fabric/java/dev/isxander/controlify/fabric/platform/client/FAPIApiCreativeTabHelper.java index 498c09a5e..dfbc9e63c 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/platform/client/FAPIApiCreativeTabHelper.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/platform/client/FAPIApiCreativeTabHelper.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.fabric.platform.client; -import dev.isxander.controlify.platform.client.CreativeTabHelper; +import dev.isxander.controlify.impl.platform.client.CreativeTabHelper; import net.fabricmc.fabric.api.client.creativetab.v1.FabricCreativeModeInventoryScreen; import net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen; import net.minecraft.world.item.CreativeModeTab; diff --git a/src/fabric/java/dev/isxander/controlify/fabric/platform/client/FabricPlatformClientImpl.java b/src/fabric/java/dev/isxander/controlify/fabric/platform/client/FabricPlatformClientImpl.java index abe5b8a50..90971484d 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/platform/client/FabricPlatformClientImpl.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/platform/client/FabricPlatformClientImpl.java @@ -6,14 +6,14 @@ */ package dev.isxander.controlify.fabric.platform.client; -import dev.isxander.controlify.platform.client.CreativeTabHelper; -import dev.isxander.controlify.platform.client.HudRenderLayer; -import dev.isxander.controlify.platform.client.PlatformClientUtilImpl; -import dev.isxander.controlify.platform.client.events.DisconnectedEvent; -import dev.isxander.controlify.platform.client.events.LifecycleEvent; -import dev.isxander.controlify.platform.client.events.ScreenRenderEvent; -import dev.isxander.controlify.platform.client.events.TickEvent; -import dev.isxander.controlify.platform.client.resource.ControlifyReloadListener; +import dev.isxander.controlify.impl.platform.client.CreativeTabHelper; +import dev.isxander.controlify.impl.platform.client.HudRenderLayer; +import dev.isxander.controlify.impl.platform.client.PlatformClientUtilImpl; +import dev.isxander.controlify.impl.platform.client.events.DisconnectedEvent; +import dev.isxander.controlify.impl.platform.client.events.LifecycleEvent; +import dev.isxander.controlify.impl.platform.client.events.ScreenRenderEvent; +import dev.isxander.controlify.impl.platform.client.events.TickEvent; +import dev.isxander.controlify.impl.platform.client.resource.ControlifyReloadListener; import dev.isxander.controlify.fabric.mixins.KeyBindingRegistryImplAccessor; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; diff --git a/src/fabric/java/dev/isxander/controlify/fabric/platform/network/C2SNetworkApiFabric.java b/src/fabric/java/dev/isxander/controlify/fabric/platform/network/C2SNetworkApiFabric.java index 9faf70398..6de311d99 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/platform/network/C2SNetworkApiFabric.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/platform/network/C2SNetworkApiFabric.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.fabric.platform.network; -import dev.isxander.controlify.platform.network.C2SNetworkApi; +import dev.isxander.controlify.impl.platform.network.C2SNetworkApi; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; diff --git a/src/fabric/java/dev/isxander/controlify/fabric/platform/network/S2CNetworkApiFabric.java b/src/fabric/java/dev/isxander/controlify/fabric/platform/network/S2CNetworkApiFabric.java index 7c4f16cb9..cfa43b0fe 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/platform/network/S2CNetworkApiFabric.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/platform/network/S2CNetworkApiFabric.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.fabric.platform.network; -import dev.isxander.controlify.platform.network.S2CNetworkApi; +import dev.isxander.controlify.impl.platform.network.S2CNetworkApi; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; diff --git a/src/gametest/java/dev/isxander/controlify/gametest/framework/CTestUtil.java b/src/gametest/java/dev/isxander/controlify/gametest/framework/CTestUtil.java index aacae76ae..2ef684ac9 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/framework/CTestUtil.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/framework/CTestUtil.java @@ -6,16 +6,9 @@ */ package dev.isxander.controlify.gametest.framework; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.config.settings.GlobalSettings; -import dev.isxander.controlify.config.settings.profile.ProfileSettings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.gyro.GyroState; -import dev.isxander.controlify.controllermanager.ControllerManager; -import dev.isxander.controlify.gametest.mixin.ControlifySettingsAccessor; import dev.isxander.controlify.gametest.mixin.ToastInstanceAccessor; import dev.isxander.controlify.gametest.mixin.ToastManagerAccessor; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.fabricmc.fabric.api.client.gametest.v1.context.ClientGameTestContext; import net.fabricmc.fabric.api.resource.v1.ResourceLoader; import net.fabricmc.fabric.api.resource.v1.pack.PackActivationType; @@ -36,10 +29,8 @@ import net.minecraft.world.entity.EntityType; import org.jetbrains.annotations.Nullable; -import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; -import java.util.stream.Collectors; import java.util.stream.Stream; @SuppressWarnings("UnstableApiUsage") diff --git a/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/ControlifyGameTestContext.java b/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/ControlifyGameTestContext.java index d748d7e31..e9f14ca2b 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/ControlifyGameTestContext.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/ControlifyGameTestContext.java @@ -6,17 +6,18 @@ */ package dev.isxander.controlify.gametest.framework.controller; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.InputMode; -import dev.isxander.controlify.config.settings.GlobalSettings; -import dev.isxander.controlify.config.settings.profile.ProfileSettings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.gyro.GyroState; -import dev.isxander.controlify.controllermanager.ControllerManager; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.api.InputMode; +import dev.isxander.controlify.impl.config.settings.GlobalSettings; +import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controller.gyro.GyroState; +import dev.isxander.controlify.impl.controllermanager.ControllerManager; import dev.isxander.controlify.gametest.mixin.ControlifySettingsAccessor; import dev.isxander.controlify.gametest.mixin.SDLControllerManagerAccessor; import dev.isxander.sdl.Sdl; import net.fabricmc.fabric.api.client.gametest.v1.context.ClientGameTestContext; +import org.joml.Vector3f; import java.util.List; import java.util.stream.Collectors; @@ -66,19 +67,19 @@ public void resetSettings() { .map(ControllerManager::getConnectedControllers) .orElseGet(List::of); var connectedUids = connectedControllers.stream() - .map(ControllerEntity::uid) + .map(ControllerImpl::uid) .collect(Collectors.toSet()); // Keep the existing objects for connected devices because input // components cache their DeviceSettings reference when attached. var deviceSettings = settingsAccessor.controlify_test$getDeviceSettings(); deviceSettings.keySet().retainAll(connectedUids); - for (ControllerEntity controller : connectedControllers) { + for (ControllerImpl controller : connectedControllers) { var device = settings.getOrCreateDeviceSettings(controller.uid()); device.name = controller.name(); device.lastSeen = System.currentTimeMillis(); - device.controllerType = controller.info().type().namespace(); - device.gyroCalibration.offset = new GyroState(); + device.controllerType = controller.info().type().id(); + device.gyroCalibration.offset = new Vector3f(); device.mapping = null; controller.setSettings(profileDefaults); diff --git a/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/DualsenseEffectDriver.java b/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/DualsenseEffectDriver.java index b50254d74..7101957e3 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/DualsenseEffectDriver.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/DualsenseEffectDriver.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.gametest.framework.controller; -import dev.isxander.controlify.driver.dualsense.DualsenseEffectsState; +import dev.isxander.controlify.impl.driver.dualsense.DualsenseEffectsState; import java.lang.foreign.MemorySegment; import java.nio.ByteBuffer; diff --git a/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/TestControllerContext.java b/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/TestControllerContext.java index b4a958cfe..df83e1073 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/TestControllerContext.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/TestControllerContext.java @@ -6,11 +6,11 @@ */ package dev.isxander.controlify.gametest.framework.controller; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controllermanager.SDLUniqueControllerID; -import dev.isxander.controlify.driver.sdl.SDLException; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controllermanager.SDLUniqueControllerID; +import dev.isxander.controlify.impl.driver.sdl.SDLException; +import dev.isxander.controlify.impl.utils.CUtil; import dev.isxander.sdl.Sdl; import dev.isxander.sdl.SdlGamepad; import dev.isxander.sdl.SdlJoystickHandle; @@ -45,7 +45,7 @@ public TestControllerContext( this.state = state; } - public ControllerEntity getControllerEntity() { + public ControllerImpl getControllerImpl() { return Controlify.instance().getControllerManager().orElseThrow() .getConnectedControllers().stream() .filter(c -> c.info().ucid().equals(new SDLUniqueControllerID(this.joystickId))) diff --git a/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/VirtualControllerBuilder.java b/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/VirtualControllerBuilder.java index c62a96d10..b5f2fb1e6 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/VirtualControllerBuilder.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/VirtualControllerBuilder.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.gametest.framework.controller; -import dev.isxander.controlify.driver.sdl.SDLException; +import dev.isxander.controlify.impl.driver.sdl.SDLException; import dev.isxander.controlify.test.framework.SdlVirtualJoystickDescBuilder; import dev.isxander.sdl.*; import net.fabricmc.fabric.api.client.gametest.v1.context.ClientGameTestContext; diff --git a/src/gametest/java/dev/isxander/controlify/gametest/mixin/ControlifySettingsAccessor.java b/src/gametest/java/dev/isxander/controlify/gametest/mixin/ControlifySettingsAccessor.java index 9b627f2ab..ed2a902c3 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/mixin/ControlifySettingsAccessor.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/mixin/ControlifySettingsAccessor.java @@ -6,10 +6,10 @@ */ package dev.isxander.controlify.gametest.mixin; -import dev.isxander.controlify.config.settings.ControlifySettings; -import dev.isxander.controlify.config.settings.GlobalSettings; -import dev.isxander.controlify.config.settings.device.DeviceSettings; -import dev.isxander.controlify.config.settings.profile.ProfileSettings; +import dev.isxander.controlify.impl.config.settings.ControlifySettings; +import dev.isxander.controlify.impl.config.settings.GlobalSettings; +import dev.isxander.controlify.impl.config.settings.device.DeviceSettings; +import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; diff --git a/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerManagerAccessor.java b/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerManagerAccessor.java index bd8a23aa6..0451fdda8 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerManagerAccessor.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerManagerAccessor.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.gametest.mixin; -import dev.isxander.controlify.controllermanager.SDLControllerManager; +import dev.isxander.controlify.impl.controllermanager.SDLControllerManager; import dev.isxander.sdl.Sdl; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; diff --git a/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerManagerMixin.java b/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerManagerMixin.java index e06af92b4..461803a2c 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerManagerMixin.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerManagerMixin.java @@ -8,12 +8,12 @@ import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controllermanager.SDLControllerManager; -import dev.isxander.controlify.controllermanager.SDLUniqueControllerID; -import dev.isxander.controlify.controllermanager.UniqueControllerID; -import dev.isxander.controlify.hid.ControllerHIDInfo; -import dev.isxander.controlify.utils.log.ControlifyLogger; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controllermanager.SDLControllerManager; +import dev.isxander.controlify.impl.controllermanager.SDLUniqueControllerID; +import dev.isxander.controlify.impl.controllermanager.UniqueControllerID; +import dev.isxander.controlify.impl.hid.ControllerHIDInfo; +import dev.isxander.controlify.impl.utils.log.ControlifyLogger; import dev.isxander.sdl.Sdl; import dev.isxander.sdl.SdlJoystickId; import org.spongepowered.asm.mixin.Final; @@ -30,11 +30,11 @@ public class SDLControllerManagerMixin { /// Only load virtual joysticks in test environments @WrapMethod(method = "createController") - private Optional preventNonVirtual( + private Optional preventNonVirtual( UniqueControllerID ucid, ControllerHIDInfo hidInfo, ControlifyLogger controllerLogger, - Operation> original + Operation> original ) { SdlJoystickId jid = ((SDLUniqueControllerID) ucid).jid(); 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 6d1bbbb1e..c7de9a886 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/AdaptiveTriggerTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/AdaptiveTriggerTests.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.gametest.tests; -import dev.isxander.controlify.driver.dualsense.DualsenseEffectsState; -import dev.isxander.controlify.driver.dualsense.DualsenseTriggerEffect; +import dev.isxander.controlify.impl.driver.dualsense.DualsenseEffectsState; +import dev.isxander.controlify.impl.driver.dualsense.DualsenseTriggerEffect; import dev.isxander.controlify.gametest.framework.CTestUtil; import dev.isxander.controlify.gametest.framework.controller.ControlifyGameTestContext; import dev.isxander.controlify.gametest.framework.controller.DualsenseEffectDriver; @@ -64,7 +64,7 @@ public void runTest(ClientGameTestContext context) { testTriggerEffectsDisabled(context, controller); - controller.getControllerEntity().settings().dualsense.triggerEffects = true; + controller.getControllerImpl().settings().dualsense.triggerEffects = true; context.waitTick(); @@ -76,7 +76,7 @@ public void runTest(ClientGameTestContext context) { testResourcePackPriority(context, world, controller); testServerTagRule(context, world, controller); - controller.getControllerEntity().settings().dualsense.triggerEffects = false; + controller.getControllerImpl().settings().dualsense.triggerEffects = false; context.waitTick(); waitForEffects(context, controller, new DualsenseEffectsState()); } @@ -91,7 +91,7 @@ private static void testTriggerEffectsDisabled( ClientGameTestContext context, TestControllerContext controller ) { - controller.getControllerEntity().settings().dualsense.triggerEffects = false; + controller.getControllerImpl().settings().dualsense.triggerEffects = false; context.waitTick(); waitForEffects(context, controller, new DualsenseEffectsState()); } diff --git a/src/gametest/java/dev/isxander/controlify/gametest/tests/ButtonGuideTests.java b/src/gametest/java/dev/isxander/controlify/gametest/tests/ButtonGuideTests.java index 8dbdbf306..04b87b985 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/ButtonGuideTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/ButtonGuideTests.java @@ -7,14 +7,14 @@ package dev.isxander.controlify.gametest.tests; import dev.isxander.controlify.api.contextual.InGameContext; -import dev.isxander.controlify.api.contextual.GuideLocation; -import dev.isxander.controlify.contextual.*; +import dev.isxander.controlify.impl.contextual.GuideLocation; +import dev.isxander.controlify.impl.contextual.*; import dev.isxander.controlify.gametest.framework.CEntityTypes; import dev.isxander.controlify.gametest.framework.CTestUtil; import dev.isxander.controlify.gametest.framework.TestUnitWorldContext; import dev.isxander.controlify.gametest.framework.controller.ControlifyGameTestContext; -import dev.isxander.controlify.gui.guide.GuideInstanceImpl; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.gui.guide.GuideInstanceImpl; +import dev.isxander.controlify.impl.utils.CUtil; import net.fabricmc.fabric.api.client.gametest.v1.FabricClientGameTest; import net.fabricmc.fabric.api.client.gametest.v1.context.ClientGameTestContext; import net.minecraft.commands.arguments.EntityAnchorArgument; @@ -52,7 +52,7 @@ public void runTest(ClientGameTestContext context) { .orElseThrow(); assertRuleLocation(ruleEngine, "inventory", GuideLocation.LEFT); assertRuleLocation(ruleEngine, "radial_menu", GuideLocation.LEFT); - var inGameContext = InGameContext.create(minecraft, controller.getControllerEntity()); + var inGameContext = InGameContext.create(minecraft, controller.getControllerImpl()); var state = domain.calculateState(inGameContext, ruleEngine.factDependencies()); assertFact(state, "looking_at_entity"); assertFact(state, "looking_at_merchant"); 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 3754daea7..623dda7ee 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/KeyboardLayoutTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/KeyboardLayoutTests.java @@ -8,8 +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.controlify.impl.screenop.keyboard.KeyboardWidget; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import dev.isxander.sdl.SdlGamepad; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.gametest.v1.FabricClientGameTest; diff --git a/src/gametest/java/dev/isxander/controlify/gametest/tests/ManyHotplugTests.java b/src/gametest/java/dev/isxander/controlify/gametest/tests/ManyHotplugTests.java index f640319b7..d63eaa856 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/ManyHotplugTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/ManyHotplugTests.java @@ -29,7 +29,7 @@ public void runTest(ClientGameTestContext context) { context.waitTicks(3); // finds within ControllerManager if it exists - controller.getControllerEntity(); + controller.getControllerImpl(); } CTestUtil.clearToasts(context); } diff --git a/src/gametest/java/dev/isxander/controlify/gametest/tests/RecipeBookNavigationTests.java b/src/gametest/java/dev/isxander/controlify/gametest/tests/RecipeBookNavigationTests.java index 4cc635bef..df6b882fc 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/RecipeBookNavigationTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/RecipeBookNavigationTests.java @@ -6,14 +6,14 @@ */ package dev.isxander.controlify.gametest.tests; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.InputMode; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.api.InputMode; import dev.isxander.controlify.gametest.framework.TestUnitWorldContext; import dev.isxander.controlify.gametest.framework.controller.ControlifyGameTestContext; import dev.isxander.controlify.gametest.mixin.RecipeBookPageAccessor; import dev.isxander.controlify.mixins.feature.virtualmouse.snapping.RecipeBookComponentAccessor; -import dev.isxander.controlify.screenop.compat.vanilla.RecipeBookScreenProcessor; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.screenop.compat.vanilla.RecipeBookScreenProcessor; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import dev.isxander.sdl.SdlGamepad; import net.fabricmc.fabric.api.client.gametest.v1.FabricClientGameTest; import net.fabricmc.fabric.api.client.gametest.v1.context.ClientGameTestContext; @@ -46,7 +46,7 @@ public void runTest(ClientGameTestContext context) { context.runOnClient(client -> { client.player.getRecipeBook().setOpen(RecipeBookType.CRAFTING, true); client.player.getRecipeBook().setFiltering(RecipeBookType.CRAFTING, false); - Controlify.instance().setCurrentController(controller.getControllerEntity(), true); + Controlify.instance().setCurrentController(controller.getControllerImpl(), true); Controlify.instance().setInputMode(InputMode.CONTROLLER); }); region.runOnServer((level, player) -> { 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 b5628ff39..e420f23dc 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/ResetControlifySettingsTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/ResetControlifySettingsTests.java @@ -6,12 +6,12 @@ */ package dev.isxander.controlify.gametest.tests; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.controller.gyro.GyroState; -import dev.isxander.controlify.gametest.framework.CTestUtil; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.controller.gyro.GyroState; import dev.isxander.controlify.gametest.framework.controller.ControlifyGameTestContext; import net.fabricmc.fabric.api.client.gametest.v1.FabricClientGameTest; import net.fabricmc.fabric.api.client.gametest.v1.context.ClientGameTestContext; +import org.joml.Vector3f; /// Ensures that {@link ControlifyGameTestContext#resetSettings()} /// works appropriately, not leaving behind any stale references. @@ -25,11 +25,11 @@ public void runTest(ClientGameTestContext context) { .withXbox() .attach()) { var controlify = Controlify.instance(); - var controllerEntity = controller.getControllerEntity(); + var ControllerImpl = controller.getControllerImpl(); var settings = controlify.config().getSettings(); - var cachedDeviceSettings = settings.getOrCreateDeviceSettings(controllerEntity.uid()); + var cachedDeviceSettings = settings.getOrCreateDeviceSettings(ControllerImpl.uid()); - cachedDeviceSettings.gyroCalibration.offset = new GyroState(1f, 2f, 3f); + cachedDeviceSettings.gyroCalibration.offset = new Vector3f(1f, 2f, 3f); settings.globalSettings().mixedInput = true; controlifyContext.resetSettings(); @@ -37,13 +37,13 @@ public void runTest(ClientGameTestContext context) { if (settings.globalSettings().mixedInput) { throw new AssertionError("Global settings were not reset"); } - if (cachedDeviceSettings != settings.getOrCreateDeviceSettings(controllerEntity.uid())) { + if (cachedDeviceSettings != settings.getOrCreateDeviceSettings(ControllerImpl.uid())) { throw new AssertionError("Connected controller device settings reference was replaced"); } if (!cachedDeviceSettings.gyroCalibration.offset.equals(new GyroState())) { throw new AssertionError("Device settings were not reset"); } - if (controllerEntity.settings() != controlify.config().getActiveProfile()) { + if (ControllerImpl.settings() != controlify.config().getActiveProfile()) { throw new AssertionError("Controller was not rebound to the default active profile"); } } 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 231263390..05fd3785b 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/ServerPolicyTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/ServerPolicyTests.java @@ -6,12 +6,11 @@ */ package dev.isxander.controlify.gametest.tests; -import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.gametest.framework.CTestUtil; import dev.isxander.controlify.gametest.framework.controller.ControlifyGameTestContext; -import dev.isxander.controlify.server.ControlifyServer; -import dev.isxander.controlify.server.ControlifyServerConfig; -import dev.isxander.controlify.server.ServerPolicies; +import dev.isxander.controlify.impl.server.ControlifyServer; +import dev.isxander.controlify.impl.server.ControlifyServerConfig; import net.fabricmc.fabric.api.client.gametest.v1.FabricClientGameTest; import net.fabricmc.fabric.api.client.gametest.v1.context.ClientGameTestContext; import net.minecraft.server.MinecraftServer; 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 d66c54502..b6dcfde3d 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/VirtualControllerInputTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/VirtualControllerInputTests.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.gametest.tests; -import dev.isxander.controlify.input.mapping.GamepadInputs; -import dev.isxander.controlify.controller.input.InputComponent; +import dev.isxander.controlify.impl.input.mapping.GamepadInputs; +import dev.isxander.controlify.impl.controller.input.InputComponent; import dev.isxander.controlify.gametest.framework.CTestUtil; import dev.isxander.controlify.gametest.framework.controller.ControlifyGameTestContext; import dev.isxander.controlify.gametest.framework.controller.TestControllerContext; @@ -21,8 +21,8 @@ //? if >=26.3 { import com.mojang.blaze3d.platform.SDLEventHandler; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.InputMode; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.api.InputMode; import dev.isxander.controlify.gametest.mixin.SDLEventHandlerAccessor; import org.lwjgl.sdl.SDL_Event; //?} @@ -58,8 +58,8 @@ public void runTest(ClientGameTestContext context) { try (var controller = controlify.virtualControllerBuilder() .withXbox() .attach()) { - var controllerEntity = controller.getControllerEntity(); - var input = controllerEntity.input().orElseThrow(); + var ControllerImpl = controller.getControllerImpl(); + var input = ControllerImpl.input().orElseThrow(); // Fabric cancels real mouse input during tests. Controlify must not // change input mode before that cancellation takes effect. diff --git a/src/main/java/dev/isxander/controlify/action/ActionSpecRegistry.java b/src/main/java/dev/isxander/controlify/action/ActionSpecRegistry.java deleted file mode 100644 index 1fc2a2ca5..000000000 --- a/src/main/java/dev/isxander/controlify/action/ActionSpecRegistry.java +++ /dev/null @@ -1,5 +0,0 @@ -package dev.isxander.controlify.action; - -public interface ActionSpecRegistry { - ActionAccessor register(ActionSpec actionSpec); -} diff --git a/src/main/java/dev/isxander/controlify/action/activator/NoopActivator.java b/src/main/java/dev/isxander/controlify/action/activator/NoopActivator.java deleted file mode 100644 index d4d4e3632..000000000 --- a/src/main/java/dev/isxander/controlify/action/activator/NoopActivator.java +++ /dev/null @@ -1,23 +0,0 @@ -package dev.isxander.controlify.action.activator; - -import dev.isxander.controlify.action.Accumulator; -import dev.isxander.controlify.action.Activator; -import dev.isxander.controlify.input.LogicalInput; -import dev.isxander.controlify.input.signal.LogicalSignal; - -public class NoopActivator implements Activator { - @Override - public void onSignal(LogicalSignal signal, Accumulator acc) { - - } - - @Override - public LogicalInput boundInput() { - return null; - } - - @Override - public String describe() { - return "Noop"; - } -} diff --git a/src/main/java/dev/isxander/controlify/api/ControlifyApi.java b/src/main/java/dev/isxander/controlify/api/ControlifyApi.java index 0a169145f..260cc610e 100644 --- a/src/main/java/dev/isxander/controlify/api/ControlifyApi.java +++ b/src/main/java/dev/isxander/controlify/api/ControlifyApi.java @@ -6,12 +6,11 @@ */ package dev.isxander.controlify.api; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.InputMode; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.rumble.RumbleEffect; -import dev.isxander.controlify.rumble.RumbleSource; -import org.jetbrains.annotations.NotNull; +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.api.controller.rumble.RumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleEffectInstance; +import net.minecraft.resources.Identifier; import java.util.Optional; @@ -28,20 +27,20 @@ public interface ControlifyApi { /** * The controller that is currently enabled and in use. * If there is no controller disconnected or disabled, this will return {@link Optional#empty()}. - * This is the controller that is used for {@link dev.isxander.controlify.api.event.ControlifyEvents#ACTIVE_CONTROLLER_TICKED} + * This is the controller that is used for {@link ControlifyEvents#ACTIVE_CONTROLLER_TICKED} */ - @NotNull Optional getCurrentController(); + Optional getCurrentController(); /** * The last input received: a controller or keyboard/mouse. */ - @NotNull InputMode currentInputMode(); - boolean setInputMode(@NotNull InputMode mode); + InputMode currentInputMode(); + boolean setInputMode(InputMode mode); - default void playRumbleEffect(@NotNull RumbleSource rumbleSource, @NotNull RumbleEffect rumbleEffect) { - getCurrentController() - .flatMap(ControllerEntity::rumble) - .ifPresent(r -> r.rumbleManager().play(rumbleSource, rumbleEffect)); + default Optional playRumbleEffect(Identifier effectId, RumbleEffect rumbleEffect) { + return getCurrentController() + .flatMap(Controller::rumble) + .map(r -> r.playEffect(effectId, rumbleEffect)); } static ControlifyApi get() { diff --git a/src/main/java/dev/isxander/controlify/api/event/ControlifyEvents.java b/src/main/java/dev/isxander/controlify/api/ControlifyEvents.java similarity index 79% rename from src/main/java/dev/isxander/controlify/api/event/ControlifyEvents.java rename to src/main/java/dev/isxander/controlify/api/ControlifyEvents.java index 34c721b70..87b980f47 100644 --- a/src/main/java/dev/isxander/controlify/api/event/ControlifyEvents.java +++ b/src/main/java/dev/isxander/controlify/api/ControlifyEvents.java @@ -4,12 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.api.event; +package dev.isxander.controlify.api; -import dev.isxander.controlify.InputMode; -import dev.isxander.controlify.api.ingameinput.LookInputModifier; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.platform.EventHandler; +import dev.isxander.controlify.impl.api.ingameinput.LookInputModifier; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.platform.EventHandler; import org.jetbrains.annotations.Nullable; public final class ControlifyEvents { @@ -55,19 +54,19 @@ public final class ControlifyEvents { */ public static final EventHandler LOOK_INPUT_MODIFIER = EventHandler.createPlatformBackedEvent(); - public record ControllerConnected(ControllerEntity controller, boolean hotplugged) { + public record ControllerConnected(ControllerImpl controller, boolean hotplugged) { } - public record ControllerDisconnected(ControllerEntity controller) { + public record ControllerDisconnected(ControllerImpl controller) { } - public record CurrentControllerChanged(@Nullable ControllerEntity controller, boolean changeInputMode) { + public record CurrentControllerChanged(@Nullable ControllerImpl controller, boolean changeInputMode) { } public record InputModeChanged(InputMode mode) { } - public record ControllerStateUpdate(ControllerEntity controller) { + public record ControllerStateUpdate(ControllerImpl controller) { } public record VirtualMouseToggled(boolean enabled) { diff --git a/src/main/java/dev/isxander/controlify/InputMode.java b/src/main/java/dev/isxander/controlify/api/InputMode.java similarity index 89% rename from src/main/java/dev/isxander/controlify/InputMode.java rename to src/main/java/dev/isxander/controlify/api/InputMode.java index 3bedace21..fb6162b8e 100644 --- a/src/main/java/dev/isxander/controlify/InputMode.java +++ b/src/main/java/dev/isxander/controlify/api/InputMode.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify; +package dev.isxander.controlify.api; public enum InputMode { KEYBOARD_MOUSE, diff --git a/src/main/java/dev/isxander/controlify/action/Action.java b/src/main/java/dev/isxander/controlify/api/action/Action.java similarity index 65% rename from src/main/java/dev/isxander/controlify/action/Action.java rename to src/main/java/dev/isxander/controlify/api/action/Action.java index 39de6c9fe..05046ecd4 100644 --- a/src/main/java/dev/isxander/controlify/action/Action.java +++ b/src/main/java/dev/isxander/controlify/api/action/Action.java @@ -1,6 +1,6 @@ -package dev.isxander.controlify.action; +package dev.isxander.controlify.api.action; -import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.api.input.LogicalInput; import org.jspecify.annotations.Nullable; /// An action is bound to a logical input and has one or more activators @@ -12,9 +12,12 @@ public interface Action { Channel.Latch latch(); Channel.Continuous continuous(); + /// When set to null, it is unbound. void setBoundInput(@Nullable LogicalInput input); + /// When set to null, it is currently unbound. @Nullable LogicalInput boundInput(); + /// When set to null, it is by default unbound. @Nullable LogicalInput defaultInput(); } diff --git a/src/main/java/dev/isxander/controlify/action/ActionSpec.java b/src/main/java/dev/isxander/controlify/api/action/ActionSpec.java similarity index 76% rename from src/main/java/dev/isxander/controlify/action/ActionSpec.java rename to src/main/java/dev/isxander/controlify/api/action/ActionSpec.java index c39361cb5..2d08312f6 100644 --- a/src/main/java/dev/isxander/controlify/action/ActionSpec.java +++ b/src/main/java/dev/isxander/controlify/api/action/ActionSpec.java @@ -1,9 +1,10 @@ -package dev.isxander.controlify.action; +package dev.isxander.controlify.api.action; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.impl.action.ActionSpecBuilder; +import dev.isxander.controlify.impl.action.ActionSpecBuilderImpl; +import dev.isxander.controlify.impl.controller.ControllerImpl; import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; -import org.jspecify.annotations.NonNull; import java.util.Comparator; import java.util.Set; @@ -23,10 +24,10 @@ public record ActionSpec( Component description, Component category, Set contexts, - Predicate shouldExpose + Predicate shouldExpose ) implements Comparable { @Override - public int compareTo(@NonNull ActionSpec o) { + public int compareTo(ActionSpec o) { return Comparator.comparing(ActionSpec::id) .compare(this, o); } diff --git a/src/main/java/dev/isxander/controlify/action/Channel.java b/src/main/java/dev/isxander/controlify/api/action/Channel.java similarity index 94% rename from src/main/java/dev/isxander/controlify/action/Channel.java rename to src/main/java/dev/isxander/controlify/api/action/Channel.java index dfa89cfbc..120106d3f 100644 --- a/src/main/java/dev/isxander/controlify/action/Channel.java +++ b/src/main/java/dev/isxander/controlify/api/action/Channel.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.action; +package dev.isxander.controlify.api.action; /// A channel is a way of interpreting the output state of a given {@link Action}. /// An {@link Action action's} bindings sends output to any of these channels. diff --git a/src/main/java/dev/isxander/controlify/api/action/package-info.java b/src/main/java/dev/isxander/controlify/api/action/package-info.java new file mode 100644 index 000000000..cbccfc406 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/action/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package dev.isxander.controlify.api.action; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/api/contextual/ContainerContext.java b/src/main/java/dev/isxander/controlify/api/contextual/ContainerContext.java index 7f6aba855..a997271b9 100644 --- a/src/main/java/dev/isxander/controlify/api/contextual/ContainerContext.java +++ b/src/main/java/dev/isxander/controlify/api/contextual/ContainerContext.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.api.contextual; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.impl.controller.ControllerImpl; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; @@ -17,7 +17,7 @@ public record ContainerContext( @Nullable Slot hoveredSlot, ItemStack holdingItem, boolean cursorOutsideContainer, - ControllerEntity controller, + ControllerImpl controller, GuideVerbosity verbosity ) implements Context { } diff --git a/src/main/java/dev/isxander/controlify/api/contextual/Context.java b/src/main/java/dev/isxander/controlify/api/contextual/Context.java index e77d7598f..c25b54667 100644 --- a/src/main/java/dev/isxander/controlify/api/contextual/Context.java +++ b/src/main/java/dev/isxander/controlify/api/contextual/Context.java @@ -6,10 +6,10 @@ */ package dev.isxander.controlify.api.contextual; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.api.controller.Controller; public interface Context { - ControllerEntity controller(); + Controller controller(); GuideVerbosity verbosity(); } diff --git a/src/main/java/dev/isxander/controlify/api/contextual/ContextualStateContributor.java b/src/main/java/dev/isxander/controlify/api/contextual/ContextualStateContributor.java index 5b0df668b..6ef0b994a 100644 --- a/src/main/java/dev/isxander/controlify/api/contextual/ContextualStateContributor.java +++ b/src/main/java/dev/isxander/controlify/api/contextual/ContextualStateContributor.java @@ -14,7 +14,7 @@ default ContextualStateContributor andThen(ContextualStateContributor ContextualStateContributor noop() { - return (context, sink) -> {}; + return (_, _) -> {}; } @SafeVarargs diff --git a/src/main/java/dev/isxander/controlify/api/contextual/InGameContext.java b/src/main/java/dev/isxander/controlify/api/contextual/InGameContext.java index bfcf9a0d4..e809c7692 100644 --- a/src/main/java/dev/isxander/controlify/api/contextual/InGameContext.java +++ b/src/main/java/dev/isxander/controlify/api/contextual/InGameContext.java @@ -6,7 +6,8 @@ */ package dev.isxander.controlify.api.contextual; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.mixins.feature.guide.ingame.MinecraftAccessor; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; @@ -21,11 +22,11 @@ public record InGameContext( LocalPlayer player, ClientLevel level, HitResult hitResult, - ControllerEntity controller, + Controller controller, GuideVerbosity verbosity ) implements Context { @ApiStatus.Internal - public static InGameContext create(Minecraft minecraft, ControllerEntity controller) { + public static InGameContext create(Minecraft minecraft, Controller controller) { var hitResult = minecraft.hitResult; if (hitResult == null) { ((MinecraftAccessor) minecraft).controlify$invokePick(1f); @@ -38,7 +39,7 @@ public static InGameContext create(Minecraft minecraft, ControllerEntity control Objects.requireNonNull(minecraft.level, "level"), hitResult, controller, - controller.settings().generic.guide.verbosity + ((ControllerImpl) controller).settings().generic.guide.verbosity ); } } diff --git a/src/main/java/dev/isxander/controlify/api/contextual/TriggerEffectInstance.java b/src/main/java/dev/isxander/controlify/api/contextual/TriggerEffectInstance.java index 52551b404..673af410d 100644 --- a/src/main/java/dev/isxander/controlify/api/contextual/TriggerEffectInstance.java +++ b/src/main/java/dev/isxander/controlify/api/contextual/TriggerEffectInstance.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.api.contextual; -import dev.isxander.controlify.driver.dualsense.DualsenseTriggerEffect; +import dev.isxander.controlify.impl.driver.dualsense.DualsenseTriggerEffect; import org.jetbrains.annotations.ApiStatus; public interface TriggerEffectInstance extends ContextualInstance { diff --git a/src/main/java/dev/isxander/controlify/api/contextual/package-info.java b/src/main/java/dev/isxander/controlify/api/contextual/package-info.java new file mode 100644 index 000000000..ea2ec8aa8 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/contextual/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package dev.isxander.controlify.api.contextual; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/api/controller/Controller.java b/src/main/java/dev/isxander/controlify/api/controller/Controller.java new file mode 100644 index 000000000..734b6b287 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/controller/Controller.java @@ -0,0 +1,40 @@ +package dev.isxander.controlify.api.controller; + +import dev.isxander.controlify.api.controller.rumble.RumbleComponent; +import net.minecraft.resources.Identifier; + +import java.util.Optional; + +/// The API-safe controller instance. +public interface Controller { + /// The unique identifier of this controller. + /// The same physical controller should have the same + /// identifier after being unplugged and replugged. + String uid(); + + /// The friendly name of this controller. + /// + /// This may use {@link driverReportedName}, but the + /// controller's type may override it, or the user may override + /// it. + /// + /// Because the user may override this, it is not a + /// guarantee that this name is constant for the lifetime + /// of this controller object. + String name(); + + /// The friendly name of this controller reported by + /// the underlying controller driver. + /// + /// To get the display name of this controller, you + /// should use {@link name}. + String driverReportedName(); + + /// The identifier of this controller's type. + /// This is the same ID defined by `controller_identification.json5`. + Identifier typeId(); + + /// Get the rumble component that may be attached to this controller. + /// If the returned optional is empty, then the controller is not capable of rumble. + Optional rumble(); +} diff --git a/src/main/java/dev/isxander/controlify/api/controller/package-info.java b/src/main/java/dev/isxander/controlify/api/controller/package-info.java new file mode 100644 index 000000000..a6310cc72 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/controller/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package dev.isxander.controlify.api.controller; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/api/controller/rumble/ContinuousRumbleEffect.java b/src/main/java/dev/isxander/controlify/api/controller/rumble/ContinuousRumbleEffect.java new file mode 100644 index 000000000..71a6e0972 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/controller/rumble/ContinuousRumbleEffect.java @@ -0,0 +1,34 @@ +package dev.isxander.controlify.api.controller.rumble; + +import net.minecraft.world.phys.Vec3; +import java.util.function.BooleanSupplier; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.Supplier; + +/// An immutable effect definition with independent timing for every playback. +public non-sealed interface ContinuousRumbleEffect extends RumbleEffect { + @Override ContinuousRumbleEffect stopCondition(BooleanSupplier condition); + + ContinuousRumbleEffect byTick(IntFunction state); + + ContinuousRumbleEffect constant(float strong, float weak); + + /// Sets the inactivity timeout. Zero disables it; keepAlive resets its clock. + ContinuousRumbleEffect timeout(int ticks); + int timeoutTicks(); + + /// Sets the minimum duration before timeout or a stop condition can finish playback. + /// Explicitly stopping the playback handle always takes effect immediately. + ContinuousRumbleEffect minTime(int ticks); + int minimumTicks(); + + /// Applies distance attenuation relative to the camera, using squared distance / range². + /// Playback ends when no camera is available (subject to the minimum duration). + ContinuousRumbleEffect inWorld( + Supplier location, + float minMagnitude, float maxMagnitude, + float range, + Function falloff + ); +} diff --git a/src/main/java/dev/isxander/controlify/api/controller/rumble/FiniteRumbleEffect.java b/src/main/java/dev/isxander/controlify/api/controller/rumble/FiniteRumbleEffect.java new file mode 100644 index 000000000..11a2b75a4 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/controller/rumble/FiniteRumbleEffect.java @@ -0,0 +1,27 @@ +package dev.isxander.controlify.api.controller.rumble; + +import java.util.Objects; +import java.util.function.BooleanSupplier; +import java.util.function.Function; +import java.util.function.IntFunction; + +/// An immutable, finite effect definition. Each configuration method returns a copy. +public non-sealed interface FiniteRumbleEffect extends RumbleEffect { + @Override + FiniteRumbleEffect stopCondition(BooleanSupplier condition); + + int durationTicks(); + + /// Replaces the waveform with one sample for each zero-based tick. + FiniteRumbleEffect byTick(IntFunction state, int durationTicks); + + /// Replaces the waveform, sampling time in [0, 1), as tick / duration. + FiniteRumbleEffect byTime(Function state, int durationTicks); + + FiniteRumbleEffect constant(float strong, float weak, int durationTicks); + + /// Appends another waveform, retaining this effect's stop conditions. + FiniteRumbleEffect then(FiniteRumbleEffect next); + + FiniteRumbleEffect repeat(int count); +} diff --git a/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleComponent.java b/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleComponent.java new file mode 100644 index 000000000..11a514bb9 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleComponent.java @@ -0,0 +1,11 @@ +package dev.isxander.controlify.api.controller.rumble; + +import net.minecraft.resources.Identifier; + +public interface RumbleComponent { + /// Queues independent playback of a reusable effect definition for the next rumble tick. + /// Stopping the returned handle affects only this playback. + /// The effect ID identifies the event causing the rumble, allowing resource packs + /// to assign its rumble source independently of the waveform. + RumbleEffectInstance playEffect(Identifier effectId, RumbleEffect effect); +} diff --git a/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleEffect.java b/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleEffect.java new file mode 100644 index 000000000..1b71b55a1 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleEffect.java @@ -0,0 +1,28 @@ +package dev.isxander.controlify.api.controller.rumble; + +import dev.isxander.controlify.impl.controller.rumble.FiniteRumbleEffectImpl; +import dev.isxander.controlify.impl.controller.rumble.ContinuousRumbleEffectImpl; + +import java.util.function.BooleanSupplier; + +/// A reusable effect definition. Configuration methods return new definitions; +/// playback state is held by the instance returned from RumbleComponent.playEffect. +/// Callbacks may be shared by multiple playbacks and are invoked on the client thread. +public sealed interface RumbleEffect permits FiniteRumbleEffect, ContinuousRumbleEffect { + /// Samples this definition at a zero-based playback tick. + RumbleState stateAt(int tick); + + /// Returns whether playback should end before its next sample. + boolean shouldStop(); + + /// Returns a copy with an additional stop condition. Conditions are ORed. + RumbleEffect stopCondition(BooleanSupplier condition); + + static FiniteRumbleEffect finite() { + return new FiniteRumbleEffectImpl(); + } + + static ContinuousRumbleEffect continuous() { + return new ContinuousRumbleEffectImpl(); + } +} diff --git a/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleEffectInstance.java b/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleEffectInstance.java new file mode 100644 index 000000000..5a0a7f4cc --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleEffectInstance.java @@ -0,0 +1,26 @@ +package dev.isxander.controlify.api.controller.rumble; + +/// A handle on a specific instance of a queued, playing, or played rumble +/// effect. +public interface RumbleEffectInstance { + /// @return true if the queued rumble effect is playing, or has finished playing + boolean hasStarted(); + + /// @return true if queued rumble effect has finished playing. + boolean hasFinished(); + + /// Stops the effect before it has finished. + /// + /// @return true if the effect was stopped as a result of this method invocation. + boolean stop(); + + /// Resets the age of this effect instance. + /// Only applicable for continuous effects, it prevents + /// them from timing out. This should be called frequently in the + /// context that the effect should stay viable. + /// If the effect has already finished playing, the effect cannot be kept + /// alive as it is already dead. + /// + /// @return true if the effect was successfully kept alive + boolean keepAlive(); +} diff --git a/src/main/java/dev/isxander/controlify/rumble/RumbleState.java b/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleState.java similarity index 94% rename from src/main/java/dev/isxander/controlify/rumble/RumbleState.java rename to src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleState.java index a9e2d7672..8268caabc 100644 --- a/src/main/java/dev/isxander/controlify/rumble/RumbleState.java +++ b/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleState.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.rumble; +package dev.isxander.controlify.api.controller.rumble; import com.mojang.serialization.Codec; diff --git a/src/main/java/dev/isxander/controlify/rumble/TriggerRumbleState.java b/src/main/java/dev/isxander/controlify/api/controller/rumble/TriggerRumbleState.java similarity index 89% rename from src/main/java/dev/isxander/controlify/rumble/TriggerRumbleState.java rename to src/main/java/dev/isxander/controlify/api/controller/rumble/TriggerRumbleState.java index d09650f78..236db1ad5 100644 --- a/src/main/java/dev/isxander/controlify/rumble/TriggerRumbleState.java +++ b/src/main/java/dev/isxander/controlify/api/controller/rumble/TriggerRumbleState.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.rumble; +package dev.isxander.controlify.api.controller.rumble; public record TriggerRumbleState(float left, float right) { public static final TriggerRumbleState NONE = new TriggerRumbleState(0.0F, 0.0F); diff --git a/src/main/java/dev/isxander/controlify/api/controller/rumble/package-info.java b/src/main/java/dev/isxander/controlify/api/controller/rumble/package-info.java new file mode 100644 index 000000000..db9e76381 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/controller/rumble/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package dev.isxander.controlify.api.controller.rumble; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/api/buttonguide/ButtonGuideApi.java b/src/main/java/dev/isxander/controlify/api/gui/ButtonGuideApi.java similarity index 65% rename from src/main/java/dev/isxander/controlify/api/buttonguide/ButtonGuideApi.java rename to src/main/java/dev/isxander/controlify/api/gui/ButtonGuideApi.java index bf64bc3d3..22f299964 100644 --- a/src/main/java/dev/isxander/controlify/api/buttonguide/ButtonGuideApi.java +++ b/src/main/java/dev/isxander/controlify/api/gui/ButtonGuideApi.java @@ -4,13 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.api.buttonguide; +package dev.isxander.controlify.api.gui; -import dev.isxander.controlify.action.ActionAccessor; -import dev.isxander.controlify.api.bind.InputBindingSupplier; -import dev.isxander.controlify.gui.ButtonGuideRenderer; +import dev.isxander.controlify.impl.action.ActionAccessor; +import dev.isxander.controlify.impl.gui.ButtonGuideRenderer; import net.minecraft.client.gui.screens.Screen; -import org.jetbrains.annotations.NotNull; import java.util.function.Supplier; @@ -26,23 +24,23 @@ private ButtonGuideApi() { /** * Makes the button render the image of the binding specified. * This does not invoke the button press on binding trigger, only renders the guide. - * Custom behaviour should be handled inside a {@link dev.isxander.controlify.screenop.ScreenProcessor} or {@link dev.isxander.controlify.screenop.ComponentProcessor} + * Custom behaviour should be handled inside a {@link dev.isxander.controlify.impl.screenop.ScreenProcessor} or {@link dev.isxander.controlify.impl.screenop.ComponentProcessor} * * @param button button to render the guide for * @param action the action binding to render * @param renderPredicate whether the guide should be rendered */ public static void addGuideToButton( - @NotNull T button, - @NotNull ActionAccessor action, - @NotNull ButtonGuidePredicate renderPredicate) { + T button, + ActionAccessor action, + ButtonGuidePredicate renderPredicate) { ButtonGuideRenderer.registerBindingForButton(button, () -> action, renderPredicate); } public static void addGuideToButton( - @NotNull T button, - @NotNull Supplier action, - @NotNull ButtonGuidePredicate renderPredicate) { + T button, + Supplier action, + ButtonGuidePredicate renderPredicate) { ButtonGuideRenderer.registerBindingForButton(button, action, renderPredicate); } } diff --git a/src/main/java/dev/isxander/controlify/api/buttonguide/ButtonGuidePredicate.java b/src/main/java/dev/isxander/controlify/api/gui/ButtonGuidePredicate.java similarity index 57% rename from src/main/java/dev/isxander/controlify/api/buttonguide/ButtonGuidePredicate.java rename to src/main/java/dev/isxander/controlify/api/gui/ButtonGuidePredicate.java index 7fcd6bf2e..4b18006ed 100644 --- a/src/main/java/dev/isxander/controlify/api/buttonguide/ButtonGuidePredicate.java +++ b/src/main/java/dev/isxander/controlify/api/gui/ButtonGuidePredicate.java @@ -4,9 +4,8 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.api.buttonguide; +package dev.isxander.controlify.api.gui; -import net.minecraft.client.gui.components.AbstractButton; import net.minecraft.client.gui.components.AbstractWidget; @FunctionalInterface @@ -21,12 +20,4 @@ static ButtonGuidePredicate focusOnly() { static ButtonGuidePredicate always() { return btn -> true; } - - /** Always display the button guide. */ - @Deprecated - ButtonGuidePredicate ALWAYS = btn -> true; - - /** Only display the button guide when the button is focused. */ - @Deprecated - ButtonGuidePredicate FOCUS_ONLY = AbstractWidget::isFocused; } diff --git a/src/main/java/dev/isxander/controlify/api/gui/package-info.java b/src/main/java/dev/isxander/controlify/api/gui/package-info.java new file mode 100644 index 000000000..a9610dd28 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/gui/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package dev.isxander.controlify.api.gui; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/input/LogicalInput.java b/src/main/java/dev/isxander/controlify/api/input/LogicalInput.java similarity index 93% rename from src/main/java/dev/isxander/controlify/input/LogicalInput.java rename to src/main/java/dev/isxander/controlify/api/input/LogicalInput.java index bdc48e56a..da729428b 100644 --- a/src/main/java/dev/isxander/controlify/input/LogicalInput.java +++ b/src/main/java/dev/isxander/controlify/api/input/LogicalInput.java @@ -1,8 +1,8 @@ -package dev.isxander.controlify.input; +package dev.isxander.controlify.api.input; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.utils.codec.CExtraCodecs; +import dev.isxander.controlify.impl.utils.codec.CExtraCodecs; import net.minecraft.resources.Identifier; import java.util.List; diff --git a/src/main/java/dev/isxander/controlify/api/input/package-info.java b/src/main/java/dev/isxander/controlify/api/input/package-info.java new file mode 100644 index 000000000..efd120283 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/input/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package dev.isxander.controlify.api.input; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/api/package-info.java b/src/main/java/dev/isxander/controlify/api/package-info.java new file mode 100644 index 000000000..1e9f2de6c --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package dev.isxander.controlify.api; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/controller/ControllerEntity.java b/src/main/java/dev/isxander/controlify/controller/ControllerEntity.java deleted file mode 100644 index becb50736..000000000 --- a/src/main/java/dev/isxander/controlify/controller/ControllerEntity.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller; - -import dev.isxander.controlify.config.settings.profile.ProfileSettings; -import dev.isxander.controlify.controller.battery.BatteryLevelComponent; -import dev.isxander.controlify.controller.dualsense.DualsenseComponent; -import dev.isxander.controlify.controller.haptic.HDHapticComponent; -import dev.isxander.controlify.controller.info.ControllerInfo; -import dev.isxander.controlify.controller.info.DriverNameComponent; -import dev.isxander.controlify.controller.info.GUIDComponent; -import dev.isxander.controlify.controller.info.UIDComponent; -import dev.isxander.controlify.controller.led.LEDComponent; -import dev.isxander.controlify.controller.gyro.GyroComponent; -import dev.isxander.controlify.controller.impl.ECSEntityImpl; -import dev.isxander.controlify.controller.misc.BluetoothDeviceComponent; -import dev.isxander.controlify.controller.rumble.RumbleComponent; -import dev.isxander.controlify.controller.rumble.TriggerRumbleComponent; -import dev.isxander.controlify.controller.touchpad.TouchpadComponent; -import dev.isxander.controlify.driver.Driver; -import dev.isxander.controlify.controller.input.InputComponent; -import dev.isxander.controlify.utils.log.ControlifyLogger; -import net.minecraft.resources.Identifier; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; - -import java.util.Optional; -import java.util.stream.Collectors; - -public class ControllerEntity extends ECSEntityImpl { - private final ControllerInfo info; - private final Driver driver; - private ProfileSettings settings; - private final ProfileSettings defaultSettings; - private final ControlifyLogger logger; - private boolean active; - - public ControllerEntity( - ControllerInfo info, - Driver driver, - ProfileSettings settings, - ProfileSettings defaultSettings, - ControlifyLogger logger - ) { - this.info = info; - this.driver = driver; - this.settings = settings; - this.defaultSettings = defaultSettings; - this.logger = logger; - - driver.addComponents(this); - this.getAllComponents().values().forEach(c -> c.attach(this)); - - logger.debugLog("Components: {}", this.getAllComponents().keySet().stream().map(Identifier::toString).collect(Collectors.joining(", "))); - } - - public String uid() { - return this.getComponent(UIDComponent.ID).orElseThrow().value(); - } - - @NotNull public String driverName() { - return this.getComponent(DriverNameComponent.ID).orElseThrow().value(); - } - - @NotNull public String guid() { - return this.getComponent(GUIDComponent.ID).orElseThrow().value(); - } - - public ControllerInfo info() { - return this.info; - } - - @NotNull public String name() { - String friendlyName = info().type().friendlyName(); - if (friendlyName != null) - return friendlyName; - - return driverName(); - } - - public Driver drivers() { - return driver; - } - - public ProfileSettings settings() { - return settings; - } - - public void setSettings(ProfileSettings settings) { - this.settings = settings; - } - - public ProfileSettings defaultSettings() { - return defaultSettings; - } - - @Contract(pure = true) - public Optional input() { - return this.getComponent(InputComponent.ID); - } - - @Contract(pure = true) - public Optional rumble() { - return this.getComponent(RumbleComponent.ID); - } - - @Contract(pure = true) - public Optional triggerRumble() { - return this.getComponent(TriggerRumbleComponent.ID); - } - - @Contract(pure = true) - public Optional gyro() { - return this.getComponent(GyroComponent.ID); - } - - @Contract(pure = true) - public Optional touchpad() { - return this.getComponent(TouchpadComponent.ID); - } - - @Contract(pure = true) - public Optional batteryLevel() { - return this.getComponent(BatteryLevelComponent.ID); - } - - @Contract(pure = true) - public Optional hdHaptics() { - return this.getComponent(HDHapticComponent.ID); - } - - @Contract(pure = true) - public Optional dualSense() { - return this.getComponent(DualsenseComponent.ID); - } - - @Contract(pure = true) - public Optional led() { - return this.getComponent(LEDComponent.ID); - } - - @Contract(pure = true) - public Optional bluetooth() { - return this.getComponent(BluetoothDeviceComponent.ID); - } - - public void update(boolean outOfFocus) { - this.driver.update(this, outOfFocus); - } - - public boolean isActive() { - return this.active; - } - - public void setActive(boolean active) { - if (this.active == active) { - return; - } - this.active = active; - this.driver.setActive(active); - } - - public void close() { - this.setActive(false); - this.getAllComponents().values().forEach(ECSComponent::close); - this.driver.close(); - } - - public ControlifyLogger getLogger() { - return logger; - } -} diff --git a/src/main/java/dev/isxander/controlify/controller/ECSComponent.java b/src/main/java/dev/isxander/controlify/controller/ECSComponent.java deleted file mode 100644 index dd0ee1a4c..000000000 --- a/src/main/java/dev/isxander/controlify/controller/ECSComponent.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller; - -import net.minecraft.resources.Identifier; - -public interface ECSComponent { - Identifier id(); - - default void attach(ControllerEntity controller) { - - } - - default void close() { - - } -} diff --git a/src/main/java/dev/isxander/controlify/controller/ECSEntity.java b/src/main/java/dev/isxander/controlify/controller/ECSEntity.java deleted file mode 100644 index e418b2487..000000000 --- a/src/main/java/dev/isxander/controlify/controller/ECSEntity.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller; - -import net.minecraft.resources.Identifier; - -import java.util.Map; -import java.util.Optional; - -public interface ECSEntity { - Map getAllComponents(); - - boolean setComponent(T component); - - boolean removeComponent(Identifier id); - - Optional getComponent(Identifier id); -} diff --git a/src/main/java/dev/isxander/controlify/controller/SingleValueComponent.java b/src/main/java/dev/isxander/controlify/controller/SingleValueComponent.java deleted file mode 100644 index 042da6abe..000000000 --- a/src/main/java/dev/isxander/controlify/controller/SingleValueComponent.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller; - -import net.minecraft.resources.Identifier; - -public class SingleValueComponent implements ECSComponent { - private final T value; - private final Identifier id; - - public SingleValueComponent(T value, Identifier id) { - this.value = value; - this.id = id; - } - - public T value() { - return this.value; - } - - @Override - public Identifier id() { - return this.id; - } -} diff --git a/src/main/java/dev/isxander/controlify/controller/gyro/GyroState.java b/src/main/java/dev/isxander/controlify/controller/gyro/GyroState.java deleted file mode 100644 index 8d687dd04..000000000 --- a/src/main/java/dev/isxander/controlify/controller/gyro/GyroState.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller.gyro; - -import org.joml.Vector3f; -import org.joml.Vector3fc; - -public class GyroState extends Vector3f implements GyroStateC { - public GyroState(float pitch, float yaw, float roll) { - super(pitch, yaw, roll); - } - - public GyroState(GyroStateC vec) { - super(vec); - } - - public GyroState() { - } - - @Override - public GyroState mul(Vector3fc v) { - super.mul(v); - return this; - } - - @Override - public GyroState mul(float scalar) { - super.mul(scalar); - return this; - } - - @Override - public GyroState div(Vector3fc v) { - super.div(v); - return this; - } - - @Override - public GyroState div(float scalar) { - super.div(scalar); - return this; - } - - @Override - public GyroState sub(Vector3fc v) { - super.sub(v); - return this; - } - - @Override - public GyroState sub(float x, float y, float z) { - super.sub(x, y, z); - return this; - } -} diff --git a/src/main/java/dev/isxander/controlify/controller/gyro/GyroStateC.java b/src/main/java/dev/isxander/controlify/controller/gyro/GyroStateC.java deleted file mode 100644 index eabbc8f1a..000000000 --- a/src/main/java/dev/isxander/controlify/controller/gyro/GyroStateC.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller.gyro; - -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import org.joml.Vector3fc; - -/** - * Represents the current gyro delta in radians per second. - */ -public interface GyroStateC extends Vector3fc { - GyroStateC ZERO = new GyroState(0, 0, 0); - - default float pitch() { - return x(); - } - - default float yaw() { - return y(); - } - - default float roll() { - return z(); - } - - Codec CODEC_MUTABLE = RecordCodecBuilder.create(instance -> instance.group( - Codec.FLOAT.fieldOf("pitch").forGetter(GyroStateC::pitch), - Codec.FLOAT.fieldOf("yaw").forGetter(GyroStateC::yaw), - Codec.FLOAT.fieldOf("roll").forGetter(GyroStateC::roll) - ).apply(instance, GyroState::new)); -} diff --git a/src/main/java/dev/isxander/controlify/controller/impl/ECSComponentImpl.java b/src/main/java/dev/isxander/controlify/controller/impl/ECSComponentImpl.java deleted file mode 100644 index ee13dfaa4..000000000 --- a/src/main/java/dev/isxander/controlify/controller/impl/ECSComponentImpl.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller.impl; - -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.ECSComponent; - -public abstract class ECSComponentImpl implements ECSComponent { - private ControllerEntity controller; - - protected final ControllerEntity controller() { - return this.controller; - } - - @Override - public void attach(ControllerEntity controller) { - this.controller = controller; - } -} diff --git a/src/main/java/dev/isxander/controlify/controller/impl/ECSEntityImpl.java b/src/main/java/dev/isxander/controlify/controller/impl/ECSEntityImpl.java deleted file mode 100644 index 5b65446ed..000000000 --- a/src/main/java/dev/isxander/controlify/controller/impl/ECSEntityImpl.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller.impl; - -import com.google.common.collect.ImmutableMap; -import dev.isxander.controlify.controller.ECSComponent; -import dev.isxander.controlify.controller.ECSEntity; -import net.minecraft.resources.Identifier; -import org.jetbrains.annotations.Contract; - -import java.util.*; - -public class ECSEntityImpl implements ECSEntity { - private final Map components; - - public ECSEntityImpl() { - this.components = new HashMap<>(); - } - - @SuppressWarnings("unchecked") - @Override - @Contract(pure = true) - public Optional getComponent(Identifier id) { - return Optional.ofNullable((T) this.components.get(id)); - } - - @Override - public boolean setComponent(T component) { - return this.components.put(component.id(), component) != null; - } - - @Override - public boolean removeComponent(Identifier id) { - return this.components.remove(id) != null; - } - - @Override - public Map getAllComponents() { - return ImmutableMap.copyOf(this.components); - } -} diff --git a/src/main/java/dev/isxander/controlify/controller/info/ControllerInfo.java b/src/main/java/dev/isxander/controlify/controller/info/ControllerInfo.java deleted file mode 100644 index 1fc56b074..000000000 --- a/src/main/java/dev/isxander/controlify/controller/info/ControllerInfo.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller.info; - -import dev.isxander.controlify.controller.id.ControllerType; -import dev.isxander.controlify.controllermanager.UniqueControllerID; -import dev.isxander.controlify.hid.HIDID; - -public record ControllerInfo(UniqueControllerID ucid, ControllerType type, HIDID hid) { -} diff --git a/src/main/java/dev/isxander/controlify/controller/info/DriverNameComponent.java b/src/main/java/dev/isxander/controlify/controller/info/DriverNameComponent.java deleted file mode 100644 index 4862ae08a..000000000 --- a/src/main/java/dev/isxander/controlify/controller/info/DriverNameComponent.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller.info; - -import dev.isxander.controlify.controller.SingleValueComponent; -import dev.isxander.controlify.utils.CUtil; -import net.minecraft.resources.Identifier; - -public class DriverNameComponent extends SingleValueComponent { - public static final Identifier ID = CUtil.rl("driver_name"); - - public DriverNameComponent(String value) { - super(value, ID); - } -} diff --git a/src/main/java/dev/isxander/controlify/controller/info/UIDComponent.java b/src/main/java/dev/isxander/controlify/controller/info/UIDComponent.java deleted file mode 100644 index 04c71f83b..000000000 --- a/src/main/java/dev/isxander/controlify/controller/info/UIDComponent.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller.info; - -import dev.isxander.controlify.controller.SingleValueComponent; -import dev.isxander.controlify.utils.CUtil; -import net.minecraft.resources.Identifier; - -public class UIDComponent extends SingleValueComponent { - public static final Identifier ID = CUtil.rl("uid"); - - public UIDComponent(String value) { - super(value, ID); - } -} diff --git a/src/main/java/dev/isxander/controlify/controller/rumble/RumbleComponent.java b/src/main/java/dev/isxander/controlify/controller/rumble/RumbleComponent.java deleted file mode 100644 index a79bfa7c6..000000000 --- a/src/main/java/dev/isxander/controlify/controller/rumble/RumbleComponent.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller.rumble; - -import dev.isxander.controlify.config.settings.profile.RumbleSettings; -import dev.isxander.controlify.controller.impl.ECSComponentImpl; -import dev.isxander.controlify.rumble.RumbleManager; -import dev.isxander.controlify.rumble.RumbleState; -import dev.isxander.controlify.utils.CUtil; -import net.minecraft.resources.Identifier; - -import java.util.Optional; - -public class RumbleComponent extends ECSComponentImpl { - public static final Identifier ID = CUtil.rl("rumble"); - - private RumbleState state = null; - private final RumbleManager rumbleManager; - - public RumbleComponent() { - this.rumbleManager = new RumbleManager(this); - } - - public void queueRumble(RumbleState state) { - if (settings().enabled) { - this.state = state; - } - } - - public Optional consumeRumble() { - RumbleState state = this.state; - this.state = null; - return Optional.ofNullable(state); - } - - public RumbleManager rumbleManager() { - return this.rumbleManager; - } - - public RumbleSettings settings() { - return this.controller().settings().rumble; - } - - public RumbleSettings defaultSettings() { - return this.controller().defaultSettings().rumble; - } - - @Override - public Identifier id() { - return ID; - } - -} diff --git a/src/main/java/dev/isxander/controlify/controller/steamdeck/SteamDeckComponent.java b/src/main/java/dev/isxander/controlify/controller/steamdeck/SteamDeckComponent.java deleted file mode 100644 index 849deadba..000000000 --- a/src/main/java/dev/isxander/controlify/controller/steamdeck/SteamDeckComponent.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.controller.steamdeck; - -import dev.isxander.controlify.controller.impl.ECSComponentImpl; -import dev.isxander.controlify.utils.CUtil; -import net.minecraft.resources.Identifier; - -public class SteamDeckComponent extends ECSComponentImpl { - public static final Identifier ID = CUtil.rl("steam_deck"); - - @Override - public Identifier id() { - return ID; - } -} diff --git a/src/main/java/dev/isxander/controlify/Controlify.java b/src/main/java/dev/isxander/controlify/impl/Controlify.java similarity index 82% rename from src/main/java/dev/isxander/controlify/Controlify.java rename to src/main/java/dev/isxander/controlify/impl/Controlify.java index 0be77c533..c02a7c2a8 100644 --- a/src/main/java/dev/isxander/controlify/Controlify.java +++ b/src/main/java/dev/isxander/controlify/impl/Controlify.java @@ -4,54 +4,55 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify; +package dev.isxander.controlify.impl; import com.mojang.blaze3d.Blaze3D; +import dev.isxander.controlify.api.InputMode; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.api.bind.ControlifyBindApi; +import dev.isxander.controlify.impl.api.bind.ControlifyBindApi; import dev.isxander.controlify.api.contextual.ContextualDomainRegistry; -import dev.isxander.controlify.api.entrypoint.InitContext; -import dev.isxander.controlify.api.entrypoint.PreInitContext; -import dev.isxander.controlify.bindings.ControlifyBindApiImpl; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.action.radial.RadialIconManager; -import dev.isxander.controlify.action.defaults.DefaultBindManager; -import dev.isxander.controlify.compatibility.ControlifyCompat; -import dev.isxander.controlify.config.ConfigManager; -import dev.isxander.controlify.config.dto.profile.defaults.DefaultConfigManager; -import dev.isxander.controlify.contextual.ContextualDomains; -import dev.isxander.controlify.contextual.RuleSetManager; -import dev.isxander.controlify.controller.*; -import dev.isxander.controlify.controller.dualsense.TriggerEffectManager; -import dev.isxander.controlify.controller.id.ControllerTypeManager; -import dev.isxander.controlify.input.mapping.ControllerMappingManager; -import dev.isxander.controlify.controller.rumble.RumbleComponent; -import dev.isxander.controlify.controllermanager.ControllerCoordinator; -import dev.isxander.controlify.controllermanager.ControllerManager; -import dev.isxander.controlify.driver.steamdeck.SteamDeckMode; -import dev.isxander.controlify.driver.steamdeck.SteamDeckUtil; -import dev.isxander.controlify.font.InputFontMapper; -import dev.isxander.controlify.gui.screen.*; -import dev.isxander.controlify.debug.DebugProperties; -import dev.isxander.controlify.ingame.ControllerPlayerMovement; -import dev.isxander.controlify.controller.input.InputComponent; -import dev.isxander.controlify.action.activator.ActivatorConfigManager; -import dev.isxander.controlify.platform.client.PlatformClientUtil; -import dev.isxander.controlify.platform.main.PlatformMainUtil; -import dev.isxander.controlify.platform.network.SidedNetworkApi; -import dev.isxander.controlify.rumble.RumbleManager; -import dev.isxander.controlify.screenop.keyboard.KeyboardLayoutManager; -import dev.isxander.controlify.server.*; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.api.event.ControlifyEvents; -import dev.isxander.controlify.gui.guide.InGameButtonGuide; -import dev.isxander.controlify.ingame.InGameInputHandler; +import dev.isxander.controlify.impl.api.entrypoint.InitContext; +import dev.isxander.controlify.impl.api.entrypoint.PreInitContext; +import dev.isxander.controlify.impl.bindings.ControlifyBindApiImpl; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.radial.RadialIconManager; +import dev.isxander.controlify.impl.action.defaults.DefaultBindManager; +import dev.isxander.controlify.impl.compatibility.ControlifyCompat; +import dev.isxander.controlify.impl.config.ConfigManager; +import dev.isxander.controlify.impl.config.dto.profile.defaults.DefaultConfigManager; +import dev.isxander.controlify.impl.contextual.ContextualDomains; +import dev.isxander.controlify.impl.contextual.RuleSetManager; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controller.dualsense.TriggerEffectManager; +import dev.isxander.controlify.impl.controllermanager.ControllerTypeManager; +import dev.isxander.controlify.impl.input.mapping.ControllerMappingManager; +import dev.isxander.controlify.impl.controller.rumble.RumbleComponentImpl; +import dev.isxander.controlify.impl.controllermanager.ControllerCoordinator; +import dev.isxander.controlify.impl.controllermanager.ControllerManager; +import dev.isxander.controlify.impl.driver.steamdeck.SteamDeckMode; +import dev.isxander.controlify.impl.driver.steamdeck.SteamDeckUtil; +import dev.isxander.controlify.impl.font.InputFontMapper; +import dev.isxander.controlify.impl.gui.screen.*; +import dev.isxander.controlify.impl.debug.DebugProperties; +import dev.isxander.controlify.impl.ingame.ControllerPlayerMovement; +import dev.isxander.controlify.impl.action.activator.ActivatorConfigManager; +import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; +import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; +import dev.isxander.controlify.impl.platform.network.SidedNetworkApi; +import dev.isxander.controlify.impl.controller.rumble.RumbleManager; +import dev.isxander.controlify.impl.controller.rumble.RumbleSourceManager; +import dev.isxander.controlify.impl.screenop.keyboard.KeyboardLayoutManager; +import dev.isxander.controlify.impl.server.*; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.api.ControlifyEvents; +import dev.isxander.controlify.impl.gui.guide.InGameButtonGuide; +import dev.isxander.controlify.impl.ingame.InGameInputHandler; import dev.isxander.controlify.mixins.feature.virtualmouse.MouseHandlerAccessor; -import dev.isxander.controlify.server.packets.*; -import dev.isxander.controlify.sound.ControlifyClientSounds; -import dev.isxander.controlify.utils.*; -import dev.isxander.controlify.virtualmouse.VirtualMouseHandler; -import dev.isxander.controlify.wireless.LowBatteryNotifier; +import dev.isxander.controlify.impl.server.packets.*; +import dev.isxander.controlify.impl.sound.ControlifyClientSounds; +import dev.isxander.controlify.impl.utils.*; +import dev.isxander.controlify.impl.virtualmouse.VirtualMouseHandler; +import dev.isxander.controlify.impl.wireless.LowBatteryNotifier; import dev.isxander.yacl3.gui.YACLScreen; import net.minecraft.client.KeyMapping; import net.minecraft.client.Minecraft; @@ -65,7 +66,7 @@ import java.io.IOException; import java.util.*; -import static dev.isxander.controlify.utils.ControllerUtils.wrapControllerError; +import static dev.isxander.controlify.impl.utils.ControllerUtils.wrapControllerError; public class Controlify implements ControlifyApi { private static Controlify instance = null; @@ -90,6 +91,7 @@ public class Controlify implements ControlifyApi { private RadialIconManager radialIconManager; private ActivatorConfigManager activatorConfigManager; private ControllerMappingManager controllerMappingManager; + private RumbleSourceManager rumbleSourceManager; private TriggerEffectManager triggerEffectManager; @@ -136,6 +138,7 @@ public void preInitialiseControlify() { this.radialIconManager = new RadialIconManager(); this.activatorConfigManager = new ActivatorConfigManager(); this.controllerMappingManager = new ControllerMappingManager(); + this.rumbleSourceManager = new RumbleSourceManager(); PlatformClientUtil.registerAssetReloadListener(this.inputFontMapper); PlatformClientUtil.registerAssetReloadListener(this.defaultBindManager); @@ -145,6 +148,7 @@ public void preInitialiseControlify() { PlatformClientUtil.registerAssetReloadListener(this.radialIconManager); PlatformClientUtil.registerAssetReloadListener(this.activatorConfigManager); PlatformClientUtil.registerAssetReloadListener(this.controllerMappingManager); + PlatformClientUtil.registerAssetReloadListener(this.rumbleSourceManager); PlatformClientUtil.registerAssetReloadListener(RuleSetManager.GUIDE_RULES); PlatformClientUtil.registerAssetReloadListener(RuleSetManager.TRIGGER_EFFECT_RULES); ContextualDomains.touch(); @@ -157,20 +161,20 @@ public void preInitialiseControlify() { SidedNetworkApi.S2C().listenForPacket(VibrationPacket.CHANNEL, packet -> { if (config().getSettings().globalSettings().allowServerRumble) { - getCurrentController().flatMap(ControllerEntity::rumble).ifPresent(rumble -> - rumble.rumbleManager().play(packet.source(), packet.createEffect())); + getCurrentController().flatMap(ControllerImpl::rumble).ifPresent(rumble -> + rumble.rumbleManager().play(VibrationPacket.CHANNEL, packet.source(), packet.createEffect())); } }); SidedNetworkApi.S2C().listenForPacket(OriginVibrationPacket.CHANNEL, packet -> { if (config().getSettings().globalSettings().allowServerRumble) { - getCurrentController().flatMap(ControllerEntity::rumble).ifPresent(rumble -> - rumble.rumbleManager().play(packet.source(), packet.createEffect())); + getCurrentController().flatMap(ControllerImpl::rumble).ifPresent(rumble -> + rumble.rumbleManager().play(OriginVibrationPacket.CHANNEL, packet.source(), packet.createEffect())); } }); SidedNetworkApi.S2C().listenForPacket(EntityVibrationPacket.CHANNEL, packet -> { if (config().getSettings().globalSettings().allowServerRumble) { - getCurrentController().flatMap(ControllerEntity::rumble).ifPresent(rumble -> - rumble.rumbleManager().play(packet.source(), packet.createEffect())); + getCurrentController().flatMap(ControllerImpl::rumble).ifPresent(rumble -> + rumble.rumbleManager().play(EntityVibrationPacket.CHANNEL, packet.source(), packet.createEffect())); } }); SidedNetworkApi.S2C().listenForPacket(ServerPolicyPacket.CHANNEL, packet -> { @@ -366,7 +370,7 @@ public void fixedUpdate() { }); } - private void updateActiveController(ControllerEntity controller, boolean givingInput) { + private void updateActiveController(ControllerImpl controller, boolean givingInput) { if (givingInput) { minecraft.getFramerateLimitTracker().onInputReceived(); @@ -398,7 +402,7 @@ private void updateActiveController(ControllerEntity controller, boolean givingI } } - private void fixedUpdateActiveController(ControllerEntity controller, boolean outOfFocus) { + private void fixedUpdateActiveController(ControllerImpl controller, boolean outOfFocus) { boolean controllerInputSuppressed = outOfFocus || currentInputMode() == InputMode.KEYBOARD_MOUSE; this.triggerEffectManager().tick(this.minecraft, controller); this.triggerEffectManager().applyTriggerEffects(controller, controllerInputSuppressed); @@ -406,7 +410,7 @@ private void fixedUpdateActiveController(ControllerEntity controller, boolean ou boolean isPaused = minecraft.isPaused() || MinecraftUtil.getScreen() instanceof PauseScreen; boolean isConfigScreen = MinecraftUtil.getScreen() instanceof YACLScreen; - Optional rumbleManager = controller.rumble().map(RumbleComponent::rumbleManager); + Optional rumbleManager = controller.rumble().map(RumbleComponentImpl::rumbleManager); rumbleManager.ifPresent(rumble -> rumble.setSilent(outOfFocus || (isPaused && !isConfigScreen) || currentInputMode() == InputMode.KEYBOARD_MOUSE)); if (!outOfFocus) { rumbleManager.ifPresent(RumbleManager::tick); @@ -442,15 +446,15 @@ public ConfigManager config() { } @Override - public @NotNull Optional getCurrentController() { + public @NotNull Optional getCurrentController() { return this.getControllerCoordinator().flatMap(ControllerCoordinator::currentController); } - public void setCurrentController(@Nullable ControllerEntity controller, boolean changeInputMode) { + public void setCurrentController(@Nullable ControllerImpl controller, boolean changeInputMode) { this.getControllerCoordinator().ifPresent(coordinator -> coordinator.setCurrentController(controller, changeInputMode)); } - private void onCurrentControllerChanged(@Nullable ControllerEntity controller, boolean changeInputMode) { + private void onCurrentControllerChanged(@Nullable ControllerImpl controller, boolean changeInputMode) { boolean changedInputMode = false; if (controller == null) { changedInputMode = this.setInputMode(InputMode.KEYBOARD_MOUSE); @@ -510,7 +514,7 @@ public boolean setInputMode(@NotNull InputMode newInputMode) { return true; } - private void setupForController(@Nullable ControllerEntity controller) { + private void setupForController(@Nullable ControllerImpl controller) { ControllerPlayerMovement.updatePlayerInput(minecraft.player); if (controller == null) { @@ -522,7 +526,7 @@ private void setupForController(@Nullable ControllerEntity controller) { this.inGameInputHandler = new InGameInputHandler(controller); this.inGameButtonGuide = new InGameButtonGuide(controller, this.minecraft); - controller.input().ifPresent(InputComponent::resetActions); + controller.input().resetActions(); } public Optional getControllerManager() { @@ -629,6 +633,10 @@ public ControllerMappingManager controllerMappingManager() { return this.controllerMappingManager; } + public RumbleSourceManager rumbleSourceManager() { + return this.rumbleSourceManager; + } + public TriggerEffectManager triggerEffectManager() { return this.triggerEffectManager; } diff --git a/src/main/java/dev/isxander/controlify/action/Accumulator.java b/src/main/java/dev/isxander/controlify/impl/action/Accumulator.java similarity index 82% rename from src/main/java/dev/isxander/controlify/action/Accumulator.java rename to src/main/java/dev/isxander/controlify/impl/action/Accumulator.java index fb9e37d17..2737e5806 100644 --- a/src/main/java/dev/isxander/controlify/action/Accumulator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/Accumulator.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.action; +package dev.isxander.controlify.impl.action; /// Accepts action state. public interface Accumulator { diff --git a/src/main/java/dev/isxander/controlify/action/ActionAccessor.java b/src/main/java/dev/isxander/controlify/impl/action/ActionAccessor.java similarity index 64% rename from src/main/java/dev/isxander/controlify/action/ActionAccessor.java rename to src/main/java/dev/isxander/controlify/impl/action/ActionAccessor.java index 7fd0ed51a..153bbc3ce 100644 --- a/src/main/java/dev/isxander/controlify/action/ActionAccessor.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionAccessor.java @@ -1,7 +1,8 @@ -package dev.isxander.controlify.action; +package dev.isxander.controlify.impl.action; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.input.InputComponent; +import dev.isxander.controlify.api.action.Action; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controller.input.InputComponent; import net.minecraft.resources.Identifier; import org.jspecify.annotations.Nullable; @@ -11,7 +12,7 @@ public interface ActionAccessor { Identifier actionId(); - default @Nullable Action onOrNull(ControllerEntity controller) { + default @Nullable Action onOrNull(ControllerImpl controller) { return onOrNull(controller.input().orElseThrow()); } @@ -21,7 +22,7 @@ default Action on(InputComponent inputComponent) { return action; } - default Action on(ControllerEntity controller) { + default Action on(ControllerImpl controller) { return on(controller.input().orElseThrow()); } } diff --git a/src/main/java/dev/isxander/controlify/action/ActionAccessorImpl.java b/src/main/java/dev/isxander/controlify/impl/action/ActionAccessorImpl.java similarity index 64% rename from src/main/java/dev/isxander/controlify/action/ActionAccessorImpl.java rename to src/main/java/dev/isxander/controlify/impl/action/ActionAccessorImpl.java index 6ee6f2a3f..a75544968 100644 --- a/src/main/java/dev/isxander/controlify/action/ActionAccessorImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionAccessorImpl.java @@ -1,6 +1,7 @@ -package dev.isxander.controlify.action; +package dev.isxander.controlify.impl.action; -import dev.isxander.controlify.controller.input.InputComponent; +import dev.isxander.controlify.api.action.Action; +import dev.isxander.controlify.impl.controller.input.InputComponent; import net.minecraft.resources.Identifier; import org.jspecify.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/action/ActionContext.java b/src/main/java/dev/isxander/controlify/impl/action/ActionContext.java similarity index 89% rename from src/main/java/dev/isxander/controlify/action/ActionContext.java rename to src/main/java/dev/isxander/controlify/impl/action/ActionContext.java index ccaaf4763..a91baeaa6 100644 --- a/src/main/java/dev/isxander/controlify/action/ActionContext.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionContext.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.action; +package dev.isxander.controlify.impl.action; import net.minecraft.client.Minecraft; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/action/ActionGraph.java b/src/main/java/dev/isxander/controlify/impl/action/ActionGraph.java similarity index 93% rename from src/main/java/dev/isxander/controlify/action/ActionGraph.java rename to src/main/java/dev/isxander/controlify/impl/action/ActionGraph.java index 71be9fc45..1a6cd4ed4 100644 --- a/src/main/java/dev/isxander/controlify/action/ActionGraph.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionGraph.java @@ -1,8 +1,8 @@ -package dev.isxander.controlify.action; +package dev.isxander.controlify.impl.action; -import dev.isxander.controlify.input.LogicalInput; -import dev.isxander.controlify.input.signal.LogicalSignal; -import dev.isxander.controlify.utils.event.EventSink; +import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.impl.input.signal.LogicalSignal; +import dev.isxander.controlify.impl.utils.event.EventSink; import net.minecraft.resources.Identifier; import java.util.*; diff --git a/src/main/java/dev/isxander/controlify/action/ActionImpl.java b/src/main/java/dev/isxander/controlify/impl/action/ActionImpl.java similarity index 82% rename from src/main/java/dev/isxander/controlify/action/ActionImpl.java rename to src/main/java/dev/isxander/controlify/impl/action/ActionImpl.java index 6e2847cf3..f6c23225f 100644 --- a/src/main/java/dev/isxander/controlify/action/ActionImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionImpl.java @@ -1,8 +1,11 @@ -package dev.isxander.controlify.action; +package dev.isxander.controlify.impl.action; -import dev.isxander.controlify.action.activator.ActivatorConfig; -import dev.isxander.controlify.action.activator.NoopActivator; -import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.api.action.Action; +import dev.isxander.controlify.api.action.ActionSpec; +import dev.isxander.controlify.api.action.Channel; +import dev.isxander.controlify.impl.action.activator.ActivatorConfig; +import dev.isxander.controlify.impl.action.activator.NoopActivator; +import dev.isxander.controlify.api.input.LogicalInput; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/action/ActionSpecBuilder.java b/src/main/java/dev/isxander/controlify/impl/action/ActionSpecBuilder.java similarity index 64% rename from src/main/java/dev/isxander/controlify/action/ActionSpecBuilder.java rename to src/main/java/dev/isxander/controlify/impl/action/ActionSpecBuilder.java index d30c52e96..8cff36660 100644 --- a/src/main/java/dev/isxander/controlify/action/ActionSpecBuilder.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionSpecBuilder.java @@ -1,6 +1,7 @@ -package dev.isxander.controlify.action; +package dev.isxander.controlify.impl.action; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.api.action.ActionSpec; +import dev.isxander.controlify.impl.controller.ControllerImpl; import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; @@ -17,7 +18,7 @@ public interface ActionSpecBuilder { ActionSpecBuilder context(Identifier... contexts); - ActionSpecBuilder shouldExpose(Predicate predicate); + ActionSpecBuilder shouldExpose(Predicate predicate); ActionSpec build(); } diff --git a/src/main/java/dev/isxander/controlify/action/ActionSpecBuilderImpl.java b/src/main/java/dev/isxander/controlify/impl/action/ActionSpecBuilderImpl.java similarity index 86% rename from src/main/java/dev/isxander/controlify/action/ActionSpecBuilderImpl.java rename to src/main/java/dev/isxander/controlify/impl/action/ActionSpecBuilderImpl.java index c6a3d10d7..337071d48 100644 --- a/src/main/java/dev/isxander/controlify/action/ActionSpecBuilderImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionSpecBuilderImpl.java @@ -1,6 +1,7 @@ -package dev.isxander.controlify.action; +package dev.isxander.controlify.impl.action; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.api.action.ActionSpec; +import dev.isxander.controlify.impl.controller.ControllerImpl; import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; import org.apache.commons.lang3.Validate; @@ -15,7 +16,7 @@ public class ActionSpecBuilderImpl implements ActionSpecBuilder { private @Nullable Component description; private @Nullable Component category; private final Set contexts = new TreeSet<>(); - private Predicate shouldExpose = _ -> true; + private Predicate shouldExpose = _ -> true; @Override public ActionSpecBuilder id(Identifier id) { @@ -48,7 +49,7 @@ public ActionSpecBuilder context(Identifier... contexts) { } @Override - public ActionSpecBuilder shouldExpose(Predicate predicate) { + public ActionSpecBuilder shouldExpose(Predicate predicate) { this.shouldExpose = predicate; return this; } diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionSpecRegistry.java b/src/main/java/dev/isxander/controlify/impl/action/ActionSpecRegistry.java new file mode 100644 index 000000000..a65bff351 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionSpecRegistry.java @@ -0,0 +1,7 @@ +package dev.isxander.controlify.impl.action; + +import dev.isxander.controlify.api.action.ActionSpec; + +public interface ActionSpecRegistry { + ActionAccessor register(ActionSpec actionSpec); +} diff --git a/src/main/java/dev/isxander/controlify/action/ActionSpecRegistryImpl.java b/src/main/java/dev/isxander/controlify/impl/action/ActionSpecRegistryImpl.java similarity index 91% rename from src/main/java/dev/isxander/controlify/action/ActionSpecRegistryImpl.java rename to src/main/java/dev/isxander/controlify/impl/action/ActionSpecRegistryImpl.java index ae391579f..c1f832586 100644 --- a/src/main/java/dev/isxander/controlify/action/ActionSpecRegistryImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionSpecRegistryImpl.java @@ -1,4 +1,6 @@ -package dev.isxander.controlify.action; +package dev.isxander.controlify.impl.action; + +import dev.isxander.controlify.api.action.ActionSpec; import java.util.ArrayList; import java.util.Collections; diff --git a/src/main/java/dev/isxander/controlify/action/ActionState.java b/src/main/java/dev/isxander/controlify/impl/action/ActionState.java similarity index 90% rename from src/main/java/dev/isxander/controlify/action/ActionState.java rename to src/main/java/dev/isxander/controlify/impl/action/ActionState.java index 17838159e..499b60103 100644 --- a/src/main/java/dev/isxander/controlify/action/ActionState.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionState.java @@ -1,4 +1,6 @@ -package dev.isxander.controlify.action; +package dev.isxander.controlify.impl.action; + +import dev.isxander.controlify.api.action.Channel; public final class ActionState implements Accumulator, Channel.Continuous, Channel.Latch, Channel.Pulse { private int pulses = 0; diff --git a/src/main/java/dev/isxander/controlify/action/Activator.java b/src/main/java/dev/isxander/controlify/impl/action/Activator.java similarity index 64% rename from src/main/java/dev/isxander/controlify/action/Activator.java rename to src/main/java/dev/isxander/controlify/impl/action/Activator.java index 4e87f8f5a..92855b478 100644 --- a/src/main/java/dev/isxander/controlify/action/Activator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/Activator.java @@ -1,7 +1,8 @@ -package dev.isxander.controlify.action; +package dev.isxander.controlify.impl.action; -import dev.isxander.controlify.input.LogicalInput; -import dev.isxander.controlify.input.signal.LogicalSignal; +import dev.isxander.controlify.api.action.Action; +import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.impl.input.signal.LogicalSignal; /// Processes signals and activates an {@link Action}. public interface Activator { diff --git a/src/main/java/dev/isxander/controlify/action/activator/ActivatorConfig.java b/src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfig.java similarity index 89% rename from src/main/java/dev/isxander/controlify/action/activator/ActivatorConfig.java rename to src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfig.java index 908c6556c..5dfcecb8b 100644 --- a/src/main/java/dev/isxander/controlify/action/activator/ActivatorConfig.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfig.java @@ -1,9 +1,9 @@ -package dev.isxander.controlify.action.activator; +package dev.isxander.controlify.impl.action.activator; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; -import dev.isxander.controlify.action.Activator; -import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.impl.action.Activator; +import dev.isxander.controlify.api.input.LogicalInput; import net.minecraft.util.StringRepresentable; /// An immutable structure typically containing the configuration diff --git a/src/main/java/dev/isxander/controlify/action/activator/ActivatorConfigManager.java b/src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfigManager.java similarity index 87% rename from src/main/java/dev/isxander/controlify/action/activator/ActivatorConfigManager.java rename to src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfigManager.java index 34aeecf18..4efba9b63 100644 --- a/src/main/java/dev/isxander/controlify/action/activator/ActivatorConfigManager.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfigManager.java @@ -4,16 +4,16 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.action.activator; +package dev.isxander.controlify.impl.action.activator; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.controller.input.InputComponent; -import dev.isxander.controlify.platform.client.resource.SimpleControlifyReloadListener; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.controller.input.InputComponent; +import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; import net.minecraft.server.packs.resources.Resource; import net.minecraft.server.packs.resources.ResourceManager; @@ -26,7 +26,7 @@ import java.util.concurrent.Executor; public class ActivatorConfigManager implements SimpleControlifyReloadListener>> { - private static final Identifier DEFINITIONS = CUtil.rl("activators.json"); + private static final Identifier DEFINITIONS = CUtil.rl("controller/activators.json"); private static final Codec>> CODEC = Codec.unboundedMap(Identifier.CODEC, ActivatorConfig.CODEC); private static final ActivatorConfig FALLBACK = new CompoundActivator.Config(List.of()); diff --git a/src/main/java/dev/isxander/controlify/action/activator/ActivatorConfigType.java b/src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfigType.java similarity index 96% rename from src/main/java/dev/isxander/controlify/action/activator/ActivatorConfigType.java rename to src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfigType.java index 5c8f2cb97..31fd78a74 100644 --- a/src/main/java/dev/isxander/controlify/action/activator/ActivatorConfigType.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfigType.java @@ -1,7 +1,7 @@ -package dev.isxander.controlify.action.activator; +package dev.isxander.controlify.impl.action.activator; import com.mojang.serialization.MapCodec; -import dev.isxander.controlify.action.Activator; +import dev.isxander.controlify.impl.action.Activator; import net.minecraft.util.StringRepresentable; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/dev/isxander/controlify/action/activator/CompoundActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/CompoundActivator.java similarity index 82% rename from src/main/java/dev/isxander/controlify/action/activator/CompoundActivator.java rename to src/main/java/dev/isxander/controlify/impl/action/activator/CompoundActivator.java index b39c7b18e..c90c9d9f7 100644 --- a/src/main/java/dev/isxander/controlify/action/activator/CompoundActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/CompoundActivator.java @@ -1,11 +1,11 @@ -package dev.isxander.controlify.action.activator; +package dev.isxander.controlify.impl.action.activator; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.action.Accumulator; -import dev.isxander.controlify.action.Activator; -import dev.isxander.controlify.input.LogicalInput; -import dev.isxander.controlify.input.signal.LogicalSignal; +import dev.isxander.controlify.impl.action.Accumulator; +import dev.isxander.controlify.impl.action.Activator; +import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.impl.input.signal.LogicalSignal; import java.util.List; import java.util.stream.Collectors; diff --git a/src/main/java/dev/isxander/controlify/action/activator/ContinuousActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/ContinuousActivator.java similarity index 74% rename from src/main/java/dev/isxander/controlify/action/activator/ContinuousActivator.java rename to src/main/java/dev/isxander/controlify/impl/action/activator/ContinuousActivator.java index 552d00840..4bc2daa1d 100644 --- a/src/main/java/dev/isxander/controlify/action/activator/ContinuousActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/ContinuousActivator.java @@ -1,9 +1,9 @@ -package dev.isxander.controlify.action.activator; +package dev.isxander.controlify.impl.action.activator; -import dev.isxander.controlify.action.Accumulator; -import dev.isxander.controlify.action.Activator; -import dev.isxander.controlify.input.LogicalInput; -import dev.isxander.controlify.input.signal.LogicalSignal; +import dev.isxander.controlify.impl.action.Accumulator; +import dev.isxander.controlify.impl.action.Activator; +import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.impl.input.signal.LogicalSignal; public record ContinuousActivator(LogicalInput boundInput) implements Activator { @Override diff --git a/src/main/java/dev/isxander/controlify/action/activator/DoubleTapPulseActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/DoubleTapPulseActivator.java similarity index 76% rename from src/main/java/dev/isxander/controlify/action/activator/DoubleTapPulseActivator.java rename to src/main/java/dev/isxander/controlify/impl/action/activator/DoubleTapPulseActivator.java index dfd6a6c84..92d136552 100644 --- a/src/main/java/dev/isxander/controlify/action/activator/DoubleTapPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/DoubleTapPulseActivator.java @@ -1,9 +1,9 @@ -package dev.isxander.controlify.action.activator; +package dev.isxander.controlify.impl.action.activator; -import dev.isxander.controlify.action.Accumulator; -import dev.isxander.controlify.action.Activator; -import dev.isxander.controlify.input.LogicalInput; -import dev.isxander.controlify.input.signal.LogicalSignal; +import dev.isxander.controlify.impl.action.Accumulator; +import dev.isxander.controlify.impl.action.Activator; +import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.impl.input.signal.LogicalSignal; /// Produces a pulse when the input is double tapped. public record DoubleTapPulseActivator(LogicalInput boundInput) implements Activator { diff --git a/src/main/java/dev/isxander/controlify/action/activator/GuiPressPulseActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/GuiPressPulseActivator.java similarity index 76% rename from src/main/java/dev/isxander/controlify/action/activator/GuiPressPulseActivator.java rename to src/main/java/dev/isxander/controlify/impl/action/activator/GuiPressPulseActivator.java index d66ec387e..4fb6e5317 100644 --- a/src/main/java/dev/isxander/controlify/action/activator/GuiPressPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/GuiPressPulseActivator.java @@ -1,9 +1,9 @@ -package dev.isxander.controlify.action.activator; +package dev.isxander.controlify.impl.action.activator; -import dev.isxander.controlify.action.Accumulator; -import dev.isxander.controlify.action.Activator; -import dev.isxander.controlify.input.LogicalInput; -import dev.isxander.controlify.input.signal.LogicalSignal; +import dev.isxander.controlify.impl.action.Accumulator; +import dev.isxander.controlify.impl.action.Activator; +import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.impl.input.signal.LogicalSignal; /// Produces a pulse on release when no GUI navigation consumed the press. public record GuiPressPulseActivator(LogicalInput boundInput) implements Activator { diff --git a/src/main/java/dev/isxander/controlify/action/activator/HoldLatchActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/HoldLatchActivator.java similarity index 79% rename from src/main/java/dev/isxander/controlify/action/activator/HoldLatchActivator.java rename to src/main/java/dev/isxander/controlify/impl/action/activator/HoldLatchActivator.java index 35eb00978..e40dcbe21 100644 --- a/src/main/java/dev/isxander/controlify/action/activator/HoldLatchActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/HoldLatchActivator.java @@ -1,9 +1,9 @@ -package dev.isxander.controlify.action.activator; +package dev.isxander.controlify.impl.action.activator; -import dev.isxander.controlify.action.Accumulator; -import dev.isxander.controlify.action.Activator; -import dev.isxander.controlify.input.LogicalInput; -import dev.isxander.controlify.input.signal.LogicalSignal; +import dev.isxander.controlify.impl.action.Accumulator; +import dev.isxander.controlify.impl.action.Activator; +import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.impl.input.signal.LogicalSignal; /// Keeps the latch active from button press until release, without a hold delay. public record HoldLatchActivator(LogicalInput boundInput) implements Activator { diff --git a/src/main/java/dev/isxander/controlify/action/activator/HoldPulseActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/HoldPulseActivator.java similarity index 75% rename from src/main/java/dev/isxander/controlify/action/activator/HoldPulseActivator.java rename to src/main/java/dev/isxander/controlify/impl/action/activator/HoldPulseActivator.java index 004e83593..6b817b9c3 100644 --- a/src/main/java/dev/isxander/controlify/action/activator/HoldPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/HoldPulseActivator.java @@ -1,9 +1,9 @@ -package dev.isxander.controlify.action.activator; +package dev.isxander.controlify.impl.action.activator; -import dev.isxander.controlify.action.Accumulator; -import dev.isxander.controlify.action.Activator; -import dev.isxander.controlify.input.LogicalInput; -import dev.isxander.controlify.input.signal.LogicalSignal; +import dev.isxander.controlify.impl.action.Accumulator; +import dev.isxander.controlify.impl.action.Activator; +import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.impl.input.signal.LogicalSignal; /// Produces a pulse once the input reaches the hold duration. public record HoldPulseActivator(LogicalInput boundInput) implements Activator { diff --git a/src/main/java/dev/isxander/controlify/impl/action/activator/NoopActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/NoopActivator.java new file mode 100644 index 000000000..1fc73f482 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/NoopActivator.java @@ -0,0 +1,23 @@ +package dev.isxander.controlify.impl.action.activator; + +import dev.isxander.controlify.impl.action.Accumulator; +import dev.isxander.controlify.impl.action.Activator; +import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.impl.input.signal.LogicalSignal; + +public class NoopActivator implements Activator { + @Override + public void onSignal(LogicalSignal signal, Accumulator acc) { + + } + + @Override + public LogicalInput boundInput() { + return null; + } + + @Override + public String describe() { + return "Noop"; + } +} diff --git a/src/main/java/dev/isxander/controlify/action/activator/PressPulseActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/PressPulseActivator.java similarity index 75% rename from src/main/java/dev/isxander/controlify/action/activator/PressPulseActivator.java rename to src/main/java/dev/isxander/controlify/impl/action/activator/PressPulseActivator.java index 812a5616b..fa5f093fe 100644 --- a/src/main/java/dev/isxander/controlify/action/activator/PressPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/PressPulseActivator.java @@ -1,9 +1,9 @@ -package dev.isxander.controlify.action.activator; +package dev.isxander.controlify.impl.action.activator; -import dev.isxander.controlify.action.Accumulator; -import dev.isxander.controlify.action.Activator; -import dev.isxander.controlify.input.LogicalInput; -import dev.isxander.controlify.input.signal.LogicalSignal; +import dev.isxander.controlify.impl.action.Accumulator; +import dev.isxander.controlify.impl.action.Activator; +import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.impl.input.signal.LogicalSignal; /// Produces a pulse when the input is pressed. public record PressPulseActivator(LogicalInput boundInput) implements Activator { diff --git a/src/main/java/dev/isxander/controlify/action/activator/ReleasePulseActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/ReleasePulseActivator.java similarity index 75% rename from src/main/java/dev/isxander/controlify/action/activator/ReleasePulseActivator.java rename to src/main/java/dev/isxander/controlify/impl/action/activator/ReleasePulseActivator.java index c74e9bae4..698b0889f 100644 --- a/src/main/java/dev/isxander/controlify/action/activator/ReleasePulseActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/ReleasePulseActivator.java @@ -1,9 +1,9 @@ -package dev.isxander.controlify.action.activator; +package dev.isxander.controlify.impl.action.activator; -import dev.isxander.controlify.action.Accumulator; -import dev.isxander.controlify.action.Activator; -import dev.isxander.controlify.input.LogicalInput; -import dev.isxander.controlify.input.signal.LogicalSignal; +import dev.isxander.controlify.impl.action.Accumulator; +import dev.isxander.controlify.impl.action.Activator; +import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.impl.input.signal.LogicalSignal; /// Produces a pulse when the input is released. public record ReleasePulseActivator(LogicalInput boundInput) implements Activator { diff --git a/src/main/java/dev/isxander/controlify/action/activator/RepeatLatchPulseActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/RepeatLatchPulseActivator.java similarity index 91% rename from src/main/java/dev/isxander/controlify/action/activator/RepeatLatchPulseActivator.java rename to src/main/java/dev/isxander/controlify/impl/action/activator/RepeatLatchPulseActivator.java index b87ab051e..1a6213732 100644 --- a/src/main/java/dev/isxander/controlify/action/activator/RepeatLatchPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/RepeatLatchPulseActivator.java @@ -1,12 +1,12 @@ -package dev.isxander.controlify.action.activator; +package dev.isxander.controlify.impl.action.activator; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.action.Accumulator; -import dev.isxander.controlify.action.Activator; -import dev.isxander.controlify.input.LogicalInput; -import dev.isxander.controlify.input.signal.LogicalSignal; +import dev.isxander.controlify.impl.action.Accumulator; +import dev.isxander.controlify.impl.action.Activator; +import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.impl.input.signal.LogicalSignal; /// Accepts another latch binding, and sends repeat pulses out while the latch is active. public class RepeatLatchPulseActivator implements Activator { diff --git a/src/main/java/dev/isxander/controlify/action/activator/TapPulseActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/TapPulseActivator.java similarity index 75% rename from src/main/java/dev/isxander/controlify/action/activator/TapPulseActivator.java rename to src/main/java/dev/isxander/controlify/impl/action/activator/TapPulseActivator.java index fc8f5dd63..94679b10e 100644 --- a/src/main/java/dev/isxander/controlify/action/activator/TapPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/TapPulseActivator.java @@ -1,9 +1,9 @@ -package dev.isxander.controlify.action.activator; +package dev.isxander.controlify.impl.action.activator; -import dev.isxander.controlify.action.Accumulator; -import dev.isxander.controlify.action.Activator; -import dev.isxander.controlify.input.LogicalInput; -import dev.isxander.controlify.input.signal.LogicalSignal; +import dev.isxander.controlify.impl.action.Accumulator; +import dev.isxander.controlify.impl.action.Activator; +import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.impl.input.signal.LogicalSignal; /// Produces a pulse when the input is tapped. public record TapPulseActivator(LogicalInput boundInput) implements Activator { diff --git a/src/main/java/dev/isxander/controlify/action/activator/ToggleLatchActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/ToggleLatchActivator.java similarity index 85% rename from src/main/java/dev/isxander/controlify/action/activator/ToggleLatchActivator.java rename to src/main/java/dev/isxander/controlify/impl/action/activator/ToggleLatchActivator.java index 691ea2282..fe8012e29 100644 --- a/src/main/java/dev/isxander/controlify/action/activator/ToggleLatchActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/ToggleLatchActivator.java @@ -1,11 +1,11 @@ -package dev.isxander.controlify.action.activator; +package dev.isxander.controlify.impl.action.activator; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.action.Accumulator; -import dev.isxander.controlify.action.Activator; -import dev.isxander.controlify.input.LogicalInput; -import dev.isxander.controlify.input.signal.LogicalSignal; +import dev.isxander.controlify.impl.action.Accumulator; +import dev.isxander.controlify.impl.action.Activator; +import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.impl.input.signal.LogicalSignal; /// Accepts another pulse binding, and converts pulses into toggling a latch. public class ToggleLatchActivator implements Activator { diff --git a/src/main/java/dev/isxander/controlify/action/defaults/DefaultBindManager.java b/src/main/java/dev/isxander/controlify/impl/action/defaults/DefaultBindManager.java similarity index 90% rename from src/main/java/dev/isxander/controlify/action/defaults/DefaultBindManager.java rename to src/main/java/dev/isxander/controlify/impl/action/defaults/DefaultBindManager.java index bc7e14d9f..68e47e1b9 100644 --- a/src/main/java/dev/isxander/controlify/action/defaults/DefaultBindManager.java +++ b/src/main/java/dev/isxander/controlify/impl/action/defaults/DefaultBindManager.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.action.defaults; +package dev.isxander.controlify.impl.action.defaults; import com.google.gson.JsonElement; import com.google.gson.JsonParser; @@ -13,9 +13,9 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.controller.id.ControllerType; -import dev.isxander.controlify.platform.client.resource.SimpleControlifyReloadListener; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.controllermanager.ControllerType; +import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.FileToIdConverter; import net.minecraft.resources.Identifier; import net.minecraft.server.packs.resources.Resource; @@ -31,7 +31,7 @@ public class DefaultBindManager implements SimpleControlifyReloadListener { - public static final String DIRECTORY = "controllers/default_bind"; + public static final String DIRECTORY = "controller/default_bind"; private static final FileToIdConverter converter = FileToIdConverter.json(DIRECTORY); private static final Logger LOGGER = LogUtils.getLogger(); @@ -45,7 +45,7 @@ public class DefaultBindManager implements SimpleControlifyReloadListener defaultsByNamespace = new HashMap<>(); - Identifier defaultNamespaceFile = converter.idToFile(ControllerType.DEFAULT.namespace()); + Identifier defaultNamespaceFile = converter.idToFile(ControllerType.DEFAULT.id()); if (!defaultFiles.containsKey(defaultNamespaceFile)) { LOGGER.error("No default binds found! Everything will be unbound!"); return null; @@ -55,8 +55,8 @@ public class DefaultBindManager implements SimpleControlifyReloadListener> stack : defaultFiles.entrySet()) { Identifier id = stack.getKey(); @@ -66,7 +66,7 @@ public class DefaultBindManager implements SimpleControlifyReloadListener> defaults = this.readDefaults(id, files); - // add the default namespace to the bottom + // add the default id to the bottom defaults.getSecond().add( new LayeredDefaultBindProvider.Layer(defaultNamespaceDefaults, false) ); @@ -115,7 +115,7 @@ public CompletableFuture apply(@Nullable Preparations data, ResourceManage public DefaultBindProvider getDefaultBindProvider(Identifier namespace) { DefaultBindProvider provider = this.defaultsByNamespace.get(namespace); if (provider == null) - provider = this.defaultsByNamespace.get(ControllerType.DEFAULT.namespace()); + provider = this.defaultsByNamespace.get(ControllerType.DEFAULT.id()); if (provider == null) provider = DefaultBindProvider.EMPTY; return provider; diff --git a/src/main/java/dev/isxander/controlify/action/defaults/DefaultBindProvider.java b/src/main/java/dev/isxander/controlify/impl/action/defaults/DefaultBindProvider.java similarity index 76% rename from src/main/java/dev/isxander/controlify/action/defaults/DefaultBindProvider.java rename to src/main/java/dev/isxander/controlify/impl/action/defaults/DefaultBindProvider.java index f18f820be..1c8414c09 100644 --- a/src/main/java/dev/isxander/controlify/action/defaults/DefaultBindProvider.java +++ b/src/main/java/dev/isxander/controlify/impl/action/defaults/DefaultBindProvider.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.action.defaults; +package dev.isxander.controlify.impl.action.defaults; -import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.api.input.LogicalInput; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/action/defaults/LayeredDefaultBindProvider.java b/src/main/java/dev/isxander/controlify/impl/action/defaults/LayeredDefaultBindProvider.java similarity index 92% rename from src/main/java/dev/isxander/controlify/action/defaults/LayeredDefaultBindProvider.java rename to src/main/java/dev/isxander/controlify/impl/action/defaults/LayeredDefaultBindProvider.java index e469b92ca..a37f6731c 100644 --- a/src/main/java/dev/isxander/controlify/action/defaults/LayeredDefaultBindProvider.java +++ b/src/main/java/dev/isxander/controlify/impl/action/defaults/LayeredDefaultBindProvider.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.action.defaults; +package dev.isxander.controlify.impl.action.defaults; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.api.input.LogicalInput; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/action/defaults/MapBackedDefaultBindProvider.java b/src/main/java/dev/isxander/controlify/impl/action/defaults/MapBackedDefaultBindProvider.java similarity index 83% rename from src/main/java/dev/isxander/controlify/action/defaults/MapBackedDefaultBindProvider.java rename to src/main/java/dev/isxander/controlify/impl/action/defaults/MapBackedDefaultBindProvider.java index 46d4c1824..c3697c3dd 100644 --- a/src/main/java/dev/isxander/controlify/action/defaults/MapBackedDefaultBindProvider.java +++ b/src/main/java/dev/isxander/controlify/impl/action/defaults/MapBackedDefaultBindProvider.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.action.defaults; +package dev.isxander.controlify.impl.action.defaults; import com.mojang.serialization.Codec; import com.mojang.serialization.Keyable; import com.mojang.serialization.MapCodec; -import dev.isxander.controlify.api.bind.ControlifyBindApi; -import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.impl.api.bind.ControlifyBindApi; +import dev.isxander.controlify.api.input.LogicalInput; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/action/radial/RadialIcon.java b/src/main/java/dev/isxander/controlify/impl/action/radial/RadialIcon.java similarity index 98% rename from src/main/java/dev/isxander/controlify/action/radial/RadialIcon.java rename to src/main/java/dev/isxander/controlify/impl/action/radial/RadialIcon.java index 21112c3ff..7d11c61ea 100644 --- a/src/main/java/dev/isxander/controlify/action/radial/RadialIcon.java +++ b/src/main/java/dev/isxander/controlify/impl/action/radial/RadialIcon.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.action.radial; +package dev.isxander.controlify.impl.action.radial; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; diff --git a/src/main/java/dev/isxander/controlify/action/radial/RadialIconExtractor.java b/src/main/java/dev/isxander/controlify/impl/action/radial/RadialIconExtractor.java similarity index 97% rename from src/main/java/dev/isxander/controlify/action/radial/RadialIconExtractor.java rename to src/main/java/dev/isxander/controlify/impl/action/radial/RadialIconExtractor.java index d2714c87a..6c51c493a 100644 --- a/src/main/java/dev/isxander/controlify/action/radial/RadialIconExtractor.java +++ b/src/main/java/dev/isxander/controlify/impl/action/radial/RadialIconExtractor.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.action.radial; +package dev.isxander.controlify.impl.action.radial; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; diff --git a/src/main/java/dev/isxander/controlify/action/radial/RadialIconManager.java b/src/main/java/dev/isxander/controlify/impl/action/radial/RadialIconManager.java similarity index 88% rename from src/main/java/dev/isxander/controlify/action/radial/RadialIconManager.java rename to src/main/java/dev/isxander/controlify/impl/action/radial/RadialIconManager.java index ea7dbd269..aece6c14f 100644 --- a/src/main/java/dev/isxander/controlify/action/radial/RadialIconManager.java +++ b/src/main/java/dev/isxander/controlify/impl/action/radial/RadialIconManager.java @@ -4,15 +4,15 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.action.radial; +package dev.isxander.controlify.impl.action.radial; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.platform.client.resource.SimpleControlifyReloadListener; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; import net.minecraft.server.packs.resources.Resource; import net.minecraft.server.packs.resources.ResourceManager; @@ -27,7 +27,7 @@ public class RadialIconManager implements SimpleControlifyReloadListener> { public static final Identifier EMPTY = CUtil.rl("empty"); - private static final Identifier DEFINITIONS = CUtil.rl("radial_icons.json"); + private static final Identifier DEFINITIONS = CUtil.rl("controller/radial_icons.json"); private static final Codec> CODEC = Codec.unboundedMap(Identifier.CODEC, RadialIcon.CODEC); private static final RadialIcon FALLBACK = RadialIcon.model(Identifier.withDefaultNamespace("book")); diff --git a/src/main/java/dev/isxander/controlify/api/entrypoint/ControlifyEntrypoint.java b/src/main/java/dev/isxander/controlify/impl/api/entrypoint/ControlifyEntrypoint.java similarity index 94% rename from src/main/java/dev/isxander/controlify/api/entrypoint/ControlifyEntrypoint.java rename to src/main/java/dev/isxander/controlify/impl/api/entrypoint/ControlifyEntrypoint.java index da3c7507c..9af422e92 100644 --- a/src/main/java/dev/isxander/controlify/api/entrypoint/ControlifyEntrypoint.java +++ b/src/main/java/dev/isxander/controlify/impl/api/entrypoint/ControlifyEntrypoint.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.api.entrypoint; +package dev.isxander.controlify.impl.api.entrypoint; import dev.isxander.controlify.api.ControlifyApi; diff --git a/src/main/java/dev/isxander/controlify/api/entrypoint/InitContext.java b/src/main/java/dev/isxander/controlify/impl/api/entrypoint/InitContext.java similarity index 81% rename from src/main/java/dev/isxander/controlify/api/entrypoint/InitContext.java rename to src/main/java/dev/isxander/controlify/impl/api/entrypoint/InitContext.java index bbeaa8817..404acd5f1 100644 --- a/src/main/java/dev/isxander/controlify/api/entrypoint/InitContext.java +++ b/src/main/java/dev/isxander/controlify/impl/api/entrypoint/InitContext.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.api.entrypoint; +package dev.isxander.controlify.impl.api.entrypoint; import dev.isxander.controlify.api.ControlifyApi; diff --git a/src/main/java/dev/isxander/controlify/api/entrypoint/PreInitContext.java b/src/main/java/dev/isxander/controlify/impl/api/entrypoint/PreInitContext.java similarity index 72% rename from src/main/java/dev/isxander/controlify/api/entrypoint/PreInitContext.java rename to src/main/java/dev/isxander/controlify/impl/api/entrypoint/PreInitContext.java index af0a053a5..da6194cc6 100644 --- a/src/main/java/dev/isxander/controlify/api/entrypoint/PreInitContext.java +++ b/src/main/java/dev/isxander/controlify/impl/api/entrypoint/PreInitContext.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.api.entrypoint; +package dev.isxander.controlify.impl.api.entrypoint; -import dev.isxander.controlify.api.bind.ControlifyBindApi; +import dev.isxander.controlify.impl.api.bind.ControlifyBindApi; import dev.isxander.controlify.api.contextual.ContextualDomainRegistry; public interface PreInitContext { diff --git a/src/main/java/dev/isxander/controlify/api/ingameinput/LookInputModifier.java b/src/main/java/dev/isxander/controlify/impl/api/ingameinput/LookInputModifier.java similarity index 76% rename from src/main/java/dev/isxander/controlify/api/ingameinput/LookInputModifier.java rename to src/main/java/dev/isxander/controlify/impl/api/ingameinput/LookInputModifier.java index c2f7f51d1..81baff9d5 100644 --- a/src/main/java/dev/isxander/controlify/api/ingameinput/LookInputModifier.java +++ b/src/main/java/dev/isxander/controlify/impl/api/ingameinput/LookInputModifier.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.api.ingameinput; +package dev.isxander.controlify.impl.api.ingameinput; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.platform.EventHandler; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.platform.EventHandler; import org.joml.Vector2f; import java.util.function.BiFunction; @@ -17,7 +17,7 @@ * Callbacks should modify the {@link #lookInput()} vector to modify the look input. * Be aware that multiple callbacks could be called, so make sure to be considerate of other mods. */ -public record LookInputModifier(Vector2f lookInput, ControllerEntity controller) { +public record LookInputModifier(Vector2f lookInput, ControllerImpl controller) { /** * Creates a callback to modify the x and y individually. * @@ -25,7 +25,7 @@ public record LookInputModifier(Vector2f lookInput, ControllerEntity controller) * @param y the function for modifying the y-axis * @return the new callback */ - public static EventHandler.Callback functional(BiFunction x, BiFunction y) { + public static EventHandler.Callback functional(BiFunction x, BiFunction y) { return event -> { event.lookInput.x = x.apply(event.lookInput.x, event.controller); event.lookInput.y = y.apply(event.lookInput.y, event.controller); @@ -38,7 +38,7 @@ public static EventHandler.Callback functional(BiFunction zeroIf(Predicate condition) { + static EventHandler.Callback zeroIf(Predicate condition) { return event -> { if (condition.test(event.controller)) { event.lookInput.set(0, 0); diff --git a/src/main/java/dev/isxander/controlify/api/vmousesnapping/ISnapBehaviour.java b/src/main/java/dev/isxander/controlify/impl/api/vmousesnapping/ISnapBehaviour.java similarity index 91% rename from src/main/java/dev/isxander/controlify/api/vmousesnapping/ISnapBehaviour.java rename to src/main/java/dev/isxander/controlify/impl/api/vmousesnapping/ISnapBehaviour.java index 2a349ed91..25a2b19f1 100644 --- a/src/main/java/dev/isxander/controlify/api/vmousesnapping/ISnapBehaviour.java +++ b/src/main/java/dev/isxander/controlify/impl/api/vmousesnapping/ISnapBehaviour.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.api.vmousesnapping; +package dev.isxander.controlify.impl.api.vmousesnapping; import java.util.Set; import java.util.function.Consumer; diff --git a/src/main/java/dev/isxander/controlify/api/vmousesnapping/SnapPoint.java b/src/main/java/dev/isxander/controlify/impl/api/vmousesnapping/SnapPoint.java similarity index 90% rename from src/main/java/dev/isxander/controlify/api/vmousesnapping/SnapPoint.java rename to src/main/java/dev/isxander/controlify/impl/api/vmousesnapping/SnapPoint.java index ffa6bd946..02e770bc4 100644 --- a/src/main/java/dev/isxander/controlify/api/vmousesnapping/SnapPoint.java +++ b/src/main/java/dev/isxander/controlify/impl/api/vmousesnapping/SnapPoint.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.api.vmousesnapping; +package dev.isxander.controlify.impl.api.vmousesnapping; import org.joml.Vector2i; import org.joml.Vector2ic; diff --git a/src/main/java/dev/isxander/controlify/bindings/BindContext.java b/src/main/java/dev/isxander/controlify/impl/bindings/BindContext.java similarity index 84% rename from src/main/java/dev/isxander/controlify/bindings/BindContext.java rename to src/main/java/dev/isxander/controlify/impl/bindings/BindContext.java index e6ba87f22..8a5aa88b9 100644 --- a/src/main/java/dev/isxander/controlify/bindings/BindContext.java +++ b/src/main/java/dev/isxander/controlify/impl/bindings/BindContext.java @@ -4,14 +4,14 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.bindings; +package dev.isxander.controlify.impl.bindings; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.gui.screen.RadialMenuScreen; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.MinecraftUtil; -import dev.isxander.controlify.virtualmouse.VirtualMouseBehaviour; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.gui.screen.RadialMenuScreen; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.MinecraftUtil; +import dev.isxander.controlify.impl.virtualmouse.VirtualMouseBehaviour; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/bindings/ControlifyBindings.java b/src/main/java/dev/isxander/controlify/impl/bindings/ControlifyBindings.java similarity index 98% rename from src/main/java/dev/isxander/controlify/bindings/ControlifyBindings.java rename to src/main/java/dev/isxander/controlify/impl/bindings/ControlifyBindings.java index f1b3fd418..58e0d183c 100644 --- a/src/main/java/dev/isxander/controlify/bindings/ControlifyBindings.java +++ b/src/main/java/dev/isxander/controlify/impl/bindings/ControlifyBindings.java @@ -4,12 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.bindings; +package dev.isxander.controlify.impl.bindings; -import dev.isxander.controlify.api.bind.ControlifyBindApi; -import dev.isxander.controlify.api.bind.InputBindingSupplier; -import dev.isxander.controlify.platform.client.PlatformClientUtil; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.api.bind.ControlifyBindApi; +import dev.isxander.controlify.impl.api.bind.InputBindingSupplier; +import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.ChatFormatting; import net.minecraft.client.KeyMapping; import net.minecraft.client.Minecraft; diff --git a/src/main/java/dev/isxander/controlify/bindings/KeyMappingHandle.java b/src/main/java/dev/isxander/controlify/impl/bindings/KeyMappingHandle.java similarity index 55% rename from src/main/java/dev/isxander/controlify/bindings/KeyMappingHandle.java rename to src/main/java/dev/isxander/controlify/impl/bindings/KeyMappingHandle.java index 5b430074e..beba3d178 100644 --- a/src/main/java/dev/isxander/controlify/bindings/KeyMappingHandle.java +++ b/src/main/java/dev/isxander/controlify/impl/bindings/KeyMappingHandle.java @@ -4,14 +4,14 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.bindings; +package dev.isxander.controlify.impl.bindings; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.impl.controller.ControllerImpl; import java.util.function.BooleanSupplier; public interface KeyMappingHandle { void controlify$setPressed(boolean isDown); - void controlify$addToggleCondition(ControllerEntity controller, BooleanSupplier condition); + void controlify$addToggleCondition(ControllerImpl controller, BooleanSupplier condition); } diff --git a/src/main/java/dev/isxander/controlify/fixes/boatfix/AnalogBoatInput.java b/src/main/java/dev/isxander/controlify/impl/boatfix/AnalogBoatInput.java similarity index 87% rename from src/main/java/dev/isxander/controlify/fixes/boatfix/AnalogBoatInput.java rename to src/main/java/dev/isxander/controlify/impl/boatfix/AnalogBoatInput.java index 137a98149..e9761bc6c 100644 --- a/src/main/java/dev/isxander/controlify/fixes/boatfix/AnalogBoatInput.java +++ b/src/main/java/dev/isxander/controlify/impl/boatfix/AnalogBoatInput.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.fixes.boatfix; +package dev.isxander.controlify.impl.boatfix; import dev.isxander.controlify.mixins.feature.patches.analogueboat.AbstractBoatMixin; diff --git a/src/main/java/dev/isxander/controlify/compatibility/CompatMixinPlatform.java b/src/main/java/dev/isxander/controlify/impl/compatibility/CompatMixinPlatform.java similarity index 81% rename from src/main/java/dev/isxander/controlify/compatibility/CompatMixinPlatform.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/CompatMixinPlatform.java index f73a79e39..82b424b7c 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/CompatMixinPlatform.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/CompatMixinPlatform.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility; +package dev.isxander.controlify.impl.compatibility; @FunctionalInterface public interface CompatMixinPlatform { diff --git a/src/main/java/dev/isxander/controlify/compatibility/CompatMixinPlugin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/CompatMixinPlugin.java similarity index 98% rename from src/main/java/dev/isxander/controlify/compatibility/CompatMixinPlugin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/CompatMixinPlugin.java index 25186290a..bc62b26b2 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/CompatMixinPlugin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/CompatMixinPlugin.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility; +package dev.isxander.controlify.impl.compatibility; import org.objectweb.asm.tree.ClassNode; import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; diff --git a/src/main/java/dev/isxander/controlify/compatibility/ControlifyCompat.java b/src/main/java/dev/isxander/controlify/impl/compatibility/ControlifyCompat.java similarity index 80% rename from src/main/java/dev/isxander/controlify/compatibility/ControlifyCompat.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/ControlifyCompat.java index d50059884..87a47cd7f 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/ControlifyCompat.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/ControlifyCompat.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility; +package dev.isxander.controlify.impl.compatibility; -import dev.isxander.controlify.platform.main.PlatformMainUtil; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.util.Util; import java.util.HashSet; @@ -28,7 +28,7 @@ public static void init() { try { wrapCompatCall( SIMPLE_VOICE_CHAT, - dev.isxander.controlify.compatibility.simplevoicechat.SimpleVoiceChatCompat::init + dev.isxander.controlify.impl.compatibility.simplevoicechat.SimpleVoiceChatCompat::init ); } catch (NoClassDefFoundError e) { disabledMods.add(SIMPLE_VOICE_CHAT); @@ -39,7 +39,7 @@ public static void init() { try { wrapCompatCall( FANCY_MENU, - dev.isxander.controlify.compatibility.fancymenu.FancyMenuCompat::registerActions + dev.isxander.controlify.impl.compatibility.fancymenu.FancyMenuCompat::registerActions ); } catch (NoClassDefFoundError e) { disabledMods.add(FANCY_MENU); diff --git a/src/main/java/dev/isxander/controlify/compatibility/fancymenu/FancyMenuCompat.java b/src/main/java/dev/isxander/controlify/impl/compatibility/fancymenu/FancyMenuCompat.java similarity index 85% rename from src/main/java/dev/isxander/controlify/compatibility/fancymenu/FancyMenuCompat.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/fancymenu/FancyMenuCompat.java index 9c4f78d56..efc560f69 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/fancymenu/FancyMenuCompat.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/fancymenu/FancyMenuCompat.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.fancymenu; +package dev.isxander.controlify.impl.compatibility.fancymenu; //? if fancy_menu { diff --git a/src/main/java/dev/isxander/controlify/compatibility/fancymenu/OpenControlifySettingsAction.java b/src/main/java/dev/isxander/controlify/impl/compatibility/fancymenu/OpenControlifySettingsAction.java similarity index 84% rename from src/main/java/dev/isxander/controlify/compatibility/fancymenu/OpenControlifySettingsAction.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/fancymenu/OpenControlifySettingsAction.java index c0d53df54..c4905900b 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/fancymenu/OpenControlifySettingsAction.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/fancymenu/OpenControlifySettingsAction.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.fancymenu; +package dev.isxander.controlify.impl.compatibility.fancymenu; //? if fancy_menu { import de.keksuccino.fancymenu.customization.action.Action; -import dev.isxander.controlify.gui.screen.ControlifySettingsScreen; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.gui.screen.ControlifySettingsScreen; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.network.chat.Component; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/compatibility/iris/IrisMixinPlugin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/iris/IrisMixinPlugin.java similarity index 66% rename from src/main/java/dev/isxander/controlify/compatibility/iris/IrisMixinPlugin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/iris/IrisMixinPlugin.java index 7caf475c6..0525fa487 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/iris/IrisMixinPlugin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/iris/IrisMixinPlugin.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.iris; +package dev.isxander.controlify.impl.compatibility.iris; -import dev.isxander.controlify.compatibility.CompatMixinPlugin; +import dev.isxander.controlify.impl.compatibility.CompatMixinPlugin; public class IrisMixinPlugin extends CompatMixinPlugin { @Override diff --git a/src/main/java/dev/isxander/controlify/compatibility/iris/mixins/BaseOptionElementWidgetMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/iris/mixins/BaseOptionElementWidgetMixin.java similarity index 78% rename from src/main/java/dev/isxander/controlify/compatibility/iris/mixins/BaseOptionElementWidgetMixin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/iris/mixins/BaseOptionElementWidgetMixin.java index 50d7ca7f6..10803cdd8 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/iris/mixins/BaseOptionElementWidgetMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/iris/mixins/BaseOptionElementWidgetMixin.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.iris.mixins; +package dev.isxander.controlify.impl.compatibility.iris.mixins; //? if iris { -import dev.isxander.controlify.compatibility.iris.screenop.BaseOptionElementComponentProcessor; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.impl.compatibility.iris.screenop.BaseOptionElementComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; import net.irisshaders.iris.gui.NavigationController; import net.irisshaders.iris.gui.element.widget.BaseOptionElementWidget; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/compatibility/iris/screenop/BaseOptionElementComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/iris/screenop/BaseOptionElementComponentProcessor.java similarity index 78% rename from src/main/java/dev/isxander/controlify/compatibility/iris/screenop/BaseOptionElementComponentProcessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/iris/screenop/BaseOptionElementComponentProcessor.java index 138d6ee85..70ece565d 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/iris/screenop/BaseOptionElementComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/iris/screenop/BaseOptionElementComponentProcessor.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.iris.screenop; +package dev.isxander.controlify.impl.compatibility.iris.screenop; //? if iris { -import dev.isxander.controlify.screenop.compat.AbstractSliderComponentProcessor; +import dev.isxander.controlify.impl.screenop.compat.AbstractSliderComponentProcessor; import java.util.function.Consumer; diff --git a/src/main/java/dev/isxander/controlify/compatibility/rrls/RrlsMixinPlugin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/rrls/RrlsMixinPlugin.java similarity index 66% rename from src/main/java/dev/isxander/controlify/compatibility/rrls/RrlsMixinPlugin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/rrls/RrlsMixinPlugin.java index 0b1b7f937..16f42bda5 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/rrls/RrlsMixinPlugin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/rrls/RrlsMixinPlugin.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.rrls; +package dev.isxander.controlify.impl.compatibility.rrls; -import dev.isxander.controlify.compatibility.CompatMixinPlugin; +import dev.isxander.controlify.impl.compatibility.CompatMixinPlugin; public class RrlsMixinPlugin extends CompatMixinPlugin { @Override diff --git a/src/main/java/dev/isxander/controlify/compatibility/rrls/mixins/HideTypeMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/rrls/mixins/HideTypeMixin.java similarity index 92% rename from src/main/java/dev/isxander/controlify/compatibility/rrls/mixins/HideTypeMixin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/rrls/mixins/HideTypeMixin.java index 68d479f57..5b80ee800 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/rrls/mixins/HideTypeMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/rrls/mixins/HideTypeMixin.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.rrls.mixins; +package dev.isxander.controlify.impl.compatibility.rrls.mixins; import com.llamalad7.mixinextras.injector.ModifyReturnValue; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.utils.CUtil; import org.spongepowered.asm.mixin.Dynamic; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Pseudo; diff --git a/src/main/java/dev/isxander/controlify/compatibility/rso/RSOMixinPlugin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/rso/RSOMixinPlugin.java similarity index 67% rename from src/main/java/dev/isxander/controlify/compatibility/rso/RSOMixinPlugin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/rso/RSOMixinPlugin.java index 867051aa0..bfb7c9ee6 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/rso/RSOMixinPlugin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/rso/RSOMixinPlugin.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.rso; +package dev.isxander.controlify.impl.compatibility.rso; -import dev.isxander.controlify.compatibility.CompatMixinPlugin; +import dev.isxander.controlify.impl.compatibility.CompatMixinPlugin; public class RSOMixinPlugin extends CompatMixinPlugin { @Override diff --git a/src/main/java/dev/isxander/controlify/compatibility/rso/mixins/AbstractFrameAccessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/rso/mixins/AbstractFrameAccessor.java similarity index 90% rename from src/main/java/dev/isxander/controlify/compatibility/rso/mixins/AbstractFrameAccessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/rso/mixins/AbstractFrameAccessor.java index d55e061be..e620bc981 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/rso/mixins/AbstractFrameAccessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/rso/mixins/AbstractFrameAccessor.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.rso.mixins; +package dev.isxander.controlify.impl.compatibility.rso.mixins; //? if reeses_sodium_options { /*import me.flashyreese.mods.reeses_sodium_options.client.gui.frame.AbstractFrame; diff --git a/src/main/java/dev/isxander/controlify/compatibility/rso/mixins/SodiumVideoOptionsScreenMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/rso/mixins/SodiumVideoOptionsScreenMixin.java similarity index 98% rename from src/main/java/dev/isxander/controlify/compatibility/rso/mixins/SodiumVideoOptionsScreenMixin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/rso/mixins/SodiumVideoOptionsScreenMixin.java index 7ef7837b3..3ee55bd84 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/rso/mixins/SodiumVideoOptionsScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/rso/mixins/SodiumVideoOptionsScreenMixin.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.rso.mixins; +package dev.isxander.controlify.impl.compatibility.rso.mixins; //? if reeses_sodium_options { /*import com.llamalad7.mixinextras.injector.ModifyExpressionValue; diff --git a/src/main/java/dev/isxander/controlify/compatibility/rso/mixins/TabFrameAccessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/rso/mixins/TabFrameAccessor.java similarity index 92% rename from src/main/java/dev/isxander/controlify/compatibility/rso/mixins/TabFrameAccessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/rso/mixins/TabFrameAccessor.java index 55189e1f7..e8983b55e 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/rso/mixins/TabFrameAccessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/rso/mixins/TabFrameAccessor.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.rso.mixins; +package dev.isxander.controlify.impl.compatibility.rso.mixins; //? if reeses_sodium_options { /*import me.flashyreese.mods.reeses_sodium_options.client.gui.frame.AbstractFrame; diff --git a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/SVCMixinPlugin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/SVCMixinPlugin.java similarity index 64% rename from src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/SVCMixinPlugin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/SVCMixinPlugin.java index eeb850f03..134273d6f 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/SVCMixinPlugin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/SVCMixinPlugin.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.simplevoicechat; +package dev.isxander.controlify.impl.compatibility.simplevoicechat; -import dev.isxander.controlify.compatibility.CompatMixinPlugin; +import dev.isxander.controlify.impl.compatibility.CompatMixinPlugin; public class SVCMixinPlugin extends CompatMixinPlugin { @Override diff --git a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/SimpleVoiceChatCompat.java b/src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/SimpleVoiceChatCompat.java similarity index 86% rename from src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/SimpleVoiceChatCompat.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/SimpleVoiceChatCompat.java index 12d0f5de2..d00ccbac0 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/SimpleVoiceChatCompat.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/SimpleVoiceChatCompat.java @@ -4,18 +4,18 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.simplevoicechat; +package dev.isxander.controlify.impl.compatibility.simplevoicechat; //? if simple_voice_chat { 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; -import dev.isxander.controlify.api.bind.InputBindingSupplier; -import dev.isxander.controlify.compatibility.simplevoicechat.mixins.KeyEventsAccessor; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.api.ControlifyEvents; +import dev.isxander.controlify.impl.api.bind.ControlifyBindApi; +import dev.isxander.controlify.impl.api.bind.InputBindingSupplier; +import dev.isxander.controlify.impl.compatibility.simplevoicechat.mixins.KeyEventsAccessor; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; @@ -51,7 +51,7 @@ public static void init() { .keyEmulation(KeyEvents.KEY_MUTE)); ControlifyEvents.ACTIVE_CONTROLLER_TICKED.register(event -> { - ControllerEntity controller = event.controller(); + ControllerImpl controller = event.controller(); var pttHold = pttHoldSupplier.on(controller); var pttToggle = pttToggleSupplier.on(controller); diff --git a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/KeyEventsAccessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/mixins/KeyEventsAccessor.java similarity index 84% rename from src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/KeyEventsAccessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/mixins/KeyEventsAccessor.java index ecc4a2f14..263a98771 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/KeyEventsAccessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/mixins/KeyEventsAccessor.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.simplevoicechat.mixins; +package dev.isxander.controlify.impl.compatibility.simplevoicechat.mixins; //? if simple_voice_chat { diff --git a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/PTTKeyHandlerMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/mixins/PTTKeyHandlerMixin.java similarity index 87% rename from src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/PTTKeyHandlerMixin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/mixins/PTTKeyHandlerMixin.java index 2ce75f8c3..9c29d6b81 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/simplevoicechat/mixins/PTTKeyHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/mixins/PTTKeyHandlerMixin.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.simplevoicechat.mixins; +package dev.isxander.controlify.impl.compatibility.simplevoicechat.mixins; //? if simple_voice_chat { import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import de.maxhenkel.voicechat.voice.client.PTTKeyHandler; -import dev.isxander.controlify.compatibility.simplevoicechat.SimpleVoiceChatCompat; +import dev.isxander.controlify.impl.compatibility.simplevoicechat.SimpleVoiceChatCompat; import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/SodiumMixinPlugin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/SodiumMixinPlugin.java similarity index 66% rename from src/main/java/dev/isxander/controlify/compatibility/sodium/SodiumMixinPlugin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/sodium/SodiumMixinPlugin.java index 2899a76a3..0f337f3ae 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/SodiumMixinPlugin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/SodiumMixinPlugin.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.sodium; +package dev.isxander.controlify.impl.compatibility.sodium; -import dev.isxander.controlify.compatibility.CompatMixinPlugin; +import dev.isxander.controlify.impl.compatibility.CompatMixinPlugin; public class SodiumMixinPlugin extends CompatMixinPlugin { @Override diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/AbstractWidgetMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/AbstractWidgetMixin.java similarity index 88% rename from src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/AbstractWidgetMixin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/AbstractWidgetMixin.java index 91090a871..c936c38aa 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/AbstractWidgetMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/AbstractWidgetMixin.java @@ -4,12 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.sodium.mixins; +package dev.isxander.controlify.impl.compatibility.sodium.mixins; //? if sodium { import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.impl.Controlify; import net.minecraft.client.InputType; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/CycleControlElementMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/CycleControlElementMixin.java similarity index 72% rename from src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/CycleControlElementMixin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/CycleControlElementMixin.java index 8c3aa78ba..62575c030 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/CycleControlElementMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/CycleControlElementMixin.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.sodium.mixins; +package dev.isxander.controlify.impl.compatibility.sodium.mixins; //? if sodium { -import dev.isxander.controlify.compatibility.sodium.screenop.CycleControlProcessor; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.impl.compatibility.sodium.screenop.CycleControlProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetAccessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/FlatButtonWidgetAccessor.java similarity index 86% rename from src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetAccessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/FlatButtonWidgetAccessor.java index 6cd2513d5..0f75a167b 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetAccessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/FlatButtonWidgetAccessor.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.sodium.mixins; +package dev.isxander.controlify.impl.compatibility.sodium.mixins; //? if sodium { diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/FlatButtonWidgetMixin.java similarity index 91% rename from src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetMixin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/FlatButtonWidgetMixin.java index c9e592836..6fca9d01f 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/FlatButtonWidgetMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/FlatButtonWidgetMixin.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.sodium.mixins; +package dev.isxander.controlify.impl.compatibility.sodium.mixins; //? if sodium { import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.gui.ButtonGuideRenderer; +import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.impl.gui.ButtonGuideRenderer; import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; import net.minecraft.network.chat.Component; import org.objectweb.asm.Opcodes; diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/SliderControlElementMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/SliderControlElementMixin.java similarity index 83% rename from src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/SliderControlElementMixin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/SliderControlElementMixin.java index 2b8c05ed1..13f9e9e1d 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/SliderControlElementMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/SliderControlElementMixin.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.sodium.mixins; +package dev.isxander.controlify.impl.compatibility.sodium.mixins; //? if sodium { -import dev.isxander.controlify.compatibility.sodium.screenop.SliderControlProcessor; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.impl.compatibility.sodium.screenop.SliderControlProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; import net.caffeinemc.mods.sodium.client.config.structure.IntegerOption; import net.caffeinemc.mods.sodium.client.gui.ColorTheme; import net.caffeinemc.mods.sodium.client.gui.options.control.AbstractOptionList; diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/TickBoxControlElementMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/TickBoxControlElementMixin.java similarity index 72% rename from src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/TickBoxControlElementMixin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/TickBoxControlElementMixin.java index 82efa42b0..60c3297c4 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/TickBoxControlElementMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/TickBoxControlElementMixin.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.sodium.mixins; +package dev.isxander.controlify.impl.compatibility.sodium.mixins; //? if sodium { -import dev.isxander.controlify.compatibility.sodium.screenop.TickBoxControlProcessor; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.impl.compatibility.sodium.screenop.TickBoxControlProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/VideoSettingsScreenMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/VideoSettingsScreenMixin.java similarity index 90% rename from src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/VideoSettingsScreenMixin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/VideoSettingsScreenMixin.java index cf87cfae3..9c29763bf 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/mixins/VideoSettingsScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/VideoSettingsScreenMixin.java @@ -4,14 +4,14 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.sodium.mixins; +package dev.isxander.controlify.impl.compatibility.sodium.mixins; //? if sodium { -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; +import dev.isxander.controlify.impl.compatibility.sodium.screenop.SodiumGuiScreenProcessor; +import dev.isxander.controlify.impl.compatibility.sodium.screenop.SodiumScreenOperations; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; import net.caffeinemc.mods.sodium.client.config.ConfigManager; import net.caffeinemc.mods.sodium.client.config.structure.Page; import net.caffeinemc.mods.sodium.client.gui.VideoSettingsScreen; diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/CycleControlProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/CycleControlProcessor.java similarity index 69% rename from src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/CycleControlProcessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/CycleControlProcessor.java index d86593f8a..f3b87073a 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/CycleControlProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/CycleControlProcessor.java @@ -4,12 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.sodium.screenop; +package dev.isxander.controlify.impl.compatibility.sodium.screenop; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; import java.util.function.Consumer; @@ -21,7 +21,7 @@ public CycleControlProcessor(Consumer cycleMethod) { } @Override - public boolean overrideControllerButtons(ScreenProcessor screen, ControllerEntity controller) { + public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { if (ControlifyBindings.GUI_SECONDARY_NAVI_RIGHT.on(controller).justPressed() || ControlifyBindings.GUI_PRESS.on(controller).justPressed() ) { diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SliderControlProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SliderControlProcessor.java similarity index 76% rename from src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SliderControlProcessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SliderControlProcessor.java index 266ba02dc..19247d025 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SliderControlProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SliderControlProcessor.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.sodium.screenop; +package dev.isxander.controlify.impl.compatibility.sodium.screenop; -import dev.isxander.controlify.screenop.compat.AbstractSliderComponentProcessor; +import dev.isxander.controlify.impl.screenop.compat.AbstractSliderComponentProcessor; import java.util.function.Consumer; diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java similarity index 73% rename from src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java index 12667a3fb..ab154ebd9 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java @@ -4,16 +4,16 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.sodium.screenop; +package dev.isxander.controlify.impl.compatibility.sodium.screenop; //? if sodium { -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; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.ScreenProcessor; +import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.compatibility.sodium.mixins.FlatButtonWidgetAccessor; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; import net.minecraft.client.gui.screens.Screen; public class SodiumGuiScreenProcessor extends ScreenProcessor { @@ -25,12 +25,12 @@ public SodiumGuiScreenProcessor(Screen screen, SodiumScreenOperations operations } @Override - protected void handleComponentNavigation(ControllerEntity controller) { + protected void handleComponentNavigation(ControllerImpl controller) { super.handleComponentNavigation(controller); } @Override - protected void handleButtons(ControllerEntity controller) { + protected void handleButtons(ControllerImpl controller) { if (ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).justPressed()) { ((FlatButtonWidgetAccessor) operations.controlify$getApplyButton()).invokeDoAction(); } diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumScreenOperations.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumScreenOperations.java similarity index 87% rename from src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumScreenOperations.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumScreenOperations.java index be61f804f..026e253e7 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/SodiumScreenOperations.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumScreenOperations.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.sodium.screenop; +package dev.isxander.controlify.impl.compatibility.sodium.screenop; //? if sodium { diff --git a/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/TickBoxControlProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/TickBoxControlProcessor.java similarity index 67% rename from src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/TickBoxControlProcessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/TickBoxControlProcessor.java index 376066774..11754626d 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/sodium/screenop/TickBoxControlProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/TickBoxControlProcessor.java @@ -4,12 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.sodium.screenop; +package dev.isxander.controlify.impl.compatibility.sodium.screenop; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; public class TickBoxControlProcessor implements ComponentProcessor { private final Runnable toggleMethod; @@ -19,7 +19,7 @@ public TickBoxControlProcessor(Runnable toggleMethod) { } @Override - public boolean overrideControllerButtons(ScreenProcessor screen, ControllerEntity controller) { + public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { if (ControlifyBindings.GUI_PRESS.on(controller).guiPressed().get()) { toggleMethod.run(); return true; diff --git a/src/main/java/dev/isxander/controlify/compatibility/yacl/YACLMixinPlugin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/YACLMixinPlugin.java similarity index 67% rename from src/main/java/dev/isxander/controlify/compatibility/yacl/YACLMixinPlugin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/yacl/YACLMixinPlugin.java index ab2ad90cd..9163b7d88 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/yacl/YACLMixinPlugin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/YACLMixinPlugin.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.yacl; +package dev.isxander.controlify.impl.compatibility.yacl; -import dev.isxander.controlify.compatibility.CompatMixinPlugin; +import dev.isxander.controlify.impl.compatibility.CompatMixinPlugin; public class YACLMixinPlugin extends CompatMixinPlugin { @Override diff --git a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/ControllerWidgetAccessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/ControllerWidgetAccessor.java similarity index 87% rename from src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/ControllerWidgetAccessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/ControllerWidgetAccessor.java index b71b7b205..ef171811c 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/ControllerWidgetAccessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/ControllerWidgetAccessor.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.yacl.mixins; +package dev.isxander.controlify.impl.compatibility.yacl.mixins; import dev.isxander.yacl3.api.Controller; import dev.isxander.yacl3.gui.controllers.ControllerWidget; diff --git a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/CyclingControllerElementMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/CyclingControllerElementMixin.java similarity index 68% rename from src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/CyclingControllerElementMixin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/CyclingControllerElementMixin.java index 867ffaa98..2bd77edb5 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/CyclingControllerElementMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/CyclingControllerElementMixin.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.yacl.mixins; +package dev.isxander.controlify.impl.compatibility.yacl.mixins; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ComponentProcessorProvider; -import dev.isxander.controlify.compatibility.yacl.screenop.CyclingControllerElementComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.impl.compatibility.yacl.screenop.CyclingControllerElementComponentProcessor; import dev.isxander.yacl3.gui.controllers.cycling.CyclingControllerElement; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; diff --git a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/GroupSeparatorEntryAccessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/GroupSeparatorEntryAccessor.java similarity index 87% rename from src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/GroupSeparatorEntryAccessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/GroupSeparatorEntryAccessor.java index 5982f854d..c6ea351fa 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/GroupSeparatorEntryAccessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/GroupSeparatorEntryAccessor.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.yacl.mixins; +package dev.isxander.controlify.impl.compatibility.yacl.mixins; import dev.isxander.yacl3.gui.OptionListWidget; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/OptionListWidgetMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/OptionListWidgetMixin.java similarity index 67% rename from src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/OptionListWidgetMixin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/OptionListWidgetMixin.java index 023fb1593..d47ac7454 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/OptionListWidgetMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/OptionListWidgetMixin.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.yacl.mixins; +package dev.isxander.controlify.impl.compatibility.yacl.mixins; -import dev.isxander.controlify.compatibility.yacl.screenop.OptionListWidgetComponentProcessor; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.impl.compatibility.yacl.screenop.OptionListWidgetComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; import dev.isxander.yacl3.gui.OptionListWidget; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; diff --git a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/SliderControllerElementMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/SliderControllerElementMixin.java similarity index 68% rename from src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/SliderControllerElementMixin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/SliderControllerElementMixin.java index bdc484c14..8b0f11aa7 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/SliderControllerElementMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/SliderControllerElementMixin.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.yacl.mixins; +package dev.isxander.controlify.impl.compatibility.yacl.mixins; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ComponentProcessorProvider; -import dev.isxander.controlify.compatibility.yacl.screenop.SliderControllerElementComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.impl.compatibility.yacl.screenop.SliderControllerElementComponentProcessor; import dev.isxander.yacl3.gui.controllers.slider.SliderControllerElement; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; diff --git a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/StringControllerElementAccessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/StringControllerElementAccessor.java similarity index 86% rename from src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/StringControllerElementAccessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/StringControllerElementAccessor.java index e47c13b88..bd40e6141 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/StringControllerElementAccessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/StringControllerElementAccessor.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.yacl.mixins; +package dev.isxander.controlify.impl.compatibility.yacl.mixins; import dev.isxander.yacl3.gui.controllers.string.StringControllerElement; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/StringControllerElementMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/StringControllerElementMixin.java similarity index 68% rename from src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/StringControllerElementMixin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/StringControllerElementMixin.java index ffd082b3b..ae449df55 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/StringControllerElementMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/StringControllerElementMixin.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.yacl.mixins; +package dev.isxander.controlify.impl.compatibility.yacl.mixins; -import dev.isxander.controlify.compatibility.yacl.screenop.StringControllerElementComponentProcessor; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.impl.compatibility.yacl.screenop.StringControllerElementComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; import dev.isxander.yacl3.gui.controllers.string.StringControllerElement; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; diff --git a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/YACLScreenCategoryTabAccessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenCategoryTabAccessor.java similarity index 90% rename from src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/YACLScreenCategoryTabAccessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenCategoryTabAccessor.java index fae748607..09a2f3ad2 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/YACLScreenCategoryTabAccessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenCategoryTabAccessor.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.yacl.mixins; +package dev.isxander.controlify.impl.compatibility.yacl.mixins; import dev.isxander.yacl3.gui.OptionListWidget; import dev.isxander.yacl3.gui.WidgetAndType; diff --git a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/YACLScreenCategoryTabMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenCategoryTabMixin.java similarity index 78% rename from src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/YACLScreenCategoryTabMixin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenCategoryTabMixin.java index 9fb2756f1..89370f043 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/YACLScreenCategoryTabMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenCategoryTabMixin.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.yacl.mixins; +package dev.isxander.controlify.impl.compatibility.yacl.mixins; -import dev.isxander.controlify.api.buttonguide.ButtonGuideApi; -import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate; -import dev.isxander.controlify.bindings.ControlifyBindings; +import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; import dev.isxander.yacl3.gui.YACLScreen; import net.minecraft.client.gui.components.Button; import org.spongepowered.asm.mixin.Final; diff --git a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/YACLScreenMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenMixin.java similarity index 66% rename from src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/YACLScreenMixin.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenMixin.java index 3515ed9f6..c77e766e2 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/yacl/mixins/YACLScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenMixin.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.yacl.mixins; +package dev.isxander.controlify.impl.compatibility.yacl.mixins; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.compatibility.yacl.screenop.YACLScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.compatibility.yacl.screenop.YACLScreenProcessor; import dev.isxander.yacl3.gui.YACLScreen; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; diff --git a/src/main/java/dev/isxander/controlify/compatibility/yacl/screenop/CyclingControllerElementComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/CyclingControllerElementComponentProcessor.java similarity index 73% rename from src/main/java/dev/isxander/controlify/compatibility/yacl/screenop/CyclingControllerElementComponentProcessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/CyclingControllerElementComponentProcessor.java index 1fb987ae8..216cac244 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/yacl/screenop/CyclingControllerElementComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/CyclingControllerElementComponentProcessor.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.yacl.screenop; +package dev.isxander.controlify.impl.compatibility.yacl.screenop; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.compatibility.yacl.mixins.ControllerWidgetAccessor; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.compatibility.yacl.mixins.ControllerWidgetAccessor; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.yacl3.gui.controllers.cycling.CyclingControllerElement; public class CyclingControllerElementComponentProcessor implements ComponentProcessor { @@ -23,7 +23,7 @@ public CyclingControllerElementComponentProcessor(CyclingControllerElement cycli } @Override - public boolean overrideControllerNavigation(ScreenProcessor screen, ControllerEntity controller) { + public boolean overrideControllerNavigation(ScreenProcessor screen, ControllerImpl controller) { boolean left = ControlifyBindings.GUI_SECONDARY_NAVI_LEFT.on(controller).digitalNow(); boolean right = ControlifyBindings.GUI_SECONDARY_NAVI_RIGHT.on(controller).digitalNow(); diff --git a/src/main/java/dev/isxander/controlify/compatibility/yacl/screenop/OptionListWidgetComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/OptionListWidgetComponentProcessor.java similarity index 82% rename from src/main/java/dev/isxander/controlify/compatibility/yacl/screenop/OptionListWidgetComponentProcessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/OptionListWidgetComponentProcessor.java index 79e4d6a6b..160306be5 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/yacl/screenop/OptionListWidgetComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/OptionListWidgetComponentProcessor.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.yacl.screenop; +package dev.isxander.controlify.impl.compatibility.yacl.screenop; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.compatibility.yacl.mixins.GroupSeparatorEntryAccessor; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.compatibility.yacl.mixins.GroupSeparatorEntryAccessor; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.yacl3.gui.OptionListWidget; import org.jetbrains.annotations.Nullable; @@ -27,7 +27,7 @@ public OptionListWidgetComponentProcessor(OptionListWidget optionListWidget) { * Allows navigating between groups using secondary navigation up/down. */ @Override - public boolean overrideControllerNavigation(ScreenProcessor screen, ControllerEntity controller) { + public boolean overrideControllerNavigation(ScreenProcessor screen, ControllerImpl controller) { boolean up = ControlifyBindings.GUI_SECONDARY_NAVI_UP.on(controller).justPressed(); boolean down = ControlifyBindings.GUI_SECONDARY_NAVI_DOWN.on(controller).justPressed(); if (up || down) { diff --git a/src/main/java/dev/isxander/controlify/compatibility/yacl/screenop/SliderControllerElementComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/SliderControllerElementComponentProcessor.java similarity index 74% rename from src/main/java/dev/isxander/controlify/compatibility/yacl/screenop/SliderControllerElementComponentProcessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/SliderControllerElementComponentProcessor.java index 4f396ebd4..35a32bf1d 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/yacl/screenop/SliderControllerElementComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/SliderControllerElementComponentProcessor.java @@ -4,14 +4,14 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.yacl.screenop; - -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.compatibility.yacl.mixins.ControllerWidgetAccessor; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.utils.HoldRepeatHelper; +package dev.isxander.controlify.impl.compatibility.yacl.screenop; + +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.compatibility.yacl.mixins.ControllerWidgetAccessor; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.utils.HoldRepeatHelper; import dev.isxander.yacl3.gui.controllers.slider.SliderControllerElement; public class SliderControllerElementComponentProcessor implements ComponentProcessor { @@ -23,7 +23,7 @@ public SliderControllerElementComponentProcessor(SliderControllerElement element } @Override - public boolean overrideControllerButtons(ScreenProcessor screen, ControllerEntity controller) { + public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { var left = ControlifyBindings.GUI_SECONDARY_NAVI_LEFT.on(controller).digitalNow(); var leftPrev = ControlifyBindings.GUI_SECONDARY_NAVI_LEFT.on(controller).digitalPrev(); var right = ControlifyBindings.GUI_SECONDARY_NAVI_RIGHT.on(controller).digitalNow(); diff --git a/src/main/java/dev/isxander/controlify/compatibility/yacl/screenop/StringControllerElementComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/StringControllerElementComponentProcessor.java similarity index 74% rename from src/main/java/dev/isxander/controlify/compatibility/yacl/screenop/StringControllerElementComponentProcessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/StringControllerElementComponentProcessor.java index ac0f15fbd..28355cb28 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/yacl/screenop/StringControllerElementComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/StringControllerElementComponentProcessor.java @@ -4,17 +4,17 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.yacl.screenop; +package dev.isxander.controlify.impl.compatibility.yacl.screenop; import com.mojang.blaze3d.platform.InputConstants; -import dev.isxander.controlify.compatibility.yacl.mixins.StringControllerElementAccessor; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.keyboard.ComponentKeyboardBehaviour; -import dev.isxander.controlify.screenop.keyboard.InputTarget; -import dev.isxander.controlify.screenop.keyboard.KeyboardLayouts; -import dev.isxander.controlify.screenop.keyboard.KeyboardOverlayScreen; +import dev.isxander.controlify.impl.compatibility.yacl.mixins.StringControllerElementAccessor; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.keyboard.ComponentKeyboardBehaviour; +import dev.isxander.controlify.impl.screenop.keyboard.InputTarget; +import dev.isxander.controlify.impl.screenop.keyboard.KeyboardLayouts; +import dev.isxander.controlify.impl.screenop.keyboard.KeyboardOverlayScreen; import dev.isxander.yacl3.gui.controllers.string.StringControllerElement; import net.minecraft.client.input.CharacterEvent; import net.minecraft.client.input.KeyEvent; @@ -27,7 +27,7 @@ public StringControllerElementComponentProcessor(StringControllerElement element } @Override - public ComponentKeyboardBehaviour getKeyboardBehaviour(ScreenProcessor screen, ControllerEntity controller) { + public ComponentKeyboardBehaviour getKeyboardBehaviour(ScreenProcessor screen, ControllerImpl controller) { return new ComponentKeyboardBehaviour.Handled( KeyboardLayouts.simple(), new StringInputTarget(), diff --git a/src/main/java/dev/isxander/controlify/compatibility/yacl/screenop/YACLScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/YACLScreenProcessor.java similarity index 75% rename from src/main/java/dev/isxander/controlify/compatibility/yacl/screenop/YACLScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/YACLScreenProcessor.java index 9c9351fad..2bcc57edf 100644 --- a/src/main/java/dev/isxander/controlify/compatibility/yacl/screenop/YACLScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/YACLScreenProcessor.java @@ -4,12 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.compatibility.yacl.screenop; +package dev.isxander.controlify.impl.compatibility.yacl.screenop; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.compatibility.yacl.mixins.YACLScreenCategoryTabAccessor; -import dev.isxander.controlify.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.compatibility.yacl.mixins.YACLScreenCategoryTabAccessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.yacl3.gui.OptionListWidget; import dev.isxander.yacl3.gui.WidgetAndType; import dev.isxander.yacl3.gui.YACLScreen; @@ -20,7 +20,7 @@ public YACLScreenProcessor(YACLScreen screen) { } @Override - protected void handleButtons(ControllerEntity controller) { + protected void handleButtons(ControllerImpl controller) { if (ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).justPressed()) { if (screen.tabManager.getCurrentTab() instanceof YACLScreen.CategoryTab categoryTab) { ((YACLScreenCategoryTabAccessor) categoryTab).getSaveFinishedButton().onPress(null); @@ -32,7 +32,7 @@ protected void handleButtons(ControllerEntity controller) { } @Override - protected void onTabChanged(ControllerEntity controller) { + protected void onTabChanged(ControllerImpl controller) { if (screen.tabManager.getCurrentTab() instanceof YACLScreen.CategoryTab categoryTab) { WidgetAndType optionListHolder = ((YACLScreenCategoryTabAccessor) categoryTab).getOptionList(); OptionListWidget optionList = optionListHolder.getType(); diff --git a/src/main/java/dev/isxander/controlify/config/ConfigManager.java b/src/main/java/dev/isxander/controlify/impl/config/ConfigManager.java similarity index 96% rename from src/main/java/dev/isxander/controlify/config/ConfigManager.java rename to src/main/java/dev/isxander/controlify/impl/config/ConfigManager.java index f510bc58e..860a0abd3 100644 --- a/src/main/java/dev/isxander/controlify/config/ConfigManager.java +++ b/src/main/java/dev/isxander/controlify/impl/config/ConfigManager.java @@ -4,18 +4,18 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config; +package dev.isxander.controlify.impl.config; import com.google.gson.*; import com.mojang.logging.LogUtils; import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; -import dev.isxander.controlify.config.dto.SharedConfig; -import dev.isxander.controlify.config.dto.dfu.ControlifyDataFixer; -import dev.isxander.controlify.config.dto.profile.ProfileConfig; -import dev.isxander.controlify.config.settings.ControlifySettings; -import dev.isxander.controlify.config.settings.profile.ProfileSettings; -import dev.isxander.controlify.debug.DebugProperties; +import dev.isxander.controlify.impl.config.dto.SharedConfig; +import dev.isxander.controlify.impl.config.dto.dfu.ControlifyDataFixer; +import dev.isxander.controlify.impl.config.dto.profile.ProfileConfig; +import dev.isxander.controlify.impl.config.settings.ControlifySettings; +import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; +import dev.isxander.controlify.impl.debug.DebugProperties; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; diff --git a/src/main/java/dev/isxander/controlify/config/ConfigMigrator.java b/src/main/java/dev/isxander/controlify/impl/config/ConfigMigrator.java similarity index 92% rename from src/main/java/dev/isxander/controlify/config/ConfigMigrator.java rename to src/main/java/dev/isxander/controlify/impl/config/ConfigMigrator.java index f45313fee..3db68f6c1 100644 --- a/src/main/java/dev/isxander/controlify/config/ConfigMigrator.java +++ b/src/main/java/dev/isxander/controlify/impl/config/ConfigMigrator.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config; +package dev.isxander.controlify.impl.config; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -15,13 +15,13 @@ import com.mojang.serialization.DataResult; import com.mojang.serialization.Dynamic; import com.mojang.serialization.JsonOps; -import dev.isxander.controlify.config.dto.SharedConfig; -import dev.isxander.controlify.config.dto.dfu.ControlifyDataFixer; -import dev.isxander.controlify.config.dto.dfu.ControlifyTypeReferences; -import dev.isxander.controlify.config.dto.profile.ProfileConfig; -import dev.isxander.controlify.config.dto.profile.defaults.DefaultConfigManager; -import dev.isxander.controlify.config.settings.ControlifySettings; -import dev.isxander.controlify.config.settings.profile.ProfileSettings; +import dev.isxander.controlify.impl.config.dto.SharedConfig; +import dev.isxander.controlify.impl.config.dto.dfu.ControlifyDataFixer; +import dev.isxander.controlify.impl.config.dto.dfu.ControlifyTypeReferences; +import dev.isxander.controlify.impl.config.dto.profile.ProfileConfig; +import dev.isxander.controlify.impl.config.dto.profile.defaults.DefaultConfigManager; +import dev.isxander.controlify.impl.config.settings.ControlifySettings; +import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; import java.io.IOException; import java.util.ArrayList; diff --git a/src/main/java/dev/isxander/controlify/config/dto/GlobalConfig.java b/src/main/java/dev/isxander/controlify/impl/config/dto/GlobalConfig.java similarity index 94% rename from src/main/java/dev/isxander/controlify/config/dto/GlobalConfig.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/GlobalConfig.java index c4766d70c..5fece7c93 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/GlobalConfig.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/GlobalConfig.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto; +package dev.isxander.controlify.impl.config.dto; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.reacharound.ReachAroundMode; +import dev.isxander.controlify.impl.reacharound.ReachAroundMode; import java.util.List; diff --git a/src/main/java/dev/isxander/controlify/config/dto/SharedConfig.java b/src/main/java/dev/isxander/controlify/impl/config/dto/SharedConfig.java similarity index 85% rename from src/main/java/dev/isxander/controlify/config/dto/SharedConfig.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/SharedConfig.java index 563556398..9aaa985c3 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/SharedConfig.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/SharedConfig.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto; +package dev.isxander.controlify.impl.config.dto; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.config.dto.device.DeviceConfig; +import dev.isxander.controlify.impl.config.dto.device.DeviceConfig; import java.util.Map; diff --git a/src/main/java/dev/isxander/controlify/config/dto/device/DeviceConfig.java b/src/main/java/dev/isxander/controlify/impl/config/dto/device/DeviceConfig.java similarity index 79% rename from src/main/java/dev/isxander/controlify/config/dto/device/DeviceConfig.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/device/DeviceConfig.java index 56b92a71f..c74a64bf8 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/device/DeviceConfig.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/device/DeviceConfig.java @@ -4,12 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.device; +package dev.isxander.controlify.impl.config.dto.device; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.controller.id.ControllerType; -import dev.isxander.controlify.input.mapping.ControllerMapping; +import dev.isxander.controlify.impl.controllermanager.ControllerType; +import dev.isxander.controlify.impl.input.mapping.ControllerMapping; import net.minecraft.resources.Identifier; import java.util.Optional; @@ -24,7 +24,7 @@ public record DeviceConfig( public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( Codec.STRING.optionalFieldOf("name", "").forGetter(DeviceConfig::name), Codec.LONG.optionalFieldOf("last_seen", 0L).forGetter(DeviceConfig::lastSeen), - Identifier.CODEC.optionalFieldOf("controller_type", ControllerType.DEFAULT.namespace()).forGetter(DeviceConfig::controllerType), + Identifier.CODEC.optionalFieldOf("controller_type", ControllerType.DEFAULT.id()).forGetter(DeviceConfig::controllerType), GyroCalibrationConfig.CODEC.fieldOf("gyro_calibration").forGetter(DeviceConfig::gyroCalibration), ControllerMapping.CODEC.optionalFieldOf("mapping").forGetter(DeviceConfig::mapping) ).apply(instance, DeviceConfig::new)); diff --git a/src/main/java/dev/isxander/controlify/config/dto/device/GyroCalibrationConfig.java b/src/main/java/dev/isxander/controlify/impl/config/dto/device/GyroCalibrationConfig.java similarity index 60% rename from src/main/java/dev/isxander/controlify/config/dto/device/GyroCalibrationConfig.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/device/GyroCalibrationConfig.java index 409c71998..fdd059f18 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/device/GyroCalibrationConfig.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/device/GyroCalibrationConfig.java @@ -4,16 +4,18 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.device; +package dev.isxander.controlify.impl.config.dto.device; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.controller.gyro.GyroStateC; +import net.minecraft.util.ExtraCodecs; +import org.joml.Vector3f; +import org.joml.Vector3fc; public record GyroCalibrationConfig( - GyroStateC offset + Vector3fc offset ) { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - GyroStateC.CODEC_MUTABLE.optionalFieldOf("offset", GyroStateC.ZERO).forGetter(GyroCalibrationConfig::offset) + ExtraCodecs.VECTOR3F.optionalFieldOf("offset", new Vector3f()).forGetter(GyroCalibrationConfig::offset) ).apply(instance, GyroCalibrationConfig::new)); } diff --git a/src/main/java/dev/isxander/controlify/config/dto/dfu/ControlifyDataFixer.java b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/ControlifyDataFixer.java similarity index 88% rename from src/main/java/dev/isxander/controlify/config/dto/dfu/ControlifyDataFixer.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/dfu/ControlifyDataFixer.java index f10e97d35..42b750006 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/dfu/ControlifyDataFixer.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/ControlifyDataFixer.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.dfu; +package dev.isxander.controlify.impl.config.dto.dfu; import com.mojang.datafixers.DataFixer; import com.mojang.datafixers.DataFixerBuilder; -import dev.isxander.controlify.config.dto.dfu.fixes.*; -import dev.isxander.controlify.config.settings.GlobalSettings; -import dev.isxander.controlify.config.settings.profile.ProfileSettings; +import dev.isxander.controlify.impl.config.dto.dfu.fixes.*; +import dev.isxander.controlify.impl.config.settings.GlobalSettings; +import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; public final class ControlifyDataFixer { public static final int CURRENT_VERSION = 9; diff --git a/src/main/java/dev/isxander/controlify/config/dto/dfu/ControlifySchemas.java b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/ControlifySchemas.java similarity index 97% rename from src/main/java/dev/isxander/controlify/config/dto/dfu/ControlifySchemas.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/dfu/ControlifySchemas.java index 3c01b75f4..b9113575c 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/dfu/ControlifySchemas.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/ControlifySchemas.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.dfu; +package dev.isxander.controlify.impl.config.dto.dfu; import com.mojang.datafixers.DSL; import com.mojang.datafixers.schemas.Schema; diff --git a/src/main/java/dev/isxander/controlify/config/dto/dfu/ControlifyTypeReferences.java b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/ControlifyTypeReferences.java similarity index 93% rename from src/main/java/dev/isxander/controlify/config/dto/dfu/ControlifyTypeReferences.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/dfu/ControlifyTypeReferences.java index 5003c4a2d..69987e79a 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/dfu/ControlifyTypeReferences.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/ControlifyTypeReferences.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.dfu; +package dev.isxander.controlify.impl.config.dto.dfu; import com.mojang.datafixers.DSL; diff --git a/src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/AnalogueMovementWhitelistFix.java b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/AnalogueMovementWhitelistFix.java similarity index 89% rename from src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/AnalogueMovementWhitelistFix.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/AnalogueMovementWhitelistFix.java index dd786fae9..e658c2ae6 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/AnalogueMovementWhitelistFix.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/AnalogueMovementWhitelistFix.java @@ -4,14 +4,14 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.dfu.fixes; +package dev.isxander.controlify.impl.config.dto.dfu.fixes; import com.mojang.datafixers.DSL; import com.mojang.datafixers.DataFix; import com.mojang.datafixers.TypeRewriteRule; import com.mojang.datafixers.schemas.Schema; import com.mojang.serialization.Dynamic; -import dev.isxander.controlify.config.dto.dfu.ControlifyTypeReferences; +import dev.isxander.controlify.impl.config.dto.dfu.ControlifyTypeReferences; import java.util.stream.Stream; diff --git a/src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/DualsenseConfigFix.java b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/DualsenseConfigFix.java similarity index 85% rename from src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/DualsenseConfigFix.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/DualsenseConfigFix.java index 3f11e7d53..25ae2a3f0 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/DualsenseConfigFix.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/DualsenseConfigFix.java @@ -4,15 +4,15 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.dfu.fixes; +package dev.isxander.controlify.impl.config.dto.dfu.fixes; import com.mojang.datafixers.DSL; import com.mojang.datafixers.DataFix; import com.mojang.datafixers.TypeRewriteRule; import com.mojang.datafixers.schemas.Schema; import com.mojang.serialization.Dynamic; -import dev.isxander.controlify.config.dto.dfu.ControlifyTypeReferences; -import dev.isxander.controlify.config.settings.profile.ProfileSettings; +import dev.isxander.controlify.impl.config.dto.dfu.ControlifyTypeReferences; +import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; public final class DualsenseConfigFix extends DataFix { private final ProfileSettings profileDefaults; diff --git a/src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/GeneratedBindingIdsFix.java b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/GeneratedBindingIdsFix.java similarity index 94% rename from src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/GeneratedBindingIdsFix.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/GeneratedBindingIdsFix.java index a37582f36..077416a9e 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/GeneratedBindingIdsFix.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/GeneratedBindingIdsFix.java @@ -4,14 +4,14 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.dfu.fixes; +package dev.isxander.controlify.impl.config.dto.dfu.fixes; import com.mojang.datafixers.DSL; import com.mojang.datafixers.DataFix; import com.mojang.datafixers.TypeRewriteRule; import com.mojang.datafixers.schemas.Schema; import com.mojang.serialization.Dynamic; -import dev.isxander.controlify.config.dto.dfu.ControlifyTypeReferences; +import dev.isxander.controlify.impl.config.dto.dfu.ControlifyTypeReferences; import java.util.LinkedHashMap; import java.util.Map; diff --git a/src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/GuideGuiScaleFix.java b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/GuideGuiScaleFix.java similarity index 86% rename from src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/GuideGuiScaleFix.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/GuideGuiScaleFix.java index ebb376807..db939e853 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/GuideGuiScaleFix.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/GuideGuiScaleFix.java @@ -4,15 +4,15 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.dfu.fixes; +package dev.isxander.controlify.impl.config.dto.dfu.fixes; import com.mojang.datafixers.DSL; import com.mojang.datafixers.DataFix; import com.mojang.datafixers.TypeRewriteRule; import com.mojang.datafixers.schemas.Schema; import com.mojang.serialization.Dynamic; -import dev.isxander.controlify.config.dto.dfu.ControlifyTypeReferences; -import dev.isxander.controlify.config.settings.profile.ProfileSettings; +import dev.isxander.controlify.impl.config.dto.dfu.ControlifyTypeReferences; +import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; public class GuideGuiScaleFix extends DataFix { private final ProfileSettings profileDefaults; diff --git a/src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/GyroLowPassFix.java b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/GyroLowPassFix.java similarity index 82% rename from src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/GyroLowPassFix.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/GyroLowPassFix.java index 27c2fef8d..d2e5eeb07 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/GyroLowPassFix.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/GyroLowPassFix.java @@ -1,12 +1,12 @@ -package dev.isxander.controlify.config.dto.dfu.fixes; +package dev.isxander.controlify.impl.config.dto.dfu.fixes; import com.mojang.datafixers.DSL; import com.mojang.datafixers.DataFix; import com.mojang.datafixers.TypeRewriteRule; import com.mojang.datafixers.schemas.Schema; import com.mojang.serialization.Dynamic; -import dev.isxander.controlify.config.dto.dfu.ControlifyTypeReferences; -import dev.isxander.controlify.config.settings.profile.ProfileSettings; +import dev.isxander.controlify.impl.config.dto.dfu.ControlifyTypeReferences; +import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; public class GyroLowPassFix extends DataFix { private final ProfileSettings profileDefaults; diff --git a/src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/HorizontalLookInvertFix.java b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/HorizontalLookInvertFix.java similarity index 89% rename from src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/HorizontalLookInvertFix.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/HorizontalLookInvertFix.java index 3eb94df99..c1ce914db 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/HorizontalLookInvertFix.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/HorizontalLookInvertFix.java @@ -4,14 +4,14 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.dfu.fixes; +package dev.isxander.controlify.impl.config.dto.dfu.fixes; import com.mojang.datafixers.DSL; import com.mojang.datafixers.DataFix; import com.mojang.datafixers.TypeRewriteRule; import com.mojang.datafixers.schemas.Schema; import com.mojang.serialization.Dynamic; -import dev.isxander.controlify.config.dto.dfu.ControlifyTypeReferences; +import dev.isxander.controlify.impl.config.dto.dfu.ControlifyTypeReferences; public final class HorizontalLookInvertFix extends DataFix { public HorizontalLookInvertFix(Schema outputSchema) { diff --git a/src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/RadialMenuActionsFix.java b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/RadialMenuActionsFix.java similarity index 93% rename from src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/RadialMenuActionsFix.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/RadialMenuActionsFix.java index f9cd727ba..d4b0d71f5 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/RadialMenuActionsFix.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/RadialMenuActionsFix.java @@ -4,14 +4,14 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.dfu.fixes; +package dev.isxander.controlify.impl.config.dto.dfu.fixes; import com.mojang.datafixers.DSL; import com.mojang.datafixers.DataFix; import com.mojang.datafixers.TypeRewriteRule; import com.mojang.datafixers.schemas.Schema; import com.mojang.serialization.Dynamic; -import dev.isxander.controlify.config.dto.dfu.ControlifyTypeReferences; +import dev.isxander.controlify.impl.config.dto.dfu.ControlifyTypeReferences; import java.util.LinkedHashSet; import java.util.List; diff --git a/src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/TheHolyMigrationFix.java b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/TheHolyMigrationFix.java similarity index 97% rename from src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/TheHolyMigrationFix.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/TheHolyMigrationFix.java index 093f37b97..d0d7725b6 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/dfu/fixes/TheHolyMigrationFix.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/TheHolyMigrationFix.java @@ -4,16 +4,16 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.dfu.fixes; +package dev.isxander.controlify.impl.config.dto.dfu.fixes; import com.mojang.datafixers.DSL; import com.mojang.datafixers.DataFix; import com.mojang.datafixers.TypeRewriteRule; import com.mojang.datafixers.schemas.Schema; import com.mojang.serialization.Dynamic; -import dev.isxander.controlify.config.dto.dfu.ControlifyTypeReferences; -import dev.isxander.controlify.config.settings.GlobalSettings; -import dev.isxander.controlify.config.settings.profile.ProfileSettings; +import dev.isxander.controlify.impl.config.dto.dfu.ControlifyTypeReferences; +import dev.isxander.controlify.impl.config.settings.GlobalSettings; +import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; import java.util.Map; import java.util.Optional; diff --git a/src/main/java/dev/isxander/controlify/config/dto/profile/BluetoothDeviceConfig.java b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/BluetoothDeviceConfig.java similarity index 90% rename from src/main/java/dev/isxander/controlify/config/dto/profile/BluetoothDeviceConfig.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/profile/BluetoothDeviceConfig.java index 445b0d50c..bf838c791 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/profile/BluetoothDeviceConfig.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/BluetoothDeviceConfig.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.profile; +package dev.isxander.controlify.impl.config.dto.profile; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; diff --git a/src/main/java/dev/isxander/controlify/config/dto/profile/DualsenseConfig.java b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/DualsenseConfig.java similarity index 90% rename from src/main/java/dev/isxander/controlify/config/dto/profile/DualsenseConfig.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/profile/DualsenseConfig.java index 064842fd0..1e89db8f9 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/profile/DualsenseConfig.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/DualsenseConfig.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.profile; +package dev.isxander.controlify.impl.config.dto.profile; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; diff --git a/src/main/java/dev/isxander/controlify/config/dto/profile/GenericControllerConfig.java b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/GenericControllerConfig.java similarity index 97% rename from src/main/java/dev/isxander/controlify/config/dto/profile/GenericControllerConfig.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/profile/GenericControllerConfig.java index 7eccea863..c547a033c 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/profile/GenericControllerConfig.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/GenericControllerConfig.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.profile; +package dev.isxander.controlify.impl.config.dto.profile; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; diff --git a/src/main/java/dev/isxander/controlify/config/dto/profile/GyroConfig.java b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/GyroConfig.java similarity index 88% rename from src/main/java/dev/isxander/controlify/config/dto/profile/GyroConfig.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/profile/GyroConfig.java index 21682d7c8..774d43307 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/profile/GyroConfig.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/GyroConfig.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.profile; +package dev.isxander.controlify.impl.config.dto.profile; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.controller.gyro.GyroButtonMode; -import dev.isxander.controlify.controller.gyro.GyroYawMode; +import dev.isxander.controlify.impl.controller.gyro.GyroButtonMode; +import dev.isxander.controlify.impl.controller.gyro.GyroYawMode; public record GyroConfig( float lookSensitivity, diff --git a/src/main/java/dev/isxander/controlify/config/dto/profile/HDHapticConfig.java b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/HDHapticConfig.java similarity index 89% rename from src/main/java/dev/isxander/controlify/config/dto/profile/HDHapticConfig.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/profile/HDHapticConfig.java index 5c43a0b32..df7204edb 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/profile/HDHapticConfig.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/HDHapticConfig.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.profile; +package dev.isxander.controlify.impl.config.dto.profile; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; diff --git a/src/main/java/dev/isxander/controlify/config/dto/profile/InputConfig.java b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/InputConfig.java similarity index 95% rename from src/main/java/dev/isxander/controlify/config/dto/profile/InputConfig.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/profile/InputConfig.java index 9ec34705a..efd7809ba 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/profile/InputConfig.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/InputConfig.java @@ -4,12 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.profile; +package dev.isxander.controlify.impl.config.dto.profile; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.ingame.InputCurves; -import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.impl.ingame.InputCurves; +import dev.isxander.controlify.api.input.LogicalInput; import net.minecraft.resources.Identifier; import java.util.List; diff --git a/src/main/java/dev/isxander/controlify/config/dto/profile/ProfileConfig.java b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/ProfileConfig.java similarity index 96% rename from src/main/java/dev/isxander/controlify/config/dto/profile/ProfileConfig.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/profile/ProfileConfig.java index 1daf893f6..e376ce2b9 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/profile/ProfileConfig.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/ProfileConfig.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.profile; +package dev.isxander.controlify.impl.config.dto.profile; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; diff --git a/src/main/java/dev/isxander/controlify/config/dto/profile/RumbleConfig.java b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/RumbleConfig.java similarity index 92% rename from src/main/java/dev/isxander/controlify/config/dto/profile/RumbleConfig.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/profile/RumbleConfig.java index 0b1b86505..7b31787e1 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/profile/RumbleConfig.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/RumbleConfig.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.profile; +package dev.isxander.controlify.impl.config.dto.profile; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; diff --git a/src/main/java/dev/isxander/controlify/config/dto/profile/defaults/DefaultConfigManager.java b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/defaults/DefaultConfigManager.java similarity index 91% rename from src/main/java/dev/isxander/controlify/config/dto/profile/defaults/DefaultConfigManager.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/profile/defaults/DefaultConfigManager.java index 9dc1a1e4c..2d1d58ffd 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/profile/defaults/DefaultConfigManager.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/defaults/DefaultConfigManager.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.profile.defaults; +package dev.isxander.controlify.impl.config.dto.profile.defaults; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -12,9 +12,9 @@ import com.mojang.logging.LogUtils; import com.mojang.serialization.DataResult; import com.mojang.serialization.JsonOps; -import dev.isxander.controlify.config.dto.profile.ProfileConfig; -import dev.isxander.controlify.platform.client.resource.SimpleControlifyReloadListener; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.config.dto.profile.ProfileConfig; +import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.CrashReport; import net.minecraft.ReportedException; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/config/dto/profile/defaults/DefaultConfigProvider.java b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/defaults/DefaultConfigProvider.java similarity index 61% rename from src/main/java/dev/isxander/controlify/config/dto/profile/defaults/DefaultConfigProvider.java rename to src/main/java/dev/isxander/controlify/impl/config/dto/profile/defaults/DefaultConfigProvider.java index 924c14fcf..efef27e0e 100644 --- a/src/main/java/dev/isxander/controlify/config/dto/profile/defaults/DefaultConfigProvider.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/defaults/DefaultConfigProvider.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.dto.profile.defaults; +package dev.isxander.controlify.impl.config.dto.profile.defaults; -import dev.isxander.controlify.config.dto.profile.ProfileConfig; +import dev.isxander.controlify.impl.config.dto.profile.ProfileConfig; public interface DefaultConfigProvider { boolean isReady(); diff --git a/src/main/java/dev/isxander/controlify/config/settings/ControlifySettings.java b/src/main/java/dev/isxander/controlify/impl/config/settings/ControlifySettings.java similarity index 90% rename from src/main/java/dev/isxander/controlify/config/settings/ControlifySettings.java rename to src/main/java/dev/isxander/controlify/impl/config/settings/ControlifySettings.java index 612294d00..5b33fe0d9 100644 --- a/src/main/java/dev/isxander/controlify/config/settings/ControlifySettings.java +++ b/src/main/java/dev/isxander/controlify/impl/config/settings/ControlifySettings.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.settings; +package dev.isxander.controlify.impl.config.settings; -import dev.isxander.controlify.config.dto.SharedConfig; -import dev.isxander.controlify.config.settings.device.DeviceSettings; -import dev.isxander.controlify.config.settings.profile.ProfileSettings; +import dev.isxander.controlify.impl.config.dto.SharedConfig; +import dev.isxander.controlify.impl.config.settings.device.DeviceSettings; +import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap; import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap; import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMaps; diff --git a/src/main/java/dev/isxander/controlify/config/settings/GlobalSettings.java b/src/main/java/dev/isxander/controlify/impl/config/settings/GlobalSettings.java similarity index 95% rename from src/main/java/dev/isxander/controlify/config/settings/GlobalSettings.java rename to src/main/java/dev/isxander/controlify/impl/config/settings/GlobalSettings.java index 828c5abf9..c918fdb21 100644 --- a/src/main/java/dev/isxander/controlify/config/settings/GlobalSettings.java +++ b/src/main/java/dev/isxander/controlify/impl/config/settings/GlobalSettings.java @@ -4,12 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.settings; +package dev.isxander.controlify.impl.config.settings; import com.google.common.collect.Sets; -import dev.isxander.controlify.config.dto.GlobalConfig; -import dev.isxander.controlify.reacharound.ReachAroundMode; -import dev.isxander.controlify.server.ServerPolicies; +import dev.isxander.controlify.impl.config.dto.GlobalConfig; +import dev.isxander.controlify.impl.reacharound.ReachAroundMode; +import dev.isxander.controlify.impl.server.ServerPolicies; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.client.multiplayer.ServerData; diff --git a/src/main/java/dev/isxander/controlify/config/settings/device/DeviceSettings.java b/src/main/java/dev/isxander/controlify/impl/config/settings/device/DeviceSettings.java similarity index 82% rename from src/main/java/dev/isxander/controlify/config/settings/device/DeviceSettings.java rename to src/main/java/dev/isxander/controlify/impl/config/settings/device/DeviceSettings.java index 130f749d6..9f21b5b13 100644 --- a/src/main/java/dev/isxander/controlify/config/settings/device/DeviceSettings.java +++ b/src/main/java/dev/isxander/controlify/impl/config/settings/device/DeviceSettings.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.settings.device; +package dev.isxander.controlify.impl.config.settings.device; -import dev.isxander.controlify.config.dto.device.DeviceConfig; -import dev.isxander.controlify.controller.id.ControllerType; -import dev.isxander.controlify.input.mapping.ControllerMapping; +import dev.isxander.controlify.impl.config.dto.device.DeviceConfig; +import dev.isxander.controlify.impl.controllermanager.ControllerType; +import dev.isxander.controlify.impl.input.mapping.ControllerMapping; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; @@ -25,7 +25,7 @@ public class DeviceSettings { private DeviceSettings(String uid) { this.name = uid; this.lastSeen = 0L; - this.controllerType = ControllerType.DEFAULT.namespace(); + this.controllerType = ControllerType.DEFAULT.id(); this.gyroCalibration = GyroCalibrationSettings.defaults(); this.mapping = null; } diff --git a/src/main/java/dev/isxander/controlify/config/settings/device/GyroCalibrationSettings.java b/src/main/java/dev/isxander/controlify/impl/config/settings/device/GyroCalibrationSettings.java similarity index 64% rename from src/main/java/dev/isxander/controlify/config/settings/device/GyroCalibrationSettings.java rename to src/main/java/dev/isxander/controlify/impl/config/settings/device/GyroCalibrationSettings.java index 51f733d64..047c476e5 100644 --- a/src/main/java/dev/isxander/controlify/config/settings/device/GyroCalibrationSettings.java +++ b/src/main/java/dev/isxander/controlify/impl/config/settings/device/GyroCalibrationSettings.java @@ -4,21 +4,21 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.settings.device; +package dev.isxander.controlify.impl.config.settings.device; -import dev.isxander.controlify.config.dto.device.GyroCalibrationConfig; -import dev.isxander.controlify.controller.gyro.GyroState; -import dev.isxander.controlify.controller.gyro.GyroStateC; +import dev.isxander.controlify.impl.config.dto.device.GyroCalibrationConfig; +import org.joml.Vector3f; +import org.joml.Vector3fc; public class GyroCalibrationSettings { - public GyroStateC offset; + public Vector3fc offset; private GyroCalibrationSettings() { - this.offset = new GyroState(); + this.offset = new Vector3f(); } public GyroCalibrationSettings( - GyroStateC offset + Vector3fc offset ) { this.offset = offset; } diff --git a/src/main/java/dev/isxander/controlify/config/settings/profile/BluetoothDeviceSettings.java b/src/main/java/dev/isxander/controlify/impl/config/settings/profile/BluetoothDeviceSettings.java similarity index 79% rename from src/main/java/dev/isxander/controlify/config/settings/profile/BluetoothDeviceSettings.java rename to src/main/java/dev/isxander/controlify/impl/config/settings/profile/BluetoothDeviceSettings.java index b1ed3f634..4e3fb98db 100644 --- a/src/main/java/dev/isxander/controlify/config/settings/profile/BluetoothDeviceSettings.java +++ b/src/main/java/dev/isxander/controlify/impl/config/settings/profile/BluetoothDeviceSettings.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.settings.profile; +package dev.isxander.controlify.impl.config.settings.profile; -import dev.isxander.controlify.config.dto.profile.BluetoothDeviceConfig; +import dev.isxander.controlify.impl.config.dto.profile.BluetoothDeviceConfig; public class BluetoothDeviceSettings { public boolean dontShowWarning; diff --git a/src/main/java/dev/isxander/controlify/config/settings/profile/DualsenseSettings.java b/src/main/java/dev/isxander/controlify/impl/config/settings/profile/DualsenseSettings.java similarity index 78% rename from src/main/java/dev/isxander/controlify/config/settings/profile/DualsenseSettings.java rename to src/main/java/dev/isxander/controlify/impl/config/settings/profile/DualsenseSettings.java index a69a951ac..ab1b8e29b 100644 --- a/src/main/java/dev/isxander/controlify/config/settings/profile/DualsenseSettings.java +++ b/src/main/java/dev/isxander/controlify/impl/config/settings/profile/DualsenseSettings.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.settings.profile; +package dev.isxander.controlify.impl.config.settings.profile; -import dev.isxander.controlify.config.dto.profile.DualsenseConfig; +import dev.isxander.controlify.impl.config.dto.profile.DualsenseConfig; public class DualsenseSettings { public boolean triggerEffects; diff --git a/src/main/java/dev/isxander/controlify/config/settings/profile/GenericControllerSettings.java b/src/main/java/dev/isxander/controlify/impl/config/settings/profile/GenericControllerSettings.java similarity index 96% rename from src/main/java/dev/isxander/controlify/config/settings/profile/GenericControllerSettings.java rename to src/main/java/dev/isxander/controlify/impl/config/settings/profile/GenericControllerSettings.java index 278455d1a..ce5531b90 100644 --- a/src/main/java/dev/isxander/controlify/config/settings/profile/GenericControllerSettings.java +++ b/src/main/java/dev/isxander/controlify/impl/config/settings/profile/GenericControllerSettings.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.settings.profile; +package dev.isxander.controlify.impl.config.settings.profile; import dev.isxander.controlify.api.contextual.GuideVerbosity; -import dev.isxander.controlify.config.dto.profile.GenericControllerConfig; +import dev.isxander.controlify.impl.config.dto.profile.GenericControllerConfig; public class GenericControllerSettings { public boolean autoJump; diff --git a/src/main/java/dev/isxander/controlify/config/settings/profile/GyroSettings.java b/src/main/java/dev/isxander/controlify/impl/config/settings/profile/GyroSettings.java similarity index 83% rename from src/main/java/dev/isxander/controlify/config/settings/profile/GyroSettings.java rename to src/main/java/dev/isxander/controlify/impl/config/settings/profile/GyroSettings.java index 1a3b669c1..c52e6c33e 100644 --- a/src/main/java/dev/isxander/controlify/config/settings/profile/GyroSettings.java +++ b/src/main/java/dev/isxander/controlify/impl/config/settings/profile/GyroSettings.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.settings.profile; +package dev.isxander.controlify.impl.config.settings.profile; -import dev.isxander.controlify.config.dto.profile.GyroConfig; -import dev.isxander.controlify.controller.gyro.GyroButtonMode; -import dev.isxander.controlify.controller.gyro.GyroYawMode; +import dev.isxander.controlify.impl.config.dto.profile.GyroConfig; +import dev.isxander.controlify.impl.controller.gyro.GyroButtonMode; +import dev.isxander.controlify.impl.controller.gyro.GyroYawMode; public class GyroSettings { public float lookSensitivity; diff --git a/src/main/java/dev/isxander/controlify/config/settings/profile/HDHapticSettings.java b/src/main/java/dev/isxander/controlify/impl/config/settings/profile/HDHapticSettings.java similarity index 76% rename from src/main/java/dev/isxander/controlify/config/settings/profile/HDHapticSettings.java rename to src/main/java/dev/isxander/controlify/impl/config/settings/profile/HDHapticSettings.java index 8083e9f6d..1d4efd1dc 100644 --- a/src/main/java/dev/isxander/controlify/config/settings/profile/HDHapticSettings.java +++ b/src/main/java/dev/isxander/controlify/impl/config/settings/profile/HDHapticSettings.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.settings.profile; +package dev.isxander.controlify.impl.config.settings.profile; -import dev.isxander.controlify.config.dto.profile.HDHapticConfig; +import dev.isxander.controlify.impl.config.dto.profile.HDHapticConfig; public class HDHapticSettings { public boolean enabled; diff --git a/src/main/java/dev/isxander/controlify/config/settings/profile/InputSettings.java b/src/main/java/dev/isxander/controlify/impl/config/settings/profile/InputSettings.java similarity index 95% rename from src/main/java/dev/isxander/controlify/config/settings/profile/InputSettings.java rename to src/main/java/dev/isxander/controlify/impl/config/settings/profile/InputSettings.java index 359ada37d..97d4d7ef4 100644 --- a/src/main/java/dev/isxander/controlify/config/settings/profile/InputSettings.java +++ b/src/main/java/dev/isxander/controlify/impl/config/settings/profile/InputSettings.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.settings.profile; +package dev.isxander.controlify.impl.config.settings.profile; -import dev.isxander.controlify.config.dto.profile.InputConfig; -import dev.isxander.controlify.ingame.InputCurves; -import dev.isxander.controlify.input.LogicalInput; +import dev.isxander.controlify.impl.config.dto.profile.InputConfig; +import dev.isxander.controlify.impl.ingame.InputCurves; +import dev.isxander.controlify.api.input.LogicalInput; import net.minecraft.resources.Identifier; import java.util.HashMap; diff --git a/src/main/java/dev/isxander/controlify/config/settings/profile/ProfileSettings.java b/src/main/java/dev/isxander/controlify/impl/config/settings/profile/ProfileSettings.java similarity index 92% rename from src/main/java/dev/isxander/controlify/config/settings/profile/ProfileSettings.java rename to src/main/java/dev/isxander/controlify/impl/config/settings/profile/ProfileSettings.java index f4b624f37..6cdc70b68 100644 --- a/src/main/java/dev/isxander/controlify/config/settings/profile/ProfileSettings.java +++ b/src/main/java/dev/isxander/controlify/impl/config/settings/profile/ProfileSettings.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.settings.profile; +package dev.isxander.controlify.impl.config.settings.profile; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.config.dto.profile.ProfileConfig; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.config.dto.profile.ProfileConfig; import org.jetbrains.annotations.Nullable; import java.util.Optional; diff --git a/src/main/java/dev/isxander/controlify/config/settings/profile/RumbleSettings.java b/src/main/java/dev/isxander/controlify/impl/config/settings/profile/RumbleSettings.java similarity index 83% rename from src/main/java/dev/isxander/controlify/config/settings/profile/RumbleSettings.java rename to src/main/java/dev/isxander/controlify/impl/config/settings/profile/RumbleSettings.java index ec8403ba1..b2581d0b5 100644 --- a/src/main/java/dev/isxander/controlify/config/settings/profile/RumbleSettings.java +++ b/src/main/java/dev/isxander/controlify/impl/config/settings/profile/RumbleSettings.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config.settings.profile; +package dev.isxander.controlify.impl.config.settings.profile; -import dev.isxander.controlify.config.dto.profile.RumbleConfig; -import dev.isxander.controlify.rumble.RumbleSource; -import dev.isxander.controlify.rumble.RumbleState; +import dev.isxander.controlify.impl.config.dto.profile.RumbleConfig; +import dev.isxander.controlify.impl.controller.rumble.RumbleSource; +import dev.isxander.controlify.api.controller.rumble.RumbleState; import net.minecraft.resources.Identifier; import java.util.HashMap; diff --git a/src/main/java/dev/isxander/controlify/contextual/ContextualDebug.java b/src/main/java/dev/isxander/controlify/impl/contextual/ContextualDebug.java similarity index 97% rename from src/main/java/dev/isxander/controlify/contextual/ContextualDebug.java rename to src/main/java/dev/isxander/controlify/impl/contextual/ContextualDebug.java index 8f80d80c0..39ee72840 100644 --- a/src/main/java/dev/isxander/controlify/contextual/ContextualDebug.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/ContextualDebug.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.contextual; +package dev.isxander.controlify.impl.contextual; -import dev.isxander.controlify.debug.DebugProperties; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.debug.DebugProperties; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; import net.minecraft.world.entity.Entity; import net.minecraft.world.item.ItemInstance; diff --git a/src/main/java/dev/isxander/controlify/contextual/ContextualDomainImpl.java b/src/main/java/dev/isxander/controlify/impl/contextual/ContextualDomainImpl.java similarity index 93% rename from src/main/java/dev/isxander/controlify/contextual/ContextualDomainImpl.java rename to src/main/java/dev/isxander/controlify/impl/contextual/ContextualDomainImpl.java index 045575ca7..33b76a332 100644 --- a/src/main/java/dev/isxander/controlify/contextual/ContextualDomainImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/ContextualDomainImpl.java @@ -4,17 +4,17 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.contextual; +package dev.isxander.controlify.impl.contextual; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.mojang.serialization.DataResult; import com.mojang.serialization.JsonOps; import dev.isxander.controlify.api.contextual.*; -import dev.isxander.controlify.controller.dualsense.TriggerEffectInstanceImpl; -import dev.isxander.controlify.gui.guide.GuideInstanceImpl; -import dev.isxander.controlify.platform.client.resource.SimpleControlifyReloadListener; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.controller.dualsense.TriggerEffectInstanceImpl; +import dev.isxander.controlify.impl.gui.guide.GuideInstanceImpl; +import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.multiplayer.CacheSlot; @@ -29,6 +29,7 @@ import net.minecraft.util.PlaceholderLookupProvider; import net.minecraft.util.RegistryContextSwapper; import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.NonNull; import java.io.BufferedReader; import java.util.*; @@ -56,14 +57,14 @@ public Identifier id() { } @Override - public void registerContributor(ContextualStateContributor contributor) { + public void registerContributor(@NonNull ContextualStateContributor contributor) { Objects.requireNonNull(contributor, "contributor"); this.contributor = this.contributor.andThen(contributor); } @Override - public GuideInstance createGuideInstance(Font font) { + public GuideInstance createGuideInstance(@NonNull Font font) { RuleSetManager ruleSetManager = RuleSetManager.GUIDE_RULES; return new GuideInstanceImpl<>(this, () -> this.requireRuleEngine(ruleSetManager, "guide"), font); diff --git a/src/main/java/dev/isxander/controlify/contextual/ContextualDomains.java b/src/main/java/dev/isxander/controlify/impl/contextual/ContextualDomains.java similarity index 90% rename from src/main/java/dev/isxander/controlify/contextual/ContextualDomains.java rename to src/main/java/dev/isxander/controlify/impl/contextual/ContextualDomains.java index f69f272b0..20fa7db36 100644 --- a/src/main/java/dev/isxander/controlify/contextual/ContextualDomains.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/ContextualDomains.java @@ -4,15 +4,15 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.contextual; +package dev.isxander.controlify.impl.contextual; import dev.isxander.controlify.api.contextual.ContainerContext; import dev.isxander.controlify.api.contextual.InGameContext; import dev.isxander.controlify.api.contextual.Context; import dev.isxander.controlify.api.contextual.ContextualDomain; import dev.isxander.controlify.api.contextual.ContextualDomainRegistry; -import dev.isxander.controlify.platform.client.PlatformClientUtil; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; import java.util.ArrayList; diff --git a/src/main/java/dev/isxander/controlify/contextual/ContextualPredicate.java b/src/main/java/dev/isxander/controlify/impl/contextual/ContextualPredicate.java similarity index 95% rename from src/main/java/dev/isxander/controlify/contextual/ContextualPredicate.java rename to src/main/java/dev/isxander/controlify/impl/contextual/ContextualPredicate.java index b1d674da5..2dbff9805 100644 --- a/src/main/java/dev/isxander/controlify/contextual/ContextualPredicate.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/ContextualPredicate.java @@ -4,14 +4,14 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.contextual; +package dev.isxander.controlify.impl.contextual; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.utils.codec.CExtraCodecs; -import dev.isxander.controlify.utils.predicates.ClientEntityPredicate; -import dev.isxander.controlify.utils.predicates.ClientPredicateUtils; +import dev.isxander.controlify.impl.utils.codec.CExtraCodecs; +import dev.isxander.controlify.impl.utils.predicates.ClientEntityPredicate; +import dev.isxander.controlify.impl.utils.predicates.ClientPredicateUtils; //? if >=26.2 { import net.minecraft.advancements.predicates.*; //?} else { diff --git a/src/main/java/dev/isxander/controlify/contextual/ContextualState.java b/src/main/java/dev/isxander/controlify/impl/contextual/ContextualState.java similarity index 92% rename from src/main/java/dev/isxander/controlify/contextual/ContextualState.java rename to src/main/java/dev/isxander/controlify/impl/contextual/ContextualState.java index 5e36298c8..7625e9619 100644 --- a/src/main/java/dev/isxander/controlify/contextual/ContextualState.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/ContextualState.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.contextual; +package dev.isxander.controlify.impl.contextual; import net.minecraft.resources.Identifier; import net.minecraft.world.entity.Entity; diff --git a/src/main/java/dev/isxander/controlify/contextual/ContextualStateAccumulator.java b/src/main/java/dev/isxander/controlify/impl/contextual/ContextualStateAccumulator.java similarity index 83% rename from src/main/java/dev/isxander/controlify/contextual/ContextualStateAccumulator.java rename to src/main/java/dev/isxander/controlify/impl/contextual/ContextualStateAccumulator.java index a43ea2778..09cafe5fe 100644 --- a/src/main/java/dev/isxander/controlify/contextual/ContextualStateAccumulator.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/ContextualStateAccumulator.java @@ -4,15 +4,15 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.contextual; +package dev.isxander.controlify.impl.contextual; import dev.isxander.controlify.api.contextual.ContextualStateSink; import net.minecraft.resources.Identifier; import net.minecraft.world.entity.Entity; import net.minecraft.world.item.ItemInstance; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.state.pattern.BlockInWorld; import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.NonNull; import java.util.Collections; import java.util.LinkedHashMap; @@ -36,12 +36,12 @@ public ContextualStateAccumulator(ContextualState state) { } @Override - public void contributeFact(Identifier id, boolean value) { + public void contributeFact(@NonNull Identifier id, boolean value) { this.facts.put(Objects.requireNonNull(id, "id"), value); } @Override - public void contributeItem(Identifier slot, @Nullable ItemInstance item) { + public void contributeItem(@NonNull Identifier slot, @Nullable ItemInstance item) { if (item == null) { return; } @@ -49,7 +49,7 @@ public void contributeItem(Identifier slot, @Nullable ItemInstance item) { } @Override - public void contributeBlock(Identifier slot, @Nullable BlockInWorld block) { + public void contributeBlock(@NonNull Identifier slot, @Nullable BlockInWorld block) { if (block == null) { return; } @@ -57,7 +57,7 @@ public void contributeBlock(Identifier slot, @Nullable BlockInWorld block) { } @Override - public void contributeEntity(Identifier slot, @Nullable Entity entity) { + public void contributeEntity(@NonNull Identifier slot, @Nullable Entity entity) { if (entity == null) { return; } diff --git a/src/main/java/dev/isxander/controlify/contextual/ContextualStateContributors.java b/src/main/java/dev/isxander/controlify/impl/contextual/ContextualStateContributors.java similarity index 94% rename from src/main/java/dev/isxander/controlify/contextual/ContextualStateContributors.java rename to src/main/java/dev/isxander/controlify/impl/contextual/ContextualStateContributors.java index f2a0ad0a4..a81430411 100644 --- a/src/main/java/dev/isxander/controlify/contextual/ContextualStateContributors.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/ContextualStateContributors.java @@ -4,11 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.contextual; +package dev.isxander.controlify.impl.contextual; import dev.isxander.controlify.api.contextual.*; +import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.mixins.feature.guide.ingame.PlayerAccessor; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.BlockItem; @@ -103,11 +104,11 @@ public final class ContextualStateContributors { sink.contributeFact( CUtil.rl("is_toggle_sneak"), - context.controller().settings().generic.toggleSneak + ((ControllerImpl) context.controller()).settings().generic.toggleSneak ); sink.contributeFact( CUtil.rl("is_toggle_sprint"), - context.controller().settings().generic.toggleSprint + ((ControllerImpl) context.controller()).settings().generic.toggleSprint ); sink.contributeFact( CUtil.rl("input_moving"), diff --git a/src/main/java/dev/isxander/controlify/contextual/CyclicFactDependencyException.java b/src/main/java/dev/isxander/controlify/impl/contextual/CyclicFactDependencyException.java similarity index 93% rename from src/main/java/dev/isxander/controlify/contextual/CyclicFactDependencyException.java rename to src/main/java/dev/isxander/controlify/impl/contextual/CyclicFactDependencyException.java index 7fa8df3f1..458b6adf2 100644 --- a/src/main/java/dev/isxander/controlify/contextual/CyclicFactDependencyException.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/CyclicFactDependencyException.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.contextual; +package dev.isxander.controlify.impl.contextual; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/contextual/FactDefinition.java b/src/main/java/dev/isxander/controlify/impl/contextual/FactDefinition.java similarity index 95% rename from src/main/java/dev/isxander/controlify/contextual/FactDefinition.java rename to src/main/java/dev/isxander/controlify/impl/contextual/FactDefinition.java index 32b4782ca..49cf251a6 100644 --- a/src/main/java/dev/isxander/controlify/contextual/FactDefinition.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/FactDefinition.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.contextual; +package dev.isxander.controlify.impl.contextual; import com.mojang.serialization.Codec; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/contextual/FactDependencyGraph.java b/src/main/java/dev/isxander/controlify/impl/contextual/FactDependencyGraph.java similarity index 99% rename from src/main/java/dev/isxander/controlify/contextual/FactDependencyGraph.java rename to src/main/java/dev/isxander/controlify/impl/contextual/FactDependencyGraph.java index bdfdae8c7..67ed4ffcd 100644 --- a/src/main/java/dev/isxander/controlify/contextual/FactDependencyGraph.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/FactDependencyGraph.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.contextual; +package dev.isxander.controlify.impl.contextual; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/api/contextual/GuideLocation.java b/src/main/java/dev/isxander/controlify/impl/contextual/GuideLocation.java similarity index 93% rename from src/main/java/dev/isxander/controlify/api/contextual/GuideLocation.java rename to src/main/java/dev/isxander/controlify/impl/contextual/GuideLocation.java index 2a5d38d15..f6d8ba2d0 100644 --- a/src/main/java/dev/isxander/controlify/api/contextual/GuideLocation.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/GuideLocation.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.api.contextual; +package dev.isxander.controlify.impl.contextual; import com.mojang.serialization.Codec; import net.minecraft.util.StringRepresentable; diff --git a/src/main/java/dev/isxander/controlify/contextual/GuideRule.java b/src/main/java/dev/isxander/controlify/impl/contextual/GuideRule.java similarity index 87% rename from src/main/java/dev/isxander/controlify/contextual/GuideRule.java rename to src/main/java/dev/isxander/controlify/impl/contextual/GuideRule.java index 8da435544..dc7927175 100644 --- a/src/main/java/dev/isxander/controlify/contextual/GuideRule.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/GuideRule.java @@ -4,12 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.contextual; +package dev.isxander.controlify.impl.contextual; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.api.bind.InputBindingSupplier; -import dev.isxander.controlify.api.contextual.GuideLocation; +import dev.isxander.controlify.impl.api.bind.InputBindingSupplier; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/contextual/Rule.java b/src/main/java/dev/isxander/controlify/impl/contextual/Rule.java similarity index 91% rename from src/main/java/dev/isxander/controlify/contextual/Rule.java rename to src/main/java/dev/isxander/controlify/impl/contextual/Rule.java index 9c092d6cc..ecd785118 100644 --- a/src/main/java/dev/isxander/controlify/contextual/Rule.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/Rule.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.contextual; +package dev.isxander.controlify.impl.contextual; public interface Rule { /// Used to identify when multiple rules are overriding each other. diff --git a/src/main/java/dev/isxander/controlify/contextual/RuleEngine.java b/src/main/java/dev/isxander/controlify/impl/contextual/RuleEngine.java similarity index 98% rename from src/main/java/dev/isxander/controlify/contextual/RuleEngine.java rename to src/main/java/dev/isxander/controlify/impl/contextual/RuleEngine.java index 3c7fcfc26..9609a53d5 100644 --- a/src/main/java/dev/isxander/controlify/contextual/RuleEngine.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/RuleEngine.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.contextual; +package dev.isxander.controlify.impl.contextual; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/contextual/RuleSet.java b/src/main/java/dev/isxander/controlify/impl/contextual/RuleSet.java similarity index 93% rename from src/main/java/dev/isxander/controlify/contextual/RuleSet.java rename to src/main/java/dev/isxander/controlify/impl/contextual/RuleSet.java index 0f957ba16..8ac7b2825 100644 --- a/src/main/java/dev/isxander/controlify/contextual/RuleSet.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/RuleSet.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.contextual; +package dev.isxander.controlify.impl.contextual; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; diff --git a/src/main/java/dev/isxander/controlify/contextual/RuleSetManager.java b/src/main/java/dev/isxander/controlify/impl/contextual/RuleSetManager.java similarity index 96% rename from src/main/java/dev/isxander/controlify/contextual/RuleSetManager.java rename to src/main/java/dev/isxander/controlify/impl/contextual/RuleSetManager.java index 238adf9db..a15532b2c 100644 --- a/src/main/java/dev/isxander/controlify/contextual/RuleSetManager.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/RuleSetManager.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.contextual; +package dev.isxander.controlify.impl.contextual; import com.google.gson.JsonElement; import com.google.gson.JsonParser; @@ -12,8 +12,8 @@ import com.mojang.serialization.DataResult; import com.mojang.serialization.DynamicOps; import com.mojang.serialization.JsonOps; -import dev.isxander.controlify.platform.client.resource.SimpleControlifyReloadListener; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.client.multiplayer.CacheSlot; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientRegistryLayer; @@ -37,7 +37,7 @@ public class RuleSetManager> implements SimpleControlifyRel public static final RuleSetManager TRIGGER_EFFECT_RULES = new RuleSetManager<>(TriggerEffectRule.CODEC, "trigger_effect"); - private static final String DIRECTORY = "contextual/"; + private static final String DIRECTORY = "controller/contextual/"; private final Codec> ruleSetCodec; private final Codec ruleCodec; diff --git a/src/main/java/dev/isxander/controlify/contextual/TriggerEffectRule.java b/src/main/java/dev/isxander/controlify/impl/contextual/TriggerEffectRule.java similarity index 79% rename from src/main/java/dev/isxander/controlify/contextual/TriggerEffectRule.java rename to src/main/java/dev/isxander/controlify/impl/contextual/TriggerEffectRule.java index 370401396..9fd21f190 100644 --- a/src/main/java/dev/isxander/controlify/contextual/TriggerEffectRule.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/TriggerEffectRule.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.contextual; +package dev.isxander.controlify.impl.contextual; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.api.bind.InputBindingSupplier; -import dev.isxander.controlify.controller.dualsense.TriggerEffectCodecs; -import dev.isxander.controlify.driver.dualsense.DualsenseTriggerEffect; +import dev.isxander.controlify.impl.api.bind.InputBindingSupplier; +import dev.isxander.controlify.impl.controller.dualsense.TriggerEffectCodecs; +import dev.isxander.controlify.impl.driver.dualsense.DualsenseTriggerEffect; import net.minecraft.resources.Identifier; /// @param binding the binding supplier associated with this action. diff --git a/src/main/java/dev/isxander/controlify/impl/controller/ControllerComponent.java b/src/main/java/dev/isxander/controlify/impl/controller/ControllerComponent.java new file mode 100644 index 000000000..be775d40b --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/controller/ControllerComponent.java @@ -0,0 +1,27 @@ +package dev.isxander.controlify.impl.controller; + +import net.minecraft.resources.Identifier; + +public abstract class ControllerComponent { + private final Identifier id; + private ControllerImpl controller; + + public ControllerComponent(Identifier id) { + this.id = id; + } + + public final Identifier id() { + return this.id; + } + + public void attach(ControllerImpl controller) { + this.controller = controller; + } + + public void close() { + } + + protected final ControllerImpl controller() { + return this.controller; + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/controller/ControllerImpl.java b/src/main/java/dev/isxander/controlify/impl/controller/ControllerImpl.java new file mode 100644 index 000000000..a4a82df12 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/controller/ControllerImpl.java @@ -0,0 +1,194 @@ +package dev.isxander.controlify.impl.controller; + +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; +import dev.isxander.controlify.impl.controller.battery.BatteryLevelComponent; +import dev.isxander.controlify.impl.controller.dualsense.DualsenseComponent; +import dev.isxander.controlify.impl.controller.haptic.HDHapticComponent; +import dev.isxander.controlify.impl.controller.misc.BluetoothDeviceComponent; +import dev.isxander.controlify.impl.controller.misc.DriverNameComponent; +import dev.isxander.controlify.impl.controller.misc.UIDComponent; +import dev.isxander.controlify.impl.controller.gyro.GyroComponent; +import dev.isxander.controlify.impl.controller.input.InputComponent; +import dev.isxander.controlify.impl.controller.led.LEDComponent; +import dev.isxander.controlify.impl.controller.rumble.RumbleComponentImpl; +import dev.isxander.controlify.impl.controller.rumble.TriggerRumbleComponent; +import dev.isxander.controlify.impl.controller.steamdeck.SteamDeckComponent; +import dev.isxander.controlify.impl.controller.touchpad.TouchpadComponent; +import dev.isxander.controlify.impl.driver.Driver; +import dev.isxander.controlify.impl.utils.log.ControlifyLogger; +import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +public final class ControllerImpl implements Controller { + private final ControllerInfo info; + private final Driver driver; + private final ControlifyLogger logger; + + private final Map components; + + private ProfileSettings settings; + private final ProfileSettings defaultSettings; + + private boolean active; + + public ControllerImpl( + ControllerInfo info, + Driver driver, + ControlifyLogger logger, + ProfileSettings settings, + ProfileSettings defaultSettings + ) { + this.info = info; + this.driver = driver; + this.logger = logger; + this.components = new HashMap<>(); + this.settings = settings; + this.defaultSettings = defaultSettings; + this.active = false; + + this.driver.addComponents(this); + this.getAllComponents().values().forEach(c -> c.attach(this)); + + logger.debugLog( + "Components: {}", + this.getAllComponents() + .keySet() + .stream() + .map(Identifier::toString) + .collect(Collectors.joining(", ")) + ); + } + + @Override + public @NonNull String uid() { + return this.getComponent(UIDComponent.ID).orElseThrow().value(); + } + + @Override + public @NonNull String driverReportedName() { + return this.getComponent(DriverNameComponent.ID).orElseThrow().value(); + } + + @Override + public @NonNull String name() { + return Optional.ofNullable(this.info.type().friendlyName()) + .orElse(this.driverReportedName()); + } + + @Override + public @NonNull Identifier typeId() { + return this.info.type().id(); + } + + public @NonNull ControllerInfo info() { + return this.info; + } + + public @NonNull Driver driver() { + return this.driver; + } + + public @NonNull ProfileSettings settings() { + return this.settings; + } + + public void setSettings(@NonNull ProfileSettings settings) { + this.settings = settings; + } + + public @NonNull ProfileSettings defaultSettings() { + return this.defaultSettings; + } + + public @NonNull InputComponent input() { + // assert that all controllers must have InputComponent + return this.getComponent(InputComponent.ID).orElseThrow(); + } + + @Override + public @NonNull Optional rumble() { + return this.getComponent(RumbleComponentImpl.ID); + } + + public @NonNull Optional triggerRumble() { + return this.getComponent(TriggerRumbleComponent.ID); + } + + public @NonNull Optional gyro() { + return this.getComponent(GyroComponent.ID); + } + + public @NonNull Optional led() { + return this.getComponent(LEDComponent.ID); + } + + public @NonNull Optional touchpad() { + return this.getComponent(TouchpadComponent.ID); + } + + public @NonNull Optional batteryLevel() { + return this.getComponent(BatteryLevelComponent.ID); + } + + public @NonNull Optional dualsense() { + return this.getComponent(DualsenseComponent.ID); + } + + public @NonNull Optional hdHaptic() { + return this.getComponent(HDHapticComponent.ID); + } + + public @NonNull Optional bluetoothDevice() { + return this.getComponent(BluetoothDeviceComponent.ID); + } + + public @NonNull Optional steamDeck() { + return this.getComponent(SteamDeckComponent.ID); + } + + public void update() { + this.driver.update(this); + } + + public boolean isActive() { + return this.active; + } + + public void setActive(boolean active) { + if (this.active == active) { + return; + } + this.active = active; + this.driver.setActive(active); + } + + public void close() { + this.setActive(false); + this.getAllComponents().values().forEach(ControllerComponent::close); + this.driver.close(); + } + + public ControlifyLogger logger() { + return this.logger; + } + + @SuppressWarnings("unchecked") + private Optional getComponent(Identifier id) { + return Optional.ofNullable((T) this.components.get(id)); + } + + public boolean addComponent(ControllerComponent component) { + return this.components.put(component.id(), component) != null; + } + + public Map getAllComponents() { + return Collections.unmodifiableMap(this.components); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/controller/ControllerInfo.java b/src/main/java/dev/isxander/controlify/impl/controller/ControllerInfo.java new file mode 100644 index 000000000..09c65f859 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/controller/ControllerInfo.java @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.controller; + +import dev.isxander.controlify.impl.controllermanager.ControllerType; +import dev.isxander.controlify.impl.controllermanager.UniqueControllerID; +import dev.isxander.controlify.impl.hid.HIDID; + +public record ControllerInfo(UniqueControllerID ucid, ControllerType type, HIDID hid) { +} diff --git a/src/main/java/dev/isxander/controlify/impl/controller/SingleValueControllerComponent.java b/src/main/java/dev/isxander/controlify/impl/controller/SingleValueControllerComponent.java new file mode 100644 index 000000000..0975e1d85 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/controller/SingleValueControllerComponent.java @@ -0,0 +1,16 @@ +package dev.isxander.controlify.impl.controller; + +import net.minecraft.resources.Identifier; + +public class SingleValueControllerComponent extends ControllerComponent { + private final T value; + + public SingleValueControllerComponent(Identifier id, T value) { + super(id); + this.value = value; + } + + public T value() { + return this.value; + } +} diff --git a/src/main/java/dev/isxander/controlify/controller/battery/BatteryLevelComponent.java b/src/main/java/dev/isxander/controlify/impl/controller/battery/BatteryLevelComponent.java similarity index 62% rename from src/main/java/dev/isxander/controlify/controller/battery/BatteryLevelComponent.java rename to src/main/java/dev/isxander/controlify/impl/controller/battery/BatteryLevelComponent.java index cc8e748ca..3271ef9a2 100644 --- a/src/main/java/dev/isxander/controlify/controller/battery/BatteryLevelComponent.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/battery/BatteryLevelComponent.java @@ -4,17 +4,21 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.battery; +package dev.isxander.controlify.impl.controller.battery; -import dev.isxander.controlify.controller.impl.ECSComponentImpl; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.controller.ControllerComponent; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; -public class BatteryLevelComponent extends ECSComponentImpl { +public class BatteryLevelComponent extends ControllerComponent { public static final Identifier ID = CUtil.rl("battery_level"); private PowerState batteryLevel = new PowerState.Unknown(); + public BatteryLevelComponent() { + super(ID); + } + public PowerState getBatteryLevel() { return this.batteryLevel; } @@ -22,9 +26,4 @@ public PowerState getBatteryLevel() { public void setBatteryLevel(PowerState batteryLevel) { this.batteryLevel = batteryLevel; } - - @Override - public Identifier id() { - return ID; - } } diff --git a/src/main/java/dev/isxander/controlify/controller/battery/PowerState.java b/src/main/java/dev/isxander/controlify/impl/controller/battery/PowerState.java similarity index 89% rename from src/main/java/dev/isxander/controlify/controller/battery/PowerState.java rename to src/main/java/dev/isxander/controlify/impl/controller/battery/PowerState.java index 82ce9a6ed..26e07e07c 100644 --- a/src/main/java/dev/isxander/controlify/controller/battery/PowerState.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/battery/PowerState.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.battery; +package dev.isxander.controlify.impl.controller.battery; public sealed interface PowerState { default int percent() { diff --git a/src/main/java/dev/isxander/controlify/controller/dualsense/DualsenseComponent.java b/src/main/java/dev/isxander/controlify/impl/controller/dualsense/DualsenseComponent.java similarity index 77% rename from src/main/java/dev/isxander/controlify/controller/dualsense/DualsenseComponent.java rename to src/main/java/dev/isxander/controlify/impl/controller/dualsense/DualsenseComponent.java index 7f8741f2f..70f8719c2 100644 --- a/src/main/java/dev/isxander/controlify/controller/dualsense/DualsenseComponent.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/dualsense/DualsenseComponent.java @@ -4,17 +4,17 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.dualsense; +package dev.isxander.controlify.impl.controller.dualsense; -import dev.isxander.controlify.config.settings.profile.DualsenseSettings; -import dev.isxander.controlify.controller.impl.ECSComponentImpl; -import dev.isxander.controlify.driver.dualsense.DualsenseTriggerEffect; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.config.settings.profile.DualsenseSettings; +import dev.isxander.controlify.impl.controller.ControllerComponent; +import dev.isxander.controlify.impl.driver.dualsense.DualsenseTriggerEffect; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; import java.util.Objects; -public class DualsenseComponent extends ECSComponentImpl { +public class DualsenseComponent extends ControllerComponent { public static final Identifier ID = CUtil.rl("dualsense"); private boolean muteLight; @@ -24,6 +24,10 @@ public class DualsenseComponent extends ECSComponentImpl { private boolean dirty; + public DualsenseComponent() { + super(ID); + } + public void setLeftTriggerEffect(DualsenseTriggerEffect effect) { if (!Objects.equals(effect, this.leftTriggerEffect)) { this.setDirty(); @@ -74,9 +78,4 @@ public DualsenseSettings settings() { public DualsenseSettings defaultSettings() { return this.controller().defaultSettings().dualsense; } - - @Override - public Identifier id() { - return ID; - } } diff --git a/src/main/java/dev/isxander/controlify/controller/dualsense/TriggerEffectCodecs.java b/src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectCodecs.java similarity index 96% rename from src/main/java/dev/isxander/controlify/controller/dualsense/TriggerEffectCodecs.java rename to src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectCodecs.java index 8f1822d89..c8b31cb4f 100644 --- a/src/main/java/dev/isxander/controlify/controller/dualsense/TriggerEffectCodecs.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectCodecs.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.dualsense; +package dev.isxander.controlify.impl.controller.dualsense; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.driver.dualsense.DualsenseTriggerEffect; -import dev.isxander.controlify.utils.codec.CExtraCodecs; +import dev.isxander.controlify.impl.driver.dualsense.DualsenseTriggerEffect; +import dev.isxander.controlify.impl.utils.codec.CExtraCodecs; import net.minecraft.util.ExtraCodecs; import java.util.Map; diff --git a/src/main/java/dev/isxander/controlify/controller/dualsense/TriggerEffectInstanceImpl.java b/src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectInstanceImpl.java similarity index 81% rename from src/main/java/dev/isxander/controlify/controller/dualsense/TriggerEffectInstanceImpl.java rename to src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectInstanceImpl.java index 735a5f09e..36d120e9f 100644 --- a/src/main/java/dev/isxander/controlify/controller/dualsense/TriggerEffectInstanceImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectInstanceImpl.java @@ -4,18 +4,18 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.dualsense; +package dev.isxander.controlify.impl.controller.dualsense; -import dev.isxander.controlify.api.bind.InputBinding; +import dev.isxander.controlify.impl.api.bind.InputBinding; import dev.isxander.controlify.api.contextual.Context; import dev.isxander.controlify.api.contextual.TriggerEffectInstance; -import dev.isxander.controlify.contextual.ContextualDomainImpl; -import dev.isxander.controlify.contextual.ContextualState; -import dev.isxander.controlify.contextual.RuleEngine; -import dev.isxander.controlify.contextual.TriggerEffectRule; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.input.mapping.GamepadInputs; -import dev.isxander.controlify.driver.dualsense.DualsenseTriggerEffect; +import dev.isxander.controlify.impl.contextual.ContextualDomainImpl; +import dev.isxander.controlify.impl.contextual.ContextualState; +import dev.isxander.controlify.impl.contextual.RuleEngine; +import dev.isxander.controlify.impl.contextual.TriggerEffectRule; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.input.mapping.GamepadInputs; +import dev.isxander.controlify.impl.driver.dualsense.DualsenseTriggerEffect; import net.minecraft.resources.Identifier; import java.util.List; @@ -78,7 +78,7 @@ public DualsenseTriggerEffect getRightTriggerEffect() { return this.rightEffect; } - private Trigger getTrigger(ControllerEntity controller, TriggerEffectRule rule) { + private Trigger getTrigger(ControllerImpl controller, TriggerEffectRule rule) { InputBinding binding = rule.binding().onOrNull(controller); if (binding == null) { diff --git a/src/main/java/dev/isxander/controlify/controller/dualsense/TriggerEffectManager.java b/src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectManager.java similarity index 73% rename from src/main/java/dev/isxander/controlify/controller/dualsense/TriggerEffectManager.java rename to src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectManager.java index 1052acf8c..86de48682 100644 --- a/src/main/java/dev/isxander/controlify/controller/dualsense/TriggerEffectManager.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectManager.java @@ -4,18 +4,16 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.dualsense; +package dev.isxander.controlify.impl.controller.dualsense; import dev.isxander.controlify.api.contextual.InGameContext; import dev.isxander.controlify.api.contextual.TriggerEffectInstance; -import dev.isxander.controlify.contextual.ContextualDomains; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.driver.dualsense.DualsenseTriggerEffect; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.contextual.ContextualDomains; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.driver.dualsense.DualsenseTriggerEffect; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.client.Minecraft; -import java.util.*; - public class TriggerEffectManager { private final TriggerEffectInstance instance; @@ -25,13 +23,13 @@ public TriggerEffectManager() { } public void applyTriggerEffects( - ControllerEntity controller, + ControllerImpl controller, boolean inputSuppressed ) { applyTriggerEffects(controller, instance, inputSuppressed); } - public void tick(Minecraft minecraft, ControllerEntity controller) { + public void tick(Minecraft minecraft, ControllerImpl controller) { if (minecraft.player == null || minecraft.level == null) { return; } @@ -42,7 +40,7 @@ public void tick(Minecraft minecraft, ControllerEntity controller) { } public static void applyTriggerEffects( - ControllerEntity controller, + ControllerImpl controller, TriggerEffectInstance instance, boolean inputSuppressed ) { @@ -58,7 +56,7 @@ public static void applyTriggerEffects( }); } - public static boolean shouldUseTriggerEffects(ControllerEntity controller, boolean inputSuppressed) { + public static boolean shouldUseTriggerEffects(ControllerImpl controller, boolean inputSuppressed) { return !inputSuppressed && MinecraftUtil.getScreen() == null && Minecraft.getInstance().player != null diff --git a/src/main/java/dev/isxander/controlify/controller/gyro/GyroButtonMode.java b/src/main/java/dev/isxander/controlify/impl/controller/gyro/GyroButtonMode.java similarity index 91% rename from src/main/java/dev/isxander/controlify/controller/gyro/GyroButtonMode.java rename to src/main/java/dev/isxander/controlify/impl/controller/gyro/GyroButtonMode.java index 7b5c48cf5..880e7467d 100644 --- a/src/main/java/dev/isxander/controlify/controller/gyro/GyroButtonMode.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/gyro/GyroButtonMode.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.gyro; +package dev.isxander.controlify.impl.controller.gyro; import com.mojang.serialization.Codec; import net.minecraft.util.StringRepresentable; diff --git a/src/main/java/dev/isxander/controlify/controller/gyro/GyroCalibrator.java b/src/main/java/dev/isxander/controlify/impl/controller/gyro/GyroCalibrator.java similarity index 89% rename from src/main/java/dev/isxander/controlify/controller/gyro/GyroCalibrator.java rename to src/main/java/dev/isxander/controlify/impl/controller/gyro/GyroCalibrator.java index a626e4fe7..0545cf3ae 100644 --- a/src/main/java/dev/isxander/controlify/controller/gyro/GyroCalibrator.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/gyro/GyroCalibrator.java @@ -4,7 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.gyro; +package dev.isxander.controlify.impl.controller.gyro; + +import org.joml.Vector3f; +import org.joml.Vector3fc; /** * Implements rolling gyro calibration using exponential moving average @@ -24,7 +27,7 @@ public class GyroCalibrator { private static final int MIN_STATIONARY_SAMPLES = 10; // Current bias estimate (offset to subtract from raw readings) - private final GyroState biasEstimate = new GyroState(0, 0, 0); + private final Vector3f biasEstimate = new Vector3f(0, 0, 0); // Counter for consecutive stationary samples private int stationarySampleCount = 0; @@ -62,11 +65,11 @@ private boolean isStationary(float x, float y, float z) { /** * Get the current bias estimate (for saving to config). */ - public GyroStateC getCurrentBias() { - return new GyroState(this.biasEstimate); + public Vector3f getCurrentBias() { + return new Vector3f(this.biasEstimate); } - public void setBias(GyroStateC bias) { + public void setBias(Vector3fc bias) { this.biasEstimate.set(bias.x(), bias.y(), bias.z()); this.stationarySampleCount = 0; } diff --git a/src/main/java/dev/isxander/controlify/controller/gyro/GyroComponent.java b/src/main/java/dev/isxander/controlify/impl/controller/gyro/GyroComponent.java similarity index 69% rename from src/main/java/dev/isxander/controlify/controller/gyro/GyroComponent.java rename to src/main/java/dev/isxander/controlify/impl/controller/gyro/GyroComponent.java index c8409deb9..e3a057d4a 100644 --- a/src/main/java/dev/isxander/controlify/controller/gyro/GyroComponent.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/gyro/GyroComponent.java @@ -4,57 +4,60 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.gyro; - -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.config.settings.device.DeviceSettings; -import dev.isxander.controlify.config.settings.profile.GyroSettings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.impl.ECSComponentImpl; -import dev.isxander.controlify.input.pipeline.InputPipeline; -import dev.isxander.controlify.input.SensorType; -import dev.isxander.controlify.input.signal.LogicalSignal; -import dev.isxander.controlify.input.signal.PhysicalSensorSignal; -import dev.isxander.controlify.input.signal.PhysicalSignal; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.event.EventSource; +package dev.isxander.controlify.impl.controller.gyro; + +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.config.settings.device.DeviceSettings; +import dev.isxander.controlify.impl.config.settings.profile.GyroSettings; +import dev.isxander.controlify.impl.controller.ControllerComponent; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.input.pipeline.InputPipeline; +import dev.isxander.controlify.impl.input.SensorType; +import dev.isxander.controlify.impl.input.signal.LogicalSignal; +import dev.isxander.controlify.impl.input.signal.PhysicalSensorSignal; +import dev.isxander.controlify.impl.input.signal.PhysicalSignal; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.event.EventSource; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; +import org.joml.Vector3fc; import java.util.Objects; -public class GyroComponent extends ECSComponentImpl { +public class GyroComponent extends ControllerComponent { public static final Identifier ID = CUtil.rl("gyro"); private final InputPipeline inputPipeline; private @Nullable EventSource.Subscription calibrationSubscription; private @Nullable EventSource.Subscription sensorSubscription; private DeviceSettings deviceSettings; - private GyroStateC gyroState = GyroStateC.ZERO; + private Vector3fc gyroState = new Vector3f(); private static final long MAX_SAMPLE_AGE_NANOS = 50_000_000L; private long lastStateTimeNanos; private final GyroCalibrator calibrator = new GyroCalibrator(); private static final long CALIBRATION_SAVE_INTERVAL_NANOS = 5_000_000_000L; private long lastCalibrationSaveTimeNanos; - private GyroStateC lastSavedBias = GyroStateC.ZERO; + private Vector3fc lastSavedBias = new Vector3f(); public GyroComponent(InputPipeline inputPipeline) { + super(ID); this.inputPipeline = Objects.requireNonNull(inputPipeline); } @Override - public void attach(ControllerEntity controller) { + public void attach(ControllerImpl controller) { super.attach(controller); // Load the calibration from device settings this.deviceSettings = Controlify.instance().config().getSettings() .getOrCreateDeviceSettings(controller.uid()); - GyroStateC savedOffset = this.deviceSettings.gyroCalibration.offset; + Vector3fc savedOffset = this.deviceSettings.gyroCalibration.offset; // Initialize the calibrator with the saved offset calibrator.setBias(savedOffset); - lastSavedBias = new GyroState(savedOffset); + lastSavedBias = new Vector3f(savedOffset); this.lastCalibrationSaveTimeNanos = System.nanoTime(); this.calibrationSubscription = this.inputPipeline.sourceRawPhysicalSignals().subscribe(this::onRawSignal); @@ -84,31 +87,31 @@ private void onSignal(LogicalSignal signal) { if (signal instanceof LogicalSignal.SensorInterval(_, _, SensorType sensorType, float x, float y, float z) && sensorType == SensorType.GYROSCOPE) { // The pipeline has already calibrated, filtered and integrated these values into radians. - this.gyroState = new GyroState(x, y, z); + this.gyroState = new Vector3f(x, y, z); this.lastStateTimeNanos = System.nanoTime(); } } /// Returns the latest integrated gyro interval in radians. /// Missing samples expire naturally, independently of controller selection. - public GyroStateC getState() { + public Vector3fc getState() { return System.nanoTime() - this.lastStateTimeNanos > MAX_SAMPLE_AGE_NANOS - ? GyroStateC.ZERO : this.gyroState; + ? new Vector3f() : this.gyroState; } private void saveCalibrationIfChanged() { - GyroStateC currentBias = this.deviceSettings.gyroCalibration.offset; + Vector3fc currentBias = this.deviceSettings.gyroCalibration.offset; // Check if bias has changed significantly (more than 0.001 rad/s on any axis) float threshold = 0.001f; - if (Math.abs(currentBias.pitch() - lastSavedBias.pitch()) > threshold || - Math.abs(currentBias.yaw() - lastSavedBias.yaw()) > threshold || - Math.abs(currentBias.roll() - lastSavedBias.roll()) > threshold) { + if (Math.abs(currentBias.x() - lastSavedBias.x()) > threshold || + Math.abs(currentBias.y() - lastSavedBias.y()) > threshold || + Math.abs(currentBias.z() - lastSavedBias.z()) > threshold) { // Settings update on every sample; persistence is checked at a fixed time interval. Controlify.instance().config().markDirty(); - lastSavedBias = new GyroState(currentBias); + lastSavedBias = new Vector3f(currentBias); } } @@ -124,11 +127,6 @@ public GyroSettings defaultSettings() { return this.controller().defaultSettings().gyro; } - @Override - public Identifier id() { - return ID; - } - @Override public void close() { if (this.calibrationSubscription != null) { diff --git a/src/main/java/dev/isxander/controlify/controller/gyro/GyroYawMode.java b/src/main/java/dev/isxander/controlify/impl/controller/gyro/GyroYawMode.java similarity index 90% rename from src/main/java/dev/isxander/controlify/controller/gyro/GyroYawMode.java rename to src/main/java/dev/isxander/controlify/impl/controller/gyro/GyroYawMode.java index ab31977bf..2214b10a5 100644 --- a/src/main/java/dev/isxander/controlify/controller/gyro/GyroYawMode.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/gyro/GyroYawMode.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.gyro; +package dev.isxander.controlify.impl.controller.gyro; import com.mojang.serialization.Codec; import net.minecraft.util.StringRepresentable; diff --git a/src/main/java/dev/isxander/controlify/controller/haptic/CompleteSoundData.java b/src/main/java/dev/isxander/controlify/impl/controller/haptic/CompleteSoundData.java similarity index 80% rename from src/main/java/dev/isxander/controlify/controller/haptic/CompleteSoundData.java rename to src/main/java/dev/isxander/controlify/impl/controller/haptic/CompleteSoundData.java index 91beaf208..a287508b0 100644 --- a/src/main/java/dev/isxander/controlify/controller/haptic/CompleteSoundData.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/haptic/CompleteSoundData.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.haptic; +package dev.isxander.controlify.impl.controller.haptic; import javax.sound.sampled.AudioFormat; diff --git a/src/main/java/dev/isxander/controlify/controller/haptic/HDHapticComponent.java b/src/main/java/dev/isxander/controlify/impl/controller/haptic/HDHapticComponent.java similarity index 90% rename from src/main/java/dev/isxander/controlify/controller/haptic/HDHapticComponent.java rename to src/main/java/dev/isxander/controlify/impl/controller/haptic/HDHapticComponent.java index 08d072574..35ba1b068 100644 --- a/src/main/java/dev/isxander/controlify/controller/haptic/HDHapticComponent.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/haptic/HDHapticComponent.java @@ -4,15 +4,15 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.haptic; +package dev.isxander.controlify.impl.controller.haptic; import com.mojang.blaze3d.audio.SoundBuffer; -import dev.isxander.controlify.config.settings.profile.HDHapticSettings; -import dev.isxander.controlify.controller.impl.ECSComponentImpl; +import dev.isxander.controlify.impl.config.settings.profile.HDHapticSettings; +import dev.isxander.controlify.impl.controller.ControllerComponent; import dev.isxander.controlify.mixins.feature.hdhaptics.SoundBufferAccessor; import dev.isxander.controlify.mixins.feature.hdhaptics.SoundEngineAccessor; import dev.isxander.controlify.mixins.feature.hdhaptics.SoundManagerAccessor; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.client.Minecraft; import net.minecraft.client.sounds.SoundBufferLibrary; import net.minecraft.client.sounds.SoundEngine; @@ -28,7 +28,7 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; -public class HDHapticComponent extends ECSComponentImpl { +public class HDHapticComponent extends ControllerComponent { public static final Identifier ID = CUtil.rl("hd_haptics"); private Consumer playHapticConsumer; @@ -40,6 +40,7 @@ public class HDHapticComponent extends ECSComponentImpl { private static final Map hapticData = new HashMap<>(); public HDHapticComponent() { + super(ID); this.randomSource = RandomSource.create(); } @@ -105,9 +106,4 @@ public HDHapticSettings settings() { public HDHapticSettings defaultSettings() { return this.controller().defaultSettings().hdHaptic; } - - @Override - public Identifier id() { - return ID; - } } diff --git a/src/main/java/dev/isxander/controlify/controller/haptic/HapticEffects.java b/src/main/java/dev/isxander/controlify/impl/controller/haptic/HapticEffects.java similarity index 72% rename from src/main/java/dev/isxander/controlify/controller/haptic/HapticEffects.java rename to src/main/java/dev/isxander/controlify/impl/controller/haptic/HapticEffects.java index ae911302f..263e70480 100644 --- a/src/main/java/dev/isxander/controlify/controller/haptic/HapticEffects.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/haptic/HapticEffects.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.haptic; +package dev.isxander.controlify.impl.controller.haptic; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; public final class HapticEffects { diff --git a/src/main/java/dev/isxander/controlify/controller/input/InputComponent.java b/src/main/java/dev/isxander/controlify/impl/controller/input/InputComponent.java similarity index 75% rename from src/main/java/dev/isxander/controlify/controller/input/InputComponent.java rename to src/main/java/dev/isxander/controlify/impl/controller/input/InputComponent.java index 2a93f7b71..24348e5a9 100644 --- a/src/main/java/dev/isxander/controlify/controller/input/InputComponent.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/input/InputComponent.java @@ -1,30 +1,32 @@ -package dev.isxander.controlify.controller.input; - -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.action.defaults.DefaultBindProvider; -import dev.isxander.controlify.config.settings.device.DeviceSettings; -import dev.isxander.controlify.config.settings.profile.InputSettings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.impl.ECSComponentImpl; -import dev.isxander.controlify.input.LogicalInput; -import dev.isxander.controlify.input.SensorType; -import dev.isxander.controlify.input.mapping.ControllerMapping; -import dev.isxander.controlify.input.mapping.ControllerMappingManager; -import dev.isxander.controlify.action.ActionGraph; -import dev.isxander.controlify.action.ActionImpl; -import dev.isxander.controlify.action.ActionSpec; -import dev.isxander.controlify.action.ActionSpecRegistryImpl; -import dev.isxander.controlify.action.activator.ActivatorConfig; -import dev.isxander.controlify.input.pipeline.InputPipeline; -import dev.isxander.controlify.input.pipeline.SensorProcessorStage; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.event.EventSource; +package dev.isxander.controlify.impl.controller.input; + +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.action.defaults.DefaultBindProvider; +import dev.isxander.controlify.impl.config.settings.device.DeviceSettings; +import dev.isxander.controlify.impl.config.settings.profile.InputSettings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controller.impl.ECSComponentImpl; +import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.impl.controller.ControllerComponent; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.input.SensorType; +import dev.isxander.controlify.impl.input.mapping.ControllerMapping; +import dev.isxander.controlify.impl.input.mapping.ControllerMappingManager; +import dev.isxander.controlify.impl.action.ActionGraph; +import dev.isxander.controlify.impl.action.ActionImpl; +import dev.isxander.controlify.api.action.ActionSpec; +import dev.isxander.controlify.impl.action.ActionSpecRegistryImpl; +import dev.isxander.controlify.impl.action.activator.ActivatorConfig; +import dev.isxander.controlify.impl.input.pipeline.InputPipeline; +import dev.isxander.controlify.impl.input.pipeline.SensorProcessorStage; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.event.EventSource; import net.minecraft.resources.Identifier; import org.jspecify.annotations.Nullable; import java.util.*; -public class InputComponent extends ECSComponentImpl { +public class InputComponent extends ControllerComponent { public static final Identifier ID = CUtil.rl("input"); private final Identifier controllerType; @@ -49,6 +51,7 @@ public InputComponent( Set supportedSensors, boolean isGamepad ) { + super(ID); this.controllerType = controllerType; this.inputPipeline = inputPipeline; @@ -62,7 +65,7 @@ public InputComponent( } @Override - public void attach(ControllerEntity controller) { + public void attach(ControllerImpl controller) { super.attach(controller); this.deviceSettings = Controlify.instance().config().getSettings() @@ -126,11 +129,6 @@ public void onGuiNavigate() { this.inputPipeline.stagePhysicalToLogical().onGuiNavigate(); } - @Override - public Identifier id() { - return ID; - } - public InputSettings settings() { return this.controller().settings().input; } diff --git a/src/main/java/dev/isxander/controlify/controller/led/LEDComponent.java b/src/main/java/dev/isxander/controlify/impl/controller/led/LEDComponent.java similarity index 86% rename from src/main/java/dev/isxander/controlify/controller/led/LEDComponent.java rename to src/main/java/dev/isxander/controlify/impl/controller/led/LEDComponent.java index 3d78419ed..820faaddf 100644 --- a/src/main/java/dev/isxander/controlify/controller/led/LEDComponent.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/led/LEDComponent.java @@ -4,22 +4,22 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.led; +package dev.isxander.controlify.impl.controller.led; -import dev.isxander.controlify.controller.impl.ECSComponentImpl; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.controller.ControllerComponent; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; -import org.jetbrains.annotations.ApiStatus; import java.util.Arrays; -public class LEDComponent extends ECSComponentImpl { +public class LEDComponent extends ControllerComponent { public static final Identifier ID = CUtil.rl("led"); private final int[] ledColors; private boolean dirty = false; public LEDComponent(int ledCount) { + super(ID); this.ledColors = new int[ledCount]; } @@ -77,15 +77,9 @@ public int getCount() { return ledColors.length; } - @ApiStatus.Internal public boolean consumeDirty() { boolean old = this.dirty; this.dirty = false; return old; } - - @Override - public Identifier id() { - return ID; - } } diff --git a/src/main/java/dev/isxander/controlify/controller/misc/BluetoothDeviceComponent.java b/src/main/java/dev/isxander/controlify/impl/controller/misc/BluetoothDeviceComponent.java similarity index 50% rename from src/main/java/dev/isxander/controlify/controller/misc/BluetoothDeviceComponent.java rename to src/main/java/dev/isxander/controlify/impl/controller/misc/BluetoothDeviceComponent.java index 73171ce2b..b9483aac5 100644 --- a/src/main/java/dev/isxander/controlify/controller/misc/BluetoothDeviceComponent.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/misc/BluetoothDeviceComponent.java @@ -4,16 +4,21 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.misc; +package dev.isxander.controlify.impl.controller.misc; -import dev.isxander.controlify.config.settings.profile.BluetoothDeviceSettings; -import dev.isxander.controlify.controller.impl.ECSComponentImpl; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.config.settings.profile.BluetoothDeviceSettings; +import dev.isxander.controlify.impl.controller.impl.ECSComponentImpl; +import dev.isxander.controlify.impl.controller.ControllerComponent; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; -public class BluetoothDeviceComponent extends ECSComponentImpl { +public class BluetoothDeviceComponent extends ControllerComponent { public static final Identifier ID = CUtil.rl("bluetooth"); + public BluetoothDeviceComponent() { + super(ID); + } + public BluetoothDeviceSettings settings() { return this.controller().settings().bluetoothDevice; } @@ -21,9 +26,4 @@ public BluetoothDeviceSettings settings() { public BluetoothDeviceSettings defaultSettings() { return this.controller().defaultSettings().bluetoothDevice; } - - @Override - public Identifier id() { - return ID; - } } diff --git a/src/main/java/dev/isxander/controlify/impl/controller/misc/DriverNameComponent.java b/src/main/java/dev/isxander/controlify/impl/controller/misc/DriverNameComponent.java new file mode 100644 index 000000000..83b6c630d --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/controller/misc/DriverNameComponent.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.controller.misc; + +import dev.isxander.controlify.impl.controller.SingleValueControllerComponent; +import dev.isxander.controlify.impl.utils.CUtil; +import net.minecraft.resources.Identifier; + +public class DriverNameComponent extends SingleValueControllerComponent { + public static final Identifier ID = CUtil.rl("driver_name"); + + public DriverNameComponent(String value) { + super(ID, value); + } +} diff --git a/src/main/java/dev/isxander/controlify/controller/info/GUIDComponent.java b/src/main/java/dev/isxander/controlify/impl/controller/misc/GUIDComponent.java similarity index 67% rename from src/main/java/dev/isxander/controlify/controller/info/GUIDComponent.java rename to src/main/java/dev/isxander/controlify/impl/controller/misc/GUIDComponent.java index 8fa487c0b..61e978b0c 100644 --- a/src/main/java/dev/isxander/controlify/controller/info/GUIDComponent.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/misc/GUIDComponent.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.info; +package dev.isxander.controlify.impl.controller.misc; -import dev.isxander.controlify.controller.SingleValueComponent; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.controller.SingleValueComponent; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; public class GUIDComponent extends SingleValueComponent { diff --git a/src/main/java/dev/isxander/controlify/impl/controller/misc/UIDComponent.java b/src/main/java/dev/isxander/controlify/impl/controller/misc/UIDComponent.java new file mode 100644 index 000000000..f8e0bf97f --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/controller/misc/UIDComponent.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.controller.misc; + +import dev.isxander.controlify.impl.controller.SingleValueControllerComponent; +import dev.isxander.controlify.impl.utils.CUtil; +import net.minecraft.resources.Identifier; + +public class UIDComponent extends SingleValueControllerComponent { + public static final Identifier ID = CUtil.rl("uid"); + + public UIDComponent(String value) { + super(ID, value); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/controller/rumble/ContinuousRumbleEffectImpl.java b/src/main/java/dev/isxander/controlify/impl/controller/rumble/ContinuousRumbleEffectImpl.java new file mode 100644 index 000000000..eb9ce78b6 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/controller/rumble/ContinuousRumbleEffectImpl.java @@ -0,0 +1,122 @@ +package dev.isxander.controlify.impl.controller.rumble; + +import dev.isxander.controlify.api.controller.rumble.ContinuousRumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleState; +import net.minecraft.client.Minecraft; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.Vec3; +import org.jspecify.annotations.NonNull; + +import java.util.Objects; +import java.util.function.BooleanSupplier; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; + +public final class ContinuousRumbleEffectImpl implements ContinuousRumbleEffect { + private final IntFunction state; + private final BooleanSupplier stop; + private final int timeout, minimum; + private final UnaryOperator attenuation; + + public ContinuousRumbleEffectImpl() { + this(_ -> RumbleState.NONE, () -> false, 0, 0, value -> value); + } + + private ContinuousRumbleEffectImpl(IntFunction state, BooleanSupplier stop, + int timeout, int minimum, UnaryOperator attenuation) { + this.state = Objects.requireNonNull(state); + this.stop = stop; + this.timeout = timeout; + this.minimum = minimum; + this.attenuation = attenuation; + } + + @Override + public int timeoutTicks() { + return this.timeout; + } + + @Override + public int minimumTicks() { + return this.minimum; + } + + @Override + public boolean shouldStop() { + return this.stop.getAsBoolean(); + } + + @Override + public @NonNull RumbleState stateAt(int tick) { + return this.attenuation.apply(Objects.requireNonNull(this.state.apply(tick))); + } + + @Override + public @NonNull ContinuousRumbleEffect byTick(@NonNull IntFunction state) { + return new ContinuousRumbleEffectImpl(state, this.stop, this.timeout, this.minimum, this.attenuation); + } + + @Override + public @NonNull ContinuousRumbleEffect constant(float strong, float weak) { + return byTick(_ -> new RumbleState(strong, weak)); + } + + @Override + public @NonNull ContinuousRumbleEffect stopCondition(@NonNull BooleanSupplier condition) { + Objects.requireNonNull(condition); + return new ContinuousRumbleEffectImpl( + this.state, + () -> this.stop.getAsBoolean() || condition.getAsBoolean(), + this.timeout, + this.minimum, + this.attenuation + ); + } + + @Override + public @NonNull ContinuousRumbleEffect timeout(int ticks) { + if (ticks < 0) { + throw new IllegalArgumentException("Timeout cannot be negative"); + } + return new ContinuousRumbleEffectImpl(this.state, this.stop, ticks, this.minimum, this.attenuation); + } + + @Override + public @NonNull ContinuousRumbleEffect minTime(int ticks) { + if (ticks < 0) { + throw new IllegalArgumentException("Minimum duration cannot be negative"); + } + return new ContinuousRumbleEffectImpl(this.state, this.stop, this.timeout, ticks, this.attenuation); + } + + @Override + public @NonNull ContinuousRumbleEffect inWorld( + @NonNull Supplier location, + float minMagnitude, float maxMagnitude, + float range, + @NonNull Function falloff + ) { + Objects.requireNonNull(location); + Objects.requireNonNull(falloff); + if (!Float.isFinite(range) || range <= 0) { + throw new IllegalArgumentException("Range must be finite and positive"); + } + if (!Float.isFinite(minMagnitude) || !Float.isFinite(maxMagnitude) || minMagnitude < 0 || maxMagnitude < minMagnitude) { + throw new IllegalArgumentException("Magnitudes must be finite, nonnegative and ordered"); + } + return new ContinuousRumbleEffectImpl( + this.state, + () -> this.stop.getAsBoolean() || Minecraft.getInstance().getCameraEntity() == null, + this.timeout, this.minimum, value -> { + var camera = Minecraft.getInstance().getCameraEntity(); + if (camera == null) { + return RumbleState.NONE; + } + float distance = (float) Mth.clamp(camera.distanceToSqr(location.get()) / ((double) range * range), 0, 1); + return value.mul(Mth.lerp(falloff.apply(1 - distance), minMagnitude, maxMagnitude)); + } + ); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/controller/rumble/FiniteRumbleEffectImpl.java b/src/main/java/dev/isxander/controlify/impl/controller/rumble/FiniteRumbleEffectImpl.java new file mode 100644 index 000000000..64f32ba34 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/controller/rumble/FiniteRumbleEffectImpl.java @@ -0,0 +1,97 @@ +package dev.isxander.controlify.impl.controller.rumble; + +import dev.isxander.controlify.api.controller.rumble.FiniteRumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleState; +import org.jspecify.annotations.NonNull; + +import java.util.Arrays; +import java.util.Objects; +import java.util.function.BooleanSupplier; +import java.util.function.Function; +import java.util.function.IntFunction; + +public final class FiniteRumbleEffectImpl implements FiniteRumbleEffect { + private final RumbleState[] frames; + private final BooleanSupplier stop; + + public FiniteRumbleEffectImpl() { + this(new RumbleState[0], () -> false); + } + + private FiniteRumbleEffectImpl(RumbleState[] frames, BooleanSupplier stop) { + this.frames = frames; + this.stop = stop; + } + + @Override + public int durationTicks() { + return this.frames.length; + } + + @Override + public @NonNull RumbleState stateAt(int tick) { + return this.frames[tick]; + } + + @Override + public boolean shouldStop() { + return this.stop.getAsBoolean(); + } + + @Override + public @NonNull FiniteRumbleEffect stopCondition(@NonNull BooleanSupplier condition) { + Objects.requireNonNull(condition); + return new FiniteRumbleEffectImpl(this.frames, () -> this.stop.getAsBoolean() || condition.getAsBoolean()); + } + + @Override + public @NonNull FiniteRumbleEffect byTick(@NonNull IntFunction state, int durationTicks) { + Objects.requireNonNull(state); + if (durationTicks < 0) { + throw new IllegalArgumentException("Duration cannot be negative"); + } + RumbleState[] frames = new RumbleState[durationTicks]; + for (int i = 0; i < frames.length; i++) { + frames[i] = Objects.requireNonNull(state.apply(i)); + } + return new FiniteRumbleEffectImpl(frames, this.stop); + } + + @Override + public @NonNull FiniteRumbleEffect byTime(@NonNull Function state, int durationTicks) { + Objects.requireNonNull(state); + return byTick(tick -> state.apply((float) tick / durationTicks), durationTicks); + } + + @Override + public @NonNull FiniteRumbleEffect constant(float strong, float weak, int durationTicks) { + return byTick(_ -> new RumbleState(strong, weak), durationTicks); + } + + @Override + public @NonNull FiniteRumbleEffect then(@NonNull FiniteRumbleEffect next) { + Objects.requireNonNull(next); + int length = next.durationTicks(); + if (length < 0) { + throw new IllegalArgumentException("Duration cannot be negative"); + } + + RumbleState[] frames = Arrays.copyOf(this.frames, Math.addExact(this.frames.length, length)); + for (int i = 0; i < length; i++) { + frames[this.frames.length + i] = Objects.requireNonNull(next.stateAt(i)); + } + return new FiniteRumbleEffectImpl(frames, this.stop); + } + + @Override + public @NonNull FiniteRumbleEffect repeat(int count) { + if (count <= 0) { + throw new IllegalArgumentException("Repeat count must be positive"); + } + RumbleState[] frames = new RumbleState[Math.multiplyExact(this.frames.length, count)]; + for (int i = 0; i < count && this.frames.length > 0; i++) { + System.arraycopy(this.frames, 0, frames, i * this.frames.length, this.frames.length); + } + return new FiniteRumbleEffectImpl(frames, this.stop); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleComponentImpl.java b/src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleComponentImpl.java new file mode 100644 index 000000000..ed6469e50 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleComponentImpl.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.controller.rumble; + +import dev.isxander.controlify.api.controller.rumble.RumbleComponent; +import dev.isxander.controlify.api.controller.rumble.RumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleEffectInstance; +import dev.isxander.controlify.api.controller.rumble.RumbleState; +import dev.isxander.controlify.impl.config.settings.profile.RumbleSettings; +import dev.isxander.controlify.impl.controller.ControllerComponent; +import dev.isxander.controlify.impl.utils.CUtil; +import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; + +import java.util.Optional; + +public class RumbleComponentImpl extends ControllerComponent implements RumbleComponent { + public static final Identifier ID = CUtil.rl("rumble"); + + private RumbleState state = null; + private final RumbleManager rumbleManager; + + public RumbleComponentImpl() { + super(ID); + this.rumbleManager = new RumbleManager(this); + } + + @Override + public @NonNull RumbleEffectInstance playEffect(@NonNull Identifier effectId, @NonNull RumbleEffect effect) { + return this.rumbleManager.play(effectId, effect); + } + + public void queueRumble(RumbleState state) { + if (settings().enabled || state.isZero()) { + this.state = state; + } + } + + public Optional consumeRumble() { + RumbleState state = this.state; + this.state = null; + return Optional.ofNullable(state); + } + + public RumbleManager rumbleManager() { + return this.rumbleManager; + } + + public RumbleSettings settings() { + return this.controller().settings().rumble; + } + + public RumbleSettings defaultSettings() { + return this.controller().defaultSettings().rumble; + } + +} diff --git a/src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleEffectInstanceImpl.java b/src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleEffectInstanceImpl.java new file mode 100644 index 000000000..1292bb2fa --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleEffectInstanceImpl.java @@ -0,0 +1,98 @@ +package dev.isxander.controlify.impl.controller.rumble; + +import dev.isxander.controlify.api.controller.rumble.ContinuousRumbleEffect; +import dev.isxander.controlify.api.controller.rumble.FiniteRumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleEffectInstance; +import dev.isxander.controlify.api.controller.rumble.RumbleState; +import net.minecraft.resources.Identifier; + +import java.util.Objects; + +/// Playback state belongs to the handle, never to the reusable definition. +public final class RumbleEffectInstanceImpl implements RumbleEffectInstance { + private final Identifier effectId; + private final RumbleEffect effect; + private int tick, inactivity; + private boolean started, finished, stopRequested; + private RumbleState state = RumbleState.NONE; + + public RumbleEffectInstanceImpl(Identifier effectId, RumbleEffect effect) { + this.effectId = Objects.requireNonNull(effectId); + this.effect = Objects.requireNonNull(effect); + } + + public Identifier effectId() { + return effectId; + } + + @Override + public boolean hasStarted() { + return this.started || this.finished; + } + + @Override + public boolean hasFinished() { + return this.finished; + } + + public RumbleState currentState() { + return this.state; + } + + @Override + public boolean stop() { + if (this.finished) { + return false; + } + this.finished = true; + this.state = RumbleState.NONE; + return true; + } + + @Override + public boolean keepAlive() { + if (this.finished || !(this.effect instanceof ContinuousRumbleEffect)) { + return false; + } + this.inactivity = 0; + return true; + } + + public void tick() { + if (this.finished) { + return; + } + this.stopRequested |= this.effect.shouldStop(); + switch (this.effect) { + case FiniteRumbleEffect finite -> { + if (this.stopRequested || this.tick >= finite.durationTicks()) { + this.stop(); + return; + } + } + case ContinuousRumbleEffect continuous -> { + if (this.tick >= continuous.minimumTicks() && (stopRequested || continuous.timeoutTicks() > 0 && inactivity >= continuous.timeoutTicks())) { + this.stop(); + return; + } + } + } + + this.state = Objects.requireNonNull(effect.stateAt(tick)); + this.started = true; + if (this.tick < Integer.MAX_VALUE) { + tick++; + } + if (this.inactivity < Integer.MAX_VALUE) { + this.inactivity++; + } + + if (this.effect instanceof FiniteRumbleEffect finite && this.tick >= finite.durationTicks()) + this.finished = true; + if (this.effect instanceof ContinuousRumbleEffect continuous && this.tick >= continuous.minimumTicks() + && (this.stopRequested || continuous.timeoutTicks() > 0 && this.inactivity >= continuous.timeoutTicks())) { + this.finished = true; + } + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleManager.java b/src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleManager.java new file mode 100644 index 000000000..1d5c5ab4b --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleManager.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.controller.rumble; + +import dev.isxander.controlify.api.controller.rumble.RumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleEffectInstance; +import dev.isxander.controlify.api.controller.rumble.RumbleState; +import dev.isxander.controlify.impl.Controlify; +import net.minecraft.resources.Identifier; + +import java.util.ArrayList; +import java.util.List; + +public class RumbleManager { + private final RumbleComponentImpl controller; + private final List effectQueue = new ArrayList<>(); + + private boolean silent, wasSilent; + + public RumbleManager(RumbleComponentImpl controller) { + this.controller = controller; + } + + public RumbleEffectInstance play(Identifier effectId, RumbleEffect effect) { + return play(effectId, RumbleSource.MASTER, effect); + } + + /// Internal entry point for server-specified fallback sources. + public RumbleEffectInstance play(Identifier effectId, RumbleSource source, RumbleEffect effect) { + var instance = new RumbleEffectInstanceImpl(effectId, effect); + this.effectQueue.add(new PlayingEffect(source, instance)); + return instance; + } + + public void tick() { + this.effectQueue.removeIf(e -> e.effect().hasFinished()); + this.effectQueue.forEach(e -> e.effect().tick()); + + if (effectQueue.isEmpty()) { + clearRumble(); + return; + } + + float strong = 0f, weak = 0f; + RumbleSourceManager sourceManager = Controlify.instance().rumbleSourceManager(); + for (PlayingEffect effect : effectQueue) { + RumbleSource source = sourceManager.getSource(effect.effect().effectId(), effect.source()); + RumbleState effectState = controller.settings().applyRumbleStrength(effect.effect().currentState(), source); + strong = Math.max(strong, effectState.strong()); + weak = Math.max(weak, effectState.weak()); + } + RumbleState state = new RumbleState(strong, weak); + + if (state.isZero()) { + clearRumble(); + return; + } + + if (silent || !controller.settings().enabled) { + clearRumble(); + } else { + controller.queueRumble(state); + wasSilent = false; + } + } + + private void clearRumble() { + if (wasSilent) + return; + + controller.queueRumble(RumbleState.NONE); + wasSilent = true; + } + + public void clearEffects() { + for (PlayingEffect instance : effectQueue) { + instance.effect().stop(); + } + effectQueue.clear(); + clearRumble(); + } + + public void setSilent(boolean silent) { + this.silent = silent; + } + + public boolean isPlaying() { + return !effectQueue.isEmpty(); + } + + private record PlayingEffect(RumbleSource source, RumbleEffectInstanceImpl effect) { + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleSource.java b/src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleSource.java new file mode 100644 index 000000000..3bc24ffe5 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleSource.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.controller.rumble; + +import com.mojang.serialization.Codec; +import dev.isxander.controlify.impl.utils.CUtil; +import net.minecraft.resources.Identifier; + +import java.util.Objects; + +/// A source identifier, with no registration or availability implied by construction. +/// Available sources are determined by RumbleSourceManager's resource-pack assignments. +public record RumbleSource(Identifier id) { + public static final Codec CODEC = Identifier.CODEC + .xmap(RumbleSource::new, RumbleSource::id); + + /// The global gain/fallback source; this constant does not declare its availability. + public static final RumbleSource MASTER = new RumbleSource(CUtil.rl("master")); + + public RumbleSource { + Objects.requireNonNull(id); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleSourceManager.java b/src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleSourceManager.java new file mode 100644 index 000000000..5753e2a33 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/controller/rumble/RumbleSourceManager.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.controller.rumble; + +import com.google.gson.JsonParser; +import com.mojang.serialization.Codec; +import com.mojang.serialization.JsonOps; +import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; +import dev.isxander.controlify.impl.utils.CUtil; +import net.minecraft.resources.Identifier; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.server.packs.resources.ResourceManager; + +import java.io.BufferedReader; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +/// Merges effect-to-source assignments from assets/controlify/controller/rumble_sources.json. +/// Higher-priority packs override individual effect IDs. The distinct source IDs in +/// the merged assignments define all available sources. Unassigned effects use their +/// caller-specified fallback if available, otherwise master. +public final class RumbleSourceManager implements SimpleControlifyReloadListener> { + private static final Identifier DEFINITIONS = CUtil.rl("controller/rumble_sources.json"); + private static final Codec> CODEC = Codec.unboundedMap(Identifier.CODEC, RumbleSource.CODEC); + + private volatile Map sourcesByEffect = Map.of(); + + public RumbleSource getSource(Identifier effectId, RumbleSource fallback) { + Map assignments = this.sourcesByEffect; + RumbleSource assigned = assignments.get(effectId); + if (assigned != null) return assigned; + return assignments.containsValue(fallback) ? fallback : RumbleSource.MASTER; + } + + /// Every distinct source named in the loaded assignments, including custom namespaces. + /// No built-in list is added; removing assignments also removes otherwise unused sources. + public List sources() { + return this.sourcesByEffect.values().stream() + .distinct() + .sorted(Comparator.comparing(source -> source.id().toString())) + .toList(); + } + + @Override + public CompletableFuture> load(ResourceManager manager, Executor executor) { + return CompletableFuture.supplyAsync(() -> { + Map assignments = new HashMap<>(); + // Resource stacks are ordered from lowest to highest pack priority. + for (Resource resource : manager.getResourceStack(DEFINITIONS)) { + try (BufferedReader reader = resource.openAsReader()) { + CODEC.parse(JsonOps.INSTANCE, JsonParser.parseReader(reader)) + .resultOrPartial(error -> CUtil.LOGGER.error( + "Failed to parse rumble source assignments from pack '{}': {}", + resource.sourcePackId(), error + )) + .ifPresent(assignments::putAll); + } catch (Exception e) { + CUtil.LOGGER.error("Failed to load rumble source assignments from pack '{}'", resource.sourcePackId(), e); + } + } + return Map.copyOf(assignments); + }, executor); + } + + @Override + public CompletableFuture apply(Map data, ResourceManager manager, Executor executor) { + return CompletableFuture.runAsync(() -> this.sourcesByEffect = Map.copyOf(data), executor); + } + + @Override + public Identifier getReloadId() { + return CUtil.rl("rumble_sources"); + } +} diff --git a/src/main/java/dev/isxander/controlify/controller/rumble/TriggerRumbleComponent.java b/src/main/java/dev/isxander/controlify/impl/controller/rumble/TriggerRumbleComponent.java similarity index 62% rename from src/main/java/dev/isxander/controlify/controller/rumble/TriggerRumbleComponent.java rename to src/main/java/dev/isxander/controlify/impl/controller/rumble/TriggerRumbleComponent.java index 916196661..a3493167f 100644 --- a/src/main/java/dev/isxander/controlify/controller/rumble/TriggerRumbleComponent.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/rumble/TriggerRumbleComponent.java @@ -4,21 +4,25 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.rumble; +package dev.isxander.controlify.impl.controller.rumble; -import dev.isxander.controlify.controller.impl.ECSComponentImpl; -import dev.isxander.controlify.rumble.TriggerRumbleState; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.api.controller.rumble.TriggerRumbleState; +import dev.isxander.controlify.impl.controller.ControllerComponent; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.NotNull; import java.util.Optional; -public class TriggerRumbleComponent extends ECSComponentImpl { +public class TriggerRumbleComponent extends ControllerComponent { public static final Identifier ID = CUtil.rl("trigger_rumble"); private TriggerRumbleState state = null; + public TriggerRumbleComponent() { + super(ID); + } + public void queueTriggerRumble(@NotNull TriggerRumbleState state) { this.state = state; } @@ -28,9 +32,4 @@ public Optional consumeTriggerRumble() { this.state = null; return Optional.ofNullable(state); } - - @Override - public Identifier id() { - return ID; - } } diff --git a/src/main/java/dev/isxander/controlify/rumble/effects/UseItemEffectHolder.java b/src/main/java/dev/isxander/controlify/impl/controller/rumble/effects/UseItemEffectHolder.java similarity index 50% rename from src/main/java/dev/isxander/controlify/rumble/effects/UseItemEffectHolder.java rename to src/main/java/dev/isxander/controlify/impl/controller/rumble/effects/UseItemEffectHolder.java index 5e2b86a4c..d660ebb46 100644 --- a/src/main/java/dev/isxander/controlify/rumble/effects/UseItemEffectHolder.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/rumble/effects/UseItemEffectHolder.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.rumble.effects; +package dev.isxander.controlify.impl.controller.rumble.effects; -import dev.isxander.controlify.rumble.ContinuousRumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleEffectInstance; import org.jetbrains.annotations.Nullable; public interface UseItemEffectHolder { - @Nullable ContinuousRumbleEffect controlify$getUseItemEffect(); + @Nullable RumbleEffectInstance controlify$getUseItemEffect(); } diff --git a/src/main/java/dev/isxander/controlify/impl/controller/steamdeck/SteamDeckComponent.java b/src/main/java/dev/isxander/controlify/impl/controller/steamdeck/SteamDeckComponent.java new file mode 100644 index 000000000..d5ccf8521 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/controller/steamdeck/SteamDeckComponent.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.controller.steamdeck; + +import dev.isxander.controlify.impl.controller.ControllerComponent; +import dev.isxander.controlify.impl.utils.CUtil; +import net.minecraft.resources.Identifier; + +public class SteamDeckComponent extends ControllerComponent { + public static final Identifier ID = CUtil.rl("steam_deck"); + + public SteamDeckComponent() { + super(ID); + } +} diff --git a/src/main/java/dev/isxander/controlify/controller/touchpad/TouchpadComponent.java b/src/main/java/dev/isxander/controlify/impl/controller/touchpad/TouchpadComponent.java similarity index 60% rename from src/main/java/dev/isxander/controlify/controller/touchpad/TouchpadComponent.java rename to src/main/java/dev/isxander/controlify/impl/controller/touchpad/TouchpadComponent.java index ba47c98a3..e202afd4e 100644 --- a/src/main/java/dev/isxander/controlify/controller/touchpad/TouchpadComponent.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/touchpad/TouchpadComponent.java @@ -4,27 +4,23 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.touchpad; +package dev.isxander.controlify.impl.controller.touchpad; -import dev.isxander.controlify.controller.impl.ECSComponentImpl; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.controller.ControllerComponent; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; -public class TouchpadComponent extends ECSComponentImpl { +public class TouchpadComponent extends ControllerComponent { public static final Identifier ID = CUtil.rl("touchpad"); private final Touchpads touchpads; public TouchpadComponent(Touchpads touchpads) { + super(ID); this.touchpads = touchpads; } public Touchpads.Touchpad[] touchpads() { return touchpads.touchpads(); } - - @Override - public Identifier id() { - return ID; - } } diff --git a/src/main/java/dev/isxander/controlify/controller/touchpad/Touchpads.java b/src/main/java/dev/isxander/controlify/impl/controller/touchpad/Touchpads.java similarity index 95% rename from src/main/java/dev/isxander/controlify/controller/touchpad/Touchpads.java rename to src/main/java/dev/isxander/controlify/impl/controller/touchpad/Touchpads.java index 2c403902c..45148dd1f 100644 --- a/src/main/java/dev/isxander/controlify/controller/touchpad/Touchpads.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/touchpad/Touchpads.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.touchpad; +package dev.isxander.controlify.impl.controller.touchpad; import org.joml.Vector2fc; diff --git a/src/main/java/dev/isxander/controlify/controllermanager/ControllerBackend.java b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerBackend.java similarity index 85% rename from src/main/java/dev/isxander/controlify/controllermanager/ControllerBackend.java rename to src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerBackend.java index 75317ffdb..18974c4d7 100644 --- a/src/main/java/dev/isxander/controlify/controllermanager/ControllerBackend.java +++ b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerBackend.java @@ -1,8 +1,7 @@ -package dev.isxander.controlify.controllermanager; +package dev.isxander.controlify.impl.controllermanager; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.controller.id.ControllerType; -import dev.isxander.controlify.hid.HIDID; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.hid.HIDID; /// A controller backend is responsible for listening to controller events /// and creating controllers using the referenced {@link ControllerManager controller manager}. diff --git a/src/main/java/dev/isxander/controlify/controllermanager/ControllerCoordinator.java b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerCoordinator.java similarity index 79% rename from src/main/java/dev/isxander/controlify/controllermanager/ControllerCoordinator.java rename to src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerCoordinator.java index 54c2fe21e..626dc785a 100644 --- a/src/main/java/dev/isxander/controlify/controllermanager/ControllerCoordinator.java +++ b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerCoordinator.java @@ -4,24 +4,24 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controllermanager; - -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.api.event.ControlifyEvents; -import dev.isxander.controlify.config.ConfigManager; -import dev.isxander.controlify.config.settings.device.DeviceSettings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.driver.sdl.SDLNativesLoader; -import dev.isxander.controlify.gui.screen.AskToMapControllerScreen; -import dev.isxander.controlify.gui.screen.BluetoothWarningScreen; -import dev.isxander.controlify.input.signal.PhysicalInputSignal; -import dev.isxander.controlify.platform.main.PlatformMainUtil; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.DebugLog; -import dev.isxander.controlify.utils.MinecraftUtil; -import dev.isxander.controlify.utils.SetupWizard; -import dev.isxander.controlify.utils.SetupWizardManager; -import dev.isxander.controlify.utils.event.EventSource; +package dev.isxander.controlify.impl.controllermanager; + +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.api.ControlifyEvents; +import dev.isxander.controlify.impl.config.ConfigManager; +import dev.isxander.controlify.impl.config.settings.device.DeviceSettings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.driver.sdl.SDLNativesLoader; +import dev.isxander.controlify.impl.gui.screen.AskToMapControllerScreen; +import dev.isxander.controlify.impl.gui.screen.BluetoothWarningScreen; +import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; +import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.DebugLog; +import dev.isxander.controlify.impl.utils.MinecraftUtil; +import dev.isxander.controlify.impl.utils.SetupWizard; +import dev.isxander.controlify.impl.utils.SetupWizardManager; +import dev.isxander.controlify.impl.utils.event.EventSource; import net.minecraft.network.chat.Component; import org.jetbrains.annotations.Nullable; @@ -31,7 +31,7 @@ import java.util.Optional; import java.util.Set; -import static dev.isxander.controlify.utils.ControllerUtils.wrapControllerError; +import static dev.isxander.controlify.impl.utils.ControllerUtils.wrapControllerError; /// Owns controller discovery, selection, activation and activity subscriptions. /// Publishes selection changes through {@link ControlifyEvents#CURRENT_CONTROLLER_CHANGED}. @@ -40,11 +40,11 @@ public class ControllerCoordinator implements AutoCloseable { private final ConfigManager config; private final SetupWizardManager setupWizardManager; private final ControllerManager controllerManager = new ControllerManager(); - private final Map inputSubscriptions = new HashMap<>(); - private final Set controllersWithInput = new LinkedHashSet<>(); + private final Map inputSubscriptions = new HashMap<>(); + private final Set controllersWithInput = new LinkedHashSet<>(); private @Nullable ControllerBackend backend; - private @Nullable ControllerEntity currentController; + private @Nullable ControllerImpl currentController; private boolean hasDiscoveredControllers; private boolean closed; @@ -78,7 +78,7 @@ public ControllerManager controllerManager() { return this.controllerManager; } - public Optional currentController() { + public Optional currentController() { return Optional.ofNullable(this.currentController); } @@ -129,7 +129,7 @@ public boolean update(boolean outOfFocus) { return !outOfFocus && this.controllersWithInput.contains(this.currentController); } - public void setCurrentController(@Nullable ControllerEntity controller, boolean changeInputMode) { + public void setCurrentController(@Nullable ControllerImpl controller, boolean changeInputMode) { if (this.closed || this.currentController == controller) { return; } @@ -149,7 +149,7 @@ public void setCurrentController(@Nullable ControllerEntity controller, boolean public void applyControllerSelection(boolean changeInputMode) { String selectedUid = this.config.getActiveProfile().controllerUid; - ControllerEntity selected = this.controllerManager.getAllControllers().stream() + ControllerImpl selected = this.controllerManager.getAllControllers().stream() .filter(controller -> selectedUid == null || selectedUid.equals(controller.uid())) .findFirst().orElse(null); this.setCurrentController(selected, changeInputMode); @@ -161,7 +161,7 @@ public void applyActiveProfile() { this.applyControllerSelection(true); } - private void onControllerAdded(ControllerEntity controller, boolean hotplugged) { + private void onControllerAdded(ControllerImpl controller, boolean hotplugged) { if (this.closed) { return; } @@ -178,7 +178,7 @@ private void onControllerAdded(ControllerEntity controller, boolean hotplugged) DeviceSettings rememberedDevice = this.config.getSettings().getOrCreateDeviceSettings(controller.uid()); rememberedDevice.name = controller.name(); rememberedDevice.lastSeen = System.currentTimeMillis(); - rememberedDevice.controllerType = controller.info().type().namespace(); + rememberedDevice.controllerType = controller.info().type().id(); this.config.markDirty(); String selectedUid = this.config.getActiveProfile().controllerUid; @@ -205,7 +205,7 @@ private void onControllerAdded(ControllerEntity controller, boolean hotplugged) this.setupWizardManager.push(wizard); } - private void onControllerRemoved(ControllerEntity controller) { + private void onControllerRemoved(ControllerImpl controller) { if (this.closed) { return; } @@ -215,7 +215,7 @@ private void onControllerRemoved(ControllerEntity controller) { } this.controllersWithInput.remove(controller); if (this.currentController == controller) { - ControllerEntity fallback = this.config.getActiveProfile().controllerUid == null + ControllerImpl fallback = this.config.getActiveProfile().controllerUid == null ? this.controllerManager.getAllControllers().stream() .filter(candidate -> candidate != controller).findFirst().orElse(null) : null; diff --git a/src/main/java/dev/isxander/controlify/controllermanager/ControllerManager.java b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerManager.java similarity index 66% rename from src/main/java/dev/isxander/controlify/controllermanager/ControllerManager.java rename to src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerManager.java index 46b6e8161..7b5ee0ca8 100644 --- a/src/main/java/dev/isxander/controlify/controllermanager/ControllerManager.java +++ b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerManager.java @@ -1,20 +1,19 @@ -package dev.isxander.controlify.controllermanager; +package dev.isxander.controlify.impl.controllermanager; -import dev.isxander.controlify.api.event.ControlifyEvents; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.id.ControllerType; -import dev.isxander.controlify.driver.steamdeck.SteamDeckUtil; -import dev.isxander.controlify.hid.HIDID; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.ControllerUtils; -import dev.isxander.controlify.utils.log.ControlifyLogger; +import dev.isxander.controlify.api.ControlifyEvents; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.driver.steamdeck.SteamDeckUtil; +import dev.isxander.controlify.impl.hid.HIDID; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.ControllerUtils; +import dev.isxander.controlify.impl.utils.log.ControlifyLogger; import java.io.Closeable; import java.util.*; public class ControllerManager implements Closeable { - private final Map controllersByUid; - private final Map controllersByUcid; + private final Map controllersByUid; + private final Map controllersByUcid; public ControllerManager() { this.controllersByUid = new HashMap<>(); @@ -47,7 +46,7 @@ public boolean canAddController( return true; } - public void addController(ControllerEntity controller, boolean hotplugged) { + public void addController(ControllerImpl controller, boolean hotplugged) { this.controllersByUcid.put(controller.info().ucid(), controller); this.controllersByUid.put(controller.uid(), controller); @@ -61,7 +60,7 @@ public void addController(ControllerEntity controller, boolean hotplugged) { } public void removeController(UniqueControllerID controllerUcid) { - ControllerEntity controller = this.controllersByUcid.get(controllerUcid); + ControllerImpl controller = this.controllersByUcid.get(controllerUcid); ControlifyEvents.CONTROLLER_DISCONNECTED.invoke(new ControlifyEvents.ControllerDisconnected(controller)); @@ -75,20 +74,20 @@ public void removeController(UniqueControllerID controllerUcid) { controller.close(); } - public Optional getControllerByUcid(UniqueControllerID ucid) { + public Optional getControllerByUcid(UniqueControllerID ucid) { return Optional.ofNullable(this.controllersByUcid.get(ucid)); } - public Optional getControllerByUid(String uid) { + public Optional getControllerByUid(String uid) { return Optional.ofNullable(this.controllersByUid.get(uid)); } - public Collection getAllControllers() { + public Collection getAllControllers() { return Collections.unmodifiableCollection(this.controllersByUcid.values()); } @Override public void close() { - this.controllersByUcid.values().forEach(ControllerEntity::close); + this.controllersByUcid.values().forEach(ControllerImpl::close); } } diff --git a/src/main/java/dev/isxander/controlify/controller/id/ControllerType.java b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerType.java similarity index 73% rename from src/main/java/dev/isxander/controlify/controller/id/ControllerType.java rename to src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerType.java index 4b0d01146..b7a190313 100644 --- a/src/main/java/dev/isxander/controlify/controller/id/ControllerType.java +++ b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerType.java @@ -4,19 +4,19 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.id; +package dev.isxander.controlify.impl.controllermanager; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.driver.steamdeck.SteamDeckUtil; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.codec.CExtraCodecs; +import dev.isxander.controlify.impl.driver.steamdeck.SteamDeckUtil; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.codec.CExtraCodecs; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; public record ControllerType( - Identifier namespace, + Identifier id, @Nullable String friendlyName, @Nullable Identifier mappingId, boolean forceJoystick, @@ -25,7 +25,7 @@ public record ControllerType( public static final ControllerType DEFAULT = new ControllerType(CUtil.rl("default"), null, null, false, false); public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - Identifier.CODEC.optionalFieldOf("namespace", DEFAULT.namespace()).forGetter(ControllerType::namespace), + Identifier.CODEC.optionalFieldOf("namespace", DEFAULT.id()).forGetter(ControllerType::id), CExtraCodecs.nullableField(Codec.STRING, "name").forGetter(ControllerType::friendlyName), CExtraCodecs.nullableField(Identifier.CODEC, "mapping").forGetter(ControllerType::mappingId), Codec.BOOL.optionalFieldOf("force_joystick", false).forGetter(ControllerType::forceJoystick), @@ -33,10 +33,10 @@ public record ControllerType( ).apply(instance, ControllerType::new)); public Identifier getIconSprite() { - return namespace.withPrefix("controllers/"); + return id.withPrefix("controllers/"); } public boolean isSteamDeck() { - return namespace.equals(SteamDeckUtil.STEAM_DECK_NAMESPACE); + return id.equals(SteamDeckUtil.STEAM_DECK_NAMESPACE); } } diff --git a/src/main/java/dev/isxander/controlify/controller/id/ControllerTypeManager.java b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerTypeManager.java similarity index 87% rename from src/main/java/dev/isxander/controlify/controller/id/ControllerTypeManager.java rename to src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerTypeManager.java index 458afcf3f..ea6c3fa1b 100644 --- a/src/main/java/dev/isxander/controlify/controller/id/ControllerTypeManager.java +++ b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerTypeManager.java @@ -4,22 +4,21 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controller.id; +package dev.isxander.controlify.impl.controllermanager; import com.google.gson.JsonElement; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.JsonOps; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controllermanager.ControllerManager; -import dev.isxander.controlify.hid.ControllerHIDInfo; -import dev.isxander.controlify.hid.HIDDevice; -import dev.isxander.controlify.hid.HIDID; -import dev.isxander.controlify.platform.client.resource.SimpleControlifyReloadListener; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.JsonTreeParser; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.hid.ControllerHIDInfo; +import dev.isxander.controlify.impl.hid.HIDDevice; +import dev.isxander.controlify.impl.hid.HIDID; +import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.JsonTreeParser; import net.minecraft.util.Util; import net.minecraft.resources.Identifier; import net.minecraft.server.packs.resources.Resource; @@ -108,13 +107,13 @@ public void triggerFullTypeReload() { if (controllerManagerOpt.isPresent()) { ControllerManager controllerManager = controllerManagerOpt.get(); - for (ControllerEntity controller : controllerManager.getConnectedControllers()) { + for (ControllerImpl controller : controllerManager.getAllControllers()) { reloadTypeForController(controllerManager, controller); } } } - public void reloadTypeForController(ControllerManager controllerManager, ControllerEntity controller) { + public void reloadTypeForController(ControllerManager controllerManager, ControllerImpl controller) { Optional hidOpt = controller.info().hid(); if (hidOpt.isEmpty()) return; diff --git a/src/main/java/dev/isxander/controlify/controllermanager/SDLControllerBackend.java b/src/main/java/dev/isxander/controlify/impl/controllermanager/SDLControllerBackend.java similarity index 88% rename from src/main/java/dev/isxander/controlify/controllermanager/SDLControllerBackend.java rename to src/main/java/dev/isxander/controlify/impl/controllermanager/SDLControllerBackend.java index fc0d497a1..7393070c3 100644 --- a/src/main/java/dev/isxander/controlify/controllermanager/SDLControllerBackend.java +++ b/src/main/java/dev/isxander/controlify/impl/controllermanager/SDLControllerBackend.java @@ -1,18 +1,17 @@ -package dev.isxander.controlify.controllermanager; - -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.config.settings.profile.ProfileSettings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.id.ControllerType; -import dev.isxander.controlify.controller.info.ControllerInfo; -import dev.isxander.controlify.debug.DebugProperties; -import dev.isxander.controlify.driver.Driver; -import dev.isxander.controlify.driver.sdl.*; -import dev.isxander.controlify.hid.HIDID; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.event.EventSource; -import dev.isxander.controlify.utils.event.EventTap; -import dev.isxander.controlify.utils.log.ControlifyLogger; +package dev.isxander.controlify.impl.controllermanager; + +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controller.ControllerInfo; +import dev.isxander.controlify.impl.debug.DebugProperties; +import dev.isxander.controlify.impl.driver.Driver; +import dev.isxander.controlify.impl.driver.sdl.*; +import dev.isxander.controlify.impl.hid.HIDID; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.event.EventSource; +import dev.isxander.controlify.impl.utils.event.EventTap; +import dev.isxander.controlify.impl.utils.log.ControlifyLogger; import dev.isxander.sdl.*; import net.minecraft.client.Minecraft; import net.minecraft.server.packs.resources.Resource; @@ -165,7 +164,7 @@ private void createAndAddController( } var controllerInfo = new ControllerInfo(ucid, type, hidId); - var controller = new ControllerEntity( + var controller = new ControllerImpl( controllerInfo, driver, Controlify.instance().config().getActiveProfile(), diff --git a/src/main/java/dev/isxander/controlify/controllermanager/SDLUniqueControllerID.java b/src/main/java/dev/isxander/controlify/impl/controllermanager/SDLUniqueControllerID.java similarity index 90% rename from src/main/java/dev/isxander/controlify/controllermanager/SDLUniqueControllerID.java rename to src/main/java/dev/isxander/controlify/impl/controllermanager/SDLUniqueControllerID.java index f888c57ef..002e3e94c 100644 --- a/src/main/java/dev/isxander/controlify/controllermanager/SDLUniqueControllerID.java +++ b/src/main/java/dev/isxander/controlify/impl/controllermanager/SDLUniqueControllerID.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.controllermanager; +package dev.isxander.controlify.impl.controllermanager; import dev.isxander.sdl.SdlJoystickId; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/dev/isxander/controlify/controllermanager/UniqueControllerID.java b/src/main/java/dev/isxander/controlify/impl/controllermanager/UniqueControllerID.java similarity index 88% rename from src/main/java/dev/isxander/controlify/controllermanager/UniqueControllerID.java rename to src/main/java/dev/isxander/controlify/impl/controllermanager/UniqueControllerID.java index 99886cb70..05284e8fd 100644 --- a/src/main/java/dev/isxander/controlify/controllermanager/UniqueControllerID.java +++ b/src/main/java/dev/isxander/controlify/impl/controllermanager/UniqueControllerID.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.controllermanager; +package dev.isxander.controlify.impl.controllermanager; /** * A unique, abstract identifier for a controller. diff --git a/src/main/java/dev/isxander/controlify/debug/DebugProperties.java b/src/main/java/dev/isxander/controlify/impl/debug/DebugProperties.java similarity index 95% rename from src/main/java/dev/isxander/controlify/debug/DebugProperties.java rename to src/main/java/dev/isxander/controlify/impl/debug/DebugProperties.java index b33753310..87209d97f 100644 --- a/src/main/java/dev/isxander/controlify/debug/DebugProperties.java +++ b/src/main/java/dev/isxander/controlify/impl/debug/DebugProperties.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.debug; +package dev.isxander.controlify.impl.debug; -import dev.isxander.controlify.platform.main.PlatformMainUtil; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; +import dev.isxander.controlify.impl.utils.CUtil; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; diff --git a/src/main/java/dev/isxander/controlify/driver/ComponentAdderDriver.java b/src/main/java/dev/isxander/controlify/impl/driver/ComponentAdderDriver.java similarity index 51% rename from src/main/java/dev/isxander/controlify/driver/ComponentAdderDriver.java rename to src/main/java/dev/isxander/controlify/impl/driver/ComponentAdderDriver.java index b1a87c928..49fb63d81 100644 --- a/src/main/java/dev/isxander/controlify/driver/ComponentAdderDriver.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/ComponentAdderDriver.java @@ -4,21 +4,21 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.driver; +package dev.isxander.controlify.impl.driver; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.impl.controller.ControllerImpl; import java.util.function.Consumer; public class ComponentAdderDriver implements Driver { - private final Consumer componentAdder; + private final Consumer componentAdder; - public ComponentAdderDriver(Consumer componentAdder) { + public ComponentAdderDriver(Consumer componentAdder) { this.componentAdder = componentAdder; } @Override - public void addComponents(ControllerEntity controller) { + public void addComponents(ControllerImpl controller) { componentAdder.accept(controller); } } diff --git a/src/main/java/dev/isxander/controlify/driver/CompoundDriver.java b/src/main/java/dev/isxander/controlify/impl/driver/CompoundDriver.java similarity index 77% rename from src/main/java/dev/isxander/controlify/driver/CompoundDriver.java rename to src/main/java/dev/isxander/controlify/impl/driver/CompoundDriver.java index 5175847a9..460cc2b71 100644 --- a/src/main/java/dev/isxander/controlify/driver/CompoundDriver.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/CompoundDriver.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.driver; +package dev.isxander.controlify.impl.driver; import com.google.common.collect.Lists; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.impl.controller.ControllerImpl; import java.util.List; import java.util.stream.Collectors; @@ -20,16 +20,16 @@ public CompoundDriver(List drivers) { } @Override - public void addComponents(ControllerEntity controller) { + public void addComponents(ControllerImpl controller) { for (Driver driver : Lists.reverse(drivers)) { driver.addComponents(controller); } } @Override - public void update(ControllerEntity controller, boolean outOfFocus) { + public void update(ControllerImpl controller, boolean outOfFocus) { for (Driver driver : drivers) { - driver.update(controller, outOfFocus); + driver.update(controller); } } diff --git a/src/main/java/dev/isxander/controlify/driver/Driver.java b/src/main/java/dev/isxander/controlify/impl/driver/Driver.java similarity index 62% rename from src/main/java/dev/isxander/controlify/driver/Driver.java rename to src/main/java/dev/isxander/controlify/impl/driver/Driver.java index 3f4e0c43f..a58727298 100644 --- a/src/main/java/dev/isxander/controlify/driver/Driver.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/Driver.java @@ -4,14 +4,14 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.driver; +package dev.isxander.controlify.impl.driver; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.impl.controller.ControllerImpl; public interface Driver { - default void addComponents(ControllerEntity controller) {} + default void addComponents(ControllerImpl controller) {} - default void update(ControllerEntity controller, boolean outOfFocus) {} + default void update(ControllerImpl controller) {} /// Enables resources needed only by the active controller, such as sensor sampling. /// Buttons and axes must remain available while inactive for controller selection. diff --git a/src/main/java/dev/isxander/controlify/driver/dualsense/DualsenseEffectsState.java b/src/main/java/dev/isxander/controlify/impl/driver/dualsense/DualsenseEffectsState.java similarity index 99% rename from src/main/java/dev/isxander/controlify/driver/dualsense/DualsenseEffectsState.java rename to src/main/java/dev/isxander/controlify/impl/driver/dualsense/DualsenseEffectsState.java index 48d02c485..9fdd683a4 100644 --- a/src/main/java/dev/isxander/controlify/driver/dualsense/DualsenseEffectsState.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/dualsense/DualsenseEffectsState.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.driver.dualsense; +package dev.isxander.controlify.impl.driver.dualsense; import org.intellij.lang.annotations.MagicConstant; diff --git a/src/main/java/dev/isxander/controlify/driver/dualsense/DualsenseTriggerEffect.java b/src/main/java/dev/isxander/controlify/impl/driver/dualsense/DualsenseTriggerEffect.java similarity index 99% rename from src/main/java/dev/isxander/controlify/driver/dualsense/DualsenseTriggerEffect.java rename to src/main/java/dev/isxander/controlify/impl/driver/dualsense/DualsenseTriggerEffect.java index 33edb3e73..b825d9664 100644 --- a/src/main/java/dev/isxander/controlify/driver/dualsense/DualsenseTriggerEffect.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/dualsense/DualsenseTriggerEffect.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.driver.dualsense; +package dev.isxander.controlify.impl.driver.dualsense; import org.apache.commons.lang3.Validate; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/dev/isxander/controlify/driver/dualsense/DualsenseTriggerEffectTypes.java b/src/main/java/dev/isxander/controlify/impl/driver/dualsense/DualsenseTriggerEffectTypes.java similarity index 96% rename from src/main/java/dev/isxander/controlify/driver/dualsense/DualsenseTriggerEffectTypes.java rename to src/main/java/dev/isxander/controlify/impl/driver/dualsense/DualsenseTriggerEffectTypes.java index 9fc0680a7..01a77b78e 100644 --- a/src/main/java/dev/isxander/controlify/driver/dualsense/DualsenseTriggerEffectTypes.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/dualsense/DualsenseTriggerEffectTypes.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.driver.dualsense; +package dev.isxander.controlify.impl.driver.dualsense; public final class DualsenseTriggerEffectTypes { diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/DecodedGUID.java b/src/main/java/dev/isxander/controlify/impl/driver/sdl/DecodedGUID.java similarity index 98% rename from src/main/java/dev/isxander/controlify/driver/sdl/DecodedGUID.java rename to src/main/java/dev/isxander/controlify/impl/driver/sdl/DecodedGUID.java index 1b11e4ab7..a66b07ce1 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/DecodedGUID.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/sdl/DecodedGUID.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.driver.sdl; +package dev.isxander.controlify.impl.driver.sdl; import dev.isxander.sdl.SdlGuid; import org.apache.commons.lang3.Validate; diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDL3GamepadDriver.java b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDL3GamepadDriver.java similarity index 89% rename from src/main/java/dev/isxander/controlify/driver/sdl/SDL3GamepadDriver.java rename to src/main/java/dev/isxander/controlify/impl/driver/sdl/SDL3GamepadDriver.java index 7f2ae3f30..16ca790ab 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDL3GamepadDriver.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDL3GamepadDriver.java @@ -4,17 +4,16 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.driver.sdl; - -import dev.isxander.controlify.controller.id.ControllerType; -import dev.isxander.controlify.controller.touchpad.TouchpadComponent; -import dev.isxander.controlify.controller.gyro.GyroComponent; -import dev.isxander.controlify.controller.*; -import dev.isxander.controlify.controller.touchpad.Touchpads; -import dev.isxander.controlify.controller.input.InputComponent; -import dev.isxander.controlify.input.SensorType; -import dev.isxander.controlify.utils.event.EventSource; -import dev.isxander.controlify.utils.log.ControlifyLogger; +package dev.isxander.controlify.impl.driver.sdl; + +import dev.isxander.controlify.impl.controllermanager.ControllerType; +import dev.isxander.controlify.impl.controller.touchpad.TouchpadComponent; +import dev.isxander.controlify.impl.controller.gyro.GyroComponent; +import dev.isxander.controlify.impl.controller.touchpad.Touchpads; +import dev.isxander.controlify.impl.controller.input.InputComponent; +import dev.isxander.controlify.impl.input.SensorType; +import dev.isxander.controlify.impl.utils.event.EventSource; +import dev.isxander.controlify.impl.utils.log.ControlifyLogger; import dev.isxander.sdl.*; import net.minecraft.resources.Identifier; @@ -63,12 +62,12 @@ public SDL3GamepadDriver(Sdl sdl, SdlGamepadHandle ptrController, EventSource implements Driver { private EventSource.Subscription eventSubscription; protected BatteryLevelComponent batteryLevelComponent; - protected RumbleComponent rumbleComponent; + protected RumbleComponentImpl rumbleComponent; protected TriggerRumbleComponent triggerRumbleComponent; protected HDHapticComponent hdHapticComponent; protected LEDComponent ledComponent; @@ -128,7 +128,7 @@ public SDLCommonDriver(Sdl sdl, SdlController ptrController, SdlJoystickId jid, // open audio device for dualsense hd haptics this.dualsenseAudioHandles = new ArrayList<>(); - this.isDualsense = CUtil.rl("dualsense").equals(type.namespace()); + this.isDualsense = CUtil.rl("dualsense").equals(type.id()); if (this.isDualsense) { logger.debugLog("DualSense controller detected."); @@ -160,14 +160,14 @@ public SDLCommonDriver(Sdl sdl, SdlController ptrController, SdlJoystickId jid, } @Override - public void addComponents(ControllerEntity controller) { + public void addComponents(ControllerImpl controller) { controller.setComponent(new DriverNameComponent(this.name)); controller.setComponent(new GUIDComponent(this.guidString)); controller.setComponent(new UIDComponent(createUid())); controller.setComponent(this.batteryLevelComponent = new BatteryLevelComponent()); if (this.isRumbleSupported) { - controller.setComponent(this.rumbleComponent = new RumbleComponent()); + controller.setComponent(this.rumbleComponent = new RumbleComponentImpl()); } if (this.isTriggerRumbleSupported) { controller.setComponent(this.triggerRumbleComponent = new TriggerRumbleComponent()); @@ -214,7 +214,7 @@ private void onBatteryEvent(SDLControllerEvent.Battery event) { } @Override - public void update(ControllerEntity controller, boolean outOfFocus) { + public void update(ControllerImpl controller) { if (ptrController == null) { throw new IllegalStateException("Tried to update controller when it's closed."); } diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLControllerEvent.java b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLControllerEvent.java similarity index 99% rename from src/main/java/dev/isxander/controlify/driver/sdl/SDLControllerEvent.java rename to src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLControllerEvent.java index b1d763393..944ee9898 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDLControllerEvent.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLControllerEvent.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.driver.sdl; +package dev.isxander.controlify.impl.driver.sdl; import dev.isxander.sdl.SdlJoystickId; diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLControllerEventStage.java b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLControllerEventStage.java similarity index 93% rename from src/main/java/dev/isxander/controlify/driver/sdl/SDLControllerEventStage.java rename to src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLControllerEventStage.java index dc862b7a9..cd0c73c93 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDLControllerEventStage.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLControllerEventStage.java @@ -1,7 +1,7 @@ -package dev.isxander.controlify.driver.sdl; +package dev.isxander.controlify.impl.driver.sdl; -import dev.isxander.controlify.utils.event.EventSink; -import dev.isxander.controlify.utils.event.EventStage; +import dev.isxander.controlify.impl.utils.event.EventSink; +import dev.isxander.controlify.impl.utils.event.EventStage; import dev.isxander.sdl.*; public class SDLControllerEventStage implements EventStage { diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLException.java b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLException.java similarity index 91% rename from src/main/java/dev/isxander/controlify/driver/sdl/SDLException.java rename to src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLException.java index c416b6c90..06b9b90a8 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDLException.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLException.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.driver.sdl; +package dev.isxander.controlify.impl.driver.sdl; import dev.isxander.sdl.Sdl; diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLGamepadInputMapping.java b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLGamepadInputMapping.java similarity index 90% rename from src/main/java/dev/isxander/controlify/driver/sdl/SDLGamepadInputMapping.java rename to src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLGamepadInputMapping.java index e7dad49a1..7004736dc 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDLGamepadInputMapping.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLGamepadInputMapping.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.driver.sdl; +package dev.isxander.controlify.impl.driver.sdl; -import dev.isxander.controlify.controller.impl.ControllerStateImpl; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.controller.impl.ControllerStateImpl; +import dev.isxander.controlify.impl.utils.CUtil; import dev.isxander.sdl.SdlGamepad; import dev.isxander.sdl.SdlGamepadHandle; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLInputConversions.java b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLInputConversions.java similarity index 97% rename from src/main/java/dev/isxander/controlify/driver/sdl/SDLInputConversions.java rename to src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLInputConversions.java index dda85452f..033fb493e 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDLInputConversions.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLInputConversions.java @@ -1,7 +1,7 @@ -package dev.isxander.controlify.driver.sdl; +package dev.isxander.controlify.impl.driver.sdl; -import dev.isxander.controlify.input.mapping.GamepadInputs; -import dev.isxander.controlify.input.SensorType; +import dev.isxander.controlify.impl.input.mapping.GamepadInputs; +import dev.isxander.controlify.impl.input.SensorType; import dev.isxander.sdl.SdlEvents; import dev.isxander.sdl.SdlGamepad; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLJoystickConnectionState.java b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLJoystickConnectionState.java similarity index 92% rename from src/main/java/dev/isxander/controlify/driver/sdl/SDLJoystickConnectionState.java rename to src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLJoystickConnectionState.java index 3b445b171..cb1b2a912 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDLJoystickConnectionState.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLJoystickConnectionState.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.driver.sdl; +package dev.isxander.controlify.impl.driver.sdl; public enum SDLJoystickConnectionState { INVALID(-1), diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLNativesLoader.java b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLNativesLoader.java similarity index 95% rename from src/main/java/dev/isxander/controlify/driver/sdl/SDLNativesLoader.java rename to src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLNativesLoader.java index 6524f9184..b2b83498d 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDLNativesLoader.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLNativesLoader.java @@ -4,12 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.driver.sdl; +package dev.isxander.controlify.impl.driver.sdl; -import dev.isxander.controlify.platform.main.PlatformMainUtil; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.Platform; -import dev.isxander.controlify.utils.log.ControlifyLogger; +import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.Platform; +import dev.isxander.controlify.impl.utils.log.ControlifyLogger; import dev.isxander.sdl.Sdl; import dev.isxander.sdl.SdlLoader; import dev.isxander.sdl.SdlVersion; diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLToPhysicalStage.java b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLToPhysicalStage.java similarity index 86% rename from src/main/java/dev/isxander/controlify/driver/sdl/SDLToPhysicalStage.java rename to src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLToPhysicalStage.java index bf22dcbe5..da46ad9a0 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDLToPhysicalStage.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLToPhysicalStage.java @@ -1,12 +1,12 @@ -package dev.isxander.controlify.driver.sdl; - -import dev.isxander.controlify.input.mapping.JoystickInputs; -import dev.isxander.controlify.input.SensorType; -import dev.isxander.controlify.input.signal.PhysicalInputSignal; -import dev.isxander.controlify.input.signal.PhysicalSensorSignal; -import dev.isxander.controlify.input.signal.PhysicalSignal; -import dev.isxander.controlify.utils.event.EventSink; -import dev.isxander.controlify.utils.event.EventStage; +package dev.isxander.controlify.impl.driver.sdl; + +import dev.isxander.controlify.impl.input.mapping.JoystickInputs; +import dev.isxander.controlify.impl.input.SensorType; +import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; +import dev.isxander.controlify.impl.input.signal.PhysicalSensorSignal; +import dev.isxander.controlify.impl.input.signal.PhysicalSignal; +import dev.isxander.controlify.impl.utils.event.EventSink; +import dev.isxander.controlify.impl.utils.event.EventStage; import net.minecraft.resources.Identifier; import java.util.Arrays; diff --git a/src/main/java/dev/isxander/controlify/driver/sdl/SDLUtil.java b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLUtil.java similarity index 94% rename from src/main/java/dev/isxander/controlify/driver/sdl/SDLUtil.java rename to src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLUtil.java index 21ddc2a34..29214a77e 100644 --- a/src/main/java/dev/isxander/controlify/driver/sdl/SDLUtil.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLUtil.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.driver.sdl; +package dev.isxander.controlify.impl.driver.sdl; import dev.isxander.sdl.Sdl; import dev.isxander.sdl.SdlGamepadHandle; diff --git a/src/main/java/dev/isxander/controlify/driver/steamdeck/CEFDebuggerFileResult.java b/src/main/java/dev/isxander/controlify/impl/driver/steamdeck/CEFDebuggerFileResult.java similarity index 93% rename from src/main/java/dev/isxander/controlify/driver/steamdeck/CEFDebuggerFileResult.java rename to src/main/java/dev/isxander/controlify/impl/driver/steamdeck/CEFDebuggerFileResult.java index 960b2ac2f..722e460aa 100644 --- a/src/main/java/dev/isxander/controlify/driver/steamdeck/CEFDebuggerFileResult.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/steamdeck/CEFDebuggerFileResult.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.driver.steamdeck; +package dev.isxander.controlify.impl.driver.steamdeck; public enum CEFDebuggerFileResult { /** The file has just been created, but requires steam to restart to take effect **/ diff --git a/src/main/java/dev/isxander/controlify/driver/steamdeck/SteamDeckDriver.java b/src/main/java/dev/isxander/controlify/impl/driver/steamdeck/SteamDeckDriver.java similarity index 87% rename from src/main/java/dev/isxander/controlify/driver/steamdeck/SteamDeckDriver.java rename to src/main/java/dev/isxander/controlify/impl/driver/steamdeck/SteamDeckDriver.java index 021e6fc3b..e88f7b966 100644 --- a/src/main/java/dev/isxander/controlify/driver/steamdeck/SteamDeckDriver.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/steamdeck/SteamDeckDriver.java @@ -4,26 +4,26 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.driver.steamdeck; - -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.battery.BatteryLevelComponent; -import dev.isxander.controlify.controller.battery.PowerState; -import dev.isxander.controlify.controller.gyro.GyroComponent; -import dev.isxander.controlify.controller.gyro.GyroState; -import dev.isxander.controlify.controller.impl.ControllerStateImpl; -import dev.isxander.controlify.controller.info.DriverNameComponent; -import dev.isxander.controlify.controller.info.GUIDComponent; -import dev.isxander.controlify.controller.info.UIDComponent; -import dev.isxander.controlify.input.mapping.GamepadInputs; -import dev.isxander.controlify.controller.input.InputComponent; -import dev.isxander.controlify.controller.steamdeck.SteamDeckComponent; -import dev.isxander.controlify.controller.touchpad.TouchpadComponent; -import dev.isxander.controlify.controller.touchpad.Touchpads; -import dev.isxander.controlify.driver.Driver; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.log.ControlifyLogger; +package dev.isxander.controlify.impl.driver.steamdeck; + +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controller.battery.BatteryLevelComponent; +import dev.isxander.controlify.impl.controller.battery.PowerState; +import dev.isxander.controlify.impl.controller.gyro.GyroComponent; +import dev.isxander.controlify.impl.controller.gyro.GyroState; +import dev.isxander.controlify.impl.controller.impl.ControllerStateImpl; +import dev.isxander.controlify.impl.controller.misc.DriverNameComponent; +import dev.isxander.controlify.impl.controller.misc.GUIDComponent; +import dev.isxander.controlify.impl.controller.misc.UIDComponent; +import dev.isxander.controlify.impl.input.mapping.GamepadInputs; +import dev.isxander.controlify.impl.controller.input.InputComponent; +import dev.isxander.controlify.impl.controller.steamdeck.SteamDeckComponent; +import dev.isxander.controlify.impl.controller.touchpad.TouchpadComponent; +import dev.isxander.controlify.impl.controller.touchpad.Touchpads; +import dev.isxander.controlify.impl.driver.Driver; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.log.ControlifyLogger; import dev.isxander.deckapi.api.ControllerButton; import dev.isxander.deckapi.api.ControllerState; import dev.isxander.deckapi.api.SteamDeck; @@ -36,7 +36,7 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; -import static dev.isxander.controlify.utils.CUtil.*; +import static dev.isxander.controlify.impl.utils.CUtil.*; public class SteamDeckDriver implements Driver { private static SteamDeck deck; @@ -56,7 +56,7 @@ public SteamDeckDriver(ControlifyLogger logger) { } @Override - public void addComponents(ControllerEntity controller) { + public void addComponents(ControllerImpl controller) { controller.setComponent(new DriverNameComponent("Steam Deck")); controller.setComponent(new GUIDComponent("steamdeck")); controller.setComponent(new UIDComponent(CUtil.createUIDFromBytes("steamdeck".getBytes()))); @@ -123,7 +123,7 @@ private void ensurePolling() { } @Override - public void update(ControllerEntity controller, boolean outOfFocus) { + public void update(ControllerImpl controller, boolean outOfFocus) { ensurePolling(); ControllerStateImpl state = new ControllerStateImpl(); diff --git a/src/main/java/dev/isxander/controlify/driver/steamdeck/SteamDeckMode.java b/src/main/java/dev/isxander/controlify/impl/driver/steamdeck/SteamDeckMode.java similarity index 87% rename from src/main/java/dev/isxander/controlify/driver/steamdeck/SteamDeckMode.java rename to src/main/java/dev/isxander/controlify/impl/driver/steamdeck/SteamDeckMode.java index 9de0fc616..bc671aa5c 100644 --- a/src/main/java/dev/isxander/controlify/driver/steamdeck/SteamDeckMode.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/steamdeck/SteamDeckMode.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.driver.steamdeck; +package dev.isxander.controlify.impl.driver.steamdeck; public enum SteamDeckMode { GAMING_MODE, diff --git a/src/main/java/dev/isxander/controlify/driver/steamdeck/SteamDeckUtil.java b/src/main/java/dev/isxander/controlify/impl/driver/steamdeck/SteamDeckUtil.java similarity index 94% rename from src/main/java/dev/isxander/controlify/driver/steamdeck/SteamDeckUtil.java rename to src/main/java/dev/isxander/controlify/impl/driver/steamdeck/SteamDeckUtil.java index 010e05184..3c7c65033 100644 --- a/src/main/java/dev/isxander/controlify/driver/steamdeck/SteamDeckUtil.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/steamdeck/SteamDeckUtil.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.driver.steamdeck; +package dev.isxander.controlify.impl.driver.steamdeck; -import dev.isxander.controlify.debug.DebugProperties; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.log.ControlifyLogger; +import dev.isxander.controlify.impl.debug.DebugProperties; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.log.ControlifyLogger; import dev.isxander.deckapi.api.SteamDeck; import dev.isxander.deckapi.api.SteamDeckException; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/font/BindingFontHelper.java b/src/main/java/dev/isxander/controlify/impl/font/BindingFontHelper.java similarity index 94% rename from src/main/java/dev/isxander/controlify/font/BindingFontHelper.java rename to src/main/java/dev/isxander/controlify/impl/font/BindingFontHelper.java index 40d1e1eaf..5abb256ee 100644 --- a/src/main/java/dev/isxander/controlify/font/BindingFontHelper.java +++ b/src/main/java/dev/isxander/controlify/impl/font/BindingFontHelper.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.font; +package dev.isxander.controlify.impl.font; -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.client.gui.Font; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.FontDescription; diff --git a/src/main/java/dev/isxander/controlify/font/InputFontMapper.java b/src/main/java/dev/isxander/controlify/impl/font/InputFontMapper.java similarity index 91% rename from src/main/java/dev/isxander/controlify/font/InputFontMapper.java rename to src/main/java/dev/isxander/controlify/impl/font/InputFontMapper.java index d7d4322ad..4a17525eb 100644 --- a/src/main/java/dev/isxander/controlify/font/InputFontMapper.java +++ b/src/main/java/dev/isxander/controlify/impl/font/InputFontMapper.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.font; +package dev.isxander.controlify.impl.font; import com.google.common.collect.ImmutableMap; import com.google.gson.JsonElement; @@ -13,11 +13,11 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.JsonOps; -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.bindings.input.Input; -import dev.isxander.controlify.controller.id.ControllerType; -import dev.isxander.controlify.platform.client.resource.SimpleControlifyReloadListener; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.impl.bindings.input.Input; +import dev.isxander.controlify.impl.controllermanager.ControllerType; +import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.FontDescription; import net.minecraft.network.chat.MutableComponent; @@ -54,7 +54,7 @@ public class InputFontMapper implements SimpleControlifyReloadListener load(ResourceManager manager, Executor executor) { @@ -75,7 +75,7 @@ public CompletableFuture load(ResourceManager manager, Executor ex return Stream.of(Pair.of(namespace, map)); } } catch (Exception e) { - CUtil.LOGGER.error("Failed to load font mappings for namespace {}", namespace, e); + CUtil.LOGGER.error("Failed to load font mappings for id {}", namespace, e); } return Stream.empty(); @@ -91,7 +91,7 @@ public CompletableFuture apply(Preparations data, ResourceManager manager, ImmutableMap.Builder builder = ImmutableMap.builder(); data.mappings().forEach(builder::put); mappings = builder.build(); - defaultFontMap = mappings.get(ControllerType.DEFAULT.namespace()); + defaultFontMap = mappings.get(ControllerType.DEFAULT.id()); }, executor); } diff --git a/src/main/java/dev/isxander/controlify/gui/ButtonGuideRenderer.java b/src/main/java/dev/isxander/controlify/impl/gui/ButtonGuideRenderer.java similarity index 87% rename from src/main/java/dev/isxander/controlify/gui/ButtonGuideRenderer.java rename to src/main/java/dev/isxander/controlify/impl/gui/ButtonGuideRenderer.java index ac55002a1..88d675032 100644 --- a/src/main/java/dev/isxander/controlify/gui/ButtonGuideRenderer.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/ButtonGuideRenderer.java @@ -4,12 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui; +package dev.isxander.controlify.impl.gui; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.action.Action; -import dev.isxander.controlify.action.ActionAccessor; -import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.api.action.Action; +import dev.isxander.controlify.impl.action.ActionAccessor; +import dev.isxander.controlify.api.gui.ButtonGuidePredicate; import net.minecraft.client.Minecraft; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; diff --git a/src/main/java/dev/isxander/controlify/gui/components/FakePositionPlainTextButton.java b/src/main/java/dev/isxander/controlify/impl/gui/components/FakePositionPlainTextButton.java similarity index 95% rename from src/main/java/dev/isxander/controlify/gui/components/FakePositionPlainTextButton.java rename to src/main/java/dev/isxander/controlify/impl/gui/components/FakePositionPlainTextButton.java index 4bb2e35d9..22bc4399a 100644 --- a/src/main/java/dev/isxander/controlify/gui/components/FakePositionPlainTextButton.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/components/FakePositionPlainTextButton.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.components; +package dev.isxander.controlify.impl.gui.components; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.components.PlainTextButton; diff --git a/src/main/java/dev/isxander/controlify/gui/components/PlainTextWidget.java b/src/main/java/dev/isxander/controlify/impl/gui/components/PlainTextWidget.java similarity index 96% rename from src/main/java/dev/isxander/controlify/gui/components/PlainTextWidget.java rename to src/main/java/dev/isxander/controlify/impl/gui/components/PlainTextWidget.java index c4d806498..b6e7cee13 100644 --- a/src/main/java/dev/isxander/controlify/gui/components/PlainTextWidget.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/components/PlainTextWidget.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.components; +package dev.isxander.controlify.impl.gui.components; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.ComponentPath; diff --git a/src/main/java/dev/isxander/controlify/gui/controllers/BindController.java b/src/main/java/dev/isxander/controlify/impl/gui/controllers/BindController.java similarity index 82% rename from src/main/java/dev/isxander/controlify/gui/controllers/BindController.java rename to src/main/java/dev/isxander/controlify/impl/gui/controllers/BindController.java index fe076895f..4d0afc7c6 100644 --- a/src/main/java/dev/isxander/controlify/gui/controllers/BindController.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/controllers/BindController.java @@ -4,20 +4,19 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.controllers; +package dev.isxander.controlify.impl.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.*; -import dev.isxander.controlify.controller.*; -import dev.isxander.controlify.controller.input.ControllerStateView; -import dev.isxander.controlify.controller.input.HatState; -import dev.isxander.controlify.controller.input.InputComponent; -import dev.isxander.controlify.gui.screen.BindConsumerScreen; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.bindings.input.*; +import dev.isxander.controlify.impl.controller.input.ControllerStateView; +import dev.isxander.controlify.impl.controller.input.HatState; +import dev.isxander.controlify.impl.controller.input.InputComponent; +import dev.isxander.controlify.impl.gui.screen.BindConsumerScreen; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import dev.isxander.yacl3.api.Controller; import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.utils.Dimension; @@ -34,10 +33,10 @@ public class BindController implements Controller { private final Option option; - public final ControllerEntity controller; + public final ControllerImpl controller; private boolean conflicting; - public BindController(Option option, ControllerEntity controller) { + public BindController(Option option, ControllerImpl controller) { this.option = option; this.controller = controller; } @@ -82,7 +81,7 @@ protected void extractValueText(GuiGraphicsExtractor graphics, int mouseX, int m if (EmptyInput.equals(bind)) return; Component text = Controlify.instance().inputFontMapper() - .getComponentFromBind(control.controller.info().type().namespace(), bind); + .getComponentFromBind(control.controller.info().type().id(), bind); int width = textRenderer.width(text); graphics.text(textRenderer, text, getDimension().xLimit() - width - 1, (int)(getDimension().centerY() - textRenderer.lineHeight / 2f + 1), -1, false); @@ -115,7 +114,7 @@ private void openConsumerScreen() { } @Override - public boolean overrideControllerButtons(ScreenProcessor screen, ControllerEntity controller) { + public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { if (controller != control.controller) return true; if (ControlifyBindings.GUI_PRESS.on(controller).justPressed()) { @@ -137,7 +136,7 @@ protected int getUnhoveredControlWidth() { return textRenderer.width(awaitingText); Component text = Controlify.instance().inputFontMapper() - .getComponentFromBind(control.controller.info().type().namespace(), control.option().pendingValue()); + .getComponentFromBind(control.controller.info().type().id(), control.option().pendingValue()); return textRenderer.width(text); } diff --git a/src/main/java/dev/isxander/controlify/gui/controllers/Deadzone2DImageRenderer.java b/src/main/java/dev/isxander/controlify/impl/gui/controllers/Deadzone2DImageRenderer.java similarity index 90% rename from src/main/java/dev/isxander/controlify/gui/controllers/Deadzone2DImageRenderer.java rename to src/main/java/dev/isxander/controlify/impl/gui/controllers/Deadzone2DImageRenderer.java index 30c7eace6..8bd12aa78 100644 --- a/src/main/java/dev/isxander/controlify/gui/controllers/Deadzone2DImageRenderer.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/controllers/Deadzone2DImageRenderer.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.controllers; +package dev.isxander.controlify.impl.gui.controllers; -import dev.isxander.controlify.input.mapping.DeadzoneGroup; -import dev.isxander.controlify.controller.input.InputComponent; -import dev.isxander.controlify.utils.render.elements.CircleElementRenderState; +import dev.isxander.controlify.impl.input.mapping.DeadzoneGroup; +import dev.isxander.controlify.impl.controller.input.InputComponent; +import dev.isxander.controlify.impl.utils.render.elements.CircleElementRenderState; import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.gui.image.ImageRenderer; import net.minecraft.client.Minecraft; diff --git a/src/main/java/dev/isxander/controlify/gui/guide/GuideInstanceImpl.java b/src/main/java/dev/isxander/controlify/impl/gui/guide/GuideInstanceImpl.java similarity index 81% rename from src/main/java/dev/isxander/controlify/gui/guide/GuideInstanceImpl.java rename to src/main/java/dev/isxander/controlify/impl/gui/guide/GuideInstanceImpl.java index 1b0444259..d51f8cfc6 100644 --- a/src/main/java/dev/isxander/controlify/gui/guide/GuideInstanceImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/guide/GuideInstanceImpl.java @@ -4,23 +4,24 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.guide; +package dev.isxander.controlify.impl.gui.guide; -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.api.contextual.GuideLocation; +import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.impl.contextual.GuideLocation; import dev.isxander.controlify.api.contextual.Context; import dev.isxander.controlify.api.contextual.GuideInstance; -import dev.isxander.controlify.contextual.ContextualDomainImpl; -import dev.isxander.controlify.contextual.ContextualState; -import dev.isxander.controlify.contextual.GuideRule; -import dev.isxander.controlify.contextual.RuleEngine; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.font.BindingFontHelper; +import dev.isxander.controlify.impl.contextual.ContextualDomainImpl; +import dev.isxander.controlify.impl.contextual.ContextualState; +import dev.isxander.controlify.impl.contextual.GuideRule; +import dev.isxander.controlify.impl.contextual.RuleEngine; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.font.BindingFontHelper; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Renderable; import net.minecraft.network.chat.Component; +import org.jspecify.annotations.NonNull; import java.util.List; import java.util.function.Supplier; @@ -52,7 +53,7 @@ public void update(C context) { var leftBuilder = new PrecomputedLines.Builder(); var rightBuilder = new PrecomputedLines.Builder(); - ControllerEntity controller = context.controller(); + ControllerImpl controller = context.controller(); for (GuideRule guide : guides) { InputBinding binding = guide.binding().onOrNull(controller); @@ -91,7 +92,7 @@ public void update(C context) { } @Override - public void extractRenderState(GuiGraphicsExtractor graphics, boolean bottomAligned, boolean textContrast, int guiScale) { + public void extractRenderState(@NonNull GuiGraphicsExtractor graphics, boolean bottomAligned, boolean textContrast, int guiScale) { GuideRenderer.extractRenderState( graphics, this, diff --git a/src/main/java/dev/isxander/controlify/gui/guide/GuideRenderer.java b/src/main/java/dev/isxander/controlify/impl/gui/guide/GuideRenderer.java similarity index 98% rename from src/main/java/dev/isxander/controlify/gui/guide/GuideRenderer.java rename to src/main/java/dev/isxander/controlify/impl/gui/guide/GuideRenderer.java index d0864217d..aa3ab56c9 100644 --- a/src/main/java/dev/isxander/controlify/gui/guide/GuideRenderer.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/guide/GuideRenderer.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.guide; +package dev.isxander.controlify.impl.gui.guide; import com.google.common.collect.Lists; import com.mojang.blaze3d.platform.Window; diff --git a/src/main/java/dev/isxander/controlify/gui/guide/InGameButtonGuide.java b/src/main/java/dev/isxander/controlify/impl/gui/guide/InGameButtonGuide.java similarity index 75% rename from src/main/java/dev/isxander/controlify/gui/guide/InGameButtonGuide.java rename to src/main/java/dev/isxander/controlify/impl/gui/guide/InGameButtonGuide.java index e29e7182c..e0f72c38c 100644 --- a/src/main/java/dev/isxander/controlify/gui/guide/InGameButtonGuide.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/guide/InGameButtonGuide.java @@ -4,24 +4,23 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.guide; +package dev.isxander.controlify.impl.gui.guide; import dev.isxander.controlify.api.contextual.GuideInstance; -import dev.isxander.controlify.config.settings.profile.GenericControllerSettings; -import dev.isxander.controlify.contextual.ContextualDomains; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.impl.config.settings.profile.GenericControllerSettings; +import dev.isxander.controlify.impl.contextual.ContextualDomains; +import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.api.contextual.InGameContext; -import dev.isxander.controlify.mixins.feature.guide.ingame.MinecraftAccessor; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; public class InGameButtonGuide { - private final ControllerEntity controller; + private final ControllerImpl controller; private final Minecraft minecraft; private final GuideInstance guideInstance; - public InGameButtonGuide(ControllerEntity controller, Minecraft minecraft) { + public InGameButtonGuide(ControllerImpl controller, Minecraft minecraft) { this.controller = controller; this.minecraft = minecraft; this.guideInstance = ContextualDomains.INSTANCE.inGame().createGuideInstance(minecraft.font); diff --git a/src/main/java/dev/isxander/controlify/gui/guide/PrecomputedLines.java b/src/main/java/dev/isxander/controlify/impl/gui/guide/PrecomputedLines.java similarity index 95% rename from src/main/java/dev/isxander/controlify/gui/guide/PrecomputedLines.java rename to src/main/java/dev/isxander/controlify/impl/gui/guide/PrecomputedLines.java index b8465f8b4..db2d162ba 100644 --- a/src/main/java/dev/isxander/controlify/gui/guide/PrecomputedLines.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/guide/PrecomputedLines.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.guide; +package dev.isxander.controlify.impl.gui.guide; import net.minecraft.network.chat.Component; diff --git a/src/main/java/dev/isxander/controlify/gui/layout/AbstractLayoutComponent.java b/src/main/java/dev/isxander/controlify/impl/gui/layout/AbstractLayoutComponent.java similarity index 95% rename from src/main/java/dev/isxander/controlify/gui/layout/AbstractLayoutComponent.java rename to src/main/java/dev/isxander/controlify/impl/gui/layout/AbstractLayoutComponent.java index c340d8821..48b6d5446 100644 --- a/src/main/java/dev/isxander/controlify/gui/layout/AbstractLayoutComponent.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/layout/AbstractLayoutComponent.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.layout; +package dev.isxander.controlify.impl.gui.layout; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/dev/isxander/controlify/gui/layout/AnchorPoint.java b/src/main/java/dev/isxander/controlify/impl/gui/layout/AnchorPoint.java similarity index 92% rename from src/main/java/dev/isxander/controlify/gui/layout/AnchorPoint.java rename to src/main/java/dev/isxander/controlify/impl/gui/layout/AnchorPoint.java index 236c1be60..91d5750a6 100644 --- a/src/main/java/dev/isxander/controlify/gui/layout/AnchorPoint.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/layout/AnchorPoint.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.layout; +package dev.isxander.controlify.impl.gui.layout; import org.joml.Vector2i; diff --git a/src/main/java/dev/isxander/controlify/gui/layout/ColumnLayoutComponent.java b/src/main/java/dev/isxander/controlify/impl/gui/layout/ColumnLayoutComponent.java similarity index 98% rename from src/main/java/dev/isxander/controlify/gui/layout/ColumnLayoutComponent.java rename to src/main/java/dev/isxander/controlify/impl/gui/layout/ColumnLayoutComponent.java index 9df464f94..da856ba3f 100644 --- a/src/main/java/dev/isxander/controlify/gui/layout/ColumnLayoutComponent.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/layout/ColumnLayoutComponent.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.layout; +package dev.isxander.controlify.impl.gui.layout; import net.minecraft.client.gui.GuiGraphicsExtractor; import org.apache.commons.lang3.Validate; diff --git a/src/main/java/dev/isxander/controlify/gui/layout/PositionedComponent.java b/src/main/java/dev/isxander/controlify/impl/gui/layout/PositionedComponent.java similarity index 97% rename from src/main/java/dev/isxander/controlify/gui/layout/PositionedComponent.java rename to src/main/java/dev/isxander/controlify/impl/gui/layout/PositionedComponent.java index 84eb8f482..533f925a6 100644 --- a/src/main/java/dev/isxander/controlify/gui/layout/PositionedComponent.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/layout/PositionedComponent.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.layout; +package dev.isxander.controlify.impl.gui.layout; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Renderable; diff --git a/src/main/java/dev/isxander/controlify/gui/layout/RenderComponent.java b/src/main/java/dev/isxander/controlify/impl/gui/layout/RenderComponent.java similarity index 89% rename from src/main/java/dev/isxander/controlify/gui/layout/RenderComponent.java rename to src/main/java/dev/isxander/controlify/impl/gui/layout/RenderComponent.java index 9bb67b2db..1afb27ab6 100644 --- a/src/main/java/dev/isxander/controlify/gui/layout/RenderComponent.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/layout/RenderComponent.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.layout; +package dev.isxander.controlify.impl.gui.layout; import net.minecraft.client.gui.GuiGraphicsExtractor; import org.joml.Vector2ic; diff --git a/src/main/java/dev/isxander/controlify/gui/layout/RowLayoutComponent.java b/src/main/java/dev/isxander/controlify/impl/gui/layout/RowLayoutComponent.java similarity index 98% rename from src/main/java/dev/isxander/controlify/gui/layout/RowLayoutComponent.java rename to src/main/java/dev/isxander/controlify/impl/gui/layout/RowLayoutComponent.java index 691455dc3..69f1e282c 100644 --- a/src/main/java/dev/isxander/controlify/gui/layout/RowLayoutComponent.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/layout/RowLayoutComponent.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.layout; +package dev.isxander.controlify.impl.gui.layout; import net.minecraft.client.gui.GuiGraphicsExtractor; import org.apache.commons.lang3.Validate; diff --git a/src/main/java/dev/isxander/controlify/gui/screen/AskToMapControllerScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/AskToMapControllerScreen.java similarity index 77% rename from src/main/java/dev/isxander/controlify/gui/screen/AskToMapControllerScreen.java rename to src/main/java/dev/isxander/controlify/impl/gui/screen/AskToMapControllerScreen.java index dbeaa46d4..5acb702f2 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/AskToMapControllerScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/AskToMapControllerScreen.java @@ -4,16 +4,16 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.screen; +package dev.isxander.controlify.impl.gui.screen; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.client.gui.screens.ConfirmScreen; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; public class AskToMapControllerScreen extends ConfirmScreen { - public AskToMapControllerScreen(ControllerEntity controller, Screen lastScreen) { + public AskToMapControllerScreen(ControllerImpl controller, Screen lastScreen) { super( (confirmed) -> { if (confirmed) { diff --git a/src/main/java/dev/isxander/controlify/gui/screen/BindConsumerScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/BindConsumerScreen.java similarity index 89% rename from src/main/java/dev/isxander/controlify/gui/screen/BindConsumerScreen.java rename to src/main/java/dev/isxander/controlify/impl/gui/screen/BindConsumerScreen.java index 764eb1ebf..54450313f 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/BindConsumerScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/BindConsumerScreen.java @@ -4,14 +4,14 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.screen; - -import dev.isxander.controlify.bindings.input.Input; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.gui.controllers.BindController; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.utils.MinecraftUtil; +package dev.isxander.controlify.impl.gui.screen; + +import dev.isxander.controlify.impl.bindings.input.Input; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.gui.controllers.BindController; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.utils.Dimension; import net.minecraft.client.gui.GuiGraphicsExtractor; @@ -150,7 +150,7 @@ public ScreenProcessorImpl(BindConsumerScreen screen) { } @Override - public void onControllerFixedUpdate(ControllerEntity controller) { + public void onControllerFixedUpdate(ControllerImpl controller) { // prevent all other controller input logic } } diff --git a/src/main/java/dev/isxander/controlify/gui/screen/BluetoothWarningScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/BluetoothWarningScreen.java similarity index 79% rename from src/main/java/dev/isxander/controlify/gui/screen/BluetoothWarningScreen.java rename to src/main/java/dev/isxander/controlify/impl/gui/screen/BluetoothWarningScreen.java index 3ec03631c..12b792119 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/BluetoothWarningScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/BluetoothWarningScreen.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.screen; +package dev.isxander.controlify.impl.gui.screen; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.controller.misc.BluetoothDeviceComponent; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.controller.misc.BluetoothDeviceComponent; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.client.gui.screens.ConfirmScreen; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.CommonComponents; diff --git a/src/main/java/dev/isxander/controlify/gui/screen/ControlifySettingsScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/ControlifySettingsScreen.java similarity index 94% rename from src/main/java/dev/isxander/controlify/gui/screen/ControlifySettingsScreen.java rename to src/main/java/dev/isxander/controlify/impl/gui/screen/ControlifySettingsScreen.java index 54057a692..e9b9f8f92 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/ControlifySettingsScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/ControlifySettingsScreen.java @@ -4,22 +4,22 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.screen; +package dev.isxander.controlify.impl.gui.screen; import com.google.common.collect.ImmutableList; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.api.buttonguide.ButtonGuideApi; -import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.config.settings.device.DeviceSettings; -import dev.isxander.controlify.config.settings.profile.ProfileSettings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.id.ControllerType; -import dev.isxander.controlify.gui.components.PlainTextWidget; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.config.settings.device.DeviceSettings; +import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controllermanager.ControllerType; +import dev.isxander.controlify.impl.gui.components.PlainTextWidget; import dev.isxander.controlify.mixins.feature.ui.AbstractSelectionListAccessor; -import dev.isxander.controlify.screenop.ScreenControllerEventListener; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.screenop.ScreenControllerEventListener; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.ChatFormatting; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.*; @@ -37,7 +37,6 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; import net.minecraft.util.Mth; -import net.minecraft.util.Util; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; @@ -193,7 +192,7 @@ public void extractBackground(@NonNull GuiGraphicsExtractor graphics, int mouseX } @Override - public void onControllerInput(ControllerEntity controller) { + public void onControllerInput(ControllerImpl controller) { if (ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).justPressed()) { // Button impl does not use NonNull parameter globalSettingsButton.onPress(null); @@ -333,12 +332,12 @@ private void updateProfileInfo(boolean force) { ? Component.translatable("controlify.gui.carousel.entry.profile", displayedProfileIndex) : Component.translatable("controlify.gui.carousel.entry.named_profile", displayedProfileIndex, profileSettings.name); - Optional controller = this.getController(profileSettings); + Optional controller = this.getController(profileSettings); Component controllerName; Identifier controllerIcon; boolean controllerConnected; if (controller.isPresent()) { - ControllerEntity entity = controller.get(); + ControllerImpl entity = controller.get(); controllerName = Component.literal(entity.name()); controllerIcon = entity.info().type().getIconSprite(); controllerConnected = true; @@ -390,7 +389,7 @@ private ProfileSettings getProfileSettings() { return profile; } - private Optional getController(ProfileSettings profileSettings) { + private Optional getController(ProfileSettings profileSettings) { if (profileSettings.controllerUid == null) { return Controlify.instance().getCurrentController(); } diff --git a/src/main/java/dev/isxander/controlify/gui/screen/ControllerConfigScreenFactory.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerConfigScreenFactory.java similarity index 92% rename from src/main/java/dev/isxander/controlify/gui/screen/ControllerConfigScreenFactory.java rename to src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerConfigScreenFactory.java index 3473aa7d5..5ad143665 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/ControllerConfigScreenFactory.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerConfigScreenFactory.java @@ -4,53 +4,51 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.screen; +package dev.isxander.controlify.impl.gui.screen; -import dev.isxander.controlify.Controlify; import dev.isxander.controlify.api.contextual.GuideVerbosity; -import dev.isxander.controlify.bindings.BindContext; -import dev.isxander.controlify.bindings.ControlifyBindApiImpl; -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.api.bind.InputBindingSupplier; -import dev.isxander.controlify.bindings.input.EmptyInput; -import dev.isxander.controlify.bindings.input.Input; -import dev.isxander.controlify.config.settings.device.DeviceSettings; -import dev.isxander.controlify.config.settings.profile.ProfileSettings; -import dev.isxander.controlify.config.settings.profile.GenericControllerSettings; -import dev.isxander.controlify.config.settings.profile.InputSettings; -import dev.isxander.controlify.controller.*; -import dev.isxander.controlify.controller.gyro.GyroButtonMode; -import dev.isxander.controlify.controller.gyro.GyroYawMode; -import dev.isxander.controlify.input.mapping.DeadzoneGroup; -import dev.isxander.controlify.controller.input.InputComponent; -import dev.isxander.controlify.input.mapping.Inputs; -import dev.isxander.controlify.controller.rumble.RumbleComponent; -import dev.isxander.controlify.gui.controllers.BindController; -import dev.isxander.controlify.gui.controllers.Deadzone2DImageRenderer; -import dev.isxander.controlify.ingame.InputCurves; -import dev.isxander.controlify.rumble.BasicRumbleEffect; -import dev.isxander.controlify.rumble.RumbleSource; -import dev.isxander.controlify.rumble.RumbleState; -import dev.isxander.controlify.server.ServerPolicies; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.api.controller.rumble.RumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleState; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.impl.api.bind.InputBindingSupplier; +import dev.isxander.controlify.impl.bindings.BindContext; +import dev.isxander.controlify.impl.bindings.ControlifyBindApiImpl; +import dev.isxander.controlify.impl.bindings.input.EmptyInput; +import dev.isxander.controlify.impl.bindings.input.Input; +import dev.isxander.controlify.impl.config.settings.device.DeviceSettings; +import dev.isxander.controlify.impl.config.settings.profile.GenericControllerSettings; +import dev.isxander.controlify.impl.config.settings.profile.InputSettings; +import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; +import dev.isxander.controlify.impl.controller.gyro.GyroButtonMode; +import dev.isxander.controlify.impl.controller.gyro.GyroYawMode; +import dev.isxander.controlify.impl.controller.input.InputComponent; +import dev.isxander.controlify.impl.controller.rumble.RumbleComponentImpl; +import dev.isxander.controlify.impl.controller.rumble.RumbleSource; +import dev.isxander.controlify.impl.gui.controllers.BindController; +import dev.isxander.controlify.impl.gui.controllers.Deadzone2DImageRenderer; +import dev.isxander.controlify.impl.ingame.InputCurves; +import dev.isxander.controlify.impl.input.mapping.DeadzoneGroup; +import dev.isxander.controlify.impl.input.mapping.Inputs; +import dev.isxander.controlify.impl.server.ServerPolicies; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import dev.isxander.yacl3.api.*; import dev.isxander.yacl3.api.controller.*; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; import net.minecraft.ChatFormatting; -import net.minecraft.network.chat.MutableComponent; -import net.minecraft.util.Util; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; import net.minecraft.resources.Identifier; +import net.minecraft.util.Util; import org.jetbrains.annotations.Nullable; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; - public class ControllerConfigScreenFactory { private static final ValueFormatter percentFormatter = v -> Component.literal(String.format("%.0f%%", v*100)); private static final ValueFormatter percentOrOffFormatter = v -> v == 0 ? CommonComponents.OPTION_OFF : percentFormatter.format(v); @@ -62,7 +60,7 @@ public class ControllerConfigScreenFactory { public static Screen generateConfigScreen( Screen parent, ProfileSettings settings, ProfileSettings defaults, - @Nullable ControllerEntity controller + @Nullable ControllerImpl controller ) { return new ControllerConfigScreenFactory().generateConfigScreen0( parent, @@ -76,7 +74,7 @@ private Screen generateConfigScreen0( Screen parent, ProfileSettings settings, ProfileSettings defaults, - Optional controller + Optional controller ) { String initialControllerUid = settings.controllerUid; var advancedCategory = createAdvancedCategory(settings, defaults, controller); @@ -102,7 +100,7 @@ private Screen generateConfigScreen0( private ConfigCategory createBasicCategory( ProfileSettings settings, ProfileSettings defaults, - Optional controller + Optional controller ) { var sensitivityGroup = makeSensitivityGroup(settings, defaults, controller); var controlsGroup = makeControlsGroup(settings, defaults, controller); @@ -174,7 +172,7 @@ private OptionGroup makeProfileGroup(ProfileSettings settings) { private Optional makeSensitivityGroup( ProfileSettings settings, ProfileSettings defaults, - Optional controller + Optional controller ) { var builder = OptionGroup.createBuilder() .name(Component.translatable("controlify.gui.config.group.sensitivity")); @@ -268,7 +266,7 @@ private Optional makeSensitivityGroup( private Optional makeControlsGroup( ProfileSettings settings, ProfileSettings defaults, - Optional controller + Optional controller ) { if (controller.isEmpty()) return Optional.empty(); @@ -331,7 +329,7 @@ private Optional makeControlsGroup( private Optional makeAccessibilityGroup( ProfileSettings settings, ProfileSettings defaults, - Optional controller + Optional controller ) { GenericControllerSettings gSettings = settings.generic; GenericControllerSettings gDefaults = defaults.generic; @@ -415,9 +413,9 @@ private Optional makeAccessibilityGroup( private Optional makeDeadzoneGroup( ProfileSettings settings, ProfileSettings defaults, - Optional controller + Optional controller ) { - Optional inputOpt = controller.flatMap(ControllerEntity::input); + Optional inputOpt = controller.flatMap(ControllerImpl::input); if (inputOpt.isEmpty()) return Optional.empty(); @@ -474,7 +472,7 @@ private Optional makeDeadzoneGroup( private ConfigCategory createAdvancedCategory( ProfileSettings settings, ProfileSettings defaults, - Optional controller + Optional controller ) { var builder = ConfigCategory.createBuilder() .name(Component.translatable("controlify.config.category.advanced")); @@ -490,16 +488,16 @@ private ConfigCategory createAdvancedCategory( private Optional makeControllerMappingGroup( ProfileSettings settings, ProfileSettings defaults, - Optional controller + Optional controller ) { - Optional inputOpt = controller.flatMap(ControllerEntity::input); + Optional inputOpt = controller.flatMap(ControllerImpl::input); if (inputOpt.isEmpty()) return Optional.empty(); InputComponent input = inputOpt.get(); DeviceSettings config = Controlify.instance().config().getSettings() .getOrCreateDeviceSettings(controller.get().uid()); - DeviceSettings def = DeviceSettings.defaults(controller.map(ControllerEntity::uid).orElse("")); + DeviceSettings def = DeviceSettings.defaults(controller.map(ControllerImpl::uid).orElse("")); return Optional.of(OptionGroup.createBuilder() .name(Component.translatable("controlify.gui.group.controller_mapping")) @@ -521,7 +519,7 @@ private Optional makeControllerMappingGroup( private Optional makeVibrationGroup( ProfileSettings settings, ProfileSettings defaults, - Optional controller + Optional controller ) { var vibrationGroup = OptionGroup.createBuilder() .name(Component.translatable("controlify.gui.group.vibration")) @@ -556,7 +554,7 @@ private Optional makeVibrationGroup( .controller(TickBoxControllerBuilder::create) .build()); - for (RumbleSource source : RumbleSource.values()) { + for (RumbleSource source : Controlify.instance().rumbleSourceManager().sources()) { var option = Option.createBuilder() .name(Component.translatable("controlify.vibration_strength." + source.id().getNamespace() + "." + source.id().getPath())) .description(OptionDescription.createBuilder() @@ -577,21 +575,21 @@ private Optional makeVibrationGroup( vibrationGroup.option(option); } if (controller.isPresent() && controller.get().rumble().isPresent()) { - RumbleComponent rumble = controller.get().rumble().get(); + RumbleComponentImpl rumble = controller.get().rumble().get(); vibrationGroup.option(ButtonOption.createBuilder() .name(Component.translatable("controlify.gui.test_vibration")) .description(OptionDescription.of(Component.translatable("controlify.gui.test_vibration.tooltip"))) .action((screen, btn) -> { - rumble.rumbleManager().play( - RumbleSource.MASTER, - BasicRumbleEffect.byTime(t -> new RumbleState(0f, t), 20) - .seq(BasicRumbleEffect.byTime(t -> new RumbleState(0f, 1 - t), 20)) + rumble.playEffect( + CUtil.rl("test_vibration"), + RumbleEffect.finite().byTime(t -> new RumbleState(0f, t), 20) + .then(RumbleEffect.finite().byTime(t -> new RumbleState(0f, 1 - t), 20)) .repeat(3) - .seq(BasicRumbleEffect.constant(1f, 0f, 5) - .seq(BasicRumbleEffect.constant(0f, 1f, 5)) + .then(RumbleEffect.finite().constant(1f, 0f, 5) + .then(RumbleEffect.finite().constant(0f, 1f, 5)) .repeat(10) ) - .earlyFinish(BasicRumbleEffect.finishOnScreenChange()) + .stopCondition(() -> MinecraftUtil.getScreen() != screen) ); }) .build()); @@ -603,7 +601,7 @@ private Optional makeVibrationGroup( private Optional makeGyroGroup( ProfileSettings settings, ProfileSettings defaults, - Optional controller + Optional controller ) { var gyroGroup = OptionGroup.createBuilder() .name(Component.translatable("controlify.gui.group.gyro")) @@ -716,7 +714,7 @@ private Optional makeGyroGroup( private Optional makeTriggerEffectsGroup( ProfileSettings settings, ProfileSettings defaults, - Optional controller + Optional controller ) { var group = OptionGroup.createBuilder() .name(Component.translatable("controlify.gui.group.trigger_effects")) @@ -742,7 +740,7 @@ private Optional makeTriggerEffectsGroup( private Optional makeBindsCategory( ProfileSettings settings, ProfileSettings defaults, - Optional controller + Optional controller ) { var category = ConfigCategory.createBuilder() @@ -840,11 +838,11 @@ private static Map> groupBindings(Collection createBindingOpt(InputBindingSupplier bindingSupplier, ControllerEntity controller) { + private static Option.Builder createBindingOpt(InputBindingSupplier bindingSupplier, ControllerImpl controller) { return createBindingOpt(bindingSupplier.on(controller), controller); } - private static Option.Builder createBindingOpt(InputBinding binding, @Nullable ControllerEntity controller) { + private static Option.Builder createBindingOpt(InputBinding binding, @Nullable ControllerImpl controller) { return Option.createBuilder() .name(binding.name()) .description(v -> OptionDescription.createBuilder() @@ -852,7 +850,7 @@ private static Option.Builder createBindingOpt(InputBinding binding, @Nul .text(Component.translatable("controlify.gui.bind.currently_bound_to", Component.empty() .append(Controlify.instance().inputFontMapper().getComponentFromInputs( - controller.info().type().namespace(), + controller.info().type().id(), v.getRelevantInputs() )) .append(CommonComponents.SPACE) @@ -861,7 +859,7 @@ private static Option.Builder createBindingOpt(InputBinding binding, @Nul .text(v.equals(binding.defaultInput()) ? Component.empty() : Component.translatable("controlify.gui.bind.default_bound_to", Component.empty() .append(Controlify.instance().inputFontMapper().getComponentFromInputs( - controller.info().type().namespace(), + controller.info().type().id(), binding.defaultInput().getRelevantInputs() )) .append(CommonComponents.SPACE) diff --git a/src/main/java/dev/isxander/controlify/gui/screen/ControllerMappingMakerScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerMappingMakerScreen.java similarity index 93% rename from src/main/java/dev/isxander/controlify/gui/screen/ControllerMappingMakerScreen.java rename to src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerMappingMakerScreen.java index 8e080af83..322ebbbed 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/ControllerMappingMakerScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerMappingMakerScreen.java @@ -4,20 +4,20 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.screen; - -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.config.settings.device.DeviceSettings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.input.mapping.*; -import dev.isxander.controlify.input.mapping.*; -import dev.isxander.controlify.screenop.ScreenControllerEventListener; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.MinecraftUtil; -import dev.isxander.controlify.utils.render.RenderUtils; -import dev.isxander.controlify.utils.ColorUtils; +package dev.isxander.controlify.impl.gui.screen; + +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.config.settings.device.DeviceSettings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controller.input.mapping.*; +import dev.isxander.controlify.impl.input.mapping.*; +import dev.isxander.controlify.impl.screenop.ScreenControllerEventListener; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.MinecraftUtil; +import dev.isxander.controlify.impl.utils.render.RenderUtils; +import dev.isxander.controlify.impl.utils.ColorUtils; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.Tooltip; @@ -326,7 +326,7 @@ public ScreenProcessorImpl(ControllerMappingMakerScreen screen) { } @Override - public void onControllerFixedUpdate(ControllerEntity controller) { + public void onControllerFixedUpdate(ControllerImpl controller) { } } diff --git a/src/main/java/dev/isxander/controlify/gui/screen/DontInterruptScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/DontInterruptScreen.java similarity index 77% rename from src/main/java/dev/isxander/controlify/gui/screen/DontInterruptScreen.java rename to src/main/java/dev/isxander/controlify/impl/gui/screen/DontInterruptScreen.java index dcc9cd965..54bbabf7c 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/DontInterruptScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/DontInterruptScreen.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.screen; +package dev.isxander.controlify.impl.gui.screen; public interface DontInterruptScreen { } diff --git a/src/main/java/dev/isxander/controlify/gui/screen/GlobalSettingsScreenFactory.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/GlobalSettingsScreenFactory.java similarity index 94% rename from src/main/java/dev/isxander/controlify/gui/screen/GlobalSettingsScreenFactory.java rename to src/main/java/dev/isxander/controlify/impl/gui/screen/GlobalSettingsScreenFactory.java index 1e885fe5a..58385aff3 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/GlobalSettingsScreenFactory.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/GlobalSettingsScreenFactory.java @@ -4,18 +4,18 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.screen; +package dev.isxander.controlify.impl.gui.screen; -import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.config.settings.GlobalSettings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.driver.steamdeck.SteamDeckUtil; -import dev.isxander.controlify.reacharound.ReachAroundMode; -import dev.isxander.controlify.server.ServerPolicies; -import dev.isxander.controlify.server.ServerPolicy; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.DebugDump; +import dev.isxander.controlify.impl.config.settings.GlobalSettings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.driver.steamdeck.SteamDeckUtil; +import dev.isxander.controlify.impl.reacharound.ReachAroundMode; +import dev.isxander.controlify.impl.server.ServerPolicies; +import dev.isxander.controlify.impl.server.ServerPolicy; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.DebugDump; import dev.isxander.yacl3.api.*; import dev.isxander.yacl3.api.controller.*; import net.minecraft.ChatFormatting; @@ -72,7 +72,7 @@ public static Screen createGlobalSettingsScreen(Screen parent) { .controller(TickBoxControllerBuilder::create) .addListener((opt, val) -> { ControlifyApi.get().getCurrentController() - .flatMap(ControllerEntity::rumble) + .flatMap(ControllerImpl::rumble) .ifPresent(rumble -> rumble.rumbleManager().clearEffects()); }) .build()) diff --git a/src/main/java/dev/isxander/controlify/gui/screen/ProfileSelectionScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/ProfileSelectionScreen.java similarity index 94% rename from src/main/java/dev/isxander/controlify/gui/screen/ProfileSelectionScreen.java rename to src/main/java/dev/isxander/controlify/impl/gui/screen/ProfileSelectionScreen.java index a77ac7bb5..86cfd07cb 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/ProfileSelectionScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/ProfileSelectionScreen.java @@ -4,19 +4,19 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.screen; - -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.api.buttonguide.ButtonGuideApi; -import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.config.settings.device.DeviceSettings; -import dev.isxander.controlify.config.settings.profile.ProfileSettings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.id.ControllerType; -import dev.isxander.controlify.screenop.ScreenControllerEventListener; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.MinecraftUtil; +package dev.isxander.controlify.impl.gui.screen; + +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.config.settings.device.DeviceSettings; +import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controllermanager.ControllerType; +import dev.isxander.controlify.impl.screenop.ScreenControllerEventListener; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.ChatFormatting; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.AbstractButton; @@ -200,7 +200,7 @@ public void extractRenderState(@NonNull GuiGraphicsExtractor graphics, int mouse } @Override - public void onControllerInput(ControllerEntity controller) { + public void onControllerInput(ControllerImpl controller) { if (this.selectedProfileIndex >= 0 && ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).justPressed()) { this.switchProfile(this.selectedProfileIndex, false); @@ -326,7 +326,7 @@ private ControllerDisplay controllerDisplay(ProfileSettings profile) { ); } - ControllerEntity connected = Controlify.instance().getControllerManager() + ControllerImpl connected = Controlify.instance().getControllerManager() .flatMap(manager -> manager.getConnectedControllers().stream() .filter(controller -> profile.controllerUid.equals(controller.uid())) .findFirst()) diff --git a/src/main/java/dev/isxander/controlify/gui/screen/RadialItems.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialItems.java similarity index 92% rename from src/main/java/dev/isxander/controlify/gui/screen/RadialItems.java rename to src/main/java/dev/isxander/controlify/impl/gui/screen/RadialItems.java index f910ed596..2f3ee3e06 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/RadialItems.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialItems.java @@ -4,16 +4,16 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.screen; - -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.api.bind.InputBindingSupplier; -import dev.isxander.controlify.action.radial.RadialIcon; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.action.radial.RadialIconManager; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.utils.CUtil; +package dev.isxander.controlify.impl.gui.screen; + +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.impl.api.bind.InputBindingSupplier; +import dev.isxander.controlify.impl.action.radial.RadialIcon; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.radial.RadialIconManager; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen; import net.minecraft.client.player.inventory.Hotbar; @@ -33,7 +33,7 @@ public final class RadialItems { public static final RadialMenuScreen.RadialItem EMPTY_ACTION = new RadialItemRecord(Component.empty(), RadialIcon.EMPTY, () -> false, RadialIconManager.EMPTY); - public static RadialMenuScreen.RadialItem[] createBindings(ControllerEntity controller) { + public static RadialMenuScreen.RadialItem[] createBindings(ControllerImpl controller) { List actions = controller.settings().input.radialMenu.radialActions; RadialMenuScreen.RadialItem[] items = new RadialMenuScreen.RadialItem[actions.size()]; @@ -44,7 +44,7 @@ public static RadialMenuScreen.RadialItem[] createBindings(ControllerEntity cont return items; } - public static List createBindingPages(ControllerEntity controller) { + public static List createBindingPages(ControllerImpl controller) { RadialMenuScreen.RadialItem[] items = createBindings(controller); return RadialMenuPages.partition(Arrays.asList(items)).stream() .map(page -> page.toArray(RadialMenuScreen.RadialItem[]::new)) @@ -134,7 +134,7 @@ public static RadialMenuScreen.RadialItem[] createHotbarItemSelect() { return items; } - public static RadialMenuScreen.RadialItem[] createDebug(ControllerEntity controller) { + public static RadialMenuScreen.RadialItem[] createDebug(ControllerImpl controller) { return List.of( ControlifyBindings.TOGGLE_DEBUG_MENU, ControlifyBindings.TOGGLE_DEBUG_MENU_FPS, @@ -180,7 +180,7 @@ private static RadialIcon getIconForItem(ItemStack stack) { return model != null ? RadialIcon.model(model) : RadialIcon.EMPTY; } - private static RadialMenuScreen.RadialItem getItemForBinding(Identifier id, ControllerEntity controller) { + private static RadialMenuScreen.RadialItem getItemForBinding(Identifier id, ControllerImpl controller) { InputBinding binding = controller.input().orElseThrow().getBinding(id); RadialIconManager radialIconManager = Controlify.instance().radialIconManager(); diff --git a/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuEditModel.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuEditModel.java similarity index 97% rename from src/main/java/dev/isxander/controlify/gui/screen/RadialMenuEditModel.java rename to src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuEditModel.java index c9f81df74..2c0c18b77 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuEditModel.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuEditModel.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.screen; +package dev.isxander.controlify.impl.gui.screen; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuEditScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuEditScreen.java similarity index 95% rename from src/main/java/dev/isxander/controlify/gui/screen/RadialMenuEditScreen.java rename to src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuEditScreen.java index 1ce96d25a..dcbe5340f 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuEditScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuEditScreen.java @@ -4,22 +4,22 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.screen; +package dev.isxander.controlify.impl.gui.screen; import com.mojang.blaze3d.platform.InputConstants; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.action.radial.RadialIcon; -import dev.isxander.controlify.api.buttonguide.ButtonGuideApi; -import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.action.radial.RadialIconExtractor; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.haptic.HapticEffects; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.utils.MinecraftUtil; -import dev.isxander.controlify.virtualmouse.VirtualMouseBehaviour; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.impl.action.radial.RadialIcon; +import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.radial.RadialIconExtractor; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controller.haptic.HapticEffects; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.utils.MinecraftUtil; +import dev.isxander.controlify.impl.virtualmouse.VirtualMouseBehaviour; import net.minecraft.ChatFormatting; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.AbstractButton; @@ -61,7 +61,7 @@ public class RadialMenuEditScreen extends Screen implements ScreenProcessorProvi private static final int ROW_GAP = 2; private final Screen parent; - private final ControllerEntity controller; + private final ControllerImpl controller; private final RadialMenuEditModel model; private final List candidates; private final Map candidatesById; @@ -75,7 +75,7 @@ public class RadialMenuEditScreen extends Screen implements ScreenProcessorProvi private Identifier focusAfterRebuild; private int equippedInsertionIndex; - public RadialMenuEditScreen(Screen parent, ControllerEntity controller) { + public RadialMenuEditScreen(Screen parent, ControllerImpl controller) { super(Component.translatable("controlify.radial_menu.editor.title")); this.parent = parent; this.controller = controller; @@ -640,7 +640,7 @@ public Processor(RadialMenuEditScreen screen) { } @Override - protected void handleComponentNavigation(ControllerEntity controller) { + protected void handleComponentNavigation(ControllerImpl controller) { if (!screen.model.isCarrying()) { super.handleComponentNavigation(controller); return; @@ -664,7 +664,7 @@ protected void handleComponentNavigation(ControllerEntity controller) { } @Override - protected void handleButtons(ControllerEntity controller) { + protected void handleButtons(ControllerImpl controller) { if (screen.model.isCarrying()) { if (ControlifyBindings.GUI_PRESS.on(controller).guiPressed().get()) { screen.dropCarried(); diff --git a/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuPages.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuPages.java similarity index 93% rename from src/main/java/dev/isxander/controlify/gui/screen/RadialMenuPages.java rename to src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuPages.java index 6ae415e11..442eb276f 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuPages.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuPages.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.screen; +package dev.isxander.controlify.impl.gui.screen; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuScreen.java similarity index 87% rename from src/main/java/dev/isxander/controlify/gui/screen/RadialMenuScreen.java rename to src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuScreen.java index 0e07e4b47..2df14d4f8 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuScreen.java @@ -4,24 +4,24 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.screen; - -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.action.radial.RadialIcon; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.action.radial.RadialIconExtractor; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.haptic.HapticEffects; -import dev.isxander.controlify.gui.screen.RadialItems.RadialPage; -import dev.isxander.controlify.screenop.ScreenControllerEventListener; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.sound.ControlifyClientSounds; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.MinecraftUtil; -import dev.isxander.controlify.utils.animation.api.Animation; -import dev.isxander.controlify.utils.animation.api.EasingFunction; -import dev.isxander.controlify.virtualmouse.VirtualMouseBehaviour; +package dev.isxander.controlify.impl.gui.screen; + +import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.impl.action.radial.RadialIcon; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.radial.RadialIconExtractor; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controller.haptic.HapticEffects; +import dev.isxander.controlify.impl.gui.screen.RadialItems.RadialPage; +import dev.isxander.controlify.impl.screenop.ScreenControllerEventListener; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.sound.ControlifyClientSounds; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.MinecraftUtil; +import dev.isxander.controlify.impl.utils.animation.api.Animation; +import dev.isxander.controlify.impl.utils.animation.api.EasingFunction; +import dev.isxander.controlify.impl.virtualmouse.VirtualMouseBehaviour; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.TextAlignment; import net.minecraft.client.gui.components.MultiLineLabel; @@ -46,7 +46,7 @@ import java.util.List; public class RadialMenuScreen extends Screen implements ScreenControllerEventListener, ScreenProcessorProvider { - private final ControllerEntity controller; + private final ControllerImpl controller; private final @Nullable Screen parent; private final Component footerText; private final List userPages; @@ -65,19 +65,19 @@ public class RadialMenuScreen extends Screen implements ScreenControllerEventLis /** * Creates a single-page radial menu. Existing special-purpose radial menus use this path. */ - public RadialMenuScreen(ControllerEntity controller, InputBinding openBind, RadialItem[] items, Component text, @Nullable Screen parent) { + public RadialMenuScreen(ControllerImpl controller, InputBinding openBind, RadialItem[] items, Component text, @Nullable Screen parent) { this(controller, openBind, Collections.singletonList(items), List.of(), text, parent); } /** * Creates a paged radial menu. Page boundaries are a presentation concern and are not persisted. */ - public RadialMenuScreen(ControllerEntity controller, @Nullable InputBinding openBind, List pages, Component text, @Nullable Screen parent) { + public RadialMenuScreen(ControllerImpl controller, @Nullable InputBinding openBind, List pages, Component text, @Nullable Screen parent) { this(controller, openBind, pages, List.of(), text, parent); } public RadialMenuScreen( - ControllerEntity controller, + ControllerImpl controller, @Nullable InputBinding openBind, List userPages, List bonusPages, @@ -147,7 +147,7 @@ private void changePage(int direction) { } @Override - public void onControllerInput(ControllerEntity controller) { + public void onControllerInput(ControllerImpl controller) { if (this.controller != controller) return; if (openBind == null) { @@ -364,7 +364,7 @@ public Processor(RadialMenuScreen screen) { } @Override - protected void handleTabNavigation(ControllerEntity controller) { + protected void handleTabNavigation(ControllerImpl controller) { if (ControlifyBindings.GUI_NEXT_TAB.on(controller).justPressed()) { screen.changePage(1); } else if (ControlifyBindings.GUI_PREV_TAB.on(controller).justPressed()) { diff --git a/src/main/java/dev/isxander/controlify/gui/screen/SteamDeckAlerts.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/SteamDeckAlerts.java similarity index 96% rename from src/main/java/dev/isxander/controlify/gui/screen/SteamDeckAlerts.java rename to src/main/java/dev/isxander/controlify/impl/gui/screen/SteamDeckAlerts.java index ef4844534..a0efa0920 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/SteamDeckAlerts.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/SteamDeckAlerts.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.gui.screen; +package dev.isxander.controlify.impl.gui.screen; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; diff --git a/src/main/java/dev/isxander/controlify/hid/ControllerHIDInfo.java b/src/main/java/dev/isxander/controlify/impl/hid/ControllerHIDInfo.java similarity index 78% rename from src/main/java/dev/isxander/controlify/hid/ControllerHIDInfo.java rename to src/main/java/dev/isxander/controlify/impl/hid/ControllerHIDInfo.java index 5435db2b3..8047d7b70 100644 --- a/src/main/java/dev/isxander/controlify/hid/ControllerHIDInfo.java +++ b/src/main/java/dev/isxander/controlify/impl/hid/ControllerHIDInfo.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.hid; +package dev.isxander.controlify.impl.hid; import com.google.common.primitives.Ints; -import dev.isxander.controlify.controller.id.ControllerType; +import dev.isxander.controlify.impl.controllermanager.ControllerType; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -29,10 +29,10 @@ public Optional createControllerUID(int controllerIndex) { md.update(Ints.toByteArray(hid.productId())); }); - String namespace = type().namespace().toString(); - if ("controlify".equals(type().namespace().getNamespace())) { + String namespace = type().id().toString(); + if ("controlify".equals(type().id().getNamespace())) { // maintains backwards compatibility - namespace = type().namespace().getPath(); + namespace = type().id().getPath(); } md.update(namespace.getBytes()); diff --git a/src/main/java/dev/isxander/controlify/hid/HIDDevice.java b/src/main/java/dev/isxander/controlify/impl/hid/HIDDevice.java similarity index 87% rename from src/main/java/dev/isxander/controlify/hid/HIDDevice.java rename to src/main/java/dev/isxander/controlify/impl/hid/HIDDevice.java index e6c5697a0..057d6ba9e 100644 --- a/src/main/java/dev/isxander/controlify/hid/HIDDevice.java +++ b/src/main/java/dev/isxander/controlify/impl/hid/HIDDevice.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.hid; +package dev.isxander.controlify.impl.hid; public record HIDDevice(HIDID hidid, String path) { public int vendorId() { diff --git a/src/main/java/dev/isxander/controlify/hid/HIDID.java b/src/main/java/dev/isxander/controlify/impl/hid/HIDID.java similarity index 85% rename from src/main/java/dev/isxander/controlify/hid/HIDID.java rename to src/main/java/dev/isxander/controlify/impl/hid/HIDID.java index bcfb3db8c..84f90c292 100644 --- a/src/main/java/dev/isxander/controlify/hid/HIDID.java +++ b/src/main/java/dev/isxander/controlify/impl/hid/HIDID.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.hid; +package dev.isxander.controlify.impl.hid; import com.mojang.serialization.Codec; -import dev.isxander.controlify.utils.codec.CExtraCodecs; +import dev.isxander.controlify.impl.utils.codec.CExtraCodecs; import org.jetbrains.annotations.NotNull; import java.util.HexFormat; diff --git a/src/main/java/dev/isxander/controlify/ingame/ControllerPlayerMovement.java b/src/main/java/dev/isxander/controlify/impl/ingame/ControllerPlayerMovement.java similarity index 90% rename from src/main/java/dev/isxander/controlify/ingame/ControllerPlayerMovement.java rename to src/main/java/dev/isxander/controlify/impl/ingame/ControllerPlayerMovement.java index c450a3c43..4ab5c12a6 100644 --- a/src/main/java/dev/isxander/controlify/ingame/ControllerPlayerMovement.java +++ b/src/main/java/dev/isxander/controlify/impl/ingame/ControllerPlayerMovement.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.ingame; +package dev.isxander.controlify.impl.ingame; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.client.Minecraft; import net.minecraft.client.player.KeyboardInput; import net.minecraft.client.player.LocalPlayer; @@ -20,11 +20,11 @@ import net.minecraft.world.entity.player.Input; public class ControllerPlayerMovement extends ClientInput { - private final ControllerEntity controller; + private final ControllerImpl controller; private final LocalPlayer player; private boolean wasFlying, wasPassenger; - public ControllerPlayerMovement(ControllerEntity controller, LocalPlayer player) { + public ControllerPlayerMovement(ControllerImpl controller, LocalPlayer player) { this.controller = controller; this.player = player; } diff --git a/src/main/java/dev/isxander/controlify/ingame/DualInput.java b/src/main/java/dev/isxander/controlify/impl/ingame/DualInput.java similarity index 97% rename from src/main/java/dev/isxander/controlify/ingame/DualInput.java rename to src/main/java/dev/isxander/controlify/impl/ingame/DualInput.java index e2b6ea6ba..e03bb0611 100644 --- a/src/main/java/dev/isxander/controlify/ingame/DualInput.java +++ b/src/main/java/dev/isxander/controlify/impl/ingame/DualInput.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.ingame; +package dev.isxander.controlify.impl.ingame; import net.minecraft.util.Mth; import net.minecraft.world.phys.Vec2; diff --git a/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java b/src/main/java/dev/isxander/controlify/impl/ingame/InGameInputHandler.java similarity index 92% rename from src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java rename to src/main/java/dev/isxander/controlify/impl/ingame/InGameInputHandler.java index 4f8a86587..5997bbf52 100644 --- a/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java +++ b/src/main/java/dev/isxander/controlify/impl/ingame/InGameInputHandler.java @@ -4,31 +4,31 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.ingame; - -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.api.ingameinput.LookInputModifier; -import dev.isxander.controlify.api.event.ControlifyEvents; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.config.settings.profile.GyroSettings; -import dev.isxander.controlify.config.settings.profile.InputSettings; -import dev.isxander.controlify.controller.gyro.GyroState; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.gyro.GyroButtonMode; -import dev.isxander.controlify.controller.gyro.GyroComponent; -import dev.isxander.controlify.controller.input.InputComponent; -import dev.isxander.controlify.driver.steamdeck.SteamDeckDriver; -import dev.isxander.controlify.gui.screen.RadialItems; -import dev.isxander.controlify.gui.screen.RadialItems.RadialPage; -import dev.isxander.controlify.gui.screen.RadialMenuScreen; +package dev.isxander.controlify.impl.ingame; + +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.api.ingameinput.LookInputModifier; +import dev.isxander.controlify.api.ControlifyEvents; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.config.settings.profile.GyroSettings; +import dev.isxander.controlify.impl.config.settings.profile.InputSettings; +import dev.isxander.controlify.impl.controller.gyro.GyroState; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controller.gyro.GyroButtonMode; +import dev.isxander.controlify.impl.controller.gyro.GyroComponent; +import dev.isxander.controlify.impl.controller.input.InputComponent; +import dev.isxander.controlify.impl.driver.steamdeck.SteamDeckDriver; +import dev.isxander.controlify.impl.gui.screen.RadialItems; +import dev.isxander.controlify.impl.gui.screen.RadialItems.RadialPage; +import dev.isxander.controlify.impl.gui.screen.RadialMenuScreen; import dev.isxander.controlify.mixins.feature.steamdeck.ScreenshotAccessor; -import dev.isxander.controlify.server.ServerPolicies; -import dev.isxander.controlify.utils.ControllerUtils; -import dev.isxander.controlify.utils.DebugOverlayHelper; -import dev.isxander.controlify.utils.HoldRepeatHelper; -import dev.isxander.controlify.utils.MinecraftUtil; -import dev.isxander.controlify.utils.animation.api.Animation; -import dev.isxander.controlify.utils.animation.api.EasingFunction; +import dev.isxander.controlify.impl.server.ServerPolicies; +import dev.isxander.controlify.impl.utils.ControllerUtils; +import dev.isxander.controlify.impl.utils.DebugOverlayHelper; +import dev.isxander.controlify.impl.utils.HoldRepeatHelper; +import dev.isxander.controlify.impl.utils.MinecraftUtil; +import dev.isxander.controlify.impl.utils.animation.api.Animation; +import dev.isxander.controlify.impl.utils.animation.api.EasingFunction; import net.minecraft.client.CameraType; import net.minecraft.client.Minecraft; import net.minecraft.client.Screenshot; @@ -54,7 +54,7 @@ import java.util.List; public class InGameInputHandler { - private final ControllerEntity controller; + private final ControllerImpl controller; private final Controlify controlify; private final Minecraft minecraft; @@ -72,7 +72,7 @@ public class InGameInputHandler { private final HoldRepeatHelper hotbarNextRepeatHelper; private final HoldRepeatHelper hotbarPrevRepeatHelper; - public InGameInputHandler(ControllerEntity controller) { + public InGameInputHandler(ControllerImpl controller) { this.controller = controller; this.minecraft = Minecraft.getInstance(); this.controlify = Controlify.instance(); diff --git a/src/main/java/dev/isxander/controlify/ingame/InputCurve.java b/src/main/java/dev/isxander/controlify/impl/ingame/InputCurve.java similarity index 95% rename from src/main/java/dev/isxander/controlify/ingame/InputCurve.java rename to src/main/java/dev/isxander/controlify/impl/ingame/InputCurve.java index 6baa60075..e65140541 100644 --- a/src/main/java/dev/isxander/controlify/ingame/InputCurve.java +++ b/src/main/java/dev/isxander/controlify/impl/ingame/InputCurve.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.ingame; +package dev.isxander.controlify.impl.ingame; /** * A functional interface representing an input curve transformation. diff --git a/src/main/java/dev/isxander/controlify/ingame/InputCurves.java b/src/main/java/dev/isxander/controlify/impl/ingame/InputCurves.java similarity index 96% rename from src/main/java/dev/isxander/controlify/ingame/InputCurves.java rename to src/main/java/dev/isxander/controlify/impl/ingame/InputCurves.java index 6e2229ee5..2e7dc1cdf 100644 --- a/src/main/java/dev/isxander/controlify/ingame/InputCurves.java +++ b/src/main/java/dev/isxander/controlify/impl/ingame/InputCurves.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.ingame; +package dev.isxander.controlify.impl.ingame; import com.mojang.serialization.Codec; import dev.isxander.yacl3.api.NameableEnum; diff --git a/src/main/java/dev/isxander/controlify/ingame/PickBlockAccessor.java b/src/main/java/dev/isxander/controlify/impl/ingame/PickBlockAccessor.java similarity index 83% rename from src/main/java/dev/isxander/controlify/ingame/PickBlockAccessor.java rename to src/main/java/dev/isxander/controlify/impl/ingame/PickBlockAccessor.java index 677bb8dc9..36aa7c2e1 100644 --- a/src/main/java/dev/isxander/controlify/ingame/PickBlockAccessor.java +++ b/src/main/java/dev/isxander/controlify/impl/ingame/PickBlockAccessor.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.ingame; +package dev.isxander.controlify.impl.ingame; public interface PickBlockAccessor { void controlify$pickBlock(); diff --git a/src/main/java/dev/isxander/controlify/input/SensorType.java b/src/main/java/dev/isxander/controlify/impl/input/SensorType.java similarity index 93% rename from src/main/java/dev/isxander/controlify/input/SensorType.java rename to src/main/java/dev/isxander/controlify/impl/input/SensorType.java index 220cba2cc..4087817a6 100644 --- a/src/main/java/dev/isxander/controlify/input/SensorType.java +++ b/src/main/java/dev/isxander/controlify/impl/input/SensorType.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.input; +package dev.isxander.controlify.impl.input; public enum SensorType { /// Sensor components are `xyz` and in radians. diff --git a/src/main/java/dev/isxander/controlify/input/mapping/ControllerMapping.java b/src/main/java/dev/isxander/controlify/impl/input/mapping/ControllerMapping.java similarity index 97% rename from src/main/java/dev/isxander/controlify/input/mapping/ControllerMapping.java rename to src/main/java/dev/isxander/controlify/impl/input/mapping/ControllerMapping.java index 8559df0c9..4b7987801 100644 --- a/src/main/java/dev/isxander/controlify/input/mapping/ControllerMapping.java +++ b/src/main/java/dev/isxander/controlify/impl/input/mapping/ControllerMapping.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.input.mapping; +package dev.isxander.controlify.impl.input.mapping; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; diff --git a/src/main/java/dev/isxander/controlify/input/mapping/ControllerMappingManager.java b/src/main/java/dev/isxander/controlify/impl/input/mapping/ControllerMappingManager.java similarity index 92% rename from src/main/java/dev/isxander/controlify/input/mapping/ControllerMappingManager.java rename to src/main/java/dev/isxander/controlify/impl/input/mapping/ControllerMappingManager.java index 6224bdd7b..be909bf06 100644 --- a/src/main/java/dev/isxander/controlify/input/mapping/ControllerMappingManager.java +++ b/src/main/java/dev/isxander/controlify/impl/input/mapping/ControllerMappingManager.java @@ -1,11 +1,11 @@ -package dev.isxander.controlify.input.mapping; +package dev.isxander.controlify.impl.input.mapping; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.mojang.serialization.DataResult; import com.mojang.serialization.JsonOps; -import dev.isxander.controlify.platform.client.resource.SimpleControlifyReloadListener; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.FileToIdConverter; import net.minecraft.resources.Identifier; import net.minecraft.server.packs.resources.Resource; diff --git a/src/main/java/dev/isxander/controlify/input/mapping/DeadzoneGroup.java b/src/main/java/dev/isxander/controlify/impl/input/mapping/DeadzoneGroup.java similarity index 92% rename from src/main/java/dev/isxander/controlify/input/mapping/DeadzoneGroup.java rename to src/main/java/dev/isxander/controlify/impl/input/mapping/DeadzoneGroup.java index 7cbbb3cb1..fc1f8c1ac 100644 --- a/src/main/java/dev/isxander/controlify/input/mapping/DeadzoneGroup.java +++ b/src/main/java/dev/isxander/controlify/impl/input/mapping/DeadzoneGroup.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.input.mapping; +package dev.isxander.controlify.impl.input.mapping; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; diff --git a/src/main/java/dev/isxander/controlify/input/mapping/GamepadInputs.java b/src/main/java/dev/isxander/controlify/impl/input/mapping/GamepadInputs.java similarity index 97% rename from src/main/java/dev/isxander/controlify/input/mapping/GamepadInputs.java rename to src/main/java/dev/isxander/controlify/impl/input/mapping/GamepadInputs.java index bd654e24d..309bb3ce7 100644 --- a/src/main/java/dev/isxander/controlify/input/mapping/GamepadInputs.java +++ b/src/main/java/dev/isxander/controlify/impl/input/mapping/GamepadInputs.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.input.mapping; +package dev.isxander.controlify.impl.input.mapping; import com.google.common.collect.Sets; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; import java.util.List; diff --git a/src/main/java/dev/isxander/controlify/input/mapping/GestureInputs.java b/src/main/java/dev/isxander/controlify/impl/input/mapping/GestureInputs.java similarity index 64% rename from src/main/java/dev/isxander/controlify/input/mapping/GestureInputs.java rename to src/main/java/dev/isxander/controlify/impl/input/mapping/GestureInputs.java index 66fcfb7cf..ba67cfafc 100644 --- a/src/main/java/dev/isxander/controlify/input/mapping/GestureInputs.java +++ b/src/main/java/dev/isxander/controlify/impl/input/mapping/GestureInputs.java @@ -1,6 +1,6 @@ -package dev.isxander.controlify.input.mapping; +package dev.isxander.controlify.impl.input.mapping; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; public final class GestureInputs { diff --git a/src/main/java/dev/isxander/controlify/input/mapping/Inputs.java b/src/main/java/dev/isxander/controlify/impl/input/mapping/Inputs.java similarity index 94% rename from src/main/java/dev/isxander/controlify/input/mapping/Inputs.java rename to src/main/java/dev/isxander/controlify/impl/input/mapping/Inputs.java index beccfc754..74ffc4bdb 100644 --- a/src/main/java/dev/isxander/controlify/input/mapping/Inputs.java +++ b/src/main/java/dev/isxander/controlify/impl/input/mapping/Inputs.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.input.mapping; +package dev.isxander.controlify.impl.input.mapping; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; diff --git a/src/main/java/dev/isxander/controlify/input/mapping/JoystickInputs.java b/src/main/java/dev/isxander/controlify/impl/input/mapping/JoystickInputs.java similarity index 91% rename from src/main/java/dev/isxander/controlify/input/mapping/JoystickInputs.java rename to src/main/java/dev/isxander/controlify/impl/input/mapping/JoystickInputs.java index 8fbf51cb8..bb20148f9 100644 --- a/src/main/java/dev/isxander/controlify/input/mapping/JoystickInputs.java +++ b/src/main/java/dev/isxander/controlify/impl/input/mapping/JoystickInputs.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.input.mapping; +package dev.isxander.controlify.impl.input.mapping; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; public final class JoystickInputs { diff --git a/src/main/java/dev/isxander/controlify/input/mapping/MapType.java b/src/main/java/dev/isxander/controlify/impl/input/mapping/MapType.java similarity index 90% rename from src/main/java/dev/isxander/controlify/input/mapping/MapType.java rename to src/main/java/dev/isxander/controlify/impl/input/mapping/MapType.java index f5fbbddcd..c80b207c5 100644 --- a/src/main/java/dev/isxander/controlify/input/mapping/MapType.java +++ b/src/main/java/dev/isxander/controlify/impl/input/mapping/MapType.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.input.mapping; +package dev.isxander.controlify.impl.input.mapping; import com.mojang.serialization.Codec; import net.minecraft.util.StringRepresentable; diff --git a/src/main/java/dev/isxander/controlify/input/mapping/MappingEntry.java b/src/main/java/dev/isxander/controlify/impl/input/mapping/MappingEntry.java similarity index 95% rename from src/main/java/dev/isxander/controlify/input/mapping/MappingEntry.java rename to src/main/java/dev/isxander/controlify/impl/input/mapping/MappingEntry.java index 845b81f6b..a1743f2bd 100644 --- a/src/main/java/dev/isxander/controlify/input/mapping/MappingEntry.java +++ b/src/main/java/dev/isxander/controlify/impl/input/mapping/MappingEntry.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.input.mapping; +package dev.isxander.controlify.impl.input.mapping; import com.mojang.serialization.Codec; -import dev.isxander.controlify.input.signal.PhysicalInputSignal; -import dev.isxander.controlify.utils.MthExt; +import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; +import dev.isxander.controlify.impl.utils.MthExt; import net.minecraft.resources.Identifier; public sealed interface MappingEntry { diff --git a/src/main/java/dev/isxander/controlify/input/mapping/MappingEntryCodecs.java b/src/main/java/dev/isxander/controlify/impl/input/mapping/MappingEntryCodecs.java similarity index 98% rename from src/main/java/dev/isxander/controlify/input/mapping/MappingEntryCodecs.java rename to src/main/java/dev/isxander/controlify/impl/input/mapping/MappingEntryCodecs.java index 94c6ca3f2..8e94a35b1 100644 --- a/src/main/java/dev/isxander/controlify/input/mapping/MappingEntryCodecs.java +++ b/src/main/java/dev/isxander/controlify/impl/input/mapping/MappingEntryCodecs.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.input.mapping; +package dev.isxander.controlify.impl.input.mapping; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; diff --git a/src/main/java/dev/isxander/controlify/input/pipeline/DeadzoneStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStage.java similarity index 89% rename from src/main/java/dev/isxander/controlify/input/pipeline/DeadzoneStage.java rename to src/main/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStage.java index 26020acb4..4e9aa5a14 100644 --- a/src/main/java/dev/isxander/controlify/input/pipeline/DeadzoneStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStage.java @@ -1,10 +1,10 @@ -package dev.isxander.controlify.input.pipeline; +package dev.isxander.controlify.impl.input.pipeline; -import dev.isxander.controlify.input.mapping.DeadzoneGroup; -import dev.isxander.controlify.input.signal.PhysicalInputSignal; -import dev.isxander.controlify.input.signal.PhysicalSignal; -import dev.isxander.controlify.utils.event.EventSink; -import dev.isxander.controlify.utils.event.UnaryEventStage; +import dev.isxander.controlify.impl.input.mapping.DeadzoneGroup; +import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; +import dev.isxander.controlify.impl.input.signal.PhysicalSignal; +import dev.isxander.controlify.impl.utils.event.EventSink; +import dev.isxander.controlify.impl.utils.event.UnaryEventStage; import it.unimi.dsi.fastutil.objects.*; import net.minecraft.resources.Identifier; import net.minecraft.util.Mth; diff --git a/src/main/java/dev/isxander/controlify/input/pipeline/GestureRecogniserStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/GestureRecogniserStage.java similarity index 56% rename from src/main/java/dev/isxander/controlify/input/pipeline/GestureRecogniserStage.java rename to src/main/java/dev/isxander/controlify/impl/input/pipeline/GestureRecogniserStage.java index ec478d8f7..c5042ea49 100644 --- a/src/main/java/dev/isxander/controlify/input/pipeline/GestureRecogniserStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/GestureRecogniserStage.java @@ -1,10 +1,10 @@ -package dev.isxander.controlify.input.pipeline; +package dev.isxander.controlify.impl.input.pipeline; -import dev.isxander.controlify.input.mapping.GestureInputs; -import dev.isxander.controlify.input.signal.PhysicalSignal; -import dev.isxander.controlify.input.signal.PhysicalInputSignal; -import dev.isxander.controlify.utils.event.EventSink; -import dev.isxander.controlify.utils.event.UnaryEventStage; +import dev.isxander.controlify.impl.input.mapping.GestureInputs; +import dev.isxander.controlify.impl.input.signal.PhysicalSignal; +import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; +import dev.isxander.controlify.impl.utils.event.EventSink; +import dev.isxander.controlify.impl.utils.event.UnaryEventStage; /// Processes sensor data and recognizes distinct gestures such as shaking of the controller. /// This stage then emits {@link PhysicalInputSignal.ButtonChanged} events diff --git a/src/main/java/dev/isxander/controlify/input/pipeline/InputDomainExpansionStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputDomainExpansionStage.java similarity index 83% rename from src/main/java/dev/isxander/controlify/input/pipeline/InputDomainExpansionStage.java rename to src/main/java/dev/isxander/controlify/impl/input/pipeline/InputDomainExpansionStage.java index 4b94ca556..50516b2f2 100644 --- a/src/main/java/dev/isxander/controlify/input/pipeline/InputDomainExpansionStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputDomainExpansionStage.java @@ -1,9 +1,9 @@ -package dev.isxander.controlify.input.pipeline; +package dev.isxander.controlify.impl.input.pipeline; -import dev.isxander.controlify.input.signal.PhysicalInputSignal; -import dev.isxander.controlify.input.signal.PhysicalSignal; -import dev.isxander.controlify.utils.event.EventSink; -import dev.isxander.controlify.utils.event.UnaryEventStage; +import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; +import dev.isxander.controlify.impl.input.signal.PhysicalSignal; +import dev.isxander.controlify.impl.utils.event.EventSink; +import dev.isxander.controlify.impl.utils.event.UnaryEventStage; import net.minecraft.resources.Identifier; import java.util.HashSet; diff --git a/src/main/java/dev/isxander/controlify/input/pipeline/InputPipeline.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputPipeline.java similarity index 86% rename from src/main/java/dev/isxander/controlify/input/pipeline/InputPipeline.java rename to src/main/java/dev/isxander/controlify/impl/input/pipeline/InputPipeline.java index 764b924d3..5475b9921 100644 --- a/src/main/java/dev/isxander/controlify/input/pipeline/InputPipeline.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputPipeline.java @@ -1,11 +1,11 @@ -package dev.isxander.controlify.input.pipeline; - -import dev.isxander.controlify.input.mapping.ControllerMapping; -import dev.isxander.controlify.input.signal.LogicalSignal; -import dev.isxander.controlify.input.signal.PhysicalSignal; -import dev.isxander.controlify.utils.event.EventSink; -import dev.isxander.controlify.utils.event.EventSource; -import dev.isxander.controlify.utils.event.EventTap; +package dev.isxander.controlify.impl.input.pipeline; + +import dev.isxander.controlify.impl.input.mapping.ControllerMapping; +import dev.isxander.controlify.impl.input.signal.LogicalSignal; +import dev.isxander.controlify.impl.input.signal.PhysicalSignal; +import dev.isxander.controlify.impl.utils.event.EventSink; +import dev.isxander.controlify.impl.utils.event.EventSource; +import dev.isxander.controlify.impl.utils.event.EventTap; public class InputPipeline implements EventSink { private final EventTap tapRawPhysicalSignals; diff --git a/src/main/java/dev/isxander/controlify/input/pipeline/MappingStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/MappingStage.java similarity index 68% rename from src/main/java/dev/isxander/controlify/input/pipeline/MappingStage.java rename to src/main/java/dev/isxander/controlify/impl/input/pipeline/MappingStage.java index 38d83fcba..d87e2a87d 100644 --- a/src/main/java/dev/isxander/controlify/input/pipeline/MappingStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/MappingStage.java @@ -1,11 +1,11 @@ -package dev.isxander.controlify.input.pipeline; +package dev.isxander.controlify.impl.input.pipeline; -import dev.isxander.controlify.input.mapping.ControllerMapping; -import dev.isxander.controlify.input.mapping.MappingEntry; -import dev.isxander.controlify.input.signal.PhysicalInputSignal; -import dev.isxander.controlify.input.signal.PhysicalSignal; -import dev.isxander.controlify.utils.event.EventSink; -import dev.isxander.controlify.utils.event.UnaryEventStage; +import dev.isxander.controlify.impl.input.mapping.ControllerMapping; +import dev.isxander.controlify.impl.input.mapping.MappingEntry; +import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; +import dev.isxander.controlify.impl.input.signal.PhysicalSignal; +import dev.isxander.controlify.impl.utils.event.EventSink; +import dev.isxander.controlify.impl.utils.event.UnaryEventStage; import net.minecraft.resources.Identifier; import java.util.Map; diff --git a/src/main/java/dev/isxander/controlify/input/pipeline/PhysicalToLogicalStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/PhysicalToLogicalStage.java similarity index 93% rename from src/main/java/dev/isxander/controlify/input/pipeline/PhysicalToLogicalStage.java rename to src/main/java/dev/isxander/controlify/impl/input/pipeline/PhysicalToLogicalStage.java index 385d2d0bd..0c7938cf7 100644 --- a/src/main/java/dev/isxander/controlify/input/pipeline/PhysicalToLogicalStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/PhysicalToLogicalStage.java @@ -1,12 +1,12 @@ -package dev.isxander.controlify.input.pipeline; - -import dev.isxander.controlify.input.LogicalInput; -import dev.isxander.controlify.input.signal.LogicalSignal; -import dev.isxander.controlify.input.signal.PhysicalInputSignal; -import dev.isxander.controlify.input.signal.PhysicalSensorSignal; -import dev.isxander.controlify.input.signal.PhysicalSignal; -import dev.isxander.controlify.utils.event.EventSink; -import dev.isxander.controlify.utils.event.EventStage; +package dev.isxander.controlify.impl.input.pipeline; + +import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.impl.input.signal.LogicalSignal; +import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; +import dev.isxander.controlify.impl.input.signal.PhysicalSensorSignal; +import dev.isxander.controlify.impl.input.signal.PhysicalSignal; +import dev.isxander.controlify.impl.utils.event.EventSink; +import dev.isxander.controlify.impl.utils.event.EventStage; import net.minecraft.resources.Identifier; import java.util.HashMap; diff --git a/src/main/java/dev/isxander/controlify/input/pipeline/SensorAccumulatorStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorAccumulatorStage.java similarity index 87% rename from src/main/java/dev/isxander/controlify/input/pipeline/SensorAccumulatorStage.java rename to src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorAccumulatorStage.java index 24fb9310f..da13d90ed 100644 --- a/src/main/java/dev/isxander/controlify/input/pipeline/SensorAccumulatorStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorAccumulatorStage.java @@ -1,11 +1,11 @@ -package dev.isxander.controlify.input.pipeline; - -import dev.isxander.controlify.input.SensorType; -import dev.isxander.controlify.input.signal.LogicalSignal; -import dev.isxander.controlify.input.signal.PhysicalSensorSignal; -import dev.isxander.controlify.input.signal.PhysicalSignal; -import dev.isxander.controlify.utils.event.EventSink; -import dev.isxander.controlify.utils.event.EventStage; +package dev.isxander.controlify.impl.input.pipeline; + +import dev.isxander.controlify.impl.input.SensorType; +import dev.isxander.controlify.impl.input.signal.LogicalSignal; +import dev.isxander.controlify.impl.input.signal.PhysicalSensorSignal; +import dev.isxander.controlify.impl.input.signal.PhysicalSignal; +import dev.isxander.controlify.impl.utils.event.EventSink; +import dev.isxander.controlify.impl.utils.event.EventStage; import java.util.EnumMap; import java.util.Map; diff --git a/src/main/java/dev/isxander/controlify/input/pipeline/SensorProcessorStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorProcessorStage.java similarity index 90% rename from src/main/java/dev/isxander/controlify/input/pipeline/SensorProcessorStage.java rename to src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorProcessorStage.java index 4ec21c7d5..77185a49c 100644 --- a/src/main/java/dev/isxander/controlify/input/pipeline/SensorProcessorStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorProcessorStage.java @@ -1,10 +1,10 @@ -package dev.isxander.controlify.input.pipeline; +package dev.isxander.controlify.impl.input.pipeline; -import dev.isxander.controlify.input.SensorType; -import dev.isxander.controlify.input.signal.PhysicalSensorSignal; -import dev.isxander.controlify.input.signal.PhysicalSignal; -import dev.isxander.controlify.utils.event.EventSink; -import dev.isxander.controlify.utils.event.UnaryEventStage; +import dev.isxander.controlify.impl.input.SensorType; +import dev.isxander.controlify.impl.input.signal.PhysicalSensorSignal; +import dev.isxander.controlify.impl.input.signal.PhysicalSignal; +import dev.isxander.controlify.impl.utils.event.EventSink; +import dev.isxander.controlify.impl.utils.event.UnaryEventStage; import org.joml.Vector3f; import org.joml.Vector3fc; import org.jspecify.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/input/signal/LogicalSignal.java b/src/main/java/dev/isxander/controlify/impl/input/signal/LogicalSignal.java similarity index 87% rename from src/main/java/dev/isxander/controlify/input/signal/LogicalSignal.java rename to src/main/java/dev/isxander/controlify/impl/input/signal/LogicalSignal.java index 233d4c1a4..d8bf4ae14 100644 --- a/src/main/java/dev/isxander/controlify/input/signal/LogicalSignal.java +++ b/src/main/java/dev/isxander/controlify/impl/input/signal/LogicalSignal.java @@ -1,8 +1,8 @@ -package dev.isxander.controlify.input.signal; +package dev.isxander.controlify.impl.input.signal; -import dev.isxander.controlify.input.LogicalInput; -import dev.isxander.controlify.input.SensorType; -import dev.isxander.controlify.input.pipeline.SensorAccumulatorStage; +import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.impl.input.SensorType; +import dev.isxander.controlify.impl.input.pipeline.SensorAccumulatorStage; public sealed interface LogicalSignal { /// The time this signal occurred. diff --git a/src/main/java/dev/isxander/controlify/input/signal/PhysicalInputSignal.java b/src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalInputSignal.java similarity index 94% rename from src/main/java/dev/isxander/controlify/input/signal/PhysicalInputSignal.java rename to src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalInputSignal.java index aaf72386d..d8d19fd9e 100644 --- a/src/main/java/dev/isxander/controlify/input/signal/PhysicalInputSignal.java +++ b/src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalInputSignal.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.input.signal; +package dev.isxander.controlify.impl.input.signal; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/input/signal/PhysicalSensorSignal.java b/src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalSensorSignal.java similarity index 88% rename from src/main/java/dev/isxander/controlify/input/signal/PhysicalSensorSignal.java rename to src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalSensorSignal.java index 69367f56f..87389424c 100644 --- a/src/main/java/dev/isxander/controlify/input/signal/PhysicalSensorSignal.java +++ b/src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalSensorSignal.java @@ -1,6 +1,6 @@ -package dev.isxander.controlify.input.signal; +package dev.isxander.controlify.impl.input.signal; -import dev.isxander.controlify.input.SensorType; +import dev.isxander.controlify.impl.input.SensorType; /// Represents a single IMU sample /// @param timeNanos timestamp of the event in nanoseconds diff --git a/src/main/java/dev/isxander/controlify/input/signal/PhysicalSignal.java b/src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalSignal.java similarity index 86% rename from src/main/java/dev/isxander/controlify/input/signal/PhysicalSignal.java rename to src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalSignal.java index 10ec1a5cc..37485d8f2 100644 --- a/src/main/java/dev/isxander/controlify/input/signal/PhysicalSignal.java +++ b/src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalSignal.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.input.signal; +package dev.isxander.controlify.impl.input.signal; public sealed interface PhysicalSignal permits PhysicalInputSignal, PhysicalSensorSignal, PhysicalSignal.Frame { /// timestamp of the signal in nanoseconds diff --git a/src/main/java/dev/isxander/controlify/platform/ArrayBackedEventHandler.java b/src/main/java/dev/isxander/controlify/impl/platform/ArrayBackedEventHandler.java similarity index 91% rename from src/main/java/dev/isxander/controlify/platform/ArrayBackedEventHandler.java rename to src/main/java/dev/isxander/controlify/impl/platform/ArrayBackedEventHandler.java index 7235d782c..1ec13c73e 100644 --- a/src/main/java/dev/isxander/controlify/platform/ArrayBackedEventHandler.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/ArrayBackedEventHandler.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform; +package dev.isxander.controlify.impl.platform; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/dev/isxander/controlify/platform/Environment.java b/src/main/java/dev/isxander/controlify/impl/platform/Environment.java similarity index 78% rename from src/main/java/dev/isxander/controlify/platform/Environment.java rename to src/main/java/dev/isxander/controlify/impl/platform/Environment.java index c76124bc3..772ddd4ad 100644 --- a/src/main/java/dev/isxander/controlify/platform/Environment.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/Environment.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform; +package dev.isxander.controlify.impl.platform; public enum Environment { CLIENT, diff --git a/src/main/java/dev/isxander/controlify/platform/Event.java b/src/main/java/dev/isxander/controlify/impl/platform/Event.java similarity index 76% rename from src/main/java/dev/isxander/controlify/platform/Event.java rename to src/main/java/dev/isxander/controlify/impl/platform/Event.java index 35c1e70bd..9a38b539a 100644 --- a/src/main/java/dev/isxander/controlify/platform/Event.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/Event.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform; +package dev.isxander.controlify.impl.platform; public interface Event { } diff --git a/src/main/java/dev/isxander/controlify/platform/EventHandler.java b/src/main/java/dev/isxander/controlify/impl/platform/EventHandler.java similarity index 89% rename from src/main/java/dev/isxander/controlify/platform/EventHandler.java rename to src/main/java/dev/isxander/controlify/impl/platform/EventHandler.java index 8566a2fb1..bcd4311e2 100644 --- a/src/main/java/dev/isxander/controlify/platform/EventHandler.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/EventHandler.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform; +package dev.isxander.controlify.impl.platform; public interface EventHandler { void register(Callback event); diff --git a/src/main/java/dev/isxander/controlify/platform/client/CreativeTabHelper.java b/src/main/java/dev/isxander/controlify/impl/platform/client/CreativeTabHelper.java similarity index 89% rename from src/main/java/dev/isxander/controlify/platform/client/CreativeTabHelper.java rename to src/main/java/dev/isxander/controlify/impl/platform/client/CreativeTabHelper.java index 0f7c35230..cf9b14dbd 100644 --- a/src/main/java/dev/isxander/controlify/platform/client/CreativeTabHelper.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/client/CreativeTabHelper.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform.client; +package dev.isxander.controlify.impl.platform.client; import net.minecraft.world.item.CreativeModeTab; diff --git a/src/main/java/dev/isxander/controlify/platform/client/HudRenderLayer.java b/src/main/java/dev/isxander/controlify/impl/platform/client/HudRenderLayer.java similarity index 86% rename from src/main/java/dev/isxander/controlify/platform/client/HudRenderLayer.java rename to src/main/java/dev/isxander/controlify/impl/platform/client/HudRenderLayer.java index 468194d54..67d6e1343 100644 --- a/src/main/java/dev/isxander/controlify/platform/client/HudRenderLayer.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/client/HudRenderLayer.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform.client; +package dev.isxander.controlify.impl.platform.client; import net.minecraft.client.DeltaTracker; import net.minecraft.client.gui.GuiGraphicsExtractor; diff --git a/src/main/java/dev/isxander/controlify/platform/client/PlatformClientUtil.java b/src/main/java/dev/isxander/controlify/impl/platform/client/PlatformClientUtil.java similarity index 87% rename from src/main/java/dev/isxander/controlify/platform/client/PlatformClientUtil.java rename to src/main/java/dev/isxander/controlify/impl/platform/client/PlatformClientUtil.java index 26b5b97b6..54126f35b 100644 --- a/src/main/java/dev/isxander/controlify/platform/client/PlatformClientUtil.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/client/PlatformClientUtil.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform.client; +package dev.isxander.controlify.impl.platform.client; -import dev.isxander.controlify.platform.client.events.DisconnectedEvent; -import dev.isxander.controlify.platform.client.events.LifecycleEvent; -import dev.isxander.controlify.platform.client.events.ScreenRenderEvent; -import dev.isxander.controlify.platform.client.events.TickEvent; -import dev.isxander.controlify.platform.client.resource.ControlifyReloadListener; +import dev.isxander.controlify.impl.platform.client.events.DisconnectedEvent; +import dev.isxander.controlify.impl.platform.client.events.LifecycleEvent; +import dev.isxander.controlify.impl.platform.client.events.ScreenRenderEvent; +import dev.isxander.controlify.impl.platform.client.events.TickEvent; +import dev.isxander.controlify.impl.platform.client.resource.ControlifyReloadListener; import net.minecraft.client.KeyMapping; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.navigation.ScreenRectangle; diff --git a/src/main/java/dev/isxander/controlify/platform/client/PlatformClientUtilImpl.java b/src/main/java/dev/isxander/controlify/impl/platform/client/PlatformClientUtilImpl.java similarity index 90% rename from src/main/java/dev/isxander/controlify/platform/client/PlatformClientUtilImpl.java rename to src/main/java/dev/isxander/controlify/impl/platform/client/PlatformClientUtilImpl.java index 39d0deeb7..4b59befc4 100644 --- a/src/main/java/dev/isxander/controlify/platform/client/PlatformClientUtilImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/client/PlatformClientUtilImpl.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform.client; +package dev.isxander.controlify.impl.platform.client; -import dev.isxander.controlify.platform.client.events.*; -import dev.isxander.controlify.platform.client.resource.ControlifyReloadListener; +import dev.isxander.controlify.impl.platform.client.events.*; +import dev.isxander.controlify.impl.platform.client.resource.ControlifyReloadListener; import net.minecraft.client.KeyMapping; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.navigation.ScreenRectangle; diff --git a/src/main/java/dev/isxander/controlify/platform/client/events/DisconnectedEvent.java b/src/main/java/dev/isxander/controlify/impl/platform/client/events/DisconnectedEvent.java similarity index 81% rename from src/main/java/dev/isxander/controlify/platform/client/events/DisconnectedEvent.java rename to src/main/java/dev/isxander/controlify/impl/platform/client/events/DisconnectedEvent.java index a82216988..c355084f3 100644 --- a/src/main/java/dev/isxander/controlify/platform/client/events/DisconnectedEvent.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/client/events/DisconnectedEvent.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform.client.events; +package dev.isxander.controlify.impl.platform.client.events; import net.minecraft.client.Minecraft; diff --git a/src/main/java/dev/isxander/controlify/platform/client/events/LifecycleEvent.java b/src/main/java/dev/isxander/controlify/impl/platform/client/events/LifecycleEvent.java similarity index 81% rename from src/main/java/dev/isxander/controlify/platform/client/events/LifecycleEvent.java rename to src/main/java/dev/isxander/controlify/impl/platform/client/events/LifecycleEvent.java index 82cad65c1..c72576056 100644 --- a/src/main/java/dev/isxander/controlify/platform/client/events/LifecycleEvent.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/client/events/LifecycleEvent.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform.client.events; +package dev.isxander.controlify.impl.platform.client.events; import net.minecraft.client.Minecraft; diff --git a/src/main/java/dev/isxander/controlify/platform/client/events/ScreenRenderEvent.java b/src/main/java/dev/isxander/controlify/impl/platform/client/events/ScreenRenderEvent.java similarity index 86% rename from src/main/java/dev/isxander/controlify/platform/client/events/ScreenRenderEvent.java rename to src/main/java/dev/isxander/controlify/impl/platform/client/events/ScreenRenderEvent.java index 229a32215..d5e21459e 100644 --- a/src/main/java/dev/isxander/controlify/platform/client/events/ScreenRenderEvent.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/client/events/ScreenRenderEvent.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform.client.events; +package dev.isxander.controlify.impl.platform.client.events; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.screens.Screen; diff --git a/src/main/java/dev/isxander/controlify/platform/client/events/TickEvent.java b/src/main/java/dev/isxander/controlify/impl/platform/client/events/TickEvent.java similarity index 93% rename from src/main/java/dev/isxander/controlify/platform/client/events/TickEvent.java rename to src/main/java/dev/isxander/controlify/impl/platform/client/events/TickEvent.java index ca04c021b..6df6bb8f2 100644 --- a/src/main/java/dev/isxander/controlify/platform/client/events/TickEvent.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/client/events/TickEvent.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform.client.events; +package dev.isxander.controlify.impl.platform.client.events; import net.minecraft.client.Minecraft; diff --git a/src/main/java/dev/isxander/controlify/platform/client/resource/ControlifyReloadListener.java b/src/main/java/dev/isxander/controlify/impl/platform/client/resource/ControlifyReloadListener.java similarity index 84% rename from src/main/java/dev/isxander/controlify/platform/client/resource/ControlifyReloadListener.java rename to src/main/java/dev/isxander/controlify/impl/platform/client/resource/ControlifyReloadListener.java index 35e338da9..b1aeb615a 100644 --- a/src/main/java/dev/isxander/controlify/platform/client/resource/ControlifyReloadListener.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/client/resource/ControlifyReloadListener.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform.client.resource; +package dev.isxander.controlify.impl.platform.client.resource; import net.minecraft.resources.Identifier; import net.minecraft.server.packs.resources.PreparableReloadListener; diff --git a/src/main/java/dev/isxander/controlify/platform/client/resource/SimpleControlifyReloadListener.java b/src/main/java/dev/isxander/controlify/impl/platform/client/resource/SimpleControlifyReloadListener.java similarity index 96% rename from src/main/java/dev/isxander/controlify/platform/client/resource/SimpleControlifyReloadListener.java rename to src/main/java/dev/isxander/controlify/impl/platform/client/resource/SimpleControlifyReloadListener.java index d99326e4c..0760b3eb0 100644 --- a/src/main/java/dev/isxander/controlify/platform/client/resource/SimpleControlifyReloadListener.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/client/resource/SimpleControlifyReloadListener.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform.client.resource; +package dev.isxander.controlify.impl.platform.client.resource; import net.minecraft.server.packs.resources.ResourceManager; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/dev/isxander/controlify/platform/main/PlatformMainUtil.java b/src/main/java/dev/isxander/controlify/impl/platform/main/PlatformMainUtil.java similarity index 80% rename from src/main/java/dev/isxander/controlify/platform/main/PlatformMainUtil.java rename to src/main/java/dev/isxander/controlify/impl/platform/main/PlatformMainUtil.java index b421b1d96..89960b99c 100644 --- a/src/main/java/dev/isxander/controlify/platform/main/PlatformMainUtil.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/main/PlatformMainUtil.java @@ -4,15 +4,15 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform.main; - -import dev.isxander.controlify.api.entrypoint.ControlifyEntrypoint; -import dev.isxander.controlify.platform.Environment; -import dev.isxander.controlify.platform.main.events.CommandRegistrationCallbackEvent; -import dev.isxander.controlify.platform.main.events.HandshakeCompletionEvent; -import dev.isxander.controlify.platform.main.events.PlayerJoinedEvent; -import dev.isxander.controlify.platform.network.C2SNetworkApi; -import dev.isxander.controlify.platform.network.S2CNetworkApi; +package dev.isxander.controlify.impl.platform.main; + +import dev.isxander.controlify.impl.api.entrypoint.ControlifyEntrypoint; +import dev.isxander.controlify.impl.platform.Environment; +import dev.isxander.controlify.impl.platform.main.events.CommandRegistrationCallbackEvent; +import dev.isxander.controlify.impl.platform.main.events.HandshakeCompletionEvent; +import dev.isxander.controlify.impl.platform.main.events.PlayerJoinedEvent; +import dev.isxander.controlify.impl.platform.network.C2SNetworkApi; +import dev.isxander.controlify.impl.platform.network.S2CNetworkApi; import net.minecraft.core.Registry; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; diff --git a/src/main/java/dev/isxander/controlify/platform/main/PlatformMainUtilImpl.java b/src/main/java/dev/isxander/controlify/impl/platform/main/PlatformMainUtilImpl.java similarity index 71% rename from src/main/java/dev/isxander/controlify/platform/main/PlatformMainUtilImpl.java rename to src/main/java/dev/isxander/controlify/impl/platform/main/PlatformMainUtilImpl.java index 09a116265..31b0d89f4 100644 --- a/src/main/java/dev/isxander/controlify/platform/main/PlatformMainUtilImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/main/PlatformMainUtilImpl.java @@ -4,15 +4,15 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform.main; - -import dev.isxander.controlify.api.entrypoint.ControlifyEntrypoint; -import dev.isxander.controlify.platform.Environment; -import dev.isxander.controlify.platform.main.events.CommandRegistrationCallbackEvent; -import dev.isxander.controlify.platform.main.events.HandshakeCompletionEvent; -import dev.isxander.controlify.platform.main.events.PlayerJoinedEvent; -import dev.isxander.controlify.platform.network.C2SNetworkApi; -import dev.isxander.controlify.platform.network.S2CNetworkApi; +package dev.isxander.controlify.impl.platform.main; + +import dev.isxander.controlify.impl.api.entrypoint.ControlifyEntrypoint; +import dev.isxander.controlify.impl.platform.Environment; +import dev.isxander.controlify.impl.platform.main.events.CommandRegistrationCallbackEvent; +import dev.isxander.controlify.impl.platform.main.events.HandshakeCompletionEvent; +import dev.isxander.controlify.impl.platform.main.events.PlayerJoinedEvent; +import dev.isxander.controlify.impl.platform.network.C2SNetworkApi; +import dev.isxander.controlify.impl.platform.network.S2CNetworkApi; import net.minecraft.core.Registry; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; diff --git a/src/main/java/dev/isxander/controlify/platform/main/events/CommandRegistrationCallbackEvent.java b/src/main/java/dev/isxander/controlify/impl/platform/main/events/CommandRegistrationCallbackEvent.java similarity index 89% rename from src/main/java/dev/isxander/controlify/platform/main/events/CommandRegistrationCallbackEvent.java rename to src/main/java/dev/isxander/controlify/impl/platform/main/events/CommandRegistrationCallbackEvent.java index 405fef1ce..12d52d30d 100644 --- a/src/main/java/dev/isxander/controlify/platform/main/events/CommandRegistrationCallbackEvent.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/main/events/CommandRegistrationCallbackEvent.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform.main.events; +package dev.isxander.controlify.impl.platform.main.events; import com.mojang.brigadier.CommandDispatcher; import net.minecraft.commands.CommandBuildContext; diff --git a/src/main/java/dev/isxander/controlify/platform/main/events/HandshakeCompletionEvent.java b/src/main/java/dev/isxander/controlify/impl/platform/main/events/HandshakeCompletionEvent.java similarity index 86% rename from src/main/java/dev/isxander/controlify/platform/main/events/HandshakeCompletionEvent.java rename to src/main/java/dev/isxander/controlify/impl/platform/main/events/HandshakeCompletionEvent.java index ec4d107b7..374675f42 100644 --- a/src/main/java/dev/isxander/controlify/platform/main/events/HandshakeCompletionEvent.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/main/events/HandshakeCompletionEvent.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform.main.events; +package dev.isxander.controlify.impl.platform.main.events; import net.minecraft.server.network.ServerLoginPacketListenerImpl; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/platform/main/events/PlayerJoinedEvent.java b/src/main/java/dev/isxander/controlify/impl/platform/main/events/PlayerJoinedEvent.java similarity index 81% rename from src/main/java/dev/isxander/controlify/platform/main/events/PlayerJoinedEvent.java rename to src/main/java/dev/isxander/controlify/impl/platform/main/events/PlayerJoinedEvent.java index 2bb5cc0f4..fc6c20196 100644 --- a/src/main/java/dev/isxander/controlify/platform/main/events/PlayerJoinedEvent.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/main/events/PlayerJoinedEvent.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform.main.events; +package dev.isxander.controlify.impl.platform.main.events; import net.minecraft.server.level.ServerPlayer; diff --git a/src/main/java/dev/isxander/controlify/platform/network/C2SNetworkApi.java b/src/main/java/dev/isxander/controlify/impl/platform/network/C2SNetworkApi.java similarity index 92% rename from src/main/java/dev/isxander/controlify/platform/network/C2SNetworkApi.java rename to src/main/java/dev/isxander/controlify/impl/platform/network/C2SNetworkApi.java index 8870ac944..677fcde12 100644 --- a/src/main/java/dev/isxander/controlify/platform/network/C2SNetworkApi.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/network/C2SNetworkApi.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform.network; +package dev.isxander.controlify.impl.platform.network; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/platform/network/S2CNetworkApi.java b/src/main/java/dev/isxander/controlify/impl/platform/network/S2CNetworkApi.java similarity index 90% rename from src/main/java/dev/isxander/controlify/platform/network/S2CNetworkApi.java rename to src/main/java/dev/isxander/controlify/impl/platform/network/S2CNetworkApi.java index 97c935404..73315e5a7 100644 --- a/src/main/java/dev/isxander/controlify/platform/network/S2CNetworkApi.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/network/S2CNetworkApi.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform.network; +package dev.isxander.controlify.impl.platform.network; import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerPlayer; diff --git a/src/main/java/dev/isxander/controlify/platform/network/SidedNetworkApi.java b/src/main/java/dev/isxander/controlify/impl/platform/network/SidedNetworkApi.java similarity index 81% rename from src/main/java/dev/isxander/controlify/platform/network/SidedNetworkApi.java rename to src/main/java/dev/isxander/controlify/impl/platform/network/SidedNetworkApi.java index 3987bf612..a1e9471ed 100644 --- a/src/main/java/dev/isxander/controlify/platform/network/SidedNetworkApi.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/network/SidedNetworkApi.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.platform.network; +package dev.isxander.controlify.impl.platform.network; -import dev.isxander.controlify.platform.main.PlatformMainUtil; +import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/reacharound/ReachAroundHandler.java b/src/main/java/dev/isxander/controlify/impl/reacharound/ReachAroundHandler.java similarity index 93% rename from src/main/java/dev/isxander/controlify/reacharound/ReachAroundHandler.java rename to src/main/java/dev/isxander/controlify/impl/reacharound/ReachAroundHandler.java index 44afd6814..76b15fdea 100644 --- a/src/main/java/dev/isxander/controlify/reacharound/ReachAroundHandler.java +++ b/src/main/java/dev/isxander/controlify/impl/reacharound/ReachAroundHandler.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.reacharound; +package dev.isxander.controlify.impl.reacharound; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.server.ServerPolicies; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.server.ServerPolicies; import net.minecraft.world.entity.Entity; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; diff --git a/src/main/java/dev/isxander/controlify/reacharound/ReachAroundMode.java b/src/main/java/dev/isxander/controlify/impl/reacharound/ReachAroundMode.java similarity index 94% rename from src/main/java/dev/isxander/controlify/reacharound/ReachAroundMode.java rename to src/main/java/dev/isxander/controlify/impl/reacharound/ReachAroundMode.java index b714f4c19..fcf7d8375 100644 --- a/src/main/java/dev/isxander/controlify/reacharound/ReachAroundMode.java +++ b/src/main/java/dev/isxander/controlify/impl/reacharound/ReachAroundMode.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.reacharound; +package dev.isxander.controlify.impl.reacharound; import com.mojang.serialization.Codec; -import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.impl.Controlify; import dev.isxander.yacl3.api.NameableEnum; import net.minecraft.client.Minecraft; import net.minecraft.network.chat.Component; diff --git a/src/main/java/dev/isxander/controlify/screenop/ComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/ComponentProcessor.java similarity index 70% rename from src/main/java/dev/isxander/controlify/screenop/ComponentProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/ComponentProcessor.java index 7afa9fdb5..1c563aad9 100644 --- a/src/main/java/dev/isxander/controlify/screenop/ComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/ComponentProcessor.java @@ -4,30 +4,30 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop; +package dev.isxander.controlify.impl.screenop; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.keyboard.ComponentKeyboardBehaviour; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.keyboard.ComponentKeyboardBehaviour; public interface ComponentProcessor extends ComponentProcessorProvider { ComponentProcessor EMPTY = new ComponentProcessor(){}; - default boolean overrideControllerNavigation(ScreenProcessor screen, ControllerEntity controller) { + default boolean overrideControllerNavigation(ScreenProcessor screen, ControllerImpl controller) { return false; } - default boolean overrideControllerButtons(ScreenProcessor screen, ControllerEntity controller) { + default boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { return false; } - default void onFocusGained(ScreenProcessor screen, ControllerEntity controller) { + default void onFocusGained(ScreenProcessor screen, ControllerImpl controller) { } default boolean shouldKeepFocusOnKeyboardMode(ScreenProcessor screen) { return false; } - default ComponentKeyboardBehaviour getKeyboardBehaviour(ScreenProcessor screen, ControllerEntity controller) { + default ComponentKeyboardBehaviour getKeyboardBehaviour(ScreenProcessor screen, ControllerImpl controller) { return ComponentKeyboardBehaviour.UNDEFINED; } diff --git a/src/main/java/dev/isxander/controlify/screenop/ComponentProcessorProvider.java b/src/main/java/dev/isxander/controlify/impl/screenop/ComponentProcessorProvider.java similarity index 93% rename from src/main/java/dev/isxander/controlify/screenop/ComponentProcessorProvider.java rename to src/main/java/dev/isxander/controlify/impl/screenop/ComponentProcessorProvider.java index 975013be6..847162b9d 100644 --- a/src/main/java/dev/isxander/controlify/screenop/ComponentProcessorProvider.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/ComponentProcessorProvider.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop; +package dev.isxander.controlify.impl.screenop; import net.minecraft.client.gui.components.events.GuiEventListener; diff --git a/src/main/java/dev/isxander/controlify/screenop/CustomFocus.java b/src/main/java/dev/isxander/controlify/impl/screenop/CustomFocus.java similarity index 84% rename from src/main/java/dev/isxander/controlify/screenop/CustomFocus.java rename to src/main/java/dev/isxander/controlify/impl/screenop/CustomFocus.java index 911b95650..715e199b0 100644 --- a/src/main/java/dev/isxander/controlify/screenop/CustomFocus.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/CustomFocus.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop; +package dev.isxander.controlify.impl.screenop; import net.minecraft.client.gui.components.events.GuiEventListener; diff --git a/src/main/java/dev/isxander/controlify/screenop/Registry.java b/src/main/java/dev/isxander/controlify/impl/screenop/Registry.java similarity index 96% rename from src/main/java/dev/isxander/controlify/screenop/Registry.java rename to src/main/java/dev/isxander/controlify/impl/screenop/Registry.java index a40487638..bcb1132ff 100644 --- a/src/main/java/dev/isxander/controlify/screenop/Registry.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/Registry.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop; +package dev.isxander.controlify.impl.screenop; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.jetbrains.annotations.ApiStatus; diff --git a/src/main/java/dev/isxander/controlify/screenop/ScreenControllerEventListener.java b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenControllerEventListener.java similarity index 50% rename from src/main/java/dev/isxander/controlify/screenop/ScreenControllerEventListener.java rename to src/main/java/dev/isxander/controlify/impl/screenop/ScreenControllerEventListener.java index d3dccd778..4aec4936d 100644 --- a/src/main/java/dev/isxander/controlify/screenop/ScreenControllerEventListener.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenControllerEventListener.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop; +package dev.isxander.controlify.impl.screenop; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.impl.controller.ControllerImpl; public interface ScreenControllerEventListener { - default void onControllerInput(ControllerEntity controller) {} + default void onControllerInput(ControllerImpl controller) {} } diff --git a/src/main/java/dev/isxander/controlify/screenop/ScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessor.java similarity index 87% rename from src/main/java/dev/isxander/controlify/screenop/ScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessor.java index 330fc61e0..42253b38b 100644 --- a/src/main/java/dev/isxander/controlify/screenop/ScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessor.java @@ -4,26 +4,25 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop; +package dev.isxander.controlify.impl.screenop; import com.mojang.blaze3d.platform.InputConstants; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.InputMode; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.controller.*; -import dev.isxander.controlify.controller.haptic.HapticEffects; -import dev.isxander.controlify.controller.input.ControllerStateView; -import dev.isxander.controlify.input.mapping.GamepadInputs; -import dev.isxander.controlify.controller.input.InputComponent; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.api.InputMode; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.impl.controller.haptic.HapticEffects; +import dev.isxander.controlify.impl.controller.input.ControllerStateView; +import dev.isxander.controlify.impl.input.mapping.GamepadInputs; +import dev.isxander.controlify.impl.controller.input.InputComponent; import dev.isxander.controlify.mixins.feature.screenop.ScreenAccessor; import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.TabNavigationBarAccessor; -import dev.isxander.controlify.screenop.keyboard.*; -import dev.isxander.controlify.sound.ControlifyClientSounds; -import dev.isxander.controlify.utils.HoldRepeatHelper; -import dev.isxander.controlify.utils.MinecraftUtil; -import dev.isxander.controlify.virtualmouse.VirtualMouseBehaviour; -import dev.isxander.controlify.virtualmouse.VirtualMouseHandler; +import dev.isxander.controlify.impl.screenop.keyboard.*; +import dev.isxander.controlify.impl.sound.ControlifyClientSounds; +import dev.isxander.controlify.impl.utils.HoldRepeatHelper; +import dev.isxander.controlify.impl.utils.MinecraftUtil; +import dev.isxander.controlify.impl.virtualmouse.VirtualMouseBehaviour; +import dev.isxander.controlify.impl.virtualmouse.VirtualMouseHandler; import net.minecraft.client.InputType; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; @@ -58,7 +57,7 @@ public ScreenProcessor(T screen) { this.holdRepeatHelper = createHoldRepeatHelper(); } - public void onControllerFixedUpdate(ControllerEntity controller) { + public void onControllerFixedUpdate(ControllerImpl controller) { Controlify.instance().virtualMouseHandler().handleControllerInput(controller); if (!Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled()) { @@ -76,7 +75,7 @@ public void onControllerFixedUpdate(ControllerEntity controller) { eventListeners.forEach(listener -> listener.onControllerInput(controller)); } - public void render(ControllerEntity controller, GuiGraphicsExtractor graphics, float tickDelta) { + public void render(ControllerImpl controller, GuiGraphicsExtractor graphics, float tickDelta) { var vmouse = Controlify.instance().virtualMouseHandler(); this.render(controller, graphics, tickDelta, vmouse.isVirtualMouseEnabled() ? Optional.of(vmouse) : Optional.empty()); } @@ -99,7 +98,7 @@ public void onInputModeChanged(InputMode mode) { } } - protected void handleComponentNavigation(ControllerEntity controller) { + protected void handleComponentNavigation(ControllerImpl controller) { if (screen.getFocused() == null) { ((ScreenAccessor) screen).invokeSetInitialFocus(); } @@ -196,7 +195,7 @@ protected void handleComponentNavigation(ControllerEntity controller) { }; } - protected void handleButtons(ControllerEntity controller) { + protected void handleButtons(ControllerImpl controller) { boolean vmouseEnabled = Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled(); InputComponent input = controller.input().orElseThrow(); boolean touchpadPressed = input.stateNow().isButtonDown(GamepadInputs.TOUCHPAD_1_BUTTON); @@ -213,11 +212,11 @@ protected void handleButtons(ControllerEntity controller) { } } - protected void handleScreenVMouse(ControllerEntity controller, VirtualMouseHandler vmouse) { + protected void handleScreenVMouse(ControllerImpl controller, VirtualMouseHandler vmouse) { Minecraft.getInstance().setLastInputType(InputType.MOUSE); } - protected boolean handleComponentButtonOverride(ControllerEntity controller) { + protected boolean handleComponentButtonOverride(ControllerImpl controller) { var focusTree = getFocusTree(); while (!focusTree.isEmpty()) { var focused = focusTree.poll(); @@ -228,7 +227,7 @@ protected boolean handleComponentButtonOverride(ControllerEntity controller) { return false; } - protected boolean handleComponentNavOverride(ControllerEntity controller) { + protected boolean handleComponentNavOverride(ControllerImpl controller) { var focusTree = getFocusTree(); while (!focusTree.isEmpty()) { var focused = focusTree.poll(); @@ -238,7 +237,7 @@ protected boolean handleComponentNavOverride(ControllerEntity controller) { return false; } - protected void handleTabNavigation(ControllerEntity controller) { + protected void handleTabNavigation(ControllerImpl controller) { var nextTab = ControlifyBindings.GUI_NEXT_TAB.on(controller).justPressed(); var prevTab = ControlifyBindings.GUI_PREV_TAB.on(controller).justPressed(); @@ -262,7 +261,7 @@ protected void handleTabNavigation(ControllerEntity controller) { } } - protected void onTabChanged(ControllerEntity controller) { + protected void onTabChanged(ControllerImpl controller) { } @@ -282,7 +281,7 @@ public void onVirtualMouseToggled(boolean enabled) { } } - protected void render(ControllerEntity controller, GuiGraphicsExtractor graphics, float tickDelta, Optional vmouse) { + protected void render(ControllerImpl controller, GuiGraphicsExtractor graphics, float tickDelta, Optional vmouse) { } @@ -294,7 +293,7 @@ public void addEventListener(ScreenControllerEventListener listener) { eventListeners.add(listener); } - public boolean tryOpenKeyboard(ControllerEntity controller, @Nullable GuiEventListener element) { + public boolean tryOpenKeyboard(ControllerImpl controller, @Nullable GuiEventListener element) { var componentProcessor = ComponentProcessorProvider.provide(element); ComponentKeyboardBehaviour behaviour = componentProcessor.getKeyboardBehaviour(this, controller); diff --git a/src/main/java/dev/isxander/controlify/screenop/ScreenProcessorFactory.java b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessorFactory.java similarity index 96% rename from src/main/java/dev/isxander/controlify/screenop/ScreenProcessorFactory.java rename to src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessorFactory.java index e828ad843..567acfdbe 100644 --- a/src/main/java/dev/isxander/controlify/screenop/ScreenProcessorFactory.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessorFactory.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop; +package dev.isxander.controlify.impl.screenop; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.client.gui.screens.Screen; diff --git a/src/main/java/dev/isxander/controlify/screenop/ScreenProcessorProvider.java b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessorProvider.java similarity index 93% rename from src/main/java/dev/isxander/controlify/screenop/ScreenProcessorProvider.java rename to src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessorProvider.java index d472e2e09..d2c52c79f 100644 --- a/src/main/java/dev/isxander/controlify/screenop/ScreenProcessorProvider.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessorProvider.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop; +package dev.isxander.controlify.impl.screenop; import net.minecraft.client.gui.screens.Screen; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/AbstractSliderComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/AbstractSliderComponentProcessor.java similarity index 67% rename from src/main/java/dev/isxander/controlify/screenop/compat/AbstractSliderComponentProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/AbstractSliderComponentProcessor.java index 00ba690c4..95276c4d1 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/AbstractSliderComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/AbstractSliderComponentProcessor.java @@ -4,23 +4,23 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat; +package dev.isxander.controlify.impl.screenop.compat; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.utils.HoldRepeatHelper; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.utils.HoldRepeatHelper; /** * A component processor that handles incrementing and decrementing a slider. - * This uses {@link dev.isxander.controlify.bindings.ControllerBindings#CYCLE_OPT_FORWARD} and {@link dev.isxander.controlify.bindings.ControllerBindings#CYCLE_OPT_BACKWARD} to increment and decrement the slider. + * This uses {@link dev.isxander.controlify.impl.bindings.ControllerBindings#CYCLE_OPT_FORWARD} and {@link dev.isxander.controlify.impl.bindings.ControllerBindings#CYCLE_OPT_BACKWARD} to increment and decrement the slider. */ public abstract class AbstractSliderComponentProcessor implements ComponentProcessor { private final HoldRepeatHelper holdRepeatHelper = new HoldRepeatHelper(15, 3); @Override - public boolean overrideControllerNavigation(ScreenProcessor screen, ControllerEntity controller) { + public boolean overrideControllerNavigation(ScreenProcessor screen, ControllerImpl controller) { var left = ControlifyBindings.GUI_SECONDARY_NAVI_LEFT.on(controller).digitalNow(); var leftPrev = ControlifyBindings.GUI_SECONDARY_NAVI_LEFT.on(controller).digitalPrev(); var right = ControlifyBindings.GUI_SECONDARY_NAVI_RIGHT.on(controller).digitalNow(); diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AbstractButtonComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractButtonComponentProcessor.java similarity index 68% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AbstractButtonComponentProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractButtonComponentProcessor.java index b7aeb4503..06e96e3ce 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AbstractButtonComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractButtonComponentProcessor.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.haptic.HapticEffects; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controller.haptic.HapticEffects; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.AbstractButton; import com.mojang.blaze3d.platform.InputConstants; @@ -24,7 +24,7 @@ public AbstractButtonComponentProcessor(AbstractButton button) { } @Override - public boolean overrideControllerButtons(ScreenProcessor screen, ControllerEntity controller) { + public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { if (ControlifyBindings.GUI_PRESS.on(controller).guiPressed().get()) { controller.hdHaptics().ifPresent(hh -> hh.playHaptic(HapticEffects.NAVIGATE)); button.playDownSound(Minecraft.getInstance().getSoundManager()); diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AbstractContainerScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractContainerScreenProcessor.java similarity index 83% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AbstractContainerScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractContainerScreenProcessor.java index 4753536c1..bb0891943 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AbstractContainerScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractContainerScreenProcessor.java @@ -4,22 +4,22 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.InputMode; +import dev.isxander.controlify.api.InputMode; import dev.isxander.controlify.api.ControlifyApi; import dev.isxander.controlify.api.contextual.ContainerContext; import dev.isxander.controlify.api.contextual.GuideInstance; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.contextual.ContextualDomains; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.haptic.HapticEffects; -import dev.isxander.controlify.gui.guide.GuideRenderer; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.contextual.ContextualDomains; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controller.haptic.HapticEffects; +import dev.isxander.controlify.impl.gui.guide.GuideRenderer; import dev.isxander.controlify.mixins.feature.guide.screen.AbstractContainerScreenAccessor; import dev.isxander.controlify.mixins.feature.screenop.ScreenAccessor; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.virtualmouse.VirtualMouseBehaviour; -import dev.isxander.controlify.virtualmouse.VirtualMouseHandler; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.virtualmouse.VirtualMouseBehaviour; +import dev.isxander.controlify.impl.virtualmouse.VirtualMouseHandler; import net.minecraft.client.gui.components.Renderable; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; @@ -38,13 +38,13 @@ public class AbstractContainerScreenProcessor hoveredSlot; private final ClickSlotFunction clickSlotFunction; - private final Predicate doItemSlotActions; + private final Predicate doItemSlotActions; public AbstractContainerScreenProcessor( T screen, Supplier hoveredSlot, ClickSlotFunction clickSlotFunction, - Predicate doItemSlotActions + Predicate doItemSlotActions ) { super(screen); this.hoveredSlot = hoveredSlot; @@ -54,7 +54,7 @@ public AbstractContainerScreenProcessor( } @Override - protected void handleScreenVMouse(ControllerEntity controller, VirtualMouseHandler vmouse) { + protected void handleScreenVMouse(ControllerImpl controller, VirtualMouseHandler vmouse) { var accessor = (AbstractContainerScreenAccessor) screen; var ctx = new ContainerContext( @@ -147,7 +147,7 @@ public void onHoveredSlotChanged(Slot newSlot, Slot oldSlot) { } private void hapticNavigate() { - ControlifyApi.get().getCurrentController().flatMap(ControllerEntity::hdHaptics).ifPresent(hh -> { + ControlifyApi.get().getCurrentController().flatMap(ControllerImpl::hdHaptics).ifPresent(hh -> { hh.playHaptic(HapticEffects.NAVIGATE); }); } diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AbstractSignEditScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractSignEditScreenProcessor.java similarity index 80% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AbstractSignEditScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractSignEditScreenProcessor.java index 5ec4835ce..1c35109a8 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AbstractSignEditScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractSignEditScreenProcessor.java @@ -4,20 +4,20 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; - -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.InputMode; -import dev.isxander.controlify.api.buttonguide.ButtonGuideApi; -import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.keyboard.CommonKeyboardHints; -import dev.isxander.controlify.screenop.keyboard.KeyboardWidget; -import dev.isxander.controlify.utils.LazyComponentDims; -import dev.isxander.controlify.utils.PrecomputedComponentDims; -import dev.isxander.controlify.virtualmouse.VirtualMouseHandler; +package dev.isxander.controlify.impl.screenop.compat.vanilla; + +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.api.InputMode; +import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.keyboard.CommonKeyboardHints; +import dev.isxander.controlify.impl.screenop.keyboard.KeyboardWidget; +import dev.isxander.controlify.impl.utils.LazyComponentDims; +import dev.isxander.controlify.impl.utils.PrecomputedComponentDims; +import dev.isxander.controlify.impl.virtualmouse.VirtualMouseHandler; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.AbstractButton; import net.minecraft.client.gui.screens.inventory.AbstractSignEditScreen; @@ -56,7 +56,7 @@ public AbstractSignEditScreenProcessor( } @Override - protected void handleButtons(ControllerEntity controller) { + protected void handleButtons(ControllerImpl controller) { super.handleButtons(controller); var config = controller.settings().generic; @@ -87,7 +87,7 @@ protected void handleButtons(ControllerEntity controller) { } @Override - protected void render(ControllerEntity controller, GuiGraphicsExtractor graphics, float tickDelta, Optional vmouse) { + protected void render(ControllerImpl controller, GuiGraphicsExtractor graphics, float tickDelta, Optional vmouse) { var config = controller.settings().generic; KeyboardWidget keyboardWidget = this.keyboardWidgetSupplier.get(); if (keyboardWidget != null && config.guide.showScreenGuides) { diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AddServerLikeScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AddServerLikeScreenProcessor.java similarity index 77% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AddServerLikeScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AddServerLikeScreenProcessor.java index f0eeda1c9..323a2e867 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AddServerLikeScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AddServerLikeScreenProcessor.java @@ -4,15 +4,15 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.api.buttonguide.ButtonGuideApi; -import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.ComponentProcessorProvider; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.keyboard.KeyboardLayouts; +import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.keyboard.KeyboardLayouts; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; @@ -70,7 +70,7 @@ public void onWidgetRebuild() { } @Override - protected void handleButtons(ControllerEntity controller) { + protected void handleButtons(ControllerImpl controller) { if (ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).guiPressed().get()) { Button doneButton = doneButtonSupplier.get(); if (doneButton != null && !doneButton.isFocused()) { diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/BundleItemSlotControllerAction.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/BundleItemSlotControllerAction.java similarity index 89% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/BundleItemSlotControllerAction.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/BundleItemSlotControllerAction.java index 42795f07f..060caac87 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/BundleItemSlotControllerAction.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/BundleItemSlotControllerAction.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; import net.minecraft.util.Mth; import net.minecraft.world.item.BundleItem; import net.minecraft.world.item.ItemStack; @@ -17,7 +17,7 @@ public class BundleItemSlotControllerAction { public static boolean onControllerInput( ItemStack stack, int hoveredSlotIndex, - ControllerEntity controller, + ControllerImpl controller, SelectedBundleItemConsumer consumer ) { int uniqueItems = BundleItem.getNumberOfItemsToShow(stack); diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/ChatScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ChatScreenProcessor.java similarity index 81% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/ChatScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ChatScreenProcessor.java index 52ca35e56..4915d22ca 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/ChatScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ChatScreenProcessor.java @@ -4,19 +4,19 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; - -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.ComponentProcessorProvider; -import dev.isxander.controlify.screenop.keyboard.ComponentKeyboardBehaviour; -import dev.isxander.controlify.screenop.keyboard.CommonKeyboardHints; -import dev.isxander.controlify.screenop.keyboard.KeyboardWidget; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.utils.HoldRepeatHelper; -import dev.isxander.controlify.utils.LazyComponentDims; -import dev.isxander.controlify.virtualmouse.VirtualMouseHandler; +package dev.isxander.controlify.impl.screenop.compat.vanilla; + +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.impl.screenop.keyboard.ComponentKeyboardBehaviour; +import dev.isxander.controlify.impl.screenop.keyboard.CommonKeyboardHints; +import dev.isxander.controlify.impl.screenop.keyboard.KeyboardWidget; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.utils.HoldRepeatHelper; +import dev.isxander.controlify.impl.utils.LazyComponentDims; +import dev.isxander.controlify.impl.virtualmouse.VirtualMouseHandler; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.ChatScreen; @@ -54,7 +54,7 @@ public ChatScreenProcessor( } @Override - protected void handleButtons(ControllerEntity controller) { + protected void handleButtons(ControllerImpl controller) { super.handleButtons(controller); CmdSuggestionsController suggestionsController = this.suggestionsController.get(); @@ -93,7 +93,7 @@ protected void handleButtons(ControllerEntity controller) { } @Override - protected void render(ControllerEntity controller, GuiGraphicsExtractor graphics, float tickDelta, Optional vmouse) { + protected void render(ControllerImpl controller, GuiGraphicsExtractor graphics, float tickDelta, Optional vmouse) { var settings = controller.settings().generic; if (this.keyboardSupplier.get() != null && settings.guide.showScreenGuides) { @@ -133,7 +133,7 @@ public void onWidgetRebuild() { } } - private void clearCommandSuggesterHint(ControllerEntity controller) { + private void clearCommandSuggesterHint(ControllerImpl controller) { var settings = controller.settings().generic; if (settings.keyboard.hintCommandSuggester && settings.guide.showScreenGuides) { settings.keyboard.hintCommandSuggester = false; diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/CreateWorldScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/CreateWorldScreenProcessor.java similarity index 67% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/CreateWorldScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/CreateWorldScreenProcessor.java index 0ca837e28..e8ee85fa7 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/CreateWorldScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/CreateWorldScreenProcessor.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; public class CreateWorldScreenProcessor extends ScreenProcessor { @@ -20,7 +20,7 @@ public CreateWorldScreenProcessor(CreateWorldScreen screen, Runnable onCreateBut } @Override - protected void handleButtons(ControllerEntity controller) { + protected void handleButtons(ControllerImpl controller) { if (ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).justPressed()) { playClackSound(); this.onCreateButton.run(); diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/CreativeModeInventoryScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/CreativeModeInventoryScreenProcessor.java similarity index 77% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/CreativeModeInventoryScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/CreativeModeInventoryScreenProcessor.java index d35252b1d..0542dad4e 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/CreativeModeInventoryScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/CreativeModeInventoryScreenProcessor.java @@ -4,14 +4,14 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.platform.client.CreativeTabHelper; -import dev.isxander.controlify.platform.client.PlatformClientUtil; -import dev.isxander.controlify.virtualmouse.VirtualMouseHandler; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.platform.client.CreativeTabHelper; +import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; +import dev.isxander.controlify.impl.virtualmouse.VirtualMouseHandler; import net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.CreativeModeTab; @@ -23,7 +23,7 @@ public class CreativeModeInventoryScreenProcessor extends AbstractContainerScreenProcessor { private final CreativeTabHelper tabHelper; - public CreativeModeInventoryScreenProcessor(CreativeModeInventoryScreen screen, Supplier hoveredSlot, ClickSlotFunction clickSlotFunction, Predicate itemSlotAction) { + public CreativeModeInventoryScreenProcessor(CreativeModeInventoryScreen screen, Supplier hoveredSlot, ClickSlotFunction clickSlotFunction, Predicate itemSlotAction) { super(screen, hoveredSlot, clickSlotFunction, itemSlotAction); this.tabHelper = PlatformClientUtil.createCreativeTabHelper(screen); } @@ -36,7 +36,7 @@ public void onWidgetRebuild() { @SuppressWarnings("UnreachableCode") @Override - protected void handleScreenVMouse(ControllerEntity controller, VirtualMouseHandler vmouse) { + protected void handleScreenVMouse(ControllerImpl controller, VirtualMouseHandler vmouse) { List tabs = tabHelper.getTabsForPage(tabHelper.getCurrentPage()); if (ControlifyBindings.GUI_NEXT_TAB.on(controller).justPressed()) { int newIndex = tabs.indexOf(tabHelper.getSelectedTab()) + 1; diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/EditBoxComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/EditBoxComponentProcessor.java similarity index 90% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/EditBoxComponentProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/EditBoxComponentProcessor.java index 4f71ff0c6..11d5897c6 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/EditBoxComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/EditBoxComponentProcessor.java @@ -4,12 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.keyboard.*; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.keyboard.*; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.input.CharacterEvent; @@ -40,7 +40,7 @@ public EditBoxComponentProcessor(EditBox editBox, int screenWidth, int screenHei } @Override - public ComponentKeyboardBehaviour getKeyboardBehaviour(ScreenProcessor screen, ControllerEntity controller) { + public ComponentKeyboardBehaviour getKeyboardBehaviour(ScreenProcessor screen, ControllerImpl controller) { return this.getKeyboardBehaviour(); } diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/ItemSlotControllerAction.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ItemSlotControllerAction.java similarity index 71% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/ItemSlotControllerAction.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ItemSlotControllerAction.java index c08d1ab2e..d3f59852b 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/ItemSlotControllerAction.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ItemSlotControllerAction.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.impl.controller.ControllerImpl; import net.minecraft.client.gui.ItemSlotMouseAction; import net.minecraft.world.item.ItemStack; @@ -14,6 +14,6 @@ public interface ItemSlotControllerAction extends ItemSlotMouseAction { boolean controlify$onControllerInput( ItemStack stack, int hoveredSlotIndex, - ControllerEntity controller + ControllerImpl controller ); } diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/JoinMultiplayerScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/JoinMultiplayerScreenProcessor.java similarity index 86% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/JoinMultiplayerScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/JoinMultiplayerScreenProcessor.java index b91e99d8e..feee12cd0 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/JoinMultiplayerScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/JoinMultiplayerScreenProcessor.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.api.buttonguide.ButtonGuideApi; -import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.ScreenProcessor; +import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen; import net.minecraft.client.gui.screens.multiplayer.ServerSelectionList; @@ -38,7 +38,7 @@ public JoinMultiplayerScreenProcessor( } @Override - protected void handleButtons(ControllerEntity controller) { + protected void handleButtons(ControllerImpl controller) { if (screen.getFocused() != null && screen.getFocused() instanceof Button) { if (ControlifyBindings.GUI_BACK.on(controller).guiPressed().get()) { screen.setFocused(listSupplier.get()); diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/LanguageSelectionListComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/LanguageSelectionListComponentProcessor.java similarity index 72% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/LanguageSelectionListComponentProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/LanguageSelectionListComponentProcessor.java index b0e520707..e4c0d61c9 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/LanguageSelectionListComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/LanguageSelectionListComponentProcessor.java @@ -4,14 +4,14 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.compat.vanilla; -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 dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.OptionsSubScreenAccessor; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.client.Minecraft; public class LanguageSelectionListComponentProcessor implements ComponentProcessor { @@ -22,7 +22,7 @@ public LanguageSelectionListComponentProcessor(String code) { } @Override - public boolean overrideControllerButtons(ScreenProcessor screen, ControllerEntity controller) { + public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { if (ControlifyBindings.GUI_PRESS.on(controller).justPressed()) { var minecraft = Minecraft.getInstance(); var languageManager = minecraft.getLanguageManager(); diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/OptionsScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/OptionsScreenProcessor.java similarity index 73% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/OptionsScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/OptionsScreenProcessor.java index 3bcec8dd0..02998f467 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/OptionsScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/OptionsScreenProcessor.java @@ -4,12 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.api.buttonguide.ButtonGuideApi; -import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.screenop.ScreenProcessor; +import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.options.OptionsScreen; diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/OptionsSubScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/OptionsSubScreenProcessor.java similarity index 73% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/OptionsSubScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/OptionsSubScreenProcessor.java index 3a1f61e49..8ee0e326c 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/OptionsSubScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/OptionsSubScreenProcessor.java @@ -4,12 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.api.buttonguide.ButtonGuideApi; -import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.screenop.ScreenProcessor; +import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.options.OptionsSubScreen; diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/PauseScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/PauseScreenProcessor.java similarity index 81% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/PauseScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/PauseScreenProcessor.java index 5d99796d3..bcabb2fd6 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/PauseScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/PauseScreenProcessor.java @@ -4,15 +4,15 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.api.buttonguide.ButtonGuideApi; -import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.PauseScreenAccessor; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.client.gui.components.AbstractButton; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.options.OptionsScreen; @@ -30,7 +30,7 @@ public PauseScreenProcessor(PauseScreen screen, Supplier<@Nullable Button> disco } @Override - protected void handleButtons(ControllerEntity controller) { + protected void handleButtons(ControllerImpl controller) { super.handleButtons(controller); if (ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).justPressed()) { diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/RecipeBookScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/RecipeBookScreenProcessor.java similarity index 88% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/RecipeBookScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/RecipeBookScreenProcessor.java index 61187d063..84e6e4065 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/RecipeBookScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/RecipeBookScreenProcessor.java @@ -4,14 +4,14 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; +package dev.isxander.controlify.impl.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.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.mixins.feature.virtualmouse.snapping.RecipeBookComponentAccessor; import dev.isxander.controlify.mixins.feature.virtualmouse.snapping.RecipeBookPageAccessor; -import dev.isxander.controlify.virtualmouse.VirtualMouseHandler; +import dev.isxander.controlify.impl.virtualmouse.VirtualMouseHandler; import net.minecraft.client.gui.components.ImageButton; import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent; import net.minecraft.client.gui.screens.recipebook.RecipeBookTabButton; @@ -34,14 +34,14 @@ public RecipeBookScreenProcessor( RecipeBookScreenAccessor recipeBookScreenAccessor, Supplier hoveredSlot, ClickSlotFunction clickSlotFunction, - Predicate doItemSlotActions + Predicate doItemSlotActions ) { super(screen, hoveredSlot, clickSlotFunction, doItemSlotActions); this.recipeBookScreenAccessor = recipeBookScreenAccessor; } @Override - protected void handleScreenVMouse(ControllerEntity controller, VirtualMouseHandler vmouse) { + protected void handleScreenVMouse(ControllerImpl controller, VirtualMouseHandler vmouse) { super.handleScreenVMouse(controller, vmouse); RecipeBookComponent recipeBookComponent = recipeBookScreenAccessor.controlify$getRecipeBookComponent(); diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/SelectWorldScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/SelectWorldScreenProcessor.java similarity index 76% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/SelectWorldScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/SelectWorldScreenProcessor.java index 776beed79..854a4c1ba 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/SelectWorldScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/SelectWorldScreenProcessor.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.SelectWorldScreenAccessor; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; @@ -22,7 +22,7 @@ public SelectWorldScreenProcessor(SelectWorldScreen screen) { } @Override - protected void handleButtons(ControllerEntity controller) { + protected void handleButtons(ControllerImpl controller) { if (ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).justPressed()) { playClackSound(); var minecraft = Minecraft.getInstance(); diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/ServerSelectionListEntryComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ServerSelectionListEntryComponentProcessor.java similarity index 63% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/ServerSelectionListEntryComponentProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ServerSelectionListEntryComponentProcessor.java index 46cfb2ea5..5aec5e5f0 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/ServerSelectionListEntryComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ServerSelectionListEntryComponentProcessor.java @@ -4,17 +4,17 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.compat.vanilla; -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 dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.JoinMultiplayerScreenAccessor; public class ServerSelectionListEntryComponentProcessor implements ComponentProcessor { @Override - public boolean overrideControllerButtons(ScreenProcessor screen, ControllerEntity controller) { + public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { if (ControlifyBindings.GUI_PRESS.on(controller).guiPressed().get()) { 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/impl/screenop/compat/vanilla/SliderComponentProcessor.java similarity index 83% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/SliderComponentProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/SliderComponentProcessor.java index d23c8ec08..90dc8057a 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/SliderComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/SliderComponentProcessor.java @@ -4,13 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; +package dev.isxander.controlify.impl.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 dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; import net.minecraft.client.gui.components.AbstractSliderButton; import net.minecraft.client.input.KeyEvent; @@ -33,7 +33,7 @@ public SliderComponentProcessor(AbstractSliderButton component, Supplier screen, ControllerEntity controller) { + public boolean overrideControllerNavigation(ScreenProcessor screen, ControllerImpl controller) { if (!this.canChangeValueGetter.get()) return false; var canSliderChange = ++lastSliderChange > SLIDER_CHANGE_DELAY; @@ -58,7 +58,7 @@ public boolean overrideControllerNavigation(ScreenProcessor screen, Controlle } @Override - public boolean overrideControllerButtons(ScreenProcessor screen, ControllerEntity controller) { + public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { if (!this.canChangeValueGetter.get()) return false; if (ControlifyBindings.GUI_BACK.on(controller).justPressed()) { @@ -70,7 +70,7 @@ public boolean overrideControllerButtons(ScreenProcessor screen, ControllerEn } @Override - public void onFocusGained(ScreenProcessor screen, ControllerEntity controller) { + public void onFocusGained(ScreenProcessor screen, ControllerImpl controller) { this.canChangeValueSetter.accept(false); } } diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/TitleScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/TitleScreenProcessor.java similarity index 75% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/TitleScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/TitleScreenProcessor.java index 95715778b..d5ff4a4d0 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/TitleScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/TitleScreenProcessor.java @@ -4,14 +4,14 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.api.buttonguide.ButtonGuideApi; -import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.client.gui.components.AbstractButton; import net.minecraft.client.gui.screens.options.OptionsScreen; import net.minecraft.client.gui.screens.TitleScreen; @@ -22,7 +22,7 @@ public TitleScreenProcessor(TitleScreen screen) { } @Override - protected void handleButtons(ControllerEntity controller) { + protected void handleButtons(ControllerImpl controller) { if (ControlifyBindings.GUI_BACK.on(controller).justPressed()) { getWidget("menu.quit").ifPresent(widget -> { screen.setFocused(widget); diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/WorldListEntryComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/WorldListEntryComponentProcessor.java similarity index 67% rename from src/main/java/dev/isxander/controlify/screenop/compat/vanilla/WorldListEntryComponentProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/WorldListEntryComponentProcessor.java index 35d7324eb..ca505e3ca 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/WorldListEntryComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/WorldListEntryComponentProcessor.java @@ -4,18 +4,18 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.compat.vanilla; -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 dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.SelectWorldScreenAccessor; import net.minecraft.client.gui.screens.worldselection.SelectWorldScreen; public class WorldListEntryComponentProcessor implements ComponentProcessor { @Override - public boolean overrideControllerButtons(ScreenProcessor screen, ControllerEntity controller) { + public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { if (ControlifyBindings.GUI_PRESS.on(controller).guiPressed().get()) { var selectWorldScreen = (SelectWorldScreen) screen.screen; selectWorldScreen.setFocused(((SelectWorldScreenAccessor) selectWorldScreen).controlify$getPlayWorldButton()); diff --git a/src/main/java/dev/isxander/controlify/screenop/keyboard/ChatKeyboardDucky.java b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/ChatKeyboardDucky.java similarity index 87% rename from src/main/java/dev/isxander/controlify/screenop/keyboard/ChatKeyboardDucky.java rename to src/main/java/dev/isxander/controlify/impl/screenop/keyboard/ChatKeyboardDucky.java index 3d297b666..573da02a9 100644 --- a/src/main/java/dev/isxander/controlify/screenop/keyboard/ChatKeyboardDucky.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/ChatKeyboardDucky.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.keyboard; +package dev.isxander.controlify.impl.screenop.keyboard; import net.minecraft.client.gui.screens.ChatScreen; diff --git a/src/main/java/dev/isxander/controlify/screenop/keyboard/CommonKeyboardHints.java b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/CommonKeyboardHints.java similarity index 79% rename from src/main/java/dev/isxander/controlify/screenop/keyboard/CommonKeyboardHints.java rename to src/main/java/dev/isxander/controlify/impl/screenop/keyboard/CommonKeyboardHints.java index 7a215fbef..d3bf8e35f 100644 --- a/src/main/java/dev/isxander/controlify/screenop/keyboard/CommonKeyboardHints.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/CommonKeyboardHints.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.keyboard; +package dev.isxander.controlify.impl.screenop.keyboard; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.utils.LazyComponentDims; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.utils.LazyComponentDims; import net.minecraft.network.chat.Component; public final class CommonKeyboardHints { diff --git a/src/main/java/dev/isxander/controlify/screenop/keyboard/ComponentKeyboardBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/ComponentKeyboardBehaviour.java similarity index 90% rename from src/main/java/dev/isxander/controlify/screenop/keyboard/ComponentKeyboardBehaviour.java rename to src/main/java/dev/isxander/controlify/impl/screenop/keyboard/ComponentKeyboardBehaviour.java index 07420dc38..e7e0371a7 100644 --- a/src/main/java/dev/isxander/controlify/screenop/keyboard/ComponentKeyboardBehaviour.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/ComponentKeyboardBehaviour.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.keyboard; +package dev.isxander.controlify.impl.screenop.keyboard; /** * Represents the behaviour of a GUI component with respect to keyboard input. - * This is used by {@link dev.isxander.controlify.screenop.ScreenProcessor} to + * This is used by {@link dev.isxander.controlify.impl.screenop.ScreenProcessor} to * determine how/if to open a keyboard for this component when it is pressed. */ public sealed interface ComponentKeyboardBehaviour { diff --git a/src/main/java/dev/isxander/controlify/screenop/keyboard/FallbackKeyboardLayout.java b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/FallbackKeyboardLayout.java similarity index 83% rename from src/main/java/dev/isxander/controlify/screenop/keyboard/FallbackKeyboardLayout.java rename to src/main/java/dev/isxander/controlify/impl/screenop/keyboard/FallbackKeyboardLayout.java index 72c6100af..d586e974f 100644 --- a/src/main/java/dev/isxander/controlify/screenop/keyboard/FallbackKeyboardLayout.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/FallbackKeyboardLayout.java @@ -4,19 +4,19 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.keyboard; +package dev.isxander.controlify.impl.screenop.keyboard; -import dev.isxander.controlify.api.bind.InputBindingSupplier; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.api.bind.InputBindingSupplier; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Optional; -import static dev.isxander.controlify.screenop.keyboard.KeyboardLayout.Key; -import static dev.isxander.controlify.screenop.keyboard.KeyboardLayout.KeyFunction.*; +import static dev.isxander.controlify.impl.screenop.keyboard.KeyboardLayout.Key; +import static dev.isxander.controlify.impl.screenop.keyboard.KeyboardLayout.KeyFunction.*; /** * A code-defined fallback keyboard layout if the resource-pack driven ones diff --git a/src/main/java/dev/isxander/controlify/screenop/keyboard/InputTarget.java b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/InputTarget.java similarity index 97% rename from src/main/java/dev/isxander/controlify/screenop/keyboard/InputTarget.java rename to src/main/java/dev/isxander/controlify/impl/screenop/keyboard/InputTarget.java index 0fc082b4a..419f56d11 100644 --- a/src/main/java/dev/isxander/controlify/screenop/keyboard/InputTarget.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/InputTarget.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.keyboard; +package dev.isxander.controlify.impl.screenop.keyboard; /** * Represents a target/consumer for keyboard input. diff --git a/src/main/java/dev/isxander/controlify/screenop/keyboard/KeyWidget.java b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyWidget.java similarity index 93% rename from src/main/java/dev/isxander/controlify/screenop/keyboard/KeyWidget.java rename to src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyWidget.java index 75ba270ee..9151926cd 100644 --- a/src/main/java/dev/isxander/controlify/screenop/keyboard/KeyWidget.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyWidget.java @@ -4,19 +4,19 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.keyboard; +package dev.isxander.controlify.impl.screenop.keyboard; import com.mojang.blaze3d.platform.InputConstants; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.font.BindingFontHelper; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ScreenControllerEventListener; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.HoldRepeatHelper; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.font.BindingFontHelper; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ScreenControllerEventListener; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.HoldRepeatHelper; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.AbstractWidget; @@ -116,7 +116,7 @@ protected void extractWidgetRenderState(@NonNull GuiGraphicsExtractor graphics, } @Override - public boolean overrideControllerButtons(ScreenProcessor screen, ControllerEntity controller) { + public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { var guiPress = ControlifyBindings.GUI_PRESS.on(controller); // this means if you were holding the button down and navigated to this key, @@ -138,7 +138,7 @@ public boolean overrideControllerButtons(ScreenProcessor screen, ControllerEn } @Override - public void onControllerInput(ControllerEntity controller) { + public void onControllerInput(ControllerImpl controller) { this.key.shortcutBinding().ifPresent(supplier -> { InputBinding shortcutBinding = supplier.on(controller); diff --git a/src/main/java/dev/isxander/controlify/screenop/keyboard/KeyboardLayout.java b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayout.java similarity index 98% rename from src/main/java/dev/isxander/controlify/screenop/keyboard/KeyboardLayout.java rename to src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayout.java index 2173a0a46..ee02c6fd5 100644 --- a/src/main/java/dev/isxander/controlify/screenop/keyboard/KeyboardLayout.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayout.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.keyboard; +package dev.isxander.controlify.impl.screenop.keyboard; import com.mojang.datafixers.util.Either; import com.mojang.datafixers.util.Pair; @@ -12,8 +12,8 @@ import com.mojang.serialization.DataResult; import com.mojang.serialization.DynamicOps; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.api.bind.InputBindingSupplier; -import dev.isxander.controlify.utils.codec.CExtraCodecs; +import dev.isxander.controlify.impl.api.bind.InputBindingSupplier; +import dev.isxander.controlify.impl.utils.codec.CExtraCodecs; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/screenop/keyboard/KeyboardLayoutManager.java b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayoutManager.java similarity index 93% rename from src/main/java/dev/isxander/controlify/screenop/keyboard/KeyboardLayoutManager.java rename to src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayoutManager.java index 5c4485523..67cbd594b 100644 --- a/src/main/java/dev/isxander/controlify/screenop/keyboard/KeyboardLayoutManager.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayoutManager.java @@ -4,14 +4,14 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.keyboard; +package dev.isxander.controlify.impl.screenop.keyboard; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.mojang.serialization.JsonOps; -import dev.isxander.controlify.platform.client.resource.SimpleControlifyReloadListener; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.log.ControlifyLogger; +import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.log.ControlifyLogger; import net.minecraft.util.Util; import net.minecraft.client.Minecraft; import net.minecraft.resources.Identifier; @@ -30,7 +30,7 @@ */ public class KeyboardLayoutManager implements SimpleControlifyReloadListener { - private static final String PREFIX = "keyboard_layout"; + private static final String PREFIX = "controller/keyboard_layout"; private static final ControlifyLogger LOGGER = CUtil.LOGGER.createSubLogger("KeyboardLayoutManager"); private Map layouts = Map.of(); diff --git a/src/main/java/dev/isxander/controlify/screenop/keyboard/KeyboardLayoutWithId.java b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayoutWithId.java similarity index 89% rename from src/main/java/dev/isxander/controlify/screenop/keyboard/KeyboardLayoutWithId.java rename to src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayoutWithId.java index 3ddb18786..3a180ec4b 100644 --- a/src/main/java/dev/isxander/controlify/screenop/keyboard/KeyboardLayoutWithId.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayoutWithId.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.keyboard; +package dev.isxander.controlify.impl.screenop.keyboard; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/screenop/keyboard/KeyboardLayouts.java b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayouts.java similarity index 88% rename from src/main/java/dev/isxander/controlify/screenop/keyboard/KeyboardLayouts.java rename to src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayouts.java index 76a37a142..a6a1e6c5d 100644 --- a/src/main/java/dev/isxander/controlify/screenop/keyboard/KeyboardLayouts.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayouts.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.keyboard; +package dev.isxander.controlify.impl.screenop.keyboard; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; /** diff --git a/src/main/java/dev/isxander/controlify/screenop/keyboard/KeyboardOverlayScreen.java b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardOverlayScreen.java similarity index 97% rename from src/main/java/dev/isxander/controlify/screenop/keyboard/KeyboardOverlayScreen.java rename to src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardOverlayScreen.java index dfdde2771..54fcd4979 100644 --- a/src/main/java/dev/isxander/controlify/screenop/keyboard/KeyboardOverlayScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardOverlayScreen.java @@ -4,12 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.keyboard; +package dev.isxander.controlify.impl.screenop.keyboard; import com.mojang.blaze3d.platform.InputConstants; -import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.mixins.feature.screenop.GuiMixin; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.navigation.ScreenAxis; import net.minecraft.client.gui.navigation.ScreenRectangle; diff --git a/src/main/java/dev/isxander/controlify/screenop/keyboard/KeyboardWidget.java b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardWidget.java similarity index 95% rename from src/main/java/dev/isxander/controlify/screenop/keyboard/KeyboardWidget.java rename to src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardWidget.java index cc28107fd..67a3c5006 100644 --- a/src/main/java/dev/isxander/controlify/screenop/keyboard/KeyboardWidget.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardWidget.java @@ -4,15 +4,15 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.keyboard; +package dev.isxander.controlify.impl.screenop.keyboard; import com.google.common.collect.ImmutableList; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.utils.HoldRepeatHelper; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.utils.HoldRepeatHelper; import net.minecraft.client.gui.ComponentPath; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.AbstractWidget; @@ -147,7 +147,7 @@ protected void extractWidgetRenderState(@NonNull GuiGraphicsExtractor graphics, } @Override - public boolean overrideControllerButtons(ScreenProcessor screen, ControllerEntity controller) { + public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { InputTarget inputTarget = this.getInputTarget(); var settings = controller.settings().generic; diff --git a/src/main/java/dev/isxander/controlify/screenop/keyboard/MixinInputTarget.java b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/MixinInputTarget.java similarity index 96% rename from src/main/java/dev/isxander/controlify/screenop/keyboard/MixinInputTarget.java rename to src/main/java/dev/isxander/controlify/impl/screenop/keyboard/MixinInputTarget.java index 976db419a..b94c1668a 100644 --- a/src/main/java/dev/isxander/controlify/screenop/keyboard/MixinInputTarget.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/MixinInputTarget.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.screenop.keyboard; +package dev.isxander.controlify.impl.screenop.keyboard; /** * A wrapper to {@link InputTarget} to add alternative method names, diff --git a/src/main/java/dev/isxander/controlify/server/ControlifyHandshake.java b/src/main/java/dev/isxander/controlify/impl/server/ControlifyHandshake.java similarity index 89% rename from src/main/java/dev/isxander/controlify/server/ControlifyHandshake.java rename to src/main/java/dev/isxander/controlify/impl/server/ControlifyHandshake.java index 950771ee9..ed0cd697b 100644 --- a/src/main/java/dev/isxander/controlify/server/ControlifyHandshake.java +++ b/src/main/java/dev/isxander/controlify/impl/server/ControlifyHandshake.java @@ -4,12 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.server; +package dev.isxander.controlify.impl.server; import com.mojang.logging.LogUtils; -import dev.isxander.controlify.platform.client.PlatformClientUtil; -import dev.isxander.controlify.platform.main.PlatformMainUtil; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; +import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.ChatFormatting; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; diff --git a/src/main/java/dev/isxander/controlify/server/ControlifyServer.java b/src/main/java/dev/isxander/controlify/impl/server/ControlifyServer.java similarity index 91% rename from src/main/java/dev/isxander/controlify/server/ControlifyServer.java rename to src/main/java/dev/isxander/controlify/impl/server/ControlifyServer.java index a381e9f31..5b0b0f5d1 100644 --- a/src/main/java/dev/isxander/controlify/server/ControlifyServer.java +++ b/src/main/java/dev/isxander/controlify/impl/server/ControlifyServer.java @@ -4,12 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.server; +package dev.isxander.controlify.impl.server; -import dev.isxander.controlify.platform.network.SidedNetworkApi; -import dev.isxander.controlify.platform.main.PlatformMainUtil; -import dev.isxander.controlify.server.packets.*; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.platform.network.SidedNetworkApi; +import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; +import dev.isxander.controlify.impl.server.packets.*; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.server.level.ServerPlayer; import org.jetbrains.annotations.VisibleForTesting; diff --git a/src/main/java/dev/isxander/controlify/server/ControlifyServerConfig.java b/src/main/java/dev/isxander/controlify/impl/server/ControlifyServerConfig.java similarity index 84% rename from src/main/java/dev/isxander/controlify/server/ControlifyServerConfig.java rename to src/main/java/dev/isxander/controlify/impl/server/ControlifyServerConfig.java index ce61501df..66fc2f412 100644 --- a/src/main/java/dev/isxander/controlify/server/ControlifyServerConfig.java +++ b/src/main/java/dev/isxander/controlify/impl/server/ControlifyServerConfig.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.server; +package dev.isxander.controlify.impl.server; -import dev.isxander.controlify.platform.main.PlatformMainUtil; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; +import dev.isxander.controlify.impl.utils.CUtil; import dev.isxander.yacl3.config.v2.api.ConfigClassHandler; import dev.isxander.yacl3.config.v2.api.SerialEntry; import dev.isxander.yacl3.config.v2.api.serializer.GsonConfigSerializerBuilder; diff --git a/src/main/java/dev/isxander/controlify/server/ServerPolicies.java b/src/main/java/dev/isxander/controlify/impl/server/ServerPolicies.java similarity index 96% rename from src/main/java/dev/isxander/controlify/server/ServerPolicies.java rename to src/main/java/dev/isxander/controlify/impl/server/ServerPolicies.java index 0b9125bd0..40b20ccef 100644 --- a/src/main/java/dev/isxander/controlify/server/ServerPolicies.java +++ b/src/main/java/dev/isxander/controlify/impl/server/ServerPolicies.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.server; +package dev.isxander.controlify.impl.server; import java.util.Arrays; import java.util.Map; diff --git a/src/main/java/dev/isxander/controlify/server/ServerPolicy.java b/src/main/java/dev/isxander/controlify/impl/server/ServerPolicy.java similarity index 88% rename from src/main/java/dev/isxander/controlify/server/ServerPolicy.java rename to src/main/java/dev/isxander/controlify/impl/server/ServerPolicy.java index bb7494013..8ece3cdae 100644 --- a/src/main/java/dev/isxander/controlify/server/ServerPolicy.java +++ b/src/main/java/dev/isxander/controlify/impl/server/ServerPolicy.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.server; +package dev.isxander.controlify.impl.server; public enum ServerPolicy { ALLOWED, diff --git a/src/main/java/dev/isxander/controlify/server/VibrateCommand.java b/src/main/java/dev/isxander/controlify/impl/server/VibrateCommand.java similarity index 87% rename from src/main/java/dev/isxander/controlify/server/VibrateCommand.java rename to src/main/java/dev/isxander/controlify/impl/server/VibrateCommand.java index e723f5403..9be937abb 100644 --- a/src/main/java/dev/isxander/controlify/server/VibrateCommand.java +++ b/src/main/java/dev/isxander/controlify/impl/server/VibrateCommand.java @@ -4,25 +4,21 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.server; +package dev.isxander.controlify.impl.server; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.arguments.FloatArgumentType; import com.mojang.brigadier.arguments.IntegerArgumentType; -import com.mojang.brigadier.suggestion.SuggestionProvider; -import dev.isxander.controlify.platform.network.SidedNetworkApi; -import dev.isxander.controlify.rumble.RumbleSource; -import dev.isxander.controlify.rumble.RumbleState; -import dev.isxander.controlify.server.packets.EntityVibrationPacket; -import dev.isxander.controlify.server.packets.OriginVibrationPacket; -import dev.isxander.controlify.server.packets.VibrationPacket; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.platform.network.SidedNetworkApi; +import dev.isxander.controlify.impl.controller.rumble.RumbleSource; +import dev.isxander.controlify.api.controller.rumble.RumbleState; +import dev.isxander.controlify.impl.server.packets.EntityVibrationPacket; +import dev.isxander.controlify.impl.server.packets.OriginVibrationPacket; +import dev.isxander.controlify.impl.server.packets.VibrationPacket; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; -import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.commands.arguments.EntityArgument; import net.minecraft.commands.arguments.coordinates.Vec3Argument; -import net.minecraft.commands.synchronization.SuggestionProviders; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; @@ -33,16 +29,6 @@ import java.util.Locale; public class VibrateCommand { - private static final SuggestionProvider SOURCES_SUGGESTION = SuggestionProviders.register( - CUtil.rl("vibration_sources"), - (context, builder) -> SharedSuggestionProvider.suggestResource( - RumbleSource.values().stream() - .map(RumbleSource::id) - .toList(), - builder - ) - ); - public static void register(CommandDispatcher dispatcher) { dispatcher.register( Commands.literal("vibratecontroller") diff --git a/src/main/java/dev/isxander/controlify/server/packets/EntityVibrationPacket.java b/src/main/java/dev/isxander/controlify/impl/server/packets/EntityVibrationPacket.java similarity index 70% rename from src/main/java/dev/isxander/controlify/server/packets/EntityVibrationPacket.java rename to src/main/java/dev/isxander/controlify/impl/server/packets/EntityVibrationPacket.java index 7e3316ed6..a779bf497 100644 --- a/src/main/java/dev/isxander/controlify/server/packets/EntityVibrationPacket.java +++ b/src/main/java/dev/isxander/controlify/impl/server/packets/EntityVibrationPacket.java @@ -4,14 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.server.packets; +package dev.isxander.controlify.impl.server.packets; -import dev.isxander.controlify.rumble.ContinuousRumbleEffect; -import dev.isxander.controlify.rumble.RumbleEffect; -import dev.isxander.controlify.rumble.RumbleSource; -import dev.isxander.controlify.rumble.RumbleState; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.Easings; +import dev.isxander.controlify.api.controller.rumble.RumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleState; +import dev.isxander.controlify.impl.controller.rumble.RumbleSource; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.Easings; import net.minecraft.client.Minecraft; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; @@ -34,16 +33,15 @@ public record EntityVibrationPacket(int entityId, float range, int duration, Rum buf.readFloat(), buf.readInt(), RumbleState.unpackFromInt(buf.readInt()), - RumbleSource.get(buf.readIdentifier()) + new RumbleSource(buf.readIdentifier()) ) ); public RumbleEffect createEffect() { Entity entity = Minecraft.getInstance().level.getEntity(entityId); - return ContinuousRumbleEffect.builder() - .constant(state) + return RumbleEffect.continuous() + .constant(state.strong(), state.weak()) .inWorld(entity::position, 0, 1, range, Easings.toFloat(Easings::easeInSine)) - .timeout(duration) - .build(); + .timeout(duration); } } diff --git a/src/main/java/dev/isxander/controlify/server/packets/OriginVibrationPacket.java b/src/main/java/dev/isxander/controlify/impl/server/packets/OriginVibrationPacket.java similarity index 70% rename from src/main/java/dev/isxander/controlify/server/packets/OriginVibrationPacket.java rename to src/main/java/dev/isxander/controlify/impl/server/packets/OriginVibrationPacket.java index 950c4f850..84827c2e5 100644 --- a/src/main/java/dev/isxander/controlify/server/packets/OriginVibrationPacket.java +++ b/src/main/java/dev/isxander/controlify/impl/server/packets/OriginVibrationPacket.java @@ -4,14 +4,13 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.server.packets; +package dev.isxander.controlify.impl.server.packets; -import dev.isxander.controlify.rumble.ContinuousRumbleEffect; -import dev.isxander.controlify.rumble.RumbleEffect; -import dev.isxander.controlify.rumble.RumbleSource; -import dev.isxander.controlify.rumble.RumbleState; -import dev.isxander.controlify.utils.CUtil; -import dev.isxander.controlify.utils.Easings; +import dev.isxander.controlify.api.controller.rumble.RumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleState; +import dev.isxander.controlify.impl.controller.rumble.RumbleSource; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.Easings; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.Identifier; @@ -34,16 +33,15 @@ public record OriginVibrationPacket(Vector3f origin, float effectRange, int dura buf.readFloat(), buf.readVarInt(), RumbleState.unpackFromInt(buf.readInt()), - RumbleSource.get(buf.readIdentifier()) + new RumbleSource(buf.readIdentifier()) ) ); public RumbleEffect createEffect() { var originVec3 = new Vec3(origin); - return ContinuousRumbleEffect.builder() - .constant(state) + return RumbleEffect.continuous() + .constant(state.strong(), state.weak()) .inWorld(() -> originVec3, 0, 1, effectRange, Easings.toFloat(Easings::easeInSine)) - .timeout(duration) - .build(); + .timeout(duration); } } diff --git a/src/main/java/dev/isxander/controlify/server/packets/ServerPolicyPacket.java b/src/main/java/dev/isxander/controlify/impl/server/packets/ServerPolicyPacket.java similarity index 86% rename from src/main/java/dev/isxander/controlify/server/packets/ServerPolicyPacket.java rename to src/main/java/dev/isxander/controlify/impl/server/packets/ServerPolicyPacket.java index e96679ed9..f6d6ecc20 100644 --- a/src/main/java/dev/isxander/controlify/server/packets/ServerPolicyPacket.java +++ b/src/main/java/dev/isxander/controlify/impl/server/packets/ServerPolicyPacket.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.server.packets; +package dev.isxander.controlify.impl.server.packets; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/server/packets/VibrationPacket.java b/src/main/java/dev/isxander/controlify/impl/server/packets/VibrationPacket.java similarity index 67% rename from src/main/java/dev/isxander/controlify/server/packets/VibrationPacket.java rename to src/main/java/dev/isxander/controlify/impl/server/packets/VibrationPacket.java index 5c5708a44..ea3397697 100644 --- a/src/main/java/dev/isxander/controlify/server/packets/VibrationPacket.java +++ b/src/main/java/dev/isxander/controlify/impl/server/packets/VibrationPacket.java @@ -4,13 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.server.packets; +package dev.isxander.controlify.impl.server.packets; -import dev.isxander.controlify.rumble.BasicRumbleEffect; -import dev.isxander.controlify.rumble.RumbleEffect; -import dev.isxander.controlify.rumble.RumbleSource; -import dev.isxander.controlify.rumble.RumbleState; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.api.controller.rumble.RumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleState; +import dev.isxander.controlify.impl.controller.rumble.RumbleSource; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.client.Minecraft; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; @@ -28,7 +27,7 @@ public record VibrationPacket(RumbleSource source, RumbleState[] frames) { } }, buf -> { - RumbleSource source = RumbleSource.get(buf.readIdentifier()); + RumbleSource source = new RumbleSource(buf.readIdentifier()); RumbleState[] frames = new RumbleState[buf.readInt()]; for (int i = 0; i < frames.length; i++) { frames[i] = RumbleState.unpackFromInt(buf.readInt()); @@ -38,6 +37,6 @@ public record VibrationPacket(RumbleSource source, RumbleState[] frames) { ); public RumbleEffect createEffect() { - return new BasicRumbleEffect(frames).earlyFinish(() -> Minecraft.getInstance().level == null); + return RumbleEffect.finite().byTick(tick -> frames[tick], frames.length).stopCondition(() -> Minecraft.getInstance().level == null); } } diff --git a/src/main/java/dev/isxander/controlify/sound/ControlifyClientSounds.java b/src/main/java/dev/isxander/controlify/impl/sound/ControlifyClientSounds.java similarity index 89% rename from src/main/java/dev/isxander/controlify/sound/ControlifyClientSounds.java rename to src/main/java/dev/isxander/controlify/impl/sound/ControlifyClientSounds.java index add2833f1..f2d7ea12c 100644 --- a/src/main/java/dev/isxander/controlify/sound/ControlifyClientSounds.java +++ b/src/main/java/dev/isxander/controlify/impl/sound/ControlifyClientSounds.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.sound; +package dev.isxander.controlify.impl.sound; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; import net.minecraft.sounds.SoundEvent; diff --git a/src/main/java/dev/isxander/controlify/utils/CUtil.java b/src/main/java/dev/isxander/controlify/impl/utils/CUtil.java similarity index 94% rename from src/main/java/dev/isxander/controlify/utils/CUtil.java rename to src/main/java/dev/isxander/controlify/impl/utils/CUtil.java index 31ecbc478..cee65883f 100644 --- a/src/main/java/dev/isxander/controlify/utils/CUtil.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/CUtil.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; import com.mojang.blaze3d.Blaze3D; -import dev.isxander.controlify.platform.client.PlatformClientUtil; -import dev.isxander.controlify.utils.log.ControlifyLogger; +import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; +import dev.isxander.controlify.impl.utils.log.ControlifyLogger; import net.minecraft.core.HolderSet; import net.minecraft.core.TypedInstance; import net.minecraft.util.Util; @@ -18,7 +18,6 @@ import org.apache.commons.codec.binary.Hex; import org.slf4j.LoggerFactory; -import java.io.IOException; import java.net.URI; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; diff --git a/src/main/java/dev/isxander/controlify/utils/ColorUtils.java b/src/main/java/dev/isxander/controlify/impl/utils/ColorUtils.java similarity index 91% rename from src/main/java/dev/isxander/controlify/utils/ColorUtils.java rename to src/main/java/dev/isxander/controlify/impl/utils/ColorUtils.java index a42520d28..e716ce941 100644 --- a/src/main/java/dev/isxander/controlify/utils/ColorUtils.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/ColorUtils.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; import net.minecraft.util.Mth; diff --git a/src/main/java/dev/isxander/controlify/utils/ControllerUtils.java b/src/main/java/dev/isxander/controlify/impl/utils/ControllerUtils.java similarity index 92% rename from src/main/java/dev/isxander/controlify/utils/ControllerUtils.java rename to src/main/java/dev/isxander/controlify/impl/utils/ControllerUtils.java index 3c9b55f70..2c39d1f9e 100644 --- a/src/main/java/dev/isxander/controlify/utils/ControllerUtils.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/ControllerUtils.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.touchpad.Touchpads; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controller.touchpad.Touchpads; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; import net.minecraft.ReportedException; @@ -20,7 +20,7 @@ import java.util.stream.Stream; public class ControllerUtils { - public static String createControllerString(ControllerEntity controller) { + public static String createControllerString(ControllerImpl controller) { return String.format("'%s'#%s-%s (%s)", controller.name(), controller.info().ucid().toString(), @@ -29,7 +29,7 @@ public static String createControllerString(ControllerEntity controller) { ); } - public static void wrapControllerError(Runnable runnable, String errorTitle, ControllerEntity controller) { + public static void wrapControllerError(Runnable runnable, String errorTitle, ControllerImpl controller) { try { runnable.run(); } catch (Throwable e) { diff --git a/src/main/java/dev/isxander/controlify/utils/CursorUtils.java b/src/main/java/dev/isxander/controlify/impl/utils/CursorUtils.java similarity index 95% rename from src/main/java/dev/isxander/controlify/utils/CursorUtils.java rename to src/main/java/dev/isxander/controlify/impl/utils/CursorUtils.java index a99eddab9..3c1f76a20 100644 --- a/src/main/java/dev/isxander/controlify/utils/CursorUtils.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/CursorUtils.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; import com.mojang.blaze3d.platform.Window; diff --git a/src/main/java/dev/isxander/controlify/utils/DebugDump.java b/src/main/java/dev/isxander/controlify/impl/utils/DebugDump.java similarity index 89% rename from src/main/java/dev/isxander/controlify/utils/DebugDump.java rename to src/main/java/dev/isxander/controlify/impl/utils/DebugDump.java index 90986cb95..fac722efd 100644 --- a/src/main/java/dev/isxander/controlify/utils/DebugDump.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/DebugDump.java @@ -4,14 +4,14 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; import com.sun.jna.Platform; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controllermanager.ControllerManager; -import dev.isxander.controlify.platform.Environment; -import dev.isxander.controlify.platform.main.PlatformMainUtil; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controllermanager.ControllerManager; +import dev.isxander.controlify.impl.platform.Environment; +import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; import net.minecraft.SharedConstants; import net.minecraft.resources.Identifier; @@ -49,7 +49,7 @@ private static void dumpClientDebug(IndentedStringBuilder dump) { dump.line("Controller-manager: ", controllerManager.getClass().getName()); dump.line("Connected Controllers:").pushIndent(); - for (ControllerEntity controller : controllerManager.getConnectedControllers()) { + for (ControllerImpl controller : controllerManager.getConnectedControllers()) { dump.line("Name: ", controller.name()); dump.line("Identified type: ", controller.info().type()); dump.line("GUID: ", controller.guid()); diff --git a/src/main/java/dev/isxander/controlify/utils/DebugLog.java b/src/main/java/dev/isxander/controlify/impl/utils/DebugLog.java similarity index 74% rename from src/main/java/dev/isxander/controlify/utils/DebugLog.java rename to src/main/java/dev/isxander/controlify/impl/utils/DebugLog.java index 7d41b2c9f..ef29c9f36 100644 --- a/src/main/java/dev/isxander/controlify/utils/DebugLog.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/DebugLog.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; -import dev.isxander.controlify.debug.DebugProperties; +import dev.isxander.controlify.impl.debug.DebugProperties; public class DebugLog { public static void log(String message, Object... args) { diff --git a/src/main/java/dev/isxander/controlify/utils/DebugOverlayHelper.java b/src/main/java/dev/isxander/controlify/impl/utils/DebugOverlayHelper.java similarity index 98% rename from src/main/java/dev/isxander/controlify/utils/DebugOverlayHelper.java rename to src/main/java/dev/isxander/controlify/impl/utils/DebugOverlayHelper.java index bf5d3d95b..f4c7e06bc 100644 --- a/src/main/java/dev/isxander/controlify/utils/DebugOverlayHelper.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/DebugOverlayHelper.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; diff --git a/src/main/java/dev/isxander/controlify/utils/DummyMixinTarget.java b/src/main/java/dev/isxander/controlify/impl/utils/DummyMixinTarget.java similarity index 88% rename from src/main/java/dev/isxander/controlify/utils/DummyMixinTarget.java rename to src/main/java/dev/isxander/controlify/impl/utils/DummyMixinTarget.java index 2dbe9d43b..ae80b6ffc 100644 --- a/src/main/java/dev/isxander/controlify/utils/DummyMixinTarget.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/DummyMixinTarget.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; /** * This class is used when you need stonecutter to comment out a mixin completely, diff --git a/src/main/java/dev/isxander/controlify/utils/Easings.java b/src/main/java/dev/isxander/controlify/impl/utils/Easings.java similarity index 94% rename from src/main/java/dev/isxander/controlify/utils/Easings.java rename to src/main/java/dev/isxander/controlify/impl/utils/Easings.java index 525e2fbef..07d958293 100644 --- a/src/main/java/dev/isxander/controlify/utils/Easings.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/Easings.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; import net.minecraft.util.Mth; diff --git a/src/main/java/dev/isxander/controlify/utils/HoldRepeatHelper.java b/src/main/java/dev/isxander/controlify/impl/utils/HoldRepeatHelper.java similarity index 90% rename from src/main/java/dev/isxander/controlify/utils/HoldRepeatHelper.java rename to src/main/java/dev/isxander/controlify/impl/utils/HoldRepeatHelper.java index c742548e9..366d161e3 100644 --- a/src/main/java/dev/isxander/controlify/utils/HoldRepeatHelper.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/HoldRepeatHelper.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; -import dev.isxander.controlify.api.bind.InputBinding; +import dev.isxander.controlify.impl.api.bind.InputBinding; public class HoldRepeatHelper { private final int initialDelay, repeatDelay; diff --git a/src/main/java/dev/isxander/controlify/utils/InitialScreenRegistryDuck.java b/src/main/java/dev/isxander/controlify/impl/utils/InitialScreenRegistryDuck.java similarity index 94% rename from src/main/java/dev/isxander/controlify/utils/InitialScreenRegistryDuck.java rename to src/main/java/dev/isxander/controlify/impl/utils/InitialScreenRegistryDuck.java index 36a9b9987..d8792ee79 100644 --- a/src/main/java/dev/isxander/controlify/utils/InitialScreenRegistryDuck.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/InitialScreenRegistryDuck.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; diff --git a/src/main/java/dev/isxander/controlify/utils/JsonTreeParser.java b/src/main/java/dev/isxander/controlify/impl/utils/JsonTreeParser.java similarity index 97% rename from src/main/java/dev/isxander/controlify/utils/JsonTreeParser.java rename to src/main/java/dev/isxander/controlify/impl/utils/JsonTreeParser.java index 17038c402..088279850 100644 --- a/src/main/java/dev/isxander/controlify/utils/JsonTreeParser.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/JsonTreeParser.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; import com.google.gson.*; import org.quiltmc.parsers.json.JsonReader; diff --git a/src/main/java/dev/isxander/controlify/utils/LazyComponentDims.java b/src/main/java/dev/isxander/controlify/impl/utils/LazyComponentDims.java similarity index 83% rename from src/main/java/dev/isxander/controlify/utils/LazyComponentDims.java rename to src/main/java/dev/isxander/controlify/impl/utils/LazyComponentDims.java index afc7c3136..92bd98793 100644 --- a/src/main/java/dev/isxander/controlify/utils/LazyComponentDims.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/LazyComponentDims.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.controller.id.ControllerType; -import dev.isxander.controlify.font.BindingFontHelper; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.controllermanager.ControllerType; +import dev.isxander.controlify.impl.font.BindingFontHelper; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.network.chat.Component; @@ -62,8 +62,8 @@ private int getCacheKey() { minecraft.getWindow().getGuiScaledHeight(), minecraft.font, Controlify.instance().getCurrentController() - .map(c -> c.info().type().namespace()) - .orElse(ControllerType.DEFAULT.namespace()) + .map(c -> c.info().type().id()) + .orElse(ControllerType.DEFAULT.id()) ); } } diff --git a/src/main/java/dev/isxander/controlify/utils/MinecraftUtil.java b/src/main/java/dev/isxander/controlify/impl/utils/MinecraftUtil.java similarity index 97% rename from src/main/java/dev/isxander/controlify/utils/MinecraftUtil.java rename to src/main/java/dev/isxander/controlify/impl/utils/MinecraftUtil.java index 992b75634..ba763aefe 100644 --- a/src/main/java/dev/isxander/controlify/utils/MinecraftUtil.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/MinecraftUtil.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; import dev.isxander.controlify.mixins.feature.bind.GuiAccessor; import net.minecraft.client.Minecraft; diff --git a/src/main/java/dev/isxander/controlify/utils/MouseMinecraftCallNotifier.java b/src/main/java/dev/isxander/controlify/impl/utils/MouseMinecraftCallNotifier.java similarity index 83% rename from src/main/java/dev/isxander/controlify/utils/MouseMinecraftCallNotifier.java rename to src/main/java/dev/isxander/controlify/impl/utils/MouseMinecraftCallNotifier.java index f0ec7e47e..50a8b79ff 100644 --- a/src/main/java/dev/isxander/controlify/utils/MouseMinecraftCallNotifier.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/MouseMinecraftCallNotifier.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; public interface MouseMinecraftCallNotifier { void controlify$imFromMinecraftSetScreen(); diff --git a/src/main/java/dev/isxander/controlify/utils/MthExt.java b/src/main/java/dev/isxander/controlify/impl/utils/MthExt.java similarity index 89% rename from src/main/java/dev/isxander/controlify/utils/MthExt.java rename to src/main/java/dev/isxander/controlify/impl/utils/MthExt.java index 794c51f22..f7eadd197 100644 --- a/src/main/java/dev/isxander/controlify/utils/MthExt.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/MthExt.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; import net.minecraft.util.Mth; diff --git a/src/main/java/dev/isxander/controlify/utils/Platform.java b/src/main/java/dev/isxander/controlify/impl/utils/Platform.java similarity index 98% rename from src/main/java/dev/isxander/controlify/utils/Platform.java rename to src/main/java/dev/isxander/controlify/impl/utils/Platform.java index a1dda9278..4ab6534af 100644 --- a/src/main/java/dev/isxander/controlify/utils/Platform.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/Platform.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; public enum Platform { UNKNOWN, diff --git a/src/main/java/dev/isxander/controlify/utils/PrecomputedComponentDims.java b/src/main/java/dev/isxander/controlify/impl/utils/PrecomputedComponentDims.java similarity index 89% rename from src/main/java/dev/isxander/controlify/utils/PrecomputedComponentDims.java rename to src/main/java/dev/isxander/controlify/impl/utils/PrecomputedComponentDims.java index 535bab134..ae5bfe9a2 100644 --- a/src/main/java/dev/isxander/controlify/utils/PrecomputedComponentDims.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/PrecomputedComponentDims.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; -import dev.isxander.controlify.font.BindingFontHelper; +import dev.isxander.controlify.impl.font.BindingFontHelper; import net.minecraft.client.gui.Font; import net.minecraft.network.chat.Component; import net.minecraft.util.FormattedCharSequence; diff --git a/src/main/java/dev/isxander/controlify/utils/ResizableRingBuffer.java b/src/main/java/dev/isxander/controlify/impl/utils/ResizableRingBuffer.java similarity index 97% rename from src/main/java/dev/isxander/controlify/utils/ResizableRingBuffer.java rename to src/main/java/dev/isxander/controlify/impl/utils/ResizableRingBuffer.java index fc5c0a051..24eab2379 100644 --- a/src/main/java/dev/isxander/controlify/utils/ResizableRingBuffer.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/ResizableRingBuffer.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; import java.util.function.Supplier; diff --git a/src/main/java/dev/isxander/controlify/utils/SetupWizard.java b/src/main/java/dev/isxander/controlify/impl/utils/SetupWizard.java similarity index 97% rename from src/main/java/dev/isxander/controlify/utils/SetupWizard.java rename to src/main/java/dev/isxander/controlify/impl/utils/SetupWizard.java index 224ef4934..4cf79c7b1 100644 --- a/src/main/java/dev/isxander/controlify/utils/SetupWizard.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/SetupWizard.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; import net.minecraft.client.gui.screens.Screen; diff --git a/src/main/java/dev/isxander/controlify/utils/SetupWizardManager.java b/src/main/java/dev/isxander/controlify/impl/utils/SetupWizardManager.java similarity index 86% rename from src/main/java/dev/isxander/controlify/utils/SetupWizardManager.java rename to src/main/java/dev/isxander/controlify/impl/utils/SetupWizardManager.java index ca601f2ca..87422053c 100644 --- a/src/main/java/dev/isxander/controlify/utils/SetupWizardManager.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/SetupWizardManager.java @@ -1,6 +1,6 @@ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; -import dev.isxander.controlify.gui.screen.DontInterruptScreen; +import dev.isxander.controlify.impl.gui.screen.DontInterruptScreen; import java.util.ArrayDeque; import java.util.Queue; diff --git a/src/main/java/dev/isxander/controlify/utils/TrackingBodySubscriber.java b/src/main/java/dev/isxander/controlify/impl/utils/TrackingBodySubscriber.java similarity index 97% rename from src/main/java/dev/isxander/controlify/utils/TrackingBodySubscriber.java rename to src/main/java/dev/isxander/controlify/impl/utils/TrackingBodySubscriber.java index ab95f429e..2777b3b37 100644 --- a/src/main/java/dev/isxander/controlify/utils/TrackingBodySubscriber.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/TrackingBodySubscriber.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; import java.net.http.HttpResponse; import java.nio.ByteBuffer; diff --git a/src/main/java/dev/isxander/controlify/utils/TrackingConsumer.java b/src/main/java/dev/isxander/controlify/impl/utils/TrackingConsumer.java similarity index 89% rename from src/main/java/dev/isxander/controlify/utils/TrackingConsumer.java rename to src/main/java/dev/isxander/controlify/impl/utils/TrackingConsumer.java index 10df886c7..94e29ff7b 100644 --- a/src/main/java/dev/isxander/controlify/utils/TrackingConsumer.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/TrackingConsumer.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; import java.util.Optional; import java.util.function.BiConsumer; diff --git a/src/main/java/dev/isxander/controlify/utils/UnhandledCompletableFutures.java b/src/main/java/dev/isxander/controlify/impl/utils/UnhandledCompletableFutures.java similarity index 95% rename from src/main/java/dev/isxander/controlify/utils/UnhandledCompletableFutures.java rename to src/main/java/dev/isxander/controlify/impl/utils/UnhandledCompletableFutures.java index 8283a1f47..98ba6da8c 100644 --- a/src/main/java/dev/isxander/controlify/utils/UnhandledCompletableFutures.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/UnhandledCompletableFutures.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils; +package dev.isxander.controlify.impl.utils; import net.minecraft.ReportedException; import net.minecraft.client.Minecraft; diff --git a/src/main/java/dev/isxander/controlify/utils/animation/api/Animatable.java b/src/main/java/dev/isxander/controlify/impl/utils/animation/api/Animatable.java similarity index 95% rename from src/main/java/dev/isxander/controlify/utils/animation/api/Animatable.java rename to src/main/java/dev/isxander/controlify/impl/utils/animation/api/Animatable.java index cbc0d05c2..ad6efa79e 100644 --- a/src/main/java/dev/isxander/controlify/utils/animation/api/Animatable.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/animation/api/Animatable.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.animation.api; +package dev.isxander.controlify.impl.utils.animation.api; import org.jetbrains.annotations.ApiStatus; diff --git a/src/main/java/dev/isxander/controlify/utils/animation/api/Animation.java b/src/main/java/dev/isxander/controlify/impl/utils/animation/api/Animation.java similarity index 96% rename from src/main/java/dev/isxander/controlify/utils/animation/api/Animation.java rename to src/main/java/dev/isxander/controlify/impl/utils/animation/api/Animation.java index 0f9b3b6f3..99a462502 100644 --- a/src/main/java/dev/isxander/controlify/utils/animation/api/Animation.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/animation/api/Animation.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.animation.api; +package dev.isxander.controlify.impl.utils.animation.api; -import dev.isxander.controlify.utils.animation.impl.AnimationImpl; +import dev.isxander.controlify.impl.utils.animation.impl.AnimationImpl; import java.util.function.Consumer; diff --git a/src/main/java/dev/isxander/controlify/utils/animation/api/AnimationGroup.java b/src/main/java/dev/isxander/controlify/impl/utils/animation/api/AnimationGroup.java similarity index 90% rename from src/main/java/dev/isxander/controlify/utils/animation/api/AnimationGroup.java rename to src/main/java/dev/isxander/controlify/impl/utils/animation/api/AnimationGroup.java index cfdf32aa3..e7359188e 100644 --- a/src/main/java/dev/isxander/controlify/utils/animation/api/AnimationGroup.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/animation/api/AnimationGroup.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.animation.api; +package dev.isxander.controlify.impl.utils.animation.api; -import dev.isxander.controlify.utils.animation.impl.AnimationGroupImpl; +import dev.isxander.controlify.impl.utils.animation.impl.AnimationGroupImpl; /** * A group of {@link Animatable}s where all are played concurrently diff --git a/src/main/java/dev/isxander/controlify/utils/animation/api/AnimationSequence.java b/src/main/java/dev/isxander/controlify/impl/utils/animation/api/AnimationSequence.java similarity index 90% rename from src/main/java/dev/isxander/controlify/utils/animation/api/AnimationSequence.java rename to src/main/java/dev/isxander/controlify/impl/utils/animation/api/AnimationSequence.java index db303d8f3..4b8ffec6b 100644 --- a/src/main/java/dev/isxander/controlify/utils/animation/api/AnimationSequence.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/animation/api/AnimationSequence.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.animation.api; +package dev.isxander.controlify.impl.utils.animation.api; -import dev.isxander.controlify.utils.animation.impl.AnimationSequenceImpl; +import dev.isxander.controlify.impl.utils.animation.impl.AnimationSequenceImpl; /** * A sequence of {@link Animatable}s where each animatable is played one diff --git a/src/main/java/dev/isxander/controlify/utils/animation/api/EasingFunction.java b/src/main/java/dev/isxander/controlify/impl/utils/animation/api/EasingFunction.java similarity index 98% rename from src/main/java/dev/isxander/controlify/utils/animation/api/EasingFunction.java rename to src/main/java/dev/isxander/controlify/impl/utils/animation/api/EasingFunction.java index 410f14e4d..6ced261d3 100644 --- a/src/main/java/dev/isxander/controlify/utils/animation/api/EasingFunction.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/animation/api/EasingFunction.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.animation.api; +package dev.isxander.controlify.impl.utils.animation.api; import net.minecraft.util.Mth; diff --git a/src/main/java/dev/isxander/controlify/utils/animation/impl/AnimationGroupImpl.java b/src/main/java/dev/isxander/controlify/impl/utils/animation/impl/AnimationGroupImpl.java similarity index 90% rename from src/main/java/dev/isxander/controlify/utils/animation/impl/AnimationGroupImpl.java rename to src/main/java/dev/isxander/controlify/impl/utils/animation/impl/AnimationGroupImpl.java index 41f96bc0a..3ce55b795 100644 --- a/src/main/java/dev/isxander/controlify/utils/animation/impl/AnimationGroupImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/animation/impl/AnimationGroupImpl.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.animation.impl; +package dev.isxander.controlify.impl.utils.animation.impl; -import dev.isxander.controlify.utils.animation.api.Animatable; -import dev.isxander.controlify.utils.animation.api.AnimationGroup; +import dev.isxander.controlify.impl.utils.animation.api.Animatable; +import dev.isxander.controlify.impl.utils.animation.api.AnimationGroup; import it.unimi.dsi.fastutil.objects.ObjectArraySet; import org.apache.commons.lang3.Validate; diff --git a/src/main/java/dev/isxander/controlify/utils/animation/impl/AnimationImpl.java b/src/main/java/dev/isxander/controlify/impl/utils/animation/impl/AnimationImpl.java similarity index 95% rename from src/main/java/dev/isxander/controlify/utils/animation/impl/AnimationImpl.java rename to src/main/java/dev/isxander/controlify/impl/utils/animation/impl/AnimationImpl.java index eb9019fff..3d31c0e55 100644 --- a/src/main/java/dev/isxander/controlify/utils/animation/impl/AnimationImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/animation/impl/AnimationImpl.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.animation.impl; +package dev.isxander.controlify.impl.utils.animation.impl; -import dev.isxander.controlify.utils.animation.api.Animation; -import dev.isxander.controlify.utils.animation.api.EasingFunction; +import dev.isxander.controlify.impl.utils.animation.api.Animation; +import dev.isxander.controlify.impl.utils.animation.api.EasingFunction; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; diff --git a/src/main/java/dev/isxander/controlify/utils/animation/impl/AnimationSequenceImpl.java b/src/main/java/dev/isxander/controlify/impl/utils/animation/impl/AnimationSequenceImpl.java similarity index 90% rename from src/main/java/dev/isxander/controlify/utils/animation/impl/AnimationSequenceImpl.java rename to src/main/java/dev/isxander/controlify/impl/utils/animation/impl/AnimationSequenceImpl.java index 90a94a2a3..63eef4fae 100644 --- a/src/main/java/dev/isxander/controlify/utils/animation/impl/AnimationSequenceImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/animation/impl/AnimationSequenceImpl.java @@ -4,10 +4,10 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.animation.impl; +package dev.isxander.controlify.impl.utils.animation.impl; -import dev.isxander.controlify.utils.animation.api.Animatable; -import dev.isxander.controlify.utils.animation.api.AnimationSequence; +import dev.isxander.controlify.impl.utils.animation.api.Animatable; +import dev.isxander.controlify.impl.utils.animation.api.AnimationSequence; import org.apache.commons.lang3.Validate; import java.util.ArrayDeque; diff --git a/src/main/java/dev/isxander/controlify/utils/animation/impl/Animator.java b/src/main/java/dev/isxander/controlify/impl/utils/animation/impl/Animator.java similarity index 83% rename from src/main/java/dev/isxander/controlify/utils/animation/impl/Animator.java rename to src/main/java/dev/isxander/controlify/impl/utils/animation/impl/Animator.java index cd1fe8f3c..9352cdfc3 100644 --- a/src/main/java/dev/isxander/controlify/utils/animation/impl/Animator.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/animation/impl/Animator.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.animation.impl; +package dev.isxander.controlify.impl.utils.animation.impl; -import dev.isxander.controlify.utils.animation.api.Animatable; +import dev.isxander.controlify.impl.utils.animation.api.Animatable; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; diff --git a/src/main/java/dev/isxander/controlify/utils/codec/CExtraCodecs.java b/src/main/java/dev/isxander/controlify/impl/utils/codec/CExtraCodecs.java similarity index 98% rename from src/main/java/dev/isxander/controlify/utils/codec/CExtraCodecs.java rename to src/main/java/dev/isxander/controlify/impl/utils/codec/CExtraCodecs.java index 8b33daf6c..68a0ff556 100644 --- a/src/main/java/dev/isxander/controlify/utils/codec/CExtraCodecs.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/codec/CExtraCodecs.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.codec; +package dev.isxander.controlify.impl.utils.codec; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.*; diff --git a/src/main/java/dev/isxander/controlify/utils/codec/FuzzyCodec.java b/src/main/java/dev/isxander/controlify/impl/utils/codec/FuzzyCodec.java similarity index 96% rename from src/main/java/dev/isxander/controlify/utils/codec/FuzzyCodec.java rename to src/main/java/dev/isxander/controlify/impl/utils/codec/FuzzyCodec.java index 110e3e7b2..39ec15a77 100644 --- a/src/main/java/dev/isxander/controlify/utils/codec/FuzzyCodec.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/codec/FuzzyCodec.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.codec; +package dev.isxander.controlify.impl.utils.codec; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; diff --git a/src/main/java/dev/isxander/controlify/utils/codec/FuzzyMapCodec.java b/src/main/java/dev/isxander/controlify/impl/utils/codec/FuzzyMapCodec.java similarity index 96% rename from src/main/java/dev/isxander/controlify/utils/codec/FuzzyMapCodec.java rename to src/main/java/dev/isxander/controlify/impl/utils/codec/FuzzyMapCodec.java index 1c49aceda..c31de7149 100644 --- a/src/main/java/dev/isxander/controlify/utils/codec/FuzzyMapCodec.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/codec/FuzzyMapCodec.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.codec; +package dev.isxander.controlify.impl.utils.codec; import com.mojang.serialization.*; diff --git a/src/main/java/dev/isxander/controlify/utils/codec/SetCodec.java b/src/main/java/dev/isxander/controlify/impl/utils/codec/SetCodec.java similarity index 98% rename from src/main/java/dev/isxander/controlify/utils/codec/SetCodec.java rename to src/main/java/dev/isxander/controlify/impl/utils/codec/SetCodec.java index 54c09eb6a..7b4e1bdbe 100644 --- a/src/main/java/dev/isxander/controlify/utils/codec/SetCodec.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/codec/SetCodec.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.codec; +package dev.isxander.controlify.impl.utils.codec; import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.util.Unit; diff --git a/src/main/java/dev/isxander/controlify/utils/codec/StrictEitherMapCodec.java b/src/main/java/dev/isxander/controlify/impl/utils/codec/StrictEitherMapCodec.java similarity index 95% rename from src/main/java/dev/isxander/controlify/utils/codec/StrictEitherMapCodec.java rename to src/main/java/dev/isxander/controlify/impl/utils/codec/StrictEitherMapCodec.java index f8181654d..284ae5050 100644 --- a/src/main/java/dev/isxander/controlify/utils/codec/StrictEitherMapCodec.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/codec/StrictEitherMapCodec.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.codec; +package dev.isxander.controlify.impl.utils.codec; import com.mojang.serialization.*; diff --git a/src/main/java/dev/isxander/controlify/utils/event/EventSink.java b/src/main/java/dev/isxander/controlify/impl/utils/event/EventSink.java similarity index 61% rename from src/main/java/dev/isxander/controlify/utils/event/EventSink.java rename to src/main/java/dev/isxander/controlify/impl/utils/event/EventSink.java index ef93c6831..37436842d 100644 --- a/src/main/java/dev/isxander/controlify/utils/event/EventSink.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/event/EventSink.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.utils.event; +package dev.isxander.controlify.impl.utils.event; @FunctionalInterface public interface EventSink { diff --git a/src/main/java/dev/isxander/controlify/utils/event/EventSource.java b/src/main/java/dev/isxander/controlify/impl/utils/event/EventSource.java similarity index 91% rename from src/main/java/dev/isxander/controlify/utils/event/EventSource.java rename to src/main/java/dev/isxander/controlify/impl/utils/event/EventSource.java index d96dbfec8..46556805e 100644 --- a/src/main/java/dev/isxander/controlify/utils/event/EventSource.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/event/EventSource.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.utils.event; +package dev.isxander.controlify.impl.utils.event; import java.util.function.Predicate; diff --git a/src/main/java/dev/isxander/controlify/utils/event/EventStage.java b/src/main/java/dev/isxander/controlify/impl/utils/event/EventStage.java similarity index 94% rename from src/main/java/dev/isxander/controlify/utils/event/EventStage.java rename to src/main/java/dev/isxander/controlify/impl/utils/event/EventStage.java index 8d9de4c17..0f9391d99 100644 --- a/src/main/java/dev/isxander/controlify/utils/event/EventStage.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/event/EventStage.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.utils.event; +package dev.isxander.controlify.impl.utils.event; @FunctionalInterface public interface EventStage { diff --git a/src/main/java/dev/isxander/controlify/utils/event/EventTap.java b/src/main/java/dev/isxander/controlify/impl/utils/event/EventTap.java similarity index 96% rename from src/main/java/dev/isxander/controlify/utils/event/EventTap.java rename to src/main/java/dev/isxander/controlify/impl/utils/event/EventTap.java index b8658c026..ee73a45d8 100644 --- a/src/main/java/dev/isxander/controlify/utils/event/EventTap.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/event/EventTap.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.utils.event; +package dev.isxander.controlify.impl.utils.event; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; diff --git a/src/main/java/dev/isxander/controlify/utils/event/UnaryEventStage.java b/src/main/java/dev/isxander/controlify/impl/utils/event/UnaryEventStage.java similarity index 63% rename from src/main/java/dev/isxander/controlify/utils/event/UnaryEventStage.java rename to src/main/java/dev/isxander/controlify/impl/utils/event/UnaryEventStage.java index cbf80fb8e..d62293485 100644 --- a/src/main/java/dev/isxander/controlify/utils/event/UnaryEventStage.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/event/UnaryEventStage.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.utils.event; +package dev.isxander.controlify.impl.utils.event; @FunctionalInterface public interface UnaryEventStage extends EventStage { diff --git a/src/main/java/dev/isxander/controlify/utils/log/AbstractControlifyLogger.java b/src/main/java/dev/isxander/controlify/impl/utils/log/AbstractControlifyLogger.java similarity index 98% rename from src/main/java/dev/isxander/controlify/utils/log/AbstractControlifyLogger.java rename to src/main/java/dev/isxander/controlify/impl/utils/log/AbstractControlifyLogger.java index 6a5f7969b..a19fd3d57 100644 --- a/src/main/java/dev/isxander/controlify/utils/log/AbstractControlifyLogger.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/log/AbstractControlifyLogger.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.log; +package dev.isxander.controlify.impl.utils.log; import net.minecraft.CrashReport; import net.minecraft.ReportType; diff --git a/src/main/java/dev/isxander/controlify/utils/log/ControlifyLogger.java b/src/main/java/dev/isxander/controlify/impl/utils/log/ControlifyLogger.java similarity index 97% rename from src/main/java/dev/isxander/controlify/utils/log/ControlifyLogger.java rename to src/main/java/dev/isxander/controlify/impl/utils/log/ControlifyLogger.java index ebda7cb66..ca0401577 100644 --- a/src/main/java/dev/isxander/controlify/utils/log/ControlifyLogger.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/log/ControlifyLogger.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.log; +package dev.isxander.controlify.impl.utils.log; import net.minecraft.CrashReport; import org.jetbrains.annotations.Contract; diff --git a/src/main/java/dev/isxander/controlify/utils/log/ControlifyMasterLogger.java b/src/main/java/dev/isxander/controlify/impl/utils/log/ControlifyMasterLogger.java similarity index 91% rename from src/main/java/dev/isxander/controlify/utils/log/ControlifyMasterLogger.java rename to src/main/java/dev/isxander/controlify/impl/utils/log/ControlifyMasterLogger.java index 0928c23ea..721e9f8f8 100644 --- a/src/main/java/dev/isxander/controlify/utils/log/ControlifyMasterLogger.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/log/ControlifyMasterLogger.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.log; +package dev.isxander.controlify.impl.utils.log; -import dev.isxander.controlify.debug.DebugProperties; +import dev.isxander.controlify.impl.debug.DebugProperties; import org.jetbrains.annotations.Nullable; public class ControlifyMasterLogger extends AbstractControlifyLogger { diff --git a/src/main/java/dev/isxander/controlify/utils/log/ControlifySubLogger.java b/src/main/java/dev/isxander/controlify/impl/utils/log/ControlifySubLogger.java similarity index 81% rename from src/main/java/dev/isxander/controlify/utils/log/ControlifySubLogger.java rename to src/main/java/dev/isxander/controlify/impl/utils/log/ControlifySubLogger.java index f1228f8fe..3017a9bd2 100644 --- a/src/main/java/dev/isxander/controlify/utils/log/ControlifySubLogger.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/log/ControlifySubLogger.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.log; +package dev.isxander.controlify.impl.utils.log; public interface ControlifySubLogger extends ControlifyLogger { String name(); diff --git a/src/main/java/dev/isxander/controlify/utils/log/ControlifySubLoggerImpl.java b/src/main/java/dev/isxander/controlify/impl/utils/log/ControlifySubLoggerImpl.java similarity index 95% rename from src/main/java/dev/isxander/controlify/utils/log/ControlifySubLoggerImpl.java rename to src/main/java/dev/isxander/controlify/impl/utils/log/ControlifySubLoggerImpl.java index dc5d69c55..4c64becdb 100644 --- a/src/main/java/dev/isxander/controlify/utils/log/ControlifySubLoggerImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/log/ControlifySubLoggerImpl.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.log; +package dev.isxander.controlify.impl.utils.log; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/utils/log/LogLevel.java b/src/main/java/dev/isxander/controlify/impl/utils/log/LogLevel.java similarity index 78% rename from src/main/java/dev/isxander/controlify/utils/log/LogLevel.java rename to src/main/java/dev/isxander/controlify/impl/utils/log/LogLevel.java index 33b56d989..c09af6476 100644 --- a/src/main/java/dev/isxander/controlify/utils/log/LogLevel.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/log/LogLevel.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.log; +package dev.isxander.controlify.impl.utils.log; public enum LogLevel { INFO, diff --git a/src/main/java/dev/isxander/controlify/utils/log/LogMessage.java b/src/main/java/dev/isxander/controlify/impl/utils/log/LogMessage.java similarity index 96% rename from src/main/java/dev/isxander/controlify/utils/log/LogMessage.java rename to src/main/java/dev/isxander/controlify/impl/utils/log/LogMessage.java index f62d033d4..08ffff759 100644 --- a/src/main/java/dev/isxander/controlify/utils/log/LogMessage.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/log/LogMessage.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.log; +package dev.isxander.controlify.impl.utils.log; import java.io.PrintWriter; import java.io.StringWriter; diff --git a/src/main/java/dev/isxander/controlify/utils/predicates/ClientEntityPredicate.java b/src/main/java/dev/isxander/controlify/impl/utils/predicates/ClientEntityPredicate.java similarity index 98% rename from src/main/java/dev/isxander/controlify/utils/predicates/ClientEntityPredicate.java rename to src/main/java/dev/isxander/controlify/impl/utils/predicates/ClientEntityPredicate.java index 28318fb0c..e99f2efdf 100644 --- a/src/main/java/dev/isxander/controlify/utils/predicates/ClientEntityPredicate.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/predicates/ClientEntityPredicate.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.predicates; +package dev.isxander.controlify.impl.utils.predicates; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; diff --git a/src/main/java/dev/isxander/controlify/utils/predicates/ClientPredicateUtils.java b/src/main/java/dev/isxander/controlify/impl/utils/predicates/ClientPredicateUtils.java similarity index 94% rename from src/main/java/dev/isxander/controlify/utils/predicates/ClientPredicateUtils.java rename to src/main/java/dev/isxander/controlify/impl/utils/predicates/ClientPredicateUtils.java index 6e48b0cd2..559ca9cfc 100644 --- a/src/main/java/dev/isxander/controlify/utils/predicates/ClientPredicateUtils.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/predicates/ClientPredicateUtils.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.predicates; +package dev.isxander.controlify.impl.utils.predicates; -import dev.isxander.controlify.utils.CUtil; +import dev.isxander.controlify.impl.utils.CUtil; import it.unimi.dsi.fastutil.ints.IntList; //? if >=26.2 { import net.minecraft.advancements.predicates.*; diff --git a/src/main/java/dev/isxander/controlify/utils/predicates/EntityEquipmentPredicate.java b/src/main/java/dev/isxander/controlify/impl/utils/predicates/EntityEquipmentPredicate.java similarity index 98% rename from src/main/java/dev/isxander/controlify/utils/predicates/EntityEquipmentPredicate.java rename to src/main/java/dev/isxander/controlify/impl/utils/predicates/EntityEquipmentPredicate.java index 971862f2b..5b62a3e5d 100644 --- a/src/main/java/dev/isxander/controlify/utils/predicates/EntityEquipmentPredicate.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/predicates/EntityEquipmentPredicate.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.predicates; +package dev.isxander.controlify.impl.utils.predicates; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; diff --git a/src/main/java/dev/isxander/controlify/utils/render/BaseRenderState.java b/src/main/java/dev/isxander/controlify/impl/utils/render/BaseRenderState.java similarity index 95% rename from src/main/java/dev/isxander/controlify/utils/render/BaseRenderState.java rename to src/main/java/dev/isxander/controlify/impl/utils/render/BaseRenderState.java index f61f4bc81..de219062d 100644 --- a/src/main/java/dev/isxander/controlify/utils/render/BaseRenderState.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/render/BaseRenderState.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.render; +package dev.isxander.controlify.impl.utils.render; -import dev.isxander.controlify.platform.client.PlatformClientUtil; +import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.navigation.ScreenRectangle; diff --git a/src/main/java/dev/isxander/controlify/utils/render/CGuiElementRenderState.java b/src/main/java/dev/isxander/controlify/impl/utils/render/CGuiElementRenderState.java similarity index 90% rename from src/main/java/dev/isxander/controlify/utils/render/CGuiElementRenderState.java rename to src/main/java/dev/isxander/controlify/impl/utils/render/CGuiElementRenderState.java index f0a0de30b..563af49e9 100644 --- a/src/main/java/dev/isxander/controlify/utils/render/CGuiElementRenderState.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/render/CGuiElementRenderState.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.render; +package dev.isxander.controlify.impl.utils.render; -import dev.isxander.controlify.platform.client.PlatformClientUtil; +import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.renderer.state.gui.GuiElementRenderState; import net.minecraft.client.gui.render.TextureSetup; diff --git a/src/main/java/dev/isxander/controlify/utils/render/RenderUtils.java b/src/main/java/dev/isxander/controlify/impl/utils/render/RenderUtils.java similarity index 95% rename from src/main/java/dev/isxander/controlify/utils/render/RenderUtils.java rename to src/main/java/dev/isxander/controlify/impl/utils/render/RenderUtils.java index 912bb9be9..5de1781cd 100644 --- a/src/main/java/dev/isxander/controlify/utils/render/RenderUtils.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/render/RenderUtils.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.render; +package dev.isxander.controlify.impl.utils.render; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.renderer.RenderPipelines; diff --git a/src/main/java/dev/isxander/controlify/utils/render/elements/CircleElementRenderState.java b/src/main/java/dev/isxander/controlify/impl/utils/render/elements/CircleElementRenderState.java similarity index 92% rename from src/main/java/dev/isxander/controlify/utils/render/elements/CircleElementRenderState.java rename to src/main/java/dev/isxander/controlify/impl/utils/render/elements/CircleElementRenderState.java index 527cd9954..3109f6360 100644 --- a/src/main/java/dev/isxander/controlify/utils/render/elements/CircleElementRenderState.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/render/elements/CircleElementRenderState.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.utils.render.elements; +package dev.isxander.controlify.impl.utils.render.elements; import com.mojang.blaze3d.vertex.VertexConsumer; -import dev.isxander.controlify.utils.render.BaseRenderState; -import dev.isxander.controlify.utils.render.CGuiElementRenderState; +import dev.isxander.controlify.impl.utils.render.BaseRenderState; +import dev.isxander.controlify.impl.utils.render.CGuiElementRenderState; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.util.Mth; import org.jspecify.annotations.NonNull; diff --git a/src/main/java/dev/isxander/controlify/virtualmouse/SnapUtils.java b/src/main/java/dev/isxander/controlify/impl/virtualmouse/SnapUtils.java similarity index 95% rename from src/main/java/dev/isxander/controlify/virtualmouse/SnapUtils.java rename to src/main/java/dev/isxander/controlify/impl/virtualmouse/SnapUtils.java index e0e1a9bf4..cc54809cd 100644 --- a/src/main/java/dev/isxander/controlify/virtualmouse/SnapUtils.java +++ b/src/main/java/dev/isxander/controlify/impl/virtualmouse/SnapUtils.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.virtualmouse; +package dev.isxander.controlify.impl.virtualmouse; -import dev.isxander.controlify.api.vmousesnapping.SnapPoint; +import dev.isxander.controlify.impl.api.vmousesnapping.SnapPoint; import dev.isxander.controlify.mixins.feature.virtualmouse.snapping.RecipeBookComponentAccessor; import dev.isxander.controlify.mixins.feature.virtualmouse.snapping.RecipeBookPageAccessor; import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent; diff --git a/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseBehaviour.java b/src/main/java/dev/isxander/controlify/impl/virtualmouse/VirtualMouseBehaviour.java similarity index 88% rename from src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseBehaviour.java rename to src/main/java/dev/isxander/controlify/impl/virtualmouse/VirtualMouseBehaviour.java index dd640771d..1768e6322 100644 --- a/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseBehaviour.java +++ b/src/main/java/dev/isxander/controlify/impl/virtualmouse/VirtualMouseBehaviour.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.virtualmouse; +package dev.isxander.controlify.impl.virtualmouse; public enum VirtualMouseBehaviour { DEFAULT, diff --git a/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseHandler.java b/src/main/java/dev/isxander/controlify/impl/virtualmouse/VirtualMouseHandler.java similarity index 94% rename from src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseHandler.java rename to src/main/java/dev/isxander/controlify/impl/virtualmouse/VirtualMouseHandler.java index b5409039c..79160747c 100644 --- a/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseHandler.java +++ b/src/main/java/dev/isxander/controlify/impl/virtualmouse/VirtualMouseHandler.java @@ -4,26 +4,25 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.virtualmouse; +package dev.isxander.controlify.impl.virtualmouse; import com.mojang.blaze3d.platform.InputConstants; import com.mojang.datafixers.util.Pair; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.InputMode; -import dev.isxander.controlify.api.vmousesnapping.ISnapBehaviour; -import dev.isxander.controlify.api.vmousesnapping.SnapPoint; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.controller.*; -import dev.isxander.controlify.input.mapping.GamepadInputs; -import dev.isxander.controlify.controller.input.InputComponent; -import dev.isxander.controlify.controller.touchpad.TouchpadComponent; -import dev.isxander.controlify.debug.DebugProperties; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.api.event.ControlifyEvents; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.api.InputMode; +import dev.isxander.controlify.impl.api.vmousesnapping.ISnapBehaviour; +import dev.isxander.controlify.impl.api.vmousesnapping.SnapPoint; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.impl.input.mapping.GamepadInputs; +import dev.isxander.controlify.impl.controller.input.InputComponent; +import dev.isxander.controlify.impl.controller.touchpad.TouchpadComponent; +import dev.isxander.controlify.impl.debug.DebugProperties; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.api.ControlifyEvents; import dev.isxander.controlify.mixins.feature.virtualmouse.MouseHandlerAccessor; -import dev.isxander.controlify.utils.*; +import dev.isxander.controlify.impl.utils.*; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.navigation.ScreenAxis; @@ -63,7 +62,7 @@ public VirtualMouseHandler() { ControlifyEvents.INPUT_MODE_CHANGED.register(event -> this.onInputModeChanged(event.mode())); } - public void handleControllerInput(ControllerEntity controller) { + public void handleControllerInput(ControllerImpl controller) { if (MinecraftUtil.getScreen() == null) return; if (ControlifyBindings.VMOUSE_TOGGLE.on(controller).justPressed()) { @@ -177,7 +176,7 @@ public void handleControllerInput(ControllerEntity controller) { } } - public void handleCompatibilityBinds(ControllerEntity controller) { + public void handleCompatibilityBinds(ControllerImpl controller) { var windowHandle = minecraft.getWindow().handle(); // Optional touchpad = controller.touchpad(); @@ -206,7 +205,7 @@ public void handleCompatibilityBinds(ControllerEntity controller) { } } - public void handleScroll(ControllerEntity controller) { + public void handleScroll(ControllerImpl controller) { scrollY += ControlifyBindings.VMOUSE_SCROLL_UP.on(controller).analogueNow() - ControlifyBindings.VMOUSE_SCROLL_DOWN.on(controller).analogueNow(); } diff --git a/src/main/java/dev/isxander/controlify/wireless/LowBatteryNotifier.java b/src/main/java/dev/isxander/controlify/impl/wireless/LowBatteryNotifier.java similarity index 72% rename from src/main/java/dev/isxander/controlify/wireless/LowBatteryNotifier.java rename to src/main/java/dev/isxander/controlify/impl/wireless/LowBatteryNotifier.java index c06014598..939f2c16b 100644 --- a/src/main/java/dev/isxander/controlify/wireless/LowBatteryNotifier.java +++ b/src/main/java/dev/isxander/controlify/impl/wireless/LowBatteryNotifier.java @@ -4,14 +4,14 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.wireless; - -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.controller.battery.PowerState; -import dev.isxander.controlify.controller.battery.BatteryLevelComponent; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controllermanager.ControllerManager; -import dev.isxander.controlify.utils.MinecraftUtil; +package dev.isxander.controlify.impl.wireless; + +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.controller.battery.PowerState; +import dev.isxander.controlify.impl.controller.battery.BatteryLevelComponent; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controllermanager.ControllerManager; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.network.chat.Component; import java.util.HashSet; @@ -35,7 +35,7 @@ public static void tick() { if (controllerManager == null) return; - for (ControllerEntity controller : controllerManager.getAllControllers()) { + for (ControllerImpl controller : controllerManager.getAllControllers()) { PowerState batteryLevel = controller.batteryLevel() .map(BatteryLevelComponent::getBatteryLevel) .orElse(new PowerState.Unknown()); diff --git a/src/main/java/dev/isxander/controlify/mixins/core/ClientPacketListenerMixin.java b/src/main/java/dev/isxander/controlify/mixins/core/ClientPacketListenerMixin.java index 73cf3dbd1..d9c5bb82a 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/ClientPacketListenerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/ClientPacketListenerMixin.java @@ -9,7 +9,7 @@ import com.llamalad7.mixinextras.expression.Definition; import com.llamalad7.mixinextras.expression.Expression; import com.llamalad7.mixinextras.sugar.Local; -import dev.isxander.controlify.ingame.ControllerPlayerMovement; +import dev.isxander.controlify.impl.ingame.ControllerPlayerMovement; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.client.player.LocalPlayer; diff --git a/src/main/java/dev/isxander/controlify/mixins/core/GuiMixin.java b/src/main/java/dev/isxander/controlify/mixins/core/GuiMixin.java index bc4264d9d..0a4a29cb2 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/GuiMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/GuiMixin.java @@ -8,11 +8,11 @@ import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.utils.InitialScreenRegistryDuck; -import dev.isxander.controlify.utils.MinecraftUtil; -import dev.isxander.controlify.utils.MouseMinecraftCallNotifier; +import dev.isxander.controlify.impl.utils.InitialScreenRegistryDuck; +import dev.isxander.controlify.impl.utils.MinecraftUtil; +import dev.isxander.controlify.impl.utils.MouseMinecraftCallNotifier; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/core/HudMixin.java b/src/main/java/dev/isxander/controlify/mixins/core/HudMixin.java index 9266b7b96..b03395087 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/HudMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/HudMixin.java @@ -7,8 +7,8 @@ package dev.isxander.controlify.mixins.core; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.ingame.InGameInputHandler; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.ingame.InGameInputHandler; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; 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 638c93cbc..3d73b72db 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/KeyboardHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/KeyboardHandlerMixin.java @@ -68,7 +68,7 @@ private void wrapKeyboardEvents( } } *///?} else { -@Mixin(dev.isxander.controlify.utils.DummyMixinTarget.class) +@Mixin(dev.isxander.controlify.impl.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 736a94226..b41984492 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/MinecraftMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/MinecraftMixin.java @@ -6,9 +6,9 @@ */ package dev.isxander.controlify.mixins.core; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.controllermanager.ControllerCoordinator; -import dev.isxander.controlify.utils.animation.impl.Animator; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.controllermanager.ControllerCoordinator; +import dev.isxander.controlify.impl.utils.animation.impl.Animator; import net.minecraft.CrashReport; import net.minecraft.client.DeltaTracker; import net.minecraft.client.Minecraft; 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 1ebe42f4b..7aa123023 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/MouseHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/MouseHandlerMixin.java @@ -7,13 +7,9 @@ package dev.isxander.controlify.mixins.core; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -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; -import dev.isxander.controlify.InputMode; +import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.utils.MouseMinecraftCallNotifier; +import dev.isxander.controlify.impl.utils.MouseMinecraftCallNotifier; import net.minecraft.client.Minecraft; import net.minecraft.client.MouseHandler; import org.spongepowered.asm.mixin.Final; 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 fdbcedc8a..06dfcecbd 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/SDLEventHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/SDLEventHandlerMixin.java @@ -10,8 +10,8 @@ //? if >=26.3 { import com.mojang.blaze3d.platform.SDLEventHandler; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.InputMode; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.api.InputMode; import net.minecraft.client.Minecraft; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Shadow; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/bind/GuiMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/bind/GuiMixin.java index 3dfedee28..f50134fe1 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/bind/GuiMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/bind/GuiMixin.java @@ -7,8 +7,8 @@ package dev.isxander.controlify.mixins.feature.bind; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.controller.input.InputComponent; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controller.input.InputComponent; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -24,7 +24,7 @@ public class GuiMixin { @Inject(method = "setScreen", at = @At("HEAD")) private void notifyBindGuiOutputOfScreenChange(CallbackInfo ci) { - ControlifyApi.get().getCurrentController().flatMap(ControllerEntity::input) + ControlifyApi.get().getCurrentController().flatMap(ControllerImpl::input) .ifPresent(InputComponent::notifyGuiPressOutputsOfNavigate); } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/bind/HudMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/bind/HudMixin.java index 91ea5f781..a23256fac 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/bind/HudMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/bind/HudMixin.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.bind; -import dev.isxander.controlify.gui.screen.RadialMenuScreen; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.gui.screen.RadialMenuScreen; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/bind/KeyMappingMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/bind/KeyMappingMixin.java index 19fb2e0ef..a976dd5c7 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/bind/KeyMappingMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/bind/KeyMappingMixin.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.bind; -import dev.isxander.controlify.bindings.KeyMappingHandle; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.impl.bindings.KeyMappingHandle; +import dev.isxander.controlify.impl.controller.ControllerImpl; import net.minecraft.client.KeyMapping; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -35,7 +35,7 @@ public class KeyMappingMixin implements KeyMappingHandle { } @Override - public void controlify$addToggleCondition(ControllerEntity controller, BooleanSupplier condition) { + public void controlify$addToggleCondition(ControllerImpl controller, BooleanSupplier condition) { } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/bind/ToggleKeyMappingMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/bind/ToggleKeyMappingMixin.java index edb3d1ba8..6c18974b7 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/bind/ToggleKeyMappingMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/bind/ToggleKeyMappingMixin.java @@ -7,9 +7,9 @@ package dev.isxander.controlify.mixins.feature.bind; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.bindings.KeyMappingHandle; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.bindings.KeyMappingHandle; +import dev.isxander.controlify.impl.controller.ControllerImpl; import net.minecraft.client.ToggleKeyMapping; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; @@ -40,7 +40,7 @@ private boolean modifyToggleMode(boolean vanillaToggleMode) { } @Override - public void controlify$addToggleCondition(ControllerEntity controller, BooleanSupplier condition) { + public void controlify$addToggleCondition(ControllerImpl controller, BooleanSupplier condition) { BooleanSupplier oldCondition = controlifyToggleSupplier; controlifyToggleSupplier = () -> { boolean thisToggle = condition.getAsBoolean() diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/font/KeybindContentsMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/font/KeybindContentsMixin.java index 2463d8917..401c4f9e8 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/font/KeybindContentsMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/font/KeybindContentsMixin.java @@ -9,9 +9,9 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; -import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.font.BindingFontHelper; +import dev.isxander.controlify.impl.font.BindingFontHelper; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Style; import net.minecraft.network.chat.contents.KeybindContents; @@ -40,7 +40,7 @@ private Component testVisitWithStyle(KeybindContents instance, Operation c.input().isPresent()) .map(c -> Controlify.instance().inputFontMapper() .getComponentFromBinding( - c.info().type().namespace(), + c.info().type().id(), c.input().get().getBinding(Identifier.tryParse(this.name)) ) ); diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/font/TranslatableContentsMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/font/TranslatableContentsMixin.java index 7822079a4..7661cdd8c 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/font/TranslatableContentsMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/font/TranslatableContentsMixin.java @@ -8,7 +8,7 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.font.BindingFontHelper; +import dev.isxander.controlify.impl.font.BindingFontHelper; import net.minecraft.network.chat.contents.TranslatableContents; import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/ClientPacketListenerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/ClientPacketListenerMixin.java index 1e1c1768f..2fa398a92 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/ClientPacketListenerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/ClientPacketListenerMixin.java @@ -8,8 +8,8 @@ import com.llamalad7.mixinextras.expression.Definition; import com.llamalad7.mixinextras.expression.Expression; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.gui.guide.InGameButtonGuide; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.gui.guide.InGameButtonGuide; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.network.protocol.game.ClientboundLoginPacket; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/HudMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/HudMixin.java index a347853ee..42c30980c 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/HudMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/HudMixin.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.guide.ingame; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.gui.guide.InGameButtonGuide; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.gui.guide.InGameButtonGuide; import net.minecraft.client.Minecraft; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/AbstractButtonMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/AbstractButtonMixin.java index 28ad2d86e..42dfe6e79 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/AbstractButtonMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/AbstractButtonMixin.java @@ -7,8 +7,8 @@ package dev.isxander.controlify.mixins.feature.guide.screen; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.gui.ButtonGuideRenderer; +import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.impl.gui.ButtonGuideRenderer; import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; import net.minecraft.client.gui.components.AbstractButton; import net.minecraft.network.chat.Component; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/TabNavigationBarMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/TabNavigationBarMixin.java index 707da705e..1c15a76f9 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/TabNavigationBarMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/TabNavigationBarMixin.java @@ -7,9 +7,9 @@ package dev.isxander.controlify.mixins.feature.guide.screen; import com.google.common.collect.ImmutableList; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphicsExtractor; @@ -43,7 +43,7 @@ private void renderControllerButtonOverlay(GuiGraphicsExtractor graphics, int mo } } - @Unique private void renderControllerButtonOverlay(GuiGraphicsExtractor graphics, ControllerEntity controller) { + @Unique private void renderControllerButtonOverlay(GuiGraphicsExtractor graphics, ControllerImpl controller) { if (tabButtons.size() <= 1) return; // no controls necessary TabButton firstTab = tabButtons.get(0); diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/input/MinecraftMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/input/MinecraftMixin.java index 27423f9f5..ab72ea92f 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/input/MinecraftMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/input/MinecraftMixin.java @@ -7,7 +7,7 @@ package dev.isxander.controlify.mixins.feature.input; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.ingame.PickBlockAccessor; +import dev.isxander.controlify.impl.ingame.PickBlockAccessor; import net.minecraft.client.Minecraft; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/oofinput/MinecraftMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/oofinput/MinecraftMixin.java index 8ce668950..f2bc51be6 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/oofinput/MinecraftMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/oofinput/MinecraftMixin.java @@ -7,7 +7,7 @@ package dev.isxander.controlify.mixins.feature.oofinput; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.impl.Controlify; import net.minecraft.client.Minecraft; import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/patches/analogueboat/AbstractBoatMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/patches/analogueboat/AbstractBoatMixin.java index a26853db1..155fb123f 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/patches/analogueboat/AbstractBoatMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/patches/analogueboat/AbstractBoatMixin.java @@ -7,7 +7,7 @@ package dev.isxander.controlify.mixins.feature.patches.analogueboat; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.fixes.boatfix.AnalogBoatInput; +import dev.isxander.controlify.impl.boatfix.AnalogBoatInput; import net.minecraft.world.entity.vehicle.boat.AbstractBoat; import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/patches/analogueboat/LocalPlayerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/patches/analogueboat/LocalPlayerMixin.java index 6bfa0dcd5..bfdcf8fe8 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/patches/analogueboat/LocalPlayerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/patches/analogueboat/LocalPlayerMixin.java @@ -8,11 +8,11 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.fixes.boatfix.AnalogBoatInput; -import dev.isxander.controlify.ingame.InGameInputHandler; -import dev.isxander.controlify.utils.MthExt; +import dev.isxander.controlify.impl.boatfix.AnalogBoatInput; +import dev.isxander.controlify.impl.ingame.InGameInputHandler; +import dev.isxander.controlify.impl.utils.MthExt; import net.minecraft.client.player.ClientInput; import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.phys.Vec2; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/ClientPacketListenerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/ClientPacketListenerMixin.java index 8ba47c34a..696b7768a 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/ClientPacketListenerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/ClientPacketListenerMixin.java @@ -10,9 +10,9 @@ import com.llamalad7.mixinextras.expression.Expression; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.api.bind.InputBinding; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.font.BindingFontHelper; +import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.font.BindingFontHelper; import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.network.chat.Component; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/MovementTutorialStepInstanceMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/MovementTutorialStepInstanceMixin.java index 073025ab0..120c24a14 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/MovementTutorialStepInstanceMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/MovementTutorialStepInstanceMixin.java @@ -9,8 +9,8 @@ import com.llamalad7.mixinextras.expression.Definition; import com.llamalad7.mixinextras.expression.Expression; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.font.BindingFontHelper; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.font.BindingFontHelper; import net.minecraft.client.tutorial.MovementTutorialStepInstance; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/OpenInventoryTutorialStepMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/OpenInventoryTutorialStepMixin.java index 3a7455452..64d382f0f 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/OpenInventoryTutorialStepMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/OpenInventoryTutorialStepMixin.java @@ -9,8 +9,8 @@ import com.llamalad7.mixinextras.expression.Definition; import com.llamalad7.mixinextras.expression.Expression; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.font.BindingFontHelper; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.font.BindingFontHelper; import net.minecraft.client.tutorial.OpenInventoryTutorialStep; import net.minecraft.network.chat.Component; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/PunchTreeTutorialStepInstanceMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/PunchTreeTutorialStepInstanceMixin.java index a1d01749a..0984119e3 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/PunchTreeTutorialStepInstanceMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/PunchTreeTutorialStepInstanceMixin.java @@ -9,8 +9,8 @@ import com.llamalad7.mixinextras.expression.Definition; import com.llamalad7.mixinextras.expression.Expression; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.font.BindingFontHelper; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.font.BindingFontHelper; import net.minecraft.client.tutorial.PunchTreeTutorialStepInstance; import net.minecraft.network.chat.Component; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/patches/sprintfix/LocalPlayerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/patches/sprintfix/LocalPlayerMixin.java index b5b5f7956..1cf5c4c6f 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/patches/sprintfix/LocalPlayerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/patches/sprintfix/LocalPlayerMixin.java @@ -11,7 +11,7 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; -import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.impl.Controlify; import net.minecraft.client.player.ClientInput; import net.minecraft.client.player.LocalPlayer; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/reacharound/MinecraftMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/reacharound/MinecraftMixin.java index a4eb82d50..27335390c 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/reacharound/MinecraftMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/reacharound/MinecraftMixin.java @@ -7,7 +7,7 @@ package dev.isxander.controlify.mixins.feature.reacharound; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.reacharound.ReachAroundHandler; +import dev.isxander.controlify.impl.reacharound.ReachAroundHandler; import net.minecraft.client.Minecraft; import net.minecraft.world.entity.Entity; import net.minecraft.world.phys.HitResult; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/MinecraftMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/MinecraftMixin.java index 9e5bbe5e2..be4f096b9 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/MinecraftMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/MinecraftMixin.java @@ -7,7 +7,7 @@ package dev.isxander.controlify.mixins.feature.rumble; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.impl.controller.ControllerImpl; import net.minecraft.client.Minecraft; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -19,7 +19,7 @@ public class MinecraftMixin { @Inject(method = "disconnect(Lnet/minecraft/client/gui/screens/Screen;Z)V", at = @At("HEAD")) private void clearRumbleEffects(CallbackInfo ci) { ControlifyApi.get().getCurrentController() - .flatMap(ControllerEntity::rumble) + .flatMap(ControllerImpl::rumble) .ifPresent(controller -> controller.rumbleManager().clearEffects()); } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/blockbreak/MultiPlayerGameModeMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/blockbreak/MultiPlayerGameModeMixin.java index 04c2f5082..f229dadde 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/blockbreak/MultiPlayerGameModeMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/blockbreak/MultiPlayerGameModeMixin.java @@ -9,10 +9,11 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.sugar.Local; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.rumble.ContinuousRumbleEffect; -import dev.isxander.controlify.rumble.RumbleSource; -import dev.isxander.controlify.rumble.RumbleState; -import dev.isxander.controlify.utils.Easings; +import dev.isxander.controlify.api.controller.rumble.RumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleEffectInstance; +import dev.isxander.controlify.api.controller.rumble.RumbleState; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.Easings; import net.minecraft.client.multiplayer.MultiPlayerGameMode; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -25,9 +26,12 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.concurrent.atomic.AtomicBoolean; + @Mixin(MultiPlayerGameMode.class) public class MultiPlayerGameModeMixin { - @Unique private ContinuousRumbleEffect blockBreakRumble = null; + @Unique private RumbleEffectInstance blockBreakRumble = null; + @Unique private AtomicBoolean blockBreakStopped; @Inject( method = "lambda$startDestroyBlock$1", @@ -74,21 +78,22 @@ private boolean onAbortBreakingBlock(boolean original) { @Unique private void startRumble(BlockState state) { stopRumble(); - var effect = ContinuousRumbleEffect.builder() + var stopped = new AtomicBoolean(); + blockBreakStopped = stopped; + var effect = RumbleEffect.continuous() .byTick(tick -> new RumbleState( 0.02f + (float)Easings.easeInQuad(Math.min(1, state.getBlock().defaultDestroyTime() / 20f)) * 0.25f, 0.01f )) .minTime(1) - .build(); + .stopCondition(stopped::get); - blockBreakRumble = effect; - ControlifyApi.get().playRumbleEffect(RumbleSource.INTERACTION, effect); + blockBreakRumble = ControlifyApi.get().playRumbleEffect(CUtil.rl("block_break"), effect).orElse(null); } @Unique private void stopRumble() { if (blockBreakRumble != null) { - blockBreakRumble.stop(); + blockBreakStopped.set(true); blockBreakRumble = null; } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/damage/ClientPacketListenerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/damage/ClientPacketListenerMixin.java index f8257a718..5370886f1 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/damage/ClientPacketListenerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/damage/ClientPacketListenerMixin.java @@ -7,8 +7,8 @@ package dev.isxander.controlify.mixins.feature.rumble.damage; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.rumble.BasicRumbleEffect; -import dev.isxander.controlify.rumble.RumbleSource; +import dev.isxander.controlify.api.controller.rumble.RumbleEffect; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.client.player.LocalPlayer; @@ -56,18 +56,16 @@ private void onHurtAnimation(ClientboundHurtAnimationPacket packet, CallbackInfo @Unique private void playIncomingDamageRumble() { ControlifyApi.get().playRumbleEffect( - RumbleSource.PLAYER, - BasicRumbleEffect.constant(0.8f, 0.5f, 5) + CUtil.rl("incoming_damage"), + RumbleEffect.finite().constant(0.8f, 0.5f, 5) ); } @Unique private void playOutgoingDamageRumble() { ControlifyApi.get().playRumbleEffect( - RumbleSource.INTERACTION, - BasicRumbleEffect.seq( - BasicRumbleEffect.constant(0.18f, 0.4f, 1), - BasicRumbleEffect.constant(0.06f, 0.16f, 1) - ) + CUtil.rl("outgoing_damage"), + RumbleEffect.finite().constant(0.18f, 0.4f, 1) + .then(RumbleEffect.finite().constant(0.06f, 0.16f, 1)) ); } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/explosion/ClientPacketListenerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/explosion/ClientPacketListenerMixin.java index a6c5b011f..bbfb5b460 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/explosion/ClientPacketListenerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/explosion/ClientPacketListenerMixin.java @@ -7,9 +7,9 @@ package dev.isxander.controlify.mixins.feature.rumble.explosion; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.rumble.BasicRumbleEffect; -import dev.isxander.controlify.rumble.RumbleSource; -import dev.isxander.controlify.rumble.RumbleState; +import dev.isxander.controlify.api.controller.rumble.RumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleState; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.network.protocol.game.ClientboundExplodePacket; @@ -32,18 +32,16 @@ private void onClientExplosion(ClientboundExplodePacket packet, CallbackInfo ci) } ControlifyApi.get().playRumbleEffect( - RumbleSource.WORLD, - BasicRumbleEffect.seq( - BasicRumbleEffect.constant(magnitude, magnitude, 2), // initial boom - BasicRumbleEffect.byTime(t -> { + CUtil.rl("explosion"), + RumbleEffect.finite().constant(magnitude, magnitude, 2) // initial boom + .then(RumbleEffect.finite().byTime(t -> { float decay = 1f - t; decay *= decay; return new RumbleState( magnitude * 0.8f * decay, magnitude * 0.2f * decay ); - }, 8) // low-frequency aftershock - ) + }, 8)) // low-frequency aftershock ); } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/explosion/LightningBoltMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/explosion/LightningBoltMixin.java index a9aa814bb..bad9fb55e 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/explosion/LightningBoltMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/explosion/LightningBoltMixin.java @@ -8,9 +8,9 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.rumble.BasicRumbleEffect; -import dev.isxander.controlify.rumble.RumbleSource; -import dev.isxander.controlify.rumble.RumbleState; +import dev.isxander.controlify.api.controller.rumble.RumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleState; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.world.entity.LightningBolt; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -21,11 +21,9 @@ public class LightningBoltMixin { private boolean onLightningStrike(boolean client) { if (client) { ControlifyApi.get().playRumbleEffect( - RumbleSource.WORLD, - BasicRumbleEffect.seq( - BasicRumbleEffect.constant(1f, 0.2f, 6), // initial boom - BasicRumbleEffect.byTime(t -> new RumbleState(0f, 1 - t*0.2f), 10) // explosion - ) + CUtil.rl("lightning"), + RumbleEffect.finite().constant(1f, 0.2f, 6) // initial boom + .then(RumbleEffect.finite().byTime(t -> new RumbleState(0f, 1 - t*0.2f), 10)) // explosion ); } return client; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/fishing/FishingHookMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/fishing/FishingHookMixin.java index 5eb4de535..2be9370f5 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/fishing/FishingHookMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/fishing/FishingHookMixin.java @@ -8,8 +8,9 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.rumble.ContinuousRumbleEffect; -import dev.isxander.controlify.rumble.RumbleSource; +import dev.isxander.controlify.api.controller.rumble.RumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleEffectInstance; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.projectile.FishingHook; @@ -26,18 +27,17 @@ public class FishingHookMixin { @Shadow private boolean biting; @Unique private boolean isLocalPlayerHook; - @Unique private ContinuousRumbleEffect bitingRumble; + @Unique private RumbleEffectInstance bitingRumble; @ModifyExpressionValue(method = "onSyncedDataUpdated", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/syncher/SynchedEntityData;get(Lnet/minecraft/network/syncher/EntityDataAccessor;)Ljava/lang/Object;", ordinal = 1)) private Object onBitingStateUpdated(Object bitingObj) { var biting = (boolean) bitingObj; if (isLocalPlayerHook) { if (biting && !this.biting) { - this.bitingRumble = ContinuousRumbleEffect.builder() - .constant(0f, 0.05f) - .build(); + var effect = RumbleEffect.continuous() + .constant(0f, 0.05f); - ControlifyApi.get().playRumbleEffect(RumbleSource.INTERACTION, this.bitingRumble); + this.bitingRumble = ControlifyApi.get().playRumbleEffect(CUtil.rl("fishing_bite"), effect).orElse(null); } else if (!biting && this.biting) { stopBitingRumble(); } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/itembreak/LocalPlayerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/itembreak/LocalPlayerMixin.java index dc44ed737..fcd5d365c 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/itembreak/LocalPlayerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/itembreak/LocalPlayerMixin.java @@ -7,9 +7,9 @@ package dev.isxander.controlify.mixins.feature.rumble.itembreak; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.rumble.BasicRumbleEffect; -import dev.isxander.controlify.rumble.RumbleSource; -import dev.isxander.controlify.rumble.RumbleState; +import dev.isxander.controlify.api.controller.rumble.RumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleState; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; @@ -20,8 +20,8 @@ public class LocalPlayerMixin extends LivingEntityMixin { @Override protected void onBreakItemParticles(ItemStack stack, CallbackInfo ci) { ControlifyApi.get().playRumbleEffect( - RumbleSource.PLAYER, - BasicRumbleEffect.byTick(tick -> new RumbleState(tick <= 4 ? 1f : 0f, 1f), 10) + CUtil.rl("item_break"), + RumbleEffect.finite().byTick(tick -> new RumbleState(tick <= 4 ? 1f : 0f, 1f), 10) ); } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/levelevents/LevelEventHandlerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/levelevents/LevelEventHandlerMixin.java index e2f6a2f75..828fea31f 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/levelevents/LevelEventHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/levelevents/LevelEventHandlerMixin.java @@ -7,10 +7,10 @@ package dev.isxander.controlify.mixins.feature.rumble.levelevents; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.rumble.BasicRumbleEffect; -import dev.isxander.controlify.rumble.RumbleSource; -import dev.isxander.controlify.rumble.RumbleState; -import dev.isxander.controlify.utils.Easings; +import dev.isxander.controlify.api.controller.rumble.RumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleState; +import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.Easings; import net.minecraft.client.renderer.*; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.LevelEvent; @@ -25,11 +25,9 @@ public class LevelEventHandlerMixin { private void onLevelEvent(int eventId, BlockPos pos, int data, CallbackInfo ci) { switch (eventId) { case LevelEvent.SOUND_ANVIL_USED -> ControlifyApi.get().playRumbleEffect( - RumbleSource.GUI, - BasicRumbleEffect.seq( - BasicRumbleEffect.constant(1f, 0.5f, 2), - BasicRumbleEffect.empty(5) - ).repeat(3) + CUtil.rl("anvil"), + RumbleEffect.finite().constant(1f, 0.5f, 2) + .then(RumbleEffect.finite().constant(0f, 0f, 5)).repeat(3) ); } } @@ -38,25 +36,21 @@ private void onLevelEvent(int eventId, BlockPos pos, int data, CallbackInfo ci) private void onGlobalLevelEvent(int eventId, BlockPos pos, int data, CallbackInfo ci) { switch (eventId) { case LevelEvent.SOUND_DRAGON_DEATH -> ControlifyApi.get().playRumbleEffect( - RumbleSource.WORLD, - BasicRumbleEffect.seq( - BasicRumbleEffect.constant(1f, 1f, 194), - BasicRumbleEffect.byTime(t -> { + CUtil.rl("dragon_death"), + RumbleEffect.finite().constant(1f, 1f, 194) + .then(RumbleEffect.finite().byTime(t -> { float easeOutQuad = (float) Easings.easeOutQuad(t); return new RumbleState(1 - easeOutQuad, 1 - easeOutQuad); - }, 63) - ).prioritised(10) + }, 63)) ); case LevelEvent.SOUND_WITHER_BOSS_SPAWN -> ControlifyApi.get().playRumbleEffect( - RumbleSource.WORLD, - BasicRumbleEffect.seq( - BasicRumbleEffect.constant(1f, 1f, 9), - BasicRumbleEffect.constant(0.1f, 1f, 14), - BasicRumbleEffect.byTime(t -> { + CUtil.rl("wither_spawn"), + RumbleEffect.finite().constant(1f, 1f, 9) + .then(RumbleEffect.finite().constant(0.1f, 1f, 14)) + .then(RumbleEffect.finite().byTime(t -> { float easeOutQuad = 1 - (1 - t) * (1 - t); return new RumbleState(0f, 1 - easeOutQuad); - }, 56) - ).prioritised(10) + }, 56)) ); } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/slowblock/LocalPlayerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/slowblock/LocalPlayerMixin.java index cb290e01d..76660ecc5 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/slowblock/LocalPlayerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/slowblock/LocalPlayerMixin.java @@ -8,9 +8,10 @@ import com.mojang.authlib.GameProfile; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.rumble.ContinuousRumbleEffect; -import dev.isxander.controlify.rumble.RumbleSource; -import dev.isxander.controlify.rumble.RumbleState; +import dev.isxander.controlify.api.controller.rumble.RumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleEffectInstance; +import dev.isxander.controlify.api.controller.rumble.RumbleState; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.player.AbstractClientPlayer; import net.minecraft.client.player.ClientInput; @@ -29,7 +30,7 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer { @Shadow public ClientInput input; - @Unique private ContinuousRumbleEffect slowBlockRumble = null; + @Unique private RumbleEffectInstance slowBlockRumble = null; public LocalPlayerMixin(ClientLevel world, GameProfile profile) { super(world, profile); @@ -53,22 +54,21 @@ private void manageSlowBlockRumble(CallbackInfo ci) { } @Unique private void ensureRumbleStarted() { - if (slowBlockRumble == null || slowBlockRumble.isFinished()) { - slowBlockRumble = ContinuousRumbleEffect.builder() + if (slowBlockRumble == null || slowBlockRumble.hasFinished()) { + var effect = RumbleEffect.continuous() .byTick(i -> { float movementAmount = input.getMoveVector().length(); return new RumbleState(0.3f * movementAmount, 0.5f * movementAmount); }) - .timeout(100) - .build(); - ControlifyApi.get().playRumbleEffect(RumbleSource.PLAYER, slowBlockRumble); + .timeout(100); + slowBlockRumble = ControlifyApi.get().playRumbleEffect(CUtil.rl("slow_block"), effect).orElse(null); } else { - slowBlockRumble.heartbeat(); + slowBlockRumble.keepAlive(); } } @Unique private void ensureRumbleStopped() { - if (slowBlockRumble != null && !slowBlockRumble.isFinished()) { + if (slowBlockRumble != null && !slowBlockRumble.hasFinished()) { slowBlockRumble.stop(); } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/useitem/ClientPacketListenerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/useitem/ClientPacketListenerMixin.java index 16b1821d4..1ece139df 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/useitem/ClientPacketListenerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/useitem/ClientPacketListenerMixin.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.rumble.useitem; -import dev.isxander.controlify.rumble.ContinuousRumbleEffect; -import dev.isxander.controlify.rumble.effects.UseItemEffectHolder; +import dev.isxander.controlify.api.controller.rumble.RumbleEffectInstance; +import dev.isxander.controlify.impl.controller.rumble.effects.UseItemEffectHolder; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.network.protocol.game.ClientboundRespawnPacket; @@ -21,7 +21,7 @@ public class ClientPacketListenerMixin { @Inject(method = "handleRespawn", at = @At(value = "FIELD", target = "Lnet/minecraft/client/Minecraft;player:Lnet/minecraft/client/player/LocalPlayer;", opcode = Opcodes.PUTFIELD)) private void clearUseItemRumble(ClientboundRespawnPacket packet, CallbackInfo ci) { - ContinuousRumbleEffect effect = ((UseItemEffectHolder) Minecraft.getInstance().player).controlify$getUseItemEffect(); + RumbleEffectInstance effect = ((UseItemEffectHolder) Minecraft.getInstance().player).controlify$getUseItemEffect(); if (effect != null) effect.stop(); } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/useitem/LocalPlayerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/useitem/LocalPlayerMixin.java index ab93c1261..691227213 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/useitem/LocalPlayerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/useitem/LocalPlayerMixin.java @@ -7,8 +7,11 @@ package dev.isxander.controlify.mixins.feature.rumble.useitem; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.rumble.*; -import dev.isxander.controlify.rumble.effects.UseItemEffectHolder; +import dev.isxander.controlify.api.controller.rumble.RumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleEffectInstance; +import dev.isxander.controlify.api.controller.rumble.RumbleState; +import dev.isxander.controlify.impl.controller.rumble.effects.UseItemEffectHolder; +import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.InteractionHand; import net.minecraft.world.item.BowItem; @@ -21,48 +24,42 @@ @Mixin(LocalPlayer.class) public abstract class LocalPlayerMixin extends LivingEntityMixin implements UseItemEffectHolder { - @Unique private ContinuousRumbleEffect useItemRumble; + @Unique private RumbleEffectInstance useItemRumble; @Override protected void onStartUsingItem(InteractionHand hand, CallbackInfo ci, ItemStack stack) { switch (stack.getUseAnimation()) { - case BOW -> startRumble(ContinuousRumbleEffect.builder() + case BOW -> startRumble(RumbleEffect.continuous() .byTick(tick -> new RumbleState( tick % 7 <= 3 && tick > BowItem.MAX_DRAW_DURATION ? 0.1f : 0f, BowItem.getPowerForTime(tick) - )) - .build()); + ))); case CROSSBOW -> { int chargeDuration = CrossbowItem.getChargeDuration( stack, (LocalPlayer) (Object) this ); - startRumble(ContinuousRumbleEffect.builder() + startRumble(RumbleEffect.continuous() .byTick(tick -> new RumbleState( 0f, (float) tick / chargeDuration )) - .timeout(chargeDuration) - .build()); + .timeout(chargeDuration)); } - case BLOCK, SPYGLASS -> startRumble(ContinuousRumbleEffect.builder() + case BLOCK, SPYGLASS -> startRumble(RumbleEffect.continuous() .byTick(tick -> new RumbleState( 0f, tick % 4 / 4f * 0.12f + 0.05f - )) - .build()); - case EAT, DRINK -> startRumble(ContinuousRumbleEffect.builder() - .constant(0.1f, 0.2f) - .build()); - case TOOT_HORN -> startRumble(ContinuousRumbleEffect.builder() + ))); + case EAT, DRINK -> startRumble(RumbleEffect.continuous() + .constant(0.1f, 0.2f)); + case TOOT_HORN -> startRumble(RumbleEffect.continuous() .byTick(tick -> new RumbleState( Math.min(1f, tick / 10f), 0.25f - )) - .build()); - case SPEAR -> startRumble(ContinuousRumbleEffect.builder() - .constant(0.3f, 0.3f) - .build()); + ))); + case SPEAR -> startRumble(RumbleEffect.continuous() + .constant(0.3f, 0.3f)); } } @@ -79,13 +76,13 @@ protected void onStopUsingItem(CallbackInfo ci) { } } - @Unique private void startRumble(ContinuousRumbleEffect effect) { - ControlifyApi.get().playRumbleEffect(RumbleSource.INTERACTION, effect); - this.useItemRumble = effect; + @Unique private void startRumble(RumbleEffect effect) { + if (useItemRumble != null) useItemRumble.stop(); + this.useItemRumble = ControlifyApi.get().playRumbleEffect(CUtil.rl("use_item"), effect).orElse(null); } @Override - public @Nullable ContinuousRumbleEffect controlify$getUseItemEffect() { + public @Nullable RumbleEffectInstance controlify$getUseItemEffect() { return useItemRumble; } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/waterland/LocalPlayerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/waterland/LocalPlayerMixin.java index dc2d5c5b9..01d3e86c6 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/waterland/LocalPlayerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/waterland/LocalPlayerMixin.java @@ -7,10 +7,11 @@ package dev.isxander.controlify.mixins.feature.rumble.waterland; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.rumble.BasicRumbleEffect; -import dev.isxander.controlify.rumble.RumbleSource; -import dev.isxander.controlify.rumble.RumbleState; +import dev.isxander.controlify.api.controller.rumble.RumbleEffect; +import dev.isxander.controlify.api.controller.rumble.RumbleState; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.utils.CUtil; +import java.util.Objects; import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; @@ -20,8 +21,6 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.util.Objects; - @Mixin(LocalPlayer.class) public abstract class LocalPlayerMixin extends PlayerMixin { protected LocalPlayerMixin(EntityType entityType, Level level) { @@ -34,7 +33,7 @@ protected void splashRumble(CallbackInfo ci) { return; } - ControlifyApi.get().getCurrentController().flatMap(ControllerEntity::rumble).ifPresent(rumble -> { + ControlifyApi.get().getCurrentController().flatMap(ControllerImpl::rumble).ifPresent(rumble -> { Entity entity = Objects.requireNonNullElse(this.getControllingPassenger(), this); float f = entity == this ? 0.2F : 0.9F; Vec3 vec3 = entity.getDeltaMovement(); @@ -42,9 +41,9 @@ protected void splashRumble(CallbackInfo ci) { if (impactForce >= 0.05f) { float multiplier = Math.min(1, impactForce / 0.5f); - rumble.rumbleManager().play( - RumbleSource.PLAYER, - BasicRumbleEffect.byTime( + rumble.playEffect( + CUtil.rl("water_land"), + RumbleEffect.finite().byTime( t -> new RumbleState(multiplier * (1 - t), multiplier * 0.5f), impactForce < 0.25f ? 10 : 20 ) diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/ContainerEventHandlerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/ContainerEventHandlerMixin.java index 261c441a6..8cfe4936a 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/ContainerEventHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/ContainerEventHandlerMixin.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.mixins.feature.screenop; -import dev.isxander.controlify.screenop.CustomFocus; +import dev.isxander.controlify.impl.screenop.CustomFocus; import net.minecraft.client.gui.components.events.ContainerEventHandler; import net.minecraft.client.gui.components.events.GuiEventListener; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/GuiMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/GuiMixin.java index 0ad891a81..73f1a0292 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/GuiMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/GuiMixin.java @@ -8,8 +8,8 @@ import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; import com.llamalad7.mixinextras.sugar.Local; -import dev.isxander.controlify.screenop.ComponentProcessorProvider; -import dev.isxander.controlify.screenop.keyboard.KeyboardOverlayScreen; +import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.impl.screenop.keyboard.KeyboardOverlayScreen; import net.minecraft.client.gui.screens.Screen; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/ScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/ScreenMixin.java index 48bc4eb46..2a9ebf951 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/ScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/ScreenMixin.java @@ -7,10 +7,10 @@ package dev.isxander.controlify.mixins.feature.screenop; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.screenop.ScreenProcessorFactory; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.screenop.ScreenProcessorFactory; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; import net.minecraft.client.gui.screens.Screen; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/bundle/BundleMouseActionsMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/bundle/BundleMouseActionsMixin.java index a9a7f4038..f5d6f1967 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/bundle/BundleMouseActionsMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/bundle/BundleMouseActionsMixin.java @@ -6,9 +6,9 @@ */ package dev.isxander.controlify.mixins.feature.screenop.impl.bundle; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.compat.vanilla.BundleItemSlotControllerAction; -import dev.isxander.controlify.screenop.compat.vanilla.ItemSlotControllerAction; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.compat.vanilla.BundleItemSlotControllerAction; +import dev.isxander.controlify.impl.screenop.compat.vanilla.ItemSlotControllerAction; import net.minecraft.client.gui.BundleMouseActions; import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; @@ -20,7 +20,7 @@ public abstract class BundleMouseActionsMixin implements ItemSlotControllerActio protected abstract void toggleSelectedBundleItem(ItemStack bundleItem, int slotIndex, int selectedItem); @Override - public boolean controlify$onControllerInput(ItemStack stack, int hoveredSlotIndex, ControllerEntity controller) { + public boolean controlify$onControllerInput(ItemStack stack, int hoveredSlotIndex, ControllerImpl controller) { return BundleItemSlotControllerAction.onControllerInput(stack, hoveredSlotIndex, controller, this::toggleSelectedBundleItem); } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/chat/ChatComponentMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/chat/ChatComponentMixin.java index 14f3a0e88..9a1da99be 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/chat/ChatComponentMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/chat/ChatComponentMixin.java @@ -9,8 +9,8 @@ import com.llamalad7.mixinextras.expression.Definition; import com.llamalad7.mixinextras.expression.Expression; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.screenop.keyboard.ChatKeyboardDucky; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.screenop.keyboard.ChatKeyboardDucky; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.client.gui.components.ChatComponent; import net.minecraft.client.gui.screens.ChatScreen; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/chat/ChatScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/chat/ChatScreenMixin.java index 6e5637684..4e257c61c 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/chat/ChatScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/chat/ChatScreenMixin.java @@ -11,9 +11,9 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.mojang.blaze3d.platform.InputConstants; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.ChatScreenProcessor; -import dev.isxander.controlify.screenop.keyboard.*; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.ChatScreenProcessor; +import dev.isxander.controlify.impl.screenop.keyboard.*; import net.minecraft.client.gui.components.CommandSuggestions; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.components.events.GuiEventListener; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/chat/CommandSuggestionsMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/chat/CommandSuggestionsMixin.java index 77189c7e5..4963ec618 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/chat/CommandSuggestionsMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/chat/CommandSuggestionsMixin.java @@ -7,9 +7,9 @@ package dev.isxander.controlify.mixins.feature.screenop.impl.chat; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.screenop.keyboard.ChatKeyboardDucky; -import dev.isxander.controlify.screenop.compat.vanilla.ChatScreenProcessor; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.screenop.keyboard.ChatKeyboardDucky; +import dev.isxander.controlify.impl.screenop.compat.vanilla.ChatScreenProcessor; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.CommandSuggestions; import net.minecraft.client.gui.screens.ChatScreen; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/container/AbstractContainerScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/container/AbstractContainerScreenMixin.java index b90198bfd..bf0e10033 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/container/AbstractContainerScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/container/AbstractContainerScreenMixin.java @@ -9,12 +9,12 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.sugar.Share; import com.llamalad7.mixinextras.sugar.ref.LocalRef; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.AbstractContainerScreenProcessor; -import dev.isxander.controlify.screenop.compat.vanilla.ItemSlotControllerAction; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.AbstractContainerScreenProcessor; +import dev.isxander.controlify.impl.screenop.compat.vanilla.ItemSlotControllerAction; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.ItemSlotMouseAction; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; @@ -74,7 +74,7 @@ private void triggerSlotHovered(GuiGraphicsExtractor graphics, int mouseX, int m } } - @Unique protected boolean handleControllerItemSlotActions(ControllerEntity controller) { + @Unique protected boolean handleControllerItemSlotActions(ControllerImpl controller) { for (ItemSlotMouseAction itemSlotMouseAction : this.itemSlotMouseActions) { if (itemSlotMouseAction instanceof ItemSlotControllerAction itemSlotControllerAction && itemSlotMouseAction.matches(this.hoveredSlot) diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/container/AbstractRecipeBookScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/container/AbstractRecipeBookScreenMixin.java index f8fcdadfa..1a8e89175 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/container/AbstractRecipeBookScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/container/AbstractRecipeBookScreenMixin.java @@ -6,9 +6,9 @@ */ package dev.isxander.controlify.mixins.feature.screenop.impl.container; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.RecipeBookScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.RecipeBookScreenProcessor; import net.minecraft.client.gui.screens.inventory.*; import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent; import net.minecraft.client.gui.screens.recipebook.RecipeUpdateListener; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/container/CreativeModeInventoryScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/container/CreativeModeInventoryScreenMixin.java index 5e1925e3d..16d00a8ee 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/container/CreativeModeInventoryScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/container/CreativeModeInventoryScreenMixin.java @@ -6,12 +6,12 @@ */ package dev.isxander.controlify.mixins.feature.screenop.impl.container; -import dev.isxander.controlify.screenop.ComponentProcessorProvider; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.CreativeModeInventoryScreenProcessor; -import dev.isxander.controlify.screenop.compat.vanilla.EditBoxComponentProcessor; -import dev.isxander.controlify.screenop.keyboard.InputTarget; +import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.CreativeModeInventoryScreenProcessor; +import dev.isxander.controlify.impl.screenop.compat.vanilla.EditBoxComponentProcessor; +import dev.isxander.controlify.impl.screenop.keyboard.InputTarget; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/AbstractButtonMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/AbstractButtonMixin.java index 146ecfb6e..3eee8f6b9 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/AbstractButtonMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/AbstractButtonMixin.java @@ -6,9 +6,9 @@ */ package dev.isxander.controlify.mixins.feature.screenop.impl.elements; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ComponentProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.AbstractButtonComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.AbstractButtonComponentProcessor; import net.minecraft.client.gui.components.AbstractButton; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/AbstractContainerEventHandlerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/AbstractContainerEventHandlerMixin.java index 58d629604..7b0010289 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/AbstractContainerEventHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/AbstractContainerEventHandlerMixin.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.mixins.feature.screenop.impl.elements; -import dev.isxander.controlify.screenop.CustomFocus; +import dev.isxander.controlify.impl.screenop.CustomFocus; import net.minecraft.client.gui.components.events.AbstractContainerEventHandler; import net.minecraft.client.gui.components.events.GuiEventListener; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/AbstractSliderButtonMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/AbstractSliderButtonMixin.java index 9404aa0c5..c9972879a 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/AbstractSliderButtonMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/AbstractSliderButtonMixin.java @@ -7,10 +7,10 @@ package dev.isxander.controlify.mixins.feature.screenop.impl.elements; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ComponentProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.SliderComponentProcessor; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.SliderComponentProcessor; import net.minecraft.client.InputType; import net.minecraft.client.gui.components.AbstractSliderButton; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/AbstractWidgetMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/AbstractWidgetMixin.java index 2c3503a84..09abcf156 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/AbstractWidgetMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/AbstractWidgetMixin.java @@ -7,10 +7,10 @@ package dev.isxander.controlify.mixins.feature.screenop.impl.elements; import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.controller.ControllerEntity; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.input.MouseButtonEvent; import org.spongepowered.asm.mixin.Mixin; @@ -29,7 +29,7 @@ private boolean openKeyboardInVmouseMode(AbstractWidget instance, MouseButtonEve assert MinecraftUtil.getScreen() != null; Controlify controlify = Controlify.instance(); - ControllerEntity controller = controlify.getCurrentController().orElse(null); + ControllerImpl controller = controlify.getCurrentController().orElse(null); if (controller != null && controlify.virtualMouseHandler().isVirtualMouseEnabled()) { var screenProcessor = ScreenProcessorProvider.provide(MinecraftUtil.getScreen()); diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/ContainerObjectSelectionListEntryMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/ContainerObjectSelectionListEntryMixin.java index 21de78b44..fcc42c018 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/ContainerObjectSelectionListEntryMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/ContainerObjectSelectionListEntryMixin.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.mixins.feature.screenop.impl.elements; -import dev.isxander.controlify.screenop.CustomFocus; +import dev.isxander.controlify.impl.screenop.CustomFocus; import net.minecraft.client.gui.components.ContainerObjectSelectionList; import net.minecraft.client.gui.components.events.GuiEventListener; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/EditBoxMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/EditBoxMixin.java index 47c767f6c..8a8d90a1a 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/EditBoxMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/EditBoxMixin.java @@ -11,14 +11,14 @@ import com.llamalad7.mixinextras.sugar.Share; import com.llamalad7.mixinextras.sugar.ref.LocalBooleanRef; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ComponentProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.EditBoxComponentProcessor; -import dev.isxander.controlify.screenop.keyboard.CommonKeyboardHints; -import dev.isxander.controlify.screenop.keyboard.ComponentKeyboardBehaviour; -import dev.isxander.controlify.screenop.keyboard.KeyboardOverlayScreen; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.EditBoxComponentProcessor; +import dev.isxander.controlify.impl.screenop.keyboard.CommonKeyboardHints; +import dev.isxander.controlify.impl.screenop.keyboard.ComponentKeyboardBehaviour; +import dev.isxander.controlify.impl.screenop.keyboard.KeyboardOverlayScreen; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphicsExtractor; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/CreateWorldScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/CreateWorldScreenMixin.java index 3e437fc10..7e08af07b 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/CreateWorldScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/CreateWorldScreenMixin.java @@ -7,12 +7,12 @@ package dev.isxander.controlify.mixins.feature.screenop.impl.outofgame; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.api.buttonguide.ButtonGuideApi; -import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.CreateWorldScreenProcessor; +import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.CreateWorldScreenProcessor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/DirectJoinServerScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/DirectJoinServerScreenMixin.java index 4db72e6fa..80a8889ba 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/DirectJoinServerScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/DirectJoinServerScreenMixin.java @@ -9,9 +9,9 @@ import com.llamalad7.mixinextras.expression.Definition; import com.llamalad7.mixinextras.expression.Expression; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.AddServerLikeScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.AddServerLikeScreenProcessor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.DirectJoinServerScreen; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/JoinMultiplayerScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/JoinMultiplayerScreenMixin.java index af33f0054..39e5bab56 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/JoinMultiplayerScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/JoinMultiplayerScreenMixin.java @@ -9,9 +9,9 @@ import com.llamalad7.mixinextras.expression.Definition; import com.llamalad7.mixinextras.expression.Expression; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.JoinMultiplayerScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.JoinMultiplayerScreenProcessor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen; import net.minecraft.client.gui.screens.multiplayer.ServerSelectionList; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/LanguageSelectionListEntryMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/LanguageSelectionListEntryMixin.java index 22b00f5c8..9e8466a67 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/LanguageSelectionListEntryMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/LanguageSelectionListEntryMixin.java @@ -6,9 +6,9 @@ */ package dev.isxander.controlify.mixins.feature.screenop.impl.outofgame; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ComponentProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.LanguageSelectionListComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.LanguageSelectionListComponentProcessor; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/ManageServerScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/ManageServerScreenMixin.java index 38e8500f1..5035c7777 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/ManageServerScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/ManageServerScreenMixin.java @@ -9,9 +9,9 @@ import com.llamalad7.mixinextras.expression.Definition; import com.llamalad7.mixinextras.expression.Expression; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.AddServerLikeScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.AddServerLikeScreenProcessor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/OptionsScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/OptionsScreenMixin.java index 145ddb2e1..48488c1e1 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/OptionsScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/OptionsScreenMixin.java @@ -9,9 +9,9 @@ import com.llamalad7.mixinextras.expression.Definition; import com.llamalad7.mixinextras.expression.Expression; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.OptionsScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.OptionsScreenProcessor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.options.OptionsScreen; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/OptionsSubScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/OptionsSubScreenMixin.java index a15f368aa..699a40fe5 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/OptionsSubScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/OptionsSubScreenMixin.java @@ -9,9 +9,9 @@ import com.llamalad7.mixinextras.expression.Definition; import com.llamalad7.mixinextras.expression.Expression; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.OptionsSubScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.OptionsSubScreenProcessor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.options.OptionsSubScreen; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/PauseScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/PauseScreenMixin.java index 2a18151c5..a943897ef 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/PauseScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/PauseScreenMixin.java @@ -6,9 +6,9 @@ */ package dev.isxander.controlify.mixins.feature.screenop.impl.outofgame; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.PauseScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.PauseScreenProcessor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.PauseScreen; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/SelectWorldScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/SelectWorldScreenMixin.java index 50ecb363a..ed68fe563 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/SelectWorldScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/SelectWorldScreenMixin.java @@ -9,12 +9,12 @@ import com.llamalad7.mixinextras.expression.Definition; import com.llamalad7.mixinextras.expression.Expression; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.api.buttonguide.ButtonGuideApi; -import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.SelectWorldScreenProcessor; +import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.SelectWorldScreenProcessor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.worldselection.SelectWorldScreen; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/ServerSelectionListEntryMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/ServerSelectionListEntryMixin.java index acb1a5cc4..1d99a05c7 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/ServerSelectionListEntryMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/ServerSelectionListEntryMixin.java @@ -6,9 +6,9 @@ */ package dev.isxander.controlify.mixins.feature.screenop.impl.outofgame; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ComponentProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.ServerSelectionListEntryComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.ServerSelectionListEntryComponentProcessor; import net.minecraft.client.gui.screens.multiplayer.ServerSelectionList; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/TitleScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/TitleScreenMixin.java index 02a6caf34..2406f90d2 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/TitleScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/TitleScreenMixin.java @@ -6,9 +6,9 @@ */ package dev.isxander.controlify.mixins.feature.screenop.impl.outofgame; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.TitleScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.TitleScreenProcessor; import net.minecraft.client.gui.screens.TitleScreen; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/WorldSelectionListEntryMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/WorldSelectionListEntryMixin.java index ddd0adb43..369566173 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/WorldSelectionListEntryMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/WorldSelectionListEntryMixin.java @@ -6,9 +6,9 @@ */ package dev.isxander.controlify.mixins.feature.screenop.impl.outofgame; -import dev.isxander.controlify.screenop.ComponentProcessor; -import dev.isxander.controlify.screenop.ComponentProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.WorldListEntryComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.WorldListEntryComponentProcessor; import net.minecraft.client.gui.screens.worldselection.WorldSelectionList; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/sign/AbstractSignEditScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/sign/AbstractSignEditScreenMixin.java index c3557bf1f..7c4e87d18 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/sign/AbstractSignEditScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/sign/AbstractSignEditScreenMixin.java @@ -13,12 +13,12 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.mojang.blaze3d.platform.InputConstants; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.screenop.ScreenProcessor; -import dev.isxander.controlify.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.screenop.compat.vanilla.AbstractSignEditScreenProcessor; -import dev.isxander.controlify.screenop.keyboard.KeyboardLayouts; -import dev.isxander.controlify.screenop.keyboard.KeyboardWidget; -import dev.isxander.controlify.screenop.keyboard.MixinInputTarget; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.screenop.compat.vanilla.AbstractSignEditScreenProcessor; +import dev.isxander.controlify.impl.screenop.keyboard.KeyboardLayouts; +import dev.isxander.controlify.impl.screenop.keyboard.KeyboardWidget; +import dev.isxander.controlify.impl.screenop.keyboard.MixinInputTarget; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.events.GuiEventListener; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/settingsbutton/ControlsScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/settingsbutton/ControlsScreenMixin.java index 1e82fa3fb..209ce2ab7 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/settingsbutton/ControlsScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/settingsbutton/ControlsScreenMixin.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.settingsbutton; -import dev.isxander.controlify.gui.screen.ControlifySettingsScreen; -import dev.isxander.controlify.utils.MinecraftUtil; +import dev.isxander.controlify.impl.gui.screen.ControlifySettingsScreen; +import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.client.Options; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.options.OptionsSubScreen; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/util/ConnectScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/util/ConnectScreenMixin.java index a9c45a63a..4edf13e1b 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/util/ConnectScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/util/ConnectScreenMixin.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.mixins.feature.util; -import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.impl.Controlify; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.ConnectScreen; import net.minecraft.client.multiplayer.ServerData; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/GuiMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/GuiMixin.java index 1e2aa11d7..666d8c0ad 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/GuiMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/GuiMixin.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.virtualmouse; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.virtualmouse.VirtualMouseHandler; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.virtualmouse.VirtualMouseHandler; import net.minecraft.client.gui.screens.Screen; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; 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 474b93537..0f12d09ee 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 @@ -9,9 +9,9 @@ import com.llamalad7.mixinextras.injector.ModifyReturnValue; import com.llamalad7.mixinextras.sugar.Local; import com.mojang.blaze3d.platform.InputConstants; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.bindings.ControlifyBindings; -import dev.isxander.controlify.controller.ControllerEntity; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.controller.ControllerImpl; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -21,7 +21,7 @@ public class InputConstantsMixin { @ModifyReturnValue(method = "isKeyDown", at = @At("RETURN")) private static boolean modifyIsKeyDown(boolean keyDown, @Local(argsOnly = true, name = "key") int key) { if (key == InputConstants.KEY_LSHIFT) { - ControllerEntity controller = Controlify.instance().getCurrentController().orElse(null); + ControllerImpl controller = Controlify.instance().getCurrentController().orElse(null); if (controller == null) return keyDown; return keyDown diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/MinecraftMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/MinecraftMixin.java index 286ad0ed1..321ab4f71 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/MinecraftMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/MinecraftMixin.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.virtualmouse; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.virtualmouse.VirtualMouseHandler; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.virtualmouse.VirtualMouseHandler; import net.minecraft.client.Minecraft; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; 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 3104db56b..a3ff78cc9 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 @@ -8,8 +8,8 @@ import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; import com.mojang.blaze3d.platform.Window; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.InputMode; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.api.InputMode; import net.minecraft.client.MouseHandler; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/AbstractContainerScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/AbstractContainerScreenMixin.java index 1b6a9dde1..488da187f 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/AbstractContainerScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/AbstractContainerScreenMixin.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.virtualmouse.snapping; -import dev.isxander.controlify.api.vmousesnapping.ISnapBehaviour; -import dev.isxander.controlify.api.vmousesnapping.SnapPoint; +import dev.isxander.controlify.impl.api.vmousesnapping.ISnapBehaviour; +import dev.isxander.controlify.impl.api.vmousesnapping.SnapPoint; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.world.inventory.AbstractContainerMenu; import org.joml.Vector2i; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/AbstractRecipeBookScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/AbstractRecipeBookScreenMixin.java index 54dd287ce..4272aa49b 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/AbstractRecipeBookScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/AbstractRecipeBookScreenMixin.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.virtualmouse.snapping; -import dev.isxander.controlify.api.vmousesnapping.SnapPoint; -import dev.isxander.controlify.virtualmouse.SnapUtils; +import dev.isxander.controlify.impl.api.vmousesnapping.SnapPoint; +import dev.isxander.controlify.impl.virtualmouse.SnapUtils; import net.minecraft.client.gui.screens.inventory.*; import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent; import net.minecraft.world.inventory.AbstractContainerMenu; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/CreativeModeInventoryScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/CreativeModeInventoryScreenMixin.java index dd3157dcb..ec3db9e2d 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/CreativeModeInventoryScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/CreativeModeInventoryScreenMixin.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.mixins.feature.virtualmouse.snapping; -import dev.isxander.controlify.api.vmousesnapping.SnapPoint; +import dev.isxander.controlify.impl.api.vmousesnapping.SnapPoint; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen; import net.minecraft.world.item.CreativeModeTab; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/EnchantmentScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/EnchantmentScreenMixin.java index d1500bfba..fb2da8f33 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/EnchantmentScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/EnchantmentScreenMixin.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.virtualmouse.snapping; -import dev.isxander.controlify.api.vmousesnapping.ISnapBehaviour; -import dev.isxander.controlify.api.vmousesnapping.SnapPoint; +import dev.isxander.controlify.impl.api.vmousesnapping.ISnapBehaviour; +import dev.isxander.controlify.impl.api.vmousesnapping.SnapPoint; import net.minecraft.client.gui.screens.inventory.EnchantmentScreen; import net.minecraft.world.inventory.EnchantmentMenu; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/LoomScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/LoomScreenMixin.java index 7c7200803..ef2aac6ce 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/LoomScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/LoomScreenMixin.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.virtualmouse.snapping; -import dev.isxander.controlify.api.vmousesnapping.ISnapBehaviour; -import dev.isxander.controlify.api.vmousesnapping.SnapPoint; +import dev.isxander.controlify.impl.api.vmousesnapping.ISnapBehaviour; +import dev.isxander.controlify.impl.api.vmousesnapping.SnapPoint; import net.minecraft.client.gui.screens.inventory.LoomScreen; import net.minecraft.core.Holder; import net.minecraft.world.inventory.LoomMenu; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/ScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/ScreenMixin.java index ee2b5dc2f..3215273cc 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/ScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/ScreenMixin.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.virtualmouse.snapping; -import dev.isxander.controlify.api.vmousesnapping.ISnapBehaviour; -import dev.isxander.controlify.api.vmousesnapping.SnapPoint; +import dev.isxander.controlify.impl.api.vmousesnapping.ISnapBehaviour; +import dev.isxander.controlify.impl.api.vmousesnapping.SnapPoint; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.events.GuiEventListener; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/StonecutterScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/StonecutterScreenMixin.java index 0280af8b2..3cdf5e6dd 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/StonecutterScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/StonecutterScreenMixin.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.virtualmouse.snapping; -import dev.isxander.controlify.api.vmousesnapping.ISnapBehaviour; -import dev.isxander.controlify.api.vmousesnapping.SnapPoint; +import dev.isxander.controlify.impl.api.vmousesnapping.ISnapBehaviour; +import dev.isxander.controlify.impl.api.vmousesnapping.SnapPoint; import net.minecraft.client.gui.screens.inventory.StonecutterScreen; import net.minecraft.world.inventory.StonecutterMenu; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/rumble/BasicRumbleEffect.java b/src/main/java/dev/isxander/controlify/rumble/BasicRumbleEffect.java deleted file mode 100644 index 89a3d45a2..000000000 --- a/src/main/java/dev/isxander/controlify/rumble/BasicRumbleEffect.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.rumble; - -import dev.isxander.controlify.utils.MinecraftUtil; -import net.minecraft.client.gui.screens.Screen; -import org.apache.commons.lang3.Validate; - -import java.util.Arrays; -import java.util.Objects; -import java.util.function.BooleanSupplier; -import java.util.function.Function; - -public final class BasicRumbleEffect implements RumbleEffect { - private final RumbleState[] keyframes; - private int tick = 0; - private boolean finished; - private int priority = 0; - private BooleanSupplier earlyFinishCondition = () -> false; - - public BasicRumbleEffect(RumbleState[] keyframes) { - this.keyframes = keyframes; - } - - @Override - public void tick() { - tick++; - if (tick >= keyframes.length || earlyFinishCondition.getAsBoolean()) - finished = true; - } - - @Override - public RumbleState currentState() { - if (tick == 0) - throw new IllegalStateException("Effect hasn't ticked yet."); - - return keyframes[tick - 1]; - } - - @Override - public int age() { - return tick; - } - - @Override - public boolean isFinished() { - return finished; - } - - @Override - public int priority() { - return priority; - } - - public BasicRumbleEffect prioritised(int priority) { - this.priority = priority; - return this; - } - - public RumbleState[] states() { - return keyframes; - } - - public BasicRumbleEffect earlyFinish(BooleanSupplier condition) { - var current = earlyFinishCondition; - this.earlyFinishCondition = () -> current.getAsBoolean() || condition.getAsBoolean(); - return this; - } - - @Override - public boolean equals(Object obj) { - if (obj == this) return true; - if (obj == null || obj.getClass() != this.getClass()) return false; - var that = (BasicRumbleEffect) obj; - return Arrays.equals(this.states(), that.states()) - && this.priority() == that.priority(); - } - - @Override - public int hashCode() { - return Objects.hash(Arrays.hashCode(this.states()), this.priority()); - } - - @Override - public String toString() { - return "RumbleEffect[" + - "states=" + Arrays.toString(this.states()) + ',' + - "priority=" + this.priority() + ']'; - } - - public BasicRumbleEffect seq(BasicRumbleEffect other) { - return BasicRumbleEffect.seq(this, other); - } - - public BasicRumbleEffect repeat(int count) { - Validate.isTrue(count > 0, "count must be greater than 0"); - - if (count == 1) return this; - - BasicRumbleEffect effect = this; - for (int i = 0; i < count - 1; i++) { - effect = BasicRumbleEffect.seq(effect, this); - } - return effect; - } - - /** - * Creates a rumble effect where the state is determined by the tick. - * - * @param stateFunction the function that takes a tick and returns the state for that tick. - * @param durationTicks how many ticks the effect should last for. - */ - public static BasicRumbleEffect byTick(Function stateFunction, int durationTicks) { - RumbleState[] states = new RumbleState[durationTicks]; - for (int i = 0; i < durationTicks; i++) { - states[i] = stateFunction.apply(i); - } - return new BasicRumbleEffect(states); - } - - /** - * Creates a rumble effect from a function that takes a time value from 0, start, to 1, end, and returns that tick. - * - * @param stateFunction the function that takes the time value and returns the state for that tick. - * @param durationTicks how many ticks the effect should last for. - */ - public static BasicRumbleEffect byTime(Function stateFunction, int durationTicks) { - return BasicRumbleEffect.byTick(tick -> stateFunction.apply((float) tick / (float) durationTicks), durationTicks); - } - - /** - * Creates a rumble effect that has a constant state. - * - * @param strong the strong motor magnitude. - * @param weak the weak motor magnitude - * @param durationTicks how many ticks the effect should last for. - */ - public static BasicRumbleEffect constant(float strong, float weak, int durationTicks) { - return BasicRumbleEffect.byTick(tick -> new RumbleState(strong, weak), durationTicks); - } - - public static BasicRumbleEffect empty(int durationTicks) { - return BasicRumbleEffect.byTick(tick -> new RumbleState(0f, 0f), durationTicks); - } - - public static BasicRumbleEffect seq(BasicRumbleEffect... effects) { - RumbleState[] states = Arrays.stream(effects) - .flatMap(effect -> Arrays.stream(effect.states())) - .toArray(RumbleState[]::new); - - return new BasicRumbleEffect(states); - } - - public static BooleanSupplier finishOnScreenChange() { - Screen screen = MinecraftUtil.getScreen(); - return () -> screen != MinecraftUtil.getScreen(); - } -} diff --git a/src/main/java/dev/isxander/controlify/rumble/ContinuousRumbleEffect.java b/src/main/java/dev/isxander/controlify/rumble/ContinuousRumbleEffect.java deleted file mode 100644 index 5b14b14d4..000000000 --- a/src/main/java/dev/isxander/controlify/rumble/ContinuousRumbleEffect.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.rumble; - -import net.minecraft.client.Minecraft; -import net.minecraft.util.Mth; -import net.minecraft.world.phys.Vec3; -import org.apache.commons.lang3.Validate; - -import java.util.function.BooleanSupplier; -import java.util.function.Function; -import java.util.function.Supplier; - -public class ContinuousRumbleEffect implements RumbleEffect { - private final Function stateFunction; - private final int priority; - private final int timeout; - private final int minTime; - private int tick; - private int age; - private boolean stopped; - private BooleanSupplier stopCondition; - - public ContinuousRumbleEffect(Function stateFunction, int priority, int timeout, int minTime, BooleanSupplier stopCondition) { - this.stateFunction = stateFunction; - this.priority = priority; - this.timeout = timeout; - this.minTime = minTime; - this.stopCondition = stopCondition; - } - - @Override - public void tick() { - tick++; - age++; - if (stopCondition.getAsBoolean()) - stop(); - } - - @Override - public RumbleState currentState() { - if (tick == 0) - throw new IllegalStateException("Effect hasn't ticked yet."); - - return stateFunction.apply(tick - 1); - } - - public void stop() { - stopped = true; - } - - public void heartbeat() { - age = 0; - } - - @Override - public int age() { - return tick; - } - - @Override - public boolean isFinished() { - return (stopped || (timeout > 0 && age >= timeout)) && tick >= minTime; - } - - @Override - public int priority() { - return this.priority; - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - private Function stateFunction; - private int priority; - private int timeout = -1; - private int minTime; - private InWorldProperties inWorldProperties; - private BooleanSupplier stopCondition = () -> false; - - private Builder() { - } - - public Builder byTick(Function stateFunction) { - this.stateFunction = stateFunction; - return this; - } - - public Builder constant(RumbleState state) { - this.stateFunction = tick -> state; - return this; - } - - public Builder constant(float strong, float weak) { - return this.constant(new RumbleState(strong, weak)); - } - - public Builder timeout(int timeoutTicks) { - Validate.isTrue(timeoutTicks >= 0, "the timeout cannot be negative!"); - - this.timeout = timeoutTicks; - return this; - } - - public Builder minTime(int minTimeTicks) { - Validate.isTrue(minTimeTicks >= 0, "the minimum time cannot be negative!"); - - this.minTime = minTimeTicks; - return this; - } - - public Builder priority(int priority) { - this.priority = priority; - return this; - } - - public Builder inWorld(Supplier sourceLocation, float min, float max, float effectRange, Function fallofFunction) { - this.inWorldProperties = new InWorldProperties(sourceLocation, min, max, effectRange, fallofFunction); - stopCondition(() -> Minecraft.getInstance().getCameraEntity() == null); - return this; - } - - public Builder stopCondition(BooleanSupplier stopCondition) { - BooleanSupplier oldStopCondition = this.stopCondition; - this.stopCondition = () -> stopCondition.getAsBoolean() || oldStopCondition.getAsBoolean(); - return this; - } - - public ContinuousRumbleEffect build() { - Validate.notNull(stateFunction, "stateFunction cannot be null!"); - Validate.isTrue(minTime <= timeout || timeout == -1, "the minimum time cannot be greater than the timeout!"); - - var stateFunction = this.stateFunction; - if (inWorldProperties != null) - stateFunction = inWorldProperties.modify(stateFunction); - - return new ContinuousRumbleEffect(stateFunction, priority, timeout, minTime, stopCondition); - } - - private record InWorldProperties(Supplier sourceLocation, float minMagnitude, float maxMagnitude, float effectRange, Function fallofFunction) { - private Function modify(Function stateFunction) { - return tick -> { - if (Minecraft.getInstance().getCameraEntity() == null) - return RumbleState.NONE; - - float distanceSqr = (float) Minecraft.getInstance().getCameraEntity().distanceToSqr(sourceLocation.get()); - float normalizedDistance = Mth.clamp(distanceSqr / (effectRange * effectRange), 0, 1); - float multiplier = Mth.lerp(fallofFunction.apply(1f - normalizedDistance), minMagnitude, maxMagnitude); - - return stateFunction.apply(tick).mul(multiplier); - }; - } - } - } -} diff --git a/src/main/java/dev/isxander/controlify/rumble/RumbleCapable.java b/src/main/java/dev/isxander/controlify/rumble/RumbleCapable.java deleted file mode 100644 index 283bd06c2..000000000 --- a/src/main/java/dev/isxander/controlify/rumble/RumbleCapable.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.rumble; - -public interface RumbleCapable { - boolean setRumble(float strongMagnitude, float weakMagnitude); - - boolean supportsRumble(); - - RumbleState applyRumbleSourceStrength(RumbleState state, RumbleSource source); -} diff --git a/src/main/java/dev/isxander/controlify/rumble/RumbleEffect.java b/src/main/java/dev/isxander/controlify/rumble/RumbleEffect.java deleted file mode 100644 index 365470449..000000000 --- a/src/main/java/dev/isxander/controlify/rumble/RumbleEffect.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.rumble; - -public interface RumbleEffect extends Comparable { - void tick(); - RumbleState currentState(); - - boolean isFinished(); - - int priority(); - int age(); - - @Override - default int compareTo(RumbleEffect o) { - int priorityCompare = Integer.compare(o.priority(), this.priority()); - if (priorityCompare != 0) return priorityCompare; - return Integer.compare(this.age(), o.age()); - } -} diff --git a/src/main/java/dev/isxander/controlify/rumble/RumbleManager.java b/src/main/java/dev/isxander/controlify/rumble/RumbleManager.java deleted file mode 100644 index 0639896c5..000000000 --- a/src/main/java/dev/isxander/controlify/rumble/RumbleManager.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.rumble; - -import dev.isxander.controlify.controller.rumble.RumbleComponent; -import org.jetbrains.annotations.NotNull; - -import java.util.Comparator; -import java.util.PriorityQueue; -import java.util.Queue; - -public class RumbleManager { - private final RumbleComponent controller; - private final Queue effectQueue; - - private boolean silent, wasSilent; - - public RumbleManager(RumbleComponent controller) { - this.controller = controller; - this.effectQueue = new PriorityQueue<>(Comparator.comparing(RumbleEffectInstance::effect)); - } - - @Deprecated - public void play(RumbleEffect effect) { - play(RumbleSource.MASTER, effect); - } - - public void play(RumbleSource source, RumbleEffect effect) { - effectQueue.add(new RumbleEffectInstance(source, effect)); - } - - public void tick() { - effectQueue.removeIf(e -> e.effect().isFinished()); - effectQueue.forEach(e -> e.effect().tick()); - - if (effectQueue.isEmpty()) { - clearRumble(); - return; - } - - float strong = 0f, weak = 0f; - for (RumbleEffectInstance effect : effectQueue) { - RumbleState effectState = controller.settings().applyRumbleStrength(effect.effect().currentState(), effect.source()); - strong = Math.max(strong, effectState.strong()); - weak = Math.max(weak, effectState.weak()); - } - RumbleState state = new RumbleState(strong, weak); - - if (state.isZero()) { - clearRumble(); - return; - } - - if (silent) { - clearRumble(); - } else { - controller.queueRumble(state); - wasSilent = false; - } - } - - private void clearRumble() { - if (wasSilent) - return; - - controller.queueRumble(RumbleState.NONE); - wasSilent = true; - } - - public void clearEffects() { - effectQueue.clear(); - } - - public void setSilent(boolean silent) { - this.silent = silent; - } - - public boolean isPlaying() { - return !effectQueue.isEmpty(); - } - - private record RumbleEffectInstance(RumbleSource source, RumbleEffect effect) implements Comparable { - @Override - public int compareTo(@NotNull RumbleManager.RumbleEffectInstance o) { - return effect.compareTo(o.effect); - } - } -} diff --git a/src/main/java/dev/isxander/controlify/rumble/RumbleSource.java b/src/main/java/dev/isxander/controlify/rumble/RumbleSource.java deleted file mode 100644 index 5fe9d27d9..000000000 --- a/src/main/java/dev/isxander/controlify/rumble/RumbleSource.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.rumble; - -import com.google.gson.JsonObject; -import com.mojang.serialization.Codec; -import dev.isxander.controlify.utils.CUtil; -import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; -import net.minecraft.resources.Identifier; - -import java.util.*; - -public record RumbleSource(Identifier id) { - public static final Codec CODEC = Identifier.CODEC - .xmap(RumbleSource::get, RumbleSource::id); - - private static final Map SOURCES = new Object2ObjectLinkedOpenHashMap<>(); - - public static final RumbleSource - MASTER = register("master"), - PLAYER = register("player"), - WORLD = register("world"), - INTERACTION = register("interaction"), - GUI = register("gui"); - - public static RumbleSource get(Identifier id) { - RumbleSource source = SOURCES.get(id); - if (source == null) { - CUtil.LOGGER.warn("Unknown rumble source: {}. Using master.", id); - return MASTER; - } - return source; - } - - public static Collection values() { - return SOURCES.values(); - } - - public static JsonObject getDefaultJson() { - JsonObject object = new JsonObject(); - for (RumbleSource source : SOURCES.values()) { - object.addProperty(source.id().toString(), 1f); - } - return object; - } - - public static Map getDefaultMap() { - Map map = new HashMap<>(); - - for (RumbleSource source : SOURCES.values()) { - map.put(source.id(), 1f); - } - - return map; - } - - public static RumbleSource register(Identifier id) { - var source = new RumbleSource(id); - SOURCES.put(id, source); - return source; - } - - public static RumbleSource register(String identifier, String path) { - return register(Identifier.fromNamespaceAndPath(identifier, path)); - } - - private static RumbleSource register(String path) { - return register("controlify", path); - } -} diff --git a/src/main/resources/assets/controlify/controller/activators.json b/src/main/resources/assets/controlify/controller/activators.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/src/main/resources/assets/controlify/controller/activators.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/src/main/resources/assets/controlify/contextual/facts/container.json b/src/main/resources/assets/controlify/controller/contextual/facts/container.json similarity index 100% rename from src/main/resources/assets/controlify/contextual/facts/container.json rename to src/main/resources/assets/controlify/controller/contextual/facts/container.json diff --git a/src/main/resources/assets/controlify/contextual/facts/in_game.json b/src/main/resources/assets/controlify/controller/contextual/facts/in_game.json similarity index 100% rename from src/main/resources/assets/controlify/contextual/facts/in_game.json rename to src/main/resources/assets/controlify/controller/contextual/facts/in_game.json diff --git a/src/main/resources/assets/controlify/contextual/guide/container.json b/src/main/resources/assets/controlify/controller/contextual/guide/container.json similarity index 100% rename from src/main/resources/assets/controlify/contextual/guide/container.json rename to src/main/resources/assets/controlify/controller/contextual/guide/container.json diff --git a/src/main/resources/assets/controlify/contextual/guide/in_game.json b/src/main/resources/assets/controlify/controller/contextual/guide/in_game.json similarity index 100% rename from src/main/resources/assets/controlify/contextual/guide/in_game.json rename to src/main/resources/assets/controlify/controller/contextual/guide/in_game.json diff --git a/src/main/resources/assets/controlify/contextual/trigger_effect/in_game.json b/src/main/resources/assets/controlify/controller/contextual/trigger_effect/in_game.json similarity index 100% rename from src/main/resources/assets/controlify/contextual/trigger_effect/in_game.json rename to src/main/resources/assets/controlify/controller/contextual/trigger_effect/in_game.json diff --git a/src/main/resources/assets/controlify/controllers/controller_identification.json5 b/src/main/resources/assets/controlify/controller/controller_identification.json5 similarity index 100% rename from src/main/resources/assets/controlify/controllers/controller_identification.json5 rename to src/main/resources/assets/controlify/controller/controller_identification.json5 diff --git a/src/main/resources/assets/controlify/controllers/default_bind/default.json b/src/main/resources/assets/controlify/controller/default_bind/default.json similarity index 100% rename from src/main/resources/assets/controlify/controllers/default_bind/default.json rename to src/main/resources/assets/controlify/controller/default_bind/default.json diff --git a/src/main/resources/assets/controlify/controllers/default_bind/dualsense.json b/src/main/resources/assets/controlify/controller/default_bind/dualsense.json similarity index 100% rename from src/main/resources/assets/controlify/controllers/default_bind/dualsense.json rename to src/main/resources/assets/controlify/controller/default_bind/dualsense.json diff --git a/src/main/resources/assets/controlify/controllers/default_bind/switch.json b/src/main/resources/assets/controlify/controller/default_bind/switch.json similarity index 100% rename from src/main/resources/assets/controlify/controllers/default_bind/switch.json rename to src/main/resources/assets/controlify/controller/default_bind/switch.json diff --git a/src/main/resources/assets/controlify/controllers/default_config.json b/src/main/resources/assets/controlify/controller/default_config.json similarity index 100% rename from src/main/resources/assets/controlify/controllers/default_config.json rename to src/main/resources/assets/controlify/controller/default_config.json diff --git a/src/main/resources/assets/controlify/controllers/font_mappings/default.json b/src/main/resources/assets/controlify/controller/font_mapping/default.json similarity index 100% rename from src/main/resources/assets/controlify/controllers/font_mappings/default.json rename to src/main/resources/assets/controlify/controller/font_mapping/default.json diff --git a/src/main/resources/assets/controlify/controllers/font_mappings/dualsense.json b/src/main/resources/assets/controlify/controller/font_mapping/dualsense.json similarity index 100% rename from src/main/resources/assets/controlify/controllers/font_mappings/dualsense.json rename to src/main/resources/assets/controlify/controller/font_mapping/dualsense.json diff --git a/src/main/resources/assets/controlify/controllers/font_mappings/dualshock3.json b/src/main/resources/assets/controlify/controller/font_mapping/dualshock3.json similarity index 100% rename from src/main/resources/assets/controlify/controllers/font_mappings/dualshock3.json rename to src/main/resources/assets/controlify/controller/font_mapping/dualshock3.json diff --git a/src/main/resources/assets/controlify/controllers/font_mappings/dualshock4.json b/src/main/resources/assets/controlify/controller/font_mapping/dualshock4.json similarity index 100% rename from src/main/resources/assets/controlify/controllers/font_mappings/dualshock4.json rename to src/main/resources/assets/controlify/controller/font_mapping/dualshock4.json diff --git a/src/main/resources/assets/controlify/controllers/font_mappings/steam_controller.json b/src/main/resources/assets/controlify/controller/font_mapping/steam_controller.json similarity index 100% rename from src/main/resources/assets/controlify/controllers/font_mappings/steam_controller.json rename to src/main/resources/assets/controlify/controller/font_mapping/steam_controller.json diff --git a/src/main/resources/assets/controlify/controllers/font_mappings/steam_deck.json b/src/main/resources/assets/controlify/controller/font_mapping/steam_deck.json similarity index 100% rename from src/main/resources/assets/controlify/controllers/font_mappings/steam_deck.json rename to src/main/resources/assets/controlify/controller/font_mapping/steam_deck.json diff --git a/src/main/resources/assets/controlify/controllers/font_mappings/switch.json b/src/main/resources/assets/controlify/controller/font_mapping/switch.json similarity index 100% rename from src/main/resources/assets/controlify/controllers/font_mappings/switch.json rename to src/main/resources/assets/controlify/controller/font_mapping/switch.json diff --git a/src/main/resources/assets/controlify/controllers/font_mappings/xbox_one.json b/src/main/resources/assets/controlify/controller/font_mapping/xbox_one.json similarity index 100% rename from src/main/resources/assets/controlify/controllers/font_mappings/xbox_one.json rename to src/main/resources/assets/controlify/controller/font_mapping/xbox_one.json diff --git a/src/main/resources/assets/controlify/controllers/gamecontrollerdb-sdl2.txt b/src/main/resources/assets/controlify/controller/gamecontrollerdb-sdl2.txt similarity index 100% rename from src/main/resources/assets/controlify/controllers/gamecontrollerdb-sdl2.txt rename to src/main/resources/assets/controlify/controller/gamecontrollerdb-sdl2.txt diff --git a/src/main/resources/assets/controlify/controllers/gamecontrollerdb-sdl3.txt b/src/main/resources/assets/controlify/controller/gamecontrollerdb-sdl3.txt similarity index 100% rename from src/main/resources/assets/controlify/controllers/gamecontrollerdb-sdl3.txt rename to src/main/resources/assets/controlify/controller/gamecontrollerdb-sdl3.txt diff --git a/src/main/resources/assets/controlify/haptics/navigate.ogg b/src/main/resources/assets/controlify/controller/haptics/navigate.ogg similarity index 100% rename from src/main/resources/assets/controlify/haptics/navigate.ogg rename to src/main/resources/assets/controlify/controller/haptics/navigate.ogg diff --git a/src/main/resources/assets/controlify/keyboard_layout/full/en_us.json b/src/main/resources/assets/controlify/controller/keyboard_layout/full/en_us.json similarity index 100% rename from src/main/resources/assets/controlify/keyboard_layout/full/en_us.json rename to src/main/resources/assets/controlify/controller/keyboard_layout/full/en_us.json diff --git a/src/main/resources/assets/controlify/keyboard_layout/server_ip/en_us.json b/src/main/resources/assets/controlify/controller/keyboard_layout/server_ip/en_us.json similarity index 100% rename from src/main/resources/assets/controlify/keyboard_layout/server_ip/en_us.json rename to src/main/resources/assets/controlify/controller/keyboard_layout/server_ip/en_us.json diff --git a/src/main/resources/assets/controlify/keyboard_layout/simple/en_us.json b/src/main/resources/assets/controlify/controller/keyboard_layout/simple/en_us.json similarity index 100% rename from src/main/resources/assets/controlify/keyboard_layout/simple/en_us.json rename to src/main/resources/assets/controlify/controller/keyboard_layout/simple/en_us.json diff --git a/src/main/resources/assets/controlify/radial_icons.json b/src/main/resources/assets/controlify/controller/radial_icons.json similarity index 100% rename from src/main/resources/assets/controlify/radial_icons.json rename to src/main/resources/assets/controlify/controller/radial_icons.json diff --git a/src/main/resources/assets/controlify/controller/rumble_sources.json b/src/main/resources/assets/controlify/controller/rumble_sources.json new file mode 100644 index 000000000..9944fa8ec --- /dev/null +++ b/src/main/resources/assets/controlify/controller/rumble_sources.json @@ -0,0 +1,16 @@ +{ + "controlify:incoming_damage": "controlify:player", + "controlify:water_land": "controlify:player", + "controlify:slow_block": "controlify:player", + "controlify:outgoing_damage": "controlify:interaction", + "controlify:item_break": "controlify:interaction", + "controlify:use_item": "controlify:interaction", + "controlify:block_break": "controlify:interaction", + "controlify:fishing_bite": "controlify:interaction", + "controlify:explosion": "controlify:world", + "controlify:lightning": "controlify:world", + "controlify:dragon_death": "controlify:world", + "controlify:wither_spawn": "controlify:world", + "controlify:anvil": "controlify:gui", + "controlify:test_vibration": "controlify:master" +} diff --git a/src/main/resources/controlify-compat.iris.mixins.json b/src/main/resources/controlify-compat.iris.mixins.json index fcae699ce..a9128e320 100644 --- a/src/main/resources/controlify-compat.iris.mixins.json +++ b/src/main/resources/controlify-compat.iris.mixins.json @@ -1,5 +1,5 @@ { - "package": "dev.isxander.controlify.compatibility.iris.mixins", + "package": "dev.isxander.controlify.impl.compatibility.iris.mixins", "required": false, "minVersion": "0.8", "mixinextras": { @@ -11,7 +11,7 @@ "overwrites": { "requireAnnotations": true }, - "plugin": "dev.isxander.controlify.compatibility.iris.IrisMixinPlugin", + "plugin": "dev.isxander.controlify.impl.compatibility.iris.IrisMixinPlugin", "compatibilityLevel": "JAVA_25", "client": [ ], diff --git a/src/main/resources/controlify-compat.reeses-sodium-options.mixins.json b/src/main/resources/controlify-compat.reeses-sodium-options.mixins.json index 42ac8ca05..00ca7ef32 100644 --- a/src/main/resources/controlify-compat.reeses-sodium-options.mixins.json +++ b/src/main/resources/controlify-compat.reeses-sodium-options.mixins.json @@ -1,5 +1,5 @@ { - "package": "dev.isxander.controlify.compatibility.rso.mixins", + "package": "dev.isxander.controlify.impl.compatibility.rso.mixins", "required": false, "minVersion": "0.8", "mixinextras": { @@ -11,7 +11,7 @@ "overwrites": { "requireAnnotations": true }, - "plugin": "dev.isxander.controlify.compatibility.rso.RSOMixinPlugin", + "plugin": "dev.isxander.controlify.impl.compatibility.rso.RSOMixinPlugin", "compatibilityLevel": "JAVA_25", "client": [ "SodiumVideoOptionsScreenMixin" diff --git a/src/main/resources/controlify-compat.rrls.mixins.json b/src/main/resources/controlify-compat.rrls.mixins.json index d55a01ad9..1c06b7207 100644 --- a/src/main/resources/controlify-compat.rrls.mixins.json +++ b/src/main/resources/controlify-compat.rrls.mixins.json @@ -1,5 +1,5 @@ { - "package": "dev.isxander.controlify.compatibility.rrls.mixins", + "package": "dev.isxander.controlify.impl.compatibility.rrls.mixins", "required": false, "minVersion": "0.8", "mixinextras": { @@ -8,7 +8,7 @@ "injectors": { "defaultRequire": 0 }, - "plugin": "dev.isxander.controlify.compatibility.rrls.RrlsMixinPlugin", + "plugin": "dev.isxander.controlify.impl.compatibility.rrls.RrlsMixinPlugin", "compatibilityLevel": "JAVA_25", "client": [ ], diff --git a/src/main/resources/controlify-compat.simple-voice-chat.mixins.json b/src/main/resources/controlify-compat.simple-voice-chat.mixins.json index a6d1d1394..83cb1bb85 100644 --- a/src/main/resources/controlify-compat.simple-voice-chat.mixins.json +++ b/src/main/resources/controlify-compat.simple-voice-chat.mixins.json @@ -1,5 +1,5 @@ { - "package": "dev.isxander.controlify.compatibility.simplevoicechat.mixins", + "package": "dev.isxander.controlify.impl.compatibility.simplevoicechat.mixins", "required": false, "minVersion": "0.8", "mixinextras": { @@ -11,7 +11,7 @@ "overwrites": { "requireAnnotations": true }, - "plugin": "dev.isxander.controlify.compatibility.simplevoicechat.SVCMixinPlugin", + "plugin": "dev.isxander.controlify.impl.compatibility.simplevoicechat.SVCMixinPlugin", "compatibilityLevel": "JAVA_25", "client": [ ], diff --git a/src/main/resources/controlify-compat.sodium.mixins.json b/src/main/resources/controlify-compat.sodium.mixins.json index 542e4aaa1..892b0b4e0 100644 --- a/src/main/resources/controlify-compat.sodium.mixins.json +++ b/src/main/resources/controlify-compat.sodium.mixins.json @@ -1,5 +1,5 @@ { - "package": "dev.isxander.controlify.compatibility.sodium.mixins", + "package": "dev.isxander.controlify.impl.compatibility.sodium.mixins", "required": false, "minVersion": "0.8", "mixinextras": { @@ -11,7 +11,7 @@ "overwrites": { "requireAnnotations": true }, - "plugin": "dev.isxander.controlify.compatibility.sodium.SodiumMixinPlugin", + "plugin": "dev.isxander.controlify.impl.compatibility.sodium.SodiumMixinPlugin", "compatibilityLevel": "JAVA_25", "client": [ "VideoSettingsScreenMixin" diff --git a/src/main/resources/controlify-compat.yacl.mixins.json b/src/main/resources/controlify-compat.yacl.mixins.json index 2da3063e2..dfd9fdf92 100644 --- a/src/main/resources/controlify-compat.yacl.mixins.json +++ b/src/main/resources/controlify-compat.yacl.mixins.json @@ -1,5 +1,5 @@ { - "package": "dev.isxander.controlify.compatibility.yacl.mixins", + "package": "dev.isxander.controlify.impl.compatibility.yacl.mixins", "required": true, "minVersion": "0.8", "mixinextras": { @@ -11,7 +11,7 @@ "overwrites": { "requireAnnotations": true }, - "plugin": "dev.isxander.controlify.compatibility.yacl.YACLMixinPlugin", + "plugin": "dev.isxander.controlify.impl.compatibility.yacl.YACLMixinPlugin", "compatibilityLevel": "JAVA_25", "client": [ "CyclingControllerElementMixin", diff --git a/src/main/resources/resourcepacks/legacy_console/assets/controlify/contextual/guide/in_game.json b/src/main/resources/resourcepacks/legacy_console/assets/controlify/contextual/guide/in_game.json deleted file mode 100644 index 99a4c520c..000000000 --- a/src/main/resources/resourcepacks/legacy_console/assets/controlify/contextual/guide/in_game.json +++ /dev/null @@ -1,1194 +0,0 @@ -{ - "replace": true, - "rules": [ - { - "for": "controlify:jump", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:riding_happy_ghast" - ] - }, - "then": { - "translate": "controlify.guide.ingame.fly_up" - } - }, - { - "for": "controlify:jump", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "any_of": [ - "controlify:riding_saddled_camel", - "controlify:riding_saddled_nautilus" - ] - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.dash" - } - }, - { - "for": "controlify:jump", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:riding_saddled_equine" - ] - }, - "then": { - "translate": "key.jump" - } - }, - { - "for": "controlify:jump", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:creative_flying" - ] - }, - "then": { - "translate": "controlify.guide.ingame.fly_up" - } - }, - { - "for": "controlify:jump", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:in_water" - ] - }, - "then": { - "translate": "controlify.guide.ingame.swim_up" - } - }, - { - "for": "controlify:jump", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:on_climbable" - ] - }, - "then": { - "translate": "controlify.guide.ingame.climb_up" - } - }, - { - "for": "controlify:jump", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:on_ground" - ] - }, - "then": { - "translate": "key.jump" - } - }, - { - "for": "controlify:jump", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:can_elytra_fly" - ] - }, - "then": { - "translate": "controlify.guide.ingame.start_elytra" - } - }, - { - "for": "controlify:sneak", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:in_vehicle" - ] - }, - "then": { - "translate": "controlify.guide.ingame.dismount" - } - }, - { - "for": "controlify:sneak", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:creative_flying" - ] - }, - "then": { - "translate": "controlify.guide.ingame.fly_down" - } - }, - { - "for": "controlify:sneak", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:in_water" - ], - "none_of": [ - "controlify:on_ground" - ] - }, - "then": { - "translate": "controlify.guide.ingame.swim_down" - } - }, - { - "for": "controlify:sneak", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:on_climbable" - ] - }, - "then": { - "translate": "controlify.guide.ingame.climb_down" - } - }, - { - "for": "controlify:inventory", - "where": "right", - "if": "controlify:verbosity_reduced", - "then": { - "translate": "controlify.guide.ingame.inventory" - } - }, - { - "for": "controlify:attack", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_entity", - "controlify:is_spectator" - ] - }, - "then": { - "translate": "controlify.guide.ingame.spectate" - } - }, - { - "for": "controlify:attack", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_entity" - ], - "none_of": [ - "controlify:is_spectator" - ] - }, - "then": { - "translate": "controlify.guide.ingame.attack" - } - }, - { - "for": "controlify:attack", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_block", - "controlify:main_hand_is_tool" - ] - }, - "then": { - "translate": "controlify.guide.ingame.mine" - } - }, - { - "for": "controlify:attack", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_block" - ] - }, - "then": { - "translate": "controlify.guide.ingame.break" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_entity", - "controlify:is_spectator" - ] - }, - "then": { - "translate": "controlify.guide.ingame.spectate" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_merchant" - ] - }, - "then": { - "translate": "controlify.guide.ingame.trade" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_chest_boat", - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.open" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_container_minecart" - ] - }, - "then": { - "translate": "controlify.guide.ingame.open" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_rideable" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.ride" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_closed_openable" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.open" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_open_openable" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.close" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_container" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.open" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_button" - ] - }, - "then": { - "translate": "controlify.guide.ingame.press" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_lever" - ] - }, - "then": { - "translate": "controlify.guide.ingame.toggle" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_bell" - ] - }, - "then": { - "translate": "controlify.guide.ingame.ring" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_bed" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.sleep" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_crafting_table" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.craft" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_furnace" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.smelt" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_smoker" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.cook" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_brewing_stand" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.brew" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_enchanting_table" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.enchant" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_smithing_table" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.smith" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_repair_station" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.repair" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_stonecutter" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.cut_stone" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_loom" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.weave" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_cartography_table" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.edit_map" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "items": "#c:tools/bow" - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.shoot" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "predicates": { - "minecraft:charged_projectiles": {} - } - } - } - ], - "none_of": [ - { - "slot": "controlify:main_hand", - "item": { - "components": { - "minecraft:charged_projectiles": [] - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.shoot" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "items": "minecraft:crossbow", - "components": { - "minecraft:charged_projectiles": [] - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.load" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "predicates": { - "minecraft:blocks_attacks": {} - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.block" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "predicates": { - "minecraft:consumable": {} - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.consume" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "any_of": [ - { - "slot": "controlify:main_hand", - "item": { - "items": "#c:tools/trident" - } - }, - { - "slot": "controlify:main_hand", - "item": { - "items": "#minecraft:spears" - } - }, - { - "slot": "controlify:main_hand", - "item": { - "items": "#minecraft:eggs" - } - }, - { - "slot": "controlify:main_hand", - "item": { - "items": [ - "minecraft:snowball", - "minecraft:splash_potion", - "minecraft:lingering_potion", - "minecraft:experience_bottle", - "minecraft:ender_pearl", - "minecraft:wind_charge" - ] - } - } - ] - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.throw" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:fishing_hook_cast", - { - "slot": "controlify:main_hand", - "item": { - "items": "#c:tools/fishing_rod" - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.reel_in" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "items": "#c:tools/fishing_rod" - } - } - ], - "none_of": [ - "controlify:fishing_hook_cast" - ] - }, - "then": { - "translate": "controlify.guide.ingame.cast" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "items": "#c:tools/brush" - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.brush" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "items": "minecraft:spyglass" - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.zoom" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "predicates": { - "minecraft:instrument": {} - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.play" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "predicates": { - "minecraft:equippable": {} - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.equip" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_block", - "controlify:main_hand_is_block_item" - ] - }, - "then": { - "translate": "controlify.guide.ingame.place" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "items": "#c:tools/bow" - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.shoot" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "predicates": { - "minecraft:charged_projectiles": {} - } - } - } - ], - "none_of": [ - { - "slot": "controlify:off_hand", - "item": { - "components": { - "minecraft:charged_projectiles": [] - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.shoot" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "items": "minecraft:crossbow", - "components": { - "minecraft:charged_projectiles": [] - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.load" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "predicates": { - "minecraft:blocks_attacks": {} - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.block" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "predicates": { - "minecraft:consumable": {} - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.consume" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "any_of": [ - { - "slot": "controlify:off_hand", - "item": { - "items": "#c:tools/trident" - } - }, - { - "slot": "controlify:off_hand", - "item": { - "items": "#minecraft:spears" - } - }, - { - "slot": "controlify:off_hand", - "item": { - "items": "#minecraft:eggs" - } - }, - { - "slot": "controlify:off_hand", - "item": { - "items": [ - "minecraft:snowball", - "minecraft:splash_potion", - "minecraft:lingering_potion", - "minecraft:experience_bottle", - "minecraft:ender_pearl", - "minecraft:wind_charge" - ] - } - } - ] - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.throw" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:fishing_hook_cast", - { - "slot": "controlify:off_hand", - "item": { - "items": "#c:tools/fishing_rod" - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.reel_in" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "items": "#c:tools/fishing_rod" - } - } - ], - "none_of": [ - "controlify:fishing_hook_cast" - ] - }, - "then": { - "translate": "controlify.guide.ingame.cast" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "items": "#c:tools/brush" - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.brush" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "items": "minecraft:spyglass" - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.zoom" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "predicates": { - "minecraft:instrument": {} - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.play" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "predicates": { - "minecraft:equippable": {} - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.equip" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_block", - "controlify:off_hand_is_block_item" - ] - }, - "then": { - "translate": "controlify.guide.ingame.place" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_entity" - ], - "none_of": [ - "controlify:is_spectator" - ] - }, - "then": { - "translate": "controlify.guide.ingame.interact" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_block" - ] - }, - "then": { - "translate": "controlify.guide.ingame.use" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:has_item_in_either_hand" - ] - }, - "then": { - "translate": "controlify.guide.ingame.use" - } - }, - { - "for": "controlify:drop", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_reduced", - "controlify:has_item_in_main_hand" - ] - }, - "then": { - "translate": "controlify.guide.ingame.drop" - } - } - ] -} diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/ControlifyBootstrap.java b/src/neoforge/java/dev/isxander/controlify/neoforge/ControlifyBootstrap.java index 9b4429b30..a90e1c441 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/ControlifyBootstrap.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/ControlifyBootstrap.java @@ -6,14 +6,14 @@ */ package dev.isxander.controlify.neoforge; -import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.gui.screen.ControlifySettingsScreen; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.gui.screen.ControlifySettingsScreen; import dev.isxander.controlify.neoforge.platform.NeoforgePlatformMainImpl; import dev.isxander.controlify.neoforge.platform.client.NeoforgePlatformClientImpl; -import dev.isxander.controlify.platform.Environment; -import dev.isxander.controlify.platform.client.PlatformClientUtil; -import dev.isxander.controlify.platform.main.PlatformMainUtil; -import dev.isxander.controlify.server.ControlifyServer; +import dev.isxander.controlify.impl.platform.Environment; +import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; +import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; +import dev.isxander.controlify.impl.server.ControlifyServer; import net.neoforged.bus.api.IEventBus; import net.neoforged.fml.ModLoadingContext; import net.neoforged.fml.common.Mod; diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/compatibility/NeoforgeCompatMixinPlatform.java b/src/neoforge/java/dev/isxander/controlify/neoforge/compatibility/NeoforgeCompatMixinPlatform.java index 9a950488a..526a883f0 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/compatibility/NeoforgeCompatMixinPlatform.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/compatibility/NeoforgeCompatMixinPlatform.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.neoforge.compatibility; -import dev.isxander.controlify.compatibility.CompatMixinPlatform; +import dev.isxander.controlify.impl.compatibility.CompatMixinPlatform; import net.neoforged.fml.loading.FMLLoader; public final class NeoforgeCompatMixinPlatform implements CompatMixinPlatform { diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/NeoforgePlatformMainImpl.java b/src/neoforge/java/dev/isxander/controlify/neoforge/platform/NeoforgePlatformMainImpl.java index f48c17bb5..9621975bd 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/NeoforgePlatformMainImpl.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/platform/NeoforgePlatformMainImpl.java @@ -6,16 +6,16 @@ */ package dev.isxander.controlify.neoforge.platform; -import dev.isxander.controlify.api.entrypoint.ControlifyEntrypoint; +import dev.isxander.controlify.impl.api.entrypoint.ControlifyEntrypoint; import dev.isxander.controlify.neoforge.platform.network.C2SNetworkApiNeoforge; import dev.isxander.controlify.neoforge.platform.network.S2CNetworkApiNeoforge; -import dev.isxander.controlify.platform.Environment; -import dev.isxander.controlify.platform.main.PlatformMainUtilImpl; -import dev.isxander.controlify.platform.main.events.CommandRegistrationCallbackEvent; -import dev.isxander.controlify.platform.main.events.HandshakeCompletionEvent; -import dev.isxander.controlify.platform.main.events.PlayerJoinedEvent; -import dev.isxander.controlify.platform.network.C2SNetworkApi; -import dev.isxander.controlify.platform.network.S2CNetworkApi; +import dev.isxander.controlify.impl.platform.Environment; +import dev.isxander.controlify.impl.platform.main.PlatformMainUtilImpl; +import dev.isxander.controlify.impl.platform.main.events.CommandRegistrationCallbackEvent; +import dev.isxander.controlify.impl.platform.main.events.HandshakeCompletionEvent; +import dev.isxander.controlify.impl.platform.main.events.PlayerJoinedEvent; +import dev.isxander.controlify.impl.platform.network.C2SNetworkApi; +import dev.isxander.controlify.impl.platform.network.S2CNetworkApi; import net.minecraft.core.Registry; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/client/NeoforgeCreativeTabHelper.java b/src/neoforge/java/dev/isxander/controlify/neoforge/platform/client/NeoforgeCreativeTabHelper.java index 9525543e7..ee0070999 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/client/NeoforgeCreativeTabHelper.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/platform/client/NeoforgeCreativeTabHelper.java @@ -7,7 +7,7 @@ package dev.isxander.controlify.neoforge.platform.client; import dev.isxander.controlify.neoforge.mixins.CreativeModeInventoryScreenAccessor; -import dev.isxander.controlify.platform.client.CreativeTabHelper; +import dev.isxander.controlify.impl.platform.client.CreativeTabHelper; import net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen; import net.minecraft.world.item.CreativeModeTab; import net.neoforged.neoforge.client.gui.CreativeTabsScreenPage; diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/client/NeoforgePlatformClientImpl.java b/src/neoforge/java/dev/isxander/controlify/neoforge/platform/client/NeoforgePlatformClientImpl.java index 306cd8b2f..28c1e0ae2 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/client/NeoforgePlatformClientImpl.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/platform/client/NeoforgePlatformClientImpl.java @@ -7,14 +7,14 @@ package dev.isxander.controlify.neoforge.platform.client; import dev.isxander.controlify.neoforge.platform.VanillaKeyMappingHolder; -import dev.isxander.controlify.platform.client.CreativeTabHelper; -import dev.isxander.controlify.platform.client.HudRenderLayer; -import dev.isxander.controlify.platform.client.PlatformClientUtilImpl; -import dev.isxander.controlify.platform.client.events.DisconnectedEvent; -import dev.isxander.controlify.platform.client.events.LifecycleEvent; -import dev.isxander.controlify.platform.client.events.ScreenRenderEvent; -import dev.isxander.controlify.platform.client.events.TickEvent; -import dev.isxander.controlify.platform.client.resource.ControlifyReloadListener; +import dev.isxander.controlify.impl.platform.client.CreativeTabHelper; +import dev.isxander.controlify.impl.platform.client.HudRenderLayer; +import dev.isxander.controlify.impl.platform.client.PlatformClientUtilImpl; +import dev.isxander.controlify.impl.platform.client.events.DisconnectedEvent; +import dev.isxander.controlify.impl.platform.client.events.LifecycleEvent; +import dev.isxander.controlify.impl.platform.client.events.ScreenRenderEvent; +import dev.isxander.controlify.impl.platform.client.events.TickEvent; +import dev.isxander.controlify.impl.platform.client.resource.ControlifyReloadListener; import net.minecraft.client.KeyMapping; import net.minecraft.client.Minecraft; import net.minecraft.client.Options; diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/network/C2SNetworkApiNeoforge.java b/src/neoforge/java/dev/isxander/controlify/neoforge/platform/network/C2SNetworkApiNeoforge.java index 172a09708..4183116ec 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/network/C2SNetworkApiNeoforge.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/platform/network/C2SNetworkApiNeoforge.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.neoforge.platform.network; -import dev.isxander.controlify.platform.network.C2SNetworkApi; +import dev.isxander.controlify.impl.platform.network.C2SNetworkApi; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/network/S2CNetworkApiNeoforge.java b/src/neoforge/java/dev/isxander/controlify/neoforge/platform/network/S2CNetworkApiNeoforge.java index b665cb3df..1bf206584 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/network/S2CNetworkApiNeoforge.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/platform/network/S2CNetworkApiNeoforge.java @@ -6,10 +6,9 @@ */ package dev.isxander.controlify.neoforge.platform.network; -import dev.isxander.controlify.platform.Environment; -import dev.isxander.controlify.platform.main.PlatformMainUtil; -import dev.isxander.controlify.platform.network.C2SNetworkApi; -import dev.isxander.controlify.platform.network.S2CNetworkApi; +import dev.isxander.controlify.impl.platform.Environment; +import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; +import dev.isxander.controlify.impl.platform.network.S2CNetworkApi; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.Identifier; diff --git a/src/test/java/dev/isxander/controlify/config/ConfigMigrationTestHelper.java b/src/test/java/dev/isxander/controlify/impl/config/ConfigMigrationTestHelper.java similarity index 95% rename from src/test/java/dev/isxander/controlify/config/ConfigMigrationTestHelper.java rename to src/test/java/dev/isxander/controlify/impl/config/ConfigMigrationTestHelper.java index dec14d037..2e7a4d99c 100644 --- a/src/test/java/dev/isxander/controlify/config/ConfigMigrationTestHelper.java +++ b/src/test/java/dev/isxander/controlify/impl/config/ConfigMigrationTestHelper.java @@ -4,12 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.config; +package dev.isxander.controlify.impl.config; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.mojang.serialization.JsonOps; -import dev.isxander.controlify.config.dto.profile.ProfileConfig; +import dev.isxander.controlify.impl.config.dto.profile.ProfileConfig; import org.junit.jupiter.api.Assertions; import java.io.IOException; diff --git a/src/test/java/dev/isxander/controlify/contextual/FactDependencyGraphTest.java b/src/test/java/dev/isxander/controlify/impl/contextual/FactDependencyGraphTest.java similarity index 98% rename from src/test/java/dev/isxander/controlify/contextual/FactDependencyGraphTest.java rename to src/test/java/dev/isxander/controlify/impl/contextual/FactDependencyGraphTest.java index 02cd302fa..51096c70a 100644 --- a/src/test/java/dev/isxander/controlify/contextual/FactDependencyGraphTest.java +++ b/src/test/java/dev/isxander/controlify/impl/contextual/FactDependencyGraphTest.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.contextual; +package dev.isxander.controlify.impl.contextual; import net.minecraft.resources.Identifier; import org.junit.jupiter.api.Test; From 3fdbdb002863cec2692e77fd15c880e4514e4fd5 Mon Sep 17 00:00:00 2001 From: isxander Date: Mon, 19 Oct 2026 16:41:36 +0100 Subject: [PATCH 12/44] repackage the fabric and neoforge sourcesets --- build.gradle.kts | 8 +- .../{ => impl}/ControlifyBootstrap.java | 6 +- .../FabricCompatMixinPlatform.java | 2 +- .../compatibility/ModMenuIntegration.java | 2 +- .../platform/FabricBackedEventHandler.java | 2 +- .../platform/FabricPlatformMainImpl.java | 6 +- .../client/FAPIApiCreativeTabHelper.java | 2 +- .../client/FabricPlatformClientImpl.java | 2 +- .../platform/network/C2SNetworkApiFabric.java | 2 +- .../platform/network/FabricPacketWrapper.java | 2 +- .../platform/network/S2CNetworkApiFabric.java | 2 +- .../controllers/contextual/guide/in_game.json | 1194 +++++++++++++++++ .../{ => impl}/ControlifyBootstrap.java | 6 +- .../NeoforgeCompatMixinPlatform.java | 2 +- .../platform/NeoforgePlatformMainImpl.java | 6 +- .../platform/VanillaKeyMappingHolder.java | 2 +- .../client/NeoforgeCreativeTabHelper.java | 2 +- .../client/NeoforgePlatformClientImpl.java | 4 +- .../network/C2SNetworkApiNeoforge.java | 2 +- .../network/NeoforgePacketWrapper.java | 2 +- .../network/S2CNetworkApiNeoforge.java | 2 +- .../neoforge/mixins/OptionsMixin.java | 2 +- 22 files changed, 1227 insertions(+), 33 deletions(-) rename src/fabric/java/dev/isxander/controlify/fabric/{ => impl}/ControlifyBootstrap.java (83%) rename src/fabric/java/dev/isxander/controlify/fabric/{ => impl}/compatibility/FabricCompatMixinPlatform.java (88%) rename src/fabric/java/dev/isxander/controlify/fabric/{ => impl}/compatibility/ModMenuIntegration.java (89%) rename src/fabric/java/dev/isxander/controlify/fabric/{ => impl}/platform/FabricBackedEventHandler.java (93%) rename src/fabric/java/dev/isxander/controlify/fabric/{ => impl}/platform/FabricPlatformMainImpl.java (95%) rename src/fabric/java/dev/isxander/controlify/fabric/{ => impl}/platform/client/FAPIApiCreativeTabHelper.java (95%) rename src/fabric/java/dev/isxander/controlify/fabric/{ => impl}/platform/client/FabricPlatformClientImpl.java (98%) rename src/fabric/java/dev/isxander/controlify/fabric/{ => impl}/platform/network/C2SNetworkApiFabric.java (96%) rename src/fabric/java/dev/isxander/controlify/fabric/{ => impl}/platform/network/FabricPacketWrapper.java (95%) rename src/fabric/java/dev/isxander/controlify/fabric/{ => impl}/platform/network/S2CNetworkApiFabric.java (96%) create mode 100644 src/main/resources/resourcepacks/legacy_console/assets/controlify/controllers/contextual/guide/in_game.json rename src/neoforge/java/dev/isxander/controlify/neoforge/{ => impl}/ControlifyBootstrap.java (85%) rename src/neoforge/java/dev/isxander/controlify/neoforge/{ => impl}/compatibility/NeoforgeCompatMixinPlatform.java (88%) rename src/neoforge/java/dev/isxander/controlify/neoforge/{ => impl}/platform/NeoforgePlatformMainImpl.java (95%) rename src/neoforge/java/dev/isxander/controlify/neoforge/{ => impl}/platform/VanillaKeyMappingHolder.java (81%) rename src/neoforge/java/dev/isxander/controlify/neoforge/{ => impl}/platform/client/NeoforgeCreativeTabHelper.java (95%) rename src/neoforge/java/dev/isxander/controlify/neoforge/{ => impl}/platform/client/NeoforgePlatformClientImpl.java (97%) rename src/neoforge/java/dev/isxander/controlify/neoforge/{ => impl}/platform/network/C2SNetworkApiNeoforge.java (97%) rename src/neoforge/java/dev/isxander/controlify/neoforge/{ => impl}/platform/network/NeoforgePacketWrapper.java (94%) rename src/neoforge/java/dev/isxander/controlify/neoforge/{ => impl}/platform/network/S2CNetworkApiNeoforge.java (97%) diff --git a/build.gradle.kts b/build.gradle.kts index c515be924..31d95785b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -177,10 +177,10 @@ manifests { fabricModJson(sourceSets.fabric.get()) { from(commonManifest) - entrypoint("modmenu", "dev.isxander.controlify.fabric.compatibility.ModMenuIntegration") - entrypoint("main", "dev.isxander.controlify.fabric.ControlifyBootstrap") - entrypoint("client", "dev.isxander.controlify.fabric.ControlifyBootstrap") - entrypoint("server", "dev.isxander.controlify.fabric.ControlifyBootstrap") + entrypoint("modmenu", "dev.isxander.controlify.fabric.impl.compatibility.ModMenuIntegration") + entrypoint("main", "dev.isxander.controlify.fabric.impl.ControlifyBootstrap") + entrypoint("client", "dev.isxander.controlify.fabric.impl.ControlifyBootstrap") + entrypoint("server", "dev.isxander.controlify.fabric.impl.ControlifyBootstrap") dependency("fabricloader", REQUIRED, "[0.19,)") mixin("controlify-platform.fabric.mixins.json") diff --git a/src/fabric/java/dev/isxander/controlify/fabric/ControlifyBootstrap.java b/src/fabric/java/dev/isxander/controlify/fabric/impl/ControlifyBootstrap.java similarity index 83% rename from src/fabric/java/dev/isxander/controlify/fabric/ControlifyBootstrap.java rename to src/fabric/java/dev/isxander/controlify/fabric/impl/ControlifyBootstrap.java index e719426ad..9936cf34d 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/ControlifyBootstrap.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/impl/ControlifyBootstrap.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.fabric; +package dev.isxander.controlify.fabric.impl; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.fabric.platform.client.FabricPlatformClientImpl; -import dev.isxander.controlify.fabric.platform.FabricPlatformMainImpl; +import dev.isxander.controlify.fabric.impl.platform.client.FabricPlatformClientImpl; +import dev.isxander.controlify.fabric.impl.platform.FabricPlatformMainImpl; import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; import dev.isxander.controlify.impl.server.ControlifyServer; diff --git a/src/fabric/java/dev/isxander/controlify/fabric/compatibility/FabricCompatMixinPlatform.java b/src/fabric/java/dev/isxander/controlify/fabric/impl/compatibility/FabricCompatMixinPlatform.java similarity index 88% rename from src/fabric/java/dev/isxander/controlify/fabric/compatibility/FabricCompatMixinPlatform.java rename to src/fabric/java/dev/isxander/controlify/fabric/impl/compatibility/FabricCompatMixinPlatform.java index b025c8736..b4e87a999 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/compatibility/FabricCompatMixinPlatform.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/impl/compatibility/FabricCompatMixinPlatform.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.fabric.compatibility; +package dev.isxander.controlify.fabric.impl.compatibility; import dev.isxander.controlify.impl.compatibility.CompatMixinPlatform; import net.fabricmc.loader.api.FabricLoader; diff --git a/src/fabric/java/dev/isxander/controlify/fabric/compatibility/ModMenuIntegration.java b/src/fabric/java/dev/isxander/controlify/fabric/impl/compatibility/ModMenuIntegration.java similarity index 89% rename from src/fabric/java/dev/isxander/controlify/fabric/compatibility/ModMenuIntegration.java rename to src/fabric/java/dev/isxander/controlify/fabric/impl/compatibility/ModMenuIntegration.java index 28cb8a801..43a5af25c 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/compatibility/ModMenuIntegration.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/impl/compatibility/ModMenuIntegration.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.fabric.compatibility; +package dev.isxander.controlify.fabric.impl.compatibility; //? if mod_menu { diff --git a/src/fabric/java/dev/isxander/controlify/fabric/platform/FabricBackedEventHandler.java b/src/fabric/java/dev/isxander/controlify/fabric/impl/platform/FabricBackedEventHandler.java similarity index 93% rename from src/fabric/java/dev/isxander/controlify/fabric/platform/FabricBackedEventHandler.java rename to src/fabric/java/dev/isxander/controlify/fabric/impl/platform/FabricBackedEventHandler.java index eeb2d14ad..84eb767ae 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/platform/FabricBackedEventHandler.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/impl/platform/FabricBackedEventHandler.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.fabric.platform; +package dev.isxander.controlify.fabric.impl.platform; import dev.isxander.controlify.impl.platform.EventHandler; import net.fabricmc.fabric.api.event.EventFactory; diff --git a/src/fabric/java/dev/isxander/controlify/fabric/platform/FabricPlatformMainImpl.java b/src/fabric/java/dev/isxander/controlify/fabric/impl/platform/FabricPlatformMainImpl.java similarity index 95% rename from src/fabric/java/dev/isxander/controlify/fabric/platform/FabricPlatformMainImpl.java rename to src/fabric/java/dev/isxander/controlify/fabric/impl/platform/FabricPlatformMainImpl.java index 7f650640d..04529ffa4 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/platform/FabricPlatformMainImpl.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/impl/platform/FabricPlatformMainImpl.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.fabric.platform; +package dev.isxander.controlify.fabric.impl.platform; import dev.isxander.controlify.impl.api.entrypoint.ControlifyEntrypoint; -import dev.isxander.controlify.fabric.platform.network.C2SNetworkApiFabric; -import dev.isxander.controlify.fabric.platform.network.S2CNetworkApiFabric; +import dev.isxander.controlify.fabric.impl.platform.network.C2SNetworkApiFabric; +import dev.isxander.controlify.fabric.impl.platform.network.S2CNetworkApiFabric; import dev.isxander.controlify.impl.platform.Environment; import dev.isxander.controlify.impl.platform.main.PlatformMainUtilImpl; import dev.isxander.controlify.impl.platform.main.events.CommandRegistrationCallbackEvent; diff --git a/src/fabric/java/dev/isxander/controlify/fabric/platform/client/FAPIApiCreativeTabHelper.java b/src/fabric/java/dev/isxander/controlify/fabric/impl/platform/client/FAPIApiCreativeTabHelper.java similarity index 95% rename from src/fabric/java/dev/isxander/controlify/fabric/platform/client/FAPIApiCreativeTabHelper.java rename to src/fabric/java/dev/isxander/controlify/fabric/impl/platform/client/FAPIApiCreativeTabHelper.java index dfbc9e63c..5ab1973b7 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/platform/client/FAPIApiCreativeTabHelper.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/impl/platform/client/FAPIApiCreativeTabHelper.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.fabric.platform.client; +package dev.isxander.controlify.fabric.impl.platform.client; import dev.isxander.controlify.impl.platform.client.CreativeTabHelper; import net.fabricmc.fabric.api.client.creativetab.v1.FabricCreativeModeInventoryScreen; diff --git a/src/fabric/java/dev/isxander/controlify/fabric/platform/client/FabricPlatformClientImpl.java b/src/fabric/java/dev/isxander/controlify/fabric/impl/platform/client/FabricPlatformClientImpl.java similarity index 98% rename from src/fabric/java/dev/isxander/controlify/fabric/platform/client/FabricPlatformClientImpl.java rename to src/fabric/java/dev/isxander/controlify/fabric/impl/platform/client/FabricPlatformClientImpl.java index 90971484d..45764d668 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/platform/client/FabricPlatformClientImpl.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/impl/platform/client/FabricPlatformClientImpl.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.fabric.platform.client; +package dev.isxander.controlify.fabric.impl.platform.client; import dev.isxander.controlify.impl.platform.client.CreativeTabHelper; import dev.isxander.controlify.impl.platform.client.HudRenderLayer; diff --git a/src/fabric/java/dev/isxander/controlify/fabric/platform/network/C2SNetworkApiFabric.java b/src/fabric/java/dev/isxander/controlify/fabric/impl/platform/network/C2SNetworkApiFabric.java similarity index 96% rename from src/fabric/java/dev/isxander/controlify/fabric/platform/network/C2SNetworkApiFabric.java rename to src/fabric/java/dev/isxander/controlify/fabric/impl/platform/network/C2SNetworkApiFabric.java index 6de311d99..cbfbbd397 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/platform/network/C2SNetworkApiFabric.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/impl/platform/network/C2SNetworkApiFabric.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.fabric.platform.network; +package dev.isxander.controlify.fabric.impl.platform.network; import dev.isxander.controlify.impl.platform.network.C2SNetworkApi; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; diff --git a/src/fabric/java/dev/isxander/controlify/fabric/platform/network/FabricPacketWrapper.java b/src/fabric/java/dev/isxander/controlify/fabric/impl/platform/network/FabricPacketWrapper.java similarity index 95% rename from src/fabric/java/dev/isxander/controlify/fabric/platform/network/FabricPacketWrapper.java rename to src/fabric/java/dev/isxander/controlify/fabric/impl/platform/network/FabricPacketWrapper.java index e19e300d7..2bde4e2f3 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/platform/network/FabricPacketWrapper.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/impl/platform/network/FabricPacketWrapper.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.fabric.platform.network; +package dev.isxander.controlify.fabric.impl.platform.network; import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; import net.minecraft.network.FriendlyByteBuf; diff --git a/src/fabric/java/dev/isxander/controlify/fabric/platform/network/S2CNetworkApiFabric.java b/src/fabric/java/dev/isxander/controlify/fabric/impl/platform/network/S2CNetworkApiFabric.java similarity index 96% rename from src/fabric/java/dev/isxander/controlify/fabric/platform/network/S2CNetworkApiFabric.java rename to src/fabric/java/dev/isxander/controlify/fabric/impl/platform/network/S2CNetworkApiFabric.java index cfa43b0fe..0409070fe 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/platform/network/S2CNetworkApiFabric.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/impl/platform/network/S2CNetworkApiFabric.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.fabric.platform.network; +package dev.isxander.controlify.fabric.impl.platform.network; import dev.isxander.controlify.impl.platform.network.S2CNetworkApi; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; diff --git a/src/main/resources/resourcepacks/legacy_console/assets/controlify/controllers/contextual/guide/in_game.json b/src/main/resources/resourcepacks/legacy_console/assets/controlify/controllers/contextual/guide/in_game.json new file mode 100644 index 000000000..99a4c520c --- /dev/null +++ b/src/main/resources/resourcepacks/legacy_console/assets/controlify/controllers/contextual/guide/in_game.json @@ -0,0 +1,1194 @@ +{ + "replace": true, + "rules": [ + { + "for": "controlify:jump", + "where": "left", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:riding_happy_ghast" + ] + }, + "then": { + "translate": "controlify.guide.ingame.fly_up" + } + }, + { + "for": "controlify:jump", + "where": "left", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "any_of": [ + "controlify:riding_saddled_camel", + "controlify:riding_saddled_nautilus" + ] + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.dash" + } + }, + { + "for": "controlify:jump", + "where": "left", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:riding_saddled_equine" + ] + }, + "then": { + "translate": "key.jump" + } + }, + { + "for": "controlify:jump", + "where": "left", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:creative_flying" + ] + }, + "then": { + "translate": "controlify.guide.ingame.fly_up" + } + }, + { + "for": "controlify:jump", + "where": "left", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:in_water" + ] + }, + "then": { + "translate": "controlify.guide.ingame.swim_up" + } + }, + { + "for": "controlify:jump", + "where": "left", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:on_climbable" + ] + }, + "then": { + "translate": "controlify.guide.ingame.climb_up" + } + }, + { + "for": "controlify:jump", + "where": "left", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:on_ground" + ] + }, + "then": { + "translate": "key.jump" + } + }, + { + "for": "controlify:jump", + "where": "left", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:can_elytra_fly" + ] + }, + "then": { + "translate": "controlify.guide.ingame.start_elytra" + } + }, + { + "for": "controlify:sneak", + "where": "left", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:in_vehicle" + ] + }, + "then": { + "translate": "controlify.guide.ingame.dismount" + } + }, + { + "for": "controlify:sneak", + "where": "left", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:creative_flying" + ] + }, + "then": { + "translate": "controlify.guide.ingame.fly_down" + } + }, + { + "for": "controlify:sneak", + "where": "left", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:in_water" + ], + "none_of": [ + "controlify:on_ground" + ] + }, + "then": { + "translate": "controlify.guide.ingame.swim_down" + } + }, + { + "for": "controlify:sneak", + "where": "left", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:on_climbable" + ] + }, + "then": { + "translate": "controlify.guide.ingame.climb_down" + } + }, + { + "for": "controlify:inventory", + "where": "right", + "if": "controlify:verbosity_reduced", + "then": { + "translate": "controlify.guide.ingame.inventory" + } + }, + { + "for": "controlify:attack", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_entity", + "controlify:is_spectator" + ] + }, + "then": { + "translate": "controlify.guide.ingame.spectate" + } + }, + { + "for": "controlify:attack", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_entity" + ], + "none_of": [ + "controlify:is_spectator" + ] + }, + "then": { + "translate": "controlify.guide.ingame.attack" + } + }, + { + "for": "controlify:attack", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_block", + "controlify:main_hand_is_tool" + ] + }, + "then": { + "translate": "controlify.guide.ingame.mine" + } + }, + { + "for": "controlify:attack", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_block" + ] + }, + "then": { + "translate": "controlify.guide.ingame.break" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_entity", + "controlify:is_spectator" + ] + }, + "then": { + "translate": "controlify.guide.ingame.spectate" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_merchant" + ] + }, + "then": { + "translate": "controlify.guide.ingame.trade" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_chest_boat", + "controlify:sneaking" + ] + }, + "then": { + "translate": "controlify.guide.ingame.open" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_container_minecart" + ] + }, + "then": { + "translate": "controlify.guide.ingame.open" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_rideable" + ], + "none_of": [ + "controlify:sneaking" + ] + }, + "then": { + "translate": "controlify.guide.ingame.ride" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_closed_openable" + ], + "none_of": [ + "controlify:sneaking" + ] + }, + "then": { + "translate": "controlify.guide.ingame.open" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_open_openable" + ], + "none_of": [ + "controlify:sneaking" + ] + }, + "then": { + "translate": "controlify.guide.ingame.close" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_container" + ], + "none_of": [ + "controlify:sneaking" + ] + }, + "then": { + "translate": "controlify.guide.ingame.open" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_button" + ] + }, + "then": { + "translate": "controlify.guide.ingame.press" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_lever" + ] + }, + "then": { + "translate": "controlify.guide.ingame.toggle" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_bell" + ] + }, + "then": { + "translate": "controlify.guide.ingame.ring" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_bed" + ], + "none_of": [ + "controlify:sneaking" + ] + }, + "then": { + "translate": "controlify.guide.ingame.sleep" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_crafting_table" + ], + "none_of": [ + "controlify:sneaking" + ] + }, + "then": { + "translate": "controlify.guide.ingame.craft" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_furnace" + ], + "none_of": [ + "controlify:sneaking" + ] + }, + "then": { + "translate": "controlify.guide.ingame.smelt" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_smoker" + ], + "none_of": [ + "controlify:sneaking" + ] + }, + "then": { + "translate": "controlify.guide.ingame.cook" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_brewing_stand" + ], + "none_of": [ + "controlify:sneaking" + ] + }, + "then": { + "translate": "controlify.guide.ingame.brew" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_enchanting_table" + ], + "none_of": [ + "controlify:sneaking" + ] + }, + "then": { + "translate": "controlify.guide.ingame.enchant" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_smithing_table" + ], + "none_of": [ + "controlify:sneaking" + ] + }, + "then": { + "translate": "controlify.guide.ingame.smith" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_repair_station" + ], + "none_of": [ + "controlify:sneaking" + ] + }, + "then": { + "translate": "controlify.guide.ingame.repair" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_stonecutter" + ], + "none_of": [ + "controlify:sneaking" + ] + }, + "then": { + "translate": "controlify.guide.ingame.cut_stone" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_loom" + ], + "none_of": [ + "controlify:sneaking" + ] + }, + "then": { + "translate": "controlify.guide.ingame.weave" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_cartography_table" + ], + "none_of": [ + "controlify:sneaking" + ] + }, + "then": { + "translate": "controlify.guide.ingame.edit_map" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:main_hand", + "item": { + "items": "#c:tools/bow" + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.shoot" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:main_hand", + "item": { + "predicates": { + "minecraft:charged_projectiles": {} + } + } + } + ], + "none_of": [ + { + "slot": "controlify:main_hand", + "item": { + "components": { + "minecraft:charged_projectiles": [] + } + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.shoot" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:main_hand", + "item": { + "items": "minecraft:crossbow", + "components": { + "minecraft:charged_projectiles": [] + } + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.load" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:main_hand", + "item": { + "predicates": { + "minecraft:blocks_attacks": {} + } + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.block" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:main_hand", + "item": { + "predicates": { + "minecraft:consumable": {} + } + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.consume" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "any_of": [ + { + "slot": "controlify:main_hand", + "item": { + "items": "#c:tools/trident" + } + }, + { + "slot": "controlify:main_hand", + "item": { + "items": "#minecraft:spears" + } + }, + { + "slot": "controlify:main_hand", + "item": { + "items": "#minecraft:eggs" + } + }, + { + "slot": "controlify:main_hand", + "item": { + "items": [ + "minecraft:snowball", + "minecraft:splash_potion", + "minecraft:lingering_potion", + "minecraft:experience_bottle", + "minecraft:ender_pearl", + "minecraft:wind_charge" + ] + } + } + ] + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.throw" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:fishing_hook_cast", + { + "slot": "controlify:main_hand", + "item": { + "items": "#c:tools/fishing_rod" + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.reel_in" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:main_hand", + "item": { + "items": "#c:tools/fishing_rod" + } + } + ], + "none_of": [ + "controlify:fishing_hook_cast" + ] + }, + "then": { + "translate": "controlify.guide.ingame.cast" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:main_hand", + "item": { + "items": "#c:tools/brush" + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.brush" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:main_hand", + "item": { + "items": "minecraft:spyglass" + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.zoom" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:main_hand", + "item": { + "predicates": { + "minecraft:instrument": {} + } + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.play" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:main_hand", + "item": { + "predicates": { + "minecraft:equippable": {} + } + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.equip" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_block", + "controlify:main_hand_is_block_item" + ] + }, + "then": { + "translate": "controlify.guide.ingame.place" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:off_hand", + "item": { + "items": "#c:tools/bow" + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.shoot" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:off_hand", + "item": { + "predicates": { + "minecraft:charged_projectiles": {} + } + } + } + ], + "none_of": [ + { + "slot": "controlify:off_hand", + "item": { + "components": { + "minecraft:charged_projectiles": [] + } + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.shoot" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:off_hand", + "item": { + "items": "minecraft:crossbow", + "components": { + "minecraft:charged_projectiles": [] + } + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.load" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:off_hand", + "item": { + "predicates": { + "minecraft:blocks_attacks": {} + } + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.block" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:off_hand", + "item": { + "predicates": { + "minecraft:consumable": {} + } + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.consume" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "any_of": [ + { + "slot": "controlify:off_hand", + "item": { + "items": "#c:tools/trident" + } + }, + { + "slot": "controlify:off_hand", + "item": { + "items": "#minecraft:spears" + } + }, + { + "slot": "controlify:off_hand", + "item": { + "items": "#minecraft:eggs" + } + }, + { + "slot": "controlify:off_hand", + "item": { + "items": [ + "minecraft:snowball", + "minecraft:splash_potion", + "minecraft:lingering_potion", + "minecraft:experience_bottle", + "minecraft:ender_pearl", + "minecraft:wind_charge" + ] + } + } + ] + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.throw" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:fishing_hook_cast", + { + "slot": "controlify:off_hand", + "item": { + "items": "#c:tools/fishing_rod" + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.reel_in" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:off_hand", + "item": { + "items": "#c:tools/fishing_rod" + } + } + ], + "none_of": [ + "controlify:fishing_hook_cast" + ] + }, + "then": { + "translate": "controlify.guide.ingame.cast" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:off_hand", + "item": { + "items": "#c:tools/brush" + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.brush" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:off_hand", + "item": { + "items": "minecraft:spyglass" + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.zoom" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:off_hand", + "item": { + "predicates": { + "minecraft:instrument": {} + } + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.play" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + { + "slot": "controlify:off_hand", + "item": { + "predicates": { + "minecraft:equippable": {} + } + } + } + ] + }, + "then": { + "translate": "controlify.guide.ingame.equip" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_block", + "controlify:off_hand_is_block_item" + ] + }, + "then": { + "translate": "controlify.guide.ingame.place" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_entity" + ], + "none_of": [ + "controlify:is_spectator" + ] + }, + "then": { + "translate": "controlify.guide.ingame.interact" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:looking_at_block" + ] + }, + "then": { + "translate": "controlify.guide.ingame.use" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": { + "all_of": [ + "controlify:verbosity_minimal", + "controlify:has_item_in_either_hand" + ] + }, + "then": { + "translate": "controlify.guide.ingame.use" + } + }, + { + "for": "controlify:drop", + "where": "left", + "if": { + "all_of": [ + "controlify:verbosity_reduced", + "controlify:has_item_in_main_hand" + ] + }, + "then": { + "translate": "controlify.guide.ingame.drop" + } + } + ] +} diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/ControlifyBootstrap.java b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/ControlifyBootstrap.java similarity index 85% rename from src/neoforge/java/dev/isxander/controlify/neoforge/ControlifyBootstrap.java rename to src/neoforge/java/dev/isxander/controlify/neoforge/impl/ControlifyBootstrap.java index a90e1c441..e6bbafe73 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/ControlifyBootstrap.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/ControlifyBootstrap.java @@ -4,12 +4,12 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.neoforge; +package dev.isxander.controlify.neoforge.impl; import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.gui.screen.ControlifySettingsScreen; -import dev.isxander.controlify.neoforge.platform.NeoforgePlatformMainImpl; -import dev.isxander.controlify.neoforge.platform.client.NeoforgePlatformClientImpl; +import dev.isxander.controlify.neoforge.impl.platform.NeoforgePlatformMainImpl; +import dev.isxander.controlify.neoforge.impl.platform.client.NeoforgePlatformClientImpl; import dev.isxander.controlify.impl.platform.Environment; import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/compatibility/NeoforgeCompatMixinPlatform.java b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/compatibility/NeoforgeCompatMixinPlatform.java similarity index 88% rename from src/neoforge/java/dev/isxander/controlify/neoforge/compatibility/NeoforgeCompatMixinPlatform.java rename to src/neoforge/java/dev/isxander/controlify/neoforge/impl/compatibility/NeoforgeCompatMixinPlatform.java index 526a883f0..cbd96bf63 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/compatibility/NeoforgeCompatMixinPlatform.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/compatibility/NeoforgeCompatMixinPlatform.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.neoforge.compatibility; +package dev.isxander.controlify.neoforge.impl.compatibility; import dev.isxander.controlify.impl.compatibility.CompatMixinPlatform; import net.neoforged.fml.loading.FMLLoader; diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/NeoforgePlatformMainImpl.java b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/NeoforgePlatformMainImpl.java similarity index 95% rename from src/neoforge/java/dev/isxander/controlify/neoforge/platform/NeoforgePlatformMainImpl.java rename to src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/NeoforgePlatformMainImpl.java index 9621975bd..89c5683b4 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/NeoforgePlatformMainImpl.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/NeoforgePlatformMainImpl.java @@ -4,11 +4,11 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.neoforge.platform; +package dev.isxander.controlify.neoforge.impl.platform; import dev.isxander.controlify.impl.api.entrypoint.ControlifyEntrypoint; -import dev.isxander.controlify.neoforge.platform.network.C2SNetworkApiNeoforge; -import dev.isxander.controlify.neoforge.platform.network.S2CNetworkApiNeoforge; +import dev.isxander.controlify.neoforge.impl.platform.network.C2SNetworkApiNeoforge; +import dev.isxander.controlify.neoforge.impl.platform.network.S2CNetworkApiNeoforge; import dev.isxander.controlify.impl.platform.Environment; import dev.isxander.controlify.impl.platform.main.PlatformMainUtilImpl; import dev.isxander.controlify.impl.platform.main.events.CommandRegistrationCallbackEvent; diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/VanillaKeyMappingHolder.java b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/VanillaKeyMappingHolder.java similarity index 81% rename from src/neoforge/java/dev/isxander/controlify/neoforge/platform/VanillaKeyMappingHolder.java rename to src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/VanillaKeyMappingHolder.java index accd824a7..f4b0c0799 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/VanillaKeyMappingHolder.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/VanillaKeyMappingHolder.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.neoforge.platform; +package dev.isxander.controlify.neoforge.impl.platform; import net.minecraft.client.KeyMapping; diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/client/NeoforgeCreativeTabHelper.java b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/client/NeoforgeCreativeTabHelper.java similarity index 95% rename from src/neoforge/java/dev/isxander/controlify/neoforge/platform/client/NeoforgeCreativeTabHelper.java rename to src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/client/NeoforgeCreativeTabHelper.java index ee0070999..187967041 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/client/NeoforgeCreativeTabHelper.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/client/NeoforgeCreativeTabHelper.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.neoforge.platform.client; +package dev.isxander.controlify.neoforge.impl.platform.client; import dev.isxander.controlify.neoforge.mixins.CreativeModeInventoryScreenAccessor; import dev.isxander.controlify.impl.platform.client.CreativeTabHelper; diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/client/NeoforgePlatformClientImpl.java b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/client/NeoforgePlatformClientImpl.java similarity index 97% rename from src/neoforge/java/dev/isxander/controlify/neoforge/platform/client/NeoforgePlatformClientImpl.java rename to src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/client/NeoforgePlatformClientImpl.java index 28c1e0ae2..0720148c3 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/client/NeoforgePlatformClientImpl.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/client/NeoforgePlatformClientImpl.java @@ -4,9 +4,9 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.neoforge.platform.client; +package dev.isxander.controlify.neoforge.impl.platform.client; -import dev.isxander.controlify.neoforge.platform.VanillaKeyMappingHolder; +import dev.isxander.controlify.neoforge.impl.platform.VanillaKeyMappingHolder; import dev.isxander.controlify.impl.platform.client.CreativeTabHelper; import dev.isxander.controlify.impl.platform.client.HudRenderLayer; import dev.isxander.controlify.impl.platform.client.PlatformClientUtilImpl; diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/network/C2SNetworkApiNeoforge.java b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/network/C2SNetworkApiNeoforge.java similarity index 97% rename from src/neoforge/java/dev/isxander/controlify/neoforge/platform/network/C2SNetworkApiNeoforge.java rename to src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/network/C2SNetworkApiNeoforge.java index 4183116ec..5d2468cef 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/network/C2SNetworkApiNeoforge.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/network/C2SNetworkApiNeoforge.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.neoforge.platform.network; +package dev.isxander.controlify.neoforge.impl.platform.network; import dev.isxander.controlify.impl.platform.network.C2SNetworkApi; import net.minecraft.network.FriendlyByteBuf; diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/network/NeoforgePacketWrapper.java b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/network/NeoforgePacketWrapper.java similarity index 94% rename from src/neoforge/java/dev/isxander/controlify/neoforge/platform/network/NeoforgePacketWrapper.java rename to src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/network/NeoforgePacketWrapper.java index 40259d655..1b9812903 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/network/NeoforgePacketWrapper.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/network/NeoforgePacketWrapper.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.neoforge.platform.network; +package dev.isxander.controlify.neoforge.impl.platform.network; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/network/S2CNetworkApiNeoforge.java b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/network/S2CNetworkApiNeoforge.java similarity index 97% rename from src/neoforge/java/dev/isxander/controlify/neoforge/platform/network/S2CNetworkApiNeoforge.java rename to src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/network/S2CNetworkApiNeoforge.java index 1bf206584..ef0584c6b 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/platform/network/S2CNetworkApiNeoforge.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/network/S2CNetworkApiNeoforge.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.neoforge.platform.network; +package dev.isxander.controlify.neoforge.impl.platform.network; import dev.isxander.controlify.impl.platform.Environment; import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/mixins/OptionsMixin.java b/src/neoforge/java/dev/isxander/controlify/neoforge/mixins/OptionsMixin.java index f2a1c493d..b7f707481 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/mixins/OptionsMixin.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/mixins/OptionsMixin.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.neoforge.mixins; -import dev.isxander.controlify.neoforge.platform.VanillaKeyMappingHolder; +import dev.isxander.controlify.neoforge.impl.platform.VanillaKeyMappingHolder; import net.minecraft.client.KeyMapping; import net.minecraft.client.Options; import org.spongepowered.asm.mixin.Mixin; From ce182b5068e1517012351667a8d45b122e052bef Mon Sep 17 00:00:00 2001 From: isxander Date: Thu, 22 Oct 2026 02:41:00 +0100 Subject: [PATCH 13/44] it builds! it runs! --- .../fabric/impl/ControlifyBootstrap.java | 1 + .../impl/platform/FabricPlatformMainImpl.java | 29 +- .../gametest/tests/ButtonGuideTests.java | 6 +- .../tests/VirtualControllerInputTests.java | 4 +- .../controlify/api/ControlifyApi.java | 61 +-- .../controlify/api/ControlifyEntrypoint.java | 10 + .../controlify/api/ControlifyEvents.java | 76 +-- .../controlify/api/action/ActionAccessor.java | 35 ++ .../api/action/ActionContextRegistry.java | 17 + .../controlify/api/action/ActionEvents.java | 24 + .../api/action/ActionProxyManager.java | 26 + .../controlify/api/action/ActionSpec.java | 9 +- .../action/ActionSpecBuilder.java | 8 +- .../api/action/ActionSpecRegistry.java | 7 + .../api/action/BuiltinActionContexts.java | 21 + .../contextual/BuiltinContextualDomains.java | 14 + .../api/contextual/ContainerContext.java | 4 +- .../contextual/ContextualDomainRegistry.java | 10 +- .../api/contextual/ContextualEvents.java | 13 + .../controlify/api/controller/Controller.java | 8 + .../api/controller/gyro/GyroComponent.java | 11 + .../api/controller/gyro/package-info.java | 4 + .../api/controller/input/InputComponent.java | 13 + .../api/controller/input/package-info.java | 4 + .../controllermanager/ControllerEvents.java | 29 ++ .../api/controllermanager/package-info.java | 4 + .../controlify/api/font/ActionGlyphs.java | 68 +++ .../controlify/api/font/package-info.java | 5 + .../controlify/api/gui/ButtonGuideApi.java | 6 +- .../controlify/api/ingame/InGameEvents.java | 11 + .../api/ingame/LookInputModifier.java | 28 + .../controlify/api/ingame/package-info.java | 4 + .../controlify/api/input/LogicalInput.java | 11 + .../screenop}/ISnapBehaviour.java | 2 +- .../screenop}/SnapPoint.java | 2 +- .../{impl => api}/utils/event/EventSink.java | 2 +- .../utils/event/EventSource.java | 6 +- .../{impl => api}/utils/event/EventStage.java | 2 +- .../utils/event/UnaryEventStage.java | 2 +- .../api/utils/event/package-info.java | 4 + .../isxander/controlify/impl/Controlify.java | 165 +++--- .../controlify/impl/action/Accumulator.java | 12 +- .../impl/action/ActionAccessor.java | 28 - .../impl/action/ActionAccessorImpl.java | 6 +- .../controlify/impl/action/ActionContext.java | 1 + .../action/ActionContextRegistryImpl.java | 90 ++++ .../controlify/impl/action/ActionGraph.java | 5 +- .../controlify/impl/action/ActionImpl.java | 7 +- .../impl/action/ActionSpecBuilderImpl.java | 18 +- .../impl/action/ActionSpecRegistry.java | 7 - .../impl/action/ActionSpecRegistryImpl.java | 26 +- .../impl/action/ControlifyActions.java | 467 +++++++++++++++++ .../impl/action/KeyMappingHandle.java | 8 + .../activator/ActivatorConfigManager.java | 22 +- .../activator/RepeatLatchPulseActivator.java | 6 +- .../action/defaults/DefaultBindManager.java | 31 +- .../MapBackedDefaultBindProvider.java | 11 +- .../action/proxy/ActionProxyManagerImpl.java | 93 ++++ .../impl/action/proxy/KeyMappingProxy.java | 79 +++ .../impl/action/radial/RadialIconManager.java | 4 +- .../api/entrypoint/ControlifyEntrypoint.java | 34 -- .../impl/api/entrypoint/InitContext.java | 14 - .../impl/api/entrypoint/PreInitContext.java | 16 - .../api/ingameinput/LookInputModifier.java | 48 -- .../controlify/impl/bindings/BindContext.java | 76 --- .../impl/bindings/ControlifyBindings.java | 484 ------------------ .../impl/bindings/KeyMappingHandle.java | 17 - .../impl/compatibility/CompatMixinPlugin.java | 4 +- .../SimpleVoiceChatCompat.java | 169 +++--- .../sodium/mixins/FlatButtonWidgetMixin.java | 18 +- .../screenop/CycleControlProcessor.java | 8 +- .../screenop/SodiumGuiScreenProcessor.java | 16 +- .../screenop/TickBoxControlProcessor.java | 6 +- .../mixins/YACLScreenCategoryTabMixin.java | 4 +- ...ngControllerElementComponentProcessor.java | 21 +- .../OptionListWidgetComponentProcessor.java | 14 +- ...erControllerElementComponentProcessor.java | 26 +- .../yacl/screenop/YACLScreenProcessor.java | 9 +- .../defaults/DefaultConfigManager.java | 12 +- .../impl/contextual/ContextualDomains.java | 25 +- .../controlify/impl/contextual/GuideRule.java | 10 +- .../impl/contextual/TriggerEffectRule.java | 12 +- .../impl/controller/ControllerImpl.java | 31 +- .../dualsense/TriggerEffectInstanceImpl.java | 36 +- .../dualsense/TriggerEffectManager.java | 4 +- ...oComponent.java => GyroComponentImpl.java} | 15 +- ...Component.java => InputComponentImpl.java} | 119 +++-- .../misc/BluetoothDeviceComponent.java | 1 - .../impl/controller/misc/GUIDComponent.java | 19 - .../controllermanager/ControllerBackend.java | 5 + .../ControllerCoordinator.java | 116 +++-- .../controllermanager/ControllerManager.java | 22 +- .../controllermanager/ControllerType.java | 12 +- .../ControllerTypeManager.java | 38 +- .../SDLControllerBackend.java | 48 +- .../impl/driver/CompoundDriver.java | 2 +- .../impl/driver/sdl/SDL3GamepadDriver.java | 36 +- .../impl/driver/sdl/SDL3JoystickDriver.java | 14 +- .../impl/driver/sdl/SDLCommonDriver.java | 22 +- .../driver/sdl/SDLControllerEventStage.java | 7 +- .../driver/sdl/SDLGamepadInputMapping.java | 52 -- .../impl/driver/sdl/SDLToPhysicalStage.java | 7 +- .../driver/steamdeck/SteamDeckDriver.java | 147 +++--- .../impl/font/ActionGlyphsImpl.java | 33 ++ .../impl/font/BindingFontHelper.java | 72 --- .../impl/font/ComponentFontHelper.java | 42 ++ .../controlify/impl/font/InputFontMapper.java | 69 +-- .../impl/gui/ButtonGuideRenderer.java | 11 +- .../impl/gui/controllers/BindController.java | 66 +-- .../controllers/Deadzone2DImageRenderer.java | 38 +- .../impl/gui/guide/GuideInstanceImpl.java | 23 +- .../gui/screen/AskToMapControllerScreen.java | 2 +- .../impl/gui/screen/BindConsumerScreen.java | 86 +++- .../gui/screen/ControlifySettingsScreen.java | 25 +- .../screen/ControllerConfigScreenFactory.java | 168 +++--- .../screen/ControllerMappingMakerScreen.java | 204 +++++--- .../screen/GlobalSettingsScreenFactory.java | 3 +- .../gui/screen/ProfileSelectionScreen.java | 20 +- .../impl/gui/screen/RadialItems.java | 64 +-- .../impl/gui/screen/RadialMenuEditScreen.java | 84 +-- .../impl/gui/screen/RadialMenuScreen.java | 73 ++- .../impl/ingame/ControllerPlayerMovement.java | 43 +- .../impl/ingame/InGameInputHandler.java | 215 ++++---- .../mapping/ControllerMappingManager.java | 6 + .../impl/input/pipeline/DeadzoneStage.java | 19 +- .../pipeline/GestureRecogniserStage.java | 7 +- .../pipeline/InputDomainExpansionStage.java | 11 +- .../impl/input/pipeline/InputPipeline.java | 16 +- .../impl/input/pipeline/MappingStage.java | 7 +- .../pipeline/PhysicalToLogicalStage.java | 15 +- .../pipeline/SensorAccumulatorStage.java | 9 +- .../input/pipeline/SensorProcessorStage.java | 12 +- .../SimpleControlifyReloadListener.java | 13 +- .../impl/platform/main/PlatformMainUtil.java | 25 +- .../platform/main/PlatformMainUtilImpl.java | 2 +- .../impl/screenop/ScreenProcessor.java | 125 ++--- .../AbstractSliderComponentProcessor.java | 26 +- .../AbstractButtonComponentProcessor.java | 8 +- .../AbstractContainerScreenProcessor.java | 31 +- .../AbstractSignEditScreenProcessor.java | 20 +- .../vanilla/AddServerLikeScreenProcessor.java | 10 +- .../BundleItemSlotControllerAction.java | 26 +- .../compat/vanilla/ChatScreenProcessor.java | 34 +- .../vanilla/CreateWorldScreenProcessor.java | 4 +- .../CreativeModeInventoryScreenProcessor.java | 12 +- .../JoinMultiplayerScreenProcessor.java | 14 +- ...nguageSelectionListComponentProcessor.java | 8 +- .../vanilla/OptionsScreenProcessor.java | 4 +- .../vanilla/OptionsSubScreenProcessor.java | 4 +- .../compat/vanilla/PauseScreenProcessor.java | 18 +- .../vanilla/RecipeBookScreenProcessor.java | 26 +- .../vanilla/SelectWorldScreenProcessor.java | 8 +- ...rSelectionListEntryComponentProcessor.java | 6 +- .../vanilla/SliderComponentProcessor.java | 33 +- .../compat/vanilla/TitleScreenProcessor.java | 12 +- .../WorldListEntryComponentProcessor.java | 6 +- .../keyboard/CommonKeyboardHints.java | 12 +- .../keyboard/FallbackKeyboardLayout.java | 17 +- .../impl/screenop/keyboard/KeyWidget.java | 69 ++- .../screenop/keyboard/KeyboardLayout.java | 10 +- .../keyboard/KeyboardLayoutManager.java | 15 +- .../screenop/keyboard/KeyboardWidget.java | 65 ++- .../impl/utils/ControllerUtils.java | 15 +- .../controlify/impl/utils/DebugDump.java | 69 +-- .../impl/utils/HoldRepeatHelper.java | 57 +-- .../impl/utils/LazyComponentDims.java | 4 +- .../impl/utils/PrecomputedComponentDims.java | 6 +- .../impl/utils/ResizableRingBuffer.java | 82 --- .../impl/utils/TrackingBodySubscriber.java | 66 --- .../impl/utils/TrackingConsumer.java | 14 - .../utils/UnhandledCompletableFutures.java | 41 -- .../controlify/impl/utils/event/EventBus.java | 41 ++ .../controlify/impl/utils/event/EventTap.java | 34 +- .../impl/virtualmouse/SnapUtils.java | 2 +- .../virtualmouse/VirtualMouseHandler.java | 115 +++-- .../controlify/mixins/core/GuiMixin.java | 3 +- .../mixins/core/MouseHandlerMixin.java | 5 +- .../accessibility/LocalPlayerMixin.java | 7 +- .../mixins/feature/bind/GuiMixin.java | 14 +- .../mixins/feature/bind/KeyMappingMixin.java | 30 +- .../feature/bind/ToggleKeyMappingMixin.java | 52 -- .../feature/font/KeybindContentsMixin.java | 16 +- .../font/TranslatableContentsMixin.java | 10 +- .../guide/screen/AbstractButtonMixin.java | 15 +- .../guide/screen/TabNavigationBarMixin.java | 11 +- .../analogueboat/LocalPlayerMixin.java | 3 +- .../ClientPacketListenerMixin.java | 29 +- .../MovementTutorialStepInstanceMixin.java | 14 +- .../OpenInventoryTutorialStepMixin.java | 6 +- .../PunchTreeTutorialStepInstanceMixin.java | 6 +- .../mixins/feature/rumble/MinecraftMixin.java | 5 +- .../blockbreak/MultiPlayerGameModeMixin.java | 5 +- .../damage/ClientPacketListenerMixin.java | 7 +- .../explosion/ClientPacketListenerMixin.java | 5 +- .../rumble/explosion/LightningBoltMixin.java | 5 +- .../rumble/fishing/FishingHookMixin.java | 5 +- .../rumble/itembreak/LocalPlayerMixin.java | 5 +- .../levelevents/LevelEventHandlerMixin.java | 9 +- .../rumble/slowblock/LocalPlayerMixin.java | 5 +- .../rumble/useitem/LocalPlayerMixin.java | 5 +- .../rumble/waterland/LocalPlayerMixin.java | 5 +- .../screenop/impl/chat/ChatScreenMixin.java | 7 +- .../screenop/impl/elements/EditBoxMixin.java | 11 +- .../outofgame/CreateWorldScreenMixin.java | 6 +- .../outofgame/SelectWorldScreenMixin.java | 6 +- .../sign/AbstractSignEditScreenMixin.java | 7 +- .../virtualmouse/InputConstantsMixin.java | 10 +- .../AbstractContainerScreenMixin.java | 4 +- .../AbstractRecipeBookScreenMixin.java | 2 +- .../CreativeModeInventoryScreenMixin.java | 2 +- .../snapping/EnchantmentScreenMixin.java | 4 +- .../snapping/LoomScreenMixin.java | 4 +- .../virtualmouse/snapping/ScreenMixin.java | 4 +- .../snapping/StonecutterScreenMixin.java | 4 +- .../controlify/controller/activators.json | 261 +++++++++- .../controller/contextual/guide/in_game.json | 4 +- .../controller_identification.json5 | 42 +- .../controller/default_bind/default.json | 280 +++------- .../controller/default_bind/dualsense.json | 4 +- .../controller/default_bind/switch.json | 4 +- .../controlify/controller/default_config.json | 3 +- .../assets/controlify/lang/de_de.json | 104 ++-- .../assets/controlify/lang/en_us.json | 212 ++++---- .../assets/controlify/lang/es_es.json | 192 +++---- .../assets/controlify/lang/es_mx.json | 192 +++---- .../assets/controlify/lang/fi_fi.json | 4 +- .../assets/controlify/lang/fr_fr.json | 144 +++--- .../assets/controlify/lang/hu_hu.json | 132 ++--- .../assets/controlify/lang/it_it.json | 182 +++---- .../assets/controlify/lang/ja_jp.json | 184 +++---- .../assets/controlify/lang/ms_my.json | 184 +++---- .../assets/controlify/lang/pl_pl.json | 172 +++---- .../assets/controlify/lang/pt_br.json | 68 +-- .../assets/controlify/lang/pt_pt.json | 184 +++---- .../assets/controlify/lang/ro_ro.json | 34 +- .../assets/controlify/lang/ru_ru.json | 136 ++--- .../assets/controlify/lang/tr_tr.json | 150 +++--- .../assets/controlify/lang/uk_ua.json | 124 ++--- .../assets/controlify/lang/vi_vn.json | 192 +++---- .../assets/controlify/lang/zh_cn.json | 210 ++++---- .../assets/controlify/lang/zh_tw.json | 184 +++---- src/main/resources/controlify.mixins.json | 1 - .../neoforge/impl/ControlifyBootstrap.java | 1 + .../platform/NeoforgePlatformMainImpl.java | 11 +- .../client/NeoforgePlatformClientImpl.java | 2 +- 245 files changed, 5282 insertions(+), 4803 deletions(-) create mode 100644 src/main/java/dev/isxander/controlify/api/ControlifyEntrypoint.java create mode 100644 src/main/java/dev/isxander/controlify/api/action/ActionAccessor.java create mode 100644 src/main/java/dev/isxander/controlify/api/action/ActionContextRegistry.java create mode 100644 src/main/java/dev/isxander/controlify/api/action/ActionEvents.java create mode 100644 src/main/java/dev/isxander/controlify/api/action/ActionProxyManager.java rename src/main/java/dev/isxander/controlify/{impl => api}/action/ActionSpecBuilder.java (59%) create mode 100644 src/main/java/dev/isxander/controlify/api/action/ActionSpecRegistry.java create mode 100644 src/main/java/dev/isxander/controlify/api/action/BuiltinActionContexts.java create mode 100644 src/main/java/dev/isxander/controlify/api/contextual/BuiltinContextualDomains.java create mode 100644 src/main/java/dev/isxander/controlify/api/contextual/ContextualEvents.java create mode 100644 src/main/java/dev/isxander/controlify/api/controller/gyro/GyroComponent.java create mode 100644 src/main/java/dev/isxander/controlify/api/controller/gyro/package-info.java create mode 100644 src/main/java/dev/isxander/controlify/api/controller/input/InputComponent.java create mode 100644 src/main/java/dev/isxander/controlify/api/controller/input/package-info.java create mode 100644 src/main/java/dev/isxander/controlify/api/controllermanager/ControllerEvents.java create mode 100644 src/main/java/dev/isxander/controlify/api/controllermanager/package-info.java create mode 100644 src/main/java/dev/isxander/controlify/api/font/ActionGlyphs.java create mode 100644 src/main/java/dev/isxander/controlify/api/font/package-info.java create mode 100644 src/main/java/dev/isxander/controlify/api/ingame/InGameEvents.java create mode 100644 src/main/java/dev/isxander/controlify/api/ingame/LookInputModifier.java create mode 100644 src/main/java/dev/isxander/controlify/api/ingame/package-info.java rename src/main/java/dev/isxander/controlify/{impl/api/vmousesnapping => api/screenop}/ISnapBehaviour.java (91%) rename src/main/java/dev/isxander/controlify/{impl/api/vmousesnapping => api/screenop}/SnapPoint.java (90%) rename src/main/java/dev/isxander/controlify/{impl => api}/utils/event/EventSink.java (61%) rename src/main/java/dev/isxander/controlify/{impl => api}/utils/event/EventSource.java (59%) rename src/main/java/dev/isxander/controlify/{impl => api}/utils/event/EventStage.java (94%) rename src/main/java/dev/isxander/controlify/{impl => api}/utils/event/UnaryEventStage.java (63%) create mode 100644 src/main/java/dev/isxander/controlify/api/utils/event/package-info.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/action/ActionAccessor.java create mode 100644 src/main/java/dev/isxander/controlify/impl/action/ActionContextRegistryImpl.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/action/ActionSpecRegistry.java create mode 100644 src/main/java/dev/isxander/controlify/impl/action/ControlifyActions.java create mode 100644 src/main/java/dev/isxander/controlify/impl/action/KeyMappingHandle.java create mode 100644 src/main/java/dev/isxander/controlify/impl/action/proxy/ActionProxyManagerImpl.java create mode 100644 src/main/java/dev/isxander/controlify/impl/action/proxy/KeyMappingProxy.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/api/entrypoint/ControlifyEntrypoint.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/api/entrypoint/InitContext.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/api/entrypoint/PreInitContext.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/api/ingameinput/LookInputModifier.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/bindings/BindContext.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/bindings/ControlifyBindings.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/bindings/KeyMappingHandle.java rename src/main/java/dev/isxander/controlify/impl/controller/gyro/{GyroComponent.java => GyroComponentImpl.java} (88%) rename src/main/java/dev/isxander/controlify/impl/controller/input/{InputComponent.java => InputComponentImpl.java} (72%) delete mode 100644 src/main/java/dev/isxander/controlify/impl/controller/misc/GUIDComponent.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLGamepadInputMapping.java create mode 100644 src/main/java/dev/isxander/controlify/impl/font/ActionGlyphsImpl.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/font/BindingFontHelper.java create mode 100644 src/main/java/dev/isxander/controlify/impl/font/ComponentFontHelper.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/utils/ResizableRingBuffer.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/utils/TrackingBodySubscriber.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/utils/TrackingConsumer.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/utils/UnhandledCompletableFutures.java create mode 100644 src/main/java/dev/isxander/controlify/impl/utils/event/EventBus.java delete mode 100644 src/main/java/dev/isxander/controlify/mixins/feature/bind/ToggleKeyMappingMixin.java diff --git a/src/fabric/java/dev/isxander/controlify/fabric/impl/ControlifyBootstrap.java b/src/fabric/java/dev/isxander/controlify/fabric/impl/ControlifyBootstrap.java index 9936cf34d..0e2be3af3 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/impl/ControlifyBootstrap.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/impl/ControlifyBootstrap.java @@ -20,6 +20,7 @@ public class ControlifyBootstrap implements ClientModInitializer, ModInitializer @Override public void onInitializeClient() { PlatformClientUtil.IMPL = new FabricPlatformClientImpl(); + PlatformMainUtil.invokeControlifyEntrypoints(); Controlify.instance().preInitialiseControlify(); } diff --git a/src/fabric/java/dev/isxander/controlify/fabric/impl/platform/FabricPlatformMainImpl.java b/src/fabric/java/dev/isxander/controlify/fabric/impl/platform/FabricPlatformMainImpl.java index 04529ffa4..cf39a1435 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/impl/platform/FabricPlatformMainImpl.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/impl/platform/FabricPlatformMainImpl.java @@ -6,7 +6,8 @@ */ package dev.isxander.controlify.fabric.impl.platform; -import dev.isxander.controlify.impl.api.entrypoint.ControlifyEntrypoint; +import dev.isxander.controlify.api.ControlifyEntrypoint; +import dev.isxander.controlify.impl.utils.CUtil; import dev.isxander.controlify.fabric.impl.platform.network.C2SNetworkApiFabric; import dev.isxander.controlify.fabric.impl.platform.network.S2CNetworkApiFabric; import dev.isxander.controlify.impl.platform.Environment; @@ -34,6 +35,8 @@ import java.util.Arrays; import java.util.Optional; import java.util.ServiceLoader; +import java.util.HashSet; +import java.util.Set; import java.util.function.Consumer; import java.util.function.Supplier; @@ -58,9 +61,27 @@ public boolean isModLoaded(String... modIds) { @Override public void applyToControlifyEntrypoint(Consumer entrypointConsumer) { - // Use both Fabric's entrypoint system and ServiceLoader to maximize compatibility - FabricLoader.getInstance().getEntrypoints("controlify", ControlifyEntrypoint.class).forEach(entrypointConsumer); - ServiceLoader.load(ControlifyEntrypoint.class).forEach(entrypointConsumer); + // Use both discovery mechanisms, without constructing duplicate service providers. + Set> loaded = new HashSet<>(); + FabricLoader.getInstance().getEntrypointContainers("controlify", ControlifyEntrypoint.class).forEach(container -> { + try { + var entrypoint = container.getEntrypoint(); + if (loaded.add(entrypoint.getClass())) { + entrypointConsumer.accept(entrypoint); + } + } catch (Throwable throwable) { + CUtil.LOGGER.error("Failed to load Controlify addon from mod: {}", container.getProvider().getMetadata().getId(), throwable); + } + }); + ServiceLoader.load(ControlifyEntrypoint.class).stream().forEach(provider -> { + try { + if (loaded.add(provider.type())) { + entrypointConsumer.accept(provider.get()); + } + } catch (Throwable throwable) { + CUtil.LOGGER.error("Failed to load Controlify addon provider: {}", provider.type().getName(), throwable); + } + }); } @Override diff --git a/src/gametest/java/dev/isxander/controlify/gametest/tests/ButtonGuideTests.java b/src/gametest/java/dev/isxander/controlify/gametest/tests/ButtonGuideTests.java index 04b87b985..d81060868 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/ButtonGuideTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/ButtonGuideTests.java @@ -76,7 +76,7 @@ public void runTest(ClientGameTestContext context) { Set expectedBindings = Set.of("jump", "sneak", "drop", "inventory", "attack", "use"); Set actualBindings = ruleEngine.rules().stream() - .map(rule -> rule.binding().bindId()) + .map(rule -> rule.binding().actionId()) .map(id -> id.getPath()) .collect(Collectors.toSet()); if (!actualBindings.equals(expectedBindings)) { @@ -108,7 +108,7 @@ private static void assertBindingRule( String bindingPath ) { var bindingId = CUtil.rl(bindingPath); - if (rules.stream().noneMatch(rule -> rule.binding().bindId().equals(bindingId))) { + if (rules.stream().noneMatch(rule -> rule.binding().actionId().equals(bindingId))) { throw new AssertionError("Expected a matching guide rule for '" + bindingId + "': " + rules); } } @@ -120,7 +120,7 @@ private static void assertRuleLocation( ) { var bindingId = CUtil.rl(bindingPath); List rules = ruleEngine.rules().stream() - .filter(rule -> rule.binding().bindId().equals(bindingId)) + .filter(rule -> rule.binding().actionId().equals(bindingId)) .toList(); if (rules.isEmpty() || rules.stream().anyMatch(rule -> rule.location() != expectedLocation)) { throw new AssertionError( 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 b6dcfde3d..c8972a983 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/VirtualControllerInputTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/VirtualControllerInputTests.java @@ -7,7 +7,7 @@ package dev.isxander.controlify.gametest.tests; import dev.isxander.controlify.impl.input.mapping.GamepadInputs; -import dev.isxander.controlify.impl.controller.input.InputComponent; +import dev.isxander.controlify.impl.controller.input.InputComponentImpl; import dev.isxander.controlify.gametest.framework.CTestUtil; import dev.isxander.controlify.gametest.framework.controller.ControlifyGameTestContext; import dev.isxander.controlify.gametest.framework.controller.TestControllerContext; @@ -102,7 +102,7 @@ public void runTest(ClientGameTestContext context) { private static void waitForButton( ClientGameTestContext context, TestControllerContext controller, - InputComponent input, + InputComponentImpl input, Map.Entry expected, boolean down ) { diff --git a/src/main/java/dev/isxander/controlify/api/ControlifyApi.java b/src/main/java/dev/isxander/controlify/api/ControlifyApi.java index 260cc610e..7f6fd17e7 100644 --- a/src/main/java/dev/isxander/controlify/api/ControlifyApi.java +++ b/src/main/java/dev/isxander/controlify/api/ControlifyApi.java @@ -1,49 +1,42 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ package dev.isxander.controlify.api; +import dev.isxander.controlify.api.contextual.BuiltinContextualDomains; import dev.isxander.controlify.api.controller.Controller; -import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.api.controller.rumble.RumbleEffect; import dev.isxander.controlify.api.controller.rumble.RumbleEffectInstance; +import dev.isxander.controlify.impl.Controlify; import net.minecraft.resources.Identifier; +import java.util.Collection; import java.util.Optional; -/** - * Interface with Controlify in a manner where you don't need to worry about updates - * breaking! This is the recommended way to interact with Controlify. - *

- * Alternatively, to use Controlify directly, you can use {@link Controlify#instance()}. Though - * beware, things may break at any time! - *

- * Anything that is asked for from this API is safe to use, even if it is not in the API package. - */ +/// Live client queries and operations. Access through {@link #get()} or event payloads; +/// consumers do not need to retain the API or reconstruct state from notifications. public interface ControlifyApi { - /** - * The controller that is currently enabled and in use. - * If there is no controller disconnected or disabled, this will return {@link Optional#empty()}. - * This is the controller that is used for {@link ControlifyEvents#ACTIVE_CONTROLLER_TICKED} - */ - Optional getCurrentController(); - - /** - * The last input received: a controller or keyboard/mouse. - */ + /// Returns the shared client API without initializing its subsystems. Action-spec and + /// contextual-domain registries must still be obtained through their registration events; INITIALIZED + /// indicates when runtime managers are ready, rather than granting access to this API. + static ControlifyApi get() { + return Controlify.instance(); + } + + Optional getCurrentController(); + + /// An unmodifiable snapshot of the currently connected controllers. + Collection getConnectedControllers(); + + Optional getControllerByUid(String uid); + + /// Access to built-in contextual domains throughout the client lifetime, without + /// exposing the new-domain registration operations supplied during REGISTER_DOMAINS. + BuiltinContextualDomains contextualDomains(); + InputMode currentInputMode(); - boolean setInputMode(InputMode mode); - default Optional playRumbleEffect(Identifier effectId, RumbleEffect rumbleEffect) { - return getCurrentController() - .flatMap(Controller::rumble) - .map(r -> r.playEffect(effectId, rumbleEffect)); - } + /// Returns whether the mode changed. Controller modes require a current controller. + boolean setInputMode(InputMode mode); - static ControlifyApi get() { - return Controlify.instance(); + default Optional playRumbleEffect(Identifier effectId, RumbleEffect effect) { + return getCurrentController().flatMap(Controller::rumble).map(rumble -> rumble.playEffect(effectId, effect)); } } diff --git a/src/main/java/dev/isxander/controlify/api/ControlifyEntrypoint.java b/src/main/java/dev/isxander/controlify/api/ControlifyEntrypoint.java new file mode 100644 index 000000000..e455a6296 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/ControlifyEntrypoint.java @@ -0,0 +1,10 @@ +package dev.isxander.controlify.api; + +/// Registers addon event listeners before Controlify starts client setup. +/// Use Fabric's `controlify` entrypoint or Java's ServiceLoader on either loader. +/// Called once per provider class, client-side, after platform services are installed. +/// Minecraft and Controlify's managers are not yet available; obtain managers from events. +@FunctionalInterface +public interface ControlifyEntrypoint { + void onControlifyInitialize(); +} diff --git a/src/main/java/dev/isxander/controlify/api/ControlifyEvents.java b/src/main/java/dev/isxander/controlify/api/ControlifyEvents.java index 87b980f47..be9fc65b6 100644 --- a/src/main/java/dev/isxander/controlify/api/ControlifyEvents.java +++ b/src/main/java/dev/isxander/controlify/api/ControlifyEvents.java @@ -1,75 +1,17 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ package dev.isxander.controlify.api; -import dev.isxander.controlify.impl.api.ingameinput.LookInputModifier; -import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.platform.EventHandler; -import org.jetbrains.annotations.Nullable; +import dev.isxander.controlify.api.utils.event.EventSource; +import dev.isxander.controlify.impl.Controlify; public final class ControlifyEvents { - public static final EventHandler CONTROLLER_CONNECTED = EventHandler.createPlatformBackedEvent(); + /// Fires once when runtime managers and queries are usable, before backend setup + /// and discovery. Queries initially return no controllers. Still fires if backend setup fails. + public static final EventSource INITIALIZED = Controlify.Events.INITIALIZED; - public static final EventHandler CONTROLLER_DISCONNECTED = EventHandler.createPlatformBackedEvent(); + /// Fires after Controlify has applied the new input mode and updated its handlers. + public static final EventSource INPUT_MODE_CHANGED = Controlify.Events.INPUT_MODE_CHANGED; - /** - * Triggers after the current controller changes and its activation has been updated. - * The controller is null when the current selection is cleared. - */ - public static final EventHandler CURRENT_CONTROLLER_CHANGED = EventHandler.createPlatformBackedEvent(); - - /** - * Triggers when the input mode is changed from keyboard to controller or vice versa. - */ - public static final EventHandler INPUT_MODE_CHANGED = EventHandler.createPlatformBackedEvent(); - - /** - * Triggers every tick when the current controller state has been updated. - */ - public static final EventHandler ACTIVE_CONTROLLER_TICKED = EventHandler.createPlatformBackedEvent(); - - /** - * @deprecated Use {@link #ACTIVE_CONTROLLER_TICKED} instead. - */ - @Deprecated - public static final EventHandler CONTROLLER_STATE_UPDATED = ACTIVE_CONTROLLER_TICKED; - - /** - * Triggers every tick when any connected controller's state has been updated before the active controller is ticked. - */ - public static final EventHandler CONTROLLER_STATE_UPDATE = EventHandler.createPlatformBackedEvent(); - - /** - * Triggers in a GUI when the virtual mouse is toggled on or off. - */ - public static final EventHandler VIRTUAL_MOUSE_TOGGLED = EventHandler.createPlatformBackedEvent(); - - /** - * Allows you to modify the look input before it is applied to the player. - * These modifiers are called before the look input is multiplied by the sensitivity. - */ - public static final EventHandler LOOK_INPUT_MODIFIER = EventHandler.createPlatformBackedEvent(); - - public record ControllerConnected(ControllerImpl controller, boolean hotplugged) { - } - - public record ControllerDisconnected(ControllerImpl controller) { - } - - public record CurrentControllerChanged(@Nullable ControllerImpl controller, boolean changeInputMode) { - } - - public record InputModeChanged(InputMode mode) { - } - - public record ControllerStateUpdate(ControllerImpl controller) { - } - - public record VirtualMouseToggled(boolean enabled) { - } + public record InputModeChanged(ControlifyApi controlify, InputMode mode) {} + private ControlifyEvents() {} } diff --git a/src/main/java/dev/isxander/controlify/api/action/ActionAccessor.java b/src/main/java/dev/isxander/controlify/api/action/ActionAccessor.java new file mode 100644 index 000000000..b4c9248e6 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/action/ActionAccessor.java @@ -0,0 +1,35 @@ +package dev.isxander.controlify.api.action; + +import com.mojang.serialization.Codec; +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.controller.input.InputComponent; +import dev.isxander.controlify.impl.action.ActionAccessorImpl; +import net.minecraft.resources.Identifier; +import org.jspecify.annotations.Nullable; + +/// Resolves the current action on a controller or input component. Retain this accessor; +/// profile changes may replace resolved action instances. +public interface ActionAccessor { + Codec CODEC = Identifier.CODEC.xmap( + ActionAccessorImpl::new, ActionAccessor::actionId); + + @Nullable Action onOrNull(InputComponent inputComponent); + + Identifier actionId(); + + default @Nullable Action onOrNull(Controller controller) { + return onOrNull(controller.input()); + } + + default Action on(InputComponent inputComponent) { + Action action = onOrNull(inputComponent); + if (action == null) { + throw new NullPointerException("No action bound to input component " + actionId()); + } + return action; + } + + default Action on(Controller controller) { + return on(controller.input()); + } +} diff --git a/src/main/java/dev/isxander/controlify/api/action/ActionContextRegistry.java b/src/main/java/dev/isxander/controlify/api/action/ActionContextRegistry.java new file mode 100644 index 000000000..bbd90fd5e --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/action/ActionContextRegistry.java @@ -0,0 +1,17 @@ +package dev.isxander.controlify.api.action; + +import net.minecraft.client.Minecraft; +import net.minecraft.resources.Identifier; +import org.jetbrains.annotations.Contract; + +import java.util.function.Function; + +/// Registers context predicates during {@link ActionEvents#REGISTER_CONTEXTS}. +/// Context records remain internal; actions reference their identifiers only. +public interface ActionContextRegistry { + /// Registers a predicate evaluated against the current client before controller input is processed. + /// Returns id for use in {@link ActionSpecBuilder#context(Identifier...)}. + /// Throws if the identifier already exists or context registration has closed. + @Contract("_, _ -> param1") + Identifier register(Identifier id, Function isApplicable); +} diff --git a/src/main/java/dev/isxander/controlify/api/action/ActionEvents.java b/src/main/java/dev/isxander/controlify/api/action/ActionEvents.java new file mode 100644 index 000000000..6461f9a55 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/action/ActionEvents.java @@ -0,0 +1,24 @@ +package dev.isxander.controlify.api.action; + +import dev.isxander.controlify.api.utils.event.EventSource; +import dev.isxander.controlify.impl.action.ActionContextRegistryImpl; +import dev.isxander.controlify.impl.action.ActionSpecRegistryImpl; +import dev.isxander.controlify.impl.action.proxy.ActionProxyManagerImpl; + +public final class ActionEvents { + /// Fires before REGISTER_ACTIONS, with built-in contexts already registered. + /// Register addon context IDs and predicates here; the context registry locks afterwards. + public static final EventSource REGISTER_CONTEXTS = ActionContextRegistryImpl.Events.REGISTER_CONTEXTS; + + /// Fires once with Minecraft and initial resources available, before backend setup + /// or controller construction. Register definitions here; the registry locks afterwards. + /// Returned accessors may be retained and used with connected controllers. + public static final EventSource REGISTER_ACTIONS = ActionSpecRegistryImpl.Events.REGISTER_ACTIONS; + + /// Fires immediately after REGISTER_ACTIONS, before automatic modded-key actions or controllers are created. + /// Configure proxies and automatic-proxy exclusions here; the manager locks after startup registration. + public static final EventSource REGISTER_PROXIES = ActionProxyManagerImpl.Events.REGISTER_PROXIES; + + private ActionEvents() { + } +} diff --git a/src/main/java/dev/isxander/controlify/api/action/ActionProxyManager.java b/src/main/java/dev/isxander/controlify/api/action/ActionProxyManager.java new file mode 100644 index 000000000..a9c1c8440 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/action/ActionProxyManager.java @@ -0,0 +1,26 @@ +package dev.isxander.controlify.api.action; + +import net.minecraft.client.KeyMapping; +import net.minecraft.resources.Identifier; + +/// Configures how existing actions proxy operations in Minecraft. +/// Supplied by {@link ActionEvents#REGISTER_PROXIES}, immediately after action-spec registration. +/// Configure proxies and automatic-proxy exclusions in that event; registrations close after startup setup. +public interface ActionProxyManager { + /// Prevents automatic creation of an action for this key mapping. + /// Does not prevent explicitly proxying it through an existing action. + /// Useless for vanilla key mappings as they are not automatically proxied. + void dontAutoProxyKeyMapping(KeyMapping keyMapping); + + /// Proxies an already registered action to a key mapping, suppressing its automatic proxy action. + /// The listener consumes pulses to generate key clicks and reads the latch for held state. + /// It resolves the action again on each update so profile changes can replace action instances. + /// A key mapping may only be proxied by one action; an action may proxy multiple keys. + default void proxyKeyMapping(ActionAccessor action, KeyMapping keyMapping) { + proxyKeyMapping(action.actionId(), keyMapping); + } + + /// Equivalent to {@link #proxyKeyMapping(ActionAccessor, KeyMapping)} using a registered action ID. + /// Throws if the action is not registered or the key is already proxied by a different action. + void proxyKeyMapping(Identifier actionId, KeyMapping keyMapping); +} diff --git a/src/main/java/dev/isxander/controlify/api/action/ActionSpec.java b/src/main/java/dev/isxander/controlify/api/action/ActionSpec.java index 2d08312f6..e3e81b9fc 100644 --- a/src/main/java/dev/isxander/controlify/api/action/ActionSpec.java +++ b/src/main/java/dev/isxander/controlify/api/action/ActionSpec.java @@ -1,8 +1,7 @@ package dev.isxander.controlify.api.action; -import dev.isxander.controlify.impl.action.ActionSpecBuilder; +import dev.isxander.controlify.api.controller.Controller; import dev.isxander.controlify.impl.action.ActionSpecBuilderImpl; -import dev.isxander.controlify.impl.controller.ControllerImpl; import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; @@ -24,8 +23,12 @@ public record ActionSpec( Component description, Component category, Set contexts, - Predicate shouldExpose + Predicate shouldExpose ) implements Comparable { + public ActionSpec { + contexts = Set.copyOf(contexts); + } + @Override public int compareTo(ActionSpec o) { return Comparator.comparing(ActionSpec::id) diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionSpecBuilder.java b/src/main/java/dev/isxander/controlify/api/action/ActionSpecBuilder.java similarity index 59% rename from src/main/java/dev/isxander/controlify/impl/action/ActionSpecBuilder.java rename to src/main/java/dev/isxander/controlify/api/action/ActionSpecBuilder.java index 8cff36660..9d59c97e1 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionSpecBuilder.java +++ b/src/main/java/dev/isxander/controlify/api/action/ActionSpecBuilder.java @@ -1,7 +1,6 @@ -package dev.isxander.controlify.impl.action; +package dev.isxander.controlify.api.action; -import dev.isxander.controlify.api.action.ActionSpec; -import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.api.controller.Controller; import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; @@ -16,9 +15,10 @@ public interface ActionSpecBuilder { ActionSpecBuilder category(Component category); + /// Accepts IDs from {@link BuiltinActionContexts} or addon IDs registered during {@link ActionEvents#REGISTER_CONTEXTS}. ActionSpecBuilder context(Identifier... contexts); - ActionSpecBuilder shouldExpose(Predicate predicate); + ActionSpecBuilder shouldExpose(Predicate predicate); ActionSpec build(); } diff --git a/src/main/java/dev/isxander/controlify/api/action/ActionSpecRegistry.java b/src/main/java/dev/isxander/controlify/api/action/ActionSpecRegistry.java new file mode 100644 index 000000000..c5eea397f --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/action/ActionSpecRegistry.java @@ -0,0 +1,7 @@ +package dev.isxander.controlify.api.action; + +/// Receives action definitions during {@link ActionEvents#REGISTER_ACTIONS}. +public interface ActionSpecRegistry { + /// Throws if registration has closed or the identifier is already registered. + ActionAccessor register(ActionSpec actionSpec); +} diff --git a/src/main/java/dev/isxander/controlify/api/action/BuiltinActionContexts.java b/src/main/java/dev/isxander/controlify/api/action/BuiltinActionContexts.java new file mode 100644 index 000000000..517a1d29d --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/action/BuiltinActionContexts.java @@ -0,0 +1,21 @@ +package dev.isxander.controlify.api.action; + +import dev.isxander.controlify.impl.utils.CUtil; +import net.minecraft.resources.Identifier; + +/// IDs of Controlify's built-in action contexts. Use these directly in {@link ActionSpecBuilder#context(Identifier...)}. +/// Referencing these constants does not initialize or register context implementations. +public final class BuiltinActionContexts { + /// Always applicable. + public static final Identifier UNKNOWN = CUtil.rl("unknown"); + public static final Identifier IN_GAME = CUtil.rl("in_game"); + public static final Identifier ANY_SCREEN = CUtil.rl("screen"); + public static final Identifier REGULAR_SCREEN = CUtil.rl("regular_screen"); + public static final Identifier CONTAINER = CUtil.rl("container"); + public static final Identifier V_MOUSE_CURSOR = CUtil.rl("vmouse_cursor"); + public static final Identifier V_MOUSE_COMPAT = CUtil.rl("vmouse_compat"); + public static final Identifier RADIAL_MENU = CUtil.rl("radial_menu"); + + private BuiltinActionContexts() { + } +} diff --git a/src/main/java/dev/isxander/controlify/api/contextual/BuiltinContextualDomains.java b/src/main/java/dev/isxander/controlify/api/contextual/BuiltinContextualDomains.java new file mode 100644 index 000000000..d900445d3 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/contextual/BuiltinContextualDomains.java @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.api.contextual; + +/// Access to Controlify's built-in domains during registration and throughout runtime. +public interface BuiltinContextualDomains { + ContextualDomain inGame(); + + ContextualDomain container(); +} diff --git a/src/main/java/dev/isxander/controlify/api/contextual/ContainerContext.java b/src/main/java/dev/isxander/controlify/api/contextual/ContainerContext.java index a997271b9..bbc37c83d 100644 --- a/src/main/java/dev/isxander/controlify/api/contextual/ContainerContext.java +++ b/src/main/java/dev/isxander/controlify/api/contextual/ContainerContext.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.api.contextual; -import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.api.controller.Controller; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; @@ -17,7 +17,7 @@ public record ContainerContext( @Nullable Slot hoveredSlot, ItemStack holdingItem, boolean cursorOutsideContainer, - ControllerImpl controller, + Controller controller, GuideVerbosity verbosity ) implements Context { } diff --git a/src/main/java/dev/isxander/controlify/api/contextual/ContextualDomainRegistry.java b/src/main/java/dev/isxander/controlify/api/contextual/ContextualDomainRegistry.java index 048bd6c36..0b1f6efb2 100644 --- a/src/main/java/dev/isxander/controlify/api/contextual/ContextualDomainRegistry.java +++ b/src/main/java/dev/isxander/controlify/api/contextual/ContextualDomainRegistry.java @@ -8,11 +8,11 @@ import net.minecraft.resources.Identifier; -public interface ContextualDomainRegistry { - ContextualDomain inGame(); - - ContextualDomain container(); - +/// Supplied during ContextualEvents.REGISTER_DOMAINS for registering custom domains +/// and accessing built-in domains, for example to register contributors. +public interface ContextualDomainRegistry extends BuiltinContextualDomains { + /// Register during ContextualEvents.REGISTER_DOMAINS, before initial resource loading. + /// Throws when registration is closed or the domain identifier is already registered. ContextualDomain register(Identifier domainId); default ContextualDomain register(Identifier domainId, ContextualStateContributor contributor) { diff --git a/src/main/java/dev/isxander/controlify/api/contextual/ContextualEvents.java b/src/main/java/dev/isxander/controlify/api/contextual/ContextualEvents.java new file mode 100644 index 000000000..49d6a1f52 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/contextual/ContextualEvents.java @@ -0,0 +1,13 @@ +package dev.isxander.controlify.api.contextual; + +import dev.isxander.controlify.api.utils.event.EventSource; +import dev.isxander.controlify.impl.contextual.ContextualDomains; + +public final class ContextualEvents { + /// Fires once during early setup, before initial resource loading. Register domains + /// here so their resource listeners join the first reload. New-domain registration + /// closes afterwards; the registry and existing domains may be retained. + public static final EventSource REGISTER_DOMAINS = ContextualDomains.Events.REGISTER_DOMAINS; + + private ContextualEvents() {} +} diff --git a/src/main/java/dev/isxander/controlify/api/controller/Controller.java b/src/main/java/dev/isxander/controlify/api/controller/Controller.java index 734b6b287..e072f8331 100644 --- a/src/main/java/dev/isxander/controlify/api/controller/Controller.java +++ b/src/main/java/dev/isxander/controlify/api/controller/Controller.java @@ -1,6 +1,8 @@ package dev.isxander.controlify.api.controller; import dev.isxander.controlify.api.controller.rumble.RumbleComponent; +import dev.isxander.controlify.api.controller.input.InputComponent; +import dev.isxander.controlify.api.controller.gyro.GyroComponent; import net.minecraft.resources.Identifier; import java.util.Optional; @@ -34,6 +36,12 @@ public interface Controller { /// This is the same ID defined by `controller_identification.json5`. Identifier typeId(); + /// Input capabilities and current actions. + InputComponent input(); + + /// Present if this controller supports gyroscope input. + Optional gyro(); + /// Get the rumble component that may be attached to this controller. /// If the returned optional is empty, then the controller is not capable of rumble. Optional rumble(); diff --git a/src/main/java/dev/isxander/controlify/api/controller/gyro/GyroComponent.java b/src/main/java/dev/isxander/controlify/api/controller/gyro/GyroComponent.java new file mode 100644 index 000000000..d75ac23db --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/controller/gyro/GyroComponent.java @@ -0,0 +1,11 @@ +package dev.isxander.controlify.api.controller.gyro; + +import org.joml.Vector3fc; + +/// Gyroscope input. Presence in Controller.gyro() indicates gyro support. +public interface GyroComponent { + /// The latest calibrated, filtered and integrated gyro interval in radians. + /// Returns zero when the latest interval is older than 50ms. Read-only; + /// consumers do not need to participate in Controlify's internal update lifecycle. + Vector3fc getState(); +} diff --git a/src/main/java/dev/isxander/controlify/api/controller/gyro/package-info.java b/src/main/java/dev/isxander/controlify/api/controller/gyro/package-info.java new file mode 100644 index 000000000..67dc2544f --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/controller/gyro/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package dev.isxander.controlify.api.controller.gyro; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/api/controller/input/InputComponent.java b/src/main/java/dev/isxander/controlify/api/controller/input/InputComponent.java new file mode 100644 index 000000000..8c6987d33 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/controller/input/InputComponent.java @@ -0,0 +1,13 @@ +package dev.isxander.controlify.api.controller.input; + +import dev.isxander.controlify.api.action.Action; +import net.minecraft.resources.Identifier; +import org.jspecify.annotations.Nullable; + +/// Public input capabilities and current actions of a controller. +public interface InputComponent { + /// Null when unavailable. Resolve again after profile changes, which can replace actions. + @Nullable Action getAction(Identifier actionId); + + boolean isGamepad(); +} diff --git a/src/main/java/dev/isxander/controlify/api/controller/input/package-info.java b/src/main/java/dev/isxander/controlify/api/controller/input/package-info.java new file mode 100644 index 000000000..c52303e6f --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/controller/input/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package dev.isxander.controlify.api.controller.input; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/api/controllermanager/ControllerEvents.java b/src/main/java/dev/isxander/controlify/api/controllermanager/ControllerEvents.java new file mode 100644 index 000000000..11199afae --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/controllermanager/ControllerEvents.java @@ -0,0 +1,29 @@ +package dev.isxander.controlify.api.controllermanager; + +import dev.isxander.controlify.api.ControlifyApi; +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.utils.event.EventSource; +import dev.isxander.controlify.impl.controllermanager.ControllerCoordinator; +import org.jspecify.annotations.Nullable; + +public final class ControllerEvents { + /// Fires once after initial discovery, selection and config saving, including an empty + /// result. Does not fire if the backend failed to initialize. + public static final EventSource DISCOVERY_COMPLETED = ControllerCoordinator.Events.DISCOVERY_COMPLETED; + + /// Fires after insertion and internal connection setup. Selection notifications may fire first. + public static final EventSource CONTROLLER_CONNECTED = ControllerCoordinator.Events.CONTROLLER_CONNECTED; + + /// Fires after removal and selection updates, before closing the controller. It is + /// absent from queries and must not be used after the callback returns. + public static final EventSource CONTROLLER_DISCONNECTED = ControllerCoordinator.Events.CONTROLLER_DISCONNECTED; + + /// Fires after activation, input-mode and handler updates. Null means selection was cleared. + public static final EventSource CURRENT_CONTROLLER_CHANGED = ControllerCoordinator.Events.CURRENT_CONTROLLER_CHANGED; + + public record ControllerConnected(ControlifyApi controlify, Controller controller, boolean hotplugged) {} + public record ControllerDisconnected(ControlifyApi controlify, Controller controller) {} + public record CurrentControllerChanged(ControlifyApi controlify, @Nullable Controller controller) {} + + private ControllerEvents() {} +} diff --git a/src/main/java/dev/isxander/controlify/api/controllermanager/package-info.java b/src/main/java/dev/isxander/controlify/api/controllermanager/package-info.java new file mode 100644 index 000000000..e49573ec9 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/controllermanager/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package dev.isxander.controlify.api.controllermanager; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/api/font/ActionGlyphs.java b/src/main/java/dev/isxander/controlify/api/font/ActionGlyphs.java new file mode 100644 index 000000000..c82094ea4 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/font/ActionGlyphs.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.api.font; + +import dev.isxander.controlify.api.action.ActionAccessor; +import dev.isxander.controlify.impl.font.ActionGlyphsImpl; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.Identifier; + +/// Creates text components whose glyphs are resolved by each receiving Controlify client. +/// +/// Components identify an action rather than a controller or a fixed input. When displayed, +/// the client resolves the action's current binding using its selected controller and glyph set. +/// The same component can be used locally or sent by a server to multiple clients. +/// Creating it does not require a controller, a registered action, or client initialization; +/// the action must exist on the receiving client for its binding to be displayed. +/// +/// These factories only use common Minecraft classes and may be called on a dedicated server. +/// They preserve the component's action reference across vanilla component serialization. +/// Use a fallback variant for components that may reach clients without Controlify. +public final class ActionGlyphs { + private ActionGlyphs() { + } + + /// Creates a glyph component for an action, regardless of the client's current input mode. + /// Without a selected controller or Controlify, the keybind text is displayed instead; + /// use {@link #actionWithFallback(Identifier, Component)} for a readable alternative. + public static Component action(Identifier action) { + return ActionGlyphsImpl.action(action); + } + + /// Equivalent to {@link #action(Identifier)}, using only the accessor's action identifier. + /// This does not resolve or retain a controller-specific action instance. + public static Component action(ActionAccessor action) { + return action(action.actionId()); + } + + /// Creates a glyph component that displays {@code fallback} on clients without Controlify. + /// Controlify clients select the glyph branch regardless of their current input mode. + /// This fallback does not handle missing actions, unbound actions, or a missing controller. + /// Both alternatives are included in the serialized component. + public static Component actionWithFallback(Identifier action, Component fallback) { + return ActionGlyphsImpl.actionWithFallback(action, fallback); + } + + /// Equivalent to {@link #actionWithFallback(Identifier, Component)}, using the accessor's identifier. + public static Component actionWithFallback(ActionAccessor action, Component fallback) { + return actionWithFallback(action.actionId(), fallback); + } + + /// Creates a glyph component that displays {@code fallback} unless the receiving client + /// has Controlify and is using controller input mode. This is useful for messages that + /// should show an ordinary keybind or text to keyboard and mouse users. + /// This fallback does not handle missing or unbound actions. + /// Both alternatives are included in the serialized component. + public static Component actionWithActiveFallback(Identifier action, Component fallback) { + return ActionGlyphsImpl.actionWithActiveFallback(action, fallback); + } + + /// Equivalent to {@link #actionWithActiveFallback(Identifier, Component)}, using the accessor's identifier. + public static Component actionWithActiveFallback(ActionAccessor action, Component fallback) { + return actionWithActiveFallback(action.actionId(), fallback); + } +} diff --git a/src/main/java/dev/isxander/controlify/api/font/package-info.java b/src/main/java/dev/isxander/controlify/api/font/package-info.java new file mode 100644 index 000000000..6894a251f --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/font/package-info.java @@ -0,0 +1,5 @@ +/// Components for displaying controller action glyphs. +@NullMarked +package dev.isxander.controlify.api.font; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/api/gui/ButtonGuideApi.java b/src/main/java/dev/isxander/controlify/api/gui/ButtonGuideApi.java index 22f299964..eab5c61ca 100644 --- a/src/main/java/dev/isxander/controlify/api/gui/ButtonGuideApi.java +++ b/src/main/java/dev/isxander/controlify/api/gui/ButtonGuideApi.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.api.gui; -import dev.isxander.controlify.impl.action.ActionAccessor; +import dev.isxander.controlify.api.action.ActionAccessor; import dev.isxander.controlify.impl.gui.ButtonGuideRenderer; import net.minecraft.client.gui.screens.Screen; @@ -34,13 +34,13 @@ public static void addGuideToButton( T button, ActionAccessor action, ButtonGuidePredicate renderPredicate) { - ButtonGuideRenderer.registerBindingForButton(button, () -> action, renderPredicate); + ButtonGuideRenderer.registerActionForButton(button, () -> action, renderPredicate); } public static void addGuideToButton( T button, Supplier action, ButtonGuidePredicate renderPredicate) { - ButtonGuideRenderer.registerBindingForButton(button, action, renderPredicate); + ButtonGuideRenderer.registerActionForButton(button, action, renderPredicate); } } diff --git a/src/main/java/dev/isxander/controlify/api/ingame/InGameEvents.java b/src/main/java/dev/isxander/controlify/api/ingame/InGameEvents.java new file mode 100644 index 000000000..a11f745a0 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/ingame/InGameEvents.java @@ -0,0 +1,11 @@ +package dev.isxander.controlify.api.ingame; + +import dev.isxander.controlify.api.utils.event.EventSource; +import dev.isxander.controlify.impl.ingame.InGameInputHandler; + +public final class InGameEvents { + /// Modifies the final look impulse at the existing pre-application hook. + public static final EventSource LOOK_INPUT_MODIFIER = InGameInputHandler.Events.LOOK_INPUT_MODIFIER; + + private InGameEvents() {} +} diff --git a/src/main/java/dev/isxander/controlify/api/ingame/LookInputModifier.java b/src/main/java/dev/isxander/controlify/api/ingame/LookInputModifier.java new file mode 100644 index 000000000..dfc6f2486 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/ingame/LookInputModifier.java @@ -0,0 +1,28 @@ +package dev.isxander.controlify.api.ingame; + +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.utils.event.EventSink; +import org.joml.Vector2f; + +import java.util.function.BiFunction; +import java.util.function.Predicate; + +/// Modify lookInput in place before application to the player. Callbacks compose in +/// subscription order. The vector belongs to this dispatch and must not be retained. +public record LookInputModifier(Vector2f lookInput, Controller controller) { + public static EventSink functional( + BiFunction x, BiFunction y) { + return event -> { + event.lookInput.x = x.apply(event.lookInput.x, event.controller); + event.lookInput.y = y.apply(event.lookInput.y, event.controller); + }; + } + + public static EventSink zeroIf(Predicate condition) { + return event -> { + if (condition.test(event.controller)) { + event.lookInput.set(0, 0); + } + }; + } +} diff --git a/src/main/java/dev/isxander/controlify/api/ingame/package-info.java b/src/main/java/dev/isxander/controlify/api/ingame/package-info.java new file mode 100644 index 000000000..c1866e377 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/ingame/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package dev.isxander.controlify.api.ingame; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/api/input/LogicalInput.java b/src/main/java/dev/isxander/controlify/api/input/LogicalInput.java index da729428b..7ec5d1ffa 100644 --- a/src/main/java/dev/isxander/controlify/api/input/LogicalInput.java +++ b/src/main/java/dev/isxander/controlify/api/input/LogicalInput.java @@ -1,11 +1,16 @@ package dev.isxander.controlify.api.input; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Unit; import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import dev.isxander.controlify.impl.utils.codec.CExtraCodecs; import net.minecraft.resources.Identifier; +import org.jspecify.annotations.Nullable; import java.util.List; +import java.util.Optional; import java.util.Set; /// A reference to a logical button or axis. @@ -26,6 +31,12 @@ public sealed interface LogicalInput { } ); + /// Codec allowing a null LogicalInput represented by an empty map `{}` + Codec> OPTIONAL_CODEC = Codec.either(CODEC, MapCodec.unitCodec(() -> Unit.INSTANCE)).xmap( + either -> either.left(), + optional -> optional.isPresent() ? Either.left(optional.get()) : Either.right(Unit.INSTANCE) + ); + /// A logical input with a direct relationship with a physical input. record Single(Identifier physicalInput) implements LogicalInput { public static final Codec CODEC = diff --git a/src/main/java/dev/isxander/controlify/impl/api/vmousesnapping/ISnapBehaviour.java b/src/main/java/dev/isxander/controlify/api/screenop/ISnapBehaviour.java similarity index 91% rename from src/main/java/dev/isxander/controlify/impl/api/vmousesnapping/ISnapBehaviour.java rename to src/main/java/dev/isxander/controlify/api/screenop/ISnapBehaviour.java index 25a2b19f1..d84226301 100644 --- a/src/main/java/dev/isxander/controlify/impl/api/vmousesnapping/ISnapBehaviour.java +++ b/src/main/java/dev/isxander/controlify/api/screenop/ISnapBehaviour.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.impl.api.vmousesnapping; +package dev.isxander.controlify.api.screenop; import java.util.Set; import java.util.function.Consumer; diff --git a/src/main/java/dev/isxander/controlify/impl/api/vmousesnapping/SnapPoint.java b/src/main/java/dev/isxander/controlify/api/screenop/SnapPoint.java similarity index 90% rename from src/main/java/dev/isxander/controlify/impl/api/vmousesnapping/SnapPoint.java rename to src/main/java/dev/isxander/controlify/api/screenop/SnapPoint.java index 02e770bc4..5c234db53 100644 --- a/src/main/java/dev/isxander/controlify/impl/api/vmousesnapping/SnapPoint.java +++ b/src/main/java/dev/isxander/controlify/api/screenop/SnapPoint.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.impl.api.vmousesnapping; +package dev.isxander.controlify.api.screenop; import org.joml.Vector2i; import org.joml.Vector2ic; diff --git a/src/main/java/dev/isxander/controlify/impl/utils/event/EventSink.java b/src/main/java/dev/isxander/controlify/api/utils/event/EventSink.java similarity index 61% rename from src/main/java/dev/isxander/controlify/impl/utils/event/EventSink.java rename to src/main/java/dev/isxander/controlify/api/utils/event/EventSink.java index 37436842d..01095d82a 100644 --- a/src/main/java/dev/isxander/controlify/impl/utils/event/EventSink.java +++ b/src/main/java/dev/isxander/controlify/api/utils/event/EventSink.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.impl.utils.event; +package dev.isxander.controlify.api.utils.event; @FunctionalInterface public interface EventSink { diff --git a/src/main/java/dev/isxander/controlify/impl/utils/event/EventSource.java b/src/main/java/dev/isxander/controlify/api/utils/event/EventSource.java similarity index 59% rename from src/main/java/dev/isxander/controlify/impl/utils/event/EventSource.java rename to src/main/java/dev/isxander/controlify/api/utils/event/EventSource.java index 46556805e..ca9944318 100644 --- a/src/main/java/dev/isxander/controlify/impl/utils/event/EventSource.java +++ b/src/main/java/dev/isxander/controlify/api/utils/event/EventSource.java @@ -1,7 +1,10 @@ -package dev.isxander.controlify.impl.utils.event; +package dev.isxander.controlify.api.utils.event; import java.util.function.Predicate; +/// Subscription-only source. Controlify events dispatch synchronously in subscription +/// order on the emitting thread, without replay. Subscribe in ControlifyEntrypoint for +/// one-shot lifecycle events. Listener exceptions propagate and stop that emission. public interface EventSource { Subscription subscribe(EventSink listener); @@ -19,6 +22,7 @@ default EventSource filter(Predicate predicate) { @FunctionalInterface interface Subscription extends AutoCloseable { + /// Unsubscribe this registration; repeated calls have no effect. @Override void close(); // unsubscribe; no checked exception } diff --git a/src/main/java/dev/isxander/controlify/impl/utils/event/EventStage.java b/src/main/java/dev/isxander/controlify/api/utils/event/EventStage.java similarity index 94% rename from src/main/java/dev/isxander/controlify/impl/utils/event/EventStage.java rename to src/main/java/dev/isxander/controlify/api/utils/event/EventStage.java index 0f9391d99..39e83e0b5 100644 --- a/src/main/java/dev/isxander/controlify/impl/utils/event/EventStage.java +++ b/src/main/java/dev/isxander/controlify/api/utils/event/EventStage.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.impl.utils.event; +package dev.isxander.controlify.api.utils.event; @FunctionalInterface public interface EventStage { diff --git a/src/main/java/dev/isxander/controlify/impl/utils/event/UnaryEventStage.java b/src/main/java/dev/isxander/controlify/api/utils/event/UnaryEventStage.java similarity index 63% rename from src/main/java/dev/isxander/controlify/impl/utils/event/UnaryEventStage.java rename to src/main/java/dev/isxander/controlify/api/utils/event/UnaryEventStage.java index d62293485..e078ad8b0 100644 --- a/src/main/java/dev/isxander/controlify/impl/utils/event/UnaryEventStage.java +++ b/src/main/java/dev/isxander/controlify/api/utils/event/UnaryEventStage.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.impl.utils.event; +package dev.isxander.controlify.api.utils.event; @FunctionalInterface public interface UnaryEventStage extends EventStage { diff --git a/src/main/java/dev/isxander/controlify/api/utils/event/package-info.java b/src/main/java/dev/isxander/controlify/api/utils/event/package-info.java new file mode 100644 index 000000000..e4c0095e1 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/utils/event/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package dev.isxander.controlify.api.utils.event; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/impl/Controlify.java b/src/main/java/dev/isxander/controlify/impl/Controlify.java index c02a7c2a8..883cb99f6 100644 --- a/src/main/java/dev/isxander/controlify/impl/Controlify.java +++ b/src/main/java/dev/isxander/controlify/impl/Controlify.java @@ -7,16 +7,17 @@ package dev.isxander.controlify.impl; import com.mojang.blaze3d.Blaze3D; -import dev.isxander.controlify.api.InputMode; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.impl.api.bind.ControlifyBindApi; -import dev.isxander.controlify.api.contextual.ContextualDomainRegistry; -import dev.isxander.controlify.impl.api.entrypoint.InitContext; -import dev.isxander.controlify.impl.api.entrypoint.PreInitContext; -import dev.isxander.controlify.impl.bindings.ControlifyBindApiImpl; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; -import dev.isxander.controlify.impl.action.radial.RadialIconManager; +import dev.isxander.controlify.api.ControlifyEvents; +import dev.isxander.controlify.api.InputMode; +import dev.isxander.controlify.api.contextual.BuiltinContextualDomains; +import dev.isxander.controlify.impl.action.ActionContextRegistryImpl; +import dev.isxander.controlify.impl.action.ActionSpecRegistryImpl; +import dev.isxander.controlify.impl.action.ControlifyActions; +import dev.isxander.controlify.impl.action.activator.ActivatorConfigManager; import dev.isxander.controlify.impl.action.defaults.DefaultBindManager; +import dev.isxander.controlify.impl.action.proxy.ActionProxyManagerImpl; +import dev.isxander.controlify.impl.action.radial.RadialIconManager; import dev.isxander.controlify.impl.compatibility.ControlifyCompat; import dev.isxander.controlify.impl.config.ConfigManager; import dev.isxander.controlify.impl.config.dto.profile.defaults.DefaultConfigManager; @@ -24,35 +25,34 @@ import dev.isxander.controlify.impl.contextual.RuleSetManager; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.controller.dualsense.TriggerEffectManager; -import dev.isxander.controlify.impl.controllermanager.ControllerTypeManager; -import dev.isxander.controlify.impl.input.mapping.ControllerMappingManager; import dev.isxander.controlify.impl.controller.rumble.RumbleComponentImpl; +import dev.isxander.controlify.impl.controller.rumble.RumbleManager; +import dev.isxander.controlify.impl.controller.rumble.RumbleSourceManager; import dev.isxander.controlify.impl.controllermanager.ControllerCoordinator; import dev.isxander.controlify.impl.controllermanager.ControllerManager; +import dev.isxander.controlify.impl.controllermanager.ControllerTypeManager; +import dev.isxander.controlify.impl.debug.DebugProperties; import dev.isxander.controlify.impl.driver.steamdeck.SteamDeckMode; import dev.isxander.controlify.impl.driver.steamdeck.SteamDeckUtil; import dev.isxander.controlify.impl.font.InputFontMapper; +import dev.isxander.controlify.impl.gui.guide.InGameButtonGuide; import dev.isxander.controlify.impl.gui.screen.*; -import dev.isxander.controlify.impl.debug.DebugProperties; import dev.isxander.controlify.impl.ingame.ControllerPlayerMovement; -import dev.isxander.controlify.impl.action.activator.ActivatorConfigManager; +import dev.isxander.controlify.impl.ingame.InGameInputHandler; +import dev.isxander.controlify.impl.input.mapping.ControllerMappingManager; import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; import dev.isxander.controlify.impl.platform.network.SidedNetworkApi; -import dev.isxander.controlify.impl.controller.rumble.RumbleManager; -import dev.isxander.controlify.impl.controller.rumble.RumbleSourceManager; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; import dev.isxander.controlify.impl.screenop.keyboard.KeyboardLayoutManager; import dev.isxander.controlify.impl.server.*; -import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.api.ControlifyEvents; -import dev.isxander.controlify.impl.gui.guide.InGameButtonGuide; -import dev.isxander.controlify.impl.ingame.InGameInputHandler; -import dev.isxander.controlify.mixins.feature.virtualmouse.MouseHandlerAccessor; import dev.isxander.controlify.impl.server.packets.*; import dev.isxander.controlify.impl.sound.ControlifyClientSounds; import dev.isxander.controlify.impl.utils.*; +import dev.isxander.controlify.impl.utils.event.EventBus; import dev.isxander.controlify.impl.virtualmouse.VirtualMouseHandler; import dev.isxander.controlify.impl.wireless.LowBatteryNotifier; +import dev.isxander.controlify.mixins.feature.virtualmouse.MouseHandlerAccessor; import dev.isxander.yacl3.gui.YACLScreen; import net.minecraft.client.KeyMapping; import net.minecraft.client.Minecraft; @@ -61,14 +61,23 @@ import net.minecraft.network.chat.Component; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.NonNull; import org.spongepowered.asm.mixin.MixinEnvironment; +import static dev.isxander.controlify.impl.utils.ControllerUtils.wrapControllerError; import java.io.IOException; import java.util.*; -import static dev.isxander.controlify.impl.utils.ControllerUtils.wrapControllerError; - public class Controlify implements ControlifyApi { + /// This holder can initialize before the Controlify singleton or any managers. + public static final class Events { + public static final EventBus INITIALIZED = new EventBus<>(); + public static final EventBus INPUT_MODE_CHANGED = new EventBus<>(); + + private Events() { + } + } + private static Controlify instance = null; private Minecraft minecraft = null; @@ -152,6 +161,11 @@ public void preInitialiseControlify() { PlatformClientUtil.registerAssetReloadListener(RuleSetManager.GUIDE_RULES); PlatformClientUtil.registerAssetReloadListener(RuleSetManager.TRIGGER_EFFECT_RULES); ContextualDomains.touch(); + try { + ContextualDomains.Events.REGISTER_DOMAINS.emit(ContextualDomains.INSTANCE); + } finally { + ContextualDomains.INSTANCE.lock(); + } registerBuiltinPack("legacy_console"); @@ -199,24 +213,6 @@ public void preInitialiseControlify() { PlatformClientUtil.addHudLayer(CUtil.rl("button_guide"), (graphics, deltaTracker) -> inGameButtonGuide().ifPresent(guide -> guide.extractRenderState(graphics, deltaTracker.getGameTimeDeltaPartialTick(false)))); - - PlatformMainUtil.applyToControlifyEntrypoint(entrypoint -> { - try { - entrypoint.onControlifyPreInit(new PreInitContext() { - @Override - public ControlifyBindApi bindings() { - return ControlifyBindApiImpl.INSTANCE; - } - - @Override - public ContextualDomainRegistry contextualDomains() { - return ContextualDomains.INSTANCE; - } - }); - } catch (Throwable e) { - CUtil.LOGGER.error("Failed to run `onControlifyPreInit` on Controlify entrypoint: {}", entrypoint.getClass().getName(), e); - } - }); } private void registerBuiltinPack(String id) { @@ -243,29 +239,38 @@ public void initializeControlify() { this.virtualMouseHandler = new VirtualMouseHandler(); this.triggerEffectManager = new TriggerEffectManager(); - ControlifyBindings.registerModdedBindings(); + ActionContextRegistryImpl.INSTANCE.registerBuiltinContexts(); + ControlifyActions.init(); PlatformClientUtil.registerPostScreenRender((screen, graphics, mouseX, mouseY, tickDelta) -> - ControlifyApi.get().getCurrentController().ifPresent(controller -> { + this.getCurrentController().ifPresent(controller -> { virtualMouseHandler().renderVirtualMouse(graphics); ScreenProcessorProvider.provide(screen).render(controller, graphics, tickDelta); })); - ControlifyEvents.CURRENT_CONTROLLER_CHANGED.register(event -> - this.onCurrentControllerChanged(event.controller(), event.changeInputMode())); this.controllerCoordinator = new ControllerCoordinator(this, this.setupWizardManager); - if (!this.controllerCoordinator.initialize()) { - return; - } - PlatformClientUtil.registerClientTickStarted(this::fixedUpdate); - - // initialise and compatability modules that controlify implements itself - // this does NOT invoke any entrypoints. this is done in the pre-initialisation phase + // Built-in compatibility must subscribe before addon-facing lifecycle events. ControlifyCompat.init(); + Events.INITIALIZED.emit(this); + try { + try { + ActionContextRegistryImpl.Events.REGISTER_CONTEXTS.emit(ActionContextRegistryImpl.INSTANCE); + } finally { + ActionContextRegistryImpl.INSTANCE.lock(); + } + ActionSpecRegistryImpl.Events.REGISTER_ACTIONS.emit(ActionSpecRegistryImpl.INSTANCE); + ActionProxyManagerImpl.Events.REGISTER_PROXIES.emit(ActionProxyManagerImpl.INSTANCE); + ControlifyActions.registerModdedActions(); + } finally { + ActionSpecRegistryImpl.INSTANCE.lock(); + ActionProxyManagerImpl.INSTANCE.lock(); + } - // make sure people don't someone add binds after controllers could have been created - ControlifyBindApiImpl.INSTANCE.lock(); + if (!this.controllerCoordinator.initialize()) { + return; + } + PlatformClientUtil.registerClientTickStarted(_ -> this.fixedUpdate()); discoverControllers(); @@ -276,19 +281,6 @@ public void initializeControlify() { if (this.config().getSettings().globalSettings().useEnhancedSteamDeckDriver) { doSteamDeckChecks(); } - - PlatformMainUtil.applyToControlifyEntrypoint(entrypoint -> { - try { - entrypoint.onControlifyInit(new InitContext() { - @Override - public ControlifyApi controlify() { - return Controlify.this; - } - }); - } catch (Throwable e) { - CUtil.LOGGER.error("Failed to run `onControlifyInit` on Controlify entrypoint: {}", entrypoint.getClass().getName(), e); - } - }); } private void doSteamDeckChecks() { @@ -396,10 +388,6 @@ private void updateActiveController(ControllerImpl controller, boolean givingInp consecutiveInputSwitches = 0; return; } - - if (this.currentInputMode().isController()) { - ControlifyEvents.ACTIVE_CONTROLLER_TICKED.invoke(new ControlifyEvents.ControllerStateUpdate(controller)); - } } private void fixedUpdateActiveController(ControllerImpl controller, boolean outOfFocus) { @@ -450,11 +438,29 @@ public ConfigManager config() { return this.getControllerCoordinator().flatMap(ControllerCoordinator::currentController); } + @Override + public Collection getConnectedControllers() { + return this.getControllerManager() + .map(manager -> List.copyOf(manager.getAllControllers())) + .orElseGet(List::of); + } + + @Override + public Optional getControllerByUid(@NonNull String uid) { + return this.getControllerManager().flatMap(manager -> manager.getControllerByUid(uid)); + } + + @Override + public BuiltinContextualDomains contextualDomains() { + return ContextualDomains.INSTANCE; + } + public void setCurrentController(@Nullable ControllerImpl controller, boolean changeInputMode) { this.getControllerCoordinator().ifPresent(coordinator -> coordinator.setCurrentController(controller, changeInputMode)); } - private void onCurrentControllerChanged(@Nullable ControllerImpl controller, boolean changeInputMode) { + /// Internal selection callback; invoked before public selection notification. + public void onCurrentControllerChanged(@Nullable ControllerImpl controller, boolean changeInputMode) { boolean changedInputMode = false; if (controller == null) { changedInputMode = this.setInputMode(InputMode.KEYBOARD_MOUSE); @@ -468,7 +474,9 @@ private void onCurrentControllerChanged(@Nullable ControllerImpl controller, boo @Override public boolean setInputMode(@NotNull InputMode newInputMode) { - if (this.currentInputMode == newInputMode) return false; + if (this.currentInputMode == newInputMode) { + return false; + } if (newInputMode.isController() && this.getCurrentController().isEmpty()) { DebugLog.log("Attempted to switch to controller input mode with no current controller set."); return false; @@ -508,8 +516,10 @@ public boolean setInputMode(@NotNull InputMode newInputMode) { ScreenProcessorProvider.provide(MinecraftUtil.getScreen()).onInputModeChanged(newInputMode); } + this.virtualMouseHandler.onInputModeChanged(newInputMode); + // notify event listeners of input mode change - ControlifyEvents.INPUT_MODE_CHANGED.invoke(new ControlifyEvents.InputModeChanged(newInputMode)); + Events.INPUT_MODE_CHANGED.emit(new ControlifyEvents.InputModeChanged(this, newInputMode)); return true; } @@ -588,7 +598,9 @@ private void moveCursorToVirtualMouse() { } public void endTemporaryCursor() { - if (currentInputMode() != InputMode.MIXED || showMouseTicks <= 0) return; + if (currentInputMode() != InputMode.MIXED || showMouseTicks <= 0) { + return; + } showMouseTicks = 0; restoreVirtualCursor(); @@ -642,8 +654,9 @@ public TriggerEffectManager triggerEffectManager() { } public void notifyNewServer(ServerData data) { - if (!currentInputMode().isController()) + if (!currentInputMode().isController()) { return; + } if (config().getSettings().globalSettings().seenServers.add(data.ip)) { MinecraftUtil.sendToast( @@ -656,7 +669,9 @@ public void notifyNewServer(ServerData data) { } public static Controlify instance() { - if (instance == null) instance = new Controlify(); + if (instance == null) { + instance = new Controlify(); + } return instance; } } diff --git a/src/main/java/dev/isxander/controlify/impl/action/Accumulator.java b/src/main/java/dev/isxander/controlify/impl/action/Accumulator.java index 2737e5806..f5bfbc438 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/Accumulator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/Accumulator.java @@ -2,10 +2,14 @@ /// Accepts action state. public interface Accumulator { - default void firePulse() {} + default void firePulse() { + } - default void toggleLatch() {} - default void setLatch(boolean active) {} + default void toggleLatch() { + } + default void setLatch(boolean active) { + } - default void setContinuous(float value) {} + default void setContinuous(float value) { + } } diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionAccessor.java b/src/main/java/dev/isxander/controlify/impl/action/ActionAccessor.java deleted file mode 100644 index 153bbc3ce..000000000 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionAccessor.java +++ /dev/null @@ -1,28 +0,0 @@ -package dev.isxander.controlify.impl.action; - -import dev.isxander.controlify.api.action.Action; -import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.controller.input.InputComponent; -import net.minecraft.resources.Identifier; -import org.jspecify.annotations.Nullable; - -/// An accessor to get an existing action from an input component -public interface ActionAccessor { - @Nullable Action onOrNull(InputComponent inputComponent); - - Identifier actionId(); - - default @Nullable Action onOrNull(ControllerImpl controller) { - return onOrNull(controller.input().orElseThrow()); - } - - default Action on(InputComponent inputComponent) { - Action action = onOrNull(inputComponent); - if (action == null) throw new NullPointerException("No action bound to input component " + actionId()); - return action; - } - - default Action on(ControllerImpl controller) { - return on(controller.input().orElseThrow()); - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionAccessorImpl.java b/src/main/java/dev/isxander/controlify/impl/action/ActionAccessorImpl.java index a75544968..9316baabe 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionAccessorImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionAccessorImpl.java @@ -1,13 +1,15 @@ package dev.isxander.controlify.impl.action; import dev.isxander.controlify.api.action.Action; -import dev.isxander.controlify.impl.controller.input.InputComponent; +import dev.isxander.controlify.api.action.ActionAccessor; +import dev.isxander.controlify.api.controller.input.InputComponent; import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; public record ActionAccessorImpl(Identifier actionId) implements ActionAccessor { @Override - public @Nullable Action onOrNull(InputComponent inputComponent) { + public @Nullable Action onOrNull(@NonNull InputComponent inputComponent) { return inputComponent.getAction(this.actionId); } } diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionContext.java b/src/main/java/dev/isxander/controlify/impl/action/ActionContext.java index a91baeaa6..f5bc999e8 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionContext.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionContext.java @@ -6,6 +6,7 @@ import java.util.function.Function; +/// Internal context definition; the public API exposes its identifier and registration parameters only. public record ActionContext(Identifier id, Function isApplicable) implements Comparable { @Override public int compareTo(@NonNull ActionContext o) { diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionContextRegistryImpl.java b/src/main/java/dev/isxander/controlify/impl/action/ActionContextRegistryImpl.java new file mode 100644 index 000000000..972a49b4d --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionContextRegistryImpl.java @@ -0,0 +1,90 @@ +package dev.isxander.controlify.impl.action; + +import dev.isxander.controlify.api.action.ActionContextRegistry; +import dev.isxander.controlify.api.action.BuiltinActionContexts; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.gui.screen.RadialMenuScreen; +import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.utils.MinecraftUtil; +import dev.isxander.controlify.impl.utils.event.EventBus; +import dev.isxander.controlify.impl.virtualmouse.VirtualMouseBehaviour; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; + +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.function.Function; + +public final class ActionContextRegistryImpl implements ActionContextRegistry { + /// Subscriptions do not initialize the context registry singleton. + public static final class Events { + public static final EventBus REGISTER_CONTEXTS = new EventBus<>(); + + private Events() { + } + } + + public static final ActionContextRegistryImpl INSTANCE = new ActionContextRegistryImpl(); + + private final Map contexts = new TreeMap<>(); + private boolean locked; + + private ActionContextRegistryImpl() { + } + + @Override + public @NonNull Identifier register(@NonNull Identifier id, @NonNull Function isApplicable) { + if (this.locked) { + throw new IllegalStateException("Action context registration has closed"); + } + ActionContext context = new ActionContext(Objects.requireNonNull(id), Objects.requireNonNull(isApplicable)); + if (this.contexts.putIfAbsent(id, context) != null) { + throw new IllegalArgumentException("Action context is already registered: " + id); + } + return id; + } + + public void registerBuiltinContexts() { + this.register(BuiltinActionContexts.UNKNOWN, mc -> true); + this.register(BuiltinActionContexts.IN_GAME, + mc -> MinecraftUtil.getScreen() == null && mc.level != null && mc.player != null); + this.register(BuiltinActionContexts.ANY_SCREEN, mc -> MinecraftUtil.getScreen() != null); + this.register(BuiltinActionContexts.REGULAR_SCREEN, + mc -> MinecraftUtil.getScreen() != null + && !Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled()); + this.register(BuiltinActionContexts.CONTAINER, + mc -> MinecraftUtil.getScreen() instanceof AbstractContainerScreen); + this.register(BuiltinActionContexts.V_MOUSE_CURSOR, + mc -> MinecraftUtil.getScreen() != null + && ScreenProcessorProvider.provide(MinecraftUtil.getScreen()).virtualMouseBehaviour().hasCursor() + && Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled()); + this.register(BuiltinActionContexts.V_MOUSE_COMPAT, + mc -> MinecraftUtil.getScreen() != null + && ScreenProcessorProvider.provide(MinecraftUtil.getScreen()).virtualMouseBehaviour() == VirtualMouseBehaviour.ENABLED + && Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled()); + this.register(BuiltinActionContexts.RADIAL_MENU, mc -> MinecraftUtil.getScreen() instanceof RadialMenuScreen); + } + + public Set getApplicableContexts(Minecraft minecraft) { + if (!this.locked) { + throw new IllegalStateException("Action contexts must be registered before evaluating them"); + } + Set applicable = new TreeSet<>(); + for (ActionContext context : this.contexts.values()) { + if (context.isApplicable().apply(minecraft)) { + applicable.add(context); + } + } + return Collections.unmodifiableSet(applicable); + } + + public void lock() { + this.locked = true; + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionGraph.java b/src/main/java/dev/isxander/controlify/impl/action/ActionGraph.java index 1a6cd4ed4..c81699a6a 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionGraph.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionGraph.java @@ -2,8 +2,9 @@ import dev.isxander.controlify.api.input.LogicalInput; import dev.isxander.controlify.impl.input.signal.LogicalSignal; -import dev.isxander.controlify.impl.utils.event.EventSink; +import dev.isxander.controlify.api.utils.event.EventSink; import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; import java.util.*; @@ -70,7 +71,7 @@ public void setCurrentContexts(Set contexts) { } @Override - public void accept(LogicalSignal event) { + public void accept(@NonNull LogicalSignal event) { switch (event) { case LogicalSignal.Tick tick -> this.processTick(tick); case LogicalSignal.InputSignal inputSignal -> this.processInput(inputSignal); diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionImpl.java b/src/main/java/dev/isxander/controlify/impl/action/ActionImpl.java index f6c23225f..a9453da1e 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionImpl.java @@ -13,7 +13,7 @@ public class ActionImpl implements Action, Comparable { private final ActionSpec spec; private final ActionState state; private @Nullable LogicalInput boundInput; - private final @Nullable LogicalInput defaultInput; + private @Nullable LogicalInput defaultInput; private Activator activator; private final ActivatorConfig activatorConfig; @@ -56,6 +56,11 @@ public void setBoundInput(@Nullable LogicalInput boundInput) { return this.defaultInput; } + /// Refreshes resource-defined defaults without changing the current binding or activator state. + public void setDefaultInput(@Nullable LogicalInput defaultInput) { + this.defaultInput = defaultInput; + } + @Override public Channel.Pulse pulse() { return this.state(); diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionSpecBuilderImpl.java b/src/main/java/dev/isxander/controlify/impl/action/ActionSpecBuilderImpl.java index 337071d48..75ec6a311 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionSpecBuilderImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionSpecBuilderImpl.java @@ -1,10 +1,12 @@ package dev.isxander.controlify.impl.action; import dev.isxander.controlify.api.action.ActionSpec; -import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.api.action.ActionSpecBuilder; +import dev.isxander.controlify.api.controller.Controller; import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; import org.apache.commons.lang3.Validate; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import java.util.*; @@ -16,40 +18,40 @@ public class ActionSpecBuilderImpl implements ActionSpecBuilder { private @Nullable Component description; private @Nullable Component category; private final Set contexts = new TreeSet<>(); - private Predicate shouldExpose = _ -> true; + private Predicate shouldExpose = _ -> true; @Override - public ActionSpecBuilder id(Identifier id) { + public ActionSpecBuilder id(@NonNull Identifier id) { this.id = id; return this; } @Override - public ActionSpecBuilder name(Component name) { + public ActionSpecBuilder name(@NonNull Component name) { this.name = name; return this; } @Override - public ActionSpecBuilder description(Component description) { + public ActionSpecBuilder description(@NonNull Component description) { this.description = description; return this; } @Override - public ActionSpecBuilder category(Component category) { + public ActionSpecBuilder category(@NonNull Component category) { this.category = category; return this; } @Override - public ActionSpecBuilder context(Identifier... contexts) { + public ActionSpecBuilder context(Identifier @NonNull ... contexts) { this.contexts.addAll(List.of(contexts)); return this; } @Override - public ActionSpecBuilder shouldExpose(Predicate predicate) { + public ActionSpecBuilder shouldExpose(@NonNull Predicate predicate) { this.shouldExpose = predicate; return this; } diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionSpecRegistry.java b/src/main/java/dev/isxander/controlify/impl/action/ActionSpecRegistry.java deleted file mode 100644 index a65bff351..000000000 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionSpecRegistry.java +++ /dev/null @@ -1,7 +0,0 @@ -package dev.isxander.controlify.impl.action; - -import dev.isxander.controlify.api.action.ActionSpec; - -public interface ActionSpecRegistry { - ActionAccessor register(ActionSpec actionSpec); -} diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionSpecRegistryImpl.java b/src/main/java/dev/isxander/controlify/impl/action/ActionSpecRegistryImpl.java index c1f832586..abfc5054a 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionSpecRegistryImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionSpecRegistryImpl.java @@ -1,26 +1,44 @@ package dev.isxander.controlify.impl.action; +import dev.isxander.controlify.api.action.ActionAccessor; import dev.isxander.controlify.api.action.ActionSpec; +import dev.isxander.controlify.api.action.ActionSpecRegistry; +import dev.isxander.controlify.impl.utils.event.EventBus; +import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class ActionSpecRegistryImpl implements ActionSpecRegistry { + /// Addons can subscribe without initializing the registry singleton. + public static final class Events { + public static final EventBus REGISTER_ACTIONS = new EventBus<>(); + + private Events() { + } + } + public static final ActionSpecRegistryImpl INSTANCE = new ActionSpecRegistryImpl(); private final List registeredSpecs = new ArrayList<>(); - - private boolean locked = false; + private boolean locked; @Override - public ActionAccessor register(ActionSpec actionSpec) { + public ActionAccessor register(@NonNull ActionSpec actionSpec) { this.checkLocked(false); + if (this.registeredSpecs.stream().anyMatch(spec -> spec.id().equals(actionSpec.id()))) { + throw new IllegalArgumentException("Action is already registered: " + actionSpec.id()); + } this.registeredSpecs.add(actionSpec); - return new ActionAccessorImpl(actionSpec.id()); } + public boolean isRegistered(Identifier actionId) { + return this.registeredSpecs.stream().anyMatch(spec -> spec.id().equals(actionId)); + } + public List getActionSpecs() { this.checkLocked(true); return Collections.unmodifiableList(this.registeredSpecs); diff --git a/src/main/java/dev/isxander/controlify/impl/action/ControlifyActions.java b/src/main/java/dev/isxander/controlify/impl/action/ControlifyActions.java new file mode 100644 index 000000000..b2f254c23 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/action/ControlifyActions.java @@ -0,0 +1,467 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.action; + +import dev.isxander.controlify.api.action.ActionAccessor; +import dev.isxander.controlify.api.action.ActionEvents; +import dev.isxander.controlify.api.action.ActionProxyManager; +import dev.isxander.controlify.api.action.ActionSpec; +import dev.isxander.controlify.api.action.ActionSpecBuilder; +import dev.isxander.controlify.api.action.BuiltinActionContexts; +import dev.isxander.controlify.impl.action.proxy.ActionProxyManagerImpl; +import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; +import dev.isxander.controlify.impl.utils.CUtil; +import net.minecraft.ChatFormatting; +import net.minecraft.client.KeyMapping; +import net.minecraft.client.Minecraft; +import net.minecraft.client.Options; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.Identifier; + +import java.util.Locale; + +public final class ControlifyActions { + public static final String MODDED_ACTION_NAMESPACE = "controlify_modded"; + + private static final Options options = Minecraft.getInstance().options; + + public static final Component MOVEMENT_CATEGORY = KeyMapping.Category.MOVEMENT.label(); + public static final Component GAMEPLAY_CATEGORY = KeyMapping.Category.GAMEPLAY.label(); + public static final Component INVENTORY_CATEGORY = KeyMapping.Category.INVENTORY.label(); + public static final Component CREATIVE_CATEGORY = KeyMapping.Category.CREATIVE.label(); + public static final Component MISC_CATEGORY = KeyMapping.Category.MISC.label(); + public static final Component DEBUG_CATEGORY = Component.translatable("controlify.binding_category.debug"); + public static final Component GUI_CATEGORY = Component.translatable("controlify.binding_category.gui"); + public static final Component RADIAL_CATEGORY = Component.translatable("controlify.gui.radial_menu"); + public static final Component VMOUSE_CATEGORY = Component.translatable("controlify.binding_category.vmouse"); + + public static final ActionAccessor WALK_FORWARD = register(ActionSpec.builder() + .id(CUtil.rl("walk_forward")) + .category(MOVEMENT_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor WALK_BACKWARD = register(ActionSpec.builder() + .id(CUtil.rl("walk_backward")) + .category(MOVEMENT_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor WALK_LEFT = register(ActionSpec.builder() + .id(CUtil.rl("strafe_left")) + .category(MOVEMENT_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor WALK_RIGHT = register(ActionSpec.builder() + .id(CUtil.rl("strafe_right")) + .category(MOVEMENT_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor LOOK_UP = register(ActionSpec.builder() + .id(CUtil.rl("look_up")) + .category(MOVEMENT_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor LOOK_DOWN = register(ActionSpec.builder() + .id(CUtil.rl("look_down")) + .category(MOVEMENT_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor LOOK_LEFT = register(ActionSpec.builder() + .id(CUtil.rl("look_left")) + .category(MOVEMENT_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor LOOK_RIGHT = register(ActionSpec.builder() + .id(CUtil.rl("look_right")) + .category(MOVEMENT_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor GYRO_BUTTON = register(ActionSpec.builder() + .id(CUtil.rl("gyro_button")) + .category(MOVEMENT_CATEGORY) + .context(BuiltinActionContexts.IN_GAME) + .shouldExpose(c -> c.gyro().isPresent())); + public static final ActionAccessor JUMP = register(ActionSpec.builder() + .id(CUtil.rl("jump")) + .category(MOVEMENT_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor SPRINT = register(ActionSpec.builder() + .id(CUtil.rl("sprint")) + .category(MOVEMENT_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor SNEAK = register(ActionSpec.builder() + .id(CUtil.rl("sneak")) + .category(MOVEMENT_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor ATTACK = register(ActionSpec.builder() + .id(CUtil.rl("attack")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor USE = register(ActionSpec.builder() + .id(CUtil.rl("use")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor DROP_INGAME = register(ActionSpec.builder() + .id(CUtil.rl("drop")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor DROP_STACK = register(ActionSpec.builder() + .id(CUtil.rl("drop_stack")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor PAUSE = register(ActionSpec.builder() + .id(CUtil.rl("pause")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME, BuiltinActionContexts.REGULAR_SCREEN)); + public static final ActionAccessor CHANGE_PERSPECTIVE = register(ActionSpec.builder() + .id(CUtil.rl("change_perspective")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor SWAP_HANDS = register(ActionSpec.builder() + .id(CUtil.rl("swap_hands")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor NEXT_SLOT = register(ActionSpec.builder() + .id(CUtil.rl("next_slot")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor PREV_SLOT = register(ActionSpec.builder() + .id(CUtil.rl("prev_slot")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor HOTBAR_SLOT_SELECT = register(ActionSpec.builder() + .id(CUtil.rl("hotbar_item_select_radial")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME, BuiltinActionContexts.RADIAL_MENU)); + public static final ActionAccessor HOTBAR_SLOT_1 = register(ActionSpec.builder() + .id(CUtil.rl("hotbar_slot_1")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor HOTBAR_SLOT_2 = register(ActionSpec.builder() + .id(CUtil.rl("hotbar_slot_2")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor HOTBAR_SLOT_3 = register(ActionSpec.builder() + .id(CUtil.rl("hotbar_slot_3")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor HOTBAR_SLOT_4 = register(ActionSpec.builder() + .id(CUtil.rl("hotbar_slot_4")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor HOTBAR_SLOT_5 = register(ActionSpec.builder() + .id(CUtil.rl("hotbar_slot_5")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor HOTBAR_SLOT_6 = register(ActionSpec.builder() + .id(CUtil.rl("hotbar_slot_6")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor HOTBAR_SLOT_7 = register(ActionSpec.builder() + .id(CUtil.rl("hotbar_slot_7")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor HOTBAR_SLOT_8 = register(ActionSpec.builder() + .id(CUtil.rl("hotbar_slot_8")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor HOTBAR_SLOT_9 = register(ActionSpec.builder() + .id(CUtil.rl("hotbar_slot_9")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor GAME_MODE_SWITCHER = register(ActionSpec.builder() + .id(CUtil.rl("game_mode_switcher")) + .category(GAMEPLAY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME, BuiltinActionContexts.RADIAL_MENU)); + public static final ActionAccessor INVENTORY = register(ActionSpec.builder() + .id(CUtil.rl("inventory")) + .category(INVENTORY_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor INV_SELECT = register(ActionSpec.builder() + .id(CUtil.rl("inv_select")) + .category(INVENTORY_CATEGORY) + .context(BuiltinActionContexts.CONTAINER)); + public static final ActionAccessor INV_QUICK_MOVE = register(ActionSpec.builder() + .id(CUtil.rl("inv_quick_move")) + .category(INVENTORY_CATEGORY) + .context(BuiltinActionContexts.CONTAINER)); + public static final ActionAccessor INV_TAKE_HALF = register(ActionSpec.builder() + .id(CUtil.rl("inv_take_half")) + .category(INVENTORY_CATEGORY) + .context(BuiltinActionContexts.CONTAINER)); + public static final ActionAccessor DROP_INVENTORY = register(ActionSpec.builder() + .id(CUtil.rl("drop_inventory")) + .category(INVENTORY_CATEGORY) + .context(BuiltinActionContexts.CONTAINER, BuiltinActionContexts.REGULAR_SCREEN)); + public static final ActionAccessor PICK_BLOCK = register(ActionSpec.builder() + .id(CUtil.rl("pick_block")) + .category(CREATIVE_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor PICK_BLOCK_NBT = register(ActionSpec.builder() + .id(CUtil.rl("pick_block_nbt")) + .category(CREATIVE_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor HOTBAR_LOAD_RADIAL = register(ActionSpec.builder() + .id(CUtil.rl("hotbar_load_radial")) + .category(CREATIVE_CATEGORY) + .context(BuiltinActionContexts.IN_GAME, BuiltinActionContexts.RADIAL_MENU)); + public static final ActionAccessor HOTBAR_SAVE_RADIAL = register(ActionSpec.builder() + .id(CUtil.rl("hotbar_save_radial")) + .category(CREATIVE_CATEGORY) + .context(BuiltinActionContexts.IN_GAME, BuiltinActionContexts.RADIAL_MENU)); + public static final ActionAccessor OPEN_CHAT = register(ActionSpec.builder() + .id(CUtil.rl("open_chat")) + .category(MISC_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor TOGGLE_HUD_VISIBILITY = register(ActionSpec.builder() + .id(CUtil.rl("toggle_hud_visibility")) + .category(MISC_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor SHOW_PLAYER_LIST = register(ActionSpec.builder() + .id(CUtil.rl("show_player_list")) + .category(MISC_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor TAKE_SCREENSHOT = register(ActionSpec.builder() + .id(CUtil.rl("take_screenshot")) + .category(MISC_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor DEBUG_RADIAL = register(ActionSpec.builder() + .id(CUtil.rl("debug_radial")) + .category(DEBUG_CATEGORY) + .context(BuiltinActionContexts.IN_GAME, BuiltinActionContexts.RADIAL_MENU)); + public static final ActionAccessor TOGGLE_DEBUG_MENU = register(ActionSpec.builder() + .id(CUtil.rl("toggle_debug_menu")) + .category(DEBUG_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor TOGGLE_DEBUG_MENU_FPS = register(ActionSpec.builder() + .id(CUtil.rl("toggle_debug_menu_fps")) + .category(DEBUG_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor TOGGLE_DEBUG_MENU_NET = register(ActionSpec.builder() + .id(CUtil.rl("toggle_debug_menu_net")) + .category(DEBUG_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor TOGGLE_DEBUG_MENU_PROF = register(ActionSpec.builder() + .id(CUtil.rl("toggle_debug_menu_prof")) + .category(DEBUG_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor DEBUG_RELOAD_CHUNKS = register(ActionSpec.builder() + .id(CUtil.rl("debug_reload_chunks")) + .category(DEBUG_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor DEBUG_TOGGLE_CHUNK_BORDERS = register(ActionSpec.builder() + .id(CUtil.rl("debug_toggle_chunk_borders")) + .category(DEBUG_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor DEBUG_TOGGLE_ADVANCED_TOOLTIPS = register(ActionSpec.builder() + .id(CUtil.rl("debug_toggle_advanced_tooltips")) + .category(DEBUG_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor DEBUG_TOGGLE_ENTITY_HITBOXES = register(ActionSpec.builder() + .id(CUtil.rl("debug_toggle_entity_hitboxes")) + .category(DEBUG_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor DEBUG_RELOAD_RESOURCE_PACKS = register(ActionSpec.builder() + .id(CUtil.rl("debug_reload_resource_packs")) + .category(DEBUG_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor DEBUG_CLEAR_CHAT = register(ActionSpec.builder() + .id(CUtil.rl("debug_clear_chat")) + .category(DEBUG_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor DEBUG_START_STOP_PROFILING = register(ActionSpec.builder() + .id(CUtil.rl("debug_start_stop_profiling")) + .category(DEBUG_CATEGORY) + .context(BuiltinActionContexts.IN_GAME)); + public static final ActionAccessor GUI_PRESS = register(ActionSpec.builder() + .id(CUtil.rl("gui_press")) + .category(GUI_CATEGORY) + .context(BuiltinActionContexts.REGULAR_SCREEN)); + public static final ActionAccessor GUI_BACK = register(ActionSpec.builder() + .id(CUtil.rl("gui_back")) + .category(GUI_CATEGORY) + .context(BuiltinActionContexts.ANY_SCREEN)); + public static final ActionAccessor GUI_NEXT_TAB = register(ActionSpec.builder() + .id(CUtil.rl("gui_next_tab")) + .category(GUI_CATEGORY) + .context(BuiltinActionContexts.ANY_SCREEN)); + public static final ActionAccessor GUI_PREV_TAB = register(ActionSpec.builder() + .id(CUtil.rl("gui_prev_tab")) + .category(GUI_CATEGORY) + .context(BuiltinActionContexts.ANY_SCREEN)); + public static final ActionAccessor GUI_ABSTRACT_ACTION_1 = register(ActionSpec.builder() + .id(CUtil.rl("gui_abstract_action_1")) + .category(GUI_CATEGORY) + .context(BuiltinActionContexts.REGULAR_SCREEN)); + public static final ActionAccessor GUI_ABSTRACT_ACTION_2 = register(ActionSpec.builder() + .id(CUtil.rl("gui_abstract_action_2")) + .category(GUI_CATEGORY) + .context(BuiltinActionContexts.REGULAR_SCREEN)); + public static final ActionAccessor GUI_ABSTRACT_ACTION_3 = register(ActionSpec.builder() + .id(CUtil.rl("gui_abstract_action_3")) + .category(GUI_CATEGORY) + .context(BuiltinActionContexts.REGULAR_SCREEN)); + public static final ActionAccessor GUI_NAVI_UP = register(ActionSpec.builder() + .id(CUtil.rl("gui_navi_up")) + .category(GUI_CATEGORY) + .context(BuiltinActionContexts.REGULAR_SCREEN)); + public static final ActionAccessor GUI_NAVI_DOWN = register(ActionSpec.builder() + .id(CUtil.rl("gui_navi_down")) + .category(GUI_CATEGORY) + .context(BuiltinActionContexts.REGULAR_SCREEN)); + public static final ActionAccessor GUI_NAVI_LEFT = register(ActionSpec.builder() + .id(CUtil.rl("gui_navi_left")) + .category(GUI_CATEGORY) + .context(BuiltinActionContexts.REGULAR_SCREEN)); + public static final ActionAccessor GUI_NAVI_RIGHT = register(ActionSpec.builder() + .id(CUtil.rl("gui_navi_right")) + .category(GUI_CATEGORY) + .context(BuiltinActionContexts.REGULAR_SCREEN)); + public static final ActionAccessor GUI_SECONDARY_NAVI_UP = register(ActionSpec.builder() + .id(CUtil.rl("gui_secondary_navi_up")) + .category(GUI_CATEGORY) + .context(BuiltinActionContexts.CONTAINER, BuiltinActionContexts.REGULAR_SCREEN)); + public static final ActionAccessor GUI_SECONDARY_NAVI_DOWN = register(ActionSpec.builder() + .id(CUtil.rl("gui_secondary_navi_down")) + .category(GUI_CATEGORY) + .context(BuiltinActionContexts.CONTAINER, BuiltinActionContexts.REGULAR_SCREEN)); + public static final ActionAccessor GUI_SECONDARY_NAVI_LEFT = register(ActionSpec.builder() + .id(CUtil.rl("gui_secondary_navi_left")) + .category(GUI_CATEGORY) + .context(BuiltinActionContexts.CONTAINER, BuiltinActionContexts.REGULAR_SCREEN)); + public static final ActionAccessor GUI_SECONDARY_NAVI_RIGHT = register(ActionSpec.builder() + .id(CUtil.rl("gui_secondary_navi_right")) + .category(GUI_CATEGORY) + .context(BuiltinActionContexts.CONTAINER, BuiltinActionContexts.REGULAR_SCREEN)); + public static final ActionAccessor RADIAL_MENU = register(ActionSpec.builder() + .id(CUtil.rl("radial_menu")) + .category(RADIAL_CATEGORY) + .context(BuiltinActionContexts.IN_GAME, BuiltinActionContexts.RADIAL_MENU)); + public static final ActionAccessor RADIAL_AXIS_UP = register(ActionSpec.builder() + .id(CUtil.rl("radial_axis_up")) + .category(RADIAL_CATEGORY) + .context(BuiltinActionContexts.RADIAL_MENU)); + public static final ActionAccessor RADIAL_AXIS_DOWN = register(ActionSpec.builder() + .id(CUtil.rl("radial_axis_down")) + .category(RADIAL_CATEGORY) + .context(BuiltinActionContexts.RADIAL_MENU)); + public static final ActionAccessor RADIAL_AXIS_LEFT = register(ActionSpec.builder() + .id(CUtil.rl("radial_axis_left")) + .category(RADIAL_CATEGORY) + .context(BuiltinActionContexts.RADIAL_MENU)); + public static final ActionAccessor RADIAL_AXIS_RIGHT = register(ActionSpec.builder() + .id(CUtil.rl("radial_axis_right")) + .category(RADIAL_CATEGORY) + .context(BuiltinActionContexts.RADIAL_MENU)); + public static final ActionAccessor VMOUSE_MOVE_UP = register(ActionSpec.builder() + .id(CUtil.rl("vmouse_move_up")) + .category(VMOUSE_CATEGORY) + .context(BuiltinActionContexts.V_MOUSE_CURSOR)); + public static final ActionAccessor VMOUSE_MOVE_DOWN = register(ActionSpec.builder() + .id(CUtil.rl("vmouse_move_down")) + .category(VMOUSE_CATEGORY) + .context(BuiltinActionContexts.V_MOUSE_CURSOR)); + public static final ActionAccessor VMOUSE_MOVE_LEFT = register(ActionSpec.builder() + .id(CUtil.rl("vmouse_move_left")) + .category(VMOUSE_CATEGORY) + .context(BuiltinActionContexts.V_MOUSE_CURSOR)); + public static final ActionAccessor VMOUSE_MOVE_RIGHT = register(ActionSpec.builder() + .id(CUtil.rl("vmouse_move_right")) + .category(VMOUSE_CATEGORY) + .context(BuiltinActionContexts.V_MOUSE_CURSOR)); + public static final ActionAccessor VMOUSE_SNAP_UP = register(ActionSpec.builder() + .id(CUtil.rl("vmouse_snap_up")) + .category(VMOUSE_CATEGORY) + .context(BuiltinActionContexts.V_MOUSE_CURSOR)); + public static final ActionAccessor VMOUSE_SNAP_DOWN = register(ActionSpec.builder() + .id(CUtil.rl("vmouse_snap_down")) + .category(VMOUSE_CATEGORY) + .context(BuiltinActionContexts.V_MOUSE_CURSOR)); + public static final ActionAccessor VMOUSE_SNAP_LEFT = register(ActionSpec.builder() + .id(CUtil.rl("vmouse_snap_left")) + .category(VMOUSE_CATEGORY) + .context(BuiltinActionContexts.V_MOUSE_CURSOR)); + public static final ActionAccessor VMOUSE_SNAP_RIGHT = register(ActionSpec.builder() + .id(CUtil.rl("vmouse_snap_right")) + .category(VMOUSE_CATEGORY) + .context(BuiltinActionContexts.V_MOUSE_CURSOR)); + public static final ActionAccessor VMOUSE_LCLICK = register(ActionSpec.builder() + .id(CUtil.rl("vmouse_lclick")) + .category(VMOUSE_CATEGORY) + .context(BuiltinActionContexts.V_MOUSE_COMPAT, BuiltinActionContexts.V_MOUSE_CURSOR)); + public static final ActionAccessor VMOUSE_RCLICK = register(ActionSpec.builder() + .id(CUtil.rl("vmouse_rclick")) + .category(VMOUSE_CATEGORY) + .context(BuiltinActionContexts.V_MOUSE_COMPAT, BuiltinActionContexts.V_MOUSE_CURSOR)); + public static final ActionAccessor VMOUSE_SHIFT_CLICK = register(ActionSpec.builder() + .id(CUtil.rl("vmouse_shift_click")) + .category(VMOUSE_CATEGORY) + .context(BuiltinActionContexts.V_MOUSE_COMPAT, BuiltinActionContexts.V_MOUSE_CURSOR)); + public static final ActionAccessor VMOUSE_SCROLL_DOWN = register(ActionSpec.builder() + .id(CUtil.rl("vmouse_scroll_down")) + .category(VMOUSE_CATEGORY) + .context(BuiltinActionContexts.V_MOUSE_COMPAT, BuiltinActionContexts.V_MOUSE_CURSOR)); + public static final ActionAccessor VMOUSE_SCROLL_UP = register(ActionSpec.builder() + .id(CUtil.rl("vmouse_scroll_up")) + .category(VMOUSE_CATEGORY) + .context(BuiltinActionContexts.V_MOUSE_COMPAT, BuiltinActionContexts.V_MOUSE_CURSOR)); + public static final ActionAccessor VMOUSE_SHIFT = register(ActionSpec.builder() + .id(CUtil.rl("vmouse_shift")) + .category(VMOUSE_CATEGORY) + .context(BuiltinActionContexts.V_MOUSE_COMPAT, BuiltinActionContexts.V_MOUSE_CURSOR)); + public static final ActionAccessor VMOUSE_PAGE_NEXT = register(ActionSpec.builder() + .id(CUtil.rl("vmouse_page_next")) + .category(VMOUSE_CATEGORY) + .context(BuiltinActionContexts.V_MOUSE_COMPAT, BuiltinActionContexts.V_MOUSE_CURSOR)); + public static final ActionAccessor VMOUSE_PAGE_PREV = register(ActionSpec.builder() + .id(CUtil.rl("vmouse_page_prev")) + .category(VMOUSE_CATEGORY) + .context(BuiltinActionContexts.V_MOUSE_COMPAT, BuiltinActionContexts.V_MOUSE_CURSOR)); + public static final ActionAccessor VMOUSE_PAGE_DOWN = register(ActionSpec.builder() + .id(CUtil.rl("vmouse_page_down")) + .category(VMOUSE_CATEGORY) + .context(BuiltinActionContexts.V_MOUSE_COMPAT, BuiltinActionContexts.V_MOUSE_CURSOR)); + public static final ActionAccessor VMOUSE_PAGE_UP = register(ActionSpec.builder() + .id(CUtil.rl("vmouse_page_up")) + .category(VMOUSE_CATEGORY) + .context(BuiltinActionContexts.V_MOUSE_COMPAT, BuiltinActionContexts.V_MOUSE_CURSOR)); + public static final ActionAccessor VMOUSE_TOGGLE = register(ActionSpec.builder() + .id(CUtil.rl("vmouse_toggle")) + .category(VMOUSE_CATEGORY) + .context(BuiltinActionContexts.V_MOUSE_CURSOR, BuiltinActionContexts.V_MOUSE_COMPAT, BuiltinActionContexts.ANY_SCREEN)); + + private static ActionAccessor register(ActionSpecBuilder builder) { + return ActionSpecRegistryImpl.INSTANCE.register(builder.build()); + } + + public static void init() { + ActionEvents.REGISTER_PROXIES.subscribe(ControlifyActions::registerProxies); + } + + private static void registerProxies(ActionProxyManager proxies) { + proxies.proxyKeyMapping(SPRINT, options.keySprint); + proxies.proxyKeyMapping(ATTACK, options.keyAttack); + proxies.proxyKeyMapping(USE, options.keyUse); + proxies.proxyKeyMapping(OPEN_CHAT, options.keyChat); + } + + public static void registerModdedActions() { + for (KeyMapping keyMapping : PlatformClientUtil.getModdedKeyMappings()) { + if (!ActionProxyManagerImpl.INSTANCE.shouldAutomaticallyProxyKeyMapping(keyMapping)) { + continue; + } + try { + String idPath = keyMapping.getName().toLowerCase(Locale.ROOT) + .replaceAll("[^a-z0-9/._-]", "_").trim(); + Identifier id = Identifier.fromNamespaceAndPath(MODDED_ACTION_NAMESPACE, idPath); + ActionAccessor action = register(ActionSpec.builder() + .id(id) + .name(Component.translatable(keyMapping.getName())) + .description(Component.translatable("controlify.custom_binding.vanilla_description").withStyle(ChatFormatting.GRAY)) + .category(keyMapping.getCategory().label()) + .context(BuiltinActionContexts.IN_GAME)); + ActionProxyManagerImpl.INSTANCE.proxyKeyMapping(action, keyMapping); + } catch (Exception e) { + CUtil.LOGGER.error("Failed to automatically register modded action: {}", keyMapping.getName(), e); + } + } + } + + private ControlifyActions() { + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/action/KeyMappingHandle.java b/src/main/java/dev/isxander/controlify/impl/action/KeyMappingHandle.java new file mode 100644 index 000000000..e4856f9f7 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/action/KeyMappingHandle.java @@ -0,0 +1,8 @@ +package dev.isxander.controlify.impl.action; + +/// Applies emulated output without changing Minecraft's keyboard toggle policy. +public interface KeyMappingHandle { + void controlify$click(); + + void controlify$setDown(boolean down); +} diff --git a/src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfigManager.java b/src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfigManager.java index 4efba9b63..b69333dc3 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfigManager.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfigManager.java @@ -11,7 +11,9 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.impl.controller.input.InputComponent; +import dev.isxander.controlify.impl.action.ActionSpecRegistryImpl; +import dev.isxander.controlify.impl.action.proxy.ActionProxyManagerImpl; +import dev.isxander.controlify.impl.controller.input.InputComponentImpl; import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; @@ -28,12 +30,19 @@ public class ActivatorConfigManager implements SimpleControlifyReloadListener>> { private static final Identifier DEFINITIONS = CUtil.rl("controller/activators.json"); private static final Codec>> CODEC = Codec.unboundedMap(Identifier.CODEC, ActivatorConfig.CODEC); - private static final ActivatorConfig FALLBACK = new CompoundActivator.Config(List.of()); + + private static final ActivatorConfig FALLBACK = + new CompoundActivator.Config(List.of()); + private static final ActivatorConfig PROXY_ACTIVATOR = + new CompoundActivator.Config(List.of(HoldLatchActivator.Config.INSTANCE, PressPulseActivator.Config.INSTANCE)); private volatile Map> configsByAction = Map.of(); public ActivatorConfig getConfig(Identifier actionId) { - return configsByAction.getOrDefault(actionId, FALLBACK); + return configsByAction.getOrDefault( + actionId, + ActionProxyManagerImpl.INSTANCE.isKeyMappingProxy(actionId) ? PROXY_ACTIVATOR : FALLBACK + ); } @Override @@ -67,11 +76,8 @@ public CompletableFuture apply(Map> data, R return CompletableFuture.runAsync(() -> { configsByAction = data; Controlify.instance().getControllerManager().ifPresent(controllerManager -> { - for (var controller : controllerManager.getConnectedControllers()) { - // TODO - if (controller.getAllComponents().get(InputComponent.ID) instanceof InputComponent input) { - input.reloadActions(); - } + for (var controller : controllerManager.getAllControllers()) { + controller.input().reloadActions(); } }); }, executor); diff --git a/src/main/java/dev/isxander/controlify/impl/action/activator/RepeatLatchPulseActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/RepeatLatchPulseActivator.java index 1a6213732..0f8c50896 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/activator/RepeatLatchPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/RepeatLatchPulseActivator.java @@ -3,12 +3,14 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; +import dev.isxander.controlify.api.input.LogicalInput; import dev.isxander.controlify.impl.action.Accumulator; import dev.isxander.controlify.impl.action.Activator; -import dev.isxander.controlify.api.input.LogicalInput; import dev.isxander.controlify.impl.input.signal.LogicalSignal; -/// Accepts another latch binding, and sends repeat pulses out while the latch is active. +/// Emits a pulse immediately when the source latch becomes active, then repeats while held. +/// initialDelayMs delays the first repeat, not the initial pulse. +/// Do not combine with a press-pulse activator unless two initial pulses are intended. public class RepeatLatchPulseActivator implements Activator { private final Activator latchActivator; diff --git a/src/main/java/dev/isxander/controlify/impl/action/defaults/DefaultBindManager.java b/src/main/java/dev/isxander/controlify/impl/action/defaults/DefaultBindManager.java index 68e47e1b9..ff879696f 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/defaults/DefaultBindManager.java +++ b/src/main/java/dev/isxander/controlify/impl/action/defaults/DefaultBindManager.java @@ -11,8 +11,10 @@ import com.mojang.datafixers.util.Pair; import com.mojang.logging.LogUtils; import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; import com.mojang.serialization.JsonOps; import com.mojang.serialization.codecs.RecordCodecBuilder; +import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.controllermanager.ControllerType; import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; import dev.isxander.controlify.impl.utils.CUtil; @@ -62,8 +64,9 @@ public class DefaultBindManager implements SimpleControlifyReloadListener files = stack.getValue(); - if (id.equals(defaultNamespaceFile)) + if (id.equals(defaultNamespaceFile)) { continue; // already processed + } Pair> defaults = this.readDefaults(id, files); // add the default id to the bottom @@ -85,10 +88,18 @@ private Pair> readDefaults(Id for (Resource resource : files) { try (BufferedReader reader = resource.openAsReader()) { JsonElement json = JsonParser.parseReader(reader); - ControllerDefault def = ControllerDefault.CODEC.parse(JsonOps.INSTANCE, json).result().orElseThrow(); + DataResult data = ControllerDefault.CODEC.parse(JsonOps.INSTANCE, json); + + var error = data.error(); + if (error.isPresent()) { + LOGGER.error("Failed to parse for id {}: {}", id, error.get().message()); + continue; + } + + ControllerDefault def = data.getOrThrow(); // add to top takes priority - defaults.add(0, new LayeredDefaultBindProvider.Layer(def.provider(), def.clearBelow())); + defaults.addFirst(new LayeredDefaultBindProvider.Layer(def.provider(), def.clearBelow())); } catch (IOException | IllegalStateException e) { LOGGER.error("Failed to parse {}", id, e); } @@ -107,17 +118,23 @@ public CompletableFuture apply(@Nullable Preparations data, ResourceManage if (data != null) { this.defaultsByNamespace.putAll(data.map()); } - // No binding updates here: when keepDefaultBindings is true, defaults are serialized - // and must not be changed by default provider reloads. + // Refresh action defaults from the new providers without rewriting saved bindings. + Controlify.instance().getControllerManager().ifPresent(controllerManager -> { + for (var controller : controllerManager.getAllControllers()) { + controller.input().reloadDefaultInputs(); + } + }); }, executor); } public DefaultBindProvider getDefaultBindProvider(Identifier namespace) { DefaultBindProvider provider = this.defaultsByNamespace.get(namespace); - if (provider == null) + if (provider == null) { provider = this.defaultsByNamespace.get(ControllerType.DEFAULT.id()); - if (provider == null) + } + if (provider == null) { provider = DefaultBindProvider.EMPTY; + } return provider; } diff --git a/src/main/java/dev/isxander/controlify/impl/action/defaults/MapBackedDefaultBindProvider.java b/src/main/java/dev/isxander/controlify/impl/action/defaults/MapBackedDefaultBindProvider.java index c3697c3dd..a34b0e2a8 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/defaults/MapBackedDefaultBindProvider.java +++ b/src/main/java/dev/isxander/controlify/impl/action/defaults/MapBackedDefaultBindProvider.java @@ -9,21 +9,22 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.Keyable; import com.mojang.serialization.MapCodec; -import dev.isxander.controlify.impl.api.bind.ControlifyBindApi; import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.impl.action.ActionSpecRegistryImpl; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; import java.util.Map; +import java.util.Optional; -public record MapBackedDefaultBindProvider(Map map) implements DefaultBindProvider { +public record MapBackedDefaultBindProvider(Map> map) implements DefaultBindProvider { public static final MapCodec MAP_CODEC = Codec.simpleMap( - Identifier.CODEC, LogicalInput.CODEC, - Keyable.forStrings(() -> ControlifyBindApi.get().getAllBindIds().map(Identifier::toString)) + Identifier.CODEC, LogicalInput.OPTIONAL_CODEC, + Keyable.forStrings(() -> ActionSpecRegistryImpl.INSTANCE.getActionSpecs().stream().map(spec -> spec.id().toString())) ).xmap(MapBackedDefaultBindProvider::new, MapBackedDefaultBindProvider::map); @Override public @Nullable LogicalInput getDefaultInput(Identifier actionId) { - return map.get(actionId); + return this.map.getOrDefault(actionId, Optional.empty()).orElse(null); } } diff --git a/src/main/java/dev/isxander/controlify/impl/action/proxy/ActionProxyManagerImpl.java b/src/main/java/dev/isxander/controlify/impl/action/proxy/ActionProxyManagerImpl.java new file mode 100644 index 000000000..279168d66 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/action/proxy/ActionProxyManagerImpl.java @@ -0,0 +1,93 @@ +package dev.isxander.controlify.impl.action.proxy; + +import dev.isxander.controlify.api.action.ActionProxyManager; +import dev.isxander.controlify.impl.action.ActionAccessorImpl; +import dev.isxander.controlify.impl.action.ActionSpecRegistryImpl; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.utils.event.EventBus; +import net.minecraft.client.KeyMapping; +import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public final class ActionProxyManagerImpl implements ActionProxyManager { + /// Subscriptions do not initialize the proxy manager singleton. + public static final class Events { + public static final EventBus REGISTER_PROXIES = new EventBus<>(); + + private Events() { + } + } + + public static final ActionProxyManagerImpl INSTANCE = new ActionProxyManagerImpl(); + + private final Set excludedKeyMappings = new HashSet<>(); + private final Map proxiedKeyMappings = new HashMap<>(); + private final Map keyMappingProxies = new LinkedHashMap<>(); + private final EventBus proxyUpdates = new EventBus<>(); + private boolean locked; + + private ActionProxyManagerImpl() { + } + + @Override + public void dontAutoProxyKeyMapping(@NonNull KeyMapping keyMapping) { + this.checkRegistrationOpen(); + this.excludedKeyMappings.add(Objects.requireNonNull(keyMapping)); + } + + @Override + public void proxyKeyMapping(@NonNull Identifier actionId, @NonNull KeyMapping keyMapping) { + this.checkRegistrationOpen(); + Objects.requireNonNull(actionId); + Objects.requireNonNull(keyMapping); + if (!ActionSpecRegistryImpl.INSTANCE.isRegistered(actionId)) { + throw new IllegalArgumentException("Action is not registered: " + actionId); + } + Identifier previousAction = this.proxiedKeyMappings.get(keyMapping); + if (previousAction != null && !previousAction.equals(actionId)) { + throw new IllegalArgumentException("Key mapping is already proxied by " + previousAction); + } + KeyMappingProxy proxy = this.keyMappingProxies.computeIfAbsent(actionId, id -> { + KeyMappingProxy listener = new KeyMappingProxy(new ActionAccessorImpl(id)); + this.proxyUpdates.subscribe(listener); + return listener; + }); + proxy.addKeyMapping(keyMapping); + this.proxiedKeyMappings.put(keyMapping, actionId); + } + + public boolean shouldAutomaticallyProxyKeyMapping(KeyMapping keyMapping) { + return !this.excludedKeyMappings.contains(keyMapping) && !this.proxiedKeyMappings.containsKey(keyMapping); + } + + public boolean isKeyMappingProxy(Identifier actionId) { + return this.keyMappingProxies.containsKey(actionId); + } + + public void update(ControllerImpl controller) { + this.proxyUpdates.emit(controller); + } + + public void release(ControllerImpl controller) { + for (KeyMappingProxy proxy : this.keyMappingProxies.values()) { + proxy.release(controller); + } + } + + public void lock() { + this.locked = true; + } + + private void checkRegistrationOpen() { + if (this.locked) { + throw new IllegalStateException("Action proxy registration has closed"); + } + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/action/proxy/KeyMappingProxy.java b/src/main/java/dev/isxander/controlify/impl/action/proxy/KeyMappingProxy.java new file mode 100644 index 000000000..267b2034c --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/action/proxy/KeyMappingProxy.java @@ -0,0 +1,79 @@ +package dev.isxander.controlify.impl.action.proxy; + +import dev.isxander.controlify.api.action.Action; +import dev.isxander.controlify.api.action.ActionAccessor; +import dev.isxander.controlify.api.utils.event.EventSink; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.action.KeyMappingHandle; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import net.minecraft.client.KeyMapping; +import net.minecraft.client.Minecraft; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.util.LinkedHashSet; +import java.util.Set; + +/// Consumes an action's pulses and forwards its held state to explicitly registered key mappings. +final class KeyMappingProxy implements EventSink { + private final ActionAccessor action; + private final Set keyMappings = new LinkedHashSet<>(); + private @Nullable ControllerImpl owner; + private boolean held; + + KeyMappingProxy(ActionAccessor action) { + this.action = action; + } + + void addKeyMapping(KeyMapping keyMapping) { + this.keyMappings.add(keyMapping); + } + + @Override + public void accept(@NonNull ControllerImpl controller) { + Action resolvedAction = this.action.onOrNull(controller); + Minecraft minecraft = Minecraft.getInstance(); + boolean enabled = controller.isActive() + && Controlify.instance().currentInputMode().isController() + && (minecraft.isWindowActive() || Controlify.instance().config().getSettings().globalSettings().outOfFocusInput); + if (!enabled || resolvedAction == null) { + if (resolvedAction != null) { + while (resolvedAction.pulse().consumePulse()) { + // Discard ineligible input instead of replaying it when the controller becomes active. + } + } + this.release(controller); + return; + } + if (this.owner != null && this.owner != controller) { + this.release(this.owner); + } + this.owner = controller; + + while (resolvedAction.pulse().consumePulse()) { + for (KeyMapping keyMapping : this.keyMappings) { + ((KeyMappingHandle) keyMapping).controlify$click(); + } + } + boolean down = resolvedAction.latch().isLatchActive(); + if (down || this.held) { + for (KeyMapping keyMapping : this.keyMappings) { + ((KeyMappingHandle) keyMapping).controlify$setDown(down); + } + } + this.held = down; + } + + void release(ControllerImpl controller) { + if (this.owner != controller) { + return; + } + if (this.held) { + for (KeyMapping keyMapping : this.keyMappings) { + ((KeyMappingHandle) keyMapping).controlify$setDown(false); + } + } + this.owner = null; + this.held = false; + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/action/radial/RadialIconManager.java b/src/main/java/dev/isxander/controlify/impl/action/radial/RadialIconManager.java index aece6c14f..d89e4eba7 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/radial/RadialIconManager.java +++ b/src/main/java/dev/isxander/controlify/impl/action/radial/RadialIconManager.java @@ -10,7 +10,7 @@ import com.google.gson.JsonParser; import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; @@ -36,7 +36,7 @@ public class RadialIconManager implements SimpleControlifyReloadListener functional(BiFunction x, BiFunction y) { - return event -> { - event.lookInput.x = x.apply(event.lookInput.x, event.controller); - event.lookInput.y = y.apply(event.lookInput.y, event.controller); - }; - } - - /** - * Creates a new callback that zeroes out the look input if the predicate is true. - * - * @param condition the condition that, if true, sets both axes to zero - * @return the new LookInputModifier object - */ - static EventHandler.Callback zeroIf(Predicate condition) { - return event -> { - if (condition.test(event.controller)) { - event.lookInput.set(0, 0); - } - }; - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/bindings/BindContext.java b/src/main/java/dev/isxander/controlify/impl/bindings/BindContext.java deleted file mode 100644 index 8a5aa88b9..000000000 --- a/src/main/java/dev/isxander/controlify/impl/bindings/BindContext.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.bindings; - -import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.impl.gui.screen.RadialMenuScreen; -import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.impl.utils.CUtil; -import dev.isxander.controlify.impl.utils.MinecraftUtil; -import dev.isxander.controlify.impl.virtualmouse.VirtualMouseBehaviour; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; -import net.minecraft.resources.Identifier; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - -public record BindContext(Identifier id, Function isApplicable) { - public static final Map CONTEXTS = new HashMap<>(); - - public static final BindContext UNKNOWN = register( - "unknown", - mc -> true - ); - - public static final BindContext IN_GAME = register( - "in_game", - mc -> MinecraftUtil.getScreen() == null && mc.level != null && mc.player != null - ); - - public static final BindContext ANY_SCREEN = register( - "screen", - mc -> MinecraftUtil.getScreen() != null - ); - - public static final BindContext REGULAR_SCREEN = register( - "regular_screen", - mc -> MinecraftUtil.getScreen() != null - && !Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled() - ); - - public static final BindContext CONTAINER = register( - "container", - mc -> MinecraftUtil.getScreen() instanceof AbstractContainerScreen - ); - - public static final BindContext V_MOUSE_CURSOR = register( - "vmouse_cursor", - mc -> MinecraftUtil.getScreen() != null - && ScreenProcessorProvider.provide(MinecraftUtil.getScreen()).virtualMouseBehaviour().hasCursor() - && Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled() - ); - - public static final BindContext V_MOUSE_COMPAT = register( - "vmouse_compat", - mc -> MinecraftUtil.getScreen() != null - && ScreenProcessorProvider.provide(MinecraftUtil.getScreen()).virtualMouseBehaviour() == VirtualMouseBehaviour.ENABLED - && Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled() - ); - - public static final BindContext RADIAL_MENU = register( - "radial_menu", - mc -> MinecraftUtil.getScreen() instanceof RadialMenuScreen - ); - - private static BindContext register(String path, Function predicate) { - var context = new BindContext(CUtil.rl(path), predicate); - CONTEXTS.put(context.id, context); - return context; - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/bindings/ControlifyBindings.java b/src/main/java/dev/isxander/controlify/impl/bindings/ControlifyBindings.java deleted file mode 100644 index 58e0d183c..000000000 --- a/src/main/java/dev/isxander/controlify/impl/bindings/ControlifyBindings.java +++ /dev/null @@ -1,484 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.bindings; - -import dev.isxander.controlify.impl.api.bind.ControlifyBindApi; -import dev.isxander.controlify.impl.api.bind.InputBindingSupplier; -import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; -import dev.isxander.controlify.impl.utils.CUtil; -import net.minecraft.ChatFormatting; -import net.minecraft.client.KeyMapping; -import net.minecraft.client.Minecraft; -import net.minecraft.client.Options; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; - -import java.util.LinkedHashMap; -import java.util.Map; - -public final class ControlifyBindings { - static final String MODDED_BIND_NAMESPACE = "controlify_modded"; - - private static final Options options = Minecraft.getInstance().options; - - public static final Component MOVEMENT_CATEGORY = KeyMapping.Category.MOVEMENT.label(); - public static final Component GAMEPLAY_CATEGORY = KeyMapping.Category.GAMEPLAY.label(); - public static final Component INVENTORY_CATEGORY = KeyMapping.Category.INVENTORY.label(); - public static final Component CREATIVE_CATEGORY = KeyMapping.Category.CREATIVE.label(); - public static final Component MISC_CATEGORY = KeyMapping.Category.MISC.label(); - public static final Component DEBUG_CATEGORY = Component.translatable("controlify.binding_category.debug"); - public static final Component GUI_CATEGORY = Component.translatable("controlify.binding_category.gui"); - public static final Component RADIAL_CATEGORY = Component.translatable("controlify.gui.radial_menu"); - public static final Component VMOUSE_CATEGORY = Component.translatable("controlify.binding_category.vmouse"); - - public static final InputBindingSupplier WALK_FORWARD = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "walk_forward") - .category(MOVEMENT_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier WALK_BACKWARD = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "walk_backward") - .category(MOVEMENT_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier WALK_LEFT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "strafe_left") - .category(MOVEMENT_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier WALK_RIGHT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "strafe_right") - .category(MOVEMENT_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - - public static final InputBindingSupplier LOOK_UP = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "look_up") - .category(MOVEMENT_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier LOOK_DOWN = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "look_down") - .category(MOVEMENT_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier LOOK_LEFT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "look_left") - .category(MOVEMENT_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier LOOK_RIGHT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "look_right") - .category(MOVEMENT_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - - public static final InputBindingSupplier GYRO_BUTTON = ControlifyBindApi.get().registerBinding( - builder -> builder - .id("controlify", "gyro_button") - .category(MOVEMENT_CATEGORY) - .allowedContexts(BindContext.IN_GAME), - c -> c.gyro().isPresent() - ); - - public static final InputBindingSupplier JUMP = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "jump") - .category(MOVEMENT_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier SPRINT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "sprint") - .category(MOVEMENT_CATEGORY) - .allowedContexts(BindContext.IN_GAME) - .keyEmulation(options.keySprint, c -> c.settings().generic.toggleSprint)); - public static final InputBindingSupplier SNEAK = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "sneak") - .category(MOVEMENT_CATEGORY) - .allowedContexts(BindContext.IN_GAME) - .addKeyCorrelation(options.keyShift)); - - public static final InputBindingSupplier ATTACK = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "attack") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME) - .keyEmulation(options.keyAttack)); - public static final InputBindingSupplier USE = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "use") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME) - .keyEmulation(options.keyUse)); - public static final InputBindingSupplier DROP_INGAME = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "drop") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier DROP_STACK = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "drop_stack") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier PAUSE = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "pause") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME, BindContext.REGULAR_SCREEN)); - public static final InputBindingSupplier CHANGE_PERSPECTIVE = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "change_perspective") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier SWAP_HANDS = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "swap_hands") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier NEXT_SLOT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "next_slot") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier PREV_SLOT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "prev_slot") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier HOTBAR_SLOT_SELECT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "hotbar_item_select_radial") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME, BindContext.RADIAL_MENU)); - public static final InputBindingSupplier HOTBAR_SLOT_1 = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "hotbar_slot_1") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier HOTBAR_SLOT_2 = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "hotbar_slot_2") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier HOTBAR_SLOT_3 = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "hotbar_slot_3") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier HOTBAR_SLOT_4 = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "hotbar_slot_4") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier HOTBAR_SLOT_5 = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "hotbar_slot_5") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier HOTBAR_SLOT_6 = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "hotbar_slot_6") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier HOTBAR_SLOT_7 = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "hotbar_slot_7") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier HOTBAR_SLOT_8 = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "hotbar_slot_8") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier HOTBAR_SLOT_9 = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "hotbar_slot_9") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier GAME_MODE_SWITCHER = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "game_mode_switcher") - .category(GAMEPLAY_CATEGORY) - .allowedContexts(BindContext.IN_GAME, BindContext.RADIAL_MENU)); - - public static final InputBindingSupplier INVENTORY = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "inventory") - .category(INVENTORY_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier INV_SELECT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "inv_select") - .category(INVENTORY_CATEGORY) - .allowedContexts(BindContext.CONTAINER)); - public static final InputBindingSupplier INV_QUICK_MOVE = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "inv_quick_move") - .category(INVENTORY_CATEGORY) - .allowedContexts(BindContext.CONTAINER)); - public static final InputBindingSupplier INV_TAKE_HALF = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "inv_take_half") - .category(INVENTORY_CATEGORY) - .allowedContexts(BindContext.CONTAINER)); - public static final InputBindingSupplier DROP_INVENTORY = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "drop_inventory") - .category(INVENTORY_CATEGORY) - .allowedContexts(BindContext.CONTAINER, BindContext.REGULAR_SCREEN)); - - public static final InputBindingSupplier PICK_BLOCK = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "pick_block") - .category(CREATIVE_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier PICK_BLOCK_NBT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "pick_block_nbt") - .category(CREATIVE_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier HOTBAR_LOAD_RADIAL = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "hotbar_load_radial") - .category(CREATIVE_CATEGORY) - .allowedContexts(BindContext.IN_GAME, BindContext.RADIAL_MENU)); - public static final InputBindingSupplier HOTBAR_SAVE_RADIAL = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "hotbar_save_radial") - .category(CREATIVE_CATEGORY) - .allowedContexts(BindContext.IN_GAME, BindContext.RADIAL_MENU)); - - public static final InputBindingSupplier OPEN_CHAT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "open_chat") - .category(MISC_CATEGORY) - .allowedContexts(BindContext.IN_GAME) - .keyEmulation(options.keyChat)); - public static final InputBindingSupplier TOGGLE_HUD_VISIBILITY = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "toggle_hud_visibility") - .category(MISC_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier SHOW_PLAYER_LIST = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "show_player_list") - .category(MISC_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier TAKE_SCREENSHOT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "take_screenshot") - .category(MISC_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - - public static final InputBindingSupplier DEBUG_RADIAL = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "debug_radial") - .category(DEBUG_CATEGORY) - .allowedContexts(BindContext.IN_GAME, BindContext.RADIAL_MENU)); - public static final InputBindingSupplier TOGGLE_DEBUG_MENU = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "toggle_debug_menu") - .category(DEBUG_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier TOGGLE_DEBUG_MENU_FPS = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "toggle_debug_menu_fps") - .category(DEBUG_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier TOGGLE_DEBUG_MENU_NET = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "toggle_debug_menu_net") - .category(DEBUG_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier TOGGLE_DEBUG_MENU_PROF = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "toggle_debug_menu_prof") - .category(DEBUG_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier DEBUG_RELOAD_CHUNKS = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "debug_reload_chunks") - .name(Component.translatable("controlify.radial.debug.reload_chunks")) - .category(DEBUG_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier DEBUG_TOGGLE_CHUNK_BORDERS = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "debug_toggle_chunk_borders") - .name(Component.translatable("controlify.radial.debug.chunk_borders")) - .category(DEBUG_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier DEBUG_TOGGLE_ADVANCED_TOOLTIPS = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "debug_toggle_advanced_tooltips") - .name(Component.translatable("controlify.radial.debug.advanced_tooltips")) - .category(DEBUG_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier DEBUG_TOGGLE_ENTITY_HITBOXES = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "debug_toggle_entity_hitboxes") - .name(Component.translatable("controlify.radial.debug.entity_hitboxes")) - .category(DEBUG_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier DEBUG_RELOAD_RESOURCE_PACKS = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "debug_reload_resource_packs") - .name(Component.translatable("controlify.radial.debug.reload_packs")) - .category(DEBUG_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier DEBUG_CLEAR_CHAT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "debug_clear_chat") - .name(Component.translatable("controlify.radial.debug.clear_chat")) - .category(DEBUG_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier DEBUG_START_STOP_PROFILING = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "debug_start_stop_profiling") - .name(Component.translatable("controlify.radial.debug.profile")) - .category(DEBUG_CATEGORY) - .allowedContexts(BindContext.IN_GAME)); - public static final InputBindingSupplier GUI_PRESS = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "gui_press") - .category(GUI_CATEGORY) - .allowedContexts(BindContext.REGULAR_SCREEN)); - public static final InputBindingSupplier GUI_BACK = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "gui_back") - .category(GUI_CATEGORY) - .allowedContexts(BindContext.ANY_SCREEN)); - public static final InputBindingSupplier GUI_NEXT_TAB = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "gui_next_tab") - .category(GUI_CATEGORY) - .allowedContexts(BindContext.ANY_SCREEN)); - public static final InputBindingSupplier GUI_PREV_TAB = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "gui_prev_tab") - .category(GUI_CATEGORY) - .allowedContexts(BindContext.ANY_SCREEN)); - public static final InputBindingSupplier GUI_ABSTRACT_ACTION_1 = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "gui_abstract_action_1") - .category(GUI_CATEGORY) - .allowedContexts(BindContext.REGULAR_SCREEN)); - public static final InputBindingSupplier GUI_ABSTRACT_ACTION_2 = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "gui_abstract_action_2") - .category(GUI_CATEGORY) - .allowedContexts(BindContext.REGULAR_SCREEN)); - public static final InputBindingSupplier GUI_ABSTRACT_ACTION_3 = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "gui_abstract_action_3") - .category(GUI_CATEGORY) - .allowedContexts(BindContext.REGULAR_SCREEN)); - public static final InputBindingSupplier GUI_NAVI_UP = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "gui_navi_up") - .category(GUI_CATEGORY) - .allowedContexts(BindContext.REGULAR_SCREEN)); - public static final InputBindingSupplier GUI_NAVI_DOWN = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "gui_navi_down") - .category(GUI_CATEGORY) - .allowedContexts(BindContext.REGULAR_SCREEN)); - public static final InputBindingSupplier GUI_NAVI_LEFT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "gui_navi_left") - .category(GUI_CATEGORY) - .allowedContexts(BindContext.REGULAR_SCREEN)); - public static final InputBindingSupplier GUI_NAVI_RIGHT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "gui_navi_right") - .category(GUI_CATEGORY) - .allowedContexts(BindContext.REGULAR_SCREEN)); - public static final InputBindingSupplier GUI_SECONDARY_NAVI_UP = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "gui_secondary_navi_up") - .category(GUI_CATEGORY) - .allowedContexts(BindContext.CONTAINER, BindContext.REGULAR_SCREEN)); - public static final InputBindingSupplier GUI_SECONDARY_NAVI_DOWN = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "gui_secondary_navi_down") - .category(GUI_CATEGORY) - .allowedContexts(BindContext.CONTAINER, BindContext.REGULAR_SCREEN)); - public static final InputBindingSupplier GUI_SECONDARY_NAVI_LEFT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "gui_secondary_navi_left") - .category(GUI_CATEGORY) - .allowedContexts(BindContext.CONTAINER, BindContext.REGULAR_SCREEN)); - public static final InputBindingSupplier GUI_SECONDARY_NAVI_RIGHT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "gui_secondary_navi_right") - .category(GUI_CATEGORY) - .allowedContexts(BindContext.CONTAINER, BindContext.REGULAR_SCREEN)); - @Deprecated - public static final InputBindingSupplier CYCLE_OPT_FORWARD = GUI_SECONDARY_NAVI_RIGHT; - @Deprecated - public static final InputBindingSupplier CYCLE_OPT_BACKWARD = GUI_SECONDARY_NAVI_LEFT; - - public static final InputBindingSupplier RADIAL_MENU = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "radial_menu") - .category(RADIAL_CATEGORY) - .allowedContexts(BindContext.IN_GAME, BindContext.RADIAL_MENU)); - public static final InputBindingSupplier RADIAL_AXIS_UP = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "radial_axis_up") - .category(RADIAL_CATEGORY) - .allowedContexts(BindContext.RADIAL_MENU)); - public static final InputBindingSupplier RADIAL_AXIS_DOWN = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "radial_axis_down") - .category(RADIAL_CATEGORY) - .allowedContexts(BindContext.RADIAL_MENU)); - public static final InputBindingSupplier RADIAL_AXIS_LEFT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "radial_axis_left") - .category(RADIAL_CATEGORY) - .allowedContexts(BindContext.RADIAL_MENU)); - public static final InputBindingSupplier RADIAL_AXIS_RIGHT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "radial_axis_right") - .category(RADIAL_CATEGORY) - .allowedContexts(BindContext.RADIAL_MENU)); - - public static final InputBindingSupplier VMOUSE_MOVE_UP = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "vmouse_move_up") - .category(VMOUSE_CATEGORY) - .allowedContexts(BindContext.V_MOUSE_CURSOR)); - public static final InputBindingSupplier VMOUSE_MOVE_DOWN = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "vmouse_move_down") - .category(VMOUSE_CATEGORY) - .allowedContexts(BindContext.V_MOUSE_CURSOR)); - public static final InputBindingSupplier VMOUSE_MOVE_LEFT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "vmouse_move_left") - .category(VMOUSE_CATEGORY) - .allowedContexts(BindContext.V_MOUSE_CURSOR)); - public static final InputBindingSupplier VMOUSE_MOVE_RIGHT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "vmouse_move_right") - .category(VMOUSE_CATEGORY) - .allowedContexts(BindContext.V_MOUSE_CURSOR)); - public static final InputBindingSupplier VMOUSE_SNAP_UP = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "vmouse_snap_up") - .category(VMOUSE_CATEGORY) - .allowedContexts(BindContext.V_MOUSE_CURSOR)); - public static final InputBindingSupplier VMOUSE_SNAP_DOWN = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "vmouse_snap_down") - .category(VMOUSE_CATEGORY) - .allowedContexts(BindContext.V_MOUSE_CURSOR)); - public static final InputBindingSupplier VMOUSE_SNAP_LEFT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "vmouse_snap_left") - .category(VMOUSE_CATEGORY) - .allowedContexts(BindContext.V_MOUSE_CURSOR)); - public static final InputBindingSupplier VMOUSE_SNAP_RIGHT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "vmouse_snap_right") - .category(VMOUSE_CATEGORY) - .allowedContexts(BindContext.V_MOUSE_CURSOR)); - public static final InputBindingSupplier VMOUSE_LCLICK = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "vmouse_lclick") - .category(VMOUSE_CATEGORY) - .allowedContexts(BindContext.V_MOUSE_COMPAT, BindContext.V_MOUSE_CURSOR)); - public static final InputBindingSupplier VMOUSE_RCLICK = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "vmouse_rclick") - .category(VMOUSE_CATEGORY) - .allowedContexts(BindContext.V_MOUSE_COMPAT, BindContext.V_MOUSE_CURSOR)); - public static final InputBindingSupplier VMOUSE_SHIFT_CLICK = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "vmouse_shift_click") - .category(VMOUSE_CATEGORY) - .allowedContexts(BindContext.V_MOUSE_COMPAT, BindContext.V_MOUSE_CURSOR)); - public static final InputBindingSupplier VMOUSE_SCROLL_DOWN = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "vmouse_scroll_down") - .category(VMOUSE_CATEGORY) - .allowedContexts(BindContext.V_MOUSE_COMPAT, BindContext.V_MOUSE_CURSOR)); - public static final InputBindingSupplier VMOUSE_SCROLL_UP = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "vmouse_scroll_up") - .category(VMOUSE_CATEGORY) - .allowedContexts(BindContext.V_MOUSE_COMPAT, BindContext.V_MOUSE_CURSOR)); - public static final InputBindingSupplier VMOUSE_SHIFT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "vmouse_shift") - .category(VMOUSE_CATEGORY) - .allowedContexts(BindContext.V_MOUSE_COMPAT, BindContext.V_MOUSE_CURSOR)); - public static final InputBindingSupplier VMOUSE_PAGE_NEXT = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "vmouse_page_next") - .category(VMOUSE_CATEGORY) - .allowedContexts(BindContext.V_MOUSE_COMPAT, BindContext.V_MOUSE_CURSOR)); - public static final InputBindingSupplier VMOUSE_PAGE_PREV = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "vmouse_page_prev") - .category(VMOUSE_CATEGORY) - .allowedContexts(BindContext.V_MOUSE_COMPAT, BindContext.V_MOUSE_CURSOR)); - public static final InputBindingSupplier VMOUSE_PAGE_DOWN = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "vmouse_page_down") - .category(VMOUSE_CATEGORY) - .allowedContexts(BindContext.V_MOUSE_COMPAT, BindContext.V_MOUSE_CURSOR)); - public static final InputBindingSupplier VMOUSE_PAGE_UP = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "vmouse_page_up") - .category(VMOUSE_CATEGORY) - .allowedContexts(BindContext.V_MOUSE_COMPAT, BindContext.V_MOUSE_CURSOR)); - public static final InputBindingSupplier VMOUSE_TOGGLE = ControlifyBindApi.get().registerBinding(builder -> builder - .id("controlify", "vmouse_toggle") - .category(VMOUSE_CATEGORY) - .allowedContexts(BindContext.V_MOUSE_CURSOR, BindContext.V_MOUSE_COMPAT, BindContext.ANY_SCREEN)); - - private static final Map MODDED_BINDS = new LinkedHashMap<>(); - - public static void registerModdedBindings() { - for (KeyMapping keyMapping : PlatformClientUtil.getModdedKeyMappings()) { - if (!ControlifyBindApi.get().getKeyCorrelation(keyMapping).isEmpty()) - continue; - - try { - var idPath = keyMapping.getName() - .toLowerCase() - .replaceAll("[^a-z0-9/._-]", "_") - .trim(); - - var identifier = Identifier.fromNamespaceAndPath(MODDED_BIND_NAMESPACE, idPath); - - InputBindingSupplier binding = ControlifyBindApi.get().registerBinding(builder -> builder - .id(identifier) - .name(Component.translatable(keyMapping.getName())) - .description(Component.translatable("controlify.custom_binding.vanilla_description").withStyle(ChatFormatting.GRAY)) - .category(keyMapping.getCategory().label()) - .allowedContexts(BindContext.IN_GAME) - .keyEmulation(keyMapping)); - - MODDED_BINDS.put(keyMapping, binding); - } catch (Exception e) { - CUtil.LOGGER.error("Failed to automatically register modded keybind: {}", keyMapping.getName(), e); - } - } - } - - private ControlifyBindings() { - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/bindings/KeyMappingHandle.java b/src/main/java/dev/isxander/controlify/impl/bindings/KeyMappingHandle.java deleted file mode 100644 index beba3d178..000000000 --- a/src/main/java/dev/isxander/controlify/impl/bindings/KeyMappingHandle.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.bindings; - -import dev.isxander.controlify.impl.controller.ControllerImpl; - -import java.util.function.BooleanSupplier; - -public interface KeyMappingHandle { - void controlify$setPressed(boolean isDown); - - void controlify$addToggleCondition(ControllerImpl controller, BooleanSupplier condition); -} diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/CompatMixinPlugin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/CompatMixinPlugin.java index bc62b26b2..a922b02f6 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/CompatMixinPlugin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/CompatMixinPlugin.java @@ -36,14 +36,14 @@ private static Platform loadPlatform() { Loader.FABRIC, loadPlatformImpl( mixinService, - "dev.isxander.controlify.fabric.compatibility.FabricCompatMixinPlatform" + "dev.isxander.controlify.fabric.impl.compatibility.FabricCompatMixinPlatform" ) ); case "FML" -> new Platform( Loader.NEOFORGE, loadPlatformImpl( mixinService, - "dev.isxander.controlify.neoforge.compatibility.NeoforgeCompatMixinPlatform" + "dev.isxander.controlify.neoforge.impl.compatibility.NeoforgeCompatMixinPlatform" ) ); default -> throw new IllegalStateException("Unsupported Mixin service: " + mixinService.getName()); diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/SimpleVoiceChatCompat.java b/src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/SimpleVoiceChatCompat.java index d00ccbac0..7eadf3980 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/SimpleVoiceChatCompat.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/SimpleVoiceChatCompat.java @@ -10,81 +10,124 @@ import de.maxhenkel.voicechat.voice.client.ClientManager; import de.maxhenkel.voicechat.voice.client.KeyEvents; -import dev.isxander.controlify.api.ControlifyEvents; -import dev.isxander.controlify.impl.api.bind.ControlifyBindApi; -import dev.isxander.controlify.impl.api.bind.InputBindingSupplier; +import dev.isxander.controlify.api.action.Action; +import dev.isxander.controlify.api.action.ActionAccessor; +import dev.isxander.controlify.api.action.ActionEvents; +import dev.isxander.controlify.api.action.ActionProxyManager; +import dev.isxander.controlify.api.action.ActionSpec; +import dev.isxander.controlify.api.action.ActionSpecRegistry; +import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.compatibility.simplevoicechat.mixins.KeyEventsAccessor; -import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; +import dev.isxander.controlify.impl.utils.CUtil; import dev.isxander.controlify.impl.utils.MinecraftUtil; +import net.minecraft.client.Minecraft; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; +import net.minecraft.resources.Identifier; public class SimpleVoiceChatCompat { - private static InputBindingSupplier pttHoldSupplier, pttToggleSupplier; - private static InputBindingSupplier whisperHoldSupplier, whisperToggleSupplier; + private static ActionAccessor pttHoldSupplier, pttToggleSupplier; + private static ActionAccessor whisperHoldSupplier, whisperToggleSupplier; + private static ActionAccessor muteMicrophoneSupplier; private static boolean pttDown, whisperDown; + private static boolean pttToggleDown, whisperToggleDown; + private static boolean previousPttHold, previousWhisperHold; + private static Action previousPttAction, previousWhisperAction; public static void init() { + ActionEvents.REGISTER_ACTIONS.subscribe(SimpleVoiceChatCompat::registerActions); + ActionEvents.REGISTER_PROXIES.subscribe(SimpleVoiceChatCompat::registerProxies); + PlatformClientUtil.registerClientTickEnded(SimpleVoiceChatCompat::tick); + } + + private static void registerActions(ActionSpecRegistry registry) { Component category = Component.translatable("key.categories.voicechat"); - pttHoldSupplier = ControlifyBindApi.get().registerBinding(builder -> builder - .id("voicechat", "ptt_hold") - .category(category) - .addKeyCorrelation(KeyEvents.KEY_PTT)); - pttToggleSupplier = ControlifyBindApi.get().registerBinding(builder -> builder - .id("voicechat", "ptt_toggle") - .category(category) - .addKeyCorrelation(KeyEvents.KEY_PTT)); - whisperHoldSupplier = ControlifyBindApi.get().registerBinding(builder -> builder - .name(Component.translatable("key.whisper").append(CommonComponents.SPACE).append(Component.translatable("controlify.compat.svc.hold"))) - .id("voicechat", "whisper_hold") - .category(category) - .addKeyCorrelation(KeyEvents.KEY_WHISPER)); - whisperToggleSupplier = ControlifyBindApi.get().registerBinding(builder -> builder - .name(Component.translatable("key.whisper").append(CommonComponents.SPACE).append(Component.translatable("controlify.compat.svc.toggle"))) - .id("voicechat", "whisper_toggle") - .category(category) - .addKeyCorrelation(KeyEvents.KEY_WHISPER)); - ControlifyBindApi.get().registerBinding(builder -> builder - .id("voicechat", "mute_microphone") + pttHoldSupplier = register(registry, category, "ptt_hold", Component.translatable("controlify.action.voicechat.ptt_hold")); + pttToggleSupplier = register(registry, category, "ptt_toggle", Component.translatable("controlify.action.voicechat.ptt_toggle")); + whisperHoldSupplier = register(registry, category, "whisper_hold", Component.translatable("key.whisper") + .append(CommonComponents.SPACE).append(Component.translatable("controlify.compat.svc.hold"))); + whisperToggleSupplier = register(registry, category, "whisper_toggle", Component.translatable("key.whisper") + .append(CommonComponents.SPACE).append(Component.translatable("controlify.compat.svc.toggle"))); + muteMicrophoneSupplier = register(registry, category, "mute_microphone", Component.translatable("controlify.action.voicechat.mute_microphone")); + } + + private static ActionAccessor register(ActionSpecRegistry registry, Component category, String path, Component name) { + return registry.register(ActionSpec.builder() + .id(Identifier.fromNamespaceAndPath("voicechat", path)) + .name(name) .category(category) - .keyEmulation(KeyEvents.KEY_MUTE)); - - ControlifyEvents.ACTIVE_CONTROLLER_TICKED.register(event -> { - ControllerImpl controller = event.controller(); - - var pttHold = pttHoldSupplier.on(controller); - var pttToggle = pttToggleSupplier.on(controller); - var whisperHold = whisperHoldSupplier.on(controller); - var whisperToggle = whisperToggleSupplier.on(controller); - - if (pttToggle.justPressed()) { - pttDown = !pttDown; - checkConnected(); - } - if (whisperToggle.justPressed()) { - whisperDown = !whisperDown; - checkConnected(); - } - - if (pttHold.justPressed() || whisperHold.justPressed()) { - checkConnected(); - } - if (pttHold.digitalNow()) { - pttDown = true; - } else if (pttHold.justReleased()) { - pttDown = false; - } - if (whisperHold.digitalNow()) { - whisperDown = true; - } else if (whisperHold.justReleased()) { - whisperDown = false; - } - - controller.dualSense().ifPresent(ds -> { - ds.setMuteLight(ClientManager.getPlayerStateManager().isMuted()); - }); - }); + .context(CUtil.rl("unknown")) + .build()); + } + + private static void registerProxies(ActionProxyManager proxies) { + proxies.dontAutoProxyKeyMapping(KeyEvents.KEY_PTT); + proxies.dontAutoProxyKeyMapping(KeyEvents.KEY_WHISPER); + proxies.proxyKeyMapping(muteMicrophoneSupplier, KeyEvents.KEY_MUTE); + } + + private static void tick(Minecraft minecraft) { + Controlify controlify = Controlify.instance(); + var controller = controlify.getCurrentController().orElse(null); + if (controller == null || pttHoldSupplier == null || pttToggleSupplier == null + || whisperHoldSupplier == null || whisperToggleSupplier == null) { + clearState(); + return; + } + + // Resolve on each tick: profile changes may replace action instances. + Action pttHold = pttHoldSupplier.onOrNull(controller); + Action pttToggle = pttToggleSupplier.onOrNull(controller); + Action whisperHold = whisperHoldSupplier.onOrNull(controller); + Action whisperToggle = whisperToggleSupplier.onOrNull(controller); + if (pttHold == null || pttToggle == null || whisperHold == null || whisperToggle == null) { + clearState(); + return; + } + if (!controlify.currentInputMode().isController() + || (!minecraft.isWindowActive() && !controlify.config().getSettings().globalSettings().outOfFocusInput)) { + // Discard ineligible toggle presses so they cannot replay when focus/input returns. + while (pttToggle.pulse().consumePulse()) {} + while (whisperToggle.pulse().consumePulse()) {} + clearState(); + return; + } + if (pttToggle != previousPttAction || whisperToggle != previousWhisperAction) { + clearState(); + previousPttAction = pttToggle; + previousWhisperAction = whisperToggle; + } + + // Pulses accumulate independently of frame/tick cadence; consume each pulse once. + while (pttToggle.pulse().consumePulse()) { + pttToggleDown = !pttToggleDown; + checkConnected(); + } + while (whisperToggle.pulse().consumePulse()) { + whisperToggleDown = !whisperToggleDown; + checkConnected(); + } + boolean pttHeld = pttHold.latch().isLatchActive(); + boolean whisperHeld = whisperHold.latch().isLatchActive(); + if ((pttHeld && !previousPttHold) || (whisperHeld && !previousWhisperHold)) { + checkConnected(); + } + previousPttHold = pttHeld; + previousWhisperHold = whisperHeld; + pttDown = pttHeld || pttToggleDown; + whisperDown = whisperHeld || whisperToggleDown; + + controller.dualsense().ifPresent(ds -> + ds.setMuteLight(ClientManager.getPlayerStateManager().isMuted())); + } + + private static void clearState() { + pttDown = whisperDown = false; + pttToggleDown = whisperToggleDown = false; + previousPttHold = previousWhisperHold = false; + previousPttAction = previousWhisperAction = null; } public static boolean isPTTDown() { diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/FlatButtonWidgetMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/FlatButtonWidgetMixin.java index 6fca9d01f..5eb8f6f5a 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/FlatButtonWidgetMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/FlatButtonWidgetMixin.java @@ -9,9 +9,10 @@ //? if sodium { import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.api.action.Action; import dev.isxander.controlify.impl.gui.ButtonGuideRenderer; import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; +import net.caffeinemc.mods.sodium.client.gui.widgets.FlatButtonWidget; import net.minecraft.network.chat.Component; import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; @@ -22,8 +23,6 @@ import java.util.Map; import java.util.Optional; -import net.caffeinemc.mods.sodium.client.gui.widgets.FlatButtonWidget; - @Mixin(FlatButtonWidget.class) public class FlatButtonWidgetMixin implements ButtonGuideRenderer { @Shadow @@ -32,7 +31,7 @@ public class FlatButtonWidgetMixin implements ButtonGuideRenderer renderData = null; - @Unique private final Map controllerMessages = new Object2ObjectArrayMap<>(2); + @Unique private final Map controllerMessages = new Object2ObjectArrayMap<>(2); @ModifyExpressionValue( method = "extractRenderState", @@ -47,8 +46,9 @@ private Component modifyRenderedLabel(Component actualLabel) { } @Unique private Component getControllerMessage(Component actualLabel) { - if (!shouldRender()) + if (!shouldRender()) { return actualLabel; + } return getBind().map(bind -> controllerMessages .computeIfAbsent(bind, b -> renderData.getControllerMessage(b, actualLabel))) @@ -62,9 +62,11 @@ private Component modifyRenderedLabel(Component actualLabel) { && renderData.shouldRender((FlatButtonWidget) (Object) this); } - @Unique private Optional getBind() { - if (renderData == null) return Optional.empty(); - return renderData.getBind(); + @Unique private Optional getBind() { + if (renderData == null) { + return Optional.empty(); + } + return renderData.getAction(); } @Override diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/CycleControlProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/CycleControlProcessor.java index f3b87073a..2e6984f70 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/CycleControlProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/CycleControlProcessor.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.impl.compatibility.sodium.screenop; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.screenop.ComponentProcessor; import dev.isxander.controlify.impl.screenop.ScreenProcessor; @@ -22,13 +22,13 @@ public CycleControlProcessor(Consumer cycleMethod) { @Override public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { - if (ControlifyBindings.GUI_SECONDARY_NAVI_RIGHT.on(controller).justPressed() - || ControlifyBindings.GUI_PRESS.on(controller).justPressed() + if (ControlifyActions.GUI_SECONDARY_NAVI_RIGHT.on(controller).pulse().consumePulse() + || ControlifyActions.GUI_PRESS.on(controller).pulse().consumePulse() ) { cycleMethod.accept(false); return true; } - if (ControlifyBindings.GUI_SECONDARY_NAVI_LEFT.on(controller).justPressed()) { + if (ControlifyActions.GUI_SECONDARY_NAVI_LEFT.on(controller).pulse().consumePulse()) { cycleMethod.accept(true); return true; } diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java index ab154ebd9..619868b49 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java @@ -10,7 +10,7 @@ import dev.isxander.controlify.api.gui.ButtonGuideApi; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.compatibility.sodium.mixins.FlatButtonWidgetAccessor; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.screenop.ScreenProcessor; @@ -31,18 +31,18 @@ protected void handleComponentNavigation(ControllerImpl controller) { @Override protected void handleButtons(ControllerImpl controller) { - if (ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).justPressed()) { + if (ControlifyActions.GUI_ABSTRACT_ACTION_1.on(controller).pulse().consumePulse()) { ((FlatButtonWidgetAccessor) operations.controlify$getApplyButton()).invokeDoAction(); } - if (ControlifyBindings.GUI_ABSTRACT_ACTION_2.on(controller).justPressed()) { + if (ControlifyActions.GUI_ABSTRACT_ACTION_2.on(controller).pulse().consumePulse()) { ((FlatButtonWidgetAccessor) operations.controlify$getUndoButton()).invokeDoAction(); } - if (ControlifyBindings.GUI_NEXT_TAB.on(controller).justPressed()) { + if (ControlifyActions.GUI_NEXT_TAB.on(controller).pulse().consumePulse()) { operations.controlify$nextPage(); } - if (ControlifyBindings.GUI_PREV_TAB.on(controller).justPressed()) { + if (ControlifyActions.GUI_PREV_TAB.on(controller).pulse().consumePulse()) { operations.controlify$prevPage(); } @@ -52,18 +52,18 @@ protected void handleButtons(ControllerImpl controller) { public void onRebuildGUI() { ButtonGuideApi.addGuideToButton( operations.controlify$getApplyButton(), - ControlifyBindings.GUI_ABSTRACT_ACTION_1, + ControlifyActions.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always() ); ButtonGuideApi.addGuideToButton( operations.controlify$getUndoButton(), - ControlifyBindings.GUI_ABSTRACT_ACTION_2, + ControlifyActions.GUI_ABSTRACT_ACTION_2, ButtonGuidePredicate.always() ); ButtonGuideApi.addGuideToButton( operations.controlify$getCloseButton(), - ControlifyBindings.GUI_BACK, + ControlifyActions.GUI_BACK, ButtonGuidePredicate.always() ); diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/TickBoxControlProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/TickBoxControlProcessor.java index 11754626d..bde32e5fb 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/TickBoxControlProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/TickBoxControlProcessor.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.impl.compatibility.sodium.screenop; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.screenop.ComponentProcessor; import dev.isxander.controlify.impl.screenop.ScreenProcessor; @@ -20,11 +20,11 @@ public TickBoxControlProcessor(Runnable toggleMethod) { @Override public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { - if (ControlifyBindings.GUI_PRESS.on(controller).guiPressed().get()) { + if (ControlifyActions.GUI_PRESS.on(controller).pulse().consumePulse()) { toggleMethod.run(); return true; } - if (ControlifyBindings.GUI_SECONDARY_NAVI_RIGHT.on(controller).justPressed() || ControlifyBindings.GUI_SECONDARY_NAVI_LEFT.on(controller).justPressed()) { + if (ControlifyActions.GUI_SECONDARY_NAVI_RIGHT.on(controller).pulse().consumePulse() || ControlifyActions.GUI_SECONDARY_NAVI_LEFT.on(controller).pulse().consumePulse()) { toggleMethod.run(); return true; } diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenCategoryTabMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenCategoryTabMixin.java index 89370f043..cf2ea1151 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenCategoryTabMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenCategoryTabMixin.java @@ -8,7 +8,7 @@ import dev.isxander.controlify.api.gui.ButtonGuideApi; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.yacl3.gui.YACLScreen; import net.minecraft.client.gui.components.Button; import org.spongepowered.asm.mixin.Final; @@ -25,6 +25,6 @@ public class YACLScreenCategoryTabMixin { @Inject(method = "", at = @At("RETURN"), require = 0) private void onConstructCategory(CallbackInfo ci) { - ButtonGuideApi.addGuideToButton(saveFinishedButton, ControlifyBindings.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always()); + ButtonGuideApi.addGuideToButton(saveFinishedButton, ControlifyActions.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always()); } } diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/CyclingControllerElementComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/CyclingControllerElementComponentProcessor.java index 216cac244..19d6cdf4e 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/CyclingControllerElementComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/CyclingControllerElementComponentProcessor.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.impl.compatibility.yacl.screenop; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.compatibility.yacl.mixins.ControllerWidgetAccessor; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.screenop.ComponentProcessor; @@ -15,8 +15,6 @@ public class CyclingControllerElementComponentProcessor implements ComponentProcessor { private final CyclingControllerElement cyclingController; - private boolean prevLeft, prevRight; - public CyclingControllerElementComponentProcessor(CyclingControllerElement cyclingController) { this.cyclingController = cyclingController; @@ -24,31 +22,22 @@ public CyclingControllerElementComponentProcessor(CyclingControllerElement cycli @Override public boolean overrideControllerNavigation(ScreenProcessor screen, ControllerImpl controller) { - boolean left = ControlifyBindings.GUI_SECONDARY_NAVI_LEFT.on(controller).digitalNow(); - boolean right = ControlifyBindings.GUI_SECONDARY_NAVI_RIGHT.on(controller).digitalNow(); + boolean left = ControlifyActions.GUI_SECONDARY_NAVI_LEFT.on(controller).pulse().consumePulse(); + boolean right = ControlifyActions.GUI_SECONDARY_NAVI_RIGHT.on(controller).pulse().consumePulse(); if (!((ControllerWidgetAccessor) cyclingController).getControl().option().available()) { return false; } - if (left && !prevLeft) { - prevLeft = true; - prevRight = false; - + if (left) { cyclingController.cycleValue(-1); return true; - } else if (right && !prevRight) { - prevLeft = false; - prevRight = true; - + } else if (right) { cyclingController.cycleValue(1); return true; } else { - prevLeft = left; - prevRight = right; - return false; } } diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/OptionListWidgetComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/OptionListWidgetComponentProcessor.java index 160306be5..07eb6c32a 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/OptionListWidgetComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/OptionListWidgetComponentProcessor.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.impl.compatibility.yacl.screenop; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.compatibility.yacl.mixins.GroupSeparatorEntryAccessor; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.screenop.ComponentProcessor; @@ -28,8 +28,8 @@ public OptionListWidgetComponentProcessor(OptionListWidget optionListWidget) { */ @Override public boolean overrideControllerNavigation(ScreenProcessor screen, ControllerImpl controller) { - boolean up = ControlifyBindings.GUI_SECONDARY_NAVI_UP.on(controller).justPressed(); - boolean down = ControlifyBindings.GUI_SECONDARY_NAVI_DOWN.on(controller).justPressed(); + boolean up = ControlifyActions.GUI_SECONDARY_NAVI_UP.on(controller).pulse().consumePulse(); + boolean down = ControlifyActions.GUI_SECONDARY_NAVI_DOWN.on(controller).pulse().consumePulse(); if (up || down) { OptionListWidget.GroupSeparatorEntry nextGroup = findNextGroup(optionListWidget, up ? -1 : 1); if (nextGroup != null) { @@ -57,8 +57,12 @@ public boolean overrideControllerNavigation(ScreenProcessor screen, Controlle List entries = optionList.children(); OptionListWidget.Entry focusedEntry = optionList.getFocused(); int currentIndex = entries.indexOf(focusedEntry); - if (direction < 0) currentIndex -= 2; - if (currentIndex < 0) currentIndex = 0; + if (direction < 0) { + currentIndex -= 2; + } + if (currentIndex < 0) { + currentIndex = 0; + } for (int i = currentIndex; i >= 0 && i < entries.size(); i += direction) { OptionListWidget.Entry entry = entries.get(i); diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/SliderControllerElementComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/SliderControllerElementComponentProcessor.java index 35a32bf1d..ae549db7b 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/SliderControllerElementComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/SliderControllerElementComponentProcessor.java @@ -6,17 +6,15 @@ */ package dev.isxander.controlify.impl.compatibility.yacl.screenop; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.compatibility.yacl.mixins.ControllerWidgetAccessor; import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.screenop.ComponentProcessor; -import dev.isxander.controlify.impl.utils.HoldRepeatHelper; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.yacl3.gui.controllers.slider.SliderControllerElement; public class SliderControllerElementComponentProcessor implements ComponentProcessor { private final SliderControllerElement slider; - private final HoldRepeatHelper holdRepeatHelper = new HoldRepeatHelper(15, 3); public SliderControllerElementComponentProcessor(SliderControllerElement element) { this.slider = element; @@ -24,33 +22,21 @@ public SliderControllerElementComponentProcessor(SliderControllerElement element @Override public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { - var left = ControlifyBindings.GUI_SECONDARY_NAVI_LEFT.on(controller).digitalNow(); - var leftPrev = ControlifyBindings.GUI_SECONDARY_NAVI_LEFT.on(controller).digitalPrev(); - var right = ControlifyBindings.GUI_SECONDARY_NAVI_RIGHT.on(controller).digitalNow(); - var rightPrev = ControlifyBindings.GUI_SECONDARY_NAVI_RIGHT.on(controller).digitalPrev(); + var left = ControlifyActions.GUI_SECONDARY_NAVI_LEFT.on(controller).pulse().consumePulse(); + var right = ControlifyActions.GUI_SECONDARY_NAVI_RIGHT.on(controller).pulse().consumePulse(); if (!((ControllerWidgetAccessor) slider).getControl().option().available()) { return false; } - boolean repeatEventAvailable = holdRepeatHelper.canNavigate(); - - if (left && (repeatEventAvailable || !leftPrev)) { + if (left) { slider.incrementValue(-1); - - if (!leftPrev) - holdRepeatHelper.reset(); - } else if (right && (repeatEventAvailable || !rightPrev)) { + } else if (right) { slider.incrementValue(1); - - if (!rightPrev) - holdRepeatHelper.reset(); } else { return false; } - holdRepeatHelper.onNavigate(); - return true; } } diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/YACLScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/YACLScreenProcessor.java index 2bcc57edf..47e5b741e 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/YACLScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/YACLScreenProcessor.java @@ -6,9 +6,9 @@ */ package dev.isxander.controlify.impl.compatibility.yacl.screenop; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; -import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.compatibility.yacl.mixins.YACLScreenCategoryTabAccessor; +import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.yacl3.gui.OptionListWidget; import dev.isxander.yacl3.gui.WidgetAndType; @@ -21,7 +21,7 @@ public YACLScreenProcessor(YACLScreen screen) { @Override protected void handleButtons(ControllerImpl controller) { - if (ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).justPressed()) { + if (ControlifyActions.GUI_ABSTRACT_ACTION_1.on(controller).pulse().consumePulse()) { if (screen.tabManager.getCurrentTab() instanceof YACLScreen.CategoryTab categoryTab) { ((YACLScreenCategoryTabAccessor) categoryTab).getSaveFinishedButton().onPress(null); } @@ -40,8 +40,9 @@ protected void onTabChanged(ControllerImpl controller) { screen.setFocused(optionListHolder.getWidget()); - if (optionList.children().isEmpty()) + if (optionList.children().isEmpty()) { return; + } for (OptionListWidget.Entry entry : optionList.children()) { entry.setFocused(false); diff --git a/src/main/java/dev/isxander/controlify/impl/config/dto/profile/defaults/DefaultConfigManager.java b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/defaults/DefaultConfigManager.java index 2d1d58ffd..1e78e3439 100644 --- a/src/main/java/dev/isxander/controlify/impl/config/dto/profile/defaults/DefaultConfigManager.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/defaults/DefaultConfigManager.java @@ -12,6 +12,7 @@ import com.mojang.logging.LogUtils; import com.mojang.serialization.DataResult; import com.mojang.serialization.JsonOps; +import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.config.dto.profile.ProfileConfig; import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; import dev.isxander.controlify.impl.utils.CUtil; @@ -28,7 +29,7 @@ import java.util.concurrent.Executor; public class DefaultConfigManager implements SimpleControlifyReloadListener, DefaultConfigProvider { - public static final Identifier DEFAULT_RESOURCE = CUtil.rl("controllers/default_config.json"); + public static final Identifier DEFAULT_RESOURCE = CUtil.rl("controller/default_config.json"); private static final Logger LOGGER = LogUtils.getLogger(); private ProfileConfig defaultConfig; @@ -76,7 +77,14 @@ public CompletableFuture load(ResourceManager manager, Executor e @Override public CompletableFuture apply(ProfileConfig data, ResourceManager manager, Executor executor) { - return CompletableFuture.runAsync(() -> this.defaultConfig = data, executor); + return CompletableFuture.runAsync(() -> { + this.defaultConfig = data; + Controlify.instance().getControllerManager().ifPresent(controllerManager -> { + for (var controller : controllerManager.getAllControllers()) { + controller.reloadDefaultSettings(); + } + }); + }, executor); } @Override diff --git a/src/main/java/dev/isxander/controlify/impl/contextual/ContextualDomains.java b/src/main/java/dev/isxander/controlify/impl/contextual/ContextualDomains.java index 20fa7db36..05c185d1b 100644 --- a/src/main/java/dev/isxander/controlify/impl/contextual/ContextualDomains.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/ContextualDomains.java @@ -13,16 +13,25 @@ import dev.isxander.controlify.api.contextual.ContextualDomainRegistry; import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.event.EventBus; import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; import java.util.ArrayList; import java.util.List; public final class ContextualDomains implements ContextualDomainRegistry { + /// Loading this holder does not construct domains or register resource listeners. + public static final class Events { + public static final EventBus REGISTER_DOMAINS = new EventBus<>(); + + private Events() {} + } public static final ContextualDomains INSTANCE = new ContextualDomains(); private final List> domains = new ArrayList<>(); + private boolean locked; private final ContextualDomain inGame = register( CUtil.rl("in_game"), ContextualStateContributors.IN_GAME @@ -33,17 +42,23 @@ public final class ContextualDomains implements ContextualDomainRegistry { ); @Override - public ContextualDomain inGame() { + public @NonNull ContextualDomain inGame() { return this.inGame; } @Override - public ContextualDomain container() { + public @NonNull ContextualDomain container() { return this.container; } @Override - public ContextualDomain register(Identifier domainId) { + public @NonNull ContextualDomain register(@NonNull Identifier domainId) { + if (this.locked) { + throw new IllegalStateException("Contextual domains must be registered during REGISTER_DOMAINS"); + } + if (this.domains.stream().anyMatch(domain -> domain.id().equals(domainId))) { + throw new IllegalArgumentException("Contextual domain is already registered: " + domainId); + } var domain = new ContextualDomainImpl(domainId); this.domains.add(domain); PlatformClientUtil.registerAssetReloadListener(domain); @@ -56,6 +71,10 @@ public void invalidateResolvedFactGraphs() { public static void touch() {} + public void lock() { + this.locked = true; + } + private ContextualDomains() { } } diff --git a/src/main/java/dev/isxander/controlify/impl/contextual/GuideRule.java b/src/main/java/dev/isxander/controlify/impl/contextual/GuideRule.java index dc7927175..6e3f4be7e 100644 --- a/src/main/java/dev/isxander/controlify/impl/contextual/GuideRule.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/GuideRule.java @@ -8,19 +8,19 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.impl.api.bind.InputBindingSupplier; +import dev.isxander.controlify.api.action.ActionAccessor; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.resources.Identifier; public record GuideRule( - InputBindingSupplier binding, + ActionAccessor action, GuideLocation location, ContextualPredicate predicate, Component text ) implements Rule { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - InputBindingSupplier.CODEC.fieldOf("for").forGetter(GuideRule::binding), + ActionAccessor.CODEC.fieldOf("for").forGetter(GuideRule::action), GuideLocation.CODEC.fieldOf("where").forGetter(GuideRule::location), ContextualPredicate.CODEC.fieldOf("if").forGetter(GuideRule::predicate), ComponentSerialization.CODEC.fieldOf("then").forGetter(GuideRule::text) @@ -28,11 +28,11 @@ public record GuideRule( @Override public Key key() { - return new Key(this.binding().bindId(), this.location()); + return new Key(this.action().actionId(), this.location()); } public record Key( - Identifier binding, + Identifier action, GuideLocation location ) {} } diff --git a/src/main/java/dev/isxander/controlify/impl/contextual/TriggerEffectRule.java b/src/main/java/dev/isxander/controlify/impl/contextual/TriggerEffectRule.java index 9fd21f190..fe82ffca1 100644 --- a/src/main/java/dev/isxander/controlify/impl/contextual/TriggerEffectRule.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/TriggerEffectRule.java @@ -8,26 +8,26 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.impl.api.bind.InputBindingSupplier; +import dev.isxander.controlify.api.action.ActionAccessor; import dev.isxander.controlify.impl.controller.dualsense.TriggerEffectCodecs; import dev.isxander.controlify.impl.driver.dualsense.DualsenseTriggerEffect; import net.minecraft.resources.Identifier; -/// @param binding the binding supplier associated with this action. -/// if the binding is not bound to either trigger, it is ignored. +/// @param action the action supplier associated with this action. +/// if the action is not bound to either trigger, it is ignored. public record TriggerEffectRule( - InputBindingSupplier binding, + ActionAccessor action, ContextualPredicate predicate, DualsenseTriggerEffect effect ) implements Rule { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - InputBindingSupplier.CODEC.fieldOf("for").forGetter(TriggerEffectRule::binding), + ActionAccessor.CODEC.fieldOf("for").forGetter(TriggerEffectRule::action), ContextualPredicate.CODEC.fieldOf("if").forGetter(TriggerEffectRule::predicate), TriggerEffectCodecs.CODEC.fieldOf("then").forGetter(TriggerEffectRule::effect) ).apply(instance, TriggerEffectRule::new)); @Override public Identifier key() { - return this.binding.bindId(); + return this.action.actionId(); } } diff --git a/src/main/java/dev/isxander/controlify/impl/controller/ControllerImpl.java b/src/main/java/dev/isxander/controlify/impl/controller/ControllerImpl.java index a4a82df12..6694b4187 100644 --- a/src/main/java/dev/isxander/controlify/impl/controller/ControllerImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/ControllerImpl.java @@ -4,13 +4,13 @@ import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; import dev.isxander.controlify.impl.controller.battery.BatteryLevelComponent; import dev.isxander.controlify.impl.controller.dualsense.DualsenseComponent; +import dev.isxander.controlify.impl.controller.gyro.GyroComponentImpl; import dev.isxander.controlify.impl.controller.haptic.HDHapticComponent; +import dev.isxander.controlify.impl.controller.input.InputComponentImpl; +import dev.isxander.controlify.impl.controller.led.LEDComponent; import dev.isxander.controlify.impl.controller.misc.BluetoothDeviceComponent; import dev.isxander.controlify.impl.controller.misc.DriverNameComponent; import dev.isxander.controlify.impl.controller.misc.UIDComponent; -import dev.isxander.controlify.impl.controller.gyro.GyroComponent; -import dev.isxander.controlify.impl.controller.input.InputComponent; -import dev.isxander.controlify.impl.controller.led.LEDComponent; import dev.isxander.controlify.impl.controller.rumble.RumbleComponentImpl; import dev.isxander.controlify.impl.controller.rumble.TriggerRumbleComponent; import dev.isxander.controlify.impl.controller.steamdeck.SteamDeckComponent; @@ -34,7 +34,7 @@ public final class ControllerImpl implements Controller { private final Map components; private ProfileSettings settings; - private final ProfileSettings defaultSettings; + private ProfileSettings defaultSettings; private boolean active; @@ -78,7 +78,7 @@ public ControllerImpl( @Override public @NonNull String name() { - return Optional.ofNullable(this.info.type().friendlyName()) + return this.info.type().friendlyName() .orElse(this.driverReportedName()); } @@ -103,13 +103,18 @@ public void setSettings(@NonNull ProfileSettings settings) { this.settings = settings; } + public void reloadDefaultSettings() { + this.defaultSettings = ProfileSettings.createDefault(); + } + public @NonNull ProfileSettings defaultSettings() { return this.defaultSettings; } - public @NonNull InputComponent input() { - // assert that all controllers must have InputComponent - return this.getComponent(InputComponent.ID).orElseThrow(); + @Override + public @NonNull InputComponentImpl input() { + // assert that all controllers must have InputComponentImpl + return this.getComponent(InputComponentImpl.ID).orElseThrow(); } @Override @@ -121,8 +126,9 @@ public void setSettings(@NonNull ProfileSettings settings) { return this.getComponent(TriggerRumbleComponent.ID); } - public @NonNull Optional gyro() { - return this.getComponent(GyroComponent.ID); + @Override + public @NonNull Optional gyro() { + return this.getComponent(GyroComponentImpl.ID); } public @NonNull Optional led() { @@ -141,7 +147,7 @@ public void setSettings(@NonNull ProfileSettings settings) { return this.getComponent(DualsenseComponent.ID); } - public @NonNull Optional hdHaptic() { + public @NonNull Optional hdHaptics() { return this.getComponent(HDHapticComponent.ID); } @@ -167,6 +173,9 @@ public void setActive(boolean active) { } this.active = active; this.driver.setActive(active); + if (!active) { + this.input().resetActions(); + } } public void close() { diff --git a/src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectInstanceImpl.java b/src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectInstanceImpl.java index 36d120e9f..94a7d88fb 100644 --- a/src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectInstanceImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectInstanceImpl.java @@ -6,17 +6,19 @@ */ package dev.isxander.controlify.impl.controller.dualsense; -import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.api.action.Action; import dev.isxander.controlify.api.contextual.Context; import dev.isxander.controlify.api.contextual.TriggerEffectInstance; +import dev.isxander.controlify.api.input.LogicalInput; import dev.isxander.controlify.impl.contextual.ContextualDomainImpl; import dev.isxander.controlify.impl.contextual.ContextualState; import dev.isxander.controlify.impl.contextual.RuleEngine; import dev.isxander.controlify.impl.contextual.TriggerEffectRule; import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.input.mapping.GamepadInputs; import dev.isxander.controlify.impl.driver.dualsense.DualsenseTriggerEffect; +import dev.isxander.controlify.impl.input.mapping.GamepadInputs; import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; import java.util.List; import java.util.function.Supplier; @@ -38,8 +40,10 @@ public TriggerEffectInstanceImpl( @Override public void update(C context) { + var controller = (ControllerImpl) context.controller(); + RuleEngine ruleEngine = this.ruleEngineSupplier.get(); - boolean useTriggerEffects = context.controller().dualSense() + boolean useTriggerEffects = controller.dualsense() .map(ds -> ds.settings().triggerEffects) .orElse(false); if (!useTriggerEffects) { @@ -51,46 +55,46 @@ public void update(C context) { ContextualState state = this.domain.calculateState(context, ruleEngine.factDependencies()); List passedRules = ruleEngine.evaluate( state, - rule -> getTrigger(context.controller(), rule) != Trigger.NEITHER, + rule -> getTrigger(controller, rule) != Trigger.NEITHER, "trigger_effect/" + this.domain.id() ); // there should only be one each this.leftEffect = passedRules.stream() - .filter(rule -> getTrigger(context.controller(), rule) == Trigger.LEFT) + .filter(rule -> getTrigger(controller, rule) == Trigger.LEFT) .map(TriggerEffectRule::effect) .findAny() .orElse(DualsenseTriggerEffect.Off.INSTANCE); this.rightEffect = passedRules.stream() - .filter(rule -> getTrigger(context.controller(), rule) == Trigger.RIGHT) + .filter(rule -> getTrigger(controller, rule) == Trigger.RIGHT) .map(TriggerEffectRule::effect) .findAny() .orElse(DualsenseTriggerEffect.Off.INSTANCE); } @Override - public DualsenseTriggerEffect getLeftTriggerEffect() { + public @NonNull DualsenseTriggerEffect getLeftTriggerEffect() { return this.leftEffect; } @Override - public DualsenseTriggerEffect getRightTriggerEffect() { + public @NonNull DualsenseTriggerEffect getRightTriggerEffect() { return this.rightEffect; } private Trigger getTrigger(ControllerImpl controller, TriggerEffectRule rule) { - InputBinding binding = rule.binding().onOrNull(controller); + Action action = rule.action().onOrNull(controller); - if (binding == null) { + if (action == null) { return Trigger.NEITHER; } - List relevantInputs = binding.boundInput().getRelevantInputs(); - - if (relevantInputs.contains(GamepadInputs.LEFT_TRIGGER_AXIS)) { - return Trigger.LEFT; - } else if (relevantInputs.contains(GamepadInputs.RIGHT_TRIGGER_AXIS)) { - return Trigger.RIGHT; + if (action.boundInput() instanceof LogicalInput.Single(Identifier physicalInput)) { + if (GamepadInputs.LEFT_TRIGGER_AXIS.equals(physicalInput)) { + return Trigger.LEFT; + } else if (GamepadInputs.RIGHT_TRIGGER_AXIS.equals(physicalInput)) { + return Trigger.RIGHT; + } } return Trigger.NEITHER; diff --git a/src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectManager.java b/src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectManager.java index 86de48682..8ddaed4bf 100644 --- a/src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectManager.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectManager.java @@ -44,7 +44,7 @@ public static void applyTriggerEffects( TriggerEffectInstance instance, boolean inputSuppressed ) { - controller.dualSense().ifPresent(ds -> { + controller.dualsense().ifPresent(ds -> { if (!shouldUseTriggerEffects(controller, inputSuppressed)) { ds.setLeftTriggerEffect(DualsenseTriggerEffect.Off.INSTANCE); ds.setRightTriggerEffect(DualsenseTriggerEffect.Off.INSTANCE); @@ -60,6 +60,6 @@ public static boolean shouldUseTriggerEffects(ControllerImpl controller, boolean return !inputSuppressed && MinecraftUtil.getScreen() == null && Minecraft.getInstance().player != null - && controller.dualSense().map(ds -> ds.settings().triggerEffects).orElse(false); + && controller.dualsense().map(ds -> ds.settings().triggerEffects).orElse(false); } } diff --git a/src/main/java/dev/isxander/controlify/impl/controller/gyro/GyroComponent.java b/src/main/java/dev/isxander/controlify/impl/controller/gyro/GyroComponentImpl.java similarity index 88% rename from src/main/java/dev/isxander/controlify/impl/controller/gyro/GyroComponent.java rename to src/main/java/dev/isxander/controlify/impl/controller/gyro/GyroComponentImpl.java index e3a057d4a..c9ef27b4f 100644 --- a/src/main/java/dev/isxander/controlify/impl/controller/gyro/GyroComponent.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/gyro/GyroComponentImpl.java @@ -7,25 +7,28 @@ package dev.isxander.controlify.impl.controller.gyro; import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.api.controller.gyro.GyroComponent; import dev.isxander.controlify.impl.config.settings.device.DeviceSettings; import dev.isxander.controlify.impl.config.settings.profile.GyroSettings; import dev.isxander.controlify.impl.controller.ControllerComponent; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.input.pipeline.InputPipeline; +import dev.isxander.controlify.impl.input.pipeline.SensorProcessorStage; import dev.isxander.controlify.impl.input.SensorType; import dev.isxander.controlify.impl.input.signal.LogicalSignal; import dev.isxander.controlify.impl.input.signal.PhysicalSensorSignal; import dev.isxander.controlify.impl.input.signal.PhysicalSignal; import dev.isxander.controlify.impl.utils.CUtil; -import dev.isxander.controlify.impl.utils.event.EventSource; +import dev.isxander.controlify.api.utils.event.EventSource; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; import org.joml.Vector3fc; import java.util.Objects; +import java.util.List; -public class GyroComponent extends ControllerComponent { +public class GyroComponentImpl extends ControllerComponent implements GyroComponent { public static final Identifier ID = CUtil.rl("gyro"); private final InputPipeline inputPipeline; private @Nullable EventSource.Subscription calibrationSubscription; @@ -41,7 +44,7 @@ public class GyroComponent extends ControllerComponent { private long lastCalibrationSaveTimeNanos; private Vector3fc lastSavedBias = new Vector3f(); - public GyroComponent(InputPipeline inputPipeline) { + public GyroComponentImpl(InputPipeline inputPipeline) { super(ID); this.inputPipeline = Objects.requireNonNull(inputPipeline); } @@ -60,6 +63,11 @@ public void attach(ControllerImpl controller) { lastSavedBias = new Vector3f(savedOffset); this.lastCalibrationSaveTimeNanos = System.nanoTime(); + this.inputPipeline.stageSensorProcessor().setProcessors(SensorType.GYROSCOPE, List.of( + new SensorProcessorStage.BiasCorrection(() -> this.deviceSettings.gyroCalibration.offset), + new SensorProcessorStage.LowPassFilter(() -> this.settings().lowPassCutoffHz) + )); + this.calibrationSubscription = this.inputPipeline.sourceRawPhysicalSignals().subscribe(this::onRawSignal); this.sensorSubscription = this.inputPipeline.sourceLogicalSignals().subscribe(this::onSignal); } @@ -94,6 +102,7 @@ private void onSignal(LogicalSignal signal) { /// Returns the latest integrated gyro interval in radians. /// Missing samples expire naturally, independently of controller selection. + @Override public Vector3fc getState() { return System.nanoTime() - this.lastStateTimeNanos > MAX_SAMPLE_AGE_NANOS ? new Vector3f() : this.gyroState; diff --git a/src/main/java/dev/isxander/controlify/impl/controller/input/InputComponent.java b/src/main/java/dev/isxander/controlify/impl/controller/input/InputComponentImpl.java similarity index 72% rename from src/main/java/dev/isxander/controlify/impl/controller/input/InputComponent.java rename to src/main/java/dev/isxander/controlify/impl/controller/input/InputComponentImpl.java index 24348e5a9..863a9a1cb 100644 --- a/src/main/java/dev/isxander/controlify/impl/controller/input/InputComponent.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/input/InputComponentImpl.java @@ -1,36 +1,36 @@ package dev.isxander.controlify.impl.controller.input; +import dev.isxander.controlify.api.action.ActionSpec; +import dev.isxander.controlify.api.controller.input.InputComponent; +import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.api.utils.event.EventSource; import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.action.ActionGraph; +import dev.isxander.controlify.impl.action.ActionImpl; +import dev.isxander.controlify.impl.action.ActionSpecRegistryImpl; +import dev.isxander.controlify.impl.action.activator.ActivatorConfig; import dev.isxander.controlify.impl.action.defaults.DefaultBindProvider; +import dev.isxander.controlify.impl.action.proxy.ActionProxyManagerImpl; import dev.isxander.controlify.impl.config.settings.device.DeviceSettings; import dev.isxander.controlify.impl.config.settings.profile.InputSettings; -import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.controller.impl.ECSComponentImpl; -import dev.isxander.controlify.api.input.LogicalInput; import dev.isxander.controlify.impl.controller.ControllerComponent; import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.input.SensorType; import dev.isxander.controlify.impl.input.mapping.ControllerMapping; import dev.isxander.controlify.impl.input.mapping.ControllerMappingManager; -import dev.isxander.controlify.impl.action.ActionGraph; -import dev.isxander.controlify.impl.action.ActionImpl; -import dev.isxander.controlify.api.action.ActionSpec; -import dev.isxander.controlify.impl.action.ActionSpecRegistryImpl; -import dev.isxander.controlify.impl.action.activator.ActivatorConfig; import dev.isxander.controlify.impl.input.pipeline.InputPipeline; -import dev.isxander.controlify.impl.input.pipeline.SensorProcessorStage; import dev.isxander.controlify.impl.utils.CUtil; -import dev.isxander.controlify.impl.utils.event.EventSource; import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import java.util.*; -public class InputComponent extends ControllerComponent { +public class InputComponentImpl extends ControllerComponent implements InputComponent { public static final Identifier ID = CUtil.rl("input"); private final Identifier controllerType; private DeviceSettings deviceSettings; + private ControllerMapping controllerMapping; private final InputPipeline inputPipeline; private final ActionGraph actionGraph; @@ -39,16 +39,14 @@ public class InputComponent extends ControllerComponent { private final Set physicalButtons, physicalAxes; private final boolean isGamepad; - private final Set supportedSensors; private EventSource.Subscription actionGraphSub; - public InputComponent( + public InputComponentImpl( Identifier controllerType, InputPipeline inputPipeline, Set physicalButtons, Set physicalAxes, - Set supportedSensors, boolean isGamepad ) { super(ID); @@ -57,7 +55,6 @@ public InputComponent( this.physicalButtons = Set.copyOf(physicalButtons); this.physicalAxes = Set.copyOf(physicalAxes); - this.supportedSensors = Set.copyOf(supportedSensors); this.isGamepad = isGamepad; this.actionGraph = new ActionGraph(); @@ -71,11 +68,44 @@ public void attach(ControllerImpl controller) { this.deviceSettings = Controlify.instance().config().getSettings() .getOrCreateDeviceSettings(controller.uid()); + this.reloadControllerMapping(); + this.inputPipeline.stageDeadzone().setDeadzoneSupplier(axis -> settings().sensitivity.getDeadzone((Identifier) axis)); + + this.reloadActions(); + this.actionGraphSub = this.inputPipeline.sourceLogicalSignals().subscribe(signal -> { + this.actionGraph.accept(signal); + ActionProxyManagerImpl.INSTANCE.update(this.controller()); + }); + } + + @Override + public @Nullable ActionImpl getAction(@NonNull Identifier actionId) { + return this.actions.get(actionId); + } + + public Collection getAllActions() { + return Collections.unmodifiableCollection(this.actions.values()); + } + + public ActionGraph actionGraph() { + return this.actionGraph; + } + + public InputPipeline inputPipeline() { + return this.inputPipeline; + } + + public ControllerMapping controllerMapping() { + return this.controllerMapping; + } + + /// Refreshes resource-defined mappings while preserving a device-specific override. + public void reloadControllerMapping() { ControllerMapping controllerMapping = this.deviceSettings.mapping; if (controllerMapping == null) { ControllerMappingManager controllerMappingManager = Controlify.instance().controllerMappingManager(); controllerMapping = controllerMappingManager - .getMappingById(controller.info().type().mappingId()) + .getMappingById(this.controller().info().type().mappingId().orElse(null)) .orElse(null); } if (controllerMapping == null) { @@ -83,38 +113,29 @@ public void attach(ControllerImpl controller) { ? ControllerMapping.GAMEPAD_DEADZONES_ONLY_MAPPING : ControllerMapping.NO_MAPPING; } - - this.inputPipeline.stageMapping().setControllerMapping(controllerMapping); - this.inputPipeline.stageDeadzone().setDeadzoneGroups(controllerMapping.deadzones().values()); - this.inputPipeline.stageDeadzone().setDeadzoneSupplier(axis -> settings().sensitivity.getDeadzone((Identifier) axis)); - this.inputPipeline.stageSensorProcessor().setProcessors(sensorType -> { - if (sensorType == SensorType.GYROSCOPE) { - return List.of( - new SensorProcessorStage.BiasCorrection(() -> this.deviceSettings.gyroCalibration.offset), - new SensorProcessorStage.LowPassFilter(() -> this.controller().settings().gyro.lowPassCutoffHz) - ); - } else { - return List.of(); - } - }); - - this.reloadActions(); - this.actionGraphSub = this.inputPipeline.sourceLogicalSignals().subscribe(this.actionGraph); - } - - public @Nullable ActionImpl getAction(Identifier actionId) { - return this.actions.get(actionId); - } - - public InputPipeline inputPipeline() { - return this.inputPipeline; + if (controllerMapping.equals(this.controllerMapping)) { + return; + } + this.resetActions(); + this.controllerMapping = controllerMapping; + this.inputPipeline.setControllerMapping(controllerMapping); } public void resetActions() { this.actions.values().forEach(ActionImpl::reset); + ActionProxyManagerImpl.INSTANCE.update(this.controller()); + } + + public void reloadDefaultInputs() { + DefaultBindProvider provider = Controlify.instance().defaultBindManager() + .getDefaultBindProvider(this.controllerType); + for (ActionImpl action : this.actions.values()) { + action.setDefaultInput(provider.getDefaultInput(action.spec().id())); + } } public void reloadActions() { + ActionProxyManagerImpl.INSTANCE.release(this.controller()); this.actions.clear(); this.actionGraph.clearActions(); @@ -137,6 +158,7 @@ public InputSettings defaultSettings() { return this.controller().defaultSettings().input; } + @Override public boolean isGamepad() { return this.isGamepad; } @@ -149,20 +171,9 @@ public Set physicalAxes() { return this.physicalAxes; } - public boolean supportsSensor(SensorType type) { - return this.supportedSensors.contains(type); - } - - public boolean supportsGyro() { - return this.supportedSensors.stream().anyMatch(SensorType::isGyroscope); - } - - public boolean supportsAccelerometer() { - return this.supportedSensors.stream().anyMatch(SensorType::isAccelerometer); - } - @Override public void close() { + ActionProxyManagerImpl.INSTANCE.release(this.controller()); super.close(); this.actionGraphSub.close(); diff --git a/src/main/java/dev/isxander/controlify/impl/controller/misc/BluetoothDeviceComponent.java b/src/main/java/dev/isxander/controlify/impl/controller/misc/BluetoothDeviceComponent.java index b9483aac5..084e35e4f 100644 --- a/src/main/java/dev/isxander/controlify/impl/controller/misc/BluetoothDeviceComponent.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/misc/BluetoothDeviceComponent.java @@ -7,7 +7,6 @@ package dev.isxander.controlify.impl.controller.misc; import dev.isxander.controlify.impl.config.settings.profile.BluetoothDeviceSettings; -import dev.isxander.controlify.impl.controller.impl.ECSComponentImpl; import dev.isxander.controlify.impl.controller.ControllerComponent; import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/impl/controller/misc/GUIDComponent.java b/src/main/java/dev/isxander/controlify/impl/controller/misc/GUIDComponent.java deleted file mode 100644 index 61e978b0c..000000000 --- a/src/main/java/dev/isxander/controlify/impl/controller/misc/GUIDComponent.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.controller.misc; - -import dev.isxander.controlify.impl.controller.SingleValueComponent; -import dev.isxander.controlify.impl.utils.CUtil; -import net.minecraft.resources.Identifier; - -public class GUIDComponent extends SingleValueComponent { - public static final Identifier ID = CUtil.rl("guid"); - - public GUIDComponent(String value) { - super(value, ID); - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerBackend.java b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerBackend.java index 18974c4d7..baeb8b308 100644 --- a/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerBackend.java +++ b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerBackend.java @@ -1,6 +1,7 @@ package dev.isxander.controlify.impl.controllermanager; import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.controller.ControllerInfo; import dev.isxander.controlify.impl.hid.HIDID; /// A controller backend is responsible for listening to controller events @@ -22,6 +23,10 @@ public ControllerBackend(Controlify controlify, ControllerManager controllerMana /// controllers before ever polling events. public abstract void discoverControllers(); + /// Opens a controller with an already identified type and adds it to the manager. + /// Also used after closing a controller whose resource-defined type changed. + public abstract void createAndAddController(ControllerInfo info, boolean hotplugged); + /// Called once per frame, polls events from the backend and processes /// them, potentially creating controllers and adding them to {@link ControllerManager}. public abstract void pollEvents(); diff --git a/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerCoordinator.java b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerCoordinator.java index 626dc785a..7b40622be 100644 --- a/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerCoordinator.java +++ b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerCoordinator.java @@ -6,24 +6,30 @@ */ package dev.isxander.controlify.impl.controllermanager; +import dev.isxander.controlify.api.ControlifyApi; +import dev.isxander.controlify.api.InputMode; +import dev.isxander.controlify.api.controllermanager.ControllerEvents; +import dev.isxander.controlify.api.utils.event.EventSource; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.api.ControlifyEvents; +import dev.isxander.controlify.impl.action.ActionContextRegistryImpl; import dev.isxander.controlify.impl.config.ConfigManager; import dev.isxander.controlify.impl.config.settings.device.DeviceSettings; import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controller.ControllerInfo; import dev.isxander.controlify.impl.driver.sdl.SDLNativesLoader; import dev.isxander.controlify.impl.gui.screen.AskToMapControllerScreen; import dev.isxander.controlify.impl.gui.screen.BluetoothWarningScreen; import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; -import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; import dev.isxander.controlify.impl.utils.CUtil; import dev.isxander.controlify.impl.utils.DebugLog; import dev.isxander.controlify.impl.utils.MinecraftUtil; import dev.isxander.controlify.impl.utils.SetupWizard; import dev.isxander.controlify.impl.utils.SetupWizardManager; -import dev.isxander.controlify.impl.utils.event.EventSource; +import dev.isxander.controlify.impl.utils.event.EventBus; +import net.minecraft.client.Minecraft; import net.minecraft.network.chat.Component; import org.jetbrains.annotations.Nullable; +import static dev.isxander.controlify.impl.utils.ControllerUtils.wrapControllerError; import java.util.HashMap; import java.util.LinkedHashSet; @@ -31,15 +37,24 @@ import java.util.Optional; import java.util.Set; -import static dev.isxander.controlify.impl.utils.ControllerUtils.wrapControllerError; - /// Owns controller discovery, selection, activation and activity subscriptions. -/// Publishes selection changes through {@link ControlifyEvents#CURRENT_CONTROLLER_CHANGED}. +/// Publishes selection changes through {@link ControllerEvents#CURRENT_CONTROLLER_CHANGED}. public class ControllerCoordinator implements AutoCloseable { + /// Does not initialize a coordinator, backend or any manager when addons subscribe. + public static final class Events { + public static final EventBus DISCOVERY_COMPLETED = new EventBus<>(); + public static final EventBus CONTROLLER_CONNECTED = new EventBus<>(); + public static final EventBus CONTROLLER_DISCONNECTED = new EventBus<>(); + public static final EventBus CURRENT_CONTROLLER_CHANGED = new EventBus<>(); + + private Events() { + } + } + private final Controlify controlify; private final ConfigManager config; private final SetupWizardManager setupWizardManager; - private final ControllerManager controllerManager = new ControllerManager(); + private final ControllerManager controllerManager; private final Map inputSubscriptions = new HashMap<>(); private final Set controllersWithInput = new LinkedHashSet<>(); @@ -53,20 +68,18 @@ public ControllerCoordinator(Controlify controlify, SetupWizardManager setupWiza this.config = controlify.config(); this.setupWizardManager = setupWizardManager; - ControlifyEvents.CONTROLLER_CONNECTED.register(event -> - this.onControllerAdded(event.controller(), event.hotplugged())); - ControlifyEvents.CONTROLLER_DISCONNECTED.register(event -> - this.onControllerRemoved(event.controller())); + this.controllerManager = new ControllerManager(this::onControllerAdded, this::onControllerRemoved); } public boolean initialize() { try { this.backend = new SDLControllerBackend(SDLNativesLoader.load(), this.controlify, this.controllerManager); + this.backend.setup(); } catch (Throwable throwable) { CUtil.LOGGER.error("Failed to initialize controller backend", throwable); + this.backend = null; return false; } - this.backend.setup(); return true; } @@ -99,13 +112,7 @@ public void discoverControllers() { this.applyControllerSelection(false); this.config.saveIfDirty(); - PlatformMainUtil.applyToControlifyEntrypoint(entrypoint -> { - try { - entrypoint.onControllersDiscovered(this.controlify); - } catch (Throwable e) { - CUtil.LOGGER.error("Failed to run `onControllersDiscovered` on Controlify entrypoint: {}", entrypoint.getClass().getName(), e); - } - }); + Events.DISCOVERY_COMPLETED.emit(this.controlify); } /// Polls every controller and resolves selection after all events have been received. @@ -117,6 +124,11 @@ public boolean update(boolean outOfFocus) { return false; } + var contexts = ActionContextRegistryImpl.INSTANCE.getApplicableContexts(Minecraft.getInstance()); + for (var controller : this.controllerManager.getAllControllers()) { + controller.input().actionGraph().setCurrentContexts(outOfFocus ? Set.of() : contexts); + } + this.backend.pollEvents(); if (!outOfFocus && this.config.getActiveProfile().controllerUid == null @@ -143,8 +155,8 @@ public void setCurrentController(@Nullable ControllerImpl controller, boolean ch } else { DebugLog.log("Cleared current controller."); } - ControlifyEvents.CURRENT_CONTROLLER_CHANGED.invoke( - new ControlifyEvents.CurrentControllerChanged(controller, changeInputMode)); + this.controlify.onCurrentControllerChanged(controller, changeInputMode); + Events.CURRENT_CONTROLLER_CHANGED.emit(new ControllerEvents.CurrentControllerChanged(this.controlify, controller)); } public void applyControllerSelection(boolean changeInputMode) { @@ -155,6 +167,32 @@ public void applyControllerSelection(boolean changeInputMode) { this.setCurrentController(selected, changeInputMode); } + /// Recreates type-dependent drivers and components through the normal remove/add lifecycle. + public void reinitController(ControllerImpl controller, ControllerType newType) { + if (!this.isReady()) { + return; + } + var info = controller.info(); + if (this.controllerManager.getControllerByUcid(info.ucid()).orElse(null) != controller) { + throw new IllegalArgumentException("Controller is not registered with this coordinator"); + } + ControllerImpl previousSelection = this.currentController; + InputMode previousInputMode = this.controlify.currentInputMode(); + this.controllerManager.removeController(info.ucid()); + try { + this.backend.createAndAddController(new ControllerInfo(info.ucid(), newType, info.hid()), false); + } finally { + // Adding an auto-selected controller must not steal selection from another device. + if (previousSelection == controller) { + this.controllerManager.getControllerByUcid(info.ucid()) + .ifPresent(replacement -> this.setCurrentController(replacement, false)); + } else { + this.setCurrentController(previousSelection, false); + } + this.controlify.setInputMode(previousInputMode); + } + } + public void applyActiveProfile() { this.controllerManager.getAllControllers().forEach(controller -> controller.setSettings(this.config.getActiveProfile())); @@ -165,15 +203,16 @@ private void onControllerAdded(ControllerImpl controller, boolean hotplugged) { if (this.closed) { return; } - controller.input().ifPresent(input -> { - EventSource.Subscription previous = this.inputSubscriptions.remove(controller); - if (previous != null) { - previous.close(); - } - this.inputSubscriptions.put(controller, input.inputPipeline().sourceProcessedPhysicalSignals() - .filter(signal -> signal instanceof PhysicalInputSignal) - .subscribe(_ -> this.controllersWithInput.add(controller))); - }); + EventSource.Subscription previous = this.inputSubscriptions.remove(controller); + if (previous != null) { + previous.close(); + } + this.inputSubscriptions.put(controller, controller.input().inputPipeline().sourceProcessedPhysicalSignals() + .filter(signal -> signal instanceof PhysicalInputSignal) + .subscribe(s -> { + this.controllersWithInput.add(controller); + System.out.println(s); + })); DeviceSettings rememberedDevice = this.config.getSettings().getOrCreateDeviceSettings(controller.uid()); rememberedDevice.name = controller.name(); @@ -188,14 +227,13 @@ private void onControllerAdded(ControllerImpl controller, boolean hotplugged) { SetupWizard wizard = new SetupWizard(); wizard.addStage( - () -> controller.input().map(input -> !input.isGamepad() - && this.config.getSettings().getOrCreateDeviceSettings(controller.uid()).mapping == null) - .orElse(false), + () -> !controller.input().isGamepad() + && this.config.getSettings().getOrCreateDeviceSettings(controller.uid()).mapping == null, nextScreen -> new AskToMapControllerScreen(controller, nextScreen)); wizard.addStage( - () -> controller.dualSense().isPresent() - && controller.bluetooth().map(bt -> !bt.settings().dontShowWarning).orElse(false), - nextScreen -> new BluetoothWarningScreen(controller.bluetooth().orElseThrow(), nextScreen)); + () -> controller.dualsense().isPresent() + && controller.bluetoothDevice().map(bt -> !bt.settings().dontShowWarning).orElse(false), + nextScreen -> new BluetoothWarningScreen(controller.bluetoothDevice().orElseThrow(), nextScreen)); if (hotplugged) { MinecraftUtil.sendToast( Component.translatable("controlify.toast.controller_connected.title"), @@ -203,6 +241,7 @@ private void onControllerAdded(ControllerImpl controller, boolean hotplugged) { this.config.saveIfDirty(); } this.setupWizardManager.push(wizard); + Events.CONTROLLER_CONNECTED.emit(new ControllerEvents.ControllerConnected(this.controlify, controller, hotplugged)); } private void onControllerRemoved(ControllerImpl controller) { @@ -224,6 +263,7 @@ private void onControllerRemoved(ControllerImpl controller) { MinecraftUtil.sendToast( Component.translatable("controlify.toast.controller_disconnected.title"), Component.translatable("controlify.toast.controller_disconnected.description", controller.name()), false); + Events.CONTROLLER_DISCONNECTED.emit(new ControllerEvents.ControllerDisconnected(this.controlify, controller)); } @Override @@ -238,4 +278,8 @@ public void close() { this.currentController = null; this.controllerManager.close(); } + + public @Nullable ControllerBackend backend() { + return this.backend; + } } diff --git a/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerManager.java b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerManager.java index 7b5ee0ca8..70684cfdc 100644 --- a/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerManager.java +++ b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerManager.java @@ -1,6 +1,5 @@ package dev.isxander.controlify.impl.controllermanager; -import dev.isxander.controlify.api.ControlifyEvents; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.driver.steamdeck.SteamDeckUtil; import dev.isxander.controlify.impl.hid.HIDID; @@ -10,12 +9,19 @@ import java.io.Closeable; import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.Consumer; public class ControllerManager implements Closeable { private final Map controllersByUid; private final Map controllersByUcid; - public ControllerManager() { + private final BiConsumer onAdded; + private final Consumer onRemoved; + + public ControllerManager(BiConsumer onAdded, Consumer onRemoved) { + this.onAdded = onAdded; + this.onRemoved = onRemoved; this.controllersByUid = new HashMap<>(); this.controllersByUcid = new HashMap<>(); } @@ -53,7 +59,7 @@ public void addController(ControllerImpl controller, boolean hotplugged) { CUtil.LOGGER.log("Controller connected: {}", ControllerUtils.createControllerString(controller)); ControllerUtils.wrapControllerError( - () -> ControlifyEvents.CONTROLLER_CONNECTED.invoke(new ControlifyEvents.ControllerConnected(controller, hotplugged)), + () -> this.onAdded.accept(controller, hotplugged), "Connecting controller", controller ); @@ -62,8 +68,6 @@ public void addController(ControllerImpl controller, boolean hotplugged) { public void removeController(UniqueControllerID controllerUcid) { ControllerImpl controller = this.controllersByUcid.get(controllerUcid); - ControlifyEvents.CONTROLLER_DISCONNECTED.invoke(new ControlifyEvents.ControllerDisconnected(controller)); - if (controller == null) { throw new IllegalArgumentException("The given ucid is not associated with any connected controller"); } @@ -71,7 +75,11 @@ public void removeController(UniqueControllerID controllerUcid) { this.controllersByUcid.remove(controllerUcid, controller); this.controllersByUid.remove(controller.uid(), controller); - controller.close(); + try { + this.onRemoved.accept(controller); + } finally { + controller.close(); + } } public Optional getControllerByUcid(UniqueControllerID ucid) { @@ -89,5 +97,7 @@ public Collection getAllControllers() { @Override public void close() { this.controllersByUcid.values().forEach(ControllerImpl::close); + this.controllersByUcid.clear(); + this.controllersByUid.clear(); } } diff --git a/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerType.java b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerType.java index b7a190313..03e02592e 100644 --- a/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerType.java +++ b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerType.java @@ -15,19 +15,21 @@ import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; +import java.util.Optional; + public record ControllerType( Identifier id, - @Nullable String friendlyName, - @Nullable Identifier mappingId, + Optional friendlyName, + Optional mappingId, boolean forceJoystick, boolean preventLoading ) { public static final ControllerType DEFAULT = new ControllerType(CUtil.rl("default"), null, null, false, false); public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - Identifier.CODEC.optionalFieldOf("namespace", DEFAULT.id()).forGetter(ControllerType::id), - CExtraCodecs.nullableField(Codec.STRING, "name").forGetter(ControllerType::friendlyName), - CExtraCodecs.nullableField(Identifier.CODEC, "mapping").forGetter(ControllerType::mappingId), + Identifier.CODEC.optionalFieldOf("id", DEFAULT.id()).forGetter(ControllerType::id), + Codec.STRING.optionalFieldOf("friendly_name").forGetter(ControllerType::friendlyName), + Identifier.CODEC.optionalFieldOf("mapping_id").forGetter(ControllerType::mappingId), Codec.BOOL.optionalFieldOf("force_joystick", false).forGetter(ControllerType::forceJoystick), Codec.BOOL.optionalFieldOf("prevent_loading", false).forGetter(ControllerType::preventLoading) ).apply(instance, ControllerType::new)); diff --git a/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerTypeManager.java b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerTypeManager.java index ea6c3fa1b..768ebb208 100644 --- a/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerTypeManager.java +++ b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerTypeManager.java @@ -13,8 +13,6 @@ import com.mojang.serialization.codecs.RecordCodecBuilder; import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.hid.ControllerHIDInfo; -import dev.isxander.controlify.impl.hid.HIDDevice; import dev.isxander.controlify.impl.hid.HIDID; import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; import dev.isxander.controlify.impl.utils.CUtil; @@ -55,7 +53,7 @@ public Map getTypeMap() { @Override public CompletableFuture load(ResourceManager manager, Executor executor) { - return CompletableFuture.supplyAsync(() -> manager.getResourceStack(CUtil.rl("controllers/controller_identification.json5")), executor) + return CompletableFuture.supplyAsync(() -> manager.getResourceStack(CUtil.rl("controller/controller_identification.json5")), executor) .thenCompose(resources -> { List>>> futures = new ArrayList<>(); for (Resource resource : resources) { @@ -103,34 +101,18 @@ public CompletableFuture apply(Preparations data, ResourceManager manager, } public void triggerFullTypeReload() { - Optional controllerManagerOpt = Controlify.instance().getControllerManager(); - if (controllerManagerOpt.isPresent()) { - ControllerManager controllerManager = controllerManagerOpt.get(); - - for (ControllerImpl controller : controllerManager.getAllControllers()) { - reloadTypeForController(controllerManager, controller); + Controlify.instance().getControllerCoordinator().ifPresent(coordinator -> { + // Reinitialization replaces entries in the manager's live collection. + for (ControllerImpl controller : List.copyOf(coordinator.controllerManager().getAllControllers())) { + reloadTypeForController(coordinator, controller); } - } + }); } - public void reloadTypeForController(ControllerManager controllerManager, ControllerImpl controller) { - Optional hidOpt = controller.info().hid(); - if (hidOpt.isEmpty()) return; - - HIDDevice hid = hidOpt.get(); - - ControllerType newType = this.getControllerType(hid.hidid()); - ControllerType oldType = controller.info().type(); - - // re-initialise the controller if its type has changed - if (!newType.equals(oldType)) { - controllerManager.reinitController( - controller, - new ControllerHIDInfo( - newType, - controller.info().hid() - ) - ); + public void reloadTypeForController(ControllerCoordinator coordinator, ControllerImpl controller) { + ControllerType newType = this.getControllerType(controller.info().hid()); + if (!newType.equals(controller.info().type())) { + coordinator.reinitController(controller, newType); } } diff --git a/src/main/java/dev/isxander/controlify/impl/controllermanager/SDLControllerBackend.java b/src/main/java/dev/isxander/controlify/impl/controllermanager/SDLControllerBackend.java index 7393070c3..3437dbc0c 100644 --- a/src/main/java/dev/isxander/controlify/impl/controllermanager/SDLControllerBackend.java +++ b/src/main/java/dev/isxander/controlify/impl/controllermanager/SDLControllerBackend.java @@ -9,8 +9,8 @@ import dev.isxander.controlify.impl.driver.sdl.*; import dev.isxander.controlify.impl.hid.HIDID; import dev.isxander.controlify.impl.utils.CUtil; -import dev.isxander.controlify.impl.utils.event.EventSource; -import dev.isxander.controlify.impl.utils.event.EventTap; +import dev.isxander.controlify.impl.utils.event.EventBus; +import dev.isxander.controlify.api.utils.event.EventSource; import dev.isxander.controlify.impl.utils.log.ControlifyLogger; import dev.isxander.sdl.*; import net.minecraft.client.Minecraft; @@ -24,7 +24,7 @@ public class SDLControllerBackend extends ControllerBackend { private final Sdl sdl; private final ControlifyLogger logger; private final SDLControllerEventStage controllerEventStage; - private final EventTap controllerEventTap; + private final EventBus controllerEventBus; private SdlEvent event; private long lastFrameTimeNanos = -1; @@ -32,9 +32,9 @@ public class SDLControllerBackend extends ControllerBackend { public SDLControllerBackend(Sdl sdl, Controlify controlify, ControllerManager controllerManager) { super(controlify, controllerManager); this.sdl = sdl; - this.logger = CUtil.LOGGER.createSubLogger("SDLControllerManagerBackend"); + this.logger = CUtil.LOGGER.createSubLogger("SDLControllerBackend"); this.controllerEventStage = new SDLControllerEventStage(); - this.controllerEventTap = new EventTap<>(); + this.controllerEventBus = new EventBus<>(); this.event = new SdlEvent(); } @@ -43,7 +43,7 @@ public void setup() { this.logger.debugLog("Loading gamepad mappings..."); Optional resourceOpt = Minecraft.getInstance().getResourceManager() - .getResource(CUtil.rl("controllers/gamecontrollerdb-sdl3.txt")); + .getResource(CUtil.rl("controller/gamecontrollerdb-sdl3.txt")); if (resourceOpt.isEmpty()) { this.logger.error("Failed to find game controller database."); return; @@ -58,7 +58,9 @@ public void setup() { SdlIoStreamHandle stream = null; try { stream = this.sdl.ioStream().SDL_IOFromConstMem(byteBuffer); - if (stream.isNull()) throw new IllegalStateException("Failed to open stream"); + if (stream.isNull()) { + throw new IllegalStateException("Failed to open stream"); + } int count = this.sdl.gamepad().SDL_AddGamepadMappingsFromIO(stream, true); if (count < 0) { @@ -70,7 +72,9 @@ public void setup() { } } finally { if (stream != null) { - this.sdl.ioStream().SDL_CloseIO(stream); + // I think SDL_AddGamepadMappingsFromIO closes this stream + // as it JVM crashes if I call this + //this.sdl.ioStream().SDL_CloseIO(stream); } } } catch (Throwable e) { @@ -106,7 +110,7 @@ public void pollEvents() { } private void onControllerEvent(SDLControllerEvent event) { - this.controllerEventTap.emit(event); + this.controllerEventBus.emit(event); switch (event) { case SDLControllerEvent.JoyDevice(SdlJoystickId which, _, SDLControllerEvent.DeviceEvent.Type eventType) @@ -116,7 +120,8 @@ private void onControllerEvent(SDLControllerEvent event) { case SDLControllerEvent.JoyDevice(SdlJoystickId which, _, SDLControllerEvent.DeviceEvent.Type type) when type == SDLControllerEvent.DeviceEvent.Type.REMOVED -> { - this.controllerManager.removeController(new SDLUniqueControllerID(which)); + this.controllerManager.getControllerByUcid(new SDLUniqueControllerID(which)) + .ifPresent(controller -> this.controllerManager.removeController(controller.info().ucid())); } default -> {} @@ -124,16 +129,19 @@ private void onControllerEvent(SDLControllerEvent event) { } private void tryCreateAndAddController(SdlJoystickId jid, boolean hotplugged) { - var ucid = new SDLUniqueControllerID(jid); - - var logger = this.logger.createSubLogger("Controller #" + ucid); - logger.debugLog("New controller detected!"); - HIDID hidId = this.getJoystickHIDID(jid); ControllerType type = this.getControllerType(hidId); + this.createAndAddController(new ControllerInfo(new SDLUniqueControllerID(jid), type, hidId), hotplugged); + } - if (this.controllerManager.canAddController(ucid, hidId, type, logger)) { - this.createAndAddController(ucid, hidId, type, logger, hotplugged); + @Override + public void createAndAddController(ControllerInfo info, boolean hotplugged) { + if (!(info.ucid() instanceof SDLUniqueControllerID ucid)) { + throw new IllegalArgumentException("SDL backend requires an SDL controller ID"); + } + var logger = this.logger.createSubLogger("Controller #" + ucid); + if (this.controllerManager.canAddController(ucid, info.hid(), info.type(), logger)) { + this.createAndAddController(ucid, info.hid(), info.type(), logger, hotplugged); } } @@ -150,7 +158,7 @@ private void createAndAddController( boolean isGamepad = this.sdl.gamepad().SDL_IsGamepad(jid) && !DebugProperties.FORCE_JOYSTICK; controllerLogger.debugLog("Controller is gamepad: {}", isGamepad); - EventSource controllerEventSource = this.controllerEventTap + EventSource controllerEventSource = this.controllerEventBus .filter(event -> event instanceof SDLControllerEvent.Frame || event instanceof SDLControllerEvent.DeviceScopedEvent scoped && scoped.which().equals(jid)); @@ -167,9 +175,9 @@ private void createAndAddController( var controller = new ControllerImpl( controllerInfo, driver, + controllerLogger, Controlify.instance().config().getActiveProfile(), - ProfileSettings.createDefault(), - controllerLogger + ProfileSettings.createDefault() ); this.controllerManager.addController(controller, hotplugged); diff --git a/src/main/java/dev/isxander/controlify/impl/driver/CompoundDriver.java b/src/main/java/dev/isxander/controlify/impl/driver/CompoundDriver.java index 460cc2b71..a3d6160d1 100644 --- a/src/main/java/dev/isxander/controlify/impl/driver/CompoundDriver.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/CompoundDriver.java @@ -27,7 +27,7 @@ public void addComponents(ControllerImpl controller) { } @Override - public void update(ControllerImpl controller, boolean outOfFocus) { + public void update(ControllerImpl controller) { for (Driver driver : drivers) { driver.update(controller); } diff --git a/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDL3GamepadDriver.java b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDL3GamepadDriver.java index 16ca790ab..e5c5389ed 100644 --- a/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDL3GamepadDriver.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDL3GamepadDriver.java @@ -6,13 +6,13 @@ */ package dev.isxander.controlify.impl.driver.sdl; +import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.controllermanager.ControllerType; import dev.isxander.controlify.impl.controller.touchpad.TouchpadComponent; -import dev.isxander.controlify.impl.controller.gyro.GyroComponent; +import dev.isxander.controlify.impl.controller.gyro.GyroComponentImpl; import dev.isxander.controlify.impl.controller.touchpad.Touchpads; -import dev.isxander.controlify.impl.controller.input.InputComponent; -import dev.isxander.controlify.impl.input.SensorType; -import dev.isxander.controlify.impl.utils.event.EventSource; +import dev.isxander.controlify.impl.controller.input.InputComponentImpl; +import dev.isxander.controlify.api.utils.event.EventSource; import dev.isxander.controlify.impl.utils.log.ControlifyLogger; import dev.isxander.sdl.*; import net.minecraft.resources.Identifier; @@ -27,12 +27,11 @@ public class SDL3GamepadDriver extends SDLCommonDriver { - private InputComponent inputComponent; + private InputComponentImpl inputComponent; private TouchpadComponent touchpadComponent; private final Set supportedButtons; private final Set supportedAxes; - private final Set supportedSensors; private final boolean isGyroSupported; private boolean active; @@ -51,12 +50,7 @@ public SDL3GamepadDriver(Sdl sdl, SdlGamepadHandle ptrController, EventSource Stream.of(SDLInputConversions.getGamepadAxis(i, true), SDLInputConversions.getGamepadAxis(i, false))) .collect(Collectors.toSet()); - this.supportedSensors = IntStream.range(0, SDL_SENSOR_COUNT) - .filter(i -> this.sdl.gamepad().SDL_GamepadHasSensor(this.ptrController, i)) - .mapToObj(SDLInputConversions::getSensorType) - .collect(Collectors.toSet()); - - this.isGyroSupported = this.supportedSensors.contains(SensorType.GYROSCOPE); + this.isGyroSupported = this.sdl.gamepad().SDL_GamepadHasSensor(this.ptrController, SDL_SENSOR_GYRO); this.numTouchpads = sdl.gamepad().SDL_GetNumGamepadTouchpads(ptrController); } @@ -65,23 +59,22 @@ public SDL3GamepadDriver(Sdl sdl, SdlGamepadHandle ptrController, EventSource 0) { - controller.setComponent(this.touchpadComponent = new TouchpadComponent( + controller.addComponent(this.touchpadComponent = new TouchpadComponent( new Touchpads( IntStream.range(0, numTouchpads) .mapToObj(i -> @@ -111,6 +104,11 @@ public void setActive(boolean active) { @Override protected void onEvent(SDLControllerEvent event) { + // filter out joystick input events if we are a gamepad + if (event instanceof SDLControllerEvent.JoystickEvent && !(event instanceof SDLControllerEvent.DeviceEvent)) { + return; + } + super.onEvent(event); switch (event) { @@ -122,8 +120,8 @@ protected void onEvent(SDLControllerEvent event) { } @Override - public void update(ControllerImpl controller, boolean outOfFocus) { - super.update(controller, outOfFocus); + public void update(ControllerImpl controller) { + super.update(controller); } @Override diff --git a/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDL3JoystickDriver.java b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDL3JoystickDriver.java index 114c54230..d1cabd07b 100644 --- a/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDL3JoystickDriver.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDL3JoystickDriver.java @@ -6,10 +6,11 @@ */ package dev.isxander.controlify.impl.driver.sdl; +import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.controllermanager.ControllerType; import dev.isxander.controlify.impl.input.mapping.JoystickInputs; -import dev.isxander.controlify.impl.controller.input.InputComponent; -import dev.isxander.controlify.impl.utils.event.EventSource; +import dev.isxander.controlify.impl.controller.input.InputComponentImpl; +import dev.isxander.controlify.api.utils.event.EventSource; import dev.isxander.controlify.impl.utils.log.ControlifyLogger; import dev.isxander.sdl.*; import net.minecraft.resources.Identifier; @@ -22,7 +23,7 @@ public class SDL3JoystickDriver extends SDLCommonDriver { - private InputComponent inputComponent; + private InputComponentImpl inputComponent; private final int numAxes, numButtons; private final Set supportedAxes; @@ -47,19 +48,18 @@ public SDL3JoystickDriver(Sdl sdl, SdlJoystickHandle ptrJoystick, EventSource { @Override - public void onEvent(SdlEvent event, EventSink downstream) { + public void onEvent(@NonNull SdlEvent event, @NonNull EventSink downstream) { long timeNanos = event.timestamp(); switch (event.data()) { diff --git a/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLGamepadInputMapping.java b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLGamepadInputMapping.java deleted file mode 100644 index 7004736dc..000000000 --- a/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLGamepadInputMapping.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.driver.sdl; - -import dev.isxander.controlify.impl.controller.impl.ControllerStateImpl; -import dev.isxander.controlify.impl.utils.CUtil; -import dev.isxander.sdl.SdlGamepad; -import dev.isxander.sdl.SdlGamepadHandle; -import net.minecraft.resources.Identifier; - -public sealed interface SDLGamepadInputMapping { - Identifier controlifyInput(); - - boolean hasInput(SdlGamepad sdl, SdlGamepadHandle gamepad); - - void applyState(SdlGamepad sdl, SdlGamepadHandle gamepad, ControllerStateImpl state); - - record ButtonMapping(Identifier controlifyInput, int sdlButton) implements SDLGamepadInputMapping { - @Override - public boolean hasInput(SdlGamepad sdl, SdlGamepadHandle gamepad) { - return sdl.SDL_GamepadHasButton(gamepad, sdlButton); - } - - @Override - public void applyState(SdlGamepad sdl, SdlGamepadHandle gamepad, ControllerStateImpl state) { - if (hasInput(sdl, gamepad)) { - state.setButton(controlifyInput, sdl.SDL_GetGamepadButton(gamepad, sdlButton)); - } - } - } - - record AxisMapping(Identifier controlifyInput, int sdlAxis, boolean positive) implements SDLGamepadInputMapping { - @Override - public boolean hasInput(SdlGamepad sdl, SdlGamepadHandle gamepad) { - return sdl.SDL_GamepadHasAxis(gamepad, sdlAxis); - } - - @Override - public void applyState(SdlGamepad sdl, SdlGamepadHandle gamepad, ControllerStateImpl state) { - if (hasInput(sdl, gamepad)) { - float f = CUtil.mapShortToFloat(sdl.SDL_GetGamepadAxis(gamepad, sdlAxis)); - f = positive ? CUtil.positiveAxis(f) : CUtil.negativeAxis(f); - - state.setAxis(controlifyInput, f); - } - } - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLToPhysicalStage.java b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLToPhysicalStage.java index da46ad9a0..3e52d77f9 100644 --- a/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLToPhysicalStage.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLToPhysicalStage.java @@ -5,9 +5,10 @@ import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; import dev.isxander.controlify.impl.input.signal.PhysicalSensorSignal; import dev.isxander.controlify.impl.input.signal.PhysicalSignal; -import dev.isxander.controlify.impl.utils.event.EventSink; -import dev.isxander.controlify.impl.utils.event.EventStage; +import dev.isxander.controlify.api.utils.event.EventSink; +import dev.isxander.controlify.api.utils.event.EventStage; import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; import java.util.Arrays; @@ -21,7 +22,7 @@ public SDLToPhysicalStage() { } @Override - public void onEvent(SDLControllerEvent event, EventSink downstream) { + public void onEvent(@NonNull SDLControllerEvent event, @NonNull EventSink downstream) { switch (event) { case SDLControllerEvent.Frame(long timeNanos, long deltaTimeNanos) -> downstream.accept(new PhysicalSignal.Frame(timeNanos, deltaTimeNanos)); diff --git a/src/main/java/dev/isxander/controlify/impl/driver/steamdeck/SteamDeckDriver.java b/src/main/java/dev/isxander/controlify/impl/driver/steamdeck/SteamDeckDriver.java index e88f7b966..17af69e81 100644 --- a/src/main/java/dev/isxander/controlify/impl/driver/steamdeck/SteamDeckDriver.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/steamdeck/SteamDeckDriver.java @@ -10,14 +10,10 @@ import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.controller.battery.BatteryLevelComponent; import dev.isxander.controlify.impl.controller.battery.PowerState; -import dev.isxander.controlify.impl.controller.gyro.GyroComponent; -import dev.isxander.controlify.impl.controller.gyro.GyroState; -import dev.isxander.controlify.impl.controller.impl.ControllerStateImpl; +import dev.isxander.controlify.impl.controller.gyro.GyroComponentImpl; import dev.isxander.controlify.impl.controller.misc.DriverNameComponent; -import dev.isxander.controlify.impl.controller.misc.GUIDComponent; import dev.isxander.controlify.impl.controller.misc.UIDComponent; -import dev.isxander.controlify.impl.input.mapping.GamepadInputs; -import dev.isxander.controlify.impl.controller.input.InputComponent; +import dev.isxander.controlify.impl.controller.input.InputComponentImpl; import dev.isxander.controlify.impl.controller.steamdeck.SteamDeckComponent; import dev.isxander.controlify.impl.controller.touchpad.TouchpadComponent; import dev.isxander.controlify.impl.controller.touchpad.Touchpads; @@ -27,7 +23,6 @@ import dev.isxander.deckapi.api.ControllerButton; import dev.isxander.deckapi.api.ControllerState; import dev.isxander.deckapi.api.SteamDeck; -import net.minecraft.util.Mth; import org.joml.Vector2f; import java.util.List; @@ -42,8 +37,8 @@ public class SteamDeckDriver implements Driver { private static SteamDeck deck; private static boolean triedToLoad = false; - private InputComponent inputComponent; - private GyroComponent gyroComponent; + private InputComponentImpl inputComponent; + private GyroComponentImpl gyroComponent; private BatteryLevelComponent batteryLevelComponent; private TouchpadComponent touchpadComponent; @@ -57,30 +52,29 @@ public SteamDeckDriver(ControlifyLogger logger) { @Override public void addComponents(ControllerImpl controller) { - controller.setComponent(new DriverNameComponent("Steam Deck")); - controller.setComponent(new GUIDComponent("steamdeck")); - controller.setComponent(new UIDComponent(CUtil.createUIDFromBytes("steamdeck".getBytes()))); - - controller.setComponent( - this.inputComponent = new InputComponent( - controller, - 20, - 10, - 0, - true, - GamepadInputs.DEADZONE_GROUPS, - controller.info().type().mappingId() - ) - ); - - controller.setComponent( - this.gyroComponent = new GyroComponent() - ); + controller.addComponent(new DriverNameComponent("Steam Deck")); + controller.addComponent(new UIDComponent(CUtil.createUIDFromBytes("steamdeck".getBytes()))); + +// controller.addComponent( +// this.inputComponent = new InputComponentImpl( +// controller, +// 20, +// 10, +// 0, +// true, +// GamepadInputs.DEADZONE_GROUPS, +// controller.info().type().mappingId() +// ) +// ); + +// controller.addComponent( +// this.gyroComponent = new GyroComponentImpl() +// ); // don't add - deck reports incorrect values this.batteryLevelComponent = new BatteryLevelComponent(); - controller.setComponent( + controller.addComponent( this.touchpadComponent = new TouchpadComponent( new Touchpads( new Touchpads.Touchpad[]{ @@ -91,7 +85,7 @@ public void addComponents(ControllerImpl controller) { ) // there are two touchpads, one for each thumb ); - controller.setComponent(new SteamDeckComponent()); + controller.addComponent(new SteamDeckComponent()); } private void ensurePolling() { @@ -123,58 +117,57 @@ private void ensurePolling() { } @Override - public void update(ControllerImpl controller, boolean outOfFocus) { + public void update(ControllerImpl controller) { ensurePolling(); - ControllerStateImpl state = new ControllerStateImpl(); ControllerState deckState = deck.getControllerState(); boolean focused = deck.isGameInFocus(); - - state.setButton(GamepadInputs.NORTH_BUTTON, deckState.getButtonState(ControllerButton.Y) && focused); - state.setButton(GamepadInputs.EAST_BUTTON, deckState.getButtonState(ControllerButton.B) && focused); - state.setButton(GamepadInputs.SOUTH_BUTTON, deckState.getButtonState(ControllerButton.A) && focused); - state.setButton(GamepadInputs.WEST_BUTTON, deckState.getButtonState(ControllerButton.X) && focused); - state.setButton(GamepadInputs.LEFT_SHOULDER_BUTTON, deckState.getButtonState(ControllerButton.L1) && focused); - state.setButton(GamepadInputs.RIGHT_SHOULDER_BUTTON, deckState.getButtonState(ControllerButton.R1) && focused); - state.setButton(GamepadInputs.START_BUTTON, deckState.getButtonState(ControllerButton.START) && focused); - state.setButton(GamepadInputs.BACK_BUTTON, deckState.getButtonState(ControllerButton.SELECT) && focused); - - // not sure if this should ever be used, since it will always open the steam menu -// state.setButton(GamepadInputs.GUIDE_BUTTON, deckState.getButtonState(ControllerButton.STEAM_HOME) && focused); - - state.setButton(GamepadInputs.LEFT_STICK_BUTTON, deckState.getButtonState(ControllerButton.L3) && focused); - state.setButton(GamepadInputs.RIGHT_STICK_BUTTON, deckState.getButtonState(ControllerButton.R3) && focused); - state.setButton(GamepadInputs.DPAD_UP_BUTTON, deckState.getButtonState(ControllerButton.DPAD_UP) && focused); - state.setButton(GamepadInputs.DPAD_DOWN_BUTTON, deckState.getButtonState(ControllerButton.DPAD_DOWN) && focused); - state.setButton(GamepadInputs.DPAD_LEFT_BUTTON, deckState.getButtonState(ControllerButton.DPAD_LEFT) && focused); - state.setButton(GamepadInputs.DPAD_RIGHT_BUTTON, deckState.getButtonState(ControllerButton.DPAD_RIGHT) && focused); - state.setButton(GamepadInputs.LEFT_PADDLE_1_BUTTON, deckState.getButtonState(ControllerButton.L4) && focused); - state.setButton(GamepadInputs.LEFT_PADDLE_2_BUTTON, deckState.getButtonState(ControllerButton.L5) && focused); - state.setButton(GamepadInputs.RIGHT_PADDLE_1_BUTTON, deckState.getButtonState(ControllerButton.R4) && focused); - state.setButton(GamepadInputs.RIGHT_PADDLE_2_BUTTON, deckState.getButtonState(ControllerButton.R5) && focused); - state.setButton(GamepadInputs.TOUCHPAD_1_BUTTON, deckState.getButtonState(ControllerButton.LEFT_TOUCHPAD_CLICK) && focused); - state.setButton(GamepadInputs.TOUCHPAD_2_BUTTON, deckState.getButtonState(ControllerButton.RIGHT_TOUCHPAD_CLICK) && focused); - state.setAxis(GamepadInputs.LEFT_STICK_AXIS_UP, zeroUnless(positiveAxis(mapShortToFloat(deckState.sLeftStickY())), focused)); - state.setAxis(GamepadInputs.LEFT_STICK_AXIS_DOWN, zeroUnless(negativeAxis(mapShortToFloat(deckState.sLeftStickY())), focused)); - state.setAxis(GamepadInputs.LEFT_STICK_AXIS_LEFT, zeroUnless(negativeAxis(mapShortToFloat(deckState.sLeftStickX())), focused)); - state.setAxis(GamepadInputs.LEFT_STICK_AXIS_RIGHT, zeroUnless(positiveAxis(mapShortToFloat(deckState.sLeftStickX())), focused)); - state.setAxis(GamepadInputs.RIGHT_STICK_AXIS_UP, zeroUnless(positiveAxis(mapShortToFloat(deckState.sRightStickY())), focused)); - state.setAxis(GamepadInputs.RIGHT_STICK_AXIS_DOWN, zeroUnless(negativeAxis(mapShortToFloat(deckState.sRightStickY())), focused)); - state.setAxis(GamepadInputs.RIGHT_STICK_AXIS_LEFT, zeroUnless(negativeAxis(mapShortToFloat(deckState.sRightStickX())), focused)); - state.setAxis(GamepadInputs.RIGHT_STICK_AXIS_RIGHT, zeroUnless(positiveAxis(mapShortToFloat(deckState.sRightStickX())), focused)); - state.setAxis(GamepadInputs.LEFT_TRIGGER_AXIS, zeroUnless(mapShortToFloat(deckState.sTriggerL()), focused)); - state.setAxis(GamepadInputs.RIGHT_TRIGGER_AXIS, zeroUnless(mapShortToFloat(deckState.sTriggerR()), focused)); - - this.inputComponent.pushState(state); - - this.gyroComponent.setState( - new GyroState( - deckState.flSoftwareGyroDegreesPerSecondPitch(), - -deckState.flSoftwareGyroDegreesPerSecondYaw(), - -deckState.flSoftwareGyroDegreesPerSecondRoll() - ).mul(Mth.DEG_TO_RAD) // we need radians per second, not degrees - ); +// +// state.setButton(GamepadInputs.NORTH_BUTTON, deckState.getButtonState(ControllerButton.Y) && focused); +// state.setButton(GamepadInputs.EAST_BUTTON, deckState.getButtonState(ControllerButton.B) && focused); +// state.setButton(GamepadInputs.SOUTH_BUTTON, deckState.getButtonState(ControllerButton.A) && focused); +// state.setButton(GamepadInputs.WEST_BUTTON, deckState.getButtonState(ControllerButton.X) && focused); +// state.setButton(GamepadInputs.LEFT_SHOULDER_BUTTON, deckState.getButtonState(ControllerButton.L1) && focused); +// state.setButton(GamepadInputs.RIGHT_SHOULDER_BUTTON, deckState.getButtonState(ControllerButton.R1) && focused); +// state.setButton(GamepadInputs.START_BUTTON, deckState.getButtonState(ControllerButton.START) && focused); +// state.setButton(GamepadInputs.BACK_BUTTON, deckState.getButtonState(ControllerButton.SELECT) && focused); +// +// // not sure if this should ever be used, since it will always open the steam menu +//// state.setButton(GamepadInputs.GUIDE_BUTTON, deckState.getButtonState(ControllerButton.STEAM_HOME) && focused); +// +// state.setButton(GamepadInputs.LEFT_STICK_BUTTON, deckState.getButtonState(ControllerButton.L3) && focused); +// state.setButton(GamepadInputs.RIGHT_STICK_BUTTON, deckState.getButtonState(ControllerButton.R3) && focused); +// state.setButton(GamepadInputs.DPAD_UP_BUTTON, deckState.getButtonState(ControllerButton.DPAD_UP) && focused); +// state.setButton(GamepadInputs.DPAD_DOWN_BUTTON, deckState.getButtonState(ControllerButton.DPAD_DOWN) && focused); +// state.setButton(GamepadInputs.DPAD_LEFT_BUTTON, deckState.getButtonState(ControllerButton.DPAD_LEFT) && focused); +// state.setButton(GamepadInputs.DPAD_RIGHT_BUTTON, deckState.getButtonState(ControllerButton.DPAD_RIGHT) && focused); +// state.setButton(GamepadInputs.LEFT_PADDLE_1_BUTTON, deckState.getButtonState(ControllerButton.L4) && focused); +// state.setButton(GamepadInputs.LEFT_PADDLE_2_BUTTON, deckState.getButtonState(ControllerButton.L5) && focused); +// state.setButton(GamepadInputs.RIGHT_PADDLE_1_BUTTON, deckState.getButtonState(ControllerButton.R4) && focused); +// state.setButton(GamepadInputs.RIGHT_PADDLE_2_BUTTON, deckState.getButtonState(ControllerButton.R5) && focused); +// state.setButton(GamepadInputs.TOUCHPAD_1_BUTTON, deckState.getButtonState(ControllerButton.LEFT_TOUCHPAD_CLICK) && focused); +// state.setButton(GamepadInputs.TOUCHPAD_2_BUTTON, deckState.getButtonState(ControllerButton.RIGHT_TOUCHPAD_CLICK) && focused); +// state.setAxis(GamepadInputs.LEFT_STICK_AXIS_UP, zeroUnless(positiveAxis(mapShortToFloat(deckState.sLeftStickY())), focused)); +// state.setAxis(GamepadInputs.LEFT_STICK_AXIS_DOWN, zeroUnless(negativeAxis(mapShortToFloat(deckState.sLeftStickY())), focused)); +// state.setAxis(GamepadInputs.LEFT_STICK_AXIS_LEFT, zeroUnless(negativeAxis(mapShortToFloat(deckState.sLeftStickX())), focused)); +// state.setAxis(GamepadInputs.LEFT_STICK_AXIS_RIGHT, zeroUnless(positiveAxis(mapShortToFloat(deckState.sLeftStickX())), focused)); +// state.setAxis(GamepadInputs.RIGHT_STICK_AXIS_UP, zeroUnless(positiveAxis(mapShortToFloat(deckState.sRightStickY())), focused)); +// state.setAxis(GamepadInputs.RIGHT_STICK_AXIS_DOWN, zeroUnless(negativeAxis(mapShortToFloat(deckState.sRightStickY())), focused)); +// state.setAxis(GamepadInputs.RIGHT_STICK_AXIS_LEFT, zeroUnless(negativeAxis(mapShortToFloat(deckState.sRightStickX())), focused)); +// state.setAxis(GamepadInputs.RIGHT_STICK_AXIS_RIGHT, zeroUnless(positiveAxis(mapShortToFloat(deckState.sRightStickX())), focused)); +// state.setAxis(GamepadInputs.LEFT_TRIGGER_AXIS, zeroUnless(mapShortToFloat(deckState.sTriggerL()), focused)); +// state.setAxis(GamepadInputs.RIGHT_TRIGGER_AXIS, zeroUnless(mapShortToFloat(deckState.sTriggerR()), focused)); +// +// this.inputComponent.pushState(state); +// +// this.gyroComponent.setState( +// new GyroState( +// deckState.flSoftwareGyroDegreesPerSecondPitch(), +// -deckState.flSoftwareGyroDegreesPerSecondYaw(), +// -deckState.flSoftwareGyroDegreesPerSecondRoll() +// ).mul(Mth.DEG_TO_RAD) // we need radians per second, not degrees +// ); this.batteryLevelComponent.setBatteryLevel( new PowerState.Depleting((int) (mapShortToFloat(deckState.sBatteryLevel()) * 100)) diff --git a/src/main/java/dev/isxander/controlify/impl/font/ActionGlyphsImpl.java b/src/main/java/dev/isxander/controlify/impl/font/ActionGlyphsImpl.java new file mode 100644 index 000000000..817a4c28f --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/font/ActionGlyphsImpl.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.font; + +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.FontDescription; +import net.minecraft.resources.Identifier; + +/// Common-side component encoding shared by the API factories and client mixins. +public final class ActionGlyphsImpl { + public static final Identifier WRAPPER_FONT = Identifier.fromNamespaceAndPath("controlify", "inputs"); + public static final String PLACEHOLDER_KEY = "controlify.placeholder"; + public static final String PLACEHOLDER_CONTROLLER_ACTIVE_KEY = "controlify.placeholder.controller_active"; + + private ActionGlyphsImpl() { + } + + public static Component action(Identifier action) { + return Component.keybind(action.toString()).withStyle(style -> style.withFont(new FontDescription.Resource(WRAPPER_FONT))); + } + + public static Component actionWithFallback(Identifier action, Component fallback) { + return Component.translatableWithFallback(PLACEHOLDER_KEY, "%2$s", action(action), fallback); + } + + public static Component actionWithActiveFallback(Identifier action, Component fallback) { + return Component.translatableWithFallback(PLACEHOLDER_CONTROLLER_ACTIVE_KEY, "%2$s", action(action), fallback); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/font/BindingFontHelper.java b/src/main/java/dev/isxander/controlify/impl/font/BindingFontHelper.java deleted file mode 100644 index 5abb256ee..000000000 --- a/src/main/java/dev/isxander/controlify/impl/font/BindingFontHelper.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.font; - -import dev.isxander.controlify.impl.api.bind.InputBinding; -import dev.isxander.controlify.impl.utils.CUtil; -import net.minecraft.client.gui.Font; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.FontDescription; -import net.minecraft.network.chat.FormattedText; -import net.minecraft.network.chat.Style; -import net.minecraft.resources.Identifier; -import net.minecraft.util.FormattedCharSequence; -import net.minecraft.util.StringDecomposer; -import org.apache.commons.lang3.mutable.MutableInt; - -public final class BindingFontHelper { - public static final Identifier WRAPPER_FONT = CUtil.rl("inputs"); - public static final String PLACEHOLDER_KEY = "controlify.placeholder"; - public static final String PLACEHOLDER_CONTROLLER_ACTIVE_KEY = "controlify.placeholder.controller_active"; - - public static Component bindingWithActiveFallback(Identifier binding, Component fallback) { - return Component.translatableWithFallback(PLACEHOLDER_CONTROLLER_ACTIVE_KEY, "%2$s", binding(binding), fallback); - } - - public static Component bindingWithActiveFallback(InputBinding binding, Component fallback) { - return bindingWithActiveFallback(binding.id(), fallback); - } - - public static Component bindingWithFallback(Identifier binding, Component fallback) { - return Component.translatableWithFallback("controlify.placeholder", "%2$s", binding(binding), fallback); - } - - public static Component bindingWithFallback(InputBinding binding, Component fallback) { - return bindingWithFallback(binding.id(), fallback); - } - - public static Component binding(Identifier binding) { - return Component.keybind(binding.toString()).withStyle(style -> style.withFont(new FontDescription.Resource(WRAPPER_FONT))); - } - - public static Component binding(InputBinding binding) { - return binding(binding.id()); - } - - public static int getComponentHeight(Font font, FormattedCharSequence text) { - MutableInt mutableInt = new MutableInt(); - text.accept((index, style, codePoint) -> { - mutableInt.setValue(Math.max(mutableInt.intValue(), getHeight(font, codePoint, style))); - return true; - }); - return mutableInt.intValue(); - } - - public static int getComponentHeight(Font font, FormattedText text) { - MutableInt mutableInt = new MutableInt(); - StringDecomposer.iterateFormatted(text, Style.EMPTY, (index, style, codePoint) -> { - mutableInt.setValue(Math.max(mutableInt.intValue(), getHeight(font, codePoint, style))); - return true; - }); - return mutableInt.intValue(); - } - - private static int getHeight(Font font, int codepoint, Style style) { - // i can't figure out how to get the height here - return 15; - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/font/ComponentFontHelper.java b/src/main/java/dev/isxander/controlify/impl/font/ComponentFontHelper.java new file mode 100644 index 000000000..a392fed00 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/font/ComponentFontHelper.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.font; + +import net.minecraft.client.gui.Font; +import net.minecraft.network.chat.FormattedText; +import net.minecraft.network.chat.Style; +import net.minecraft.util.FormattedCharSequence; +import net.minecraft.util.StringDecomposer; +import org.apache.commons.lang3.mutable.MutableInt; + +public final class ComponentFontHelper { + private ComponentFontHelper() { + } + + public static int getComponentHeight(Font font, FormattedCharSequence text) { + MutableInt mutableInt = new MutableInt(); + text.accept((index, style, codePoint) -> { + mutableInt.setValue(Math.max(mutableInt.intValue(), getHeight(font, codePoint, style))); + return true; + }); + return mutableInt.intValue(); + } + + public static int getComponentHeight(Font font, FormattedText text) { + MutableInt mutableInt = new MutableInt(); + StringDecomposer.iterateFormatted(text, Style.EMPTY, (index, style, codePoint) -> { + mutableInt.setValue(Math.max(mutableInt.intValue(), getHeight(font, codePoint, style))); + return true; + }); + return mutableInt.intValue(); + } + + private static int getHeight(Font font, int codepoint, Style style) { + // i can't figure out how to get the height here + return 15; + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/font/InputFontMapper.java b/src/main/java/dev/isxander/controlify/impl/font/InputFontMapper.java index 4a17525eb..a93ac6a06 100644 --- a/src/main/java/dev/isxander/controlify/impl/font/InputFontMapper.java +++ b/src/main/java/dev/isxander/controlify/impl/font/InputFontMapper.java @@ -13,8 +13,8 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.JsonOps; -import dev.isxander.controlify.impl.api.bind.InputBinding; -import dev.isxander.controlify.impl.bindings.input.Input; +import dev.isxander.controlify.api.action.Action; +import dev.isxander.controlify.api.input.LogicalInput; import dev.isxander.controlify.impl.controllermanager.ControllerType; import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; import dev.isxander.controlify.impl.utils.CUtil; @@ -28,7 +28,6 @@ import org.jetbrains.annotations.Nullable; import java.io.BufferedReader; -import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; @@ -99,51 +98,58 @@ public FontMap getMappings(Identifier namespace) { return mappings.getOrDefault(namespace, defaultFontMap); } - public Component getComponentFromBinding(Identifier namespace, @Nullable InputBinding binding) { - if (binding == null) { + public Component getComponentFromAction(Identifier namespace, @Nullable Action action) { + if (action == null || action.boundInput() == null) { // TODO: implement some sort of "unbound" character return Component.literal("?"); } - List relevantInputs = binding.boundInput().getRelevantInputs(); - return getComponentFromInputs(namespace, relevantInputs); + return getComponentFromInput(namespace, action.boundInput()); } - public Component getComponentFromBind(Identifier namespace, Input input) { - List relevantInputs = input.getRelevantInputs(); - return getComponentFromInputs(namespace, relevantInputs); - } - - public Component getComponentFromInputs(Identifier namespace, List inputs) { - if (inputs.isEmpty()) { + public Component getComponentFromInput(Identifier namespace, @Nullable LogicalInput input) { + if (input == null) { return Component.literal(""); } FontMap fontMap = getMappings(namespace); - MutableComponent component = Component.empty(); - StringBuilder literal = new StringBuilder(); - FontMap currentFontMap = null; + return switch (input) { + case LogicalInput.Single(var physicalInput) -> { + MappedChar mappedChar = getChar(fontMap, physicalInput); + yield createComponent(String.valueOf(mappedChar.character()), mappedChar.fontMap()); + } + case LogicalInput.ModifiedChord(var modifiers, var trigger) -> { + MutableComponent component = Component.empty(); + StringBuilder literal = new StringBuilder(); + FontMap currentFontMap = null; + + // Coalesce adjacent modifiers using the same font into a single literal. + for (Identifier modifier : modifiers) { + MappedChar mappedChar = getChar(fontMap, modifier); + if (currentFontMap != null && currentFontMap != mappedChar.fontMap()) { + MutableComponent run = createComponent(literal.toString(), currentFontMap); + component.append(run); + literal.setLength(0); + } + currentFontMap = mappedChar.fontMap(); + literal.append(mappedChar.character()); + } - for (int i = 0; i < inputs.size(); i++) { - MappedChar mappedChar = getChar(fontMap, inputs.get(i)); - if (currentFontMap != mappedChar.fontMap()) { if (currentFontMap != null) { - component.append(createComponent(literal.toString(), currentFontMap)); - literal.setLength(0); + MutableComponent run = createComponent(literal.toString(), currentFontMap); + // Use the first run as the root to avoid an empty wrapper component. + component.append(run); + component.append(Component.literal("+").withStyle(style -> style.withFont(FontDescription.DEFAULT))); } - currentFontMap = mappedChar.fontMap(); - } - if (i > 0) { - literal.append('+'); + MappedChar mappedTrigger = getChar(fontMap, trigger); + MutableComponent triggerComponent = createComponent(String.valueOf(mappedTrigger.character()), mappedTrigger.fontMap()); + yield component.append(triggerComponent); } - literal.append(mappedChar.character()); - } - - return component.append(createComponent(literal.toString(), currentFontMap)); + }; } - private Component createComponent(String literal, FontMap fontMap) { + private MutableComponent createComponent(String literal, FontMap fontMap) { return Component.literal(literal).withStyle(style -> style .withFont(new FontDescription.Resource(fontMap.namespace().withPrefix("controller/"))) .withShadowColor(0x00000000) // remove shadow @@ -171,7 +177,6 @@ public Identifier getReloadId() { } public record Preparations(Map mappings) { - } public record FontMap(Identifier namespace, char unknown, Map inputToChar) { } diff --git a/src/main/java/dev/isxander/controlify/impl/gui/ButtonGuideRenderer.java b/src/main/java/dev/isxander/controlify/impl/gui/ButtonGuideRenderer.java index 88d675032..924461b07 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/ButtonGuideRenderer.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/ButtonGuideRenderer.java @@ -6,10 +6,11 @@ */ package dev.isxander.controlify.impl.gui; -import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.api.action.Action; -import dev.isxander.controlify.impl.action.ActionAccessor; +import dev.isxander.controlify.api.action.ActionAccessor; +import dev.isxander.controlify.api.font.ActionGlyphs; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.impl.Controlify; import net.minecraft.client.Minecraft; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; @@ -23,7 +24,7 @@ public interface ButtonGuideRenderer { void controlify$setButtonGuide(RenderData renderData); - static void registerBindingForButton(T button, Supplier action, ButtonGuidePredicate renderPredicate) { + static void registerActionForButton(T button, Supplier action, ButtonGuidePredicate renderPredicate) { ((ButtonGuideRenderer) button).controlify$setButtonGuide(new RenderData<>(action, renderPredicate)); } @@ -31,13 +32,13 @@ record RenderData(Supplier action, ButtonGuidePredicate re public Component getControllerMessage(Action action, Component actualLabel) { var component = Component.empty(); if (!Minecraft.getInstance().font.isBidirectional()) { - component.append(action.inputGlyph()); + component.append(ActionGlyphs.action(action.spec().id())); component.append(CommonComponents.SPACE); } component.append(actualLabel); if (Minecraft.getInstance().font.isBidirectional()) { component.append(CommonComponents.SPACE); - component.append(action.inputGlyph()); + component.append(ActionGlyphs.action(action.spec().id())); } return component; } diff --git a/src/main/java/dev/isxander/controlify/impl/gui/controllers/BindController.java b/src/main/java/dev/isxander/controlify/impl/gui/controllers/BindController.java index 4d0afc7c6..7ab3b1f69 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/controllers/BindController.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/controllers/BindController.java @@ -7,12 +7,11 @@ package dev.isxander.controlify.impl.gui.controllers; import com.mojang.blaze3d.platform.InputConstants; +import dev.isxander.controlify.api.input.LogicalInput; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; -import dev.isxander.controlify.impl.bindings.input.*; -import dev.isxander.controlify.impl.controller.input.ControllerStateView; -import dev.isxander.controlify.impl.controller.input.HatState; -import dev.isxander.controlify.impl.controller.input.InputComponent; +import dev.isxander.controlify.impl.action.ControlifyActions; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controllermanager.ControllerType; import dev.isxander.controlify.impl.gui.screen.BindConsumerScreen; import dev.isxander.controlify.impl.screenop.ComponentProcessor; import dev.isxander.controlify.impl.screenop.ScreenProcessor; @@ -27,22 +26,21 @@ import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; import java.util.Optional; -public class BindController implements Controller { - private final Option option; +public class BindController implements Controller> { + private final Option> option; public final ControllerImpl controller; private boolean conflicting; - public BindController(Option option, ControllerImpl controller) { + public BindController(Option> option, ControllerImpl controller) { this.option = option; this.controller = controller; } @Override - public Option option() { + public Option> option() { return this.option; } @@ -78,10 +76,12 @@ protected void extractValueText(GuiGraphicsExtractor graphics, int mouseX, int m graphics.text(textRenderer, awaitingText, getDimension().xLimit() - textRenderer.width(awaitingText) - getXPadding(), (int)(getDimension().centerY() - textRenderer.lineHeight / 2f), 0xFFFFFFFF, true); } else { var bind = control.option().pendingValue(); - if (EmptyInput.equals(bind)) return; + if (bind == null) { + return; + } Component text = Controlify.instance().inputFontMapper() - .getComponentFromBind(control.controller.info().type().id(), bind); + .getComponentFromInput((control.controller == null ? ControllerType.DEFAULT.id() : control.controller.info().type().id()), bind.orElse(null)); int width = textRenderer.width(text); graphics.text(textRenderer, text, getDimension().xLimit() - width - 1, (int)(getDimension().centerY() - textRenderer.lineHeight / 2f + 1), -1, false); @@ -109,15 +109,20 @@ public boolean mouseClicked(MouseButtonEvent mouseButtonEvent, boolean doubleCli } private void openConsumerScreen() { + if (control.controller == null) { + return; + } awaitingControllerInput = true; - MinecraftUtil.setScreen(new BindConsumerScreen(this::getPressedBind, control.option(), this, MinecraftUtil.getScreen())); + MinecraftUtil.setScreen(new BindConsumerScreen(control.controller, control.option(), this, MinecraftUtil.getScreen())); } @Override public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { - if (controller != control.controller) return true; + if (controller != control.controller) { + return true; + } - if (ControlifyBindings.GUI_PRESS.on(controller).justPressed()) { + if (ControlifyActions.GUI_PRESS.on(controller).pulse().consumePulse()) { openConsumerScreen(); return true; } @@ -132,11 +137,12 @@ protected int getHoveredControlWidth() { @Override protected int getUnhoveredControlWidth() { - if (awaitingControllerInput) + if (awaitingControllerInput) { return textRenderer.width(awaitingText); + } Component text = Controlify.instance().inputFontMapper() - .getComponentFromBind(control.controller.info().type().id(), control.option().pendingValue()); + .getComponentFromInput((control.controller == null ? ControllerType.DEFAULT.id() : control.controller.info().type().id()), control.option().pendingValue().orElse(null)); return textRenderer.width(text); } @@ -145,31 +151,5 @@ protected int getValueColor() { return control.conflicting ? 0xFFFF5555 : super.getValueColor(); } - public Optional getPressedBind() { - InputComponent input = control.controller.input().orElseThrow(); - ControllerStateView state = input.stateNow(); - ControllerStateView prevState = input.stateThen(); - - for (Identifier button : state.getButtons()) { - if (state.isButtonDown(button) && !prevState.isButtonDown(button)) { - return Optional.of(new ButtonInput(button)); - } - } - - for (Identifier axis : state.getAxes()) { - if (state.getAxisState(axis) > 0.5f && prevState.getAxisState(axis) <= 0.5f) { - return Optional.of(new AxisInput(axis)); - } - } - - for (Identifier hat : state.getHats()) { - HatState hatState = state.getHatState(hat); - if (hatState != HatState.CENTERED && prevState.getHatState(hat) == HatState.CENTERED) { - return Optional.of(new HatInput(hat, hatState)); - } - } - - return Optional.empty(); - } } } diff --git a/src/main/java/dev/isxander/controlify/impl/gui/controllers/Deadzone2DImageRenderer.java b/src/main/java/dev/isxander/controlify/impl/gui/controllers/Deadzone2DImageRenderer.java index 8bd12aa78..f2255e26a 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/controllers/Deadzone2DImageRenderer.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/controllers/Deadzone2DImageRenderer.java @@ -6,29 +6,35 @@ */ package dev.isxander.controlify.impl.gui.controllers; +import dev.isxander.controlify.api.utils.event.EventSource; import dev.isxander.controlify.impl.input.mapping.DeadzoneGroup; -import dev.isxander.controlify.impl.controller.input.InputComponent; +import dev.isxander.controlify.impl.controller.input.InputComponentImpl; +import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; +import dev.isxander.controlify.impl.input.signal.PhysicalSignal; import dev.isxander.controlify.impl.utils.render.elements.CircleElementRenderState; import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.gui.image.ImageRenderer; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.resources.Identifier; import java.text.DecimalFormat; -import java.util.List; +import java.util.Arrays; import java.util.function.Supplier; public class Deadzone2DImageRenderer implements ImageRenderer { - private final InputComponent input; private final DeadzoneGroup deadzoneGroup; private final Supplier> deadzoneOption; - public Deadzone2DImageRenderer(InputComponent input, DeadzoneGroup deadzoneGroup, Supplier> deadzoneOption) { - this.input = input; + private final float[] directions; + private final EventSource.Subscription pipelineSubscription; + + public Deadzone2DImageRenderer(InputComponentImpl input, DeadzoneGroup deadzoneGroup, Supplier> deadzoneOption) { this.deadzoneGroup = deadzoneGroup; this.deadzoneOption = deadzoneOption; + this.directions = new float[4]; + Arrays.fill(this.directions, 0f); + this.pipelineSubscription = input.inputPipeline().sourceRawPhysicalSignals().subscribe(this::onSignal); } @Override @@ -37,11 +43,10 @@ public int render(GuiGraphicsExtractor graphics, int x, int y, int renderWidth, int renderHeight = (int) (radius * 2f); // axes go up -> down -> left -> right - List deadzones = deadzoneGroup.axes(); - float up = input.rawStateNow().getAxisState(deadzones.get(0)); - float down = input.rawStateNow().getAxisState(deadzones.get(1)); - float left = input.rawStateNow().getAxisState(deadzones.get(2)); - float right = input.rawStateNow().getAxisState(deadzones.get(3)); + float up = this.directions[0]; + float down = this.directions[1]; + float left = this.directions[2]; + float right = this.directions[3]; float currentX = right - left; float currentY = down - up; @@ -82,8 +87,17 @@ public int render(GuiGraphicsExtractor graphics, int x, int y, int renderWidth, return renderHeight; } + private void onSignal(PhysicalSignal signal) { + if (signal instanceof PhysicalInputSignal.AxisMoved axisMoved) { + int index = this.deadzoneGroup.axes().indexOf(axisMoved.input()); + if (index != -1) { + this.directions[index] = axisMoved.value(); + } + } + } + @Override public void close() { - + this.pipelineSubscription.close(); } } diff --git a/src/main/java/dev/isxander/controlify/impl/gui/guide/GuideInstanceImpl.java b/src/main/java/dev/isxander/controlify/impl/gui/guide/GuideInstanceImpl.java index d51f8cfc6..ed864e856 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/guide/GuideInstanceImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/guide/GuideInstanceImpl.java @@ -6,16 +6,17 @@ */ package dev.isxander.controlify.impl.gui.guide; -import dev.isxander.controlify.impl.api.bind.InputBinding; -import dev.isxander.controlify.impl.contextual.GuideLocation; +import dev.isxander.controlify.api.action.Action; import dev.isxander.controlify.api.contextual.Context; import dev.isxander.controlify.api.contextual.GuideInstance; +import dev.isxander.controlify.api.font.ActionGlyphs; import dev.isxander.controlify.impl.contextual.ContextualDomainImpl; import dev.isxander.controlify.impl.contextual.ContextualState; +import dev.isxander.controlify.impl.contextual.GuideLocation; import dev.isxander.controlify.impl.contextual.GuideRule; import dev.isxander.controlify.impl.contextual.RuleEngine; import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.font.BindingFontHelper; +import dev.isxander.controlify.impl.font.ComponentFontHelper; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphicsExtractor; @@ -53,12 +54,12 @@ public void update(C context) { var leftBuilder = new PrecomputedLines.Builder(); var rightBuilder = new PrecomputedLines.Builder(); - ControllerImpl controller = context.controller(); + var controller = (ControllerImpl) context.controller(); for (GuideRule guide : guides) { - InputBinding binding = guide.binding().onOrNull(controller); - if (binding == null || binding.isUnbound()) { - // skip this rule if the binding is not bound + Action action = guide.action().onOrNull(controller); + if (action == null || action.boundInput() == null) { + // skip this rule if the action is not bound continue; } @@ -67,14 +68,14 @@ public void update(C context) { case RIGHT -> rightBuilder; }; - // put the glyph after or before the binding glyph based on the rule's location and font direction + // put the glyph after or before the action glyph based on the rule's location and font direction boolean glyphAfter = font.isBidirectional() ^ (guide.location() == GuideLocation.RIGHT); // formulate the text to display Component text = Component.empty() - .append(glyphAfter ? guide.text() : binding.inputGlyph()) + .append(glyphAfter ? guide.text() : ActionGlyphs.action(action.spec().id())) .append(" ") - .append(glyphAfter ? binding.inputGlyph() : guide.text()); + .append(glyphAfter ? ActionGlyphs.action(action.spec().id()) : guide.text()); // precompute the width and height of the text int ruleNameWidth = font.width(guide.text()); @@ -83,7 +84,7 @@ public void update(C context) { int backgroundLeft = glyphAfter ? 0 : glyphWidth; int backgroundRight = backgroundLeft + ruleNameWidth; - int height = BindingFontHelper.getComponentHeight(font, binding.inputGlyph()); // use input glyph height only as it's bound to be the tallest + int height = ComponentFontHelper.getComponentHeight(font, ActionGlyphs.action(action.spec().id())); // use input glyph height only as it's bound to be the tallest builder.addLine(text, width, height, backgroundLeft, backgroundRight); } diff --git a/src/main/java/dev/isxander/controlify/impl/gui/screen/AskToMapControllerScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/AskToMapControllerScreen.java index 5acb702f2..4745703be 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/screen/AskToMapControllerScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/AskToMapControllerScreen.java @@ -17,7 +17,7 @@ public AskToMapControllerScreen(ControllerImpl controller, Screen lastScreen) { super( (confirmed) -> { if (confirmed) { - MinecraftUtil.setScreen(ControllerMappingMakerScreen.createGamepadMapping(controller.input().orElseThrow(), lastScreen)); + MinecraftUtil.setScreen(ControllerMappingMakerScreen.createGamepadMapping(controller, lastScreen)); } else { MinecraftUtil.setScreen(lastScreen); } diff --git a/src/main/java/dev/isxander/controlify/impl/gui/screen/BindConsumerScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/BindConsumerScreen.java index 54450313f..675ceb042 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/screen/BindConsumerScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/BindConsumerScreen.java @@ -6,9 +6,11 @@ */ package dev.isxander.controlify.impl.gui.screen; -import dev.isxander.controlify.impl.bindings.input.Input; +import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.api.utils.event.EventSource; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.gui.controllers.BindController; +import dev.isxander.controlify.impl.input.signal.LogicalSignal; import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; import dev.isxander.controlify.impl.utils.MinecraftUtil; @@ -24,8 +26,10 @@ import java.util.Optional; public class BindConsumerScreen extends Screen implements ScreenProcessorProvider { - private final BindConsumer bindConsumer; - private final Option option; + private final ControllerImpl controller; + private EventSource.Subscription inputSubscription; + private LogicalInput capturedInput; + private final Option> option; private final Screen backgroundScreen; private final BindController.BindControllerElement widgetToFocus; private final ScreenProcessorImpl screenProcessor = new ScreenProcessorImpl(this); @@ -33,15 +37,35 @@ public class BindConsumerScreen extends Screen implements ScreenProcessorProvide private int ticksTillClose; private int ticksTillInput; - public BindConsumerScreen(BindConsumer bindConsumer, Option option, BindController.BindControllerElement widgetToFocus, Screen backgroundScreen) { + public BindConsumerScreen(ControllerImpl controller, Option> option, BindController.BindControllerElement widgetToFocus, Screen backgroundScreen) { super(Component.empty()); - this.bindConsumer = bindConsumer; + this.controller = controller; this.option = option; this.widgetToFocus = widgetToFocus; this.backgroundScreen = backgroundScreen; this.ticksTillInput = 5; } + @Override + protected void init() { + if (inputSubscription != null) { + inputSubscription.close(); + } + inputSubscription = controller.input().inputPipeline().sourceLogicalSignals().subscribe(signal -> { + if (ticksTillInput == 0 && ticksTillClose == 0 && capturedInput == null + && signal instanceof LogicalSignal.Tapped tap) { + capturedInput = tap.input(); + } + }); + } + + @Override + public void removed() { + if (inputSubscription != null) { + inputSubscription.close(); + } + } + @Override public void extractRenderState(@NonNull GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { Dimension dim = widgetToFocus.getDimension(); @@ -84,9 +108,10 @@ public void tick() { // tick runs after all controller input ticks - Optional pressedBind = bindConsumer.getPressedBind(); + Optional pressedBind = Optional.ofNullable(capturedInput); if (pressedBind.isPresent()) { - option.requestSet(pressedBind.get()); + option.requestSet(pressedBind); + capturedInput = null; returnToBackground(); } } @@ -94,9 +119,15 @@ public void tick() { @Override public boolean keyPressed(@NonNull KeyEvent keyEvent) { boolean consumed = super.keyPressed(keyEvent); - if (consumed) return true; + if (consumed) { + return true; + } + + if (ticksTillInput > 0) { - if (ticksTillInput > 0) return false; + return false; + + } returnToBackground(); return true; } @@ -104,9 +135,15 @@ public boolean keyPressed(@NonNull KeyEvent keyEvent) { @Override public boolean mouseClicked(@NonNull MouseButtonEvent mouseButtonEvent, boolean doubleClick) { boolean consumed = super.mouseClicked(mouseButtonEvent, doubleClick); - if (consumed) return true; + if (consumed) { + return true; + } + + if (ticksTillInput > 0) { + + return false; - if (ticksTillInput > 0) return false; + } returnToBackground(); return true; } @@ -114,9 +151,15 @@ public boolean mouseClicked(@NonNull MouseButtonEvent mouseButtonEvent, boolean @Override public boolean mouseDragged(@NonNull MouseButtonEvent mouseButtonEvent, double dx, double dy) { boolean consumed = super.mouseDragged(mouseButtonEvent, dx, dy); - if (consumed) return true; + if (consumed) { + return true; + } - if (ticksTillInput > 0) return false; + if (ticksTillInput > 0) { + + return false; + + } returnToBackground(); return true; } @@ -124,14 +167,23 @@ public boolean mouseDragged(@NonNull MouseButtonEvent mouseButtonEvent, double d @Override public boolean mouseScrolled(double mouseX, double mouseY, double amount, double d) { boolean consumed = super.mouseScrolled(mouseX, mouseY, amount, d); - if (consumed) return true; + if (consumed) { + return true; + } + + if (ticksTillInput > 0) { + + return false; - if (ticksTillInput > 0) return false; + } returnToBackground(); return true; } private void returnToBackground() { + if (inputSubscription != null) { + inputSubscription.close(); + } ticksTillClose = 5; } @@ -140,10 +192,6 @@ public ScreenProcessor screenProcessor() { return screenProcessor; } - public interface BindConsumer { - Optional getPressedBind(); - } - private static class ScreenProcessorImpl extends ScreenProcessor { public ScreenProcessorImpl(BindConsumerScreen screen) { super(screen); diff --git a/src/main/java/dev/isxander/controlify/impl/gui/screen/ControlifySettingsScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/ControlifySettingsScreen.java index e9b9f8f92..adabf74c3 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/screen/ControlifySettingsScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/ControlifySettingsScreen.java @@ -7,19 +7,19 @@ package dev.isxander.controlify.impl.gui.screen; import com.google.common.collect.ImmutableList; -import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.api.gui.ButtonGuideApi; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.config.settings.device.DeviceSettings; import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.controllermanager.ControllerType; import dev.isxander.controlify.impl.gui.components.PlainTextWidget; -import dev.isxander.controlify.mixins.feature.ui.AbstractSelectionListAccessor; import dev.isxander.controlify.impl.screenop.ScreenControllerEventListener; import dev.isxander.controlify.impl.utils.CUtil; import dev.isxander.controlify.impl.utils.MinecraftUtil; +import dev.isxander.controlify.mixins.feature.ui.AbstractSelectionListAccessor; import net.minecraft.ChatFormatting; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.*; @@ -61,7 +61,6 @@ protected void init() { this.footerHeight = this.height - this.footerY; this.mainPaneHeight = this.footerY; - // Top Links Component donateText = Component.translatable("controlify.gui.carousel.donate") .withStyle(ChatFormatting.GOLD, ChatFormatting.BOLD); @@ -78,7 +77,6 @@ protected void init() { }, font)); artCreditBtn.setTabOrderGroup(2); - // Controller Not Detected Button controllerNotDetectedButton = this.addRenderableWidget( Button.builder( @@ -92,7 +90,6 @@ protected void init() { controllerNotDetectedButton.visible = !hasController(); FrameLayout.centerInRectangle(controllerNotDetectedButton, 0, 0, this.width, this.mainPaneHeight); - // Footer GridLayout grid = new GridLayout().columnSpacing(10); GridLayout.RowHelper rowHelper = grid.createRowHelper(2); @@ -115,7 +112,6 @@ protected void init() { grid.arrangeElements(); FrameLayout.centerInRectangle(grid, 0, this.footerY, this.width, this.footerHeight); - // Main Pane if (!this.controllerNotDetectedButton.visible) { int mainPaneX = 0; @@ -150,8 +146,8 @@ protected void init() { } } - ButtonGuideApi.addGuideToButton(globalSettingsButton, ControlifyBindings.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always()); - ButtonGuideApi.addGuideToButton(doneButton, ControlifyBindings.GUI_BACK, ButtonGuidePredicate.always()); + ButtonGuideApi.addGuideToButton(globalSettingsButton, ControlifyActions.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always()); + ButtonGuideApi.addGuideToButton(doneButton, ControlifyActions.GUI_BACK, ButtonGuidePredicate.always()); } @Override @@ -193,7 +189,7 @@ public void extractBackground(@NonNull GuiGraphicsExtractor graphics, int mouseX @Override public void onControllerInput(ControllerImpl controller) { - if (ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).justPressed()) { + if (ControlifyActions.GUI_ABSTRACT_ACTION_1.on(controller).pulse().consumePulse()) { // Button impl does not use NonNull parameter globalSettingsButton.onPress(null); } @@ -206,7 +202,7 @@ public void onClose() { private boolean hasController() { return Controlify.instance().getControllerManager() - .map(c -> !c.getConnectedControllers().isEmpty()) + .map(c -> !c.getAllControllers().isEmpty()) .orElse(false); } @@ -394,9 +390,7 @@ private Optional getController(ProfileSettings profileSettings) return Controlify.instance().getCurrentController(); } return Controlify.instance().getControllerManager().flatMap(manager -> - manager.getConnectedControllers().stream() - .filter(controller -> profileSettings.controllerUid.equals(controller.uid())) - .findFirst() + manager.getControllerByUid(profileSettings.controllerUid) ); } @@ -410,7 +404,6 @@ private Optional getController(ProfileSettings profileSettings) return NarrationPriority.NONE; } - @Override public void updateNarration(NarrationElementOutput narrationElementOutput) { narrationElementOutput.add(NarratedElementType.TITLE, this.profileNameText.getMessage()); @@ -476,7 +469,6 @@ public void extractRenderState(@NonNull GuiGraphicsExtractor graphics, int mouse return this.children; } - @Override public @NonNull NarrationPriority narrationPriority() { return NarrationPriority.NONE; @@ -484,7 +476,6 @@ public void extractRenderState(@NonNull GuiGraphicsExtractor graphics, int mouse @Override public void updateNarration(@NonNull NarrationElementOutput narrationElementOutput) { - } } } diff --git a/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerConfigScreenFactory.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerConfigScreenFactory.java index 5ad143665..735c75fb2 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerConfigScreenFactory.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerConfigScreenFactory.java @@ -6,39 +6,35 @@ */ package dev.isxander.controlify.impl.gui.screen; +import dev.isxander.controlify.api.action.Action; import dev.isxander.controlify.api.contextual.GuideVerbosity; import dev.isxander.controlify.api.controller.rumble.RumbleEffect; import dev.isxander.controlify.api.controller.rumble.RumbleState; +import dev.isxander.controlify.api.input.LogicalInput; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.impl.api.bind.InputBinding; -import dev.isxander.controlify.impl.api.bind.InputBindingSupplier; -import dev.isxander.controlify.impl.bindings.BindContext; -import dev.isxander.controlify.impl.bindings.ControlifyBindApiImpl; -import dev.isxander.controlify.impl.bindings.input.EmptyInput; -import dev.isxander.controlify.impl.bindings.input.Input; +import dev.isxander.controlify.impl.action.ActionGraph; +import dev.isxander.controlify.impl.action.ActionImpl; +import dev.isxander.controlify.impl.action.ActionSpecRegistryImpl; import dev.isxander.controlify.impl.config.settings.device.DeviceSettings; import dev.isxander.controlify.impl.config.settings.profile.GenericControllerSettings; import dev.isxander.controlify.impl.config.settings.profile.InputSettings; import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; +import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.controller.gyro.GyroButtonMode; import dev.isxander.controlify.impl.controller.gyro.GyroYawMode; -import dev.isxander.controlify.impl.controller.input.InputComponent; +import dev.isxander.controlify.impl.controller.input.InputComponentImpl; import dev.isxander.controlify.impl.controller.rumble.RumbleComponentImpl; import dev.isxander.controlify.impl.controller.rumble.RumbleSource; +import dev.isxander.controlify.impl.controllermanager.ControllerType; import dev.isxander.controlify.impl.gui.controllers.BindController; import dev.isxander.controlify.impl.gui.controllers.Deadzone2DImageRenderer; import dev.isxander.controlify.impl.ingame.InputCurves; import dev.isxander.controlify.impl.input.mapping.DeadzoneGroup; -import dev.isxander.controlify.impl.input.mapping.Inputs; import dev.isxander.controlify.impl.server.ServerPolicies; import dev.isxander.controlify.impl.utils.CUtil; import dev.isxander.controlify.impl.utils.MinecraftUtil; import dev.isxander.yacl3.api.*; import dev.isxander.yacl3.api.controller.*; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; @@ -49,6 +45,11 @@ import net.minecraft.util.Util; import org.jetbrains.annotations.Nullable; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + public class ControllerConfigScreenFactory { private static final ValueFormatter percentFormatter = v -> Component.literal(String.format("%.0f%%", v*100)); private static final ValueFormatter percentOrOffFormatter = v -> v == 0 ? CommonComponents.OPTION_OFF : percentFormatter.format(v); @@ -177,10 +178,6 @@ private Optional makeSensitivityGroup( var builder = OptionGroup.createBuilder() .name(Component.translatable("controlify.gui.config.group.sensitivity")); - if (controller.isPresent() && controller.get().input().isEmpty()) { - builder.option(LabelOption.create(Component.literal("TODO warning no input component"))); - } - InputSettings.SensitivitySettings sens = settings.input.sensitivity; InputSettings.SensitivitySettings def = defaults.input.sensitivity; @@ -268,8 +265,9 @@ private Optional makeControlsGroup( ProfileSettings defaults, Optional controller ) { - if (controller.isEmpty()) + if (controller.isEmpty()) { return Optional.empty(); + } ValueFormatter holdToggleFormatter = v -> Component.translatable("controlify.gui.format.hold_toggle." + (v ? "toggle" : "hold")); @@ -415,11 +413,11 @@ private Optional makeDeadzoneGroup( ProfileSettings defaults, Optional controller ) { - Optional inputOpt = controller.flatMap(ControllerImpl::input); - if (inputOpt.isEmpty()) + if (controller.isEmpty()) { return Optional.empty(); + } - InputComponent input = inputOpt.get(); + InputComponentImpl input = controller.get().input(); InputSettings config = settings.input; InputSettings def = defaults.input; @@ -430,7 +428,7 @@ private Optional makeDeadzoneGroup( group.option(LabelOption.create(Component.translatable("controlify.gui.stickdrift_warning").withStyle(ChatFormatting.RED))); - for (DeadzoneGroup deadzoneGroup : input.getDeadzoneGroups().values()) { + for (DeadzoneGroup deadzoneGroup : input.controllerMapping().deadzones().values()) { Identifier groupName = deadzoneGroup.name(); Component name = Component.translatable("controlify.deadzone_group." + groupName.getNamespace() + "." + groupName.getPath()); @@ -488,16 +486,16 @@ private ConfigCategory createAdvancedCategory( private Optional makeControllerMappingGroup( ProfileSettings settings, ProfileSettings defaults, - Optional controller + Optional controllerOpt ) { - Optional inputOpt = controller.flatMap(ControllerImpl::input); - if (inputOpt.isEmpty()) + if (controllerOpt.isEmpty()) { return Optional.empty(); - InputComponent input = inputOpt.get(); + } + ControllerImpl controller = controllerOpt.get(); DeviceSettings config = Controlify.instance().config().getSettings() - .getOrCreateDeviceSettings(controller.get().uid()); - DeviceSettings def = DeviceSettings.defaults(controller.map(ControllerImpl::uid).orElse("")); + .getOrCreateDeviceSettings(controller.uid()); + DeviceSettings def = DeviceSettings.defaults(controller.uid()); return Optional.of(OptionGroup.createBuilder() .name(Component.translatable("controlify.gui.group.controller_mapping")) @@ -505,7 +503,7 @@ private Optional makeControllerMappingGroup( .option(ButtonOption.createBuilder() .name(Component.translatable("controlify.gui.create_gamepad_mapping")) .description(OptionDescription.of(Component.translatable("controlify.gui.create_gamepad_mapping.tooltip"))) - .action((screen, button) -> MinecraftUtil.setScreen(ControllerMappingMakerScreen.createGamepadMapping(input, screen))) + .action((screen, button) -> MinecraftUtil.setScreen(ControllerMappingMakerScreen.createGamepadMapping(controller, screen))) .build()) .option(ButtonOption.createBuilder() .name(Component.translatable("controlify.gui.clear_mapping")) @@ -722,7 +720,7 @@ private Optional makeTriggerEffectsGroup( .text(Component.translatable("controlify.gui.trigger_effects.tooltip")) .build()); - if (controller.isPresent() && controller.get().dualSense().isEmpty()) { + if (controller.isPresent() && controller.get().dualsense().isEmpty()) { group.collapsed(true); group.option(LabelOption.create(Component.translatable("controlify.gui.group.trigger_effects.no_dualsense.tooltip").withStyle(ChatFormatting.RED))); } @@ -749,7 +747,7 @@ private Optional makeBindsCategory( InputSettings.RadialMenuSettings radialConfig = settings.input.radialMenu; InputSettings.RadialMenuSettings radialDef = defaults.input.radialMenu; - List optionBinds = new ArrayList<>(); + List optionActions = new ArrayList<>(); category .optionIf(controller.isPresent(), ButtonOption.createBuilder() @@ -774,26 +772,36 @@ private Optional makeBindsCategory( .range(2, 40).step(1).formatValue(ticksToMillisFormatter)) .build()); - Collection allBindings = controller.isPresent() - ? controller.get().input().map(InputComponent::getAllBindings).orElse(List.of()) - : ControlifyBindApiImpl.INSTANCE.provideBindsForController(null); // when giving null as controller, we get all binds - - groupBindings(allBindings).forEach((categoryName, bindGroup) -> { + Collection allActions = controller + .>map(c -> c.input().getAllActions().stream() + .filter(action -> action.spec().shouldExpose().test(c)) + .map(action -> (Action) action).toList()) + .orElseGet(() -> ActionSpecRegistryImpl.INSTANCE.getActionSpecs().stream() + .map(spec -> { + var action = new ActionImpl(spec, + settings.input.bindings.bindings.get(spec.id()), null, + Controlify.instance().activatorConfigManager().getConfig(spec.id()), + new ActionGraph()); + action.reset(); + return (Action) action; + }).toList()); + + groupActions(allActions).forEach((categoryName, bindGroup) -> { var controlsGroup = OptionGroup.createBuilder() .name(categoryName); controlsGroup.options(bindGroup.stream().map(binding -> { - Option.Builder option = createBindingOpt(binding, controller.orElse(null)) - .addListener((opt, val) -> updateConflictingBinds(optionBinds)); + Option.Builder> option = createActionOpt(binding, controller.orElse(null), settings) + .addListener((opt, val) -> updateConflictingActions(optionActions)); - Option built = option.build(); - optionBinds.add(new OptionBindPair(built, binding)); + Option> built = option.build(); + optionActions.add(new OptionActionPair(built, binding)); return built; }).toList()); category.group(controlsGroup.build()); }); - updateConflictingBinds(optionBinds); + updateConflictingActions(optionActions); category.option(ButtonOption.createBuilder() .name(Component.translatable("controlify.gui.reset_all_binds")) @@ -801,10 +809,8 @@ private Optional makeBindsCategory( .text(Component.translatable("controlify.gui.reset_all_binds.tooltip")) .build()) .action((screen, opt) -> { - for (OptionBindPair pair : optionBinds) { - // who needs a type system? - // noinspection unchecked - ((Option) pair.option()).requestSet(pair.binding.defaultInput()); + for (OptionActionPair pair : optionActions) { + pair.option().requestSet(Optional.ofNullable(pair.action.defaultInput())); } }) .build()); @@ -812,20 +818,20 @@ private Optional makeBindsCategory( return Optional.of(category.build()); } - private void updateConflictingBinds(List all) { + private void updateConflictingActions(List all) { all.forEach(pair -> ((BindController) pair.option().controller()).setConflicting(false)); - for (OptionBindPair opt : all) { - Set ctxs = opt.binding().contexts(); + for (OptionActionPair opt : all) { + Set ctxs = opt.action().spec().contexts(); - List conflicting = all.stream() - .filter(pair -> pair.binding() != opt.binding()) + List conflicting = all.stream() + .filter(pair -> pair.action() != opt.action()) .filter(pair -> { - boolean contextsMatch = pair.binding().contexts() + boolean contextsMatch = pair.action().spec().contexts() .stream() .anyMatch(ctxs::contains); - boolean bindMatches = pair.option().pendingValue().equals(opt.option().pendingValue()); - boolean bindIsNotEmpty = !(pair.option().pendingValue() instanceof EmptyInput); + boolean bindMatches = Objects.equals(pair.option().pendingValue(), opt.option().pendingValue()); + boolean bindIsNotEmpty = pair.option().pendingValue() != null; return contextsMatch && bindMatches && bindIsNotEmpty; }).toList(); @@ -833,42 +839,34 @@ private void updateConflictingBinds(List all) { } } - private static Map> groupBindings(Collection bindings) { + private static Map> groupActions(Collection bindings) { return bindings.stream() - .collect(Collectors.groupingBy(InputBinding::category, LinkedHashMap::new, Collectors.toList())); - } - - private static Option.Builder createBindingOpt(InputBindingSupplier bindingSupplier, ControllerImpl controller) { - return createBindingOpt(bindingSupplier.on(controller), controller); + .collect(Collectors.groupingBy(a -> a.spec().category(), LinkedHashMap::new, Collectors.toList())); } - private static Option.Builder createBindingOpt(InputBinding binding, @Nullable ControllerImpl controller) { - return Option.createBuilder() - .name(binding.name()) - .description(v -> OptionDescription.createBuilder() - .text(binding.description()) + private Option.Builder> createActionOpt(Action action, @Nullable ControllerImpl controller, ProfileSettings settings) { + Identifier fontNamespace = controller == null + ? ControllerType.DEFAULT.id() + : controller.info().type().id(); + return Option.>createBuilder() + .name(action.spec().name()) + .description(value -> OptionDescription.createBuilder() + .text(action.spec().description()) .text(Component.translatable("controlify.gui.bind.currently_bound_to", - Component.empty() - .append(Controlify.instance().inputFontMapper().getComponentFromInputs( - controller.info().type().id(), - v.getRelevantInputs() - )) - .append(CommonComponents.SPACE) - .append(Inputs.getInputComponentAnd(v.getRelevantInputs())) - )) - .text(v.equals(binding.defaultInput()) ? Component.empty() : Component.translatable("controlify.gui.bind.default_bound_to", - Component.empty() - .append(Controlify.instance().inputFontMapper().getComponentFromInputs( - controller.info().type().id(), - binding.defaultInput().getRelevantInputs() - )) - .append(CommonComponents.SPACE) - .append(Inputs.getInputComponentAnd( - binding.defaultInput().getRelevantInputs() - )) - )) - .build()) - .binding(EmptyInput.INSTANCE, binding::boundInput, binding::setBoundInput) + Controlify.instance().inputFontMapper().getComponentFromInput(fontNamespace, value.orElse(null)))) + .text(Objects.equals(value, action.defaultInput()) ? Component.empty() + : Component.translatable("controlify.gui.bind.default_bound_to", + Controlify.instance().inputFontMapper().getComponentFromInput(fontNamespace, action.defaultInput()))) + .build()) + .binding(Optional.ofNullable(action.defaultInput()), () -> Optional.ofNullable(action.boundInput()), value -> { + LogicalInput input = value.orElse(null); + action.setBoundInput(input); + if (input == null) { + settings.input.bindings.bindings.remove(action.spec().id()); + } else { + settings.input.bindings.bindings.put(action.spec().id(), input); + } + }) .customController(opt -> new BindController(opt, controller)); } @@ -880,6 +878,6 @@ private static MutableComponent notSupportedText(Component featureName) { return Component.translatable("controlify.gui.not_supported", featureName).withStyle(ChatFormatting.RED); } - private record OptionBindPair(Option option, InputBinding binding) { + private record OptionActionPair(Option> option, Action action) { } } diff --git a/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerMappingMakerScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerMappingMakerScreen.java index 322ebbbed..96cf51252 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerMappingMakerScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerMappingMakerScreen.java @@ -6,18 +6,23 @@ */ package dev.isxander.controlify.impl.gui.screen; +import dev.isxander.controlify.api.utils.event.EventSource; import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.config.settings.device.DeviceSettings; import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.controller.input.mapping.*; -import dev.isxander.controlify.impl.input.mapping.*; -import dev.isxander.controlify.impl.screenop.ScreenControllerEventListener; +import dev.isxander.controlify.impl.input.mapping.ControllerMapping; +import dev.isxander.controlify.impl.input.mapping.DeadzoneGroup; +import dev.isxander.controlify.impl.input.mapping.GamepadInputs; +import dev.isxander.controlify.impl.input.mapping.MapType; +import dev.isxander.controlify.impl.input.mapping.MappingEntry; +import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; +import dev.isxander.controlify.impl.input.signal.PhysicalSignal; import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.ColorUtils; import dev.isxander.controlify.impl.utils.MinecraftUtil; import dev.isxander.controlify.impl.utils.render.RenderUtils; -import dev.isxander.controlify.impl.utils.ColorUtils; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.Tooltip; @@ -27,10 +32,20 @@ import net.minecraft.resources.Identifier; import org.jspecify.annotations.NonNull; +import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.Set; -public class ControllerMappingMakerScreen extends Screen implements ScreenControllerEventListener, ScreenProcessorProvider, DontInterruptScreen { - private final InputComponent inputComponent; +public class ControllerMappingMakerScreen extends Screen implements ScreenProcessorProvider, DontInterruptScreen { + private static final float AXIS_PRESS_THRESHOLD = 0.5f; + private static final float AXIS_RELEASE_THRESHOLD = 0.25f; + + private final ControllerImpl controller; + private final Map capturedMappings = new LinkedHashMap<>(); + private final Set activeInputs = new HashSet<>(); + private EventSource.Subscription inputSubscription; private final ControllerMapping.Builder mappingBuilder = new ControllerMapping.Builder(); private final ScreenProcessor screenProcessor = new ScreenProcessorImpl(this); @@ -69,27 +84,22 @@ public class ControllerMappingMakerScreen extends Screen implements ScreenContro private Button goBackButton; private final Screen lastScreen; - public ControllerMappingMakerScreen(InputComponent inputComponent, Screen lastScreen, List stages, Iterable deadzoneGroups) { + public ControllerMappingMakerScreen(ControllerImpl controller, Screen lastScreen, List stages, Iterable deadzoneGroups) { super(Component.literal("Gamepad Emulation Mapping Creator")); - this.inputComponent = inputComponent; + this.controller = controller; this.lastScreen = lastScreen; - this.stages = stages; - mappingBuilder.putDeadzoneGroups(deadzoneGroups); - - // otherwise we will be mapping something that is already mapped - DeviceSettings deviceSettings = Controlify.instance().config().getSettings() - .getOrCreateDeviceSettings(inputComponent.getController().uid()); - deviceSettings.mapping = null; + this.stages = stages.stream().map(MappingStage::new).toList(); + this.mappingBuilder.putDeadzoneGroups(deadzoneGroups); } - public static ControllerMappingMakerScreen createGamepadMapping(InputComponent inputComponent, Screen lastScreen) { - return new ControllerMappingMakerScreen(inputComponent, lastScreen, GAMEPAD_STAGES, GamepadInputs.DEADZONE_GROUPS); + public static ControllerMappingMakerScreen createGamepadMapping(ControllerImpl controller, Screen lastScreen) { + return new ControllerMappingMakerScreen(controller, lastScreen, GAMEPAD_STAGES, GamepadInputs.DEADZONE_GROUPS); } @Override protected void init() { addRenderableWidget( - goBackButton = Button.builder( + this.goBackButton = Button.builder( Component.translatable("controlify.gui.mapping_maker.go_back"), button -> goBackStage() ) @@ -106,80 +116,108 @@ protected void init() { .tooltip(Tooltip.create(Component.translatable("controlify.gui.mapping_maker.no_map.tooltip"))) .build() ); - goBackButton.active = false; + goBackButton.active = currentStage != null && stages.indexOf(currentStage) > 0; } @Override public void tick() { - if (delayTillNextStage >= 0) { - delayTillNextStage--; - if (delayTillNextStage == -1) { - if (currentStage == null) { - this.setStage(stages.get(0)); - } else { - int index = stages.indexOf(currentStage); - int nextIndex = index + 1; - if (nextIndex >= stages.size()) { - onClose(); - } else { - this.setStage(stages.get(index + 1)); - goBackButton.active = true; - } - } - } - } else { - if (currentStage == null) { - this.setStage(stages.get(0)); - } else if (currentStage.isSatisfied() && delayTillNextStage == -1) { - delayTillNextStage = 20; - } + if (delayTillNextStage < 0 || delayTillNextStage-- > 0) { + return; + } - if (currentStage != null && !currentStage.isSatisfied()) { - ControllerStateView stateNow = this.inputComponent.stateNow(); - ControllerStateView stateThen = this.inputComponent.stateThen(); + int nextIndex = currentStage == null ? 0 : stages.indexOf(currentStage) + 1; + if (nextIndex >= stages.size()) { + onClose(); + return; + } + this.setStage(stages.get(nextIndex)); + if (currentStage.isSatisfied()) { + // A later step may already be mapped when revisiting an earlier one. + delayTillNextStage = 20; + } + } - processStage(currentStage, stateNow, stateThen); - } + @Override + public void added() { + if (this.inputSubscription != null) { + this.inputSubscription.close(); } + // Capture original source IDs before mappings, deadzones, gestures, and domain expansion. + this.inputSubscription = this.controller.input().inputPipeline() + .sourceRawPhysicalSignals().subscribe(this::onPhysicalSignal); } - private void processStage(MappingStage stage, ControllerStateView stateNow, ControllerStateView stateThen) { - for (Identifier button : stateNow.getButtons()) { - boolean now = stateNow.isButtonDown(button); - boolean prev = stateThen.isButtonDown(button); - - if (now != prev) { - MappingEntry mapping = switch (stage.outputType()) { - case BUTTON -> new MappingEntry.FromButton.ToButton(button, stage.originInput(), !now); - case AXIS -> new MappingEntry.FromButton.ToAxis(button, stage.originInput(), prev ? 1 : 0, now ? 1 : 0); - }; - mappingBuilder.putMapping(mapping); - stage.setSatisfied(true); - return; - } + @Override + public void removed() { + if (this.inputSubscription != null) { + this.inputSubscription.close(); + this.inputSubscription = null; } + } - for (Identifier axis : stateNow.getAxes()) { - float now = stateNow.getAxisState(axis); - float prev = stateThen.getAxisState(axis); - float diff = prev - now; - if (Math.abs(diff) > 0.3f) { - MappingEntry mapping = switch (stage.outputType()) { - case BUTTON -> new MappingEntry.FromAxis.ToButton(axis, stage.originInput(), 0.5f); - case AXIS -> new MappingEntry.FromAxis.ToAxis(axis, stage.originInput(), 0, 0, 1, 1); - }; - mappingBuilder.putMapping(mapping); - stage.setSatisfied(true); + private void onPhysicalSignal(PhysicalSignal signal) { + Identifier input; + MapType inputType; + boolean pressed; + switch (signal) { + case PhysicalInputSignal.ButtonChanged button -> { + input = button.input(); + inputType = MapType.BUTTON; + pressed = button.state(); + } + case PhysicalInputSignal.AxisMoved axis -> { + input = axis.input(); + inputType = MapType.AXIS; + // Hysteresis prevents stick noise around the capture threshold from retriggering. + pressed = axis.value() >= (this.activeInputs.contains(input) + ? AXIS_RELEASE_THRESHOLD : AXIS_PRESS_THRESHOLD); + } + default -> { return; } } + + // Track releases during the cooldown too, so held inputs cannot satisfy the next step. + if (!pressed) { + this.activeInputs.remove(input); + return; + } + if (!this.activeInputs.add(input) || this.delayTillNextStage >= 0 + || this.currentStage == null || this.currentStage.isSatisfied()) { + return; + } + // MappingStage requires each physical source to have a single destination. + if (this.capturedMappings.values().stream().anyMatch(mapping -> mapping.sourceInput().equals(input))) { + return; + } + + Identifier output = this.currentStage.originInput(); + MappingEntry mapping = switch (inputType) { + case BUTTON -> switch (this.currentStage.outputType()) { + case BUTTON -> new MappingEntry.FromButton.ToButton(input, output, false); + case AXIS -> new MappingEntry.FromButton.ToAxis(input, output, 0, 1); + }; + case AXIS -> switch (this.currentStage.outputType()) { + case BUTTON -> new MappingEntry.FromAxis.ToButton(input, output, AXIS_PRESS_THRESHOLD); + case AXIS -> new MappingEntry.FromAxis.ToAxis(input, output, 0, 0, 1, 1); + }; + }; + this.capturedMappings.put(output, mapping); + this.currentStage.setSatisfied(true); + this.delayTillNextStage = 20; } private void setStage(MappingStage stage) { currentStage = stage; + goBackButton.active = stages.indexOf(stage) > 0; } private void mapAsNone() { + if (currentStage == null) { + return; + } + capturedMappings.remove(currentStage.originInput()); + currentStage.setSatisfied(false); delayTillNextStage = 0; } @@ -187,17 +225,24 @@ private void mapAsNone() { public void onClose() { MinecraftUtil.setScreen(lastScreen); DeviceSettings deviceSettings = Controlify.instance().config().getSettings() - .getOrCreateDeviceSettings(inputComponent.getController().uid()); + .getOrCreateDeviceSettings(controller.uid()); + capturedMappings.values().forEach(mappingBuilder::putMapping); deviceSettings.mapping = mappingBuilder.build(); + controller.input().reloadControllerMapping(); Controlify.instance().config().saveSafely(); } private void goBackStage() { + if (currentStage == null) { + return; + } int index = stages.indexOf(currentStage); int nextIndex = index - 1; if (nextIndex >= 0) { this.setStage(stages.get(index - 1)); currentStage.setSatisfied(false); + capturedMappings.remove(currentStage.originInput()); + delayTillNextStage = -1; } goBackButton.active = nextIndex > 0; } @@ -287,8 +332,17 @@ public MappingStage(Identifier originInput, MapType outputType, Component name, this.originInput = originInput; this.outputType = outputType; this.name = name; - this.foreground = CUtil.rl("textures/gui/controllerdiagram/" + foreground + ".png");; - this.background = CUtil.rl("textures/gui/controllerdiagram/" + foreground + ".png");; + this.foreground = CUtil.rl("textures/gui/controllerdiagram/" + foreground + ".png"); + this.background = CUtil.rl("textures/gui/controllerdiagram/" + background + ".png"); + } + + private MappingStage(MappingStage template) { + this.originInput = template.originInput; + this.outputType = template.outputType; + this.name = template.name; + this.foreground = template.foreground; + this.background = template.background; + this.satisfied = false; } public Identifier originInput() { @@ -327,7 +381,7 @@ public ScreenProcessorImpl(ControllerMappingMakerScreen screen) { @Override public void onControllerFixedUpdate(ControllerImpl controller) { - + // Captured physical inputs must not also activate screen controls. } } } diff --git a/src/main/java/dev/isxander/controlify/impl/gui/screen/GlobalSettingsScreenFactory.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/GlobalSettingsScreenFactory.java index 58385aff3..963dcf749 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/screen/GlobalSettingsScreenFactory.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/GlobalSettingsScreenFactory.java @@ -7,7 +7,6 @@ package dev.isxander.controlify.impl.gui.screen; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.api.ControlifyApi; import dev.isxander.controlify.impl.config.settings.GlobalSettings; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.driver.steamdeck.SteamDeckUtil; @@ -71,7 +70,7 @@ public static Screen createGlobalSettingsScreen(Screen parent) { .binding(GlobalSettings.defaults().allowServerRumble, () -> globalSettings.allowServerRumble, v -> globalSettings.allowServerRumble = v) .controller(TickBoxControllerBuilder::create) .addListener((opt, val) -> { - ControlifyApi.get().getCurrentController() + Controlify.instance().getCurrentController() .flatMap(ControllerImpl::rumble) .ifPresent(rumble -> rumble.rumbleManager().clearEffects()); }) diff --git a/src/main/java/dev/isxander/controlify/impl/gui/screen/ProfileSelectionScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/ProfileSelectionScreen.java index 86cfd07cb..3e40c66c0 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/screen/ProfileSelectionScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/ProfileSelectionScreen.java @@ -6,10 +6,10 @@ */ package dev.isxander.controlify.impl.gui.screen; -import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.api.gui.ButtonGuideApi; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.config.settings.device.DeviceSettings; import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; import dev.isxander.controlify.impl.controller.ControllerImpl; @@ -146,10 +146,10 @@ protected void init() { ); this.layout.addToFooter(footer); - ButtonGuideApi.addGuideToButton(this.rememberButton, ControlifyBindings.GUI_PRESS, ButtonGuidePredicate.always()); - ButtonGuideApi.addGuideToButton(this.onceButton, ControlifyBindings.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always()); - ButtonGuideApi.addGuideToButton(this.deleteButton, ControlifyBindings.GUI_ABSTRACT_ACTION_2, ButtonGuidePredicate.always()); - ButtonGuideApi.addGuideToButton(backButton, ControlifyBindings.GUI_BACK, ButtonGuidePredicate.always()); + ButtonGuideApi.addGuideToButton(this.rememberButton, ControlifyActions.GUI_PRESS, ButtonGuidePredicate.always()); + ButtonGuideApi.addGuideToButton(this.onceButton, ControlifyActions.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always()); + ButtonGuideApi.addGuideToButton(this.deleteButton, ControlifyActions.GUI_ABSTRACT_ACTION_2, ButtonGuidePredicate.always()); + ButtonGuideApi.addGuideToButton(backButton, ControlifyActions.GUI_BACK, ButtonGuidePredicate.always()); this.layout.visitWidgets(this::addRenderableWidget); this.repositionElements(); @@ -202,10 +202,10 @@ public void extractRenderState(@NonNull GuiGraphicsExtractor graphics, int mouse @Override public void onControllerInput(ControllerImpl controller) { if (this.selectedProfileIndex >= 0 - && ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).justPressed()) { + && ControlifyActions.GUI_ABSTRACT_ACTION_1.on(controller).pulse().consumePulse()) { this.switchProfile(this.selectedProfileIndex, false); } else if (this.deleteButton.active - && ControlifyBindings.GUI_ABSTRACT_ACTION_2.on(controller).justPressed()) { + && ControlifyActions.GUI_ABSTRACT_ACTION_2.on(controller).pulse().consumePulse()) { this.confirmDeleteProfile(); } } @@ -327,9 +327,7 @@ private ControllerDisplay controllerDisplay(ProfileSettings profile) { } ControllerImpl connected = Controlify.instance().getControllerManager() - .flatMap(manager -> manager.getConnectedControllers().stream() - .filter(controller -> profile.controllerUid.equals(controller.uid())) - .findFirst()) + .flatMap(manager -> manager.getControllerByUid(profile.controllerUid)) .orElse(null); if (connected != null) { return new ControllerDisplay( diff --git a/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialItems.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialItems.java index 2f3ee3e06..1d70c83e9 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialItems.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialItems.java @@ -6,11 +6,11 @@ */ package dev.isxander.controlify.impl.gui.screen; +import dev.isxander.controlify.api.action.Action; +import dev.isxander.controlify.api.action.ActionAccessor; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.impl.api.bind.InputBinding; -import dev.isxander.controlify.impl.api.bind.InputBindingSupplier; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.action.radial.RadialIcon; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; import dev.isxander.controlify.impl.action.radial.RadialIconManager; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.utils.CUtil; @@ -33,19 +33,19 @@ public final class RadialItems { public static final RadialMenuScreen.RadialItem EMPTY_ACTION = new RadialItemRecord(Component.empty(), RadialIcon.EMPTY, () -> false, RadialIconManager.EMPTY); - public static RadialMenuScreen.RadialItem[] createBindings(ControllerImpl controller) { + public static RadialMenuScreen.RadialItem[] createActions(ControllerImpl controller) { List actions = controller.settings().input.radialMenu.radialActions; RadialMenuScreen.RadialItem[] items = new RadialMenuScreen.RadialItem[actions.size()]; for (int i = 0; i < actions.size(); i++) { - items[i] = getItemForBinding(actions.get(i), controller); + items[i] = getItemForAction(actions.get(i), controller); } return items; } - public static List createBindingPages(ControllerImpl controller) { - RadialMenuScreen.RadialItem[] items = createBindings(controller); + public static List createActionPages(ControllerImpl controller) { + RadialMenuScreen.RadialItem[] items = createActions(controller); return RadialMenuPages.partition(Arrays.asList(items)).stream() .map(page -> page.toArray(RadialMenuScreen.RadialItem[]::new)) .toList(); @@ -60,7 +60,7 @@ public static RadialPage createGameModes() { items[3] = new GameModeItem(GameType.SPECTATOR); return new RadialPage( - Component.translatable("controlify.binding.controlify.game_mode_switcher"), + Component.translatable("controlify.action.controlify.game_mode_switcher"), items ); } @@ -136,20 +136,20 @@ public static RadialMenuScreen.RadialItem[] createHotbarItemSelect() { public static RadialMenuScreen.RadialItem[] createDebug(ControllerImpl controller) { return List.of( - ControlifyBindings.TOGGLE_DEBUG_MENU, - ControlifyBindings.TOGGLE_DEBUG_MENU_FPS, - ControlifyBindings.TOGGLE_DEBUG_MENU_NET, - ControlifyBindings.TOGGLE_DEBUG_MENU_PROF, - ControlifyBindings.DEBUG_RELOAD_CHUNKS, - ControlifyBindings.DEBUG_TOGGLE_CHUNK_BORDERS, - ControlifyBindings.DEBUG_TOGGLE_ADVANCED_TOOLTIPS, - ControlifyBindings.DEBUG_TOGGLE_ENTITY_HITBOXES, - ControlifyBindings.DEBUG_RELOAD_RESOURCE_PACKS, - ControlifyBindings.DEBUG_CLEAR_CHAT, - ControlifyBindings.DEBUG_START_STOP_PROFILING + ControlifyActions.TOGGLE_DEBUG_MENU, + ControlifyActions.TOGGLE_DEBUG_MENU_FPS, + ControlifyActions.TOGGLE_DEBUG_MENU_NET, + ControlifyActions.TOGGLE_DEBUG_MENU_PROF, + ControlifyActions.DEBUG_RELOAD_CHUNKS, + ControlifyActions.DEBUG_TOGGLE_CHUNK_BORDERS, + ControlifyActions.DEBUG_TOGGLE_ADVANCED_TOOLTIPS, + ControlifyActions.DEBUG_TOGGLE_ENTITY_HITBOXES, + ControlifyActions.DEBUG_RELOAD_RESOURCE_PACKS, + ControlifyActions.DEBUG_CLEAR_CHAT, + ControlifyActions.DEBUG_START_STOP_PROFILING ).stream() - .map(InputBindingSupplier::bindId) - .map(id -> getItemForBinding(id, controller)) + .map(ActionAccessor::actionId) + .map(id -> getItemForAction(id, controller)) .toArray(RadialMenuScreen.RadialItem[]::new); } @@ -180,20 +180,24 @@ private static RadialIcon getIconForItem(ItemStack stack) { return model != null ? RadialIcon.model(model) : RadialIcon.EMPTY; } - private static RadialMenuScreen.RadialItem getItemForBinding(Identifier id, ControllerImpl controller) { - InputBinding binding = controller.input().orElseThrow().getBinding(id); + private static RadialMenuScreen.RadialItem getItemForAction(Identifier id, ControllerImpl controller) { + var action = controller.input().getAction(id); RadialIconManager radialIconManager = Controlify.instance().radialIconManager(); - if (binding == null || !radialIconManager.isRadialCandidate(binding)) { - CUtil.LOGGER.warn("Binding {} does not exist or is not a radial candidate", binding); + if (action == null || !radialIconManager.isRadialCandidate(action.spec().id())) { + CUtil.LOGGER.warn("Action {} does not exist or is not a radial candidate", action); return EMPTY_ACTION; } return new RadialItemRecord( - binding.name(), - radialIconManager.getIcon(binding), + action.spec().name(), + radialIconManager.getIcon(action.spec().id()), () -> { - binding.fakePress(); + var currentAction = controller.input().getAction(id); + if (currentAction == null) { + return false; + } + currentAction.state().firePulse(); return true; }, id @@ -211,7 +215,9 @@ public boolean playAction() { @Override public boolean equals(Object obj) { - if (this == obj) return true; + if (this == obj) { + return true; + } if (obj instanceof RadialItemRecord record) { return id.equals(record.id); } diff --git a/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuEditScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuEditScreen.java index dcbe5340f..a9b9713d0 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuEditScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuEditScreen.java @@ -7,12 +7,12 @@ package dev.isxander.controlify.impl.gui.screen; import com.mojang.blaze3d.platform.InputConstants; -import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.impl.api.bind.InputBinding; -import dev.isxander.controlify.impl.action.radial.RadialIcon; +import dev.isxander.controlify.api.action.Action; import dev.isxander.controlify.api.gui.ButtonGuideApi; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.action.ControlifyActions; +import dev.isxander.controlify.impl.action.radial.RadialIcon; import dev.isxander.controlify.impl.action.radial.RadialIconExtractor; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.controller.haptic.HapticEffects; @@ -32,8 +32,8 @@ import net.minecraft.client.gui.layouts.Layout; import net.minecraft.client.gui.layouts.LayoutSettings; import net.minecraft.client.gui.layouts.LinearLayout; -import net.minecraft.client.gui.narration.NarrationElementOutput; import net.minecraft.client.gui.narration.NarratedElementType; +import net.minecraft.client.gui.narration.NarrationElementOutput; import net.minecraft.client.gui.navigation.ScreenDirection; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.input.InputWithModifiers; @@ -63,8 +63,8 @@ public class RadialMenuEditScreen extends Screen implements ScreenProcessorProvi private final Screen parent; private final ControllerImpl controller; private final RadialMenuEditModel model; - private final List candidates; - private final Map candidatesById; + private final List candidates; + private final Map candidatesById; private final Processor processor = new Processor(this); private HeaderAndFooterLayout layout; @@ -80,11 +80,13 @@ public RadialMenuEditScreen(Screen parent, ControllerImpl controller) { this.parent = parent; this.controller = controller; this.model = new RadialMenuEditModel(controller.settings().input.radialMenu.radialActions); - this.candidates = controller.input().orElseThrow().getAllBindings().stream() - .filter(Controlify.instance().radialIconManager()::isRadialCandidate) + this.candidates = controller.input().getAllActions().stream() + .map(action -> action) + .filter(action -> action.spec().shouldExpose().test(controller)) + .filter(action -> Controlify.instance().radialIconManager().isRadialCandidate(action.spec().id())) .toList(); this.candidatesById = candidates.stream().collect(Collectors.toMap( - InputBinding::id, + a -> a.spec().id(), Function.identity(), (first, ignored) -> first, LinkedHashMap::new @@ -114,7 +116,7 @@ protected void init() { Button done = footer.addChild(Button.builder(CommonComponents.GUI_DONE, _ -> commitAndClose()) .width(FOOTER_BUTTON_WIDTH) .build()); - ButtonGuideApi.addGuideToButton(done, ControlifyBindings.GUI_BACK, ButtonGuidePredicate.always()); + ButtonGuideApi.addGuideToButton(done, ControlifyActions.GUI_BACK, ButtonGuidePredicate.always()); layout.addToFooter(footer); layout.visitWidgets(this::addRenderableWidget); @@ -123,7 +125,7 @@ protected void init() { private LinearLayout createAvailableColumn(int width) { LinearLayout column = LinearLayout.vertical().spacing(4); - long availableCount = candidates.stream().filter(binding -> !model.actions().contains(binding.id())).count(); + long availableCount = candidates.stream().filter(action -> !model.actions().contains(action.spec().id())).count(); column.addChild(new ColumnHeader( width, Component.translatable("controlify.radial_menu.editor.available"), @@ -141,14 +143,14 @@ private LinearLayout createAvailableColumn(int width) { )); } - Map> grouped = candidates.stream() - .filter(binding -> !model.actions().contains(binding.id())) - .collect(Collectors.groupingBy(InputBinding::category, LinkedHashMap::new, Collectors.toList())); + Map> grouped = candidates.stream() + .filter(action -> !model.actions().contains(action.spec().id())) + .collect(Collectors.groupingBy(a -> a.spec().category(), LinkedHashMap::new, Collectors.toList())); grouped.forEach((category, bindings) -> { content.addChild(new SectionDivider(entryWidth, category, false)); - for (InputBinding binding : bindings) { - ActionEntry entry = new ActionEntry(binding.id(), binding.name(), false, -1, entryWidth); - availableEntries.put(binding.id(), entry); + for (Action action : bindings) { + ActionEntry entry = new ActionEntry(action.spec().id(), action.spec().name(), false, -1, entryWidth); + availableEntries.put(action.spec().id(), entry); content.addChild(entry); } }); @@ -161,7 +163,9 @@ private LinearLayout createAvailableColumn(int width) { ScrollableLayout scrollable = createScrollable(padded(content), width); scrollable.visitWidgets(widget -> { - if (widget instanceof ContainerEventHandler container) availableContainer = container; + if (widget instanceof ContainerEventHandler container) { + availableContainer = container; + } }); column.addChild(scrollable); return column; @@ -189,10 +193,10 @@ private LinearLayout createEquippedColumn(int width) { } Identifier id = actions.get(index); - InputBinding binding = candidatesById.get(id); - Component name = binding == null + Action action = candidatesById.get(id); + Component name = action == null ? Component.translatable("controlify.radial_menu.editor.missing", id.toString()).withStyle(ChatFormatting.RED) - : binding.name(); + : action.spec().name(); ActionEntry entry = new ActionEntry(id, name, true, index, entryWidth); equippedEntries.put(id, entry); content.addChild(entry); @@ -215,7 +219,9 @@ private LinearLayout createEquippedColumn(int width) { ScrollableLayout scrollable = createScrollable(padded(content), width); scrollable.visitWidgets(widget -> { - if (widget instanceof ContainerEventHandler container) equippedContainer = container; + if (widget instanceof ContainerEventHandler container) { + equippedContainer = container; + } }); column.addChild(scrollable); return column; @@ -250,7 +256,9 @@ private ScrollableLayout createScrollable(Layout content, int width) { private void activate(Identifier id, boolean equipped) { if (!model.isCarrying()) { model.pickUp(id); - if (equipped) equippedInsertionIndex = model.actions().indexOf(id); + if (equipped) { + equippedInsertionIndex = model.actions().indexOf(id); + } focusAfterRebuild = id; rebuildWidgets(); } else if (Objects.equals(id, model.carried())) { @@ -277,7 +285,9 @@ private void activate(Identifier id, boolean equipped) { private boolean moveCarried(ScreenDirection direction) { Identifier carried = model.carried(); - if (carried == null) return false; + if (carried == null) { + return false; + } boolean changed = switch (direction) { case LEFT -> { @@ -293,7 +303,9 @@ private boolean moveCarried(ScreenDirection direction) { }; if (changed) { - if (model.carriedIsEquipped()) equippedInsertionIndex = model.actions().indexOf(carried); + if (model.carriedIsEquipped()) { + equippedInsertionIndex = model.actions().indexOf(carried); + } focusAfterRebuild = carried; rebuildWidgets(); } @@ -424,7 +436,9 @@ public ScreenProcessor screenProcessor() { private Component trim(Component text, int maxWidth) { String value = text.getString(); - if (font.width(value) <= maxWidth) return text; + if (font.width(value) <= maxWidth) { + return text; + } int ellipsisWidth = font.width("..."); return Component.literal(font.plainSubstrByWidth(value, Math.max(0, maxWidth - ellipsisWidth)) + "..."); @@ -544,10 +558,10 @@ private ActionEntry(Identifier id, Component name, boolean equipped, int positio this.equipped = equipped; this.position = position; - InputBinding binding = candidatesById.get(id); - this.icon = binding == null + Action action = candidatesById.get(id); + this.icon = action == null ? RadialIcon.EMPTY - : Controlify.instance().radialIconManager().getIcon(binding); + : Controlify.instance().radialIconManager().getIcon(action.spec().id()); } @Override @@ -647,13 +661,13 @@ protected void handleComponentNavigation(ControllerImpl controller) { } boolean moved = false; - if (ControlifyBindings.GUI_NAVI_LEFT.on(controller).justPressed()) { + if (ControlifyActions.GUI_NAVI_LEFT.on(controller).pulse().consumePulse()) { moved = screen.moveCarried(ScreenDirection.LEFT); - } else if (ControlifyBindings.GUI_NAVI_RIGHT.on(controller).justPressed()) { + } else if (ControlifyActions.GUI_NAVI_RIGHT.on(controller).pulse().consumePulse()) { moved = screen.moveCarried(ScreenDirection.RIGHT); - } else if (ControlifyBindings.GUI_NAVI_UP.on(controller).justPressed()) { + } else if (ControlifyActions.GUI_NAVI_UP.on(controller).pulse().consumePulse()) { moved = screen.moveCarried(ScreenDirection.UP); - } else if (ControlifyBindings.GUI_NAVI_DOWN.on(controller).justPressed()) { + } else if (ControlifyActions.GUI_NAVI_DOWN.on(controller).pulse().consumePulse()) { moved = screen.moveCarried(ScreenDirection.DOWN); } @@ -666,7 +680,7 @@ protected void handleComponentNavigation(ControllerImpl controller) { @Override protected void handleButtons(ControllerImpl controller) { if (screen.model.isCarrying()) { - if (ControlifyBindings.GUI_PRESS.on(controller).guiPressed().get()) { + if (ControlifyActions.GUI_PRESS.on(controller).pulse().consumePulse()) { screen.dropCarried(); playClackSound(); return; diff --git a/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuScreen.java index 2df14d4f8..3d52618ed 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuScreen.java @@ -6,9 +6,10 @@ */ package dev.isxander.controlify.impl.gui.screen; -import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.api.action.ActionAccessor; +import dev.isxander.controlify.api.font.ActionGlyphs; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.action.radial.RadialIcon; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; import dev.isxander.controlify.impl.action.radial.RadialIconExtractor; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.controller.haptic.HapticEffects; @@ -51,7 +52,7 @@ public class RadialMenuScreen extends Screen implements ScreenControllerEventLis private final Component footerText; private final List userPages; private final List bonusPages; - private final @Nullable InputBinding openBind; + private final @Nullable ActionAccessor openAction; private RadialButton[] buttons = new RadialButton[0]; private float radialRadius; @@ -65,20 +66,20 @@ public class RadialMenuScreen extends Screen implements ScreenControllerEventLis /** * Creates a single-page radial menu. Existing special-purpose radial menus use this path. */ - public RadialMenuScreen(ControllerImpl controller, InputBinding openBind, RadialItem[] items, Component text, @Nullable Screen parent) { - this(controller, openBind, Collections.singletonList(items), List.of(), text, parent); + public RadialMenuScreen(ControllerImpl controller, ActionAccessor openAction, RadialItem[] items, Component text, @Nullable Screen parent) { + this(controller, openAction, Collections.singletonList(items), List.of(), text, parent); } /** * Creates a paged radial menu. Page boundaries are a presentation concern and are not persisted. */ - public RadialMenuScreen(ControllerImpl controller, @Nullable InputBinding openBind, List pages, Component text, @Nullable Screen parent) { - this(controller, openBind, pages, List.of(), text, parent); + public RadialMenuScreen(ControllerImpl controller, @Nullable ActionAccessor openAction, List pages, Component text, @Nullable Screen parent) { + this(controller, openAction, pages, List.of(), text, parent); } public RadialMenuScreen( ControllerImpl controller, - @Nullable InputBinding openBind, + @Nullable ActionAccessor openAction, List userPages, List bonusPages, Component text, @@ -95,8 +96,8 @@ public RadialMenuScreen( .toList(); this.pageIndex = this.bonusPages.size(); this.parent = parent; - this.idleTicksTimeout = controller.input().orElseThrow().settings().radialMenu.radialButtonFocusTimeoutTicks; - this.openBind = openBind; + this.idleTicksTimeout = controller.input().settings().radialMenu.radialButtonFocusTimeoutTicks; + this.openAction = openAction; } @Override @@ -138,7 +139,9 @@ private void buildCurrentPage() { } private void changePage(int direction) { - if (pageCount() <= 1) return; + if (pageCount() <= 1) { + return; + } pageIndex = Math.floorMod(pageIndex + direction, pageCount()); buildCurrentPage(); @@ -148,14 +151,18 @@ private void changePage(int direction) { @Override public void onControllerInput(ControllerImpl controller) { - if (this.controller != controller) return; + if (this.controller != controller) { + return; + } - if (openBind == null) { - if (ControlifyBindings.GUI_BACK.on(controller).justPressed()) onClose(); + if (openAction == null) { + if (ControlifyActions.GUI_BACK.on(controller).pulse().consumePulse()) { + onClose(); + } return; } - if (!openBind.digitalNow()) { + if (!openAction.on(controller).latch().isLatchActive()) { if (selectedButton >= 0 && selectedButton < buttons.length && buttons[selectedButton].invoke()) { playClickSound(); } @@ -163,13 +170,17 @@ public void onControllerInput(ControllerImpl controller) { return; } - if (buttons.length == 0) return; + if (buttons.length == 0) { + + return; + + } - float x = ControlifyBindings.RADIAL_AXIS_RIGHT.on(controller).analogueNow() - - ControlifyBindings.RADIAL_AXIS_LEFT.on(controller).analogueNow(); - float y = ControlifyBindings.RADIAL_AXIS_DOWN.on(controller).analogueNow() - - ControlifyBindings.RADIAL_AXIS_UP.on(controller).analogueNow(); - float threshold = controller.input().orElseThrow().settings().buttonActivationThreshold; + float x = ControlifyActions.RADIAL_AXIS_RIGHT.on(controller).continuous().getContinuous() + - ControlifyActions.RADIAL_AXIS_LEFT.on(controller).continuous().getContinuous(); + float y = ControlifyActions.RADIAL_AXIS_DOWN.on(controller).continuous().getContinuous() + - ControlifyActions.RADIAL_AXIS_UP.on(controller).continuous().getContinuous(); + float threshold = controller.input().settings().buttonActivationThreshold; if (Math.abs(x) >= threshold || Math.abs(y) >= threshold) { float angle = Mth.wrapDegrees(Mth.RAD_TO_DEG * (float)Mth.atan2(y, x) - 90f) + 180f; @@ -185,7 +196,9 @@ public void onControllerInput(ControllerImpl controller) { for (int i = 0; i < buttons.length; i++) { boolean selected = i == selectedButton; buttons[i].setFocused(selected); - if (selected) setFocused(buttons[i]); + if (selected) { + setFocused(buttons[i]); + } } idleTicks = 0; } else { @@ -212,8 +225,8 @@ public void extractRenderState(@NonNull GuiGraphicsExtractor graphics, int mouse graphics.centeredText(font, page, width / 2, height - 25, -1); renderPageDots(graphics); - Component previous = ControlifyBindings.GUI_PREV_TAB.on(controller).inputGlyph(); - Component next = ControlifyBindings.GUI_NEXT_TAB.on(controller).inputGlyph(); + Component previous = ActionGlyphs.action(ControlifyActions.GUI_PREV_TAB.actionId()); + Component next = ActionGlyphs.action(ControlifyActions.GUI_NEXT_TAB.actionId()); int pageWidth = font.width(page); graphics.text(font, previous, width / 2 - pageWidth / 2 - font.width(previous) - 8, height - 25, -1); graphics.text(font, next, width / 2 + pageWidth / 2 + 8, height - 25, -1); @@ -221,13 +234,17 @@ public void extractRenderState(@NonNull GuiGraphicsExtractor graphics, int mouse } private RadialItem[] getCurrentPageItems() { - if (pageIndex < bonusPages.size()) return bonusPages.get(pageIndex).items(); + if (pageIndex < bonusPages.size()) { + return bonusPages.get(pageIndex).items(); + } return userPages.get(pageIndex - bonusPages.size()); } private Component getPageTitle() { - if (pageIndex < bonusPages.size()) return bonusPages.get(pageIndex).name(); + if (pageIndex < bonusPages.size()) { + return bonusPages.get(pageIndex).name(); + } return Component.translatable( "controlify.radial_menu.page", @@ -365,9 +382,9 @@ public Processor(RadialMenuScreen screen) { @Override protected void handleTabNavigation(ControllerImpl controller) { - if (ControlifyBindings.GUI_NEXT_TAB.on(controller).justPressed()) { + if (ControlifyActions.GUI_NEXT_TAB.on(controller).pulse().consumePulse()) { screen.changePage(1); - } else if (ControlifyBindings.GUI_PREV_TAB.on(controller).justPressed()) { + } else if (ControlifyActions.GUI_PREV_TAB.on(controller).pulse().consumePulse()) { screen.changePage(-1); } } diff --git a/src/main/java/dev/isxander/controlify/impl/ingame/ControllerPlayerMovement.java b/src/main/java/dev/isxander/controlify/impl/ingame/ControllerPlayerMovement.java index 4ab5c12a6..26fa61961 100644 --- a/src/main/java/dev/isxander/controlify/impl/ingame/ControllerPlayerMovement.java +++ b/src/main/java/dev/isxander/controlify/impl/ingame/ControllerPlayerMovement.java @@ -6,18 +6,18 @@ */ package dev.isxander.controlify.impl.ingame; +import dev.isxander.controlify.api.action.Action; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; -import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.client.Minecraft; +import net.minecraft.client.player.ClientInput; import net.minecraft.client.player.KeyboardInput; import net.minecraft.client.player.LocalPlayer; +import net.minecraft.world.entity.player.Input; import net.minecraft.world.phys.Vec2; import org.jetbrains.annotations.Nullable; -import net.minecraft.client.player.ClientInput; -import net.minecraft.world.entity.player.Input; public class ControllerPlayerMovement extends ClientInput { private final ControllerImpl controller; @@ -39,13 +39,13 @@ public void tick() { return; } - float forwardImpulse = ControlifyBindings.WALK_FORWARD.on(controller).analogueNow() - - ControlifyBindings.WALK_BACKWARD.on(controller).analogueNow(); - float leftImpulse = ControlifyBindings.WALK_LEFT.on(controller).analogueNow() - - ControlifyBindings.WALK_RIGHT.on(controller).analogueNow(); + float forwardImpulse = ControlifyActions.WALK_FORWARD.on(controller).continuous().getContinuous() + - ControlifyActions.WALK_BACKWARD.on(controller).continuous().getContinuous(); + float leftImpulse = ControlifyActions.WALK_LEFT.on(controller).continuous().getContinuous() + - ControlifyActions.WALK_RIGHT.on(controller).continuous().getContinuous(); if (Controlify.instance().config().getSettings().globalSettings().shouldUseKeyboardMovement()) { - float threshold = controller.input().orElseThrow().settings().buttonActivationThreshold; + float threshold = controller.input().settings().buttonActivationThreshold; forwardImpulse = Math.abs(forwardImpulse) >= threshold ? Math.copySign(1, forwardImpulse) : 0; leftImpulse = Math.abs(leftImpulse) >= threshold ? Math.copySign(1, leftImpulse) : 0; @@ -62,20 +62,19 @@ public void tick() { this.setMoveVec(forwardImpulse, leftImpulse); // this over-complication is so exiting a GUI with the button still held doesn't trigger a jump. - InputBinding jump = ControlifyBindings.JUMP.on(controller); - if (jump.justPressed()) - jumping = true; - if (!jump.digitalNow()) - jumping = false; + Action jump = ControlifyActions.JUMP.on(controller); + jumping = jump.pulse().consumePulse() || jump.latch().isLatchActive(); - InputBinding sneak = ControlifyBindings.SNEAK.on(controller); + Action sneak = ControlifyActions.SNEAK.on(controller); if (player.getAbilities().flying || (player.isInWater() && !player.onGround()) || player.getVehicle() != null || !controller.settings().generic.toggleSneak) { - if (sneak.justPressed()) + if (sneak.pulse().consumePulse()) { shiftKeyDown = true; - if (!sneak.digitalNow()) + } + if (!sneak.latch().isLatchActive()) { shiftKeyDown = false; + } } else { - if (sneak.justPressed()) { + if (sneak.pulse().consumePulse()) { shiftKeyDown = !shiftKeyDown; } } @@ -83,7 +82,7 @@ public void tick() { shiftKeyDown = false; } - boolean sprinting = ControlifyBindings.SPRINT.on(controller).digitalNow(); + boolean sprinting = ControlifyActions.SPRINT.on(controller).latch().isLatchActive(); this.keyPresses = new Input(up, down, left, right, jumping, shiftKeyDown, sprinting); @@ -108,8 +107,9 @@ private void setMoveVec(float forward, float left) { } public static void updatePlayerInput(@Nullable LocalPlayer player) { - if (player == null) + if (player == null) { return; + } if (shouldBeControllerInput()) { player.input = new DualInput( @@ -122,8 +122,9 @@ public static void updatePlayerInput(@Nullable LocalPlayer player) { } public static void ensureCorrectInput(@Nullable LocalPlayer player) { - if (player == null) + if (player == null) { return; + } if (shouldBeControllerInput() && player.input.getClass() == KeyboardInput.class) { updatePlayerInput(player); diff --git a/src/main/java/dev/isxander/controlify/impl/ingame/InGameInputHandler.java b/src/main/java/dev/isxander/controlify/impl/ingame/InGameInputHandler.java index 5997bbf52..c8a3df8a3 100644 --- a/src/main/java/dev/isxander/controlify/impl/ingame/InGameInputHandler.java +++ b/src/main/java/dev/isxander/controlify/impl/ingame/InGameInputHandler.java @@ -6,29 +6,27 @@ */ package dev.isxander.controlify.impl.ingame; +import dev.isxander.controlify.api.ingame.LookInputModifier; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.impl.api.ingameinput.LookInputModifier; -import dev.isxander.controlify.api.ControlifyEvents; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.config.settings.profile.GyroSettings; import dev.isxander.controlify.impl.config.settings.profile.InputSettings; -import dev.isxander.controlify.impl.controller.gyro.GyroState; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.controller.gyro.GyroButtonMode; -import dev.isxander.controlify.impl.controller.gyro.GyroComponent; -import dev.isxander.controlify.impl.controller.input.InputComponent; +import dev.isxander.controlify.impl.controller.gyro.GyroComponentImpl; +import dev.isxander.controlify.impl.controller.input.InputComponentImpl; import dev.isxander.controlify.impl.driver.steamdeck.SteamDeckDriver; -import dev.isxander.controlify.impl.gui.screen.RadialItems; import dev.isxander.controlify.impl.gui.screen.RadialItems.RadialPage; +import dev.isxander.controlify.impl.gui.screen.RadialItems; import dev.isxander.controlify.impl.gui.screen.RadialMenuScreen; -import dev.isxander.controlify.mixins.feature.steamdeck.ScreenshotAccessor; import dev.isxander.controlify.impl.server.ServerPolicies; import dev.isxander.controlify.impl.utils.ControllerUtils; import dev.isxander.controlify.impl.utils.DebugOverlayHelper; -import dev.isxander.controlify.impl.utils.HoldRepeatHelper; import dev.isxander.controlify.impl.utils.MinecraftUtil; import dev.isxander.controlify.impl.utils.animation.api.Animation; import dev.isxander.controlify.impl.utils.animation.api.EasingFunction; +import dev.isxander.controlify.impl.utils.event.EventBus; +import dev.isxander.controlify.mixins.feature.steamdeck.ScreenshotAccessor; import net.minecraft.client.CameraType; import net.minecraft.client.Minecraft; import net.minecraft.client.Screenshot; @@ -47,38 +45,37 @@ import net.minecraft.world.phys.Vec3; import org.joml.Vector2d; import org.joml.Vector2f; +import org.joml.Vector3f; import java.io.File; -import java.util.Objects; import java.util.ArrayList; import java.util.List; +import java.util.Objects; public class InGameInputHandler { + public static final class Events { + public static final EventBus LOOK_INPUT_MODIFIER = new EventBus<>(); + + private Events() { + } + } + private final ControllerImpl controller; private final Controlify controlify; private final Minecraft minecraft; private double lookInputX, lookInputY; // in degrees per tick - private final GyroState gyroInput = new GyroState(); + private final Vector3f gyroInput = new Vector3f(); private boolean gyroToggledOn; private boolean wasAiming; private Animation flickAnimation; private boolean shouldShowPlayerList; - private final HoldRepeatHelper dropRepeatHelper; - private boolean dropRepeating; - - private final HoldRepeatHelper hotbarNextRepeatHelper; - private final HoldRepeatHelper hotbarPrevRepeatHelper; - public InGameInputHandler(ControllerImpl controller) { this.controller = controller; this.minecraft = Minecraft.getInstance(); this.controlify = Controlify.instance(); - this.dropRepeatHelper = new HoldRepeatHelper(20, 1); - this.hotbarNextRepeatHelper = new HoldRepeatHelper(10, 4); - this.hotbarPrevRepeatHelper = new HoldRepeatHelper(10, 4); this.gyroToggledOn = false; } @@ -95,66 +92,53 @@ public void fixedUpdate() { } protected void handleKeybinds() { - if (MinecraftUtil.getScreen() != null) + if (MinecraftUtil.getScreen() != null) { return; + } - if (ControlifyBindings.PAUSE.on(controller).justPressed()) { + if (ControlifyActions.PAUSE.on(controller).pulse().consumePulse()) { minecraft.pauseGame(false); } if (minecraft.player != null) { Inventory inventory = minecraft.player.getInventory(); - if (hotbarNextRepeatHelper.shouldAction(ControlifyBindings.NEXT_SLOT.on(controller))) { - hotbarNextRepeatHelper.onNavigate(); - + if (ControlifyActions.NEXT_SLOT.on(controller).pulse().consumePulse()) { inventory.setSelectedSlot((inventory.getSelectedSlot() + 1) % Inventory.getSelectionSize()); } - if (hotbarPrevRepeatHelper.shouldAction(ControlifyBindings.PREV_SLOT.on(controller))) { - hotbarPrevRepeatHelper.onNavigate(); - + if (ControlifyActions.PREV_SLOT.on(controller).pulse().consumePulse()) { inventory.setSelectedSlot((inventory.getSelectedSlot() - 1 + Inventory.getSelectionSize()) % Inventory.getSelectionSize()); } var hotbarSlots = List.of( - ControlifyBindings.HOTBAR_SLOT_1, - ControlifyBindings.HOTBAR_SLOT_2, - ControlifyBindings.HOTBAR_SLOT_3, - ControlifyBindings.HOTBAR_SLOT_4, - ControlifyBindings.HOTBAR_SLOT_5, - ControlifyBindings.HOTBAR_SLOT_6, - ControlifyBindings.HOTBAR_SLOT_7, - ControlifyBindings.HOTBAR_SLOT_8, - ControlifyBindings.HOTBAR_SLOT_9 + ControlifyActions.HOTBAR_SLOT_1, + ControlifyActions.HOTBAR_SLOT_2, + ControlifyActions.HOTBAR_SLOT_3, + ControlifyActions.HOTBAR_SLOT_4, + ControlifyActions.HOTBAR_SLOT_5, + ControlifyActions.HOTBAR_SLOT_6, + ControlifyActions.HOTBAR_SLOT_7, + ControlifyActions.HOTBAR_SLOT_8, + ControlifyActions.HOTBAR_SLOT_9 ); for (int i = 0; i < hotbarSlots.size(); i++) { - if (hotbarSlots.get(i).on(controller).justPressed()) { + if (hotbarSlots.get(i).on(controller).pulse().consumePulse()) { inventory.setSelectedSlot(i); } } if (!minecraft.player.isSpectator()) { - if (ControlifyBindings.DROP_STACK.on(controller).justPressed()) { + if (ControlifyActions.DROP_STACK.on(controller).pulse().consumePulse()) { dropItem(minecraft.player, true); - } else { - if (ControlifyBindings.DROP_INGAME.on(controller).justPressed()) { - dropRepeating = true; - } else if (ControlifyBindings.DROP_INGAME.on(controller).justReleased()) { - dropRepeating = false; - } - - if (dropRepeating && dropRepeatHelper.shouldAction(ControlifyBindings.DROP_INGAME.on(controller))) { - if (dropItem(minecraft.player, false)) { - dropRepeatHelper.onNavigate(); - } - } + } else if (ControlifyActions.DROP_INGAME.on(controller).pulse().consumePulse()) { + dropItem(minecraft.player, false); } - if (ControlifyBindings.SWAP_HANDS.on(controller).justPressed()) { + if (ControlifyActions.SWAP_HANDS.on(controller).pulse().consumePulse()) { minecraft.player.connection.send(new ServerboundPlayerActionPacket(ServerboundPlayerActionPacket.Action.SWAP_ITEM_WITH_OFFHAND, BlockPos.ZERO, Direction.DOWN)); } } - if (ControlifyBindings.INVENTORY.on(controller).justPressed()) { + if (ControlifyActions.INVENTORY.on(controller).pulse().consumePulse()) { if (minecraft.gameMode.isServerControlledInventory()) { minecraft.player.sendOpenInventory(); } else { @@ -163,7 +147,7 @@ protected void handleKeybinds() { } } - if (ControlifyBindings.CHANGE_PERSPECTIVE.on(controller).justPressed()) { + if (ControlifyActions.CHANGE_PERSPECTIVE.on(controller).pulse().consumePulse()) { CameraType cameraType = minecraft.options.getCameraType(); minecraft.options.setCameraType(minecraft.options.getCameraType().cycle()); if (cameraType.isFirstPerson() != minecraft.options.getCameraType().isFirstPerson()) { @@ -174,7 +158,7 @@ protected void handleKeybinds() { //minecraft.levelRenderer.needsUpdate(); } } - if (ControlifyBindings.TOGGLE_HUD_VISIBILITY.on(controller).justPressed()) { + if (ControlifyActions.TOGGLE_HUD_VISIBILITY.on(controller).pulse().consumePulse()) { //? if >=26.2 { minecraft.gui.hud.toggle(); //?} else { @@ -182,55 +166,54 @@ protected void handleKeybinds() { *///?} } - if (ControlifyBindings.SHOW_PLAYER_LIST.on(controller).justPressed()) { + if (ControlifyActions.SHOW_PLAYER_LIST.on(controller).pulse().consumePulse()) { shouldShowPlayerList = !shouldShowPlayerList; } - - if (ControlifyBindings.TOGGLE_DEBUG_MENU.on(controller).justPressed()) { + if (ControlifyActions.TOGGLE_DEBUG_MENU.on(controller).pulse().consumePulse()) { DebugOverlayHelper.toggleOverlay(); } - if (ControlifyBindings.TOGGLE_DEBUG_MENU_FPS.on(controller).justPressed()) { + if (ControlifyActions.TOGGLE_DEBUG_MENU_FPS.on(controller).pulse().consumePulse()) { DebugOverlayHelper.toggleFpsOverlay(); } - if (ControlifyBindings.TOGGLE_DEBUG_MENU_NET.on(controller).justPressed()) { + if (ControlifyActions.TOGGLE_DEBUG_MENU_NET.on(controller).pulse().consumePulse()) { DebugOverlayHelper.toggleNetworkOverlay(); } - if (ControlifyBindings.TOGGLE_DEBUG_MENU_PROF.on(controller).justPressed()) { + if (ControlifyActions.TOGGLE_DEBUG_MENU_PROF.on(controller).pulse().consumePulse()) { DebugOverlayHelper.toggleProfilerOverlay(); } - if (ControlifyBindings.DEBUG_RELOAD_CHUNKS.on(controller).justPressed()) { + if (ControlifyActions.DEBUG_RELOAD_CHUNKS.on(controller).pulse().consumePulse()) { DebugOverlayHelper.reloadChunks(); } - if (ControlifyBindings.DEBUG_TOGGLE_CHUNK_BORDERS.on(controller).justPressed()) { + if (ControlifyActions.DEBUG_TOGGLE_CHUNK_BORDERS.on(controller).pulse().consumePulse()) { DebugOverlayHelper.toggleChunkBorders(); } - if (ControlifyBindings.DEBUG_TOGGLE_ADVANCED_TOOLTIPS.on(controller).justPressed()) { + if (ControlifyActions.DEBUG_TOGGLE_ADVANCED_TOOLTIPS.on(controller).pulse().consumePulse()) { DebugOverlayHelper.toggleAdvancedTooltips(); } - if (ControlifyBindings.DEBUG_TOGGLE_ENTITY_HITBOXES.on(controller).justPressed()) { + if (ControlifyActions.DEBUG_TOGGLE_ENTITY_HITBOXES.on(controller).pulse().consumePulse()) { DebugOverlayHelper.toggleEntityHitboxes(); } - if (ControlifyBindings.DEBUG_RELOAD_RESOURCE_PACKS.on(controller).justPressed()) { + if (ControlifyActions.DEBUG_RELOAD_RESOURCE_PACKS.on(controller).pulse().consumePulse()) { DebugOverlayHelper.reloadResourcePacks(); } - if (ControlifyBindings.DEBUG_CLEAR_CHAT.on(controller).justPressed()) { + if (ControlifyActions.DEBUG_CLEAR_CHAT.on(controller).pulse().consumePulse()) { DebugOverlayHelper.clearChat(); } - if (ControlifyBindings.DEBUG_START_STOP_PROFILING.on(controller).justPressed()) { + if (ControlifyActions.DEBUG_START_STOP_PROFILING.on(controller).pulse().consumePulse()) { DebugOverlayHelper.startStopProfiling(); } - if (ControlifyBindings.DEBUG_RADIAL.on(controller).justPressed()) { + if (ControlifyActions.DEBUG_RADIAL.on(controller).pulse().consumePulse()) { MinecraftUtil.setScreen(new RadialMenuScreen( controller, - ControlifyBindings.DEBUG_RADIAL.on(controller), + ControlifyActions.DEBUG_RADIAL, RadialItems.createDebug(controller), Component.empty(), null )); } - if (ControlifyBindings.TAKE_SCREENSHOT.on(controller).justPressed()) { + if (ControlifyActions.TAKE_SCREENSHOT.on(controller).pulse().consumePulse()) { // get file before it takes and writes the screenshot (which changes the next name) File screenshotFile = ScreenshotAccessor.controlify$invokeGetFile( new File(minecraft.gameDirectory, "screenshots") @@ -259,14 +242,14 @@ protected void handleKeybinds() { ); } - if (ControlifyBindings.PICK_BLOCK.on(controller).justPressed()) { + if (ControlifyActions.PICK_BLOCK.on(controller).pulse().consumePulse()) { ((PickBlockAccessor) minecraft).controlify$pickBlock(); } - if (ControlifyBindings.PICK_BLOCK_NBT.on(controller).justPressed()) { + if (ControlifyActions.PICK_BLOCK_NBT.on(controller).pulse().consumePulse()) { ((PickBlockAccessor) minecraft).controlify$pickBlockWithNbt(); } - if (ControlifyBindings.RADIAL_MENU.on(controller).justPressed()) { + if (ControlifyActions.RADIAL_MENU.on(controller).pulse().consumePulse()) { var bonusPages = new ArrayList(); if (canSwitchGameMode()) { bonusPages.add(RadialItems.createGameModes()); @@ -274,28 +257,28 @@ protected void handleKeybinds() { MinecraftUtil.setScreen(new RadialMenuScreen( controller, - ControlifyBindings.RADIAL_MENU.on(controller), - RadialItems.createBindingPages(controller), + ControlifyActions.RADIAL_MENU, + RadialItems.createActionPages(controller), bonusPages, Component.translatable("controlify.radial_menu.configure_hint"), null )); } - if (ControlifyBindings.GAME_MODE_SWITCHER.on(controller).justPressed() && canSwitchGameMode()) { + if (ControlifyActions.GAME_MODE_SWITCHER.on(controller).pulse().consumePulse() && canSwitchGameMode()) { MinecraftUtil.setScreen(new RadialMenuScreen( controller, - ControlifyBindings.GAME_MODE_SWITCHER.on(controller), + ControlifyActions.GAME_MODE_SWITCHER, RadialItems.createGameModes().items(), Component.empty(), null) ); } - if (ControlifyBindings.HOTBAR_SLOT_SELECT.on(controller).justPressed()) { + if (ControlifyActions.HOTBAR_SLOT_SELECT.on(controller).pulse().consumePulse()) { MinecraftUtil.setScreen(new RadialMenuScreen( controller, - ControlifyBindings.HOTBAR_SLOT_SELECT.on(controller), + ControlifyActions.HOTBAR_SLOT_SELECT, RadialItems.createHotbarItemSelect(), Component.empty(), null @@ -303,19 +286,19 @@ protected void handleKeybinds() { } if (minecraft.player != null && minecraft.player.hasInfiniteMaterials()) { - if (ControlifyBindings.HOTBAR_LOAD_RADIAL.on(controller).justPressed()) { + if (ControlifyActions.HOTBAR_LOAD_RADIAL.on(controller).pulse().consumePulse()) { MinecraftUtil.setScreen(new RadialMenuScreen( controller, - ControlifyBindings.HOTBAR_LOAD_RADIAL.on(controller), + ControlifyActions.HOTBAR_LOAD_RADIAL, RadialItems.createHotbarLoad().items(), Component.translatable("controlify.radial.hotbar_load_hint"), null )); } - if (ControlifyBindings.HOTBAR_SAVE_RADIAL.on(controller).justPressed()) { + if (ControlifyActions.HOTBAR_SAVE_RADIAL.on(controller).pulse().consumePulse()) { MinecraftUtil.setScreen(new RadialMenuScreen( controller, - ControlifyBindings.HOTBAR_SAVE_RADIAL.on(controller), + ControlifyActions.HOTBAR_SAVE_RADIAL, RadialItems.createHotbarSave().items(), Component.translatable("controlify.radial.hotbar_save_hint"), null @@ -347,11 +330,11 @@ protected void handlePlayerLookInput(boolean isController) { if (controller.gyro().map(gyro -> gyro.settings().lookSensitivity > 0 && gyro.settings().flickStick).orElse(false)) { handleFlickStick(player); } else { - controller.input().ifPresent(input -> handleRegularLook(input, lookImpulse, aiming, player)); + handleRegularLook(controller.input(), lookImpulse, aiming, player); } var modifier = new LookInputModifier(new Vector2f((float) lookImpulse.x, (float) lookImpulse.y), controller); - ControlifyEvents.LOOK_INPUT_MODIFIER.invoke(modifier); + Events.LOOK_INPUT_MODIFIER.emit(modifier); lookImpulse.set(modifier.lookInput()); lookInputX = lookImpulse.x; @@ -360,15 +343,15 @@ protected void handlePlayerLookInput(boolean isController) { wasAiming = aiming; } - protected void handleRegularLook(InputComponent input, Vector2d impulse, boolean aiming, LocalPlayer player) { + protected void handleRegularLook(InputComponentImpl input, Vector2d impulse, boolean aiming, LocalPlayer player) { InputSettings settings = input.settings(); // normal look input Vector2d regularImpulse = new Vector2d( - ControlifyBindings.LOOK_RIGHT.on(controller).analogueNow() - - ControlifyBindings.LOOK_LEFT.on(controller).analogueNow(), - ControlifyBindings.LOOK_DOWN.on(controller).analogueNow() - - ControlifyBindings.LOOK_UP.on(controller).analogueNow() + ControlifyActions.LOOK_RIGHT.on(controller).continuous().getContinuous() + - ControlifyActions.LOOK_LEFT.on(controller).continuous().getContinuous(), + ControlifyActions.LOOK_DOWN.on(controller).continuous().getContinuous() + - ControlifyActions.LOOK_UP.on(controller).continuous().getContinuous() ); if (settings.sensitivity.hLookInvert) { regularImpulse.x *= -1; @@ -411,53 +394,55 @@ protected void handleRegularLook(InputComponent input, Vector2d impulse, boolean impulse.add(regularImpulse); } - protected void handleGyroLook(GyroComponent gyro, Vector2d impulse, boolean aiming) { + protected void handleGyroLook(GyroComponentImpl gyro, Vector2d impulse, boolean aiming) { GyroSettings settings = gyro.settings(); - var gyroButton = ControlifyBindings.GYRO_BUTTON.on(controller); + var gyroButton = ControlifyActions.GYRO_BUTTON.on(controller); - if (settings.buttonMode.equals(GyroButtonMode.ON) && (!gyroButton.digitalNow() && !aiming)) { + if (settings.buttonMode.equals(GyroButtonMode.ON) && (!gyroButton.latch().isLatchActive() && !aiming)) { gyroInput.set(0); - } else if(settings.buttonMode.equals(GyroButtonMode.INVERT) && (gyroButton.digitalNow() && !aiming)) { + } else if(settings.buttonMode.equals(GyroButtonMode.INVERT) && (gyroButton.latch().isLatchActive() && !aiming)) { gyroInput.set(0); } else if(settings.buttonMode.equals(GyroButtonMode.TOGGLE) && (!gyroToggledOn && !aiming)) { gyroInput.set(0); } else { - if (settings.relativeMode) - gyroInput.add(new GyroState(gyro.getState()).mul(0.1f)); + if (settings.relativeMode) { + gyroInput.add(new Vector3f(gyro.getState()).mul(0.1f)); + } else gyroInput.set(gyro.getState()); } - if(settings.buttonMode.equals(GyroButtonMode.TOGGLE) && gyroButton.justPressed()) { - gyroToggledOn = !gyroToggledOn; + boolean gyroPressed = gyroButton.pulse().consumePulse(); + if (settings.buttonMode.equals(GyroButtonMode.TOGGLE) && gyroPressed) { + gyroToggledOn = !gyroToggledOn; } // convert radians per second into degrees per tick - GyroState thisInput = new GyroState(gyroInput) + var thisInput = new Vector3f(gyroInput) .mul(Mth.RAD_TO_DEG) .div(20) .mul(settings.lookSensitivity); - impulse.y += -thisInput.pitch() * (settings.invertPitch ? -1 : 1); + impulse.y += -thisInput.x() * (settings.invertPitch ? -1 : 1); impulse.x += switch (settings.yawMode) { - case YAW -> -thisInput.yaw(); - case ROLL -> -thisInput.roll(); - case BOTH -> -thisInput.yaw() - thisInput.roll(); + case YAW -> -thisInput.y(); + case ROLL -> -thisInput.z(); + case BOTH -> -thisInput.y() - thisInput.z(); } * (settings.invertYaw ? -1 : 1); } protected void handleFlickStick(LocalPlayer player) { - float y = ControlifyBindings.LOOK_DOWN.on(controller).analogueNow() - - ControlifyBindings.LOOK_UP.on(controller).analogueNow(); - float x = ControlifyBindings.LOOK_RIGHT.on(controller).analogueNow() - - ControlifyBindings.LOOK_LEFT.on(controller).analogueNow(); + float y = ControlifyActions.LOOK_DOWN.on(controller).continuous().getContinuous() + - ControlifyActions.LOOK_UP.on(controller).continuous().getContinuous(); + float x = ControlifyActions.LOOK_RIGHT.on(controller).continuous().getContinuous() + - ControlifyActions.LOOK_LEFT.on(controller).continuous().getContinuous(); float flickAngle = Mth.wrapDegrees((float) Mth.atan2(y, x) * Mth.RAD_TO_DEG + 90f); - if (!ControlifyBindings.LOOK_DOWN.on(controller).justPressed() - && !ControlifyBindings.LOOK_UP.on(controller).justPressed() - && !ControlifyBindings.LOOK_LEFT.on(controller).justPressed() - && !ControlifyBindings.LOOK_RIGHT.on(controller).justPressed() + if (!ControlifyActions.LOOK_DOWN.on(controller).pulse().consumePulse() + & !ControlifyActions.LOOK_UP.on(controller).pulse().consumePulse() + & !ControlifyActions.LOOK_LEFT.on(controller).pulse().consumePulse() + & !ControlifyActions.LOOK_RIGHT.on(controller).pulse().consumePulse() ) { return; } @@ -501,10 +486,12 @@ public void preventFlyDrifting() { boolean jumping = player.input.keyPresses.jump(); boolean shiftKeyDown = player.input.keyPresses.shift(); - if (!jumping) + if (!jumping) { y = Math.min(y, 0); - if (!shiftKeyDown) + } + if (!shiftKeyDown) { y = Math.max(y, 0); + } Vec2 moveVec = getMoveVec(player.input); if (moveVec.x == 0 && moveVec.y == 0) { diff --git a/src/main/java/dev/isxander/controlify/impl/input/mapping/ControllerMappingManager.java b/src/main/java/dev/isxander/controlify/impl/input/mapping/ControllerMappingManager.java index be909bf06..c4c7f5290 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/mapping/ControllerMappingManager.java +++ b/src/main/java/dev/isxander/controlify/impl/input/mapping/ControllerMappingManager.java @@ -4,6 +4,7 @@ import com.google.gson.JsonParser; import com.mojang.serialization.DataResult; import com.mojang.serialization.JsonOps; +import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.FileToIdConverter; @@ -57,6 +58,11 @@ public CompletableFuture apply(Preparations data, ResourceManager manager, return CompletableFuture.runAsync(() -> { this.mappingById.clear(); this.mappingById.putAll(data.mappingById); + Controlify.instance().getControllerManager().ifPresent(controllerManager -> { + for (var controller : controllerManager.getAllControllers()) { + controller.input().reloadControllerMapping(); + } + }); }, executor); } diff --git a/src/main/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStage.java index 4e9aa5a14..3e7afc594 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStage.java @@ -1,13 +1,16 @@ package dev.isxander.controlify.impl.input.pipeline; +import dev.isxander.controlify.impl.debug.DebugProperties; import dev.isxander.controlify.impl.input.mapping.DeadzoneGroup; import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; import dev.isxander.controlify.impl.input.signal.PhysicalSignal; -import dev.isxander.controlify.impl.utils.event.EventSink; -import dev.isxander.controlify.impl.utils.event.UnaryEventStage; +import dev.isxander.controlify.api.utils.event.EventSink; +import dev.isxander.controlify.api.utils.event.UnaryEventStage; +import dev.isxander.controlify.impl.utils.CUtil; import it.unimi.dsi.fastutil.objects.*; import net.minecraft.resources.Identifier; import net.minecraft.util.Mth; +import org.jspecify.annotations.NonNull; import java.util.Collection; import java.util.Map; @@ -30,7 +33,7 @@ public DeadzoneStage() { } @Override - public void onEvent(PhysicalSignal event, EventSink downstream) { + public void onEvent(@NonNull PhysicalSignal event, @NonNull EventSink downstream) { if (event instanceof PhysicalInputSignal.AxisMoved axisMoved) { RadialDeadzoneState state = this.stateByAxis.get(axisMoved.input()); @@ -38,6 +41,9 @@ public void onEvent(PhysicalSignal event, EventSink down state.onEvent(axisMoved, downstream); return; } + if (DebugProperties.DEBUG_DEADZONES) { + CUtil.LOGGER.log("[Deadzone] Unchanged (no group): {}", axisMoved); + } } downstream.accept(event); @@ -76,7 +82,7 @@ public RadialDeadzoneState(DeadzoneGroup deadzoneGroup) { } @Override - public void onEvent(PhysicalInputSignal.AxisMoved event, EventSink downstream) { + public void onEvent(PhysicalInputSignal.@NonNull AxisMoved event, @NonNull EventSink downstream) { this.rawAxes.put(event.input(), event.value()); float deadzone = DeadzoneStage.this.deadzoneByGroupName.getFloat(this.deadzoneGroup.name()); @@ -98,6 +104,11 @@ public void onEvent(PhysicalInputSignal.AxisMoved event, EventSink { @Override - public void onEvent(PhysicalSignal event, EventSink downstream) { + public void onEvent(@NonNull PhysicalSignal event, @NonNull EventSink downstream) { // TODO downstream.accept(event); } diff --git a/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputDomainExpansionStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputDomainExpansionStage.java index 50516b2f2..315ec3d23 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputDomainExpansionStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputDomainExpansionStage.java @@ -2,9 +2,10 @@ import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; import dev.isxander.controlify.impl.input.signal.PhysicalSignal; -import dev.isxander.controlify.impl.utils.event.EventSink; -import dev.isxander.controlify.impl.utils.event.UnaryEventStage; +import dev.isxander.controlify.api.utils.event.EventSink; +import dev.isxander.controlify.api.utils.event.UnaryEventStage; import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; import java.util.HashSet; import java.util.Set; @@ -25,8 +26,12 @@ public InputDomainExpansionStage(float actuation, float release, float pressedVa this.releasedValue = releasedValue; } + public void resetInputState() { + this.pressedButtons.clear(); + } + @Override - public void onEvent(PhysicalSignal event, EventSink downstream) { + public void onEvent(@NonNull PhysicalSignal event, @NonNull EventSink downstream) { downstream.accept(event); switch (event) { diff --git a/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputPipeline.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputPipeline.java index 5475b9921..2a4bce81b 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputPipeline.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputPipeline.java @@ -3,9 +3,10 @@ import dev.isxander.controlify.impl.input.mapping.ControllerMapping; import dev.isxander.controlify.impl.input.signal.LogicalSignal; import dev.isxander.controlify.impl.input.signal.PhysicalSignal; -import dev.isxander.controlify.impl.utils.event.EventSink; -import dev.isxander.controlify.impl.utils.event.EventSource; +import dev.isxander.controlify.api.utils.event.EventSink; +import dev.isxander.controlify.api.utils.event.EventSource; import dev.isxander.controlify.impl.utils.event.EventTap; +import org.jspecify.annotations.NonNull; public class InputPipeline implements EventSink { private final EventTap tapRawPhysicalSignals; @@ -46,8 +47,17 @@ public InputPipeline() { .discardDownstream(); } + /// Applies a new physical mapping without reopening the driver or replacing event sources. + /// The caller must reset actions to release outputs based on the previous mapping. + public void setControllerMapping(ControllerMapping mapping) { + this.stageMapping.setControllerMapping(mapping); + this.stageDeadzone.setDeadzoneGroups(mapping.deadzones().values()); + this.stageInputDomainExpansion.resetInputState(); + this.stagePhysicalToLogical.resetInputState(); + } + @Override - public void accept(PhysicalSignal event) { + public void accept(@NonNull PhysicalSignal event) { this.fullPipeline.accept(event); } diff --git a/src/main/java/dev/isxander/controlify/impl/input/pipeline/MappingStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/MappingStage.java index d87e2a87d..13121c633 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/pipeline/MappingStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/MappingStage.java @@ -4,9 +4,10 @@ import dev.isxander.controlify.impl.input.mapping.MappingEntry; import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; import dev.isxander.controlify.impl.input.signal.PhysicalSignal; -import dev.isxander.controlify.impl.utils.event.EventSink; -import dev.isxander.controlify.impl.utils.event.UnaryEventStage; +import dev.isxander.controlify.api.utils.event.EventSink; +import dev.isxander.controlify.api.utils.event.UnaryEventStage; import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; import java.util.Map; import java.util.function.Function; @@ -20,7 +21,7 @@ public MappingStage(ControllerMapping mapping) { } @Override - public void onEvent(PhysicalSignal event, EventSink downstream) { + public void onEvent(@NonNull PhysicalSignal event, @NonNull EventSink downstream) { if (event instanceof PhysicalInputSignal inputSignal) { MappingEntry mapping = this.mappingByInput.get(inputSignal.input()); diff --git a/src/main/java/dev/isxander/controlify/impl/input/pipeline/PhysicalToLogicalStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/PhysicalToLogicalStage.java index 0c7938cf7..449a505e0 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/pipeline/PhysicalToLogicalStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/PhysicalToLogicalStage.java @@ -5,9 +5,10 @@ import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; import dev.isxander.controlify.impl.input.signal.PhysicalSensorSignal; import dev.isxander.controlify.impl.input.signal.PhysicalSignal; -import dev.isxander.controlify.impl.utils.event.EventSink; -import dev.isxander.controlify.impl.utils.event.EventStage; +import dev.isxander.controlify.api.utils.event.EventSink; +import dev.isxander.controlify.api.utils.event.EventStage; import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; import java.util.HashMap; import java.util.LinkedHashMap; @@ -56,13 +57,21 @@ public PhysicalToLogicalStage( this.holdNanos = holdNanos; } + /// Clears pending gestures and axis values after the physical mapping changes. + public void resetInputState() { + this.pressedButtons.clear(); + this.activeButtons.clear(); + this.lastTaps.clear(); + this.axisValues.clear(); + } + /// Cancels GUI presses for buttons already held; subsequent presses remain eligible. public void onGuiNavigate() { this.activeButtons.values().forEach(state -> state.guiPressEligible = false); } @Override - public void onEvent(PhysicalSignal event, EventSink downstream) { + public void onEvent(@NonNull PhysicalSignal event, @NonNull EventSink downstream) { switch (event) { case PhysicalSignal.Frame(long timeNanos, long deltaTimeNanos) -> { this.sensorAccumulatorStage.onEvent(event, downstream); diff --git a/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorAccumulatorStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorAccumulatorStage.java index da13d90ed..b53677a41 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorAccumulatorStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorAccumulatorStage.java @@ -4,8 +4,9 @@ import dev.isxander.controlify.impl.input.signal.LogicalSignal; import dev.isxander.controlify.impl.input.signal.PhysicalSensorSignal; import dev.isxander.controlify.impl.input.signal.PhysicalSignal; -import dev.isxander.controlify.impl.utils.event.EventSink; -import dev.isxander.controlify.impl.utils.event.EventStage; +import dev.isxander.controlify.api.utils.event.EventSink; +import dev.isxander.controlify.api.utils.event.EventStage; +import org.jspecify.annotations.NonNull; import java.util.EnumMap; import java.util.Map; @@ -18,8 +19,8 @@ public class SensorAccumulatorStage implements EventStage downstream + @NonNull PhysicalSignal event, + @NonNull EventSink downstream ) { switch (event) { case PhysicalSignal.Frame(long timeNanos, _) -> { diff --git a/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorProcessorStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorProcessorStage.java index 77185a49c..e7e5c69f5 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorProcessorStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorProcessorStage.java @@ -3,10 +3,11 @@ import dev.isxander.controlify.impl.input.SensorType; import dev.isxander.controlify.impl.input.signal.PhysicalSensorSignal; import dev.isxander.controlify.impl.input.signal.PhysicalSignal; -import dev.isxander.controlify.impl.utils.event.EventSink; -import dev.isxander.controlify.impl.utils.event.UnaryEventStage; +import dev.isxander.controlify.api.utils.event.EventSink; +import dev.isxander.controlify.api.utils.event.UnaryEventStage; import org.joml.Vector3f; import org.joml.Vector3fc; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import java.util.EnumMap; @@ -35,8 +36,13 @@ public void setProcessors(Function> processorF } } + /// Configures one sensor without replacing processing owned by other components. + public void setProcessors(SensorType sensorType, List processors) { + this.processors.put(sensorType, List.copyOf(processors)); + } + @Override - public void onEvent(PhysicalSignal event, EventSink downstream) { + public void onEvent(@NonNull PhysicalSignal event, @NonNull EventSink downstream) { switch (event) { case PhysicalSensorSignal(long timeNanos, long sensorTimeNanos, SensorType sensorType, float x, float y, float z) when !this.processors.get(sensorType).isEmpty() -> { diff --git a/src/main/java/dev/isxander/controlify/impl/platform/client/resource/SimpleControlifyReloadListener.java b/src/main/java/dev/isxander/controlify/impl/platform/client/resource/SimpleControlifyReloadListener.java index 0760b3eb0..aecea08da 100644 --- a/src/main/java/dev/isxander/controlify/impl/platform/client/resource/SimpleControlifyReloadListener.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/client/resource/SimpleControlifyReloadListener.java @@ -21,18 +21,9 @@ public interface SimpleControlifyReloadListener extends ControlifyReloadListe @NonNull PreparationBarrier preparationBarrier, @NonNull Executor applyExecutor ) { - return reload0(preparationBarrier, sharedState.resourceManager(), loadExecutor, applyExecutor); - } - - default @NotNull CompletableFuture reload0( - PreparationBarrier preparationBarrier, - ResourceManager resourceManager, - Executor loadExecutor, - Executor applyExecutor - ) { - return load(resourceManager, loadExecutor) + return load(sharedState.resourceManager(), loadExecutor) .thenCompose(preparationBarrier::wait) - .thenCompose(data -> apply(data, resourceManager, applyExecutor)); + .thenCompose(data -> apply(data, sharedState.resourceManager(), applyExecutor)); } /** diff --git a/src/main/java/dev/isxander/controlify/impl/platform/main/PlatformMainUtil.java b/src/main/java/dev/isxander/controlify/impl/platform/main/PlatformMainUtil.java index 89960b99c..219e7be2e 100644 --- a/src/main/java/dev/isxander/controlify/impl/platform/main/PlatformMainUtil.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/main/PlatformMainUtil.java @@ -6,7 +6,8 @@ */ package dev.isxander.controlify.impl.platform.main; -import dev.isxander.controlify.impl.api.entrypoint.ControlifyEntrypoint; +import dev.isxander.controlify.api.ControlifyEntrypoint; +import dev.isxander.controlify.impl.utils.CUtil; import dev.isxander.controlify.impl.platform.Environment; import dev.isxander.controlify.impl.platform.main.events.CommandRegistrationCallbackEvent; import dev.isxander.controlify.impl.platform.main.events.HandshakeCompletionEvent; @@ -22,12 +23,15 @@ import java.io.InputStream; import java.nio.file.Path; import java.util.Optional; +import java.util.HashSet; +import java.util.Set; import java.util.function.Consumer; import java.util.function.Supplier; public final class PlatformMainUtil { public static PlatformMainUtilImpl IMPL = null; + private static boolean invokedControlifyEntrypoints; public static void registerCommandRegistrationCallback(CommandRegistrationCallbackEvent callback) { IMPL.registerCommandRegistrationCallback(callback); @@ -45,6 +49,25 @@ public static Optional> getModFileInputStream(String mod return IMPL.getModFileInputStream(modId, path); } + /// Called by client bootstraps after installing both platform implementations. + public static void invokeControlifyEntrypoints() { + if (getEnv() != Environment.CLIENT || invokedControlifyEntrypoints) { + return; + } + invokedControlifyEntrypoints = true; + Set> invoked = new HashSet<>(); + applyToControlifyEntrypoint(entrypoint -> { + if (!invoked.add(entrypoint.getClass())) { + return; + } + try { + entrypoint.onControlifyInitialize(); + } catch (Throwable throwable) { + CUtil.LOGGER.error("Failed to initialize Controlify addon: {}", entrypoint.getClass().getName(), throwable); + } + }); + } + public static void applyToControlifyEntrypoint(Consumer entrypointConsumer) { IMPL.applyToControlifyEntrypoint(entrypointConsumer); } diff --git a/src/main/java/dev/isxander/controlify/impl/platform/main/PlatformMainUtilImpl.java b/src/main/java/dev/isxander/controlify/impl/platform/main/PlatformMainUtilImpl.java index 31b0d89f4..dfd191314 100644 --- a/src/main/java/dev/isxander/controlify/impl/platform/main/PlatformMainUtilImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/platform/main/PlatformMainUtilImpl.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.impl.platform.main; -import dev.isxander.controlify.impl.api.entrypoint.ControlifyEntrypoint; +import dev.isxander.controlify.api.ControlifyEntrypoint; import dev.isxander.controlify.impl.platform.Environment; import dev.isxander.controlify.impl.platform.main.events.CommandRegistrationCallbackEvent; import dev.isxander.controlify.impl.platform.main.events.HandshakeCompletionEvent; diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessor.java index 42253b38b..3fa0fc8d4 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessor.java @@ -7,22 +7,21 @@ package dev.isxander.controlify.impl.screenop; import com.mojang.blaze3d.platform.InputConstants; -import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.api.InputMode; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; -import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.api.action.Action; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.action.ControlifyActions; +import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.controller.haptic.HapticEffects; -import dev.isxander.controlify.impl.controller.input.ControllerStateView; +import dev.isxander.controlify.impl.controller.input.InputComponentImpl; import dev.isxander.controlify.impl.input.mapping.GamepadInputs; -import dev.isxander.controlify.impl.controller.input.InputComponent; -import dev.isxander.controlify.mixins.feature.screenop.ScreenAccessor; -import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.TabNavigationBarAccessor; import dev.isxander.controlify.impl.screenop.keyboard.*; import dev.isxander.controlify.impl.sound.ControlifyClientSounds; -import dev.isxander.controlify.impl.utils.HoldRepeatHelper; import dev.isxander.controlify.impl.utils.MinecraftUtil; import dev.isxander.controlify.impl.virtualmouse.VirtualMouseBehaviour; import dev.isxander.controlify.impl.virtualmouse.VirtualMouseHandler; +import dev.isxander.controlify.mixins.feature.screenop.ScreenAccessor; +import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.TabNavigationBarAccessor; import net.minecraft.client.InputType; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; @@ -44,7 +43,6 @@ public class ScreenProcessor { public final T screen; - protected final HoldRepeatHelper holdRepeatHelper; protected static final Minecraft minecraft = Minecraft.getInstance(); private final List eventListeners = new ArrayList<>(); @@ -54,18 +52,19 @@ public ScreenProcessor(T screen) { if (screen instanceof ScreenControllerEventListener eventListener) { eventListeners.add(eventListener); } - this.holdRepeatHelper = createHoldRepeatHelper(); } public void onControllerFixedUpdate(ControllerImpl controller) { Controlify.instance().virtualMouseHandler().handleControllerInput(controller); if (!Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled()) { - if (!handleComponentNavOverride(controller)) + if (!handleComponentNavOverride(controller)) { handleComponentNavigation(controller); + } - if (!handleComponentButtonOverride(controller)) + if (!handleComponentButtonOverride(controller)) { handleButtons(controller); + } } else { handleScreenVMouse(controller, Controlify.instance().virtualMouseHandler()); } @@ -105,58 +104,20 @@ protected void handleComponentNavigation(ControllerImpl controller) { var focuses = List.copyOf(getFocusTree()); - boolean repeatEventAvailable = holdRepeatHelper.canNavigate(); - - InputComponent input = controller.input().orElseThrow(); - ControllerStateView state = input.stateNow(); - ControllerStateView prevState = input.stateThen(); - - InputBinding guiNaviRight = ControlifyBindings.GUI_NAVI_RIGHT.on(controller); - InputBinding guiNaviLeft = ControlifyBindings.GUI_NAVI_LEFT.on(controller); - InputBinding guiNaviUp = ControlifyBindings.GUI_NAVI_UP.on(controller); - InputBinding guiNaviDown = ControlifyBindings.GUI_NAVI_DOWN.on(controller); + Action guiNaviRight = ControlifyActions.GUI_NAVI_RIGHT.on(controller); + Action guiNaviLeft = ControlifyActions.GUI_NAVI_LEFT.on(controller); + Action guiNaviUp = ControlifyActions.GUI_NAVI_UP.on(controller); + Action guiNaviDown = ControlifyActions.GUI_NAVI_DOWN.on(controller); Supplier navigationFunc = null; - if (guiNaviRight.digitalNow() && (repeatEventAvailable || !guiNaviRight.digitalPrev())) { + if (guiNaviRight.pulse().consumePulse()) { navigationFunc = this.createScreenNavigationFunc(ScreenDirection.RIGHT); - - if (!guiNaviRight.digitalPrev()) - holdRepeatHelper.reset(); - } else if (guiNaviLeft.digitalNow() && (repeatEventAvailable || !guiNaviLeft.digitalPrev())) { + } else if (guiNaviLeft.pulse().consumePulse()) { navigationFunc = this.createScreenNavigationFunc(ScreenDirection.LEFT); - - if (!guiNaviLeft.digitalPrev()) - holdRepeatHelper.reset(); - } else if (guiNaviUp.digitalNow() && (repeatEventAvailable || !guiNaviUp.digitalPrev())) { + } else if (guiNaviUp.pulse().consumePulse()) { navigationFunc = this.createScreenNavigationFunc(ScreenDirection.UP); - - if (!guiNaviUp.digitalPrev()) - holdRepeatHelper.reset(); - } else if (guiNaviDown.digitalNow() && (repeatEventAvailable || !guiNaviDown.digitalPrev())) { + } else if (guiNaviDown.pulse().consumePulse()) { navigationFunc = this.createScreenNavigationFunc(ScreenDirection.DOWN); - - if (!guiNaviDown.digitalPrev()) - holdRepeatHelper.reset(); - } else if (state.isButtonDown(GamepadInputs.DPAD_RIGHT_BUTTON) && (repeatEventAvailable || !prevState.isButtonDown(GamepadInputs.DPAD_RIGHT_BUTTON))) { - navigationFunc = this.createScreenNavigationFunc(ScreenDirection.RIGHT); - - if (!prevState.isButtonDown(GamepadInputs.DPAD_RIGHT_BUTTON)) - holdRepeatHelper.reset(); - } else if (state.isButtonDown(GamepadInputs.DPAD_LEFT_BUTTON) && (repeatEventAvailable || !prevState.isButtonDown(GamepadInputs.DPAD_LEFT_BUTTON))) { - navigationFunc = this.createScreenNavigationFunc(ScreenDirection.LEFT); - - if (!prevState.isButtonDown(GamepadInputs.DPAD_LEFT_BUTTON)) - holdRepeatHelper.reset(); - } else if (state.isButtonDown(GamepadInputs.DPAD_UP_BUTTON) && (repeatEventAvailable || !prevState.isButtonDown(GamepadInputs.DPAD_UP_BUTTON))) { - navigationFunc = this.createScreenNavigationFunc(ScreenDirection.UP); - - if (!prevState.isButtonDown(GamepadInputs.DPAD_UP_BUTTON)) - holdRepeatHelper.reset(); - } else if (state.isButtonDown(GamepadInputs.DPAD_DOWN_BUTTON) && (repeatEventAvailable || !prevState.isButtonDown(GamepadInputs.DPAD_DOWN_BUTTON))) { - navigationFunc = this.createScreenNavigationFunc(ScreenDirection.DOWN); - - if (!prevState.isButtonDown(GamepadInputs.DPAD_DOWN_BUTTON)) - holdRepeatHelper.reset(); } if (navigationFunc != null) { @@ -165,9 +126,8 @@ protected void handleComponentNavigation(ControllerImpl controller) { // established yet when a newly opened screen built its widgets. minecraft.setLastInputType(InputType.KEYBOARD_ARROW); if (navigationFunc.get()) { - holdRepeatHelper.onNavigate(); - controller.input().ifPresent(InputComponent::notifyGuiPressOutputsOfNavigate); + controller.input().onGuiNavigate(); if (Controlify.instance().config().getSettings().globalSettings().extraUiSounds) { playFocusChangeSound(); @@ -197,16 +157,18 @@ protected void handleComponentNavigation(ControllerImpl controller) { protected void handleButtons(ControllerImpl controller) { boolean vmouseEnabled = Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled(); - InputComponent input = controller.input().orElseThrow(); - boolean touchpadPressed = input.stateNow().isButtonDown(GamepadInputs.TOUCHPAD_1_BUTTON); - boolean prevTouchpadPressed = input.stateThen().isButtonDown(GamepadInputs.TOUCHPAD_1_BUTTON); + InputComponentImpl input = controller.input(); +// boolean touchpadPressed = input.stateNow().isButtonDown(GamepadInputs.TOUCHPAD_1_BUTTON); +// boolean prevTouchpadPressed = input.stateThen().isButtonDown(GamepadInputs.TOUCHPAD_1_BUTTON); + boolean touchpadPressed = false; + boolean prevTouchpadPressed = false; - if (ControlifyBindings.GUI_PRESS.on(controller).guiPressed().get() || (vmouseEnabled && touchpadPressed && !prevTouchpadPressed)) { + if (ControlifyActions.GUI_PRESS.on(controller).pulse().consumePulse() || (vmouseEnabled && touchpadPressed && !prevTouchpadPressed)) { if (!this.tryOpenKeyboard(controller, screen.getFocused())) { screen.keyPressed(new KeyEvent(InputConstants.KEY_RETURN, 0, 0)); } } - if (screen.shouldCloseOnEsc() && ControlifyBindings.GUI_BACK.on(controller).guiPressed().get()) { + if (screen.shouldCloseOnEsc() && ControlifyActions.GUI_BACK.on(controller).pulse().consumePulse()) { playClackSound(); screen.onClose(); } @@ -221,7 +183,9 @@ protected boolean handleComponentButtonOverride(ControllerImpl controller) { while (!focusTree.isEmpty()) { var focused = focusTree.poll(); var processor = ComponentProcessorProvider.provide(focused); - if (processor.overrideControllerButtons(this, controller)) return true; + if (processor.overrideControllerButtons(this, controller)) { + return true; + } } return false; @@ -232,14 +196,16 @@ protected boolean handleComponentNavOverride(ControllerImpl controller) { while (!focusTree.isEmpty()) { var focused = focusTree.poll(); var processor = ComponentProcessorProvider.provide(focused); - if (processor.overrideControllerNavigation(this, controller)) return true; + if (processor.overrideControllerNavigation(this, controller)) { + return true; + } } return false; } protected void handleTabNavigation(ControllerImpl controller) { - var nextTab = ControlifyBindings.GUI_NEXT_TAB.on(controller).justPressed(); - var prevTab = ControlifyBindings.GUI_PREV_TAB.on(controller).justPressed(); + var nextTab = ControlifyActions.GUI_NEXT_TAB.on(controller).pulse().consumePulse(); + var prevTab = ControlifyActions.GUI_PREV_TAB.on(controller).pulse().consumePulse(); if (nextTab || prevTab) { screen.children().stream() @@ -252,8 +218,12 @@ protected void handleTabNavigation(ControllerImpl controller) { int currentIndex = tabs.indexOf(accessor.controlify$getTabManager().getCurrentTab()); int newIndex = currentIndex + (prevTab ? -1 : 1); - if (newIndex < 0) newIndex = tabs.size() - 1; - if (newIndex >= tabs.size()) newIndex = 0; + if (newIndex < 0) { + newIndex = tabs.size() - 1; + } + if (newIndex >= tabs.size()) { + newIndex = 0; + } navBar.selectTab(newIndex, true); onTabChanged(controller); @@ -262,7 +232,6 @@ protected void handleTabNavigation(ControllerImpl controller) { } protected void onTabChanged(ControllerImpl controller) { - } public void onWidgetRebuild() { @@ -282,7 +251,6 @@ public void onVirtualMouseToggled(boolean enabled) { } protected void render(ControllerImpl controller, GuiGraphicsExtractor graphics, float tickDelta, Optional vmouse) { - } public VirtualMouseBehaviour virtualMouseBehaviour() { @@ -318,12 +286,10 @@ public boolean tryOpenKeyboard(ControllerImpl controller, @Nullable GuiEventList } } - protected HoldRepeatHelper createHoldRepeatHelper() { - return new HoldRepeatHelper(10, 3); - } - protected Queue getFocusTree() { - if (screen.getFocused() == null) return new ArrayDeque<>(); + if (screen.getFocused() == null) { + return new ArrayDeque<>(); + } var tree = new ArrayDeque(); var focused = screen.getFocused(); @@ -331,8 +297,9 @@ protected Queue getFocusTree() { while (focused instanceof CustomFocus customFocus) { focused = customFocus.getCustomFocus(); - if (focused != null) + if (focused != null) { tree.addFirst(focused); + } } return tree; diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/AbstractSliderComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/AbstractSliderComponentProcessor.java index 95276c4d1..8ac9abd41 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/AbstractSliderComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/AbstractSliderComponentProcessor.java @@ -6,44 +6,30 @@ */ package dev.isxander.controlify.impl.screenop.compat; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.screenop.ComponentProcessor; import dev.isxander.controlify.impl.screenop.ScreenProcessor; -import dev.isxander.controlify.impl.utils.HoldRepeatHelper; /** * A component processor that handles incrementing and decrementing a slider. - * This uses {@link dev.isxander.controlify.impl.bindings.ControllerBindings#CYCLE_OPT_FORWARD} and {@link dev.isxander.controlify.impl.bindings.ControllerBindings#CYCLE_OPT_BACKWARD} to increment and decrement the slider. + * This uses {@link dev.isxander.controlify.impl.action.ControlifyActions#GUI_SECONDARY_NAVI_RIGHT} and {@link dev.isxander.controlify.impl.action.ControlifyActions#GUI_SECONDARY_NAVI_LEFT} to increment and decrement the slider. */ public abstract class AbstractSliderComponentProcessor implements ComponentProcessor { - private final HoldRepeatHelper holdRepeatHelper = new HoldRepeatHelper(15, 3); @Override public boolean overrideControllerNavigation(ScreenProcessor screen, ControllerImpl controller) { - var left = ControlifyBindings.GUI_SECONDARY_NAVI_LEFT.on(controller).digitalNow(); - var leftPrev = ControlifyBindings.GUI_SECONDARY_NAVI_LEFT.on(controller).digitalPrev(); - var right = ControlifyBindings.GUI_SECONDARY_NAVI_RIGHT.on(controller).digitalNow(); - var rightPrev = ControlifyBindings.GUI_SECONDARY_NAVI_RIGHT.on(controller).digitalPrev(); + var left = ControlifyActions.GUI_SECONDARY_NAVI_LEFT.on(controller).pulse().consumePulse(); + var right = ControlifyActions.GUI_SECONDARY_NAVI_RIGHT.on(controller).pulse().consumePulse(); - boolean repeatEventAvailable = holdRepeatHelper.canNavigate(); - - if (left && (repeatEventAvailable || !leftPrev)) { + if (left) { incrementSlider(true); - - if (!leftPrev) - holdRepeatHelper.reset(); - } else if (right && (repeatEventAvailable || !rightPrev)) { + } else if (right) { incrementSlider(false); - - if (!rightPrev) - holdRepeatHelper.reset(); } else { return false; } - holdRepeatHelper.onNavigate(); - return true; } diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractButtonComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractButtonComponentProcessor.java index 06e96e3ce..e9eaec0d7 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractButtonComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractButtonComponentProcessor.java @@ -6,14 +6,14 @@ */ package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import com.mojang.blaze3d.platform.InputConstants; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.controller.haptic.HapticEffects; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.AbstractButton; -import com.mojang.blaze3d.platform.InputConstants; import net.minecraft.client.input.KeyEvent; public class AbstractButtonComponentProcessor implements ComponentProcessor { @@ -25,7 +25,7 @@ public AbstractButtonComponentProcessor(AbstractButton button) { @Override public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { - if (ControlifyBindings.GUI_PRESS.on(controller).guiPressed().get()) { + if (ControlifyActions.GUI_PRESS.on(controller).pulse().consumePulse()) { controller.hdHaptics().ifPresent(hh -> hh.playHaptic(HapticEffects.NAVIGATE)); button.playDownSound(Minecraft.getInstance().getSoundManager()); button.onPress(new KeyEvent(InputConstants.KEY_RETURN, 0, 0)); diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractContainerScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractContainerScreenProcessor.java index bb0891943..56d0840c4 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractContainerScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractContainerScreenProcessor.java @@ -7,28 +7,27 @@ package dev.isxander.controlify.impl.screenop.compat.vanilla; import dev.isxander.controlify.api.InputMode; -import dev.isxander.controlify.api.ControlifyApi; import dev.isxander.controlify.api.contextual.ContainerContext; import dev.isxander.controlify.api.contextual.GuideInstance; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.contextual.ContextualDomains; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.controller.haptic.HapticEffects; import dev.isxander.controlify.impl.gui.guide.GuideRenderer; -import dev.isxander.controlify.mixins.feature.guide.screen.AbstractContainerScreenAccessor; -import dev.isxander.controlify.mixins.feature.screenop.ScreenAccessor; import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.virtualmouse.VirtualMouseBehaviour; import dev.isxander.controlify.impl.virtualmouse.VirtualMouseHandler; +import dev.isxander.controlify.mixins.feature.guide.screen.AbstractContainerScreenAccessor; +import dev.isxander.controlify.mixins.feature.screenop.ScreenAccessor; import net.minecraft.client.gui.components.Renderable; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; - +import net.minecraft.world.inventory.ContainerInput; import net.minecraft.world.inventory.Slot; import java.util.List; import java.util.function.Predicate; import java.util.function.Supplier; -import net.minecraft.world.inventory.ContainerInput; public class AbstractContainerScreenProcessor> extends ScreenProcessor { @@ -80,22 +79,22 @@ protected void handleScreenVMouse(ControllerImpl controller, VirtualMouseHandler } } - if (ControlifyBindings.INV_SELECT.on(controller).justPressed()) { + if (ControlifyActions.INV_SELECT.on(controller).pulse().consumePulse()) { clickSlotFunction.clickSlot(hoveredSlot, hoveredSlot.index, 0, ContainerInput.PICKUP); hapticNavigate(); } - if (ControlifyBindings.INV_QUICK_MOVE.on(controller).justPressed()) { + if (ControlifyActions.INV_QUICK_MOVE.on(controller).pulse().consumePulse()) { clickSlotFunction.clickSlot(hoveredSlot, hoveredSlot.index, 0, ContainerInput.QUICK_MOVE); hapticNavigate(); } - if (ControlifyBindings.INV_TAKE_HALF.on(controller).justPressed()) { + if (ControlifyActions.INV_TAKE_HALF.on(controller).pulse().consumePulse()) { clickSlotFunction.clickSlot(hoveredSlot, hoveredSlot.index, 1, ContainerInput.PICKUP); hapticNavigate(); } -// if (ControlifyBindings.SWAP_HANDS.on(controller).justPressed()) { +// if (ControlifyActions.SWAP_HANDS.on(controller).pulse().consumePulse()) { // clickSlotFunction.clickSlot(hoveredSlot, hoveredSlot.index, 40, ContainerInput.SWAP); // hapticNavigate(); // } @@ -104,7 +103,7 @@ protected void handleScreenVMouse(ControllerImpl controller, VirtualMouseHandler } if (!screen.getMenu().getCarried().isEmpty()) { - if (ControlifyBindings.DROP_INVENTORY.on(controller).justPressed()) { + if (ControlifyActions.DROP_INVENTORY.on(controller).pulse().consumePulse()) { clickSlotFunction.clickSlot(null, -999, 0, ContainerInput.PICKUP); hapticNavigate(); } @@ -115,7 +114,7 @@ protected void handleScreenVMouse(ControllerImpl controller, VirtualMouseHandler public void onWidgetRebuild() { this.guideRenderable = (GuideRenderer.Renderable) guideInstance.renderable(true, false); - if (ControlifyApi.get().currentInputMode().isController()) { + if (Controlify.instance().currentInputMode().isController()) { setRenderGuide(true); } } @@ -126,10 +125,10 @@ public void onInputModeChanged(InputMode mode) { } private void setRenderGuide(boolean render) { - ControlifyApi.get().getCurrentController() + Controlify.instance().getCurrentController() .ifPresent(c -> this.guideRenderable.setGuiScale(c.settings().generic.guide.screenGuiScale)); - render &= ControlifyApi.get().getCurrentController().map(c -> c.settings().generic.guide.showScreenGuides).orElse(false); + render &= Controlify.instance().getCurrentController().map(c -> c.settings().generic.guide.showScreenGuides).orElse(false); List renderables = ((ScreenAccessor) screen).controlify$getRenderables(); @@ -141,13 +140,13 @@ private void setRenderGuide(boolean render) { } public void onHoveredSlotChanged(Slot newSlot, Slot oldSlot) { - if (ControlifyApi.get().currentInputMode().isController()) { + if (Controlify.instance().currentInputMode().isController()) { hapticNavigate(); } } private void hapticNavigate() { - ControlifyApi.get().getCurrentController().flatMap(ControllerImpl::hdHaptics).ifPresent(hh -> { + Controlify.instance().getCurrentController().flatMap(ControllerImpl::hdHaptics).ifPresent(hh -> { hh.playHaptic(HapticEffects.NAVIGATE); }); } diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractSignEditScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractSignEditScreenProcessor.java index 1c35109a8..a528ee8c4 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractSignEditScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractSignEditScreenProcessor.java @@ -6,11 +6,11 @@ */ package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.api.InputMode; +import dev.isxander.controlify.api.font.ActionGlyphs; import dev.isxander.controlify.api.gui.ButtonGuideApi; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.screenop.keyboard.CommonKeyboardHints; @@ -34,8 +34,8 @@ public class AbstractSignEditScreenProcessor extends ScreenProcessor { private static final Component signLineHint = Component.translatable("controlify.hint.sign_line_change", - ControlifyBindings.GUI_SECONDARY_NAVI_UP.inputGlyph(), - ControlifyBindings.GUI_SECONDARY_NAVI_DOWN.inputGlyph()); + ActionGlyphs.action(ControlifyActions.GUI_SECONDARY_NAVI_UP.actionId()), + ActionGlyphs.action(ControlifyActions.GUI_SECONDARY_NAVI_DOWN.actionId())); private final Consumer moveCursorFunc; private final Supplier signSupplier; @@ -62,7 +62,7 @@ protected void handleButtons(ControllerImpl controller) { var config = controller.settings().generic; // move cursor down a line - if (ControlifyBindings.GUI_SECONDARY_NAVI_DOWN.on(controller).justPressed()) { + if (ControlifyActions.GUI_SECONDARY_NAVI_DOWN.on(controller).pulse().consumePulse()) { this.moveCursorFunc.accept(1); if (config.keyboard.hintSignLine && config.guide.showScreenGuides) { @@ -74,7 +74,7 @@ protected void handleButtons(ControllerImpl controller) { } // move cursor up a line - if (ControlifyBindings.GUI_SECONDARY_NAVI_UP.on(controller).justPressed()) { + if (ControlifyActions.GUI_SECONDARY_NAVI_UP.on(controller).pulse().consumePulse()) { this.moveCursorFunc.accept(-1); if (config.keyboard.hintSignLine && config.guide.showScreenGuides) { @@ -119,14 +119,10 @@ protected void render(ControllerImpl controller, GuiGraphicsExtractor graphics, public void onWidgetRebuild() { super.onWidgetRebuild(); - if (Controlify.instance().currentInputMode() == InputMode.MIXED) { - holdRepeatHelper.clearDelay(); - } - getWidget(CommonComponents.GUI_DONE).ifPresent(doneButton -> ButtonGuideApi.addGuideToButton( (AbstractButton) doneButton, - ControlifyBindings.GUI_BACK, + ControlifyActions.GUI_BACK, ButtonGuidePredicate.always() )); diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AddServerLikeScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AddServerLikeScreenProcessor.java index 323a2e867..0b453c88d 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AddServerLikeScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AddServerLikeScreenProcessor.java @@ -8,7 +8,7 @@ import dev.isxander.controlify.api.gui.ButtonGuideApi; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; import dev.isxander.controlify.impl.screenop.ScreenProcessor; @@ -54,8 +54,8 @@ public void onWidgetRebuild() { ButtonGuideApi.addGuideToButton( doneButton, () -> doneButton.isFocused() - ? ControlifyBindings.GUI_PRESS - : ControlifyBindings.GUI_ABSTRACT_ACTION_1, + ? ControlifyActions.GUI_PRESS + : ControlifyActions.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always() ); } @@ -63,7 +63,7 @@ public void onWidgetRebuild() { if (backButton != null) { ButtonGuideApi.addGuideToButton( backButton, - () -> ControlifyBindings.GUI_BACK, + () -> ControlifyActions.GUI_BACK, ButtonGuidePredicate.always() ); } @@ -71,7 +71,7 @@ public void onWidgetRebuild() { @Override protected void handleButtons(ControllerImpl controller) { - if (ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).guiPressed().get()) { + if (ControlifyActions.GUI_ABSTRACT_ACTION_1.on(controller).pulse().consumePulse()) { Button doneButton = doneButtonSupplier.get(); if (doneButton != null && !doneButton.isFocused()) { playClackSound(); diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/BundleItemSlotControllerAction.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/BundleItemSlotControllerAction.java index 060caac87..32a2d9ad9 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/BundleItemSlotControllerAction.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/BundleItemSlotControllerAction.java @@ -7,7 +7,7 @@ package dev.isxander.controlify.impl.screenop.compat.vanilla; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; import net.minecraft.util.Mth; import net.minecraft.world.item.BundleItem; @@ -25,16 +25,24 @@ public static boolean onControllerInput( if (uniqueItems > 0) { Controlify.instance().virtualMouseHandler().preventScrollingThisTick(); - boolean up = ControlifyBindings.GUI_SECONDARY_NAVI_UP.on(controller).justPressed(); - boolean down = ControlifyBindings.GUI_SECONDARY_NAVI_DOWN.on(controller).justPressed(); - boolean left = ControlifyBindings.GUI_SECONDARY_NAVI_LEFT.on(controller).justPressed(); - boolean right = ControlifyBindings.GUI_SECONDARY_NAVI_RIGHT.on(controller).justPressed(); + boolean up = ControlifyActions.GUI_SECONDARY_NAVI_UP.on(controller).pulse().consumePulse(); + boolean down = ControlifyActions.GUI_SECONDARY_NAVI_DOWN.on(controller).pulse().consumePulse(); + boolean left = ControlifyActions.GUI_SECONDARY_NAVI_LEFT.on(controller).pulse().consumePulse(); + boolean right = ControlifyActions.GUI_SECONDARY_NAVI_RIGHT.on(controller).pulse().consumePulse(); int offsetX = 0, offsetY = 0; - if (up) offsetY--; - if (down) offsetY++; - if (left) offsetX--; - if (right) offsetX++; + if (up) { + offsetY--; + } + if (down) { + offsetY++; + } + if (left) { + offsetX--; + } + if (right) { + offsetX++; + } if (offsetX != 0 || offsetY != 0) { int currentIndex = BundleItem.getSelectedItemIndex(stack); diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ChatScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ChatScreenProcessor.java index 4915d22ca..1af393317 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ChatScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ChatScreenProcessor.java @@ -6,15 +6,15 @@ */ package dev.isxander.controlify.impl.screenop.compat.vanilla; +import dev.isxander.controlify.api.font.ActionGlyphs; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; -import dev.isxander.controlify.impl.screenop.keyboard.ComponentKeyboardBehaviour; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.screenop.keyboard.CommonKeyboardHints; +import dev.isxander.controlify.impl.screenop.keyboard.ComponentKeyboardBehaviour; import dev.isxander.controlify.impl.screenop.keyboard.KeyboardWidget; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; -import dev.isxander.controlify.impl.utils.HoldRepeatHelper; import dev.isxander.controlify.impl.utils.LazyComponentDims; import dev.isxander.controlify.impl.virtualmouse.VirtualMouseHandler; import net.minecraft.client.gui.GuiGraphicsExtractor; @@ -27,8 +27,6 @@ import java.util.function.Supplier; public class ChatScreenProcessor extends ScreenProcessor { - private final HoldRepeatHelper suggestionsFwdHoldRepeatHelper = new HoldRepeatHelper(10, 4); - private final HoldRepeatHelper suggestionsBwdHoldRepeatHelper = new HoldRepeatHelper(10, 4); private final Supplier inputSupplier; private final Supplier<@Nullable KeyboardWidget> keyboardSupplier; @@ -36,9 +34,9 @@ public class ChatScreenProcessor extends ScreenProcessor { private final LazyComponentDims commandSuggesterHint = new LazyComponentDims( Component.translatable("controlify.hint.command_suggester", - ControlifyBindings.GUI_SECONDARY_NAVI_DOWN.inputGlyph(), - ControlifyBindings.GUI_SECONDARY_NAVI_UP.inputGlyph(), - ControlifyBindings.GUI_SECONDARY_NAVI_RIGHT.inputGlyph()) + ActionGlyphs.action(ControlifyActions.GUI_SECONDARY_NAVI_DOWN.actionId()), + ActionGlyphs.action(ControlifyActions.GUI_SECONDARY_NAVI_UP.actionId()), + ActionGlyphs.action(ControlifyActions.GUI_SECONDARY_NAVI_RIGHT.actionId())) ); public ChatScreenProcessor( @@ -59,30 +57,24 @@ protected void handleButtons(ControllerImpl controller) { CmdSuggestionsController suggestionsController = this.suggestionsController.get(); if (suggestionsController != null) { - if (this.suggestionsFwdHoldRepeatHelper.shouldAction(ControlifyBindings.GUI_SECONDARY_NAVI_DOWN.on(controller))) { + if (ControlifyActions.GUI_SECONDARY_NAVI_DOWN.on(controller).pulse().consumePulse()) { if (suggestionsController.controlify$cycle(1)) { - this.suggestionsFwdHoldRepeatHelper.onNavigate(); - this.suggestionsBwdHoldRepeatHelper.reset(); playFocusChangeSound(); this.clearCommandSuggesterHint(controller); } } - if (this.suggestionsBwdHoldRepeatHelper.shouldAction(ControlifyBindings.GUI_SECONDARY_NAVI_UP.on(controller))) { + if (ControlifyActions.GUI_SECONDARY_NAVI_UP.on(controller).pulse().consumePulse()) { if (suggestionsController.controlify$cycle(-1)) { - this.suggestionsBwdHoldRepeatHelper.onNavigate(); - this.suggestionsFwdHoldRepeatHelper.reset(); playFocusChangeSound(); this.clearCommandSuggesterHint(controller); } } - if (ControlifyBindings.GUI_SECONDARY_NAVI_RIGHT.on(controller).justPressed()) { + if (ControlifyActions.GUI_SECONDARY_NAVI_RIGHT.on(controller).pulse().consumePulse()) { if (suggestionsController.controlify$useSuggestion()) { - this.suggestionsFwdHoldRepeatHelper.reset(); - this.suggestionsBwdHoldRepeatHelper.reset(); playClackSound(); @@ -141,12 +133,6 @@ private void clearCommandSuggesterHint(ControllerImpl controller) { } } - @Override - protected HoldRepeatHelper createHoldRepeatHelper() { - // make the initial delay way less - return new HoldRepeatHelper(5, 4); - } - public interface CmdSuggestionsController { boolean controlify$cycle(int amount); diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/CreateWorldScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/CreateWorldScreenProcessor.java index e8ee85fa7..ff5eb0958 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/CreateWorldScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/CreateWorldScreenProcessor.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.screenop.ScreenProcessor; import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; @@ -21,7 +21,7 @@ public CreateWorldScreenProcessor(CreateWorldScreen screen, Runnable onCreateBut @Override protected void handleButtons(ControllerImpl controller) { - if (ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).justPressed()) { + if (ControlifyActions.GUI_ABSTRACT_ACTION_1.on(controller).pulse().consumePulse()) { playClackSound(); this.onCreateButton.run(); } diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/CreativeModeInventoryScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/CreativeModeInventoryScreenProcessor.java index 0542dad4e..ca8794c40 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/CreativeModeInventoryScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/CreativeModeInventoryScreenProcessor.java @@ -7,7 +7,7 @@ package dev.isxander.controlify.impl.screenop.compat.vanilla; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.platform.client.CreativeTabHelper; import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; @@ -38,14 +38,15 @@ public void onWidgetRebuild() { @Override protected void handleScreenVMouse(ControllerImpl controller, VirtualMouseHandler vmouse) { List tabs = tabHelper.getTabsForPage(tabHelper.getCurrentPage()); - if (ControlifyBindings.GUI_NEXT_TAB.on(controller).justPressed()) { + if (ControlifyActions.GUI_NEXT_TAB.on(controller).pulse().consumePulse()) { int newIndex = tabs.indexOf(tabHelper.getSelectedTab()) + 1; if (newIndex >= tabs.size()) { newIndex = 0; int newPage = tabHelper.getCurrentPage() + 1; - if (newPage >= tabHelper.getPageCount()) + if (newPage >= tabHelper.getPageCount()) { newPage = 0; + } tabHelper.setCurrentPage(newPage); tabs = tabHelper.getTabsForPage(newPage); @@ -53,12 +54,13 @@ protected void handleScreenVMouse(ControllerImpl controller, VirtualMouseHandler tabHelper.setSelectedTab(tabs.get(newIndex)); } - if (ControlifyBindings.GUI_PREV_TAB.on(controller).justPressed()) { + if (ControlifyActions.GUI_PREV_TAB.on(controller).pulse().consumePulse()) { int newIndex = tabs.indexOf(tabHelper.getSelectedTab()) - 1; if (newIndex < 0) { int newPage = tabHelper.getCurrentPage() - 1; - if (newPage < 0) + if (newPage < 0) { newPage = tabHelper.getPageCount() - 1; + } tabHelper.setCurrentPage(newPage); tabs = tabHelper.getTabsForPage(newPage); diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/JoinMultiplayerScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/JoinMultiplayerScreenProcessor.java index feee12cd0..2d723b2ad 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/JoinMultiplayerScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/JoinMultiplayerScreenProcessor.java @@ -8,7 +8,7 @@ import dev.isxander.controlify.api.gui.ButtonGuideApi; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.screenop.ScreenProcessor; import net.minecraft.client.gui.components.Button; @@ -40,13 +40,13 @@ public JoinMultiplayerScreenProcessor( @Override protected void handleButtons(ControllerImpl controller) { if (screen.getFocused() != null && screen.getFocused() instanceof Button) { - if (ControlifyBindings.GUI_BACK.on(controller).guiPressed().get()) { + if (ControlifyActions.GUI_BACK.on(controller).pulse().consumePulse()) { screen.setFocused(listSupplier.get()); return; } } - if (ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).guiPressed().get()) { + if (ControlifyActions.GUI_ABSTRACT_ACTION_1.on(controller).pulse().consumePulse()) { Button directConnectButton = this.directConnectButtonSupplier.get(); if (directConnectButton != null) { playClackSound(); @@ -54,7 +54,7 @@ protected void handleButtons(ControllerImpl controller) { } } - if (ControlifyBindings.GUI_ABSTRACT_ACTION_2.on(controller).guiPressed().get()) { + if (ControlifyActions.GUI_ABSTRACT_ACTION_2.on(controller).pulse().consumePulse()) { Button addServerButton = this.addServerButtonSupplier.get(); if (addServerButton != null) { playClackSound(); @@ -74,7 +74,7 @@ public void onWidgetRebuild() { if (backButton != null) { ButtonGuideApi.addGuideToButton( backButton, - ControlifyBindings.GUI_BACK, + ControlifyActions.GUI_BACK, ButtonGuidePredicate.always() ); } @@ -82,7 +82,7 @@ public void onWidgetRebuild() { if (directConnectButton != null) { ButtonGuideApi.addGuideToButton( directConnectButton, - ControlifyBindings.GUI_ABSTRACT_ACTION_1, + ControlifyActions.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always() ); } @@ -90,7 +90,7 @@ public void onWidgetRebuild() { if (addServerButton != null) { ButtonGuideApi.addGuideToButton( addServerButton, - ControlifyBindings.GUI_ABSTRACT_ACTION_2, + ControlifyActions.GUI_ABSTRACT_ACTION_2, ButtonGuidePredicate.always() ); } diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/LanguageSelectionListComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/LanguageSelectionListComponentProcessor.java index e4c0d61c9..6ec66256d 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/LanguageSelectionListComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/LanguageSelectionListComponentProcessor.java @@ -6,12 +6,12 @@ */ package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.screenop.ComponentProcessor; -import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.OptionsSubScreenAccessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.utils.MinecraftUtil; +import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.OptionsSubScreenAccessor; import net.minecraft.client.Minecraft; public class LanguageSelectionListComponentProcessor implements ComponentProcessor { @@ -23,7 +23,7 @@ public LanguageSelectionListComponentProcessor(String code) { @Override public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { - if (ControlifyBindings.GUI_PRESS.on(controller).justPressed()) { + if (ControlifyActions.GUI_PRESS.on(controller).pulse().consumePulse()) { var minecraft = Minecraft.getInstance(); var languageManager = minecraft.getLanguageManager(); if (!code.equals(languageManager.getSelected())) { diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/OptionsScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/OptionsScreenProcessor.java index 02998f467..a79f3ea5c 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/OptionsScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/OptionsScreenProcessor.java @@ -8,7 +8,7 @@ import dev.isxander.controlify.api.gui.ButtonGuideApi; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.screenop.ScreenProcessor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.options.OptionsScreen; @@ -31,7 +31,7 @@ public void onWidgetRebuild() { if (doneButton != null) { ButtonGuideApi.addGuideToButton( doneButton, - ControlifyBindings.GUI_BACK, + ControlifyActions.GUI_BACK, ButtonGuidePredicate.always() ); } diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/OptionsSubScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/OptionsSubScreenProcessor.java index 8ee0e326c..8811196b7 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/OptionsSubScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/OptionsSubScreenProcessor.java @@ -8,7 +8,7 @@ import dev.isxander.controlify.api.gui.ButtonGuideApi; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.screenop.ScreenProcessor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.options.OptionsSubScreen; @@ -31,7 +31,7 @@ public void onWidgetRebuild() { if (doneButton != null) { ButtonGuideApi.addGuideToButton( doneButton, - ControlifyBindings.GUI_BACK, + ControlifyActions.GUI_BACK, ButtonGuidePredicate.always() ); } diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/PauseScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/PauseScreenProcessor.java index bcabb2fd6..c5b6194f5 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/PauseScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/PauseScreenProcessor.java @@ -8,15 +8,15 @@ import dev.isxander.controlify.api.gui.ButtonGuideApi; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.PauseScreenAccessor; import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.utils.MinecraftUtil; +import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.PauseScreenAccessor; import net.minecraft.client.gui.components.AbstractButton; import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.screens.options.OptionsScreen; import net.minecraft.client.gui.screens.PauseScreen; +import net.minecraft.client.gui.screens.options.OptionsScreen; import org.jetbrains.annotations.Nullable; import java.util.function.Supplier; @@ -33,10 +33,10 @@ public PauseScreenProcessor(PauseScreen screen, Supplier<@Nullable Button> disco protected void handleButtons(ControllerImpl controller) { super.handleButtons(controller); - if (ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).justPressed()) { + if (ControlifyActions.GUI_ABSTRACT_ACTION_1.on(controller).pulse().consumePulse()) { MinecraftUtil.setScreen(new OptionsScreen(screen, minecraft.options /*? if <26.3 >>*//*,true*/ )); } - if (ControlifyBindings.GUI_ABSTRACT_ACTION_2.on(controller).justPressed()) { + if (ControlifyActions.GUI_ABSTRACT_ACTION_2.on(controller).pulse().consumePulse()) { screen.setFocused(disconnectButtonSupplier.get()); } } @@ -49,14 +49,14 @@ public void onWidgetRebuild() { getWidget("menu.returnToGame").ifPresent(widget -> { ButtonGuideApi.addGuideToButton( (AbstractButton) widget, - ControlifyBindings.GUI_BACK, + ControlifyActions.GUI_BACK, ButtonGuidePredicate.always() ); }); getWidget("menu.options").ifPresent( widget -> { ButtonGuideApi.addGuideToButton( (AbstractButton) widget, - ControlifyBindings.GUI_ABSTRACT_ACTION_1, + ControlifyActions.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always() ); }); @@ -66,8 +66,8 @@ public void onWidgetRebuild() { ButtonGuideApi.addGuideToButton( disconnectButton, () -> disconnectButton.isFocused() - ? ControlifyBindings.GUI_PRESS - : ControlifyBindings.GUI_ABSTRACT_ACTION_2, + ? ControlifyActions.GUI_PRESS + : ControlifyActions.GUI_ABSTRACT_ACTION_2, ButtonGuidePredicate.always() ); } diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/RecipeBookScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/RecipeBookScreenProcessor.java index 84e6e4065..9e55e677a 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/RecipeBookScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/RecipeBookScreenProcessor.java @@ -7,22 +7,22 @@ package dev.isxander.controlify.impl.screenop.compat.vanilla; import com.mojang.blaze3d.platform.InputConstants; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.virtualmouse.VirtualMouseHandler; import dev.isxander.controlify.mixins.feature.virtualmouse.snapping.RecipeBookComponentAccessor; import dev.isxander.controlify.mixins.feature.virtualmouse.snapping.RecipeBookPageAccessor; -import dev.isxander.controlify.impl.virtualmouse.VirtualMouseHandler; import net.minecraft.client.gui.components.ImageButton; +import net.minecraft.client.gui.screens.inventory.AbstractRecipeBookScreen; import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent; import net.minecraft.client.gui.screens.recipebook.RecipeBookTabButton; +import net.minecraft.client.input.MouseButtonEvent; +import net.minecraft.client.input.MouseButtonInfo; +import net.minecraft.world.inventory.Slot; import java.util.List; import java.util.function.Predicate; import java.util.function.Supplier; -import net.minecraft.client.gui.screens.inventory.AbstractRecipeBookScreen; -import net.minecraft.client.input.MouseButtonEvent; -import net.minecraft.client.input.MouseButtonInfo; -import net.minecraft.world.inventory.Slot; public class RecipeBookScreenProcessor> extends AbstractContainerScreenProcessor { @@ -46,7 +46,11 @@ protected void handleScreenVMouse(ControllerImpl controller, VirtualMouseHandler RecipeBookComponent recipeBookComponent = recipeBookScreenAccessor.controlify$getRecipeBookComponent(); - if (!recipeBookComponent.isVisible()) return; + if (!recipeBookComponent.isVisible()) { + + return; + + } RecipeBookComponentAccessor componentAccessor = (RecipeBookComponentAccessor) recipeBookComponent; RecipeBookPageAccessor pageAccessor = (RecipeBookPageAccessor) componentAccessor.controlify$getRecipeBookPage(); List tabs = componentAccessor @@ -56,19 +60,19 @@ protected void handleScreenVMouse(ControllerImpl controller, VirtualMouseHandler RecipeBookTabButton selectedTab = componentAccessor.controlify$getSelectedTab(); ImageButton button = null; - if (ControlifyBindings.VMOUSE_PAGE_NEXT.on(controller).justPressed()) { + if (ControlifyActions.VMOUSE_PAGE_NEXT.on(controller).pulse().consumePulse()) { button = pageAccessor.controlify$getForwardButton(); } - if (ControlifyBindings.VMOUSE_PAGE_PREV.on(controller).justPressed()) { + if (ControlifyActions.VMOUSE_PAGE_PREV.on(controller).pulse().consumePulse()) { button = pageAccessor.controlify$getBackButton(); } - if (ControlifyBindings.VMOUSE_PAGE_DOWN.on(controller).justPressed()) { + if (ControlifyActions.VMOUSE_PAGE_DOWN.on(controller).pulse().consumePulse()) { int index = tabs.indexOf(selectedTab); if (index != tabs.size() - 1) { button = tabs.get(index + 1); } } - if (ControlifyBindings.VMOUSE_PAGE_UP.on(controller).justPressed()) { + if (ControlifyActions.VMOUSE_PAGE_UP.on(controller).pulse().consumePulse()) { int index = tabs.indexOf(selectedTab); if (index != 0) { button = tabs.get(index - 1); diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/SelectWorldScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/SelectWorldScreenProcessor.java index 854a4c1ba..f0e96e72f 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/SelectWorldScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/SelectWorldScreenProcessor.java @@ -6,11 +6,11 @@ */ package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.screenop.ScreenProcessor; -import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.SelectWorldScreenAccessor; import dev.isxander.controlify.impl.utils.MinecraftUtil; +import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.SelectWorldScreenAccessor; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; @@ -23,7 +23,7 @@ public SelectWorldScreenProcessor(SelectWorldScreen screen) { @Override protected void handleButtons(ControllerImpl controller) { - if (ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).justPressed()) { + if (ControlifyActions.GUI_ABSTRACT_ACTION_1.on(controller).pulse().consumePulse()) { playClackSound(); var minecraft = Minecraft.getInstance(); CreateWorldScreen.openFresh(minecraft, () -> MinecraftUtil.setScreen(screen)); @@ -31,7 +31,7 @@ protected void handleButtons(ControllerImpl controller) { } if (screen.getFocused() != null && screen.getFocused() instanceof Button) { - if (ControlifyBindings.GUI_BACK.on(controller).guiPressed().get()) { + if (ControlifyActions.GUI_BACK.on(controller).pulse().consumePulse()) { screen.setFocused(((SelectWorldScreenAccessor) screen).controlify$getList()); return; } diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ServerSelectionListEntryComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ServerSelectionListEntryComponentProcessor.java index 5aec5e5f0..0a3a79884 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ServerSelectionListEntryComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ServerSelectionListEntryComponentProcessor.java @@ -6,16 +6,16 @@ */ package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.JoinMultiplayerScreenAccessor; public class ServerSelectionListEntryComponentProcessor implements ComponentProcessor { @Override public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { - if (ControlifyBindings.GUI_PRESS.on(controller).guiPressed().get()) { + if (ControlifyActions.GUI_PRESS.on(controller).pulse().consumePulse()) { screen.screen.setFocused(((JoinMultiplayerScreenAccessor) screen.screen).controlify$getJoinButton()); return true; } diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/SliderComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/SliderComponentProcessor.java index 90dc8057a..99d0c96cb 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/SliderComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/SliderComponentProcessor.java @@ -7,10 +7,10 @@ package dev.isxander.controlify.impl.screenop.compat.vanilla; import com.mojang.blaze3d.platform.InputConstants; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; import net.minecraft.client.gui.components.AbstractSliderButton; import net.minecraft.client.input.KeyEvent; @@ -23,9 +23,6 @@ public class SliderComponentProcessor implements ComponentProcessor { private final AbstractSliderButton component; - private static final int SLIDER_CHANGE_DELAY = 1; - private int lastSliderChange = SLIDER_CHANGE_DELAY; - public SliderComponentProcessor(AbstractSliderButton component, Supplier canChangeValueGetter, Consumer canChangeValueSetter) { this.component = component; this.canChangeValueGetter = canChangeValueGetter; @@ -34,22 +31,16 @@ public SliderComponentProcessor(AbstractSliderButton component, Supplier screen, ControllerImpl controller) { - if (!this.canChangeValueGetter.get()) return false; - - var canSliderChange = ++lastSliderChange > SLIDER_CHANGE_DELAY; + if (!this.canChangeValueGetter.get()) { + return false; + } - if (ControlifyBindings.GUI_NAVI_RIGHT.on(controller).digitalNow()) { - if (canSliderChange) { - component.keyPressed(new KeyEvent(InputConstants.KEY_RIGHT, 0, 0)); - lastSliderChange = 0; - } + if (ControlifyActions.GUI_NAVI_RIGHT.on(controller).pulse().consumePulse()) { + component.keyPressed(new KeyEvent(InputConstants.KEY_RIGHT, 0, 0)); return true; - } else if (ControlifyBindings.GUI_NAVI_LEFT.on(controller).digitalNow()) { - if (canSliderChange) { - component.keyPressed(new KeyEvent(InputConstants.KEY_LEFT, 0, 0)); - lastSliderChange = 0; - } + } else if (ControlifyActions.GUI_NAVI_LEFT.on(controller).pulse().consumePulse()) { + component.keyPressed(new KeyEvent(InputConstants.KEY_LEFT, 0, 0)); return true; } @@ -59,9 +50,11 @@ public boolean overrideControllerNavigation(ScreenProcessor screen, Controlle @Override public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { - if (!this.canChangeValueGetter.get()) return false; + if (!this.canChangeValueGetter.get()) { + return false; + } - if (ControlifyBindings.GUI_BACK.on(controller).justPressed()) { + if (ControlifyActions.GUI_BACK.on(controller).pulse().consumePulse()) { this.canChangeValueSetter.accept(false); return true; } diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/TitleScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/TitleScreenProcessor.java index d5ff4a4d0..27281648f 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/TitleScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/TitleScreenProcessor.java @@ -8,13 +8,13 @@ import dev.isxander.controlify.api.gui.ButtonGuideApi; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.client.gui.components.AbstractButton; -import net.minecraft.client.gui.screens.options.OptionsScreen; import net.minecraft.client.gui.screens.TitleScreen; +import net.minecraft.client.gui.screens.options.OptionsScreen; public class TitleScreenProcessor extends ScreenProcessor { public TitleScreenProcessor(TitleScreen screen) { @@ -23,7 +23,7 @@ public TitleScreenProcessor(TitleScreen screen) { @Override protected void handleButtons(ControllerImpl controller) { - if (ControlifyBindings.GUI_BACK.on(controller).justPressed()) { + if (ControlifyActions.GUI_BACK.on(controller).pulse().consumePulse()) { getWidget("menu.quit").ifPresent(widget -> { screen.setFocused(widget); playClackSound(); @@ -32,7 +32,7 @@ protected void handleButtons(ControllerImpl controller) { super.handleButtons(controller); - if (ControlifyBindings.GUI_ABSTRACT_ACTION_1.on(controller).justPressed()) { + if (ControlifyActions.GUI_ABSTRACT_ACTION_1.on(controller).pulse().consumePulse()) { if (getWidget("menu.options").isPresent()) { MinecraftUtil.setScreen(new OptionsScreen(screen, minecraft.options /*? if <26.3 >>*//*,false*/ )); playClackSound(); @@ -48,7 +48,7 @@ public void onWidgetRebuild() { var button = (AbstractButton) widget; ButtonGuideApi.addGuideToButton( button, - () -> button.isFocused() ? ControlifyBindings.GUI_PRESS : ControlifyBindings.GUI_BACK, + () -> button.isFocused() ? ControlifyActions.GUI_PRESS : ControlifyActions.GUI_BACK, ButtonGuidePredicate.always() ); }); @@ -57,7 +57,7 @@ public void onWidgetRebuild() { var button = (AbstractButton) widget; ButtonGuideApi.addGuideToButton( button, - ControlifyBindings.GUI_ABSTRACT_ACTION_1, + ControlifyActions.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always() ); }); diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/WorldListEntryComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/WorldListEntryComponentProcessor.java index ca505e3ca..cd4980265 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/WorldListEntryComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/WorldListEntryComponentProcessor.java @@ -6,17 +6,17 @@ */ package dev.isxander.controlify.impl.screenop.compat.vanilla; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.screenop.ComponentProcessor; +import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.SelectWorldScreenAccessor; import net.minecraft.client.gui.screens.worldselection.SelectWorldScreen; public class WorldListEntryComponentProcessor implements ComponentProcessor { @Override public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { - if (ControlifyBindings.GUI_PRESS.on(controller).guiPressed().get()) { + if (ControlifyActions.GUI_PRESS.on(controller).pulse().consumePulse()) { var selectWorldScreen = (SelectWorldScreen) screen.screen; selectWorldScreen.setFocused(((SelectWorldScreenAccessor) selectWorldScreen).controlify$getPlayWorldButton()); diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/CommonKeyboardHints.java b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/CommonKeyboardHints.java index d3bf8e35f..e740b8732 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/CommonKeyboardHints.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/CommonKeyboardHints.java @@ -6,7 +6,8 @@ */ package dev.isxander.controlify.impl.screenop.keyboard; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.api.font.ActionGlyphs; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.utils.LazyComponentDims; import net.minecraft.network.chat.Component; @@ -14,14 +15,15 @@ public final class CommonKeyboardHints { public static final LazyComponentDims TEXT_CURSOR = new LazyComponentDims( Component.translatable("controlify.hint.keyboard_cursor_movement", - ControlifyBindings.GUI_PREV_TAB.inputGlyph(), - ControlifyBindings.GUI_NEXT_TAB.inputGlyph()) + ActionGlyphs.action(ControlifyActions.GUI_PREV_TAB.actionId()), + ActionGlyphs.action(ControlifyActions.GUI_NEXT_TAB.actionId())) ); public static final LazyComponentDims OPEN_KEYBOARD = new LazyComponentDims( Component.translatable("controlify.hint.edit_box_keyboard", - ControlifyBindings.GUI_PRESS.inputGlyph()) + ActionGlyphs.action(ControlifyActions.GUI_PRESS.actionId())) ); - private CommonKeyboardHints() {} + private CommonKeyboardHints() { + } } diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/FallbackKeyboardLayout.java b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/FallbackKeyboardLayout.java index d586e974f..888f411c7 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/FallbackKeyboardLayout.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/FallbackKeyboardLayout.java @@ -6,18 +6,17 @@ */ package dev.isxander.controlify.impl.screenop.keyboard; -import dev.isxander.controlify.impl.api.bind.InputBindingSupplier; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.api.action.ActionAccessor; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; +import static dev.isxander.controlify.impl.screenop.keyboard.KeyboardLayout.Key; +import static dev.isxander.controlify.impl.screenop.keyboard.KeyboardLayout.KeyFunction.*; import java.util.List; import java.util.Optional; -import static dev.isxander.controlify.impl.screenop.keyboard.KeyboardLayout.Key; -import static dev.isxander.controlify.impl.screenop.keyboard.KeyboardLayout.KeyFunction.*; - /** * A code-defined fallback keyboard layout if the resource-pack driven ones * are not available or the layout required is not defined. @@ -38,7 +37,7 @@ public final class FallbackKeyboardLayout { k("i"), k("o"), k("p"), - k(SpecialFunc.Action.BACKSPACE, 2.0f, ControlifyBindings.GUI_ABSTRACT_ACTION_1) + k(SpecialFunc.Action.BACKSPACE, 2.0f, ControlifyActions.GUI_ABSTRACT_ACTION_1) ), List.of( k(SpecialFunc.Action.TAB, 1f, null), @@ -52,10 +51,10 @@ public final class FallbackKeyboardLayout { k("k"), k("l"), k("'", "\""), - k(SpecialFunc.Action.ENTER, 2.0f, ControlifyBindings.GUI_ABSTRACT_ACTION_2) + k(SpecialFunc.Action.ENTER, 2.0f, ControlifyActions.GUI_ABSTRACT_ACTION_2) ), List.of( - k(SpecialFunc.Action.SHIFT, 2f, ControlifyBindings.GUI_ABSTRACT_ACTION_3), + k(SpecialFunc.Action.SHIFT, 2f, ControlifyActions.GUI_ABSTRACT_ACTION_3), k("z"), k("x"), k("c"), @@ -75,7 +74,7 @@ public final class FallbackKeyboardLayout { ) ); - private static Key k(SpecialFunc.Action action, float width, @Nullable InputBindingSupplier shortcutBinding) { + private static Key k(SpecialFunc.Action action, float width, @Nullable ActionAccessor shortcutBinding) { return new Key(new SpecialFunc(action), width, Optional.ofNullable(shortcutBinding)); } private static Key k(String string) { diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyWidget.java b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyWidget.java index 9151926cd..b06786da8 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyWidget.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyWidget.java @@ -7,11 +7,11 @@ package dev.isxander.controlify.impl.screenop.keyboard; import com.mojang.blaze3d.platform.InputConstants; +import dev.isxander.controlify.api.action.Action; +import dev.isxander.controlify.api.font.ActionGlyphs; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.impl.api.bind.InputBinding; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.font.BindingFontHelper; import dev.isxander.controlify.impl.screenop.ComponentProcessor; import dev.isxander.controlify.impl.screenop.ScreenControllerEventListener; import dev.isxander.controlify.impl.screenop.ScreenProcessor; @@ -41,10 +41,12 @@ public class KeyWidget extends AbstractWidget implements ComponentProcessor, Scr private final Component regularLabel, shiftedLabel; private final boolean supportsRegular, supportsShifted; + private boolean mousePressed; + private boolean buttonPressed; private boolean shortcutPressed; - private final HoldRepeatHelper holdRepeatHelper; - - private boolean buttonPressed, mousePressed; + private boolean ignoreHeldPress; + private final HoldRepeatHelper focusedRepeatHelper = new HoldRepeatHelper(10, 2); + private final HoldRepeatHelper shortcutRepeatHelper = new HoldRepeatHelper(10, 2); private final int renderScale; private final int renderWidth, renderHeight; @@ -53,7 +55,6 @@ public KeyWidget(int x, int y, int width, int height, int renderScale, KeyboardL super(x, y, width, height, Component.literal("Key")); this.keyboard = keyboard; this.key = key; - this.holdRepeatHelper = new HoldRepeatHelper(10, 2); this.regularLabel = createLabel(key, false); this.shiftedLabel = createLabel(key, true); @@ -67,9 +68,7 @@ public KeyWidget(int x, int y, int width, int height, int renderScale, KeyboardL public void extractKeyBackground(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { this.active = this.supportsAction(); - if (!this.isFocused()) { - // the call in overrideControllerButtons won't be triggered to un-press if the key is not focused this.buttonPressed = false; } @@ -86,8 +85,6 @@ public void extractKeyBackground(GuiGraphicsExtractor graphics, int mouseX, int if (isHoveredOrFocused()) { graphics.outline(getX() - 1, getY() - 1, renderWidth + 2, renderHeight + 2, 0x80FFFFFF); - } else if (!shortcutPressed) { - this.holdRepeatHelper.reset(); } if (!this.active) { @@ -116,37 +113,38 @@ protected void extractWidgetRenderState(@NonNull GuiGraphicsExtractor graphics, } @Override - public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { - var guiPress = ControlifyBindings.GUI_PRESS.on(controller); + public void onFocusGained(ScreenProcessor screen, ControllerImpl controller) { + this.ignoreHeldPress = ControlifyActions.GUI_PRESS.on(controller).latch().isLatchActive(); + this.buttonPressed = false; + this.focusedRepeatHelper.reset(); + } - // this means if you were holding the button down and navigated to this key, - // it would not show as pressed until you release and press again - if (guiPress.justPressed()) { - this.buttonPressed = true; - } else if (guiPress.justReleased()) { - this.buttonPressed = false; + @Override + public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { + Action guiPress = ControlifyActions.GUI_PRESS.on(controller); + boolean held = guiPress.latch().isLatchActive(); + if (!held) { + this.ignoreHeldPress = false; } - - // prevent the press action if the button was navigated to whilst holding the button down - // the above visual state will not be pressed, so this should not trigger either - if (this.buttonPressed && holdRepeatHelper.shouldAction(guiPress)) { + this.buttonPressed = held && !this.ignoreHeldPress; + if (this.focusedRepeatHelper.shouldAction(this.buttonPressed)) { onPress(); - holdRepeatHelper.onNavigate(); } - + while (guiPress.pulse().consumePulse()) { + // The keyboard handles this action through its latch, including local repetition. + } return false; } @Override public void onControllerInput(ControllerImpl controller) { - this.key.shortcutBinding().ifPresent(supplier -> { - InputBinding shortcutBinding = supplier.on(controller); - - this.shortcutPressed = shortcutBinding.digitalNow(); - - if (this.holdRepeatHelper.shouldAction(shortcutBinding)) { + this.key.shortcutBinding().ifPresent(accessor -> { + Action shortcut = accessor.on(controller); + this.shortcutPressed = shortcut.latch().isLatchActive(); + boolean repeat = this.shortcutRepeatHelper.shouldAction(this.shortcutPressed); + boolean pressed = shortcut.pulse().consumePulse(); + if (pressed || repeat) { onPress(); - this.holdRepeatHelper.onNavigate(); } }); } @@ -257,9 +255,9 @@ public KeyboardLayout.KeyFunction getKeyFunction() { } public boolean isVisuallyPressed() { - return this.buttonPressed + return this.mousePressed + || this.buttonPressed || this.shortcutPressed - || this.mousePressed || this.isShiftKeyAndShifting() || this.isShiftLockKeyAndShiftLocked(); } @@ -306,7 +304,6 @@ private void changeLayout(Identifier layoutId) { @Override protected void updateWidgetNarration(NarrationElementOutput narrationElementOutput) { - } private void doScaledExtraction(GuiGraphicsExtractor graphics, Runnable runnable) { @@ -343,7 +340,7 @@ private static Component createLabel(KeyboardLayout.Key key, boolean shift) { KeyboardLayout.KeyFunction keyFunction = key.getFunction(shift); return key.shortcutBinding() - .map(b -> BindingFontHelper.binding(b.bindId())) + .map(b -> ActionGlyphs.action(b.actionId())) .map(glyph -> Component.empty() .append(glyph) .append(" ") diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayout.java b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayout.java index ee02c6fd5..8a6e01b1f 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayout.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayout.java @@ -12,7 +12,7 @@ import com.mojang.serialization.DataResult; import com.mojang.serialization.DynamicOps; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.isxander.controlify.impl.api.bind.InputBindingSupplier; +import dev.isxander.controlify.api.action.ActionAccessor; import dev.isxander.controlify.impl.utils.codec.CExtraCodecs; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; @@ -84,7 +84,7 @@ public static KeyboardLayout of(float width, List... keys) { * @param shortcutBinding an optional shortcut binding for the key, used to display a shortcut in the UI * @param identifier an optional identifier for the key, used when changing layouts to focus a specific key with a matching identifier */ - public record Key(KeyFunction regular, KeyFunction shifted, float width, Optional shortcutBinding, @Nullable String identifier) { + public record Key(KeyFunction regular, KeyFunction shifted, float width, Optional shortcutBinding, @Nullable String identifier) { private static final Codec WIDTH_CODEC = Codec.floatRange(0.1f, Float.MAX_VALUE); private static final Codec PAIR_CODEC = Codec.withAlternative( @@ -92,7 +92,7 @@ public record Key(KeyFunction regular, KeyFunction shifted, float width, Optiona KeyFunction.CODEC.fieldOf("regular").forGetter(Key::regular), KeyFunction.CODEC.optionalFieldOf("shifted").forGetter(k -> Optional.of(k.shifted)), WIDTH_CODEC.optionalFieldOf("width", 1f).forGetter(Key::width), - InputBindingSupplier.CODEC.optionalFieldOf("shortcut").forGetter(Key::shortcutBinding), + ActionAccessor.CODEC.optionalFieldOf("shortcut").forGetter(Key::shortcutBinding), Codec.STRING.optionalFieldOf("identifier").forGetter(k -> Optional.ofNullable(k.identifier)) ).apply(instance, Key::fromCodec)), CExtraCodecs.arrayPair(KeyFunction.CODEC, Key::regular, Key::shifted, Key::new) @@ -112,7 +112,7 @@ public Key(KeyFunction keyFunction, float width) { this(keyFunction, width, Optional.empty()); } - public Key(KeyFunction keyFunction, float width, Optional shortcut) { + public Key(KeyFunction keyFunction, float width, Optional shortcut) { this(keyFunction, keyFunction.createShifted(), width, shortcut, null); } @@ -120,7 +120,7 @@ public Key(KeyFunction regular, KeyFunction shifted) { this(regular, shifted, 1f, Optional.empty(), null); } - private static Key fromCodec(KeyFunction regular, Optional shifted, float width, Optional shortcut, Optional identifier) { + private static Key fromCodec(KeyFunction regular, Optional shifted, float width, Optional shortcut, Optional identifier) { return new Key( regular, shifted.orElseGet(regular::createShifted), diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayoutManager.java b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayoutManager.java index 67cbd594b..e022092e3 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayoutManager.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayoutManager.java @@ -23,7 +23,9 @@ import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Manages and loads all keyboard layouts from resource packs. @@ -46,14 +48,14 @@ public CompletableFuture load(ResourceManager manager, Executor ex .toList(); var map = Util.sequence(futures) - .thenApply(listOfEntries -> listOfEntries.stream() + .thenApply(listOfEntries -> listOfEntries.stream().flatMap(Function.identity()) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a))); return map.thenApply(Preparations::new); }); } - private CompletableFuture> loadLayout( + private CompletableFuture>> loadLayout( Identifier file, Resource resource, Executor executor ) { return CompletableFuture.supplyAsync(() -> { @@ -64,9 +66,10 @@ private CompletableFuture> loadLayo KeyboardLayout layout = KeyboardLayout.CODEC.parse(JsonOps.INSTANCE, json) .getOrThrow(reason -> new RuntimeException("Failed to parse keyboard layout " + key + ": " + reason)); - return Map.entry(key, layout); + return Stream.of(Map.entry(key, layout)); } catch (Exception e) { - throw new RuntimeException("Failed to read keyboard layout " + key + ": " + e.getMessage(), e); + LOGGER.error("Failed to read keyboard layout {}", e, key); + return Stream.empty(); } }, executor); } @@ -105,8 +108,8 @@ private static Identifier keyToFile(KeyboardLayoutKey key) { private static KeyboardLayoutKey fileToKey(Identifier file) { try { var components = file.getPath().split("/"); - var layoutPath = components[1]; - var languageCodeWithExt = components[2]; + var layoutPath = components[2]; + var languageCodeWithExt = components[3]; var languageCode = languageCodeWithExt.substring(0, languageCodeWithExt.lastIndexOf('.')); var layoutId = file.withPath(layoutPath); diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardWidget.java b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardWidget.java index 67a3c5006..2cd950f21 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardWidget.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardWidget.java @@ -8,7 +8,7 @@ import com.google.common.collect.ImmutableList; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.screenop.ComponentProcessor; import dev.isxander.controlify.impl.screenop.ScreenProcessor; @@ -51,8 +51,8 @@ public class KeyboardWidget extends AbstractWidget implements ContainerEventHand private @Nullable KeyWidget focused; private boolean isDragging; - private final HoldRepeatHelper fwdCursorHelper = new HoldRepeatHelper(10, 2); - private final HoldRepeatHelper bwdCursorHelper = new HoldRepeatHelper(10, 2); + private final HoldRepeatHelper forwardCursorRepeat = new HoldRepeatHelper(10, 2); + private final HoldRepeatHelper backwardCursorRepeat = new HoldRepeatHelper(10, 2); public KeyboardWidget(int x, int y, int width, int height, KeyboardLayoutWithId layout, InputTarget inputConsumer) { super(x, y, width, height, Component.literal("On-Screen Keyboard")); @@ -150,42 +150,38 @@ protected void extractWidgetRenderState(@NonNull GuiGraphicsExtractor graphics, public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { InputTarget inputTarget = this.getInputTarget(); var settings = controller.settings().generic; - - if (inputTarget.supportsCursorMovement()) { - if (this.fwdCursorHelper.shouldAction(ControlifyBindings.GUI_NEXT_TAB.on(controller))) { - if (inputTarget.moveCursor(1)) { - ScreenProcessor.playFocusChangeSound(); - this.fwdCursorHelper.onNavigate(); - this.bwdCursorHelper.reset(); - - if (settings.keyboard.hintCursor && settings.guide.showScreenGuides) { - settings.keyboard.hintCursor = false; - Controlify.instance().config().saveSafely(); - } - - return true; - } + boolean supportsCursor = inputTarget.supportsCursorMovement(); + boolean forward = this.forwardCursorRepeat.shouldAction(supportsCursor + && ControlifyActions.GUI_NEXT_TAB.on(controller).latch().isLatchActive()); + boolean backward = this.backwardCursorRepeat.shouldAction(supportsCursor + && ControlifyActions.GUI_PREV_TAB.on(controller).latch().isLatchActive()); + boolean movedCursor = false; + if (supportsCursor) { + while (ControlifyActions.GUI_NEXT_TAB.on(controller).pulse().consumePulse()) { + // Cursor movement uses held state so tab pulses must not also navigate the screen. + } + while (ControlifyActions.GUI_PREV_TAB.on(controller).pulse().consumePulse()) { + // Cursor movement uses held state so tab pulses must not also navigate the screen. } - if (this.bwdCursorHelper.shouldAction(ControlifyBindings.GUI_PREV_TAB.on(controller))) { - if (inputTarget.moveCursor(-1)) { - ScreenProcessor.playFocusChangeSound(); - this.bwdCursorHelper.onNavigate(); - this.fwdCursorHelper.reset(); - - if (settings.keyboard.hintCursor && settings.guide.showScreenGuides) { - settings.keyboard.hintCursor = false; - Controlify.instance().config().saveSafely(); - } - - return true; - } + if (forward) { + movedCursor = inputTarget.moveCursor(1); + } else if (backward) { + movedCursor = inputTarget.moveCursor(-1); + } + } + if (movedCursor) { + ScreenProcessor.playFocusChangeSound(); + if (settings.keyboard.hintCursor && settings.guide.showScreenGuides) { + settings.keyboard.hintCursor = false; + Controlify.instance().config().saveSafely(); } } - this.keys.forEach(k -> k.onControllerInput(controller)); + this.keys.forEach(key -> key.onControllerInput(controller)); - // prevent default button handling for gui press which would send enter which is most likely submit - return ControlifyBindings.GUI_PRESS.on(controller).guiPressed().get(); + // Suppress default Enter handling while the keyboard processes the shared GUI action. + boolean pressed = ControlifyActions.GUI_PRESS.on(controller).pulse().consumePulse(); + return movedCursor || pressed; } public void setShifted(boolean shifted) { @@ -237,7 +233,6 @@ private Optional findKey(boolean skipSearch, Predicate pre @Override protected void updateWidgetNarration(NarrationElementOutput narrationElementOutput) { - } @Override diff --git a/src/main/java/dev/isxander/controlify/impl/utils/ControllerUtils.java b/src/main/java/dev/isxander/controlify/impl/utils/ControllerUtils.java index 2c39d1f9e..108840fb4 100644 --- a/src/main/java/dev/isxander/controlify/impl/utils/ControllerUtils.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/ControllerUtils.java @@ -25,7 +25,7 @@ public static String createControllerString(ControllerImpl controller) { controller.name(), controller.info().ucid().toString(), controller.info().hid().toString(), - controller.info().type().friendlyName() + controller.info().type().friendlyName().orElse(null) ); } @@ -36,23 +36,12 @@ public static void wrapControllerError(Runnable runnable, String errorTitle, Con CrashReport crashReport = CrashReport.forThrowable(e, errorTitle); CrashReportCategory category = crashReport.addCategory("Affected controller"); category.setDetail("Controller name", controller.name()); - category.setDetail("Controller identification", controller.driverName()); + category.setDetail("Controller identification", controller.driverReportedName()); category.setDetail("Controller type", controller.getClass().getCanonicalName()); throw new ReportedException(crashReport); } } - public static float deadzone(float value, float deadzone) { - return (value - Math.copySign(Math.min(deadzone, Math.abs(value)), value)) / (1 - deadzone); - } - - public static float applyCircularityX(float x, float y) { - return (float) (x * Math.sqrt(1 - (y * y) / 2)); - } - public static float applyCircularityY(float x, float y) { - return (float) (y * Math.sqrt(1 - (x * x) / 2)); - } - public static Vector2d applyEasingToLength(double x, double y, Function easing) { double length = Math.sqrt(x * x + y * y); if(length == 0.0) diff --git a/src/main/java/dev/isxander/controlify/impl/utils/DebugDump.java b/src/main/java/dev/isxander/controlify/impl/utils/DebugDump.java index fac722efd..09bf31e04 100644 --- a/src/main/java/dev/isxander/controlify/impl/utils/DebugDump.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/DebugDump.java @@ -9,6 +9,9 @@ import com.sun.jna.Platform; import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controller.input.InputComponentImpl; +import dev.isxander.controlify.impl.controllermanager.ControllerBackend; +import dev.isxander.controlify.impl.controllermanager.ControllerCoordinator; import dev.isxander.controlify.impl.controllermanager.ControllerManager; import dev.isxander.controlify.impl.platform.Environment; import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; @@ -43,50 +46,50 @@ private static void dumpClientDebug(IndentedStringBuilder dump) { dump.line("Platform: ", Platform.RESOURCE_PREFIX); dump.line(); - Optional controllerManagerOpt = Controlify.instance().getControllerManager(); - if (controllerManagerOpt.isPresent()) { - ControllerManager controllerManager = controllerManagerOpt.get(); - dump.line("Controller-manager: ", controllerManager.getClass().getName()); - - dump.line("Connected Controllers:").pushIndent(); - for (ControllerImpl controller : controllerManager.getConnectedControllers()) { - dump.line("Name: ", controller.name()); - dump.line("Identified type: ", controller.info().type()); - dump.line("GUID: ", controller.guid()); - dump.line("UID: ", controller.uid()); - dump.line("UCID: ", controller.info().ucid()); - controller.info().hid().ifPresent(hid -> dump.line("HID: ", hid.hidid())); - - controller.input().ifPresentOrElse(input -> { + Optional controllerCoordinatorOpt = Controlify.instance().getControllerCoordinator(); + if (controllerCoordinatorOpt.isPresent()) { + ControllerCoordinator controllerCoordinator = controllerCoordinatorOpt.get(); + ControllerBackend backend = controllerCoordinator.backend(); + dump.line("Controller backend: ", backend != null ? backend.getClass().getName() : "null"); + + Optional controllerManagerOpt = Controlify.instance().getControllerManager(); + if (controllerManagerOpt.isPresent()) { + ControllerManager controllerManager = controllerManagerOpt.get(); + + dump.line("Connected Controllers:").pushIndent(); + for (ControllerImpl controller : controllerManager.getAllControllers()) { + dump.line("Name: ", controller.name()); + dump.line("Identified type: ", controller.info().type()); + dump.line("UID: ", controller.uid()); + dump.line("UCID: ", controller.info().ucid()); + dump.line("HID: ", controller.info().hid()); + + InputComponentImpl input = controller.input(); dump.line("Input Component:").pushIndent(); - - dump.line("Definitely gamepad: ", input.isDefinitelyGamepad()); + dump.line("Definitely gamepad: ", input.isGamepad()); dump.line("Available inputs:").pushIndent(); - for (Identifier button : input.stateNow().getButtons()) { + for (Identifier button : input.physicalButtons()) { dump.line("BTN ", button); } - for (Identifier axis : input.stateNow().getAxes()) { + for (Identifier axis : input.physicalAxes()) { dump.line("AXS ", axis); } - for (Identifier hat : input.stateNow().getHats()) { - dump.line("HAT ", hat); - } dump.popIndent(); // available inputs dump.popIndent(); // input component - }, () -> dump.line("Input Component: UNSUPPORTED")); - - dump.line("Rumble supported: ", controller.rumble().isPresent()); - dump.line("Trigger rumble supported: ", controller.triggerRumble().isPresent()); - dump.line("Battery level: ", controller.batteryLevel().map(b -> b.getBatteryLevel().toString()).orElse("UNSUPPORTED")); - dump.line("Gyro supported: ", controller.gyro().isPresent()); - dump.line("Touchpads supported: ", controller.touchpad().map(touchpad -> touchpad.touchpads().length).orElse(0)); - dump.line("HD haptics supported: ", controller.hdHaptics().isPresent()); - dump.line("Log:").pushIndent().line(controller.getLogger().export()).popIndent(); - dump.line(); + dump.line("Rumble supported: ", controller.rumble().isPresent()); + dump.line("Trigger rumble supported: ", controller.triggerRumble().isPresent()); + dump.line("Battery level: ", controller.batteryLevel().map(b -> b.getBatteryLevel().toString()).orElse("UNSUPPORTED")); + dump.line("Gyro supported: ", controller.gyro().isPresent()); + dump.line("Touchpads supported: ", controller.touchpad().map(touchpad -> touchpad.touchpads().length).orElse(0)); + dump.line("HD haptics supported: ", controller.hdHaptics().isPresent()); + dump.line("Log:").pushIndent().line(controller.logger().export()).popIndent(); + + dump.line(); + } + dump.popIndent(); // connected controllers } - dump.popIndent(); // connected controllers } } diff --git a/src/main/java/dev/isxander/controlify/impl/utils/HoldRepeatHelper.java b/src/main/java/dev/isxander/controlify/impl/utils/HoldRepeatHelper.java index 366d161e3..95eee4b5f 100644 --- a/src/main/java/dev/isxander/controlify/impl/utils/HoldRepeatHelper.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/HoldRepeatHelper.java @@ -6,46 +6,39 @@ */ package dev.isxander.controlify.impl.utils; -import dev.isxander.controlify.impl.api.bind.InputBinding; - -public class HoldRepeatHelper { - private final int initialDelay, repeatDelay; - private int currentDelay; - - private boolean hasResetThisTick = false; +/// Consumer-local repetition for shared actions whose general-purpose activator must not repeat. +/// Call once per fixed update, including while inactive. Delays are in fixed-update ticks. +/// Dedicated repeat actions should use the repeat-latch activator instead. +public final class HoldRepeatHelper { + private final int initialDelay; + private final int repeatDelay; + private int remainingDelay; + private boolean previouslyActive; public HoldRepeatHelper(int initialDelay, int repeatDelay) { this.initialDelay = initialDelay; this.repeatDelay = repeatDelay; - this.currentDelay = 0; - } - - public boolean canNavigate() { - return --currentDelay <= 0; - } - - public void reset() { - currentDelay = initialDelay; - hasResetThisTick = true; } - public void clearDelay() { - currentDelay = 0; - } - - public void onNavigate() { - if (!hasResetThisTick) { - currentDelay = repeatDelay; - } else { - hasResetThisTick = false; + public boolean shouldAction(boolean active) { + if (!active) { + this.reset(); + return false; + } + if (!this.previouslyActive) { + this.previouslyActive = true; + this.remainingDelay = this.initialDelay; + return true; } + if (--this.remainingDelay <= 0) { + this.remainingDelay = this.repeatDelay; + return true; + } + return false; } - public boolean shouldAction(InputBinding binding) { - boolean shouldAction = binding.digitalNow() && (canNavigate() || !binding.digitalPrev()); - if (shouldAction && !binding.digitalPrev()) { - reset(); - } - return shouldAction; + public void reset() { + this.previouslyActive = false; + this.remainingDelay = 0; } } diff --git a/src/main/java/dev/isxander/controlify/impl/utils/LazyComponentDims.java b/src/main/java/dev/isxander/controlify/impl/utils/LazyComponentDims.java index 92bd98793..06342b2ec 100644 --- a/src/main/java/dev/isxander/controlify/impl/utils/LazyComponentDims.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/LazyComponentDims.java @@ -8,7 +8,7 @@ import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.controllermanager.ControllerType; -import dev.isxander.controlify.impl.font.BindingFontHelper; +import dev.isxander.controlify.impl.font.ComponentFontHelper; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.network.chat.Component; @@ -49,7 +49,7 @@ public int getHeight() { int newCacheKey = getCacheKey(); if (this.height == -1 || this.cacheKey != newCacheKey) { this.cacheKey = newCacheKey; - this.height = BindingFontHelper.getComponentHeight(this.font, this.component); + this.height = ComponentFontHelper.getComponentHeight(this.font, this.component); } return this.height; } diff --git a/src/main/java/dev/isxander/controlify/impl/utils/PrecomputedComponentDims.java b/src/main/java/dev/isxander/controlify/impl/utils/PrecomputedComponentDims.java index ae5bfe9a2..347bbf5b5 100644 --- a/src/main/java/dev/isxander/controlify/impl/utils/PrecomputedComponentDims.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/PrecomputedComponentDims.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.impl.utils; -import dev.isxander.controlify.impl.font.BindingFontHelper; +import dev.isxander.controlify.impl.font.ComponentFontHelper; import net.minecraft.client.gui.Font; import net.minecraft.network.chat.Component; import net.minecraft.util.FormattedCharSequence; @@ -14,13 +14,13 @@ public record PrecomputedComponentDims(T component, int width, int height) { public static PrecomputedComponentDims of(Component component, Font font) { int width = font.width(component); - int height = BindingFontHelper.getComponentHeight(font, component); + int height = ComponentFontHelper.getComponentHeight(font, component); return new PrecomputedComponentDims<>(component, width, height); } public static PrecomputedComponentDims of(FormattedCharSequence component, Font font) { int width = font.width(component); - int height = BindingFontHelper.getComponentHeight(font, component); + int height = ComponentFontHelper.getComponentHeight(font, component); return new PrecomputedComponentDims<>(component, width, height); } } diff --git a/src/main/java/dev/isxander/controlify/impl/utils/ResizableRingBuffer.java b/src/main/java/dev/isxander/controlify/impl/utils/ResizableRingBuffer.java deleted file mode 100644 index 24eab2379..000000000 --- a/src/main/java/dev/isxander/controlify/impl/utils/ResizableRingBuffer.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.utils; - -import java.util.function.Supplier; - -public class ResizableRingBuffer { - private T[] elements; - - private int head; - private int tail; - - private int size; - - private final Supplier def; - - public ResizableRingBuffer(int initialSize, Supplier def) { - this.size = initialSize; - //noinspection unchecked - this.elements = (T[]) new Object[initialSize]; - this.head = 0; - this.tail = head + size - 1; - this.def = def; - } - - public void push(T element) { - head = wrapIndex(head + 1); - tail = wrapIndex(tail + 1); - elements[tail] = element; - } - - public T head() { - return get(head); - } - - public T head(int offset) { - return get(wrapIndex(head + offset)); - } - - public T tail() { - return get(tail); - } - - public T tail(int offset) { - return get(wrapIndex(tail - offset)); - } - - private T get(int index) { - T obj = elements[index]; - if (obj == null) return def.get(); - return obj; - } - - private int wrapIndex(int index) { - if (index < 0) index += size; - if (index >= size) index -= size; - return index; - } - - public void setSize(int newSize) { - if (this.size == newSize) - return; - - //noinspection unchecked - T[] newElements = (T[]) new Object[newSize]; - for (int i = 0; i < Math.min(size, newSize); i++) { - newElements[size - 1 - i] = elements[wrapIndex(tail - i + size)]; - } - tail = 0; - head = size - 1; - this.elements = newElements; - this.size = newSize; - } - - public int size() { - return this.size; - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/utils/TrackingBodySubscriber.java b/src/main/java/dev/isxander/controlify/impl/utils/TrackingBodySubscriber.java deleted file mode 100644 index 2777b3b37..000000000 --- a/src/main/java/dev/isxander/controlify/impl/utils/TrackingBodySubscriber.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.utils; - -import java.net.http.HttpResponse; -import java.nio.ByteBuffer; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.Flow; - -public class TrackingBodySubscriber implements HttpResponse.BodySubscriber { - private final HttpResponse.BodySubscriber delegate; - private final TrackingConsumer consumer; - - private long receivedBytes; - private final long contentLengthIfKnown; - - public TrackingBodySubscriber(HttpResponse.BodySubscriber delegate, TrackingConsumer consumer, long contentLengthIfKnown) { - this.delegate = delegate; - this.consumer = consumer; - this.contentLengthIfKnown = contentLengthIfKnown; - } - - @Override - public CompletionStage getBody() { - return delegate.getBody(); - } - - @Override - public void onSubscribe(Flow.Subscription subscription) { - consumer.start().accept(contentLengthIfKnown); - delegate.onSubscribe(subscription); - } - - @Override - public void onNext(List item) { - receivedBytes += countBytes(item); - delegate.onNext(item); - consumer.progressConsumer().accept(receivedBytes, contentLengthIfKnown); - } - - @Override - public void onError(Throwable throwable) { - consumer.onComplete().accept(Optional.of(throwable)); - delegate.onError(throwable); - } - - @Override - public void onComplete() { - consumer.onComplete().accept(Optional.empty()); - delegate.onComplete(); - } - - private long countBytes(List buffers) { - return buffers.stream().mapToLong(ByteBuffer::remaining).sum(); - } - - public static HttpResponse.BodyHandler bodyHandler(HttpResponse.BodyHandler delegate, TrackingConsumer consumer) { - return (responseInfo) -> new TrackingBodySubscriber<>(delegate.apply(responseInfo), consumer, responseInfo.headers().firstValueAsLong("Content-Length").orElse(-1L)); - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/utils/TrackingConsumer.java b/src/main/java/dev/isxander/controlify/impl/utils/TrackingConsumer.java deleted file mode 100644 index 94e29ff7b..000000000 --- a/src/main/java/dev/isxander/controlify/impl/utils/TrackingConsumer.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.utils; - -import java.util.Optional; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -public record TrackingConsumer(Consumer start, BiConsumer progressConsumer, Consumer> onComplete) { -} diff --git a/src/main/java/dev/isxander/controlify/impl/utils/UnhandledCompletableFutures.java b/src/main/java/dev/isxander/controlify/impl/utils/UnhandledCompletableFutures.java deleted file mode 100644 index 98ba6da8c..000000000 --- a/src/main/java/dev/isxander/controlify/impl/utils/UnhandledCompletableFutures.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.utils; - -import net.minecraft.ReportedException; -import net.minecraft.client.Minecraft; - -import java.util.concurrent.CompletableFuture; -import java.util.function.Supplier; - -/** - * Returns a CompleteableFuture that does not catch Throwables, only Exceptions - */ -public final class UnhandledCompletableFutures { - public static CompletableFuture run(Runnable runnable, Minecraft executor) { - return supply(() -> { - runnable.run(); - return null; - }, executor); - } - - public static CompletableFuture supply(Supplier supplier, Minecraft executor) { - var future = new CompletableFuture(); - executor.schedule(() -> { - try { - future.complete(supplier.get()); - } catch (Exception e) { // allows Throwable to go uncaught - future.completeExceptionally(e); - - if (e instanceof ReportedException) { - throw e; // rethrow a crash report - } - } - }); - return future; - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/utils/event/EventBus.java b/src/main/java/dev/isxander/controlify/impl/utils/event/EventBus.java new file mode 100644 index 000000000..11e379ce7 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/utils/event/EventBus.java @@ -0,0 +1,41 @@ +package dev.isxander.controlify.impl.utils.event; + +import dev.isxander.controlify.api.utils.event.EventSink; +import dev.isxander.controlify.api.utils.event.EventSource; +import org.jspecify.annotations.NonNull; + +import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; + +/// Synchronous subscription-order dispatch with a snapshot of listeners per emission. +/// Subscription changes apply to later emissions; listener exceptions propagate. +public class EventBus implements EventSource { + private final CopyOnWriteArrayList listeners = new CopyOnWriteArrayList<>(); + + /// Notifies subscribers without forwarding to another stage. + public void emit(T event) { + for (Registration registration : this.listeners) { + registration.listener.accept(event); + } + } + + @Override + public @NonNull Subscription subscribe(@NonNull EventSink listener) { + Registration registration = new Registration(Objects.requireNonNull(listener, "listener")); + this.listeners.add(registration); + return registration; + } + + private final class Registration implements Subscription { + private final EventSink listener; + + private Registration(EventSink listener) { + this.listener = listener; + } + + @Override + public void close() { + EventBus.this.listeners.remove(this); + } + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/utils/event/EventTap.java b/src/main/java/dev/isxander/controlify/impl/utils/event/EventTap.java index ee73a45d8..e61698f2d 100644 --- a/src/main/java/dev/isxander/controlify/impl/utils/event/EventTap.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/event/EventTap.java @@ -1,44 +1,28 @@ package dev.isxander.controlify.impl.utils.event; -import java.util.Objects; -import java.util.concurrent.CopyOnWriteArrayList; +import dev.isxander.controlify.api.utils.event.EventSink; +import dev.isxander.controlify.api.utils.event.EventSource; +import dev.isxander.controlify.api.utils.event.UnaryEventStage; +import org.jspecify.annotations.NonNull; /// Notifies listeners synchronously on the emitting thread before forwarding an event. /// Each emission uses a snapshot of subscriptions; changes made during a callback /// apply to subsequent emissions. Listener exceptions propagate to the caller. public class EventTap implements EventSource, UnaryEventStage { - private final CopyOnWriteArrayList listeners = new CopyOnWriteArrayList<>(); + private final EventBus bus = new EventBus<>(); @Override - public void onEvent(T event, EventSink downstream) { + public void onEvent(@NonNull T event, @NonNull EventSink downstream) { this.emit(event); downstream.accept(event); } - /// Notifies subscribers without forwarding to another stage. public void emit(T event) { - for (Registration registration : this.listeners) { - registration.listener.accept(event); - } + this.bus.emit(event); } @Override - public Subscription subscribe(EventSink listener) { - Registration registration = new Registration(Objects.requireNonNull(listener, "listener")); - this.listeners.add(registration); - return registration; - } - - private final class Registration implements Subscription { - private final EventSink listener; - - private Registration(EventSink listener) { - this.listener = listener; - } - - @Override - public void close() { - EventTap.this.listeners.remove(this); - } + public @NonNull Subscription subscribe(@NonNull EventSink listener) { + return this.bus.subscribe(listener); } } diff --git a/src/main/java/dev/isxander/controlify/impl/virtualmouse/SnapUtils.java b/src/main/java/dev/isxander/controlify/impl/virtualmouse/SnapUtils.java index cc54809cd..299c6c9bb 100644 --- a/src/main/java/dev/isxander/controlify/impl/virtualmouse/SnapUtils.java +++ b/src/main/java/dev/isxander/controlify/impl/virtualmouse/SnapUtils.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.impl.virtualmouse; -import dev.isxander.controlify.impl.api.vmousesnapping.SnapPoint; +import dev.isxander.controlify.api.screenop.SnapPoint; import dev.isxander.controlify.mixins.feature.virtualmouse.snapping.RecipeBookComponentAccessor; import dev.isxander.controlify.mixins.feature.virtualmouse.snapping.RecipeBookPageAccessor; import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent; diff --git a/src/main/java/dev/isxander/controlify/impl/virtualmouse/VirtualMouseHandler.java b/src/main/java/dev/isxander/controlify/impl/virtualmouse/VirtualMouseHandler.java index 79160747c..c09fb315e 100644 --- a/src/main/java/dev/isxander/controlify/impl/virtualmouse/VirtualMouseHandler.java +++ b/src/main/java/dev/isxander/controlify/impl/virtualmouse/VirtualMouseHandler.java @@ -8,21 +8,20 @@ import com.mojang.blaze3d.platform.InputConstants; import com.mojang.datafixers.util.Pair; -import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.api.InputMode; -import dev.isxander.controlify.impl.api.vmousesnapping.ISnapBehaviour; -import dev.isxander.controlify.impl.api.vmousesnapping.SnapPoint; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; -import dev.isxander.controlify.impl.api.bind.InputBinding; -import dev.isxander.controlify.impl.input.mapping.GamepadInputs; -import dev.isxander.controlify.impl.controller.input.InputComponent; +import dev.isxander.controlify.api.action.Action; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.action.ControlifyActions; +import dev.isxander.controlify.api.screenop.ISnapBehaviour; +import dev.isxander.controlify.api.screenop.SnapPoint; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controller.input.InputComponentImpl; import dev.isxander.controlify.impl.controller.touchpad.TouchpadComponent; import dev.isxander.controlify.impl.debug.DebugProperties; import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.api.ControlifyEvents; -import dev.isxander.controlify.mixins.feature.virtualmouse.MouseHandlerAccessor; import dev.isxander.controlify.impl.utils.*; +import dev.isxander.controlify.mixins.feature.virtualmouse.MouseHandlerAccessor; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.navigation.ScreenAxis; @@ -44,6 +43,8 @@ public class VirtualMouseHandler { private double currentX, currentY; private double scrollX, scrollY; + private final Vector2d previousMoveImpulse = new Vector2d(); + private boolean leftClickDown, rightClickDown; private float prevXFinger, prevYFinger; @@ -53,19 +54,18 @@ public class VirtualMouseHandler { private Set snapPoints; private SnapPoint lastSnappedPoint; - private final HoldRepeatHelper holdRepeatHelper = new HoldRepeatHelper(10, 3); - public VirtualMouseHandler() { this.minecraft = Minecraft.getInstance(); this.snapPoints = collectSnapPoints(); - ControlifyEvents.INPUT_MODE_CHANGED.register(event -> this.onInputModeChanged(event.mode())); } public void handleControllerInput(ControllerImpl controller) { - if (MinecraftUtil.getScreen() == null) return; + if (MinecraftUtil.getScreen() == null) { + return; + } - if (ControlifyBindings.VMOUSE_TOGGLE.on(controller).justPressed()) { + if (ControlifyActions.VMOUSE_TOGGLE.on(controller).pulse().consumePulse()) { toggleVirtualMouse(); } @@ -73,7 +73,7 @@ public void handleControllerInput(ControllerImpl controller) { return; } - InputComponent input = controller.input().orElseThrow(); + InputComponentImpl input = controller.input(); Optional touchpad = controller.touchpad(); // TODO: re-enable touchpad support @@ -94,23 +94,20 @@ public void handleControllerInput(ControllerImpl controller) { // yImpulseFinger *= 20; // } - InputBinding moveRight = ControlifyBindings.VMOUSE_MOVE_RIGHT.on(controller); - InputBinding moveLeft = ControlifyBindings.VMOUSE_MOVE_LEFT.on(controller); - InputBinding moveDown = ControlifyBindings.VMOUSE_MOVE_DOWN.on(controller); - InputBinding moveUp = ControlifyBindings.VMOUSE_MOVE_UP.on(controller); + Action moveRight = ControlifyActions.VMOUSE_MOVE_RIGHT.on(controller); + Action moveLeft = ControlifyActions.VMOUSE_MOVE_LEFT.on(controller); + Action moveDown = ControlifyActions.VMOUSE_MOVE_DOWN.on(controller); + Action moveUp = ControlifyActions.VMOUSE_MOVE_UP.on(controller); // apply an easing function directly to the vector's length // if you do easing(x), easing(y), then the diagonals where it's something like (~0.8, ~0.8) will incorrectly ease Vector2d impulse = ControllerUtils.applyEasingToLength( - moveRight.analogueNow() - moveLeft.analogueNow(), - moveDown.analogueNow() - moveUp.analogueNow(), - x -> Math.pow(x, 3) - ); - Vector2d prevImpulse = ControllerUtils.applyEasingToLength( - moveRight.analoguePrev() - moveLeft.analoguePrev(), - moveDown.analoguePrev() - moveUp.analoguePrev(), + moveRight.continuous().getContinuous() - moveLeft.continuous().getContinuous(), + moveDown.continuous().getContinuous() - moveUp.continuous().getContinuous(), x -> Math.pow(x, 3) ); + Vector2d prevImpulse = new Vector2d(previousMoveImpulse); + previousMoveImpulse.set(impulse); // Vector2f fingerImpulse = ControllerUtils.applyEasingToLength(xImpulseFinger, yImpulseFinger, x -> (float) Math.pow(x, 1.5)); // Vector2f prevFingerImpulse = ControllerUtils.applyEasingToLength(prevXFinger, prevYFinger, x -> (float) Math.pow(x, 1.5)); @@ -125,13 +122,13 @@ public void handleControllerInput(ControllerImpl controller) { // if just released stick, snap to nearest snap point if (impulse.x == 0 && impulse.y == 0) { - if ((prevImpulse.x != 0 || prevImpulse.y != 0)) + if ((prevImpulse.x != 0 || prevImpulse.y != 0)) { snapToClosestPoint(); + } } var sensitivity = input.settings().sensitivity.virtualMouseSensitivity; - if (!input.settings().sensitivity.isLCE) { float windowSizeModifier = Math.max(minecraft.getWindow().getWidth(), minecraft.getWindow().getHeight()) / 800f; @@ -147,18 +144,14 @@ public void handleControllerInput(ControllerImpl controller) { targetX = Mth.clamp(targetX, 0, minecraft.getWindow().getWidth()); targetY = Mth.clamp(targetY, 0, minecraft.getWindow().getHeight()); - if (holdRepeatHelper.shouldAction(ControlifyBindings.VMOUSE_SNAP_UP.on(controller))) { + if (ControlifyActions.VMOUSE_SNAP_UP.on(controller).pulse().consumePulse()) { snapInDirection(ScreenDirection.UP); - holdRepeatHelper.onNavigate(); - } else if (holdRepeatHelper.shouldAction(ControlifyBindings.VMOUSE_SNAP_DOWN.on(controller))) { + } else if (ControlifyActions.VMOUSE_SNAP_DOWN.on(controller).pulse().consumePulse()) { snapInDirection(ScreenDirection.DOWN); - holdRepeatHelper.onNavigate(); - } else if (holdRepeatHelper.shouldAction(ControlifyBindings.VMOUSE_SNAP_LEFT.on(controller))) { + } else if (ControlifyActions.VMOUSE_SNAP_LEFT.on(controller).pulse().consumePulse()) { snapInDirection(ScreenDirection.LEFT); - holdRepeatHelper.onNavigate(); - } else if (holdRepeatHelper.shouldAction(ControlifyBindings.VMOUSE_SNAP_RIGHT.on(controller))) { + } else if (ControlifyActions.VMOUSE_SNAP_RIGHT.on(controller).pulse().consumePulse()) { snapInDirection(ScreenDirection.RIGHT); - holdRepeatHelper.onNavigate(); } VirtualMouseBehaviour vmouseBehaviour = ScreenProcessorProvider.provide(MinecraftUtil.getScreen()) @@ -170,7 +163,7 @@ public void handleControllerInput(ControllerImpl controller) { handleScroll(controller); } - if (ControlifyBindings.GUI_BACK.on(controller).justPressed() && MinecraftUtil.getScreen() != null) { + if (ControlifyActions.GUI_BACK.on(controller).pulse().consumePulse() && MinecraftUtil.getScreen() != null) { ScreenProcessor.playClackSound(); MinecraftUtil.getScreen().onClose(); } @@ -183,31 +176,37 @@ public void handleCompatibilityBinds(ControllerImpl controller) { // List touchpadState = touchpad.map(TouchpadComponent::fingersNow).orElse(List.of()); // List prevTouchpadState = touchpad.map(TouchpadComponent::fingersThen).orElse(List.of()); - InputComponent input = controller.input().orElseThrow(); - boolean touchpadPressed = input.stateNow().isButtonDown(GamepadInputs.TOUCHPAD_1_BUTTON); - boolean prevTouchpadPressed = input.stateThen().isButtonDown(GamepadInputs.TOUCHPAD_1_BUTTON); + InputComponentImpl input = controller.input(); +// boolean touchpadPressed = input.stateNow().isButtonDown(GamepadInputs.TOUCHPAD_1_BUTTON); +// boolean prevTouchpadPressed = input.stateThen().isButtonDown(GamepadInputs.TOUCHPAD_1_BUTTON); + boolean touchpadPressed = false; + boolean prevTouchpadPressed = false; - if (ControlifyBindings.VMOUSE_LCLICK.on(controller).justPressed() || (touchpadPressed && !prevTouchpadPressed/* && touchpadState.size() == 1*/)) { + if (ControlifyActions.VMOUSE_LCLICK.on(controller).pulse().consumePulse() || (touchpadPressed && !prevTouchpadPressed/* && touchpadState.size() == 1*/)) { this.simulateMousePress(InputConstants.MOUSE_BUTTON_LEFT, InputConstants.PRESS, 0); - } else if (ControlifyBindings.VMOUSE_LCLICK.on(controller).justReleased() || (!touchpadPressed && prevTouchpadPressed)) { + leftClickDown = true; + } else if (leftClickDown && !ControlifyActions.VMOUSE_LCLICK.on(controller).latch().isLatchActive() || (!touchpadPressed && prevTouchpadPressed)) { this.simulateMousePress(InputConstants.MOUSE_BUTTON_LEFT, InputConstants.RELEASE, 0); + leftClickDown = false; } - if (ControlifyBindings.VMOUSE_RCLICK.on(controller).justPressed() || (touchpadPressed && !prevTouchpadPressed/* && touchpadState.size() == 2*/)) { + if (ControlifyActions.VMOUSE_RCLICK.on(controller).pulse().consumePulse() || (touchpadPressed && !prevTouchpadPressed/* && touchpadState.size() == 2*/)) { this.simulateMousePress(InputConstants.MOUSE_BUTTON_RIGHT, InputConstants.PRESS, 0); - } else if (ControlifyBindings.VMOUSE_RCLICK.on(controller).justReleased() || (!touchpadPressed && prevTouchpadPressed)) { + rightClickDown = true; + } else if (rightClickDown && !ControlifyActions.VMOUSE_RCLICK.on(controller).latch().isLatchActive() || (!touchpadPressed && prevTouchpadPressed)) { this.simulateMousePress(InputConstants.MOUSE_BUTTON_RIGHT, InputConstants.RELEASE, 0); + rightClickDown = false; } - if (ControlifyBindings.VMOUSE_SHIFT_CLICK.on(controller).justPressed()) { + if (ControlifyActions.VMOUSE_SHIFT_CLICK.on(controller).pulse().consumePulse()) { this.simulateMousePress(InputConstants.MOUSE_BUTTON_LEFT, InputConstants.PRESS, InputConstants.MOD_SHIFT); this.simulateMousePress(InputConstants.MOUSE_BUTTON_LEFT, InputConstants.RELEASE, InputConstants.MOD_SHIFT); } } public void handleScroll(ControllerImpl controller) { - scrollY += ControlifyBindings.VMOUSE_SCROLL_UP.on(controller).analogueNow() - - ControlifyBindings.VMOUSE_SCROLL_DOWN.on(controller).analogueNow(); + scrollY += ControlifyActions.VMOUSE_SCROLL_UP.on(controller).continuous().getContinuous() + - ControlifyActions.VMOUSE_SCROLL_DOWN.on(controller).continuous().getContinuous(); } private void simulateMousePress(int button, int action, int modifiers) { @@ -217,7 +216,9 @@ private void simulateMousePress(int button, int action, int modifiers) { } public void updateMouse() { - if (!virtualMouseEnabled) return; + if (!virtualMouseEnabled) { + return; + } float delta = minecraft.getDeltaTracker().getRealtimeDeltaTicks(); var windowHandle = minecraft.getWindow().handle(); @@ -376,7 +377,9 @@ public void onInputModeChanged(InputMode mode) { } public void renderVirtualMouse(GuiGraphicsExtractor graphics) { - if (!virtualMouseEnabled) return; + if (!virtualMouseEnabled) { + return; + } if (DebugProperties.DEBUG_SNAPPING) { for (var snapPoint : snapPoints) { @@ -406,7 +409,9 @@ public void renderVirtualMouse(GuiGraphicsExtractor graphics) { } public void enableVirtualMouse() { - if (virtualMouseEnabled) return; + if (virtualMouseEnabled) { + return; + } virtualMouseEnabled = true; @@ -418,20 +423,20 @@ public void enableVirtualMouse() { targetY = currentY = minecraft.mouseHandler.ypos(); } - ControlifyEvents.VIRTUAL_MOUSE_TOGGLED.invoke(new ControlifyEvents.VirtualMouseToggled(true)); if (MinecraftUtil.getScreen() != null) { ScreenProcessorProvider.provide(MinecraftUtil.getScreen()).onVirtualMouseToggled(true); } } public void disableVirtualMouse() { - if (!virtualMouseEnabled) return; + if (!virtualMouseEnabled) { + return; + } virtualMouseEnabled = false; targetX = currentX = minecraft.mouseHandler.xpos(); targetY = currentY = minecraft.mouseHandler.ypos(); - ControlifyEvents.VIRTUAL_MOUSE_TOGGLED.invoke(new ControlifyEvents.VirtualMouseToggled(false)); if (MinecraftUtil.getScreen() != null) { ScreenProcessorProvider.provide(MinecraftUtil.getScreen()).onVirtualMouseToggled(false); } @@ -458,7 +463,9 @@ public boolean requiresVirtualMouse() { } public void toggleVirtualMouse() { - if (MinecraftUtil.getScreen() == null) return; + if (MinecraftUtil.getScreen() == null) { + return; + } if (ScreenProcessorProvider.provide(MinecraftUtil.getScreen()).virtualMouseBehaviour() != VirtualMouseBehaviour.DEFAULT) { MinecraftUtil.sendToast( diff --git a/src/main/java/dev/isxander/controlify/mixins/core/GuiMixin.java b/src/main/java/dev/isxander/controlify/mixins/core/GuiMixin.java index 0a4a29cb2..91fc0d79f 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/GuiMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/GuiMixin.java @@ -9,7 +9,6 @@ import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.api.ControlifyApi; import dev.isxander.controlify.impl.utils.InitialScreenRegistryDuck; import dev.isxander.controlify.impl.utils.MinecraftUtil; import dev.isxander.controlify.impl.utils.MouseMinecraftCallNotifier; @@ -59,7 +58,7 @@ private void notifyInjectionToNotRun(Screen screen, CallbackInfo ci) { ) ) private void hideMouseAfterRelease(Screen screen, CallbackInfo ci) { - if (ControlifyApi.get().currentInputMode().isController()) { + if (Controlify.instance().currentInputMode().isController()) { Controlify.instance().hideMouse(true, true); } } 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 7aa123023..2743b8b16 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/MouseHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/MouseHandlerMixin.java @@ -8,7 +8,6 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.api.ControlifyApi; import dev.isxander.controlify.impl.utils.MouseMinecraftCallNotifier; import net.minecraft.client.Minecraft; import net.minecraft.client.MouseHandler; @@ -93,7 +92,7 @@ private void wrapMouseEvents( ) ) private void moveMouseIfNecessary(CallbackInfo ci) { - if (!controlify$calledFromMinecraftSetScreen && ControlifyApi.get().currentInputMode().isController()) { + if (!controlify$calledFromMinecraftSetScreen && Controlify.instance().currentInputMode().isController()) { Controlify.instance().hideMouse(true, true); } } @@ -106,7 +105,7 @@ private void resetCalledFromMinecraftSetScreen(CallbackInfo ci) { @ModifyExpressionValue(method = "grabMouse", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;isWindowActive()Z")) private boolean passWindowActiveCheckIfOOFInputIsOn(boolean isWindowActive) { - return isWindowActive || (ControlifyApi.get().currentInputMode().isController() && Controlify.instance().config().getSettings().globalSettings().outOfFocusInput); + return isWindowActive || (Controlify.instance().currentInputMode().isController() && Controlify.instance().config().getSettings().globalSettings().outOfFocusInput); } @Override diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/accessibility/LocalPlayerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/accessibility/LocalPlayerMixin.java index 2785b7862..0a8c3e4c7 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/accessibility/LocalPlayerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/accessibility/LocalPlayerMixin.java @@ -6,8 +6,9 @@ */ package dev.isxander.controlify.mixins.feature.accessibility; +import dev.isxander.controlify.impl.Controlify; + import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.api.ControlifyApi; import net.minecraft.client.player.LocalPlayer; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -16,8 +17,8 @@ public class LocalPlayerMixin { @ModifyExpressionValue(method = "sendPosition", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/OptionInstance;get()Ljava/lang/Object;")) private Object shouldUseAutoJump(Object keyboardAutoJump) { - if (ControlifyApi.get().currentInputMode().isController()) { - return ControlifyApi.get().getCurrentController() + if (Controlify.instance().currentInputMode().isController()) { + return Controlify.instance().getCurrentController() .map(controller -> controller.settings().generic.autoJump) .orElse(false); } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/bind/GuiMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/bind/GuiMixin.java index f50134fe1..972c6ba1d 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/bind/GuiMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/bind/GuiMixin.java @@ -6,9 +6,11 @@ */ package dev.isxander.controlify.mixins.feature.bind; -import dev.isxander.controlify.api.ControlifyApi; +import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.controller.input.InputComponent; +import dev.isxander.controlify.impl.controller.input.InputComponentImpl; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Gui; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -16,15 +18,15 @@ @Mixin( //? if >=26.2 { - net.minecraft.client.gui.Gui.class + Gui.class //?} else { - /*net.minecraft.client.Minecraft.class + /*Minecraft.class *///?} ) public class GuiMixin { @Inject(method = "setScreen", at = @At("HEAD")) private void notifyBindGuiOutputOfScreenChange(CallbackInfo ci) { - ControlifyApi.get().getCurrentController().flatMap(ControllerImpl::input) - .ifPresent(InputComponent::notifyGuiPressOutputsOfNavigate); + Controlify.instance().getCurrentController().map(ControllerImpl::input) + .ifPresent(InputComponentImpl::onGuiNavigate); } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/bind/KeyMappingMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/bind/KeyMappingMixin.java index a976dd5c7..055824d02 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/bind/KeyMappingMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/bind/KeyMappingMixin.java @@ -1,23 +1,12 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ package dev.isxander.controlify.mixins.feature.bind; -import dev.isxander.controlify.impl.bindings.KeyMappingHandle; -import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.action.KeyMappingHandle; import net.minecraft.client.KeyMapping; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; - -import java.util.function.BooleanSupplier; @Mixin(KeyMapping.class) public class KeyMappingMixin implements KeyMappingHandle { - @Shadow private int clickCount; @@ -25,21 +14,12 @@ public class KeyMappingMixin implements KeyMappingHandle { private boolean isDown; @Override - public void controlify$setPressed(boolean isDown) { - if (isDown) { - this.isDown = true; - this.clickCount++; - } else { - this.isDown = false; - } + public void controlify$click() { + this.clickCount++; } @Override - public void controlify$addToggleCondition(ControllerImpl controller, BooleanSupplier condition) { - - } - - @Unique protected void incClickCount() { - this.clickCount++; + public void controlify$setDown(boolean down) { + this.isDown = down; } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/bind/ToggleKeyMappingMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/bind/ToggleKeyMappingMixin.java deleted file mode 100644 index 6c18974b7..000000000 --- a/src/main/java/dev/isxander/controlify/mixins/feature/bind/ToggleKeyMappingMixin.java +++ /dev/null @@ -1,52 +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.bind; - -import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.impl.bindings.KeyMappingHandle; -import dev.isxander.controlify.impl.controller.ControllerImpl; -import net.minecraft.client.ToggleKeyMapping; -import org.spongepowered.asm.mixin.*; -import org.spongepowered.asm.mixin.injection.At; - -import java.util.function.BooleanSupplier; - -@Mixin(ToggleKeyMapping.class) -public abstract class ToggleKeyMappingMixin extends KeyMappingMixin implements KeyMappingHandle { - - @Unique private BooleanSupplier controlifyToggleSupplier = () -> false; - - @Shadow - public abstract void setDown(boolean value); - - @Override - public void controlify$setPressed(boolean isDown) { - if (isDown) - this.incClickCount(); - this.setDown(isDown); - } - - @ModifyExpressionValue(method = "setDown", at = @At(value = "INVOKE", target = "Ljava/util/function/BooleanSupplier;getAsBoolean()Z")) - private boolean modifyToggleMode(boolean vanillaToggleMode) { - if (Controlify.instance().currentInputMode().isController()) { - return controlifyToggleSupplier.getAsBoolean(); - } - return vanillaToggleMode; - } - - @Override - public void controlify$addToggleCondition(ControllerImpl controller, BooleanSupplier condition) { - BooleanSupplier oldCondition = controlifyToggleSupplier; - controlifyToggleSupplier = () -> { - boolean thisToggle = condition.getAsBoolean() - && Controlify.instance().currentInputMode().isController() - && Controlify.instance().getCurrentController().map(current -> controller == current).orElse(false); - return oldCondition.getAsBoolean() || thisToggle; - }; - } -} diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/font/KeybindContentsMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/font/KeybindContentsMixin.java index 401c4f9e8..32a432bb4 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/font/KeybindContentsMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/font/KeybindContentsMixin.java @@ -10,9 +10,9 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.impl.font.BindingFontHelper; +import dev.isxander.controlify.impl.font.ActionGlyphsImpl; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.FontDescription; import net.minecraft.network.chat.Style; import net.minecraft.network.chat.contents.KeybindContents; import net.minecraft.resources.Identifier; @@ -22,7 +22,6 @@ import org.spongepowered.asm.mixin.injection.At; import java.util.Optional; -import net.minecraft.network.chat.FontDescription; @Mixin(KeybindContents.class) public class KeybindContentsMixin { @@ -31,17 +30,16 @@ public class KeybindContentsMixin { private String name; @WrapOperation(method = "visit(Lnet/minecraft/network/chat/FormattedText$StyledContentConsumer;Lnet/minecraft/network/chat/Style;)Ljava/util/Optional;", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/chat/contents/KeybindContents;getNestedComponent()Lnet/minecraft/network/chat/Component;")) - private Component testVisitWithStyle(KeybindContents instance, Operation original, @Local(argsOnly = true) Style style) { + private Component testVisitWithStyle(KeybindContents instance, Operation original, @Local(argsOnly = true, name = "currentStyle") Style style) { boolean wrapperFont = style.getFont() instanceof FontDescription.Resource(Identifier font) - && BindingFontHelper.WRAPPER_FONT.equals(font); + && ActionGlyphsImpl.WRAPPER_FONT.equals(font); if (wrapperFont) { - Optional inputText = ControlifyApi.get().getCurrentController() - .filter(c -> c.input().isPresent()) + Optional inputText = Controlify.instance().getCurrentController() .map(c -> Controlify.instance().inputFontMapper() - .getComponentFromBinding( + .getComponentFromAction( c.info().type().id(), - c.input().get().getBinding(Identifier.tryParse(this.name)) + c.input().getAction(Identifier.tryParse(this.name)) ) ); if (inputText.isPresent()) { diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/font/TranslatableContentsMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/font/TranslatableContentsMixin.java index 7661cdd8c..33eb946cc 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/font/TranslatableContentsMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/font/TranslatableContentsMixin.java @@ -7,8 +7,8 @@ package dev.isxander.controlify.mixins.feature.font; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.impl.font.BindingFontHelper; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.font.ActionGlyphsImpl; import net.minecraft.network.chat.contents.TranslatableContents; import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; @@ -25,9 +25,9 @@ public class TranslatableContentsMixin { ) ) private String replaceControllerActiveKey(String originalKey) { - if (BindingFontHelper.PLACEHOLDER_CONTROLLER_ACTIVE_KEY.equals(originalKey)) { - if (ControlifyApi.get().currentInputMode().isController()) { - return BindingFontHelper.PLACEHOLDER_KEY; + if (ActionGlyphsImpl.PLACEHOLDER_CONTROLLER_ACTIVE_KEY.equals(originalKey)) { + if (Controlify.instance().currentInputMode().isController()) { + return ActionGlyphsImpl.PLACEHOLDER_KEY; } else { // because the key doesn't exist, it will rely on the fallback text provided by this translatable contents return originalKey; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/AbstractButtonMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/AbstractButtonMixin.java index 42dfe6e79..e14579278 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/AbstractButtonMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/AbstractButtonMixin.java @@ -7,7 +7,7 @@ package dev.isxander.controlify.mixins.feature.guide.screen; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.impl.api.bind.InputBinding; +import dev.isxander.controlify.api.action.Action; import dev.isxander.controlify.impl.gui.ButtonGuideRenderer; import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; import net.minecraft.client.gui.components.AbstractButton; @@ -23,7 +23,7 @@ @Mixin(AbstractButton.class) public abstract class AbstractButtonMixin extends AbstractWidgetMixin implements ButtonGuideRenderer { @Unique private RenderData renderData = null; - @Unique private final Map controllerMessages = new Object2ObjectArrayMap<>(2); + @Unique private final Map controllerMessages = new Object2ObjectArrayMap<>(2); @ModifyExpressionValue(method = "extractDefaultLabel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/components/AbstractButton;getMessage()Lnet/minecraft/network/chat/Component;")) protected Component modifyRenderedLabel(Component originalMessage) { @@ -42,8 +42,9 @@ protected void catchMessageSet(Component message, CallbackInfo ci) { } @Unique private Component getControllerMessage(Component actualLabel) { - if (!shouldRender()) + if (!shouldRender()) { return actualLabel; + } return getBind().map(bind -> controllerMessages .computeIfAbsent(bind, b -> renderData.getControllerMessage(b, actualLabel))) @@ -56,8 +57,10 @@ && isActive() && renderData.shouldRender((AbstractButton) (Object) this); } - @Unique private Optional getBind() { - if (renderData == null) return Optional.empty(); - return renderData.getBind(); + @Unique private Optional getBind() { + if (renderData == null) { + return Optional.empty(); + } + return renderData.getAction(); } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/TabNavigationBarMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/TabNavigationBarMixin.java index 1c15a76f9..3a850388a 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/TabNavigationBarMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/TabNavigationBarMixin.java @@ -7,8 +7,9 @@ package dev.isxander.controlify.mixins.feature.guide.screen; import com.google.common.collect.ImmutableList; +import dev.isxander.controlify.api.font.ActionGlyphs; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; @@ -44,18 +45,20 @@ private void renderControllerButtonOverlay(GuiGraphicsExtractor graphics, int mo } @Unique private void renderControllerButtonOverlay(GuiGraphicsExtractor graphics, ControllerImpl controller) { - if (tabButtons.size() <= 1) return; // no controls necessary + if (tabButtons.size() <= 1) { + return; // no controls necessary + } TabButton firstTab = tabButtons.get(0); TabButton lastTab = tabButtons.get(tabButtons.size() - 1); Font font = Minecraft.getInstance().font; - Component prevTabText = ControlifyBindings.GUI_PREV_TAB.on(controller).inputGlyph(); + Component prevTabText = ActionGlyphs.action(ControlifyActions.GUI_PREV_TAB.actionId()); int prevTabTextWidth = font.width(prevTabText); graphics.text(font, prevTabText, firstTab.getX() - 2 - prevTabTextWidth, firstTab.getY() / 2 + font.lineHeight / 2, 0xFFFFFFFF); - Component nextTabText = ControlifyBindings.GUI_NEXT_TAB.on(controller).inputGlyph(); + Component nextTabText = ActionGlyphs.action(ControlifyActions.GUI_NEXT_TAB.actionId()); graphics.text(font, nextTabText, lastTab.getX() + lastTab.getWidth() + 2, lastTab.getY() / 2 + font.lineHeight / 2, 0xFFFFFFFF); } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/patches/analogueboat/LocalPlayerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/patches/analogueboat/LocalPlayerMixin.java index bfdcf8fe8..8ba72694e 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/patches/analogueboat/LocalPlayerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/patches/analogueboat/LocalPlayerMixin.java @@ -9,7 +9,6 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.api.ControlifyApi; import dev.isxander.controlify.impl.boatfix.AnalogBoatInput; import dev.isxander.controlify.impl.ingame.InGameInputHandler; import dev.isxander.controlify.impl.utils.MthExt; @@ -37,7 +36,7 @@ private void useAnalogInput( boolean left, boolean right, boolean up, boolean down, Operation original ) { - if (ControlifyApi.get().currentInputMode().isController() && !Controlify.instance().config().getSettings().globalSettings().shouldUseKeyboardMovement()) { + if (Controlify.instance().currentInputMode().isController() && !Controlify.instance().config().getSettings().globalSettings().shouldUseKeyboardMovement()) { Vec2 moveVec = InGameInputHandler.getMoveVec(input); float forwardImpulse = moveVec.y; float rightImpulse = -moveVec.x; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/ClientPacketListenerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/ClientPacketListenerMixin.java index 696b7768a..978f2d668 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/ClientPacketListenerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/ClientPacketListenerMixin.java @@ -9,10 +9,9 @@ import com.llamalad7.mixinextras.expression.Definition; import com.llamalad7.mixinextras.expression.Expression; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.impl.api.bind.InputBinding; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; -import dev.isxander.controlify.impl.font.BindingFontHelper; +import dev.isxander.controlify.api.font.ActionGlyphs; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.action.ControlifyActions; import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.network.chat.Component; import org.spongepowered.asm.mixin.Mixin; @@ -30,10 +29,10 @@ public class ClientPacketListenerMixin { @Expression("?.keyShift.getTranslatedKeyMessage()") @ModifyExpressionValue(method = "handleSetEntityPassengersPacket", at = @At("MIXINEXTRAS:EXPRESSION")) private Component useControllerTextForSneakTip(Component original) { - return ControlifyApi.get().getCurrentController() - .flatMap(c -> ControlifyApi.get().currentInputMode().isController() ? Optional.of(c) : Optional.empty()) - .flatMap(c -> Optional.ofNullable(ControlifyBindings.SNEAK.on(c))) - .map(InputBinding::inputGlyph) + return Controlify.instance().getCurrentController() + .flatMap(c -> Controlify.instance().currentInputMode().isController() ? Optional.of(c) : Optional.empty()) + .flatMap(c -> Optional.ofNullable(ControlifyActions.SNEAK.onOrNull(c))) + .map(a -> ActionGlyphs.action(a.spec().id())) .orElse(original); } @@ -42,7 +41,7 @@ private Component useControllerTextForSneakTip(Component original) { @Expression("?.keyUp.getTranslatedKeyMessage()") @ModifyExpressionValue(method = "handleGameEvent", at = @At("MIXINEXTRAS:EXPRESSION")) private Component useTutorialUpGlyph(Component original) { - return BindingFontHelper.bindingWithActiveFallback(ControlifyBindings.WALK_FORWARD.bindId(), original); + return ActionGlyphs.actionWithActiveFallback(ControlifyActions.WALK_FORWARD.actionId(), original); } @Definition(id = "keyLeft", field = "Lnet/minecraft/client/Options;keyLeft:Lnet/minecraft/client/KeyMapping;") @@ -50,7 +49,7 @@ private Component useTutorialUpGlyph(Component original) { @Expression("?.keyLeft.getTranslatedKeyMessage()") @ModifyExpressionValue(method = "handleGameEvent", at = @At("MIXINEXTRAS:EXPRESSION")) private Component useTutorialLeftGlyph(Component original) { - return BindingFontHelper.bindingWithActiveFallback(ControlifyBindings.WALK_LEFT.bindId(), original); + return ActionGlyphs.actionWithActiveFallback(ControlifyActions.WALK_LEFT.actionId(), original); } @Definition(id = "keyDown", field = "Lnet/minecraft/client/Options;keyDown:Lnet/minecraft/client/KeyMapping;") @@ -58,7 +57,7 @@ private Component useTutorialLeftGlyph(Component original) { @Expression("?.keyDown.getTranslatedKeyMessage()") @ModifyExpressionValue(method = "handleGameEvent", at = @At("MIXINEXTRAS:EXPRESSION")) private Component useTutorialDownGlyph(Component original) { - return BindingFontHelper.bindingWithActiveFallback(ControlifyBindings.WALK_BACKWARD.bindId(), original); + return ActionGlyphs.actionWithActiveFallback(ControlifyActions.WALK_BACKWARD.actionId(), original); } @Definition(id = "keyRight", field = "Lnet/minecraft/client/Options;keyRight:Lnet/minecraft/client/KeyMapping;") @@ -66,7 +65,7 @@ private Component useTutorialDownGlyph(Component original) { @Expression("?.keyRight.getTranslatedKeyMessage()") @ModifyExpressionValue(method = "handleGameEvent", at = @At("MIXINEXTRAS:EXPRESSION")) private Component useTutorialRightGlyph(Component original) { - return BindingFontHelper.bindingWithActiveFallback(ControlifyBindings.WALK_RIGHT.bindId(), original); + return ActionGlyphs.actionWithActiveFallback(ControlifyActions.WALK_RIGHT.actionId(), original); } @Definition(id = "keyJump", field = "Lnet/minecraft/client/Options;keyJump:Lnet/minecraft/client/KeyMapping;") @@ -74,7 +73,7 @@ private Component useTutorialRightGlyph(Component original) { @Expression("?.keyJump.getTranslatedKeyMessage()") @ModifyExpressionValue(method = "handleGameEvent", at = @At("MIXINEXTRAS:EXPRESSION")) private Component useTutorialJumpGlyph(Component original) { - return BindingFontHelper.bindingWithActiveFallback(ControlifyBindings.JUMP.bindId(), original); + return ActionGlyphs.actionWithActiveFallback(ControlifyActions.JUMP.actionId(), original); } @Definition(id = "keyInventory", field = "Lnet/minecraft/client/Options;keyInventory:Lnet/minecraft/client/KeyMapping;") @@ -82,7 +81,7 @@ private Component useTutorialJumpGlyph(Component original) { @Expression("?.keyInventory.getTranslatedKeyMessage()") @ModifyExpressionValue(method = "handleGameEvent", at = @At("MIXINEXTRAS:EXPRESSION")) private Component useTutorialInventoryGlyph(Component original) { - return BindingFontHelper.bindingWithActiveFallback(ControlifyBindings.INVENTORY.bindId(), original); + return ActionGlyphs.actionWithActiveFallback(ControlifyActions.INVENTORY.actionId(), original); } @Definition(id = "keyScreenshot", field = "Lnet/minecraft/client/Options;keyScreenshot:Lnet/minecraft/client/KeyMapping;") @@ -90,6 +89,6 @@ private Component useTutorialInventoryGlyph(Component original) { @Expression("?.keyScreenshot.getTranslatedKeyMessage()") @ModifyExpressionValue(method = "handleGameEvent", at = @At("MIXINEXTRAS:EXPRESSION")) private Component useTutorialScreenshotGlyph(Component original) { - return BindingFontHelper.bindingWithActiveFallback(ControlifyBindings.TAKE_SCREENSHOT.bindId(), original); + return ActionGlyphs.actionWithActiveFallback(ControlifyActions.TAKE_SCREENSHOT.actionId(), original); } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/MovementTutorialStepInstanceMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/MovementTutorialStepInstanceMixin.java index 120c24a14..b4cdb3fcf 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/MovementTutorialStepInstanceMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/MovementTutorialStepInstanceMixin.java @@ -9,8 +9,8 @@ import com.llamalad7.mixinextras.expression.Definition; import com.llamalad7.mixinextras.expression.Expression; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; -import dev.isxander.controlify.impl.font.BindingFontHelper; +import dev.isxander.controlify.api.font.ActionGlyphs; +import dev.isxander.controlify.impl.action.ControlifyActions; import net.minecraft.client.tutorial.MovementTutorialStepInstance; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; @@ -23,35 +23,35 @@ public class MovementTutorialStepInstanceMixin { @Expression("key('forward')") @ModifyExpressionValue(method = "", at = @At("MIXINEXTRAS:EXPRESSION")) private static Component useForwardGlyph(Component original) { - return BindingFontHelper.bindingWithActiveFallback(ControlifyBindings.WALK_FORWARD.bindId(), original); + return ActionGlyphs.actionWithActiveFallback(ControlifyActions.WALK_FORWARD.actionId(), original); } @Definition(id = "key", method = "Lnet/minecraft/client/tutorial/Tutorial;key(Ljava/lang/String;)Lnet/minecraft/network/chat/Component;") @Expression("key('left')") @ModifyExpressionValue(method = "", at = @At("MIXINEXTRAS:EXPRESSION")) private static Component useLeftGlyph(Component original) { - return BindingFontHelper.bindingWithActiveFallback(ControlifyBindings.WALK_LEFT.bindId(), original); + return ActionGlyphs.actionWithActiveFallback(ControlifyActions.WALK_LEFT.actionId(), original); } @Definition(id = "key", method = "Lnet/minecraft/client/tutorial/Tutorial;key(Ljava/lang/String;)Lnet/minecraft/network/chat/Component;") @Expression("key('back')") @ModifyExpressionValue(method = "", at = @At("MIXINEXTRAS:EXPRESSION")) private static Component useBackGlyph(Component original) { - return BindingFontHelper.bindingWithActiveFallback(ControlifyBindings.WALK_BACKWARD.bindId(), original); + return ActionGlyphs.actionWithActiveFallback(ControlifyActions.WALK_BACKWARD.actionId(), original); } @Definition(id = "key", method = "Lnet/minecraft/client/tutorial/Tutorial;key(Ljava/lang/String;)Lnet/minecraft/network/chat/Component;") @Expression("key('right')") @ModifyExpressionValue(method = "", at = @At("MIXINEXTRAS:EXPRESSION")) private static Component useRightGlyph(Component original) { - return BindingFontHelper.bindingWithActiveFallback(ControlifyBindings.WALK_RIGHT.bindId(), original); + return ActionGlyphs.actionWithActiveFallback(ControlifyActions.WALK_RIGHT.actionId(), original); } @Definition(id = "key", method = "Lnet/minecraft/client/tutorial/Tutorial;key(Ljava/lang/String;)Lnet/minecraft/network/chat/Component;") @Expression("key('jump')") @ModifyExpressionValue(method = "", at = @At("MIXINEXTRAS:EXPRESSION")) private static Component useJumpGlyph(Component original) { - return BindingFontHelper.bindingWithActiveFallback(ControlifyBindings.JUMP.bindId(), original); + return ActionGlyphs.actionWithActiveFallback(ControlifyActions.JUMP.actionId(), original); } @Definition(id = "translatable", method = "Lnet/minecraft/network/chat/Component;translatable(Ljava/lang/String;)Lnet/minecraft/network/chat/MutableComponent;") diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/OpenInventoryTutorialStepMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/OpenInventoryTutorialStepMixin.java index 64d382f0f..5d50c2971 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/OpenInventoryTutorialStepMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/OpenInventoryTutorialStepMixin.java @@ -9,8 +9,8 @@ import com.llamalad7.mixinextras.expression.Definition; import com.llamalad7.mixinextras.expression.Expression; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; -import dev.isxander.controlify.impl.font.BindingFontHelper; +import dev.isxander.controlify.api.font.ActionGlyphs; +import dev.isxander.controlify.impl.action.ControlifyActions; import net.minecraft.client.tutorial.OpenInventoryTutorialStep; import net.minecraft.network.chat.Component; import org.spongepowered.asm.mixin.Mixin; @@ -22,6 +22,6 @@ public class OpenInventoryTutorialStepMixin { @Expression("key('inventory')") @ModifyExpressionValue(method = "", at = @At("MIXINEXTRAS:EXPRESSION")) private static Component useGlyph(Component original) { - return BindingFontHelper.bindingWithActiveFallback(ControlifyBindings.INVENTORY.bindId(), original); + return ActionGlyphs.actionWithActiveFallback(ControlifyActions.INVENTORY.actionId(), original); } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/PunchTreeTutorialStepInstanceMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/PunchTreeTutorialStepInstanceMixin.java index 0984119e3..5a42ddb49 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/PunchTreeTutorialStepInstanceMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/patches/keymessages/PunchTreeTutorialStepInstanceMixin.java @@ -9,8 +9,8 @@ import com.llamalad7.mixinextras.expression.Definition; import com.llamalad7.mixinextras.expression.Expression; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; -import dev.isxander.controlify.impl.font.BindingFontHelper; +import dev.isxander.controlify.api.font.ActionGlyphs; +import dev.isxander.controlify.impl.action.ControlifyActions; import net.minecraft.client.tutorial.PunchTreeTutorialStepInstance; import net.minecraft.network.chat.Component; import org.spongepowered.asm.mixin.Mixin; @@ -22,6 +22,6 @@ public class PunchTreeTutorialStepInstanceMixin { @Expression("key('attack')") @ModifyExpressionValue(method = "", at = @At("MIXINEXTRAS:EXPRESSION")) private static Component useGlyph(Component original) { - return BindingFontHelper.bindingWithActiveFallback(ControlifyBindings.ATTACK.bindId(), original); + return ActionGlyphs.actionWithActiveFallback(ControlifyActions.ATTACK.actionId(), original); } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/MinecraftMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/MinecraftMixin.java index be4f096b9..ead411034 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/MinecraftMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/MinecraftMixin.java @@ -6,7 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.rumble; -import dev.isxander.controlify.api.ControlifyApi; +import dev.isxander.controlify.impl.Controlify; + import dev.isxander.controlify.impl.controller.ControllerImpl; import net.minecraft.client.Minecraft; import org.spongepowered.asm.mixin.Mixin; @@ -18,7 +19,7 @@ public class MinecraftMixin { @Inject(method = "disconnect(Lnet/minecraft/client/gui/screens/Screen;Z)V", at = @At("HEAD")) private void clearRumbleEffects(CallbackInfo ci) { - ControlifyApi.get().getCurrentController() + Controlify.instance().getCurrentController() .flatMap(ControllerImpl::rumble) .ifPresent(controller -> controller.rumbleManager().clearEffects()); } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/blockbreak/MultiPlayerGameModeMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/blockbreak/MultiPlayerGameModeMixin.java index f229dadde..109fbaf97 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/blockbreak/MultiPlayerGameModeMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/blockbreak/MultiPlayerGameModeMixin.java @@ -6,9 +6,10 @@ */ package dev.isxander.controlify.mixins.feature.rumble.blockbreak; +import dev.isxander.controlify.impl.Controlify; + import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.sugar.Local; -import dev.isxander.controlify.api.ControlifyApi; import dev.isxander.controlify.api.controller.rumble.RumbleEffect; import dev.isxander.controlify.api.controller.rumble.RumbleEffectInstance; import dev.isxander.controlify.api.controller.rumble.RumbleState; @@ -88,7 +89,7 @@ private boolean onAbortBreakingBlock(boolean original) { .minTime(1) .stopCondition(stopped::get); - blockBreakRumble = ControlifyApi.get().playRumbleEffect(CUtil.rl("block_break"), effect).orElse(null); + blockBreakRumble = Controlify.instance().playRumbleEffect(CUtil.rl("block_break"), effect).orElse(null); } @Unique private void stopRumble() { diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/damage/ClientPacketListenerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/damage/ClientPacketListenerMixin.java index 5370886f1..6fa6c2cc1 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/damage/ClientPacketListenerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/damage/ClientPacketListenerMixin.java @@ -6,7 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.rumble.damage; -import dev.isxander.controlify.api.ControlifyApi; +import dev.isxander.controlify.impl.Controlify; + import dev.isxander.controlify.api.controller.rumble.RumbleEffect; import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.client.Minecraft; @@ -55,14 +56,14 @@ private void onHurtAnimation(ClientboundHurtAnimationPacket packet, CallbackInfo } @Unique private void playIncomingDamageRumble() { - ControlifyApi.get().playRumbleEffect( + Controlify.instance().playRumbleEffect( CUtil.rl("incoming_damage"), RumbleEffect.finite().constant(0.8f, 0.5f, 5) ); } @Unique private void playOutgoingDamageRumble() { - ControlifyApi.get().playRumbleEffect( + Controlify.instance().playRumbleEffect( CUtil.rl("outgoing_damage"), RumbleEffect.finite().constant(0.18f, 0.4f, 1) .then(RumbleEffect.finite().constant(0.06f, 0.16f, 1)) diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/explosion/ClientPacketListenerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/explosion/ClientPacketListenerMixin.java index bbfb5b460..6783ec96e 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/explosion/ClientPacketListenerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/explosion/ClientPacketListenerMixin.java @@ -6,7 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.rumble.explosion; -import dev.isxander.controlify.api.ControlifyApi; +import dev.isxander.controlify.impl.Controlify; + import dev.isxander.controlify.api.controller.rumble.RumbleEffect; import dev.isxander.controlify.api.controller.rumble.RumbleState; import dev.isxander.controlify.impl.utils.CUtil; @@ -31,7 +32,7 @@ private void onClientExplosion(ClientboundExplodePacket packet, CallbackInfo ci) return; } - ControlifyApi.get().playRumbleEffect( + Controlify.instance().playRumbleEffect( CUtil.rl("explosion"), RumbleEffect.finite().constant(magnitude, magnitude, 2) // initial boom .then(RumbleEffect.finite().byTime(t -> { diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/explosion/LightningBoltMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/explosion/LightningBoltMixin.java index bad9fb55e..ec9fb95bb 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/explosion/LightningBoltMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/explosion/LightningBoltMixin.java @@ -6,8 +6,9 @@ */ package dev.isxander.controlify.mixins.feature.rumble.explosion; +import dev.isxander.controlify.impl.Controlify; + import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.api.ControlifyApi; import dev.isxander.controlify.api.controller.rumble.RumbleEffect; import dev.isxander.controlify.api.controller.rumble.RumbleState; import dev.isxander.controlify.impl.utils.CUtil; @@ -20,7 +21,7 @@ public class LightningBoltMixin { @ModifyExpressionValue(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;isClientSide()Z")) private boolean onLightningStrike(boolean client) { if (client) { - ControlifyApi.get().playRumbleEffect( + Controlify.instance().playRumbleEffect( CUtil.rl("lightning"), RumbleEffect.finite().constant(1f, 0.2f, 6) // initial boom .then(RumbleEffect.finite().byTime(t -> new RumbleState(0f, 1 - t*0.2f), 10)) // explosion diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/fishing/FishingHookMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/fishing/FishingHookMixin.java index 2be9370f5..4ad145ebb 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/fishing/FishingHookMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/fishing/FishingHookMixin.java @@ -6,8 +6,9 @@ */ package dev.isxander.controlify.mixins.feature.rumble.fishing; +import dev.isxander.controlify.impl.Controlify; + import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.isxander.controlify.api.ControlifyApi; import dev.isxander.controlify.api.controller.rumble.RumbleEffect; import dev.isxander.controlify.api.controller.rumble.RumbleEffectInstance; import dev.isxander.controlify.impl.utils.CUtil; @@ -37,7 +38,7 @@ private Object onBitingStateUpdated(Object bitingObj) { var effect = RumbleEffect.continuous() .constant(0f, 0.05f); - this.bitingRumble = ControlifyApi.get().playRumbleEffect(CUtil.rl("fishing_bite"), effect).orElse(null); + this.bitingRumble = Controlify.instance().playRumbleEffect(CUtil.rl("fishing_bite"), effect).orElse(null); } else if (!biting && this.biting) { stopBitingRumble(); } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/itembreak/LocalPlayerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/itembreak/LocalPlayerMixin.java index fcd5d365c..1a85196f0 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/itembreak/LocalPlayerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/itembreak/LocalPlayerMixin.java @@ -6,7 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.rumble.itembreak; -import dev.isxander.controlify.api.ControlifyApi; +import dev.isxander.controlify.impl.Controlify; + import dev.isxander.controlify.api.controller.rumble.RumbleEffect; import dev.isxander.controlify.api.controller.rumble.RumbleState; import dev.isxander.controlify.impl.utils.CUtil; @@ -19,7 +20,7 @@ public class LocalPlayerMixin extends LivingEntityMixin { @Override protected void onBreakItemParticles(ItemStack stack, CallbackInfo ci) { - ControlifyApi.get().playRumbleEffect( + Controlify.instance().playRumbleEffect( CUtil.rl("item_break"), RumbleEffect.finite().byTick(tick -> new RumbleState(tick <= 4 ? 1f : 0f, 1f), 10) ); diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/levelevents/LevelEventHandlerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/levelevents/LevelEventHandlerMixin.java index 828fea31f..4bba5a410 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/levelevents/LevelEventHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/levelevents/LevelEventHandlerMixin.java @@ -6,7 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.rumble.levelevents; -import dev.isxander.controlify.api.ControlifyApi; +import dev.isxander.controlify.impl.Controlify; + import dev.isxander.controlify.api.controller.rumble.RumbleEffect; import dev.isxander.controlify.api.controller.rumble.RumbleState; import dev.isxander.controlify.impl.utils.CUtil; @@ -24,7 +25,7 @@ public class LevelEventHandlerMixin { @Inject(method = "levelEvent", at = @At("HEAD")) private void onLevelEvent(int eventId, BlockPos pos, int data, CallbackInfo ci) { switch (eventId) { - case LevelEvent.SOUND_ANVIL_USED -> ControlifyApi.get().playRumbleEffect( + case LevelEvent.SOUND_ANVIL_USED -> Controlify.instance().playRumbleEffect( CUtil.rl("anvil"), RumbleEffect.finite().constant(1f, 0.5f, 2) .then(RumbleEffect.finite().constant(0f, 0f, 5)).repeat(3) @@ -35,7 +36,7 @@ private void onLevelEvent(int eventId, BlockPos pos, int data, CallbackInfo ci) @Inject(method = "globalLevelEvent", at = @At("HEAD")) private void onGlobalLevelEvent(int eventId, BlockPos pos, int data, CallbackInfo ci) { switch (eventId) { - case LevelEvent.SOUND_DRAGON_DEATH -> ControlifyApi.get().playRumbleEffect( + case LevelEvent.SOUND_DRAGON_DEATH -> Controlify.instance().playRumbleEffect( CUtil.rl("dragon_death"), RumbleEffect.finite().constant(1f, 1f, 194) .then(RumbleEffect.finite().byTime(t -> { @@ -43,7 +44,7 @@ private void onGlobalLevelEvent(int eventId, BlockPos pos, int data, CallbackInf return new RumbleState(1 - easeOutQuad, 1 - easeOutQuad); }, 63)) ); - case LevelEvent.SOUND_WITHER_BOSS_SPAWN -> ControlifyApi.get().playRumbleEffect( + case LevelEvent.SOUND_WITHER_BOSS_SPAWN -> Controlify.instance().playRumbleEffect( CUtil.rl("wither_spawn"), RumbleEffect.finite().constant(1f, 1f, 9) .then(RumbleEffect.finite().constant(0.1f, 1f, 14)) diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/slowblock/LocalPlayerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/slowblock/LocalPlayerMixin.java index 76660ecc5..a557aac4d 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/slowblock/LocalPlayerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/slowblock/LocalPlayerMixin.java @@ -6,8 +6,9 @@ */ package dev.isxander.controlify.mixins.feature.rumble.slowblock; +import dev.isxander.controlify.impl.Controlify; + import com.mojang.authlib.GameProfile; -import dev.isxander.controlify.api.ControlifyApi; import dev.isxander.controlify.api.controller.rumble.RumbleEffect; import dev.isxander.controlify.api.controller.rumble.RumbleEffectInstance; import dev.isxander.controlify.api.controller.rumble.RumbleState; @@ -61,7 +62,7 @@ private void manageSlowBlockRumble(CallbackInfo ci) { return new RumbleState(0.3f * movementAmount, 0.5f * movementAmount); }) .timeout(100); - slowBlockRumble = ControlifyApi.get().playRumbleEffect(CUtil.rl("slow_block"), effect).orElse(null); + slowBlockRumble = Controlify.instance().playRumbleEffect(CUtil.rl("slow_block"), effect).orElse(null); } else { slowBlockRumble.keepAlive(); } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/useitem/LocalPlayerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/useitem/LocalPlayerMixin.java index 691227213..70c8102a0 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/useitem/LocalPlayerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/useitem/LocalPlayerMixin.java @@ -6,7 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.rumble.useitem; -import dev.isxander.controlify.api.ControlifyApi; +import dev.isxander.controlify.impl.Controlify; + import dev.isxander.controlify.api.controller.rumble.RumbleEffect; import dev.isxander.controlify.api.controller.rumble.RumbleEffectInstance; import dev.isxander.controlify.api.controller.rumble.RumbleState; @@ -78,7 +79,7 @@ protected void onStopUsingItem(CallbackInfo ci) { @Unique private void startRumble(RumbleEffect effect) { if (useItemRumble != null) useItemRumble.stop(); - this.useItemRumble = ControlifyApi.get().playRumbleEffect(CUtil.rl("use_item"), effect).orElse(null); + this.useItemRumble = Controlify.instance().playRumbleEffect(CUtil.rl("use_item"), effect).orElse(null); } @Override diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/waterland/LocalPlayerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/waterland/LocalPlayerMixin.java index 01d3e86c6..cef69f462 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/rumble/waterland/LocalPlayerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/rumble/waterland/LocalPlayerMixin.java @@ -6,7 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.rumble.waterland; -import dev.isxander.controlify.api.ControlifyApi; +import dev.isxander.controlify.impl.Controlify; + import dev.isxander.controlify.api.controller.rumble.RumbleEffect; import dev.isxander.controlify.api.controller.rumble.RumbleState; import dev.isxander.controlify.impl.controller.ControllerImpl; @@ -33,7 +34,7 @@ protected void splashRumble(CallbackInfo ci) { return; } - ControlifyApi.get().getCurrentController().flatMap(ControllerImpl::rumble).ifPresent(rumble -> { + Controlify.instance().getCurrentController().flatMap(ControllerImpl::rumble).ifPresent(rumble -> { Entity entity = Objects.requireNonNullElse(this.getControllingPassenger(), this); float f = entity == this ? 0.2F : 0.9F; Vec3 vec3 = entity.getDeltaMovement(); diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/chat/ChatScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/chat/ChatScreenMixin.java index 4e257c61c..4b50c5366 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/chat/ChatScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/chat/ChatScreenMixin.java @@ -6,11 +6,12 @@ */ package dev.isxander.controlify.mixins.feature.screenop.impl.chat; +import dev.isxander.controlify.impl.Controlify; + import com.llamalad7.mixinextras.expression.Definition; import com.llamalad7.mixinextras.expression.Expression; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.mojang.blaze3d.platform.InputConstants; -import dev.isxander.controlify.api.ControlifyApi; import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; import dev.isxander.controlify.impl.screenop.compat.vanilla.ChatScreenProcessor; import dev.isxander.controlify.impl.screenop.keyboard.*; @@ -56,10 +57,10 @@ protected ChatScreenMixin(Component title) { private void addKeyboard(CallbackInfo ci) { this.shiftChatAmt = 0f; - ControlifyApi.get().getCurrentController().ifPresent(c -> { + Controlify.instance().getCurrentController().ifPresent(c -> { // if the keyboard is already present, re-add it even if we're in kb/m mode since // setting fullscreen will turn it to that mode - if (!ControlifyApi.get().currentInputMode().isController() && this.keyboard == null) return; + if (!Controlify.instance().currentInputMode().isController() && this.keyboard == null) return; if (!c.settings().generic.keyboard.showOnScreenKeyboard) return; this.shiftChatAmt = 0.5f; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/EditBoxMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/EditBoxMixin.java index 8a8d90a1a..c0333a728 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/EditBoxMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/EditBoxMixin.java @@ -10,8 +10,9 @@ import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Share; import com.llamalad7.mixinextras.sugar.ref.LocalBooleanRef; -import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.api.font.ActionGlyphs; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.screenop.ComponentProcessor; import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; import dev.isxander.controlify.impl.screenop.compat.vanilla.EditBoxComponentProcessor; @@ -66,11 +67,11 @@ private String renderHintText( ) { renderHint.set(false); - ControlifyApi.get().getCurrentController().ifPresent(controller -> { + Controlify.instance().getCurrentController().ifPresent(controller -> { if (this.isFocused() && controller.settings().generic.keyboard.showOnScreenKeyboard && controller.settings().generic.guide.showScreenGuides - && ControlifyApi.get().currentInputMode().isController() + && Controlify.instance().currentInputMode().isController() && !(MinecraftUtil.getScreen() instanceof KeyboardOverlayScreen) && processor.getKeyboardBehaviour() instanceof ComponentKeyboardBehaviour.Handled ) { @@ -96,7 +97,7 @@ private String renderHintText( pose.popMatrix(); } else { - var component = ControlifyBindings.GUI_PRESS.inputGlyph(); + var component = ActionGlyphs.action(ControlifyActions.GUI_PRESS.actionId()); int width = font.width(component); graphics.text( diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/CreateWorldScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/CreateWorldScreenMixin.java index 7e08af07b..41b57deed 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/CreateWorldScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/CreateWorldScreenMixin.java @@ -9,7 +9,7 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import dev.isxander.controlify.api.gui.ButtonGuideApi; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; import dev.isxander.controlify.impl.screenop.compat.vanilla.CreateWorldScreenProcessor; @@ -45,7 +45,7 @@ public abstract class CreateWorldScreenMixin implements ScreenProcessorProvider private Button modifyCreateButton(Button button) { ButtonGuideApi.addGuideToButton( button, - ControlifyBindings.GUI_ABSTRACT_ACTION_1, + ControlifyActions.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always() ); return button; @@ -68,7 +68,7 @@ private Button modifyCreateButton(Button button) { private Button modifyCancelButton(Button button) { ButtonGuideApi.addGuideToButton( button, - ControlifyBindings.GUI_BACK, + ControlifyActions.GUI_BACK, ButtonGuidePredicate.always() ); return button; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/SelectWorldScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/SelectWorldScreenMixin.java index ed68fe563..bfb38164c 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/SelectWorldScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/SelectWorldScreenMixin.java @@ -11,7 +11,7 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import dev.isxander.controlify.api.gui.ButtonGuideApi; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; import dev.isxander.controlify.impl.screenop.compat.vanilla.SelectWorldScreenProcessor; @@ -38,7 +38,7 @@ public ScreenProcessor screenProcessor() { private Button modifyCancelButton(Button button) { ButtonGuideApi.addGuideToButton( button, - ControlifyBindings.GUI_BACK, + ControlifyActions.GUI_BACK, ButtonGuidePredicate.always() ); return button; @@ -52,7 +52,7 @@ private Button modifyCancelButton(Button button) { private Button modifyCreateButton(Button button) { ButtonGuideApi.addGuideToButton( button, - ControlifyBindings.GUI_ABSTRACT_ACTION_1, + ControlifyActions.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always() ); return button; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/sign/AbstractSignEditScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/sign/AbstractSignEditScreenMixin.java index 7c4e87d18..49fd3581a 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/sign/AbstractSignEditScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/sign/AbstractSignEditScreenMixin.java @@ -6,13 +6,14 @@ */ package dev.isxander.controlify.mixins.feature.screenop.impl.sign; +import dev.isxander.controlify.impl.Controlify; + import com.llamalad7.mixinextras.expression.Definition; import com.llamalad7.mixinextras.expression.Expression; import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.mojang.blaze3d.platform.InputConstants; -import dev.isxander.controlify.api.ControlifyApi; import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; import dev.isxander.controlify.impl.screenop.compat.vanilla.AbstractSignEditScreenProcessor; @@ -68,10 +69,10 @@ protected AbstractSignEditScreenMixin(Component title) { */ @Inject(method = "init", at = @At("HEAD")) private void addKeyboard(CallbackInfo ci) { - ControlifyApi.get().getCurrentController().ifPresent(c -> { + Controlify.instance().getCurrentController().ifPresent(c -> { // if the keyboard is already present, re-add it even if we're in kb/m mode since // setting fullscreen will turn it to that mode - if (!ControlifyApi.get().currentInputMode().isController() && this.keyboard == null) return; + if (!Controlify.instance().currentInputMode().isController() && this.keyboard == null) return; if (!c.settings().generic.keyboard.showOnScreenKeyboard) return; int keyboardHeight = (int) (this.height * 0.5f); 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 0f12d09ee..12dc9b494 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 @@ -10,7 +10,7 @@ import com.llamalad7.mixinextras.sugar.Local; import com.mojang.blaze3d.platform.InputConstants; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.impl.bindings.ControlifyBindings; +import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -22,11 +22,13 @@ public class InputConstantsMixin { private static boolean modifyIsKeyDown(boolean keyDown, @Local(argsOnly = true, name = "key") int key) { if (key == InputConstants.KEY_LSHIFT) { ControllerImpl controller = Controlify.instance().getCurrentController().orElse(null); - if (controller == null) return keyDown; + if (controller == null) { + return keyDown; + } return keyDown - || ControlifyBindings.VMOUSE_SHIFT_CLICK.on(controller).digitalNow() - || ControlifyBindings.VMOUSE_SHIFT.on(controller).digitalNow(); + || ControlifyActions.VMOUSE_SHIFT_CLICK.on(controller).latch().isLatchActive() + || ControlifyActions.VMOUSE_SHIFT.on(controller).latch().isLatchActive(); } return keyDown; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/AbstractContainerScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/AbstractContainerScreenMixin.java index 488da187f..444f4785a 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/AbstractContainerScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/AbstractContainerScreenMixin.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.virtualmouse.snapping; -import dev.isxander.controlify.impl.api.vmousesnapping.ISnapBehaviour; -import dev.isxander.controlify.impl.api.vmousesnapping.SnapPoint; +import dev.isxander.controlify.api.screenop.ISnapBehaviour; +import dev.isxander.controlify.api.screenop.SnapPoint; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.world.inventory.AbstractContainerMenu; import org.joml.Vector2i; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/AbstractRecipeBookScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/AbstractRecipeBookScreenMixin.java index 4272aa49b..5fcd5c6d1 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/AbstractRecipeBookScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/AbstractRecipeBookScreenMixin.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.mixins.feature.virtualmouse.snapping; -import dev.isxander.controlify.impl.api.vmousesnapping.SnapPoint; +import dev.isxander.controlify.api.screenop.SnapPoint; import dev.isxander.controlify.impl.virtualmouse.SnapUtils; import net.minecraft.client.gui.screens.inventory.*; import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/CreativeModeInventoryScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/CreativeModeInventoryScreenMixin.java index ec3db9e2d..218fd0b57 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/CreativeModeInventoryScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/CreativeModeInventoryScreenMixin.java @@ -6,7 +6,7 @@ */ package dev.isxander.controlify.mixins.feature.virtualmouse.snapping; -import dev.isxander.controlify.impl.api.vmousesnapping.SnapPoint; +import dev.isxander.controlify.api.screenop.SnapPoint; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen; import net.minecraft.world.item.CreativeModeTab; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/EnchantmentScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/EnchantmentScreenMixin.java index fb2da8f33..3d435f5f2 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/EnchantmentScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/EnchantmentScreenMixin.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.virtualmouse.snapping; -import dev.isxander.controlify.impl.api.vmousesnapping.ISnapBehaviour; -import dev.isxander.controlify.impl.api.vmousesnapping.SnapPoint; +import dev.isxander.controlify.api.screenop.ISnapBehaviour; +import dev.isxander.controlify.api.screenop.SnapPoint; import net.minecraft.client.gui.screens.inventory.EnchantmentScreen; import net.minecraft.world.inventory.EnchantmentMenu; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/LoomScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/LoomScreenMixin.java index ef2aac6ce..0fed88503 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/LoomScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/LoomScreenMixin.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.virtualmouse.snapping; -import dev.isxander.controlify.impl.api.vmousesnapping.ISnapBehaviour; -import dev.isxander.controlify.impl.api.vmousesnapping.SnapPoint; +import dev.isxander.controlify.api.screenop.ISnapBehaviour; +import dev.isxander.controlify.api.screenop.SnapPoint; import net.minecraft.client.gui.screens.inventory.LoomScreen; import net.minecraft.core.Holder; import net.minecraft.world.inventory.LoomMenu; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/ScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/ScreenMixin.java index 3215273cc..3bcbcee54 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/ScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/ScreenMixin.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.virtualmouse.snapping; -import dev.isxander.controlify.impl.api.vmousesnapping.ISnapBehaviour; -import dev.isxander.controlify.impl.api.vmousesnapping.SnapPoint; +import dev.isxander.controlify.api.screenop.ISnapBehaviour; +import dev.isxander.controlify.api.screenop.SnapPoint; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.events.GuiEventListener; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/StonecutterScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/StonecutterScreenMixin.java index 3cdf5e6dd..543d05318 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/StonecutterScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/StonecutterScreenMixin.java @@ -6,8 +6,8 @@ */ package dev.isxander.controlify.mixins.feature.virtualmouse.snapping; -import dev.isxander.controlify.impl.api.vmousesnapping.ISnapBehaviour; -import dev.isxander.controlify.impl.api.vmousesnapping.SnapPoint; +import dev.isxander.controlify.api.screenop.ISnapBehaviour; +import dev.isxander.controlify.api.screenop.SnapPoint; import net.minecraft.client.gui.screens.inventory.StonecutterScreen; import net.minecraft.world.inventory.StonecutterMenu; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/resources/assets/controlify/controller/activators.json b/src/main/resources/assets/controlify/controller/activators.json index 0db3279e4..245f663fb 100644 --- a/src/main/resources/assets/controlify/controller/activators.json +++ b/src/main/resources/assets/controlify/controller/activators.json @@ -1,3 +1,262 @@ { - + "controlify:walk_forward": "continuous", + "controlify:walk_backward": "continuous", + "controlify:strafe_left": "continuous", + "controlify:strafe_right": "continuous", + "controlify:look_up": [ + "continuous", + "pulse_press" + ], + "controlify:look_down": [ + "continuous", + "pulse_press" + ], + "controlify:look_left": [ + "continuous", + "pulse_press" + ], + "controlify:look_right": [ + "continuous", + "pulse_press" + ], + "controlify:gyro_button": [ + "latch_hold", + "pulse_press" + ], + "controlify:jump": [ + "latch_hold", + "pulse_press" + ], + "controlify:sprint": [ + "latch_hold", + "pulse_press" + ], + "controlify:sneak": [ + "latch_hold", + "pulse_press" + ], + "controlify:attack": [ + "latch_hold", + "pulse_press" + ], + "controlify:use": [ + "latch_hold", + "pulse_press" + ], + "controlify:drop": { + "type": "pulse_repeat_latch", + "source_latch": "latch_hold", + "initial_delay_ms": 1000, + "repeat_delay_ms": 50 + }, + "controlify:drop_stack": "pulse_press", + "controlify:pause": "pulse_press", + "controlify:change_perspective": "pulse_press", + "controlify:swap_hands": "pulse_press", + "controlify:next_slot": { + "type": "pulse_repeat_latch", + "source_latch": "latch_hold", + "initial_delay_ms": 500, + "repeat_delay_ms": 200 + }, + "controlify:prev_slot": { + "type": "pulse_repeat_latch", + "source_latch": "latch_hold", + "initial_delay_ms": 500, + "repeat_delay_ms": 200 + }, + "controlify:hotbar_item_select_radial": [ + "latch_hold", + "pulse_press" + ], + "controlify:hotbar_slot_1": "pulse_press", + "controlify:hotbar_slot_2": "pulse_press", + "controlify:hotbar_slot_3": "pulse_press", + "controlify:hotbar_slot_4": "pulse_press", + "controlify:hotbar_slot_5": "pulse_press", + "controlify:hotbar_slot_6": "pulse_press", + "controlify:hotbar_slot_7": "pulse_press", + "controlify:hotbar_slot_8": "pulse_press", + "controlify:hotbar_slot_9": "pulse_press", + "controlify:game_mode_switcher": [ + "latch_hold", + "pulse_press" + ], + "controlify:inventory": "pulse_press", + "controlify:inv_select": "pulse_press", + "controlify:inv_quick_move": "pulse_press", + "controlify:inv_take_half": "pulse_press", + "controlify:drop_inventory": "pulse_press", + "controlify:pick_block": "pulse_press", + "controlify:pick_block_nbt": "pulse_press", + "controlify:hotbar_load_radial": [ + "latch_hold", + "pulse_press" + ], + "controlify:hotbar_save_radial": [ + "latch_hold", + "pulse_press" + ], + "controlify:open_chat": [ + "latch_hold", + "pulse_press" + ], + "controlify:toggle_hud_visibility": "pulse_press", + "controlify:show_player_list": "pulse_press", + "controlify:take_screenshot": "pulse_press", + "controlify:debug_radial": [ + "latch_hold", + "pulse_press" + ], + "controlify:toggle_debug_menu": "pulse_press", + "controlify:toggle_debug_menu_fps": "pulse_press", + "controlify:toggle_debug_menu_net": "pulse_press", + "controlify:toggle_debug_menu_prof": "pulse_press", + "controlify:debug_reload_chunks": "pulse_press", + "controlify:debug_toggle_chunk_borders": "pulse_press", + "controlify:debug_toggle_advanced_tooltips": "pulse_press", + "controlify:debug_toggle_entity_hitboxes": "pulse_press", + "controlify:debug_reload_resource_packs": "pulse_press", + "controlify:debug_clear_chat": "pulse_press", + "controlify:debug_start_stop_profiling": "pulse_press", + "controlify:gui_press": [ + "latch_hold", + "pulse_gui_press" + ], + "controlify:gui_back": [ + "latch_hold", + "pulse_gui_press" + ], + "controlify:gui_next_tab": [ + "latch_hold", + "pulse_press" + ], + "controlify:gui_prev_tab": [ + "latch_hold", + "pulse_press" + ], + "controlify:gui_abstract_action_1": [ + "latch_hold", + "pulse_press" + ], + "controlify:gui_abstract_action_2": [ + "latch_hold", + "pulse_press" + ], + "controlify:gui_abstract_action_3": [ + "latch_hold", + "pulse_press" + ], + "controlify:gui_navi_up": { + "type": "pulse_repeat_latch", + "source_latch": "latch_hold", + "initial_delay_ms": 500, + "repeat_delay_ms": 150 + }, + "controlify:gui_navi_down": { + "type": "pulse_repeat_latch", + "source_latch": "latch_hold", + "initial_delay_ms": 500, + "repeat_delay_ms": 150 + }, + "controlify:gui_navi_left": { + "type": "pulse_repeat_latch", + "source_latch": "latch_hold", + "initial_delay_ms": 500, + "repeat_delay_ms": 150 + }, + "controlify:gui_navi_right": { + "type": "pulse_repeat_latch", + "source_latch": "latch_hold", + "initial_delay_ms": 500, + "repeat_delay_ms": 150 + }, + "controlify:gui_secondary_navi_up": { + "type": "pulse_repeat_latch", + "source_latch": "latch_hold", + "initial_delay_ms": 750, + "repeat_delay_ms": 150 + }, + "controlify:gui_secondary_navi_down": { + "type": "pulse_repeat_latch", + "source_latch": "latch_hold", + "initial_delay_ms": 750, + "repeat_delay_ms": 150 + }, + "controlify:gui_secondary_navi_left": { + "type": "pulse_repeat_latch", + "source_latch": "latch_hold", + "initial_delay_ms": 750, + "repeat_delay_ms": 150 + }, + "controlify:gui_secondary_navi_right": { + "type": "pulse_repeat_latch", + "source_latch": "latch_hold", + "initial_delay_ms": 750, + "repeat_delay_ms": 150 + }, + "controlify:radial_menu": [ + "latch_hold", + "pulse_press" + ], + "controlify:radial_axis_up": "continuous", + "controlify:radial_axis_down": "continuous", + "controlify:radial_axis_left": "continuous", + "controlify:radial_axis_right": "continuous", + "controlify:vmouse_move_up": "continuous", + "controlify:vmouse_move_down": "continuous", + "controlify:vmouse_move_left": "continuous", + "controlify:vmouse_move_right": "continuous", + "controlify:vmouse_snap_up": { + "type": "pulse_repeat_latch", + "source_latch": "latch_hold", + "initial_delay_ms": 500, + "repeat_delay_ms": 150 + }, + "controlify:vmouse_snap_down": { + "type": "pulse_repeat_latch", + "source_latch": "latch_hold", + "initial_delay_ms": 500, + "repeat_delay_ms": 150 + }, + "controlify:vmouse_snap_left": { + "type": "pulse_repeat_latch", + "source_latch": "latch_hold", + "initial_delay_ms": 500, + "repeat_delay_ms": 150 + }, + "controlify:vmouse_snap_right": { + "type": "pulse_repeat_latch", + "source_latch": "latch_hold", + "initial_delay_ms": 500, + "repeat_delay_ms": 150 + }, + "controlify:vmouse_lclick": [ + "latch_hold", + "pulse_press" + ], + "controlify:vmouse_rclick": [ + "latch_hold", + "pulse_press" + ], + "controlify:vmouse_shift_click": [ + "latch_hold", + "pulse_press" + ], + "controlify:vmouse_scroll_down": "continuous", + "controlify:vmouse_scroll_up": "continuous", + "controlify:vmouse_shift": [ + "latch_hold", + "pulse_press" + ], + "controlify:vmouse_page_next": "pulse_press", + "controlify:vmouse_page_prev": "pulse_press", + "controlify:vmouse_page_down": "pulse_press", + "controlify:vmouse_page_up": "pulse_press", + "controlify:vmouse_toggle": "pulse_press", + "voicechat:ptt_hold": "latch_hold", + "voicechat:ptt_toggle": "pulse_press", + "voicechat:whisper_hold": "latch_hold", + "voicechat:whisper_toggle": "pulse_press", + "voicechat:mute_microphone": "pulse_press" } diff --git a/src/main/resources/assets/controlify/controller/contextual/guide/in_game.json b/src/main/resources/assets/controlify/controller/contextual/guide/in_game.json index cf0b937b4..2a4f3ee84 100644 --- a/src/main/resources/assets/controlify/controller/contextual/guide/in_game.json +++ b/src/main/resources/assets/controlify/controller/contextual/guide/in_game.json @@ -631,7 +631,7 @@ "for": "controlify:drop_stack", "where": "right", "if": { "all_of": ["controlify:verbosity_full", "controlify:has_multiple_items_in_main_hand"] }, - "then": { "translate": "controlify.binding.controlify.drop_stack" } + "then": { "translate": "controlify.action.controlify.drop_stack" } }, { "for": "controlify:swap_hands", @@ -649,7 +649,7 @@ "for": "controlify:pick_block_nbt", "where": "right", "if": { "all_of": ["controlify:verbosity_full", "controlify:looking_at_block", "controlify:is_creative"] }, - "then": { "translate": "controlify.binding.controlify.pick_block_nbt" } + "then": { "translate": "controlify.action.controlify.pick_block_nbt" } } ] } diff --git a/src/main/resources/assets/controlify/controller/controller_identification.json5 b/src/main/resources/assets/controlify/controller/controller_identification.json5 index 970dad438..35468c423 100644 --- a/src/main/resources/assets/controlify/controller/controller_identification.json5 +++ b/src/main/resources/assets/controlify/controller/controller_identification.json5 @@ -1,7 +1,7 @@ [ { - "name": "Xbox Controller", - "namespace": "controlify:xbox_one", + "friendly_name": "Xbox Controller", + "id": "controlify:xbox_one", "hids": [ [0x45e, 0x202], // Xbox Controller @@ -29,8 +29,8 @@ ] }, { - "name": "DualShock 4 Controller", - "namespace": "controlify:dualshock4", + "friendly_name": "DualShock 4 Controller", + "id": "controlify:dualshock4", "hids": [ [0x54c, 0x5c4], @@ -39,16 +39,16 @@ ] }, { - "name": "DualShock 3 Controller", - "namespace": "controlify:dualshock3", + "friendly_name": "DualShock 3 Controller", + "id": "controlify:dualshock3", "hids": [ [0x54c, 0x0268] ] }, { - "name": "DualSense Controller", - "namespace": "controlify:dualsense", + "friendly_name": "DualSense Controller", + "id": "controlify:dualsense", "hids": [ [0x54c, 0x0ce6], @@ -56,16 +56,16 @@ ] }, { - "name": "PlayStation Portable", - "namespace": "controlify:psp", + "friendly_name": "PlayStation Portable", + "id": "controlify:psp", "hids": [ [0x0ff, 0x1cb], ] }, { - "name": "Switch Controller", - "namespace": "controlify:switch", + "friendly_name": "Switch Controller", + "id": "controlify:switch", "hids": [ [0x57e, 0x2009], // Many other switch controllers spoof this ID @@ -73,16 +73,16 @@ ] }, { - "name": "Steam Deck", - "namespace": "controlify:steam_deck", + "friendly_name": "Steam Deck", + "id": "controlify:steam_deck", "hids": [ [0x28de, 0x1205], ] }, { - "name": "Steam Controller", - "namespace": "controlify:steam_controller", + "friendly_name": "Steam Controller", + "id": "controlify:steam_controller", "hids": [ [0x28de, 0x1302], // Wired @@ -94,23 +94,23 @@ ] }, { - "name": "Stadia Controller", - "namespace": "controlify:stadia", + "friendly_name": "Stadia Controller", + "id": "controlify:stadia", "hids": [ [0x18d1, 0x9400], ] }, { - "name": "8BitDo Controller", - "namespace": "controlify:switch", + "friendly_name": "8BitDo Controller", + "id": "controlify:switch", "hids": [ [0x2DC8, 0x6001], // SN30 Pro - DInput Mode ] }, { - "name": "Thrustmaster T16000M", + "friendly_name": "Thrustmaster T16000M", "hids": [ [0x044f, 0xb10a] diff --git a/src/main/resources/assets/controlify/controller/default_bind/default.json b/src/main/resources/assets/controlify/controller/default_bind/default.json index c5a2e58a6..75c38572e 100644 --- a/src/main/resources/assets/controlify/controller/default_bind/default.json +++ b/src/main/resources/assets/controlify/controller/default_bind/default.json @@ -1,214 +1,74 @@ { "defaults": { - "controlify:walk_forward": { - "axis": "controlify:axis/left_stick_up" - }, - "controlify:walk_backward": { - "axis": "controlify:axis/left_stick_down" - }, - "controlify:strafe_left": { - "axis": "controlify:axis/left_stick_left" - }, - "controlify:strafe_right": { - "axis": "controlify:axis/left_stick_right" - }, - "controlify:look_up": { - "axis": "controlify:axis/right_stick_up" - }, - "controlify:look_down": { - "axis": "controlify:axis/right_stick_down" - }, - "controlify:look_left": { - "axis": "controlify:axis/right_stick_left" - }, - "controlify:look_right": { - "axis": "controlify:axis/right_stick_right" - }, - "controlify:gyro": { - "type": "empty" - }, - "controlify:jump": { - "button": "controlify:button/south" - }, - "controlify:sprint": { - "button": "controlify:button/left_stick" - }, - "controlify:sneak": { - "button": "controlify:button/right_stick" - }, - "controlify:attack": { - "axis": "controlify:axis/right_trigger" - }, - "controlify:use": { - "axis": "controlify:axis/left_trigger" - }, - "controlify:drop": { - "button": "controlify:button/dpad_down" - }, - "controlify:drop_inventory": { - "button": "controlify:button/north" - }, - "controlify:next_slot": { - "button": "controlify:button/right_shoulder" - }, - "controlify:prev_slot": { - "button": "controlify:button/left_shoulder" - }, - "controlify:pause": { - "button": "controlify:button/start" - }, - "controlify:inventory": { - "button": "controlify:button/north" - }, - "controlify:change_perspective": { - "button": "controlify:button/back" - }, - "controlify:swap_hands": { - "button": "controlify:button/west" - }, - "controlify:open_chat": { - "button": "controlify:button/dpad_up" - }, - "controlify:gui_press": { - "button": "controlify:button/south" - }, - "controlify:gui_back": { - "button": "controlify:button/east" - }, - "controlify:gui_next_tab": { - "button": "controlify:button/right_shoulder" - }, - "controlify:gui_prev_tab": { - "button": "controlify:button/left_shoulder" - }, - "controlify:gui_abstract_action_1": { - "button": "controlify:button/west" - }, - "controlify:gui_abstract_action_2": { - "button": "controlify:button/north" - }, - "controlify:gui_abstract_action_3": { - "button": "controlify:button/left_stick" - }, - "controlify:inv_select": { - "button": "controlify:button/south" - }, - "controlify:inv_quick_move": { - "button": "controlify:button/north" - }, - "controlify:inv_take_half": { - "button": "controlify:button/west" - }, - "controlify:bundle_navi_up": { - "axis": "controlify:axis/right_stick_up" - }, - "controlify:bundle_navi_down": { - "axis": "controlify:axis/right_stick_down" - }, - "controlify:bundle_navi_left": { - "axis": "controlify:axis/right_stick_left" - }, - "controlify:bundle_navi_right": { - "axis": "controlify:axis/right_stick_right" - }, - "controlify:pick_block": { - "button": "controlify:button/dpad_left" - }, - "controlify:radial_menu": { - "button": "controlify:button/dpad_right" - }, - "controlify:radial_axis_up": { - "axis": "controlify:axis/right_stick_up" - }, - "controlify:radial_axis_down": { - "axis": "controlify:axis/right_stick_down" - }, - "controlify:radial_axis_left": { - "axis": "controlify:axis/right_stick_left" - }, - "controlify:radial_axis_right": { - "axis": "controlify:axis/right_stick_right" - }, - "controlify:vmouse_move_up": { - "axis": "controlify:axis/left_stick_up" - }, - "controlify:vmouse_move_down": { - "axis": "controlify:axis/left_stick_down" - }, - "controlify:vmouse_move_left": { - "axis": "controlify:axis/left_stick_left" - }, - "controlify:vmouse_move_right": { - "axis": "controlify:axis/left_stick_right" - }, - "controlify:vmouse_lclick": { - "button": "controlify:button/south" - }, - "controlify:vmouse_rclick": { - "button": "controlify:button/west" - }, - "controlify:vmouse_shift_click": { - "button": "controlify:button/north" - }, - "controlify:vmouse_snap_up": { - "button": "controlify:button/dpad_up" - }, - "controlify:vmouse_snap_down": { - "button": "controlify:button/dpad_down" - }, - "controlify:vmouse_snap_left": { - "button": "controlify:button/dpad_left" - }, - "controlify:vmouse_snap_right": { - "button": "controlify:button/dpad_right" - }, - "controlify:vmouse_scroll_up": { - "axis": "controlify:axis/right_stick_up" - }, - "controlify:vmouse_scroll_down": { - "axis": "controlify:axis/right_stick_down" - }, - "controlify:vmouse_shift": { - "button": "controlify:button/left_stick" - }, - "controlify:vmouse_toggle": { - "button": "controlify:button/back" - }, - "controlify:vmouse_page_next": { - "button": "controlify:button/right_shoulder" - }, - "controlify:vmouse_page_prev": { - "button": "controlify:button/left_shoulder" - }, - "controlify:vmouse_page_down": { - "axis": "controlify:axis/right_trigger" - }, - "controlify:vmouse_page_up": { - "axis": "controlify:axis/left_trigger" - }, - "controlify:gui_navi_up": { - "axis": "controlify:axis/left_stick_up" - }, - "controlify:gui_navi_down": { - "axis": "controlify:axis/left_stick_down" - }, - "controlify:gui_navi_left": { - "axis": "controlify:axis/left_stick_left" - }, - "controlify:gui_navi_right": { - "axis": "controlify:axis/left_stick_right" - }, - "controlify:gui_secondary_navi_up": { - "axis": "controlify:axis/right_stick_up" - }, - "controlify:gui_secondary_navi_down": { - "axis": "controlify:axis/right_stick_down" - }, - "controlify:gui_secondary_navi_left": { - "axis": "controlify:axis/right_stick_left" - }, - "controlify:gui_secondary_navi_right": { - "axis": "controlify:axis/right_stick_right" - } + "controlify:walk_forward": "controlify:axis/left_stick_up", + "controlify:walk_backward": "controlify:axis/left_stick_down", + "controlify:strafe_left": "controlify:axis/left_stick_left", + "controlify:strafe_right": "controlify:axis/left_stick_right", + "controlify:look_up": "controlify:axis/right_stick_up", + "controlify:look_down": "controlify:axis/right_stick_down", + "controlify:look_left": "controlify:axis/right_stick_left", + "controlify:look_right": "controlify:axis/right_stick_right", + "controlify:gyro": {}, + "controlify:jump": "controlify:button/south", + "controlify:sprint": "controlify:button/left_stick", + "controlify:sneak": "controlify:button/right_stick", + "controlify:attack": "controlify:axis/right_trigger", + "controlify:use": "controlify:axis/left_trigger", + "controlify:drop": "controlify:button/dpad_down", + "controlify:drop_inventory": "controlify:button/north", + "controlify:next_slot": "controlify:button/right_shoulder", + "controlify:prev_slot": "controlify:button/left_shoulder", + "controlify:pause": "controlify:button/start", + "controlify:inventory": "controlify:button/north", + "controlify:change_perspective": "controlify:button/back", + "controlify:swap_hands": "controlify:button/west", + "controlify:open_chat": "controlify:button/dpad_up", + "controlify:gui_press": "controlify:button/south", + "controlify:gui_back": "controlify:button/east", + "controlify:gui_next_tab": "controlify:button/right_shoulder", + "controlify:gui_prev_tab": "controlify:button/left_shoulder", + "controlify:gui_abstract_action_1": "controlify:button/west", + "controlify:gui_abstract_action_2": "controlify:button/north", + "controlify:gui_abstract_action_3": "controlify:button/left_stick", + "controlify:inv_select": "controlify:button/south", + "controlify:inv_quick_move": "controlify:button/north", + "controlify:inv_take_half": "controlify:button/west", + "controlify:bundle_navi_up": "controlify:axis/right_stick_up", + "controlify:bundle_navi_down": "controlify:axis/right_stick_down", + "controlify:bundle_navi_left": "controlify:axis/right_stick_left", + "controlify:bundle_navi_right": "controlify:axis/right_stick_right", + "controlify:pick_block": "controlify:button/dpad_left", + "controlify:radial_menu": "controlify:button/dpad_right", + "controlify:radial_axis_up": "controlify:axis/right_stick_up", + "controlify:radial_axis_down": "controlify:axis/right_stick_down", + "controlify:radial_axis_left": "controlify:axis/right_stick_left", + "controlify:radial_axis_right": "controlify:axis/right_stick_right", + "controlify:vmouse_move_up": "controlify:axis/left_stick_up", + "controlify:vmouse_move_down": "controlify:axis/left_stick_down", + "controlify:vmouse_move_left": "controlify:axis/left_stick_left", + "controlify:vmouse_move_right": "controlify:axis/left_stick_right", + "controlify:vmouse_lclick": "controlify:button/south", + "controlify:vmouse_rclick": "controlify:button/west", + "controlify:vmouse_shift_click": "controlify:button/north", + "controlify:vmouse_snap_up": "controlify:button/dpad_up", + "controlify:vmouse_snap_down": "controlify:button/dpad_down", + "controlify:vmouse_snap_left": "controlify:button/dpad_left", + "controlify:vmouse_snap_right": "controlify:button/dpad_right", + "controlify:vmouse_scroll_up": "controlify:axis/right_stick_up", + "controlify:vmouse_scroll_down": "controlify:axis/right_stick_down", + "controlify:vmouse_shift": "controlify:button/left_stick", + "controlify:vmouse_toggle": "controlify:button/back", + "controlify:vmouse_page_next": "controlify:button/right_shoulder", + "controlify:vmouse_page_prev": "controlify:button/left_shoulder", + "controlify:vmouse_page_down": "controlify:axis/right_trigger", + "controlify:vmouse_page_up": "controlify:axis/left_trigger", + "controlify:gui_navi_up": "controlify:axis/left_stick_up", + "controlify:gui_navi_down": "controlify:axis/left_stick_down", + "controlify:gui_navi_left": "controlify:axis/left_stick_left", + "controlify:gui_navi_right": "controlify:axis/left_stick_right", + "controlify:gui_secondary_navi_up": "controlify:axis/right_stick_up", + "controlify:gui_secondary_navi_down": "controlify:axis/right_stick_down", + "controlify:gui_secondary_navi_left": "controlify:axis/right_stick_left", + "controlify:gui_secondary_navi_right": "controlify:axis/right_stick_right" } } diff --git a/src/main/resources/assets/controlify/controller/default_bind/dualsense.json b/src/main/resources/assets/controlify/controller/default_bind/dualsense.json index e8317a45c..9f4e34245 100644 --- a/src/main/resources/assets/controlify/controller/default_bind/dualsense.json +++ b/src/main/resources/assets/controlify/controller/default_bind/dualsense.json @@ -1,7 +1,5 @@ { "defaults": { - "voicechat:mute_microphone": { - "button": "controlify:button/misc_1" - } + "voicechat:mute_microphone": "controlify:button/misc_1" } } diff --git a/src/main/resources/assets/controlify/controller/default_bind/switch.json b/src/main/resources/assets/controlify/controller/default_bind/switch.json index c58782b34..a44d29afb 100644 --- a/src/main/resources/assets/controlify/controller/default_bind/switch.json +++ b/src/main/resources/assets/controlify/controller/default_bind/switch.json @@ -1,7 +1,5 @@ { "defaults": { - "controlify:take_screenshot": { - "button": "controlify:button/misc_1" - } + "controlify:take_screenshot": "controlify:button/misc_1" } } diff --git a/src/main/resources/assets/controlify/controller/default_config.json b/src/main/resources/assets/controlify/controller/default_config.json index f049f76d1..53a32fae7 100644 --- a/src/main/resources/assets/controlify/controller/default_config.json +++ b/src/main/resources/assets/controlify/controller/default_config.json @@ -72,7 +72,8 @@ "invert_yaw": false, "button_mode": "on", "yaw_mode": "yaw", - "flick_stick": false + "flick_stick": false, + "low_pass_cutoff_hz": 0 }, "bluetooth_device": { "dont_show_warning": false diff --git a/src/main/resources/assets/controlify/lang/de_de.json b/src/main/resources/assets/controlify/lang/de_de.json index ceb640a33..f791fffea 100644 --- a/src/main/resources/assets/controlify/lang/de_de.json +++ b/src/main/resources/assets/controlify/lang/de_de.json @@ -101,58 +101,58 @@ "controlify.sdl3_onboarding.message_mac": "Auf MacOS werden die Dateien für jegliche Kontroller-Erkennung benötigt. Ohne werden alle Kontroller unidentifiziert bleiben und die Nutzererfahrung erheblich beeinträchtigt.", "controlify.sdl3_onboarding.question": "Würdest du sie gerne herunterladen?", "controlify.controller_theme.default": "Standard", - "controlify.binding.controlify.walk_forward": "Vorwärts", - "controlify.binding.controlify.walk_backward": "Rückwärts", - "controlify.binding.controlify.strafe_left": "Links", - "controlify.binding.controlify.strafe_right": "Rechts", - "controlify.binding.controlify.look_up": "Hochgucken", - "controlify.binding.controlify.look_down": "Runtergucken", - "controlify.binding.controlify.look_left": "Nach links gucken", - "controlify.binding.controlify.look_right": "Nach rechts gucken", - "controlify.binding.controlify.jump": "Springen", - "controlify.binding.controlify.sneak": "Schleichen", - "controlify.binding.controlify.attack": "Angreifen/Abbauen", - "controlify.binding.controlify.sprint": "Sprinten", - "controlify.binding.controlify.next_slot": "Nächstes Schnellzugriffsfach", - "controlify.binding.controlify.prev_slot": "Vorheriges Schnellzugriffsfach", - "controlify.binding.controlify.pause": "Pausieren", - "controlify.binding.controlify.inventory": "Inventar öffnen/schließen", - "controlify.binding.controlify.change_perspective": "Perspektive wechseln", - "controlify.binding.controlify.swap_hands": "Gegenstand mit Zweithand tauschen", - "controlify.binding.controlify.open_chat": "Chat öffnen", - "controlify.binding.controlify.gui_press": "GUI bestätigen", - "controlify.binding.controlify.gui_back": "GUI zurück", - "controlify.binding.controlify.gui_next_tab": "GUI nächster Tab", - "controlify.binding.controlify.gui_prev_tab": "GUI vorheriger Tab", - "controlify.binding.controlify.gui_abstract_action_1": "GUI abstrakte Aktion 1", - "controlify.binding.controlify.gui_abstract_action_1.desc": "Eine generische Taste, die abhängig vom aktuellen Bildschirm verschiedenes macht.", - "controlify.binding.controlify.gui_abstract_action_2": "GUI abstrakte Aktion 2", - "controlify.binding.controlify.gui_abstract_action_2.desc": "Eine generische Taste, die abhängig vom aktuellen Bildschirm verschiedenes macht.", - "controlify.binding.controlify.gui_abstract_action_3.desc": "Eine generische Taste, die abhängig vom aktuellen Bildschirm verschiedenes macht.", - "controlify.binding.controlify.inv_select": "Gegenstand auswählen", - "controlify.binding.controlify.inv_quick_move": "Schneller Transfer", - "controlify.binding.controlify.inv_take_half": "Hälfte nehmen", - "controlify.binding.controlify.drop": "Gegenstand fallen lassen", - "controlify.binding.controlify.pick_block": "Block auswählen", - "controlify.binding.controlify.toggle_hud_visibility": "HUD Sichtbarkeit umschalten", - "controlify.binding.controlify.show_player_list": "Spieler auflisten", - "controlify.binding.controlify.vmouse_move_up": "VMaus hochbewegen", - "controlify.binding.controlify.vmouse_move_down": "VMaus runterbewegen", - "controlify.binding.controlify.vmouse_move_left": "VMaus nach links bewegen", - "controlify.binding.controlify.vmouse_move_right": "VMaus nach rechts bewegen", - "controlify.binding.controlify.vmouse_lclick": "VMaus LKlick", - "controlify.binding.controlify.vmouse_rclick": "VMaus RKlick", - "controlify.binding.controlify.vmouse_shift_click": "VMaus Umschalt-Klick", - "controlify.binding.controlify.vmouse_scroll_up": "VMaus hochscrollen", - "controlify.binding.controlify.vmouse_scroll_down": "VMaus runterscrollen", - "controlify.binding.controlify.vmouse_escape": "VMaus Esc-Taste", - "controlify.binding.controlify.vmouse_shift": "VMaus Umschalt-Taste", - "controlify.binding.controlify.vmouse_toggle": "Wechsel virtuelle Maus", - "controlify.binding.controlify.gui_navi_down": "GUI Navi runter", - "controlify.binding.controlify.gui_navi_up": "GUI Navi hoch", - "controlify.binding.controlify.gui_navi_left": "GUI Navi links", - "controlify.binding.controlify.gui_navi_right": "GUI Navi rechts", - "controlify.binding.controlify.clear_binding": "Belegung zurücksetzen", + "controlify.action.controlify.walk_forward": "Vorwärts", + "controlify.action.controlify.walk_backward": "Rückwärts", + "controlify.action.controlify.strafe_left": "Links", + "controlify.action.controlify.strafe_right": "Rechts", + "controlify.action.controlify.look_up": "Hochgucken", + "controlify.action.controlify.look_down": "Runtergucken", + "controlify.action.controlify.look_left": "Nach links gucken", + "controlify.action.controlify.look_right": "Nach rechts gucken", + "controlify.action.controlify.jump": "Springen", + "controlify.action.controlify.sneak": "Schleichen", + "controlify.action.controlify.attack": "Angreifen/Abbauen", + "controlify.action.controlify.sprint": "Sprinten", + "controlify.action.controlify.next_slot": "Nächstes Schnellzugriffsfach", + "controlify.action.controlify.prev_slot": "Vorheriges Schnellzugriffsfach", + "controlify.action.controlify.pause": "Pausieren", + "controlify.action.controlify.inventory": "Inventar öffnen/schließen", + "controlify.action.controlify.change_perspective": "Perspektive wechseln", + "controlify.action.controlify.swap_hands": "Gegenstand mit Zweithand tauschen", + "controlify.action.controlify.open_chat": "Chat öffnen", + "controlify.action.controlify.gui_press": "GUI bestätigen", + "controlify.action.controlify.gui_back": "GUI zurück", + "controlify.action.controlify.gui_next_tab": "GUI nächster Tab", + "controlify.action.controlify.gui_prev_tab": "GUI vorheriger Tab", + "controlify.action.controlify.gui_abstract_action_1": "GUI abstrakte Aktion 1", + "controlify.action.controlify.gui_abstract_action_1.desc": "Eine generische Taste, die abhängig vom aktuellen Bildschirm verschiedenes macht.", + "controlify.action.controlify.gui_abstract_action_2": "GUI abstrakte Aktion 2", + "controlify.action.controlify.gui_abstract_action_2.desc": "Eine generische Taste, die abhängig vom aktuellen Bildschirm verschiedenes macht.", + "controlify.action.controlify.gui_abstract_action_3.desc": "Eine generische Taste, die abhängig vom aktuellen Bildschirm verschiedenes macht.", + "controlify.action.controlify.inv_select": "Gegenstand auswählen", + "controlify.action.controlify.inv_quick_move": "Schneller Transfer", + "controlify.action.controlify.inv_take_half": "Hälfte nehmen", + "controlify.action.controlify.drop": "Gegenstand fallen lassen", + "controlify.action.controlify.pick_block": "Block auswählen", + "controlify.action.controlify.toggle_hud_visibility": "HUD Sichtbarkeit umschalten", + "controlify.action.controlify.show_player_list": "Spieler auflisten", + "controlify.action.controlify.vmouse_move_up": "VMaus hochbewegen", + "controlify.action.controlify.vmouse_move_down": "VMaus runterbewegen", + "controlify.action.controlify.vmouse_move_left": "VMaus nach links bewegen", + "controlify.action.controlify.vmouse_move_right": "VMaus nach rechts bewegen", + "controlify.action.controlify.vmouse_lclick": "VMaus LKlick", + "controlify.action.controlify.vmouse_rclick": "VMaus RKlick", + "controlify.action.controlify.vmouse_shift_click": "VMaus Umschalt-Klick", + "controlify.action.controlify.vmouse_scroll_up": "VMaus hochscrollen", + "controlify.action.controlify.vmouse_scroll_down": "VMaus runterscrollen", + "controlify.action.controlify.vmouse_escape": "VMaus Esc-Taste", + "controlify.action.controlify.vmouse_shift": "VMaus Umschalt-Taste", + "controlify.action.controlify.vmouse_toggle": "Wechsel virtuelle Maus", + "controlify.action.controlify.gui_navi_down": "GUI Navi runter", + "controlify.action.controlify.gui_navi_up": "GUI Navi hoch", + "controlify.action.controlify.gui_navi_left": "GUI Navi links", + "controlify.action.controlify.gui_navi_right": "GUI Navi rechts", + "controlify.action.controlify.clear_binding": "Belegung zurücksetzen", "controlify.input.controlify.button/left_stick": "Knüppel links", "controlify.input.controlify.button/right_stick": "Knüppel rechts", "controlify.binding_category.gui": "Benutzeroberfläche", diff --git a/src/main/resources/assets/controlify/lang/en_us.json b/src/main/resources/assets/controlify/lang/en_us.json index 3fde22ea2..308460358 100644 --- a/src/main/resources/assets/controlify/lang/en_us.json +++ b/src/main/resources/assets/controlify/lang/en_us.json @@ -294,105 +294,105 @@ "controlify.bluetooth_warning.desc": "Some controllers may be missing features when connected via Bluetooth. For example, HD haptics on DualSense. If you think you are missing a feature, try using USB before reporting.", "controlify.bluetooth_warning.dont_show": "Don't show again", - "controlify.binding.controlify.walk_forward": "Walk Forward", - "controlify.binding.controlify.walk_backward": "Walk Backward", - "controlify.binding.controlify.strafe_left": "Strafe Left", - "controlify.binding.controlify.strafe_right": "Strafe Right", - "controlify.binding.controlify.look_up": "Look Up", - "controlify.binding.controlify.look_down": "Look Down", - "controlify.binding.controlify.look_left": "Look Left", - "controlify.binding.controlify.look_right": "Look Right", - "controlify.binding.controlify.gyro_button": "Gyro Button", - "controlify.binding.controlify.jump": "Jump", - "controlify.binding.controlify.sneak": "Sneak", - "controlify.binding.controlify.attack": "Attack", - "controlify.binding.controlify.use": "Use", - "controlify.binding.controlify.sprint": "Sprint", - "controlify.binding.controlify.next_slot": "Next Hotbar Slot", - "controlify.binding.controlify.prev_slot": "Previous Hotbar Slot", - "controlify.binding.controlify.hotbar_slot_1": "Hotbar Slot 1", - "controlify.binding.controlify.hotbar_slot_2": "Hotbar Slot 2", - "controlify.binding.controlify.hotbar_slot_3": "Hotbar Slot 3", - "controlify.binding.controlify.hotbar_slot_4": "Hotbar Slot 4", - "controlify.binding.controlify.hotbar_slot_5": "Hotbar Slot 5", - "controlify.binding.controlify.hotbar_slot_6": "Hotbar Slot 6", - "controlify.binding.controlify.hotbar_slot_7": "Hotbar Slot 7", - "controlify.binding.controlify.hotbar_slot_8": "Hotbar Slot 8", - "controlify.binding.controlify.hotbar_slot_9": "Hotbar Slot 9", - "controlify.binding.controlify.hotbar_load_radial": "Load Creative Hotbar", - "controlify.binding.controlify.hotbar_save_radial": "Save Creative Hotbar", - "controlify.binding.controlify.hotbar_item_select_radial": "Hotbar Slot Select Radial", - "controlify.binding.controlify.pause": "Pause Game", - "controlify.binding.controlify.inventory": "Open Inventory", - "controlify.binding.controlify.change_perspective": "Change Perspective", - "controlify.binding.controlify.swap_hands": "Swap Hands", - "controlify.binding.controlify.open_chat": "Open Chat", - "controlify.binding.controlify.gui_press": "GUI Press", - "controlify.binding.controlify.gui_back": "GUI Back", - "controlify.binding.controlify.gui_next_tab": "GUI Next Tab", - "controlify.binding.controlify.gui_prev_tab": "GUI Previous Tab", - "controlify.binding.controlify.gui_abstract_action_1": "GUI Abstract Action 1", - "controlify.binding.controlify.gui_abstract_action_1.desc": "A generic binding that may do different things depending on the current screen.", - "controlify.binding.controlify.gui_abstract_action_2": "GUI Abstract Action 2", - "controlify.binding.controlify.gui_abstract_action_2.desc": "A generic binding that may do different things depending on the current screen.", - "controlify.binding.controlify.gui_abstract_action_3": "GUI Abstract Action 3", - "controlify.binding.controlify.gui_abstract_action_3.desc": "A generic binding that may do different things depending on the current screen.", - "controlify.binding.controlify.inv_select": "Select Item", - "controlify.binding.controlify.inv_quick_move": "Quick Move", - "controlify.binding.controlify.inv_take_half": "Take Half", - "controlify.binding.controlify.drop": "Drop Item", - "controlify.binding.controlify.drop_stack": "Drop Stack", - "controlify.binding.controlify.drop_inventory": "Drop Item (In Containers)", - "controlify.binding.controlify.pick_block": "Pick Block", - "controlify.binding.controlify.pick_block_nbt": "Pick Block (with NBT)", - "controlify.binding.controlify.take_screenshot": "Take Screenshot", - "controlify.binding.controlify.debug_radial": "Debug Radial Menu", - "controlify.binding.controlify.toggle_debug_menu": "Toggle Debug Menu (F3)", - "controlify.binding.controlify.toggle_debug_menu_fps": "Toggle Debug Menu (F3) with FPS", - "controlify.binding.controlify.toggle_debug_menu_prof": "Toggle Debug Menu (F3) with Profiler", - "controlify.binding.controlify.toggle_debug_menu_net": "Toggle Debug Menu (F3) with Net Graph", - "controlify.binding.controlify.toggle_debug_menu_chart": "Toggle Debug Menu (F3) with Charts", - "controlify.binding.controlify.toggle_hud_visibility": "Toggle HUD Visibility", - "controlify.binding.controlify.show_player_list": "Show Player List", - "controlify.binding.controlify.radial_menu": "Radial Menu", - "controlify.binding.controlify.radial_axis_up": "Radial Menu Up", - "controlify.binding.controlify.radial_axis_down": "Radial Menu Down", - "controlify.binding.controlify.radial_axis_left": "Radial Menu Left", - "controlify.binding.controlify.radial_axis_right": "Radial Menu Right", - "controlify.binding.controlify.game_mode_switcher": "Game Mode Switcher", - "controlify.binding.controlify.vmouse_move_up": "VMouse Move Up", - "controlify.binding.controlify.vmouse_move_down": "VMouse Move Down", - "controlify.binding.controlify.vmouse_move_left": "VMouse Move Left", - "controlify.binding.controlify.vmouse_move_right": "VMouse Move Right", - "controlify.binding.controlify.vmouse_lclick": "VMouse LClick", - "controlify.binding.controlify.vmouse_rclick": "VMouse RClick", - "controlify.binding.controlify.vmouse_shift_click": "VMouse Shift Click", - "controlify.binding.controlify.vmouse_snap_up": "Snap Mouse Up", - "controlify.binding.controlify.vmouse_snap_down": "Snap Mouse Down", - "controlify.binding.controlify.vmouse_snap_left": "Snap Mouse Left", - "controlify.binding.controlify.vmouse_snap_right": "Snap Mouse Right", - "controlify.binding.controlify.vmouse_scroll_up": "VMouse Scroll Up", - "controlify.binding.controlify.vmouse_scroll_down": "VMouse Scroll Down", - "controlify.binding.controlify.vmouse_escape": "VMouse Key Escape", - "controlify.binding.controlify.vmouse_shift": "VMouse Key Shift", - "controlify.binding.controlify.vmouse_page_next": "VMouse Page Next", - "controlify.binding.controlify.vmouse_page_prev": "VMouse Page Prev", - "controlify.binding.controlify.vmouse_page_down": "VMouse Page Down", - "controlify.binding.controlify.vmouse_page_up": "VMouse Page Up", - "controlify.binding.controlify.vmouse_toggle": "Toggle Virtual Mouse", - "controlify.binding.controlify.gui_navi_down": "GUI Navi Down", - "controlify.binding.controlify.gui_navi_up": "GUI Navi Up", - "controlify.binding.controlify.gui_navi_left": "GUI Navi Left", - "controlify.binding.controlify.gui_navi_right": "GUI Navi Right", - "controlify.binding.controlify.gui_secondary_navi_down": "GUI Secondary Navi Down", - "controlify.binding.controlify.gui_secondary_navi_up": "GUI Secondary Navi Up", - "controlify.binding.controlify.gui_secondary_navi_left": "GUI Secondary Navi Left", - "controlify.binding.controlify.gui_secondary_navi_right": "GUI Secondary Navi Right", - "controlify.binding.controlify.clear_binding": "Clear Binding", - - "controlify.binding.voicechat.ptt_hold": "Push-To-Talk (Hold)", - "controlify.binding.voicechat.ptt_toggle": "Push-To-Talk (Toggle)", - "controlify.binding.voicechat.mute_microphone": "Mute Microphone", + "controlify.action.controlify.walk_forward": "Walk Forward", + "controlify.action.controlify.walk_backward": "Walk Backward", + "controlify.action.controlify.strafe_left": "Strafe Left", + "controlify.action.controlify.strafe_right": "Strafe Right", + "controlify.action.controlify.look_up": "Look Up", + "controlify.action.controlify.look_down": "Look Down", + "controlify.action.controlify.look_left": "Look Left", + "controlify.action.controlify.look_right": "Look Right", + "controlify.action.controlify.gyro_button": "Gyro Button", + "controlify.action.controlify.jump": "Jump", + "controlify.action.controlify.sneak": "Sneak", + "controlify.action.controlify.attack": "Attack", + "controlify.action.controlify.use": "Use", + "controlify.action.controlify.sprint": "Sprint", + "controlify.action.controlify.next_slot": "Next Hotbar Slot", + "controlify.action.controlify.prev_slot": "Previous Hotbar Slot", + "controlify.action.controlify.hotbar_slot_1": "Hotbar Slot 1", + "controlify.action.controlify.hotbar_slot_2": "Hotbar Slot 2", + "controlify.action.controlify.hotbar_slot_3": "Hotbar Slot 3", + "controlify.action.controlify.hotbar_slot_4": "Hotbar Slot 4", + "controlify.action.controlify.hotbar_slot_5": "Hotbar Slot 5", + "controlify.action.controlify.hotbar_slot_6": "Hotbar Slot 6", + "controlify.action.controlify.hotbar_slot_7": "Hotbar Slot 7", + "controlify.action.controlify.hotbar_slot_8": "Hotbar Slot 8", + "controlify.action.controlify.hotbar_slot_9": "Hotbar Slot 9", + "controlify.action.controlify.hotbar_load_radial": "Load Creative Hotbar", + "controlify.action.controlify.hotbar_save_radial": "Save Creative Hotbar", + "controlify.action.controlify.hotbar_item_select_radial": "Hotbar Slot Select Radial", + "controlify.action.controlify.pause": "Pause Game", + "controlify.action.controlify.inventory": "Open Inventory", + "controlify.action.controlify.change_perspective": "Change Perspective", + "controlify.action.controlify.swap_hands": "Swap Hands", + "controlify.action.controlify.open_chat": "Open Chat", + "controlify.action.controlify.gui_press": "GUI Press", + "controlify.action.controlify.gui_back": "GUI Back", + "controlify.action.controlify.gui_next_tab": "GUI Next Tab", + "controlify.action.controlify.gui_prev_tab": "GUI Previous Tab", + "controlify.action.controlify.gui_abstract_action_1": "GUI Abstract Action 1", + "controlify.action.controlify.gui_abstract_action_1.desc": "A generic binding that may do different things depending on the current screen.", + "controlify.action.controlify.gui_abstract_action_2": "GUI Abstract Action 2", + "controlify.action.controlify.gui_abstract_action_2.desc": "A generic binding that may do different things depending on the current screen.", + "controlify.action.controlify.gui_abstract_action_3": "GUI Abstract Action 3", + "controlify.action.controlify.gui_abstract_action_3.desc": "A generic binding that may do different things depending on the current screen.", + "controlify.action.controlify.inv_select": "Select Item", + "controlify.action.controlify.inv_quick_move": "Quick Move", + "controlify.action.controlify.inv_take_half": "Take Half", + "controlify.action.controlify.drop": "Drop Item", + "controlify.action.controlify.drop_stack": "Drop Stack", + "controlify.action.controlify.drop_inventory": "Drop Item (In Containers)", + "controlify.action.controlify.pick_block": "Pick Block", + "controlify.action.controlify.pick_block_nbt": "Pick Block (with NBT)", + "controlify.action.controlify.take_screenshot": "Take Screenshot", + "controlify.action.controlify.debug_radial": "Debug Radial Menu", + "controlify.action.controlify.toggle_debug_menu": "Toggle Debug Menu (F3)", + "controlify.action.controlify.toggle_debug_menu_fps": "Toggle Debug Menu (F3) with FPS", + "controlify.action.controlify.toggle_debug_menu_prof": "Toggle Debug Menu (F3) with Profiler", + "controlify.action.controlify.toggle_debug_menu_net": "Toggle Debug Menu (F3) with Net Graph", + "controlify.action.controlify.toggle_debug_menu_chart": "Toggle Debug Menu (F3) with Charts", + "controlify.action.controlify.toggle_hud_visibility": "Toggle HUD Visibility", + "controlify.action.controlify.show_player_list": "Show Player List", + "controlify.action.controlify.radial_menu": "Radial Menu", + "controlify.action.controlify.radial_axis_up": "Radial Menu Up", + "controlify.action.controlify.radial_axis_down": "Radial Menu Down", + "controlify.action.controlify.radial_axis_left": "Radial Menu Left", + "controlify.action.controlify.radial_axis_right": "Radial Menu Right", + "controlify.action.controlify.game_mode_switcher": "Game Mode Switcher", + "controlify.action.controlify.vmouse_move_up": "VMouse Move Up", + "controlify.action.controlify.vmouse_move_down": "VMouse Move Down", + "controlify.action.controlify.vmouse_move_left": "VMouse Move Left", + "controlify.action.controlify.vmouse_move_right": "VMouse Move Right", + "controlify.action.controlify.vmouse_lclick": "VMouse LClick", + "controlify.action.controlify.vmouse_rclick": "VMouse RClick", + "controlify.action.controlify.vmouse_shift_click": "VMouse Shift Click", + "controlify.action.controlify.vmouse_snap_up": "Snap Mouse Up", + "controlify.action.controlify.vmouse_snap_down": "Snap Mouse Down", + "controlify.action.controlify.vmouse_snap_left": "Snap Mouse Left", + "controlify.action.controlify.vmouse_snap_right": "Snap Mouse Right", + "controlify.action.controlify.vmouse_scroll_up": "VMouse Scroll Up", + "controlify.action.controlify.vmouse_scroll_down": "VMouse Scroll Down", + "controlify.action.controlify.vmouse_escape": "VMouse Key Escape", + "controlify.action.controlify.vmouse_shift": "VMouse Key Shift", + "controlify.action.controlify.vmouse_page_next": "VMouse Page Next", + "controlify.action.controlify.vmouse_page_prev": "VMouse Page Prev", + "controlify.action.controlify.vmouse_page_down": "VMouse Page Down", + "controlify.action.controlify.vmouse_page_up": "VMouse Page Up", + "controlify.action.controlify.vmouse_toggle": "Toggle Virtual Mouse", + "controlify.action.controlify.gui_navi_down": "GUI Navi Down", + "controlify.action.controlify.gui_navi_up": "GUI Navi Up", + "controlify.action.controlify.gui_navi_left": "GUI Navi Left", + "controlify.action.controlify.gui_navi_right": "GUI Navi Right", + "controlify.action.controlify.gui_secondary_navi_down": "GUI Secondary Navi Down", + "controlify.action.controlify.gui_secondary_navi_up": "GUI Secondary Navi Up", + "controlify.action.controlify.gui_secondary_navi_left": "GUI Secondary Navi Left", + "controlify.action.controlify.gui_secondary_navi_right": "GUI Secondary Navi Right", + "controlify.action.controlify.clear_binding": "Clear Binding", + + "controlify.action.voicechat.ptt_hold": "Push-To-Talk (Hold)", + "controlify.action.voicechat.ptt_toggle": "Push-To-Talk (Toggle)", + "controlify.action.voicechat.mute_microphone": "Mute Microphone", "controlify.input.controlify.button/south": "South Face", "controlify.input.controlify.button/east": "East Face", @@ -576,13 +576,13 @@ "controlify.radial.debug.overlay_prof": "Show Overlay with Profiler", "controlify.radial.debug.overlay_chart": "Show Overlay with Charts", "controlify.radial.debug.hide_overlay": "Hide Overlay", - "controlify.radial.debug.reload_chunks": "Reload Chunks", - "controlify.radial.debug.chunk_borders": "Toggle Chunk Boundaries", - "controlify.radial.debug.advanced_tooltips": "Toggle Advanced Tooltips", - "controlify.radial.debug.entity_hitboxes": "Toggle Entity Hitboxes", - "controlify.radial.debug.reload_packs": "Reload Resource Packs", - "controlify.radial.debug.clear_chat": "Clear Chat", - "controlify.radial.debug.profile": "Start/Stop Profiling", + "controlify.action.controlify.debug_reload_chunks": "Reload Chunks", + "controlify.action.controlify.debug_toggle_chunk_borders": "Toggle Chunk Boundaries", + "controlify.action.controlify.debug_toggle_advanced_tooltips": "Toggle Advanced Tooltips", + "controlify.action.controlify.debug_toggle_entity_hitboxes": "Toggle Entity Hitboxes", + "controlify.action.controlify.debug_reload_resource_packs": "Reload Resource Packs", + "controlify.action.controlify.debug_clear_chat": "Clear Chat", + "controlify.action.controlify.debug_start_stop_profiling": "Start/Stop Profiling", "controlify.controller_submission.title": "Unknown Controller Submission", "controlify.controller_submission.message": "Please submit some of your controller info to Controlify's database to get it added in a future update.\n\nControlify sends the following information:\n- Your controller's vendor & product IDs\n- Your controller's GUID\n- The name of your controller (in the box below)\n- The version of Controlify you are currently on\n\nThis is completely anonymous and doesn't store any of your personal or account information.", diff --git a/src/main/resources/assets/controlify/lang/es_es.json b/src/main/resources/assets/controlify/lang/es_es.json index db614b026..5e420c50f 100644 --- a/src/main/resources/assets/controlify/lang/es_es.json +++ b/src/main/resources/assets/controlify/lang/es_es.json @@ -222,95 +222,95 @@ "controlify.bluetooth_warning.title": "Mando conectado vía Bluetooth", "controlify.bluetooth_warning.desc": "Algunos mandos pueden perder características cuando se conectan vía Bluetooth. Por ejemplo, háptica HD en DualSense. Si crees que te falta una característica, intenta usar USB antes de reportar.", "controlify.bluetooth_warning.dont_show": "No mostrar de nuevo", - "controlify.binding.controlify.walk_forward": "Caminar hacia delante", - "controlify.binding.controlify.walk_backward": "Caminar hacia atrás", - "controlify.binding.controlify.strafe_left": "Desplazarse hacia la izquierda", - "controlify.binding.controlify.strafe_right": "Desplazarse hacia la derecha", - "controlify.binding.controlify.look_up": "Mirar hacia arriba", - "controlify.binding.controlify.look_down": "Mirar hacia abajo", - "controlify.binding.controlify.look_left": "Mirar hacia la izquierda", - "controlify.binding.controlify.look_right": "Mirar hacia la derecha", - "controlify.binding.controlify.gyro_button": "Botón de giroscopio", - "controlify.binding.controlify.jump": "Saltar", - "controlify.binding.controlify.sneak": "Agacharse", - "controlify.binding.controlify.attack": "Atacar", - "controlify.binding.controlify.use": "Usar", - "controlify.binding.controlify.sprint": "Correr", - "controlify.binding.controlify.next_slot": "Ranura siguiente", - "controlify.binding.controlify.prev_slot": "Ranura anterior", - "controlify.binding.controlify.hotbar_load_radial": "Cargar barra creativa", - "controlify.binding.controlify.hotbar_save_radial": "Guardar barra creativa", - "controlify.binding.controlify.hotbar_item_select_radial": "Radial de selección de ranura", - "controlify.binding.controlify.pause": "Pausar juego", - "controlify.binding.controlify.inventory": "Abrir/Cerrar inventario", - "controlify.binding.controlify.change_perspective": "Cambiar cámara", - "controlify.binding.controlify.swap_hands": "Cambiar objeto de mano", - "controlify.binding.controlify.open_chat": "Abrir chat", - "controlify.binding.controlify.gui_press": "Pulsar (interfaz)", - "controlify.binding.controlify.gui_back": "Atrás (interfaz)", - "controlify.binding.controlify.gui_next_tab": "Pestaña siguiente (interfaz)", - "controlify.binding.controlify.gui_prev_tab": "Pestaña anterior (interfaz)", - "controlify.binding.controlify.gui_abstract_action_1": "Acción abstracta de interfaz 1", - "controlify.binding.controlify.gui_abstract_action_1.desc": "Una vinculación genérica que puede hacer cosas diferentes dependiendo de la pantalla actual.", - "controlify.binding.controlify.gui_abstract_action_2": "Acción abstracta de interfaz 2", - "controlify.binding.controlify.gui_abstract_action_2.desc": "Una vinculación genérica que puede hacer cosas diferentes dependiendo de la pantalla actual.", - "controlify.binding.controlify.gui_abstract_action_3": "Acción abstracta de interfaz 3", - "controlify.binding.controlify.gui_abstract_action_3.desc": "Una vinculación genérica que puede hacer cosas diferentes dependiendo de la pantalla actual.", - "controlify.binding.controlify.inv_select": "Seleccionar objeto", - "controlify.binding.controlify.inv_quick_move": "Movimiento rápido", - "controlify.binding.controlify.inv_take_half": "Tomar mitad", - "controlify.binding.controlify.drop": "Soltar objeto", - "controlify.binding.controlify.drop_stack": "Soltar pila", - "controlify.binding.controlify.drop_inventory": "Soltar objeto (en contenedores)", - "controlify.binding.controlify.pick_block": "Recoger bloque", - "controlify.binding.controlify.pick_block_nbt": "Recoger bloque (con NBT)", - "controlify.binding.controlify.take_screenshot": "Hacer captura de pantalla", - "controlify.binding.controlify.debug_radial": "Menú radial de depuración", - "controlify.binding.controlify.toggle_debug_menu": "Alternar menú de depuración (F3)", - "controlify.binding.controlify.toggle_debug_menu_fps": "Alternar menú de depuración (F3) con FPS", - "controlify.binding.controlify.toggle_debug_menu_prof": "Alternar menú de depuración (F3) con Perfilador", - "controlify.binding.controlify.toggle_debug_menu_net": "Alternar menú de depuración (F3) con Gráfico de Red", - "controlify.binding.controlify.toggle_debug_menu_chart": "Alternar menú de depuración (F3) con Gráficos", - "controlify.binding.controlify.toggle_hud_visibility": "Alternar visibilidad de la interfaz", - "controlify.binding.controlify.show_player_list": "Lista de jugadores", - "controlify.binding.controlify.radial_menu": "Menú radial", - "controlify.binding.controlify.radial_axis_up": "Menú radial arriba", - "controlify.binding.controlify.radial_axis_down": "Menú radial abajo", - "controlify.binding.controlify.radial_axis_left": "Menú radial izquierda", - "controlify.binding.controlify.radial_axis_right": "Menú radial derecha", - "controlify.binding.controlify.game_mode_switcher": "Cambiador de modo de juego", - "controlify.binding.controlify.vmouse_move_up": "Mover ratón virtual hacia arriba", - "controlify.binding.controlify.vmouse_move_down": "Mover ratón virtual hacia abajo", - "controlify.binding.controlify.vmouse_move_left": "Mover ratón virtual hacia la izquierda", - "controlify.binding.controlify.vmouse_move_right": "Mover ratón virtual hacia la derecha", - "controlify.binding.controlify.vmouse_lclick": "Clic izquierdo del ratón virtual", - "controlify.binding.controlify.vmouse_rclick": "Clic derecho del ratón virtual", - "controlify.binding.controlify.vmouse_shift_click": "Clic central del ratón virtual", - "controlify.binding.controlify.vmouse_snap_up": "Ajustar ratón arriba", - "controlify.binding.controlify.vmouse_snap_down": "Ajustar ratón abajo", - "controlify.binding.controlify.vmouse_snap_left": "Ajustar ratón izquierda", - "controlify.binding.controlify.vmouse_snap_right": "Ajustar ratón derecha", - "controlify.binding.controlify.vmouse_scroll_up": "Desplazar rueda del mouse virtual hacia arriba", - "controlify.binding.controlify.vmouse_scroll_down": "Desplazar rueda del mouse virtual hacia abajo", - "controlify.binding.controlify.vmouse_escape": "Tecla Escape ratón virtual", - "controlify.binding.controlify.vmouse_shift": "Tecla Mayús ratón virtual", - "controlify.binding.controlify.vmouse_page_next": "Página siguiente ratón virtual", - "controlify.binding.controlify.vmouse_page_prev": "Página anterior ratón virtual", - "controlify.binding.controlify.vmouse_page_down": "Página abajo ratón virtual", - "controlify.binding.controlify.vmouse_page_up": "Página arriba ratón virtual", - "controlify.binding.controlify.vmouse_toggle": "Alternar ratón virtual", - "controlify.binding.controlify.gui_navi_down": "Navegación de interfaz abajo", - "controlify.binding.controlify.gui_navi_up": "Navegación de interfaz arriba", - "controlify.binding.controlify.gui_navi_left": "Navegación de interfaz izquierda", - "controlify.binding.controlify.gui_navi_right": "Navegación de interfaz derecha", - "controlify.binding.controlify.gui_secondary_navi_down": "Navegación secundaria de interfaz abajo", - "controlify.binding.controlify.gui_secondary_navi_up": "Navegación secundaria de interfaz arriba", - "controlify.binding.controlify.gui_secondary_navi_left": "Navegación secundaria de interfaz izquierda", - "controlify.binding.controlify.gui_secondary_navi_right": "Navegación secundaria de interfaz derecha", - "controlify.binding.controlify.clear_binding": "Borrar vinculación", - "controlify.binding.voicechat.ptt_hold": "Pulsar para hablar (mantener)", - "controlify.binding.voicechat.ptt_toggle": "Pulsar para hablar (alternar)", - "controlify.binding.voicechat.mute_microphone": "Silenciar micrófono", + "controlify.action.controlify.walk_forward": "Caminar hacia delante", + "controlify.action.controlify.walk_backward": "Caminar hacia atrás", + "controlify.action.controlify.strafe_left": "Desplazarse hacia la izquierda", + "controlify.action.controlify.strafe_right": "Desplazarse hacia la derecha", + "controlify.action.controlify.look_up": "Mirar hacia arriba", + "controlify.action.controlify.look_down": "Mirar hacia abajo", + "controlify.action.controlify.look_left": "Mirar hacia la izquierda", + "controlify.action.controlify.look_right": "Mirar hacia la derecha", + "controlify.action.controlify.gyro_button": "Botón de giroscopio", + "controlify.action.controlify.jump": "Saltar", + "controlify.action.controlify.sneak": "Agacharse", + "controlify.action.controlify.attack": "Atacar", + "controlify.action.controlify.use": "Usar", + "controlify.action.controlify.sprint": "Correr", + "controlify.action.controlify.next_slot": "Ranura siguiente", + "controlify.action.controlify.prev_slot": "Ranura anterior", + "controlify.action.controlify.hotbar_load_radial": "Cargar barra creativa", + "controlify.action.controlify.hotbar_save_radial": "Guardar barra creativa", + "controlify.action.controlify.hotbar_item_select_radial": "Radial de selección de ranura", + "controlify.action.controlify.pause": "Pausar juego", + "controlify.action.controlify.inventory": "Abrir/Cerrar inventario", + "controlify.action.controlify.change_perspective": "Cambiar cámara", + "controlify.action.controlify.swap_hands": "Cambiar objeto de mano", + "controlify.action.controlify.open_chat": "Abrir chat", + "controlify.action.controlify.gui_press": "Pulsar (interfaz)", + "controlify.action.controlify.gui_back": "Atrás (interfaz)", + "controlify.action.controlify.gui_next_tab": "Pestaña siguiente (interfaz)", + "controlify.action.controlify.gui_prev_tab": "Pestaña anterior (interfaz)", + "controlify.action.controlify.gui_abstract_action_1": "Acción abstracta de interfaz 1", + "controlify.action.controlify.gui_abstract_action_1.desc": "Una vinculación genérica que puede hacer cosas diferentes dependiendo de la pantalla actual.", + "controlify.action.controlify.gui_abstract_action_2": "Acción abstracta de interfaz 2", + "controlify.action.controlify.gui_abstract_action_2.desc": "Una vinculación genérica que puede hacer cosas diferentes dependiendo de la pantalla actual.", + "controlify.action.controlify.gui_abstract_action_3": "Acción abstracta de interfaz 3", + "controlify.action.controlify.gui_abstract_action_3.desc": "Una vinculación genérica que puede hacer cosas diferentes dependiendo de la pantalla actual.", + "controlify.action.controlify.inv_select": "Seleccionar objeto", + "controlify.action.controlify.inv_quick_move": "Movimiento rápido", + "controlify.action.controlify.inv_take_half": "Tomar mitad", + "controlify.action.controlify.drop": "Soltar objeto", + "controlify.action.controlify.drop_stack": "Soltar pila", + "controlify.action.controlify.drop_inventory": "Soltar objeto (en contenedores)", + "controlify.action.controlify.pick_block": "Recoger bloque", + "controlify.action.controlify.pick_block_nbt": "Recoger bloque (con NBT)", + "controlify.action.controlify.take_screenshot": "Hacer captura de pantalla", + "controlify.action.controlify.debug_radial": "Menú radial de depuración", + "controlify.action.controlify.toggle_debug_menu": "Alternar menú de depuración (F3)", + "controlify.action.controlify.toggle_debug_menu_fps": "Alternar menú de depuración (F3) con FPS", + "controlify.action.controlify.toggle_debug_menu_prof": "Alternar menú de depuración (F3) con Perfilador", + "controlify.action.controlify.toggle_debug_menu_net": "Alternar menú de depuración (F3) con Gráfico de Red", + "controlify.action.controlify.toggle_debug_menu_chart": "Alternar menú de depuración (F3) con Gráficos", + "controlify.action.controlify.toggle_hud_visibility": "Alternar visibilidad de la interfaz", + "controlify.action.controlify.show_player_list": "Lista de jugadores", + "controlify.action.controlify.radial_menu": "Menú radial", + "controlify.action.controlify.radial_axis_up": "Menú radial arriba", + "controlify.action.controlify.radial_axis_down": "Menú radial abajo", + "controlify.action.controlify.radial_axis_left": "Menú radial izquierda", + "controlify.action.controlify.radial_axis_right": "Menú radial derecha", + "controlify.action.controlify.game_mode_switcher": "Cambiador de modo de juego", + "controlify.action.controlify.vmouse_move_up": "Mover ratón virtual hacia arriba", + "controlify.action.controlify.vmouse_move_down": "Mover ratón virtual hacia abajo", + "controlify.action.controlify.vmouse_move_left": "Mover ratón virtual hacia la izquierda", + "controlify.action.controlify.vmouse_move_right": "Mover ratón virtual hacia la derecha", + "controlify.action.controlify.vmouse_lclick": "Clic izquierdo del ratón virtual", + "controlify.action.controlify.vmouse_rclick": "Clic derecho del ratón virtual", + "controlify.action.controlify.vmouse_shift_click": "Clic central del ratón virtual", + "controlify.action.controlify.vmouse_snap_up": "Ajustar ratón arriba", + "controlify.action.controlify.vmouse_snap_down": "Ajustar ratón abajo", + "controlify.action.controlify.vmouse_snap_left": "Ajustar ratón izquierda", + "controlify.action.controlify.vmouse_snap_right": "Ajustar ratón derecha", + "controlify.action.controlify.vmouse_scroll_up": "Desplazar rueda del mouse virtual hacia arriba", + "controlify.action.controlify.vmouse_scroll_down": "Desplazar rueda del mouse virtual hacia abajo", + "controlify.action.controlify.vmouse_escape": "Tecla Escape ratón virtual", + "controlify.action.controlify.vmouse_shift": "Tecla Mayús ratón virtual", + "controlify.action.controlify.vmouse_page_next": "Página siguiente ratón virtual", + "controlify.action.controlify.vmouse_page_prev": "Página anterior ratón virtual", + "controlify.action.controlify.vmouse_page_down": "Página abajo ratón virtual", + "controlify.action.controlify.vmouse_page_up": "Página arriba ratón virtual", + "controlify.action.controlify.vmouse_toggle": "Alternar ratón virtual", + "controlify.action.controlify.gui_navi_down": "Navegación de interfaz abajo", + "controlify.action.controlify.gui_navi_up": "Navegación de interfaz arriba", + "controlify.action.controlify.gui_navi_left": "Navegación de interfaz izquierda", + "controlify.action.controlify.gui_navi_right": "Navegación de interfaz derecha", + "controlify.action.controlify.gui_secondary_navi_down": "Navegación secundaria de interfaz abajo", + "controlify.action.controlify.gui_secondary_navi_up": "Navegación secundaria de interfaz arriba", + "controlify.action.controlify.gui_secondary_navi_left": "Navegación secundaria de interfaz izquierda", + "controlify.action.controlify.gui_secondary_navi_right": "Navegación secundaria de interfaz derecha", + "controlify.action.controlify.clear_binding": "Borrar vinculación", + "controlify.action.voicechat.ptt_hold": "Pulsar para hablar (mantener)", + "controlify.action.voicechat.ptt_toggle": "Pulsar para hablar (alternar)", + "controlify.action.voicechat.mute_microphone": "Silenciar micrófono", "controlify.input.controlify.button/south": "Botón sur", "controlify.input.controlify.button/east": "Botón este", "controlify.input.controlify.button/west": "Botón oeste", @@ -441,13 +441,13 @@ "controlify.radial.debug.overlay_prof": "Mostrar superposición con Perfilador", "controlify.radial.debug.overlay_chart": "Mostrar superposición con Gráficos", "controlify.radial.debug.hide_overlay": "Ocultar superposición", - "controlify.radial.debug.reload_chunks": "Recargar chunks", - "controlify.radial.debug.chunk_borders": "Alternar bordes de chunk", - "controlify.radial.debug.advanced_tooltips": "Alternar descripciones avanzadas", - "controlify.radial.debug.entity_hitboxes": "Alternar cajas de colisión de entidades", - "controlify.radial.debug.reload_packs": "Recargar paquetes de recursos", - "controlify.radial.debug.clear_chat": "Borrar chat", - "controlify.radial.debug.profile": "Iniciar/Detener perfilado", + "controlify.action.controlify.debug_reload_chunks": "Recargar chunks", + "controlify.action.controlify.debug_toggle_chunk_borders": "Alternar bordes de chunk", + "controlify.action.controlify.debug_toggle_advanced_tooltips": "Alternar descripciones avanzadas", + "controlify.action.controlify.debug_toggle_entity_hitboxes": "Alternar cajas de colisión de entidades", + "controlify.action.controlify.debug_reload_resource_packs": "Recargar paquetes de recursos", + "controlify.action.controlify.debug_clear_chat": "Borrar chat", + "controlify.action.controlify.debug_start_stop_profiling": "Iniciar/Detener perfilado", "controlify.controller_submission.title": "Envío de mando desconocido", "controlify.controller_submission.message": "Por favor envía algo de la información de tu mando a la base de datos de Controlify para que se añada en una actualización futura.\n\nControlify envía la siguiente información:\n- Los IDs de vendedor y producto de tu mando\n- El GUID de tu mando\n- El nombre de tu mando (en el cuadro de abajo)\n- La versión de Controlify en la que estás actualmente\n\nEsto es completamente anónimo y no almacena ninguna de tu información personal o de cuenta.", "controlify.controller_submission.operational_checkbox": "¿Funciona tu mando con Controlify?", diff --git a/src/main/resources/assets/controlify/lang/es_mx.json b/src/main/resources/assets/controlify/lang/es_mx.json index 6335d5e1b..adb8fc78e 100644 --- a/src/main/resources/assets/controlify/lang/es_mx.json +++ b/src/main/resources/assets/controlify/lang/es_mx.json @@ -222,95 +222,95 @@ "controlify.bluetooth_warning.title": "Control conectado a través de Bluetooth", "controlify.bluetooth_warning.desc": "Algunos controles pueden carecer de funciones cuando se conectan a través de Bluetooth. Por ejemplo, la retroalimentación háptica HD en DualSense. Si crees que te falta una función, intenta usar USB antes de informar.", "controlify.bluetooth_warning.dont_show": "No mostrar de nuevo", - "controlify.binding.controlify.walk_forward": "Avanzar", - "controlify.binding.controlify.walk_backward": "Retroceder", - "controlify.binding.controlify.strafe_left": "Desplazarse a la Izquierda", - "controlify.binding.controlify.strafe_right": "Desplazarse a la Derecha", - "controlify.binding.controlify.look_up": "Mirar Arriba", - "controlify.binding.controlify.look_down": "Mirar Abajo", - "controlify.binding.controlify.look_left": "Mirar a la Izquierda", - "controlify.binding.controlify.look_right": "Mirar a la Derecha", - "controlify.binding.controlify.gyro_button": "Botón de Giroscopio", - "controlify.binding.controlify.jump": "Saltar", - "controlify.binding.controlify.sneak": "Agacharse", - "controlify.binding.controlify.attack": "Atacar", - "controlify.binding.controlify.use": "Usar", - "controlify.binding.controlify.sprint": "Correr", - "controlify.binding.controlify.next_slot": "Siguiente Ranura de la Barra de Acción", - "controlify.binding.controlify.prev_slot": "Ranura Anterior de la Barra de Acción", - "controlify.binding.controlify.hotbar_load_radial": "Cargar tu barra de objetos de modo creativo", - "controlify.binding.controlify.hotbar_save_radial": "Guardar tu barra de objetos de modo creativo", - "controlify.binding.controlify.hotbar_item_select_radial": "Seleccionar de la barra de objetos (Radial)", - "controlify.binding.controlify.pause": "Pausar Juego", - "controlify.binding.controlify.inventory": "Abrir Inventario", - "controlify.binding.controlify.change_perspective": "Cambiar Perspectiva", - "controlify.binding.controlify.swap_hands": "Cambiar Manos", - "controlify.binding.controlify.open_chat": "Abrir Chat", - "controlify.binding.controlify.gui_press": "Presionar GUI", - "controlify.binding.controlify.gui_back": "Volver en GUI", - "controlify.binding.controlify.gui_next_tab": "Siguiente Pestaña en GUI", - "controlify.binding.controlify.gui_prev_tab": "Pestaña Anterior en GUI", - "controlify.binding.controlify.gui_abstract_action_1": "Acción Abstracta 1 en GUI", - "controlify.binding.controlify.gui_abstract_action_1.desc": "Una asignación genérica que puede hacer diferentes cosas dependiendo de la pantalla actual.", - "controlify.binding.controlify.gui_abstract_action_2": "Acción Abstracta 2 en GUI", - "controlify.binding.controlify.gui_abstract_action_2.desc": "Una asignación genérica que puede hacer diferentes cosas dependiendo de la pantalla actual.", - "controlify.binding.controlify.gui_abstract_action_3": "Acción abstracta de interfaz 3", - "controlify.binding.controlify.gui_abstract_action_3.desc": "Una asignación genérica que puede hacer diferentes cosas dependiendo de la pantalla actual.", - "controlify.binding.controlify.inv_select": "Seleccionar Objeto", - "controlify.binding.controlify.inv_quick_move": "Movimiento Rápido", - "controlify.binding.controlify.inv_take_half": "Tomar la Mitad", - "controlify.binding.controlify.drop": "Soltar Objeto", - "controlify.binding.controlify.drop_stack": "Soltar Pila", - "controlify.binding.controlify.drop_inventory": "Soltar Objeto (En Contenedores)", - "controlify.binding.controlify.pick_block": "Seleccionar Bloque", - "controlify.binding.controlify.pick_block_nbt": "Seleccionar Bloque (con NBT)", - "controlify.binding.controlify.take_screenshot": "Tomar Captura de Pantalla", - "controlify.binding.controlify.debug_radial": "Menú radial de depuración", - "controlify.binding.controlify.toggle_debug_menu": "Alternar Menú de Depuración (F3)", - "controlify.binding.controlify.toggle_debug_menu_fps": "Alternar menú de depuración (F3) con FPS", - "controlify.binding.controlify.toggle_debug_menu_prof": "Alternar menú de depuración (F3) con Perfilador", - "controlify.binding.controlify.toggle_debug_menu_net": "Alternar menú de depuración (F3) con Gráfico de Red", - "controlify.binding.controlify.toggle_debug_menu_chart": "Alternar menú de depuración (F3) con Gráficos", - "controlify.binding.controlify.toggle_hud_visibility": "Alternar Visibilidad de la HUD", - "controlify.binding.controlify.show_player_list": "Mostrar Lista de Jugadores", - "controlify.binding.controlify.radial_menu": "Menú Radial", - "controlify.binding.controlify.radial_axis_up": "Menú Radial Arriba", - "controlify.binding.controlify.radial_axis_down": "Menú Radial Abajo", - "controlify.binding.controlify.radial_axis_left": "Menú Radial Izquierda", - "controlify.binding.controlify.radial_axis_right": "Menú Radial Derecha", - "controlify.binding.controlify.game_mode_switcher": "Selector de Modo de Juego", - "controlify.binding.controlify.vmouse_move_up": "Mover Mouse Virtual Arriba", - "controlify.binding.controlify.vmouse_move_down": "Mover Mouse Virtual Abajo", - "controlify.binding.controlify.vmouse_move_left": "Mover Mouse Virtual a la Izquierda", - "controlify.binding.controlify.vmouse_move_right": "Mover Mouse Virtual a la Derecha", - "controlify.binding.controlify.vmouse_lclick": "Clic Izquierdo del Mouse Virtual", - "controlify.binding.controlify.vmouse_rclick": "Clic Derecho del Mouse Virtual", - "controlify.binding.controlify.vmouse_shift_click": "Clic con Shift del Mouse Virtual", - "controlify.binding.controlify.vmouse_snap_up": "Ajustar Mouse Virtual Arriba", - "controlify.binding.controlify.vmouse_snap_down": "Ajustar Mouse Virtual Abajo", - "controlify.binding.controlify.vmouse_snap_left": "Ajustar Mouse Virtual a la Izquierda", - "controlify.binding.controlify.vmouse_snap_right": "Ajustar Mouse Virtual a la Derecha", - "controlify.binding.controlify.vmouse_scroll_up": "Desplazar Mouse Virtual Arriba", - "controlify.binding.controlify.vmouse_scroll_down": "Desplazar Mouse Virtual Abajo", - "controlify.binding.controlify.vmouse_escape": "Tecla Escape del Mouse Virtual", - "controlify.binding.controlify.vmouse_shift": "Tecla Shift del Mouse Virtual", - "controlify.binding.controlify.vmouse_page_next": "Página siguiente ratón virtual", - "controlify.binding.controlify.vmouse_page_prev": "Página anterior ratón virtual", - "controlify.binding.controlify.vmouse_page_down": "Página abajo ratón virtual", - "controlify.binding.controlify.vmouse_page_up": "Página arriba ratón virtual", - "controlify.binding.controlify.vmouse_toggle": "Alternar Mouse Virtual", - "controlify.binding.controlify.gui_navi_down": "Navegar hacia Abajo en GUI", - "controlify.binding.controlify.gui_navi_up": "Navegar hacia Arriba en GUI", - "controlify.binding.controlify.gui_navi_left": "Navegar hacia la Izquierda en GUI", - "controlify.binding.controlify.gui_navi_right": "Navegar hacia la Derecha en GUI", - "controlify.binding.controlify.gui_secondary_navi_down": "Navegación secundaria de interfaz abajo", - "controlify.binding.controlify.gui_secondary_navi_up": "Navegación secundaria de interfaz arriba", - "controlify.binding.controlify.gui_secondary_navi_left": "Navegación secundaria de interfaz izquierda", - "controlify.binding.controlify.gui_secondary_navi_right": "Navegación secundaria de interfaz derecha", - "controlify.binding.controlify.clear_binding": "Borrar Asignación", - "controlify.binding.voicechat.ptt_hold": "Pulsar para hablar (mantener)", - "controlify.binding.voicechat.ptt_toggle": "Pulsar para hablar (alternar)", - "controlify.binding.voicechat.mute_microphone": "Silenciar micrófono", + "controlify.action.controlify.walk_forward": "Avanzar", + "controlify.action.controlify.walk_backward": "Retroceder", + "controlify.action.controlify.strafe_left": "Desplazarse a la Izquierda", + "controlify.action.controlify.strafe_right": "Desplazarse a la Derecha", + "controlify.action.controlify.look_up": "Mirar Arriba", + "controlify.action.controlify.look_down": "Mirar Abajo", + "controlify.action.controlify.look_left": "Mirar a la Izquierda", + "controlify.action.controlify.look_right": "Mirar a la Derecha", + "controlify.action.controlify.gyro_button": "Botón de Giroscopio", + "controlify.action.controlify.jump": "Saltar", + "controlify.action.controlify.sneak": "Agacharse", + "controlify.action.controlify.attack": "Atacar", + "controlify.action.controlify.use": "Usar", + "controlify.action.controlify.sprint": "Correr", + "controlify.action.controlify.next_slot": "Siguiente Ranura de la Barra de Acción", + "controlify.action.controlify.prev_slot": "Ranura Anterior de la Barra de Acción", + "controlify.action.controlify.hotbar_load_radial": "Cargar tu barra de objetos de modo creativo", + "controlify.action.controlify.hotbar_save_radial": "Guardar tu barra de objetos de modo creativo", + "controlify.action.controlify.hotbar_item_select_radial": "Seleccionar de la barra de objetos (Radial)", + "controlify.action.controlify.pause": "Pausar Juego", + "controlify.action.controlify.inventory": "Abrir Inventario", + "controlify.action.controlify.change_perspective": "Cambiar Perspectiva", + "controlify.action.controlify.swap_hands": "Cambiar Manos", + "controlify.action.controlify.open_chat": "Abrir Chat", + "controlify.action.controlify.gui_press": "Presionar GUI", + "controlify.action.controlify.gui_back": "Volver en GUI", + "controlify.action.controlify.gui_next_tab": "Siguiente Pestaña en GUI", + "controlify.action.controlify.gui_prev_tab": "Pestaña Anterior en GUI", + "controlify.action.controlify.gui_abstract_action_1": "Acción Abstracta 1 en GUI", + "controlify.action.controlify.gui_abstract_action_1.desc": "Una asignación genérica que puede hacer diferentes cosas dependiendo de la pantalla actual.", + "controlify.action.controlify.gui_abstract_action_2": "Acción Abstracta 2 en GUI", + "controlify.action.controlify.gui_abstract_action_2.desc": "Una asignación genérica que puede hacer diferentes cosas dependiendo de la pantalla actual.", + "controlify.action.controlify.gui_abstract_action_3": "Acción abstracta de interfaz 3", + "controlify.action.controlify.gui_abstract_action_3.desc": "Una asignación genérica que puede hacer diferentes cosas dependiendo de la pantalla actual.", + "controlify.action.controlify.inv_select": "Seleccionar Objeto", + "controlify.action.controlify.inv_quick_move": "Movimiento Rápido", + "controlify.action.controlify.inv_take_half": "Tomar la Mitad", + "controlify.action.controlify.drop": "Soltar Objeto", + "controlify.action.controlify.drop_stack": "Soltar Pila", + "controlify.action.controlify.drop_inventory": "Soltar Objeto (En Contenedores)", + "controlify.action.controlify.pick_block": "Seleccionar Bloque", + "controlify.action.controlify.pick_block_nbt": "Seleccionar Bloque (con NBT)", + "controlify.action.controlify.take_screenshot": "Tomar Captura de Pantalla", + "controlify.action.controlify.debug_radial": "Menú radial de depuración", + "controlify.action.controlify.toggle_debug_menu": "Alternar Menú de Depuración (F3)", + "controlify.action.controlify.toggle_debug_menu_fps": "Alternar menú de depuración (F3) con FPS", + "controlify.action.controlify.toggle_debug_menu_prof": "Alternar menú de depuración (F3) con Perfilador", + "controlify.action.controlify.toggle_debug_menu_net": "Alternar menú de depuración (F3) con Gráfico de Red", + "controlify.action.controlify.toggle_debug_menu_chart": "Alternar menú de depuración (F3) con Gráficos", + "controlify.action.controlify.toggle_hud_visibility": "Alternar Visibilidad de la HUD", + "controlify.action.controlify.show_player_list": "Mostrar Lista de Jugadores", + "controlify.action.controlify.radial_menu": "Menú Radial", + "controlify.action.controlify.radial_axis_up": "Menú Radial Arriba", + "controlify.action.controlify.radial_axis_down": "Menú Radial Abajo", + "controlify.action.controlify.radial_axis_left": "Menú Radial Izquierda", + "controlify.action.controlify.radial_axis_right": "Menú Radial Derecha", + "controlify.action.controlify.game_mode_switcher": "Selector de Modo de Juego", + "controlify.action.controlify.vmouse_move_up": "Mover Mouse Virtual Arriba", + "controlify.action.controlify.vmouse_move_down": "Mover Mouse Virtual Abajo", + "controlify.action.controlify.vmouse_move_left": "Mover Mouse Virtual a la Izquierda", + "controlify.action.controlify.vmouse_move_right": "Mover Mouse Virtual a la Derecha", + "controlify.action.controlify.vmouse_lclick": "Clic Izquierdo del Mouse Virtual", + "controlify.action.controlify.vmouse_rclick": "Clic Derecho del Mouse Virtual", + "controlify.action.controlify.vmouse_shift_click": "Clic con Shift del Mouse Virtual", + "controlify.action.controlify.vmouse_snap_up": "Ajustar Mouse Virtual Arriba", + "controlify.action.controlify.vmouse_snap_down": "Ajustar Mouse Virtual Abajo", + "controlify.action.controlify.vmouse_snap_left": "Ajustar Mouse Virtual a la Izquierda", + "controlify.action.controlify.vmouse_snap_right": "Ajustar Mouse Virtual a la Derecha", + "controlify.action.controlify.vmouse_scroll_up": "Desplazar Mouse Virtual Arriba", + "controlify.action.controlify.vmouse_scroll_down": "Desplazar Mouse Virtual Abajo", + "controlify.action.controlify.vmouse_escape": "Tecla Escape del Mouse Virtual", + "controlify.action.controlify.vmouse_shift": "Tecla Shift del Mouse Virtual", + "controlify.action.controlify.vmouse_page_next": "Página siguiente ratón virtual", + "controlify.action.controlify.vmouse_page_prev": "Página anterior ratón virtual", + "controlify.action.controlify.vmouse_page_down": "Página abajo ratón virtual", + "controlify.action.controlify.vmouse_page_up": "Página arriba ratón virtual", + "controlify.action.controlify.vmouse_toggle": "Alternar Mouse Virtual", + "controlify.action.controlify.gui_navi_down": "Navegar hacia Abajo en GUI", + "controlify.action.controlify.gui_navi_up": "Navegar hacia Arriba en GUI", + "controlify.action.controlify.gui_navi_left": "Navegar hacia la Izquierda en GUI", + "controlify.action.controlify.gui_navi_right": "Navegar hacia la Derecha en GUI", + "controlify.action.controlify.gui_secondary_navi_down": "Navegación secundaria de interfaz abajo", + "controlify.action.controlify.gui_secondary_navi_up": "Navegación secundaria de interfaz arriba", + "controlify.action.controlify.gui_secondary_navi_left": "Navegación secundaria de interfaz izquierda", + "controlify.action.controlify.gui_secondary_navi_right": "Navegación secundaria de interfaz derecha", + "controlify.action.controlify.clear_binding": "Borrar Asignación", + "controlify.action.voicechat.ptt_hold": "Pulsar para hablar (mantener)", + "controlify.action.voicechat.ptt_toggle": "Pulsar para hablar (alternar)", + "controlify.action.voicechat.mute_microphone": "Silenciar micrófono", "controlify.input.controlify.button/south": "Botón sur", "controlify.input.controlify.button/east": "Botón este", "controlify.input.controlify.button/west": "Botón oeste", @@ -441,13 +441,13 @@ "controlify.radial.debug.overlay_prof": "Mostrar superposición con Perfilador", "controlify.radial.debug.overlay_chart": "Mostrar superposición con Gráficos", "controlify.radial.debug.hide_overlay": "Ocultar superposición", - "controlify.radial.debug.reload_chunks": "Recargar chunks", - "controlify.radial.debug.chunk_borders": "Alternar bordes de chunk", - "controlify.radial.debug.advanced_tooltips": "Alternar descripciones avanzadas", - "controlify.radial.debug.entity_hitboxes": "Alternar cajas de colisión de entidades", - "controlify.radial.debug.reload_packs": "Recargar paquetes de recursos", - "controlify.radial.debug.clear_chat": "Borrar chat", - "controlify.radial.debug.profile": "Iniciar/Detener perfilado", + "controlify.action.controlify.debug_reload_chunks": "Recargar chunks", + "controlify.action.controlify.debug_toggle_chunk_borders": "Alternar bordes de chunk", + "controlify.action.controlify.debug_toggle_advanced_tooltips": "Alternar descripciones avanzadas", + "controlify.action.controlify.debug_toggle_entity_hitboxes": "Alternar cajas de colisión de entidades", + "controlify.action.controlify.debug_reload_resource_packs": "Recargar paquetes de recursos", + "controlify.action.controlify.debug_clear_chat": "Borrar chat", + "controlify.action.controlify.debug_start_stop_profiling": "Iniciar/Detener perfilado", "controlify.controller_submission.title": "Envío de control desconocido", "controlify.controller_submission.message": "Por favor, envía información sobre tu control a la base de datos de Controlify para que se agregue en una futura actualización.\n\nControlify envía la siguiente información:\n- Los IDs de proveedor y producto de tu control\n- El GUID de tu control\n- El nombre de tu control (en el cuadro de abajo)\n- La versión de Controlify que estás utilizando actualmente\n\nEsto es completamente anónimo y no almacena ninguna información personal o de tu cuenta.", "controlify.controller_submission.operational_checkbox": "¿Tu control funciona con Controlify?", diff --git a/src/main/resources/assets/controlify/lang/fi_fi.json b/src/main/resources/assets/controlify/lang/fi_fi.json index b8cb6cfbe..3ad11e2ff 100644 --- a/src/main/resources/assets/controlify/lang/fi_fi.json +++ b/src/main/resources/assets/controlify/lang/fi_fi.json @@ -70,8 +70,8 @@ "controlify.gui.controller_unavailable": "Ohjain ei ole käytettävissä eikä sitä voi muokata.", "controlify.gui.no_sdl.title": "Controlifyn Natiivikirjasto Yhteensopimaton", "controlify.vibration_strength.controlify.player": "Pelaaja", - "controlify.binding.controlify.use": "Käytä", - "controlify.binding.controlify.radial_menu": "Ympyrävalikko", + "controlify.action.controlify.use": "Käytä", + "controlify.action.controlify.radial_menu": "Ympyrävalikko", "controlify.guide.ingame.use": "Käytä", "controlify.guide.ingame.toggle": "Päälle/pois", "controlify.input_curves.linear": "Lineaarinen", diff --git a/src/main/resources/assets/controlify/lang/fr_fr.json b/src/main/resources/assets/controlify/lang/fr_fr.json index 1d4c07b56..46b037614 100644 --- a/src/main/resources/assets/controlify/lang/fr_fr.json +++ b/src/main/resources/assets/controlify/lang/fr_fr.json @@ -194,78 +194,78 @@ "controlify.bluetooth_warning.title": "Manette connectée via Bluetooth", "controlify.bluetooth_warning.desc": "Certaines manettes peuvent manquer des fonctionnalités lorsque vous êtes connecté via Bluetooth. Par exemple, HD haptics sur DualSense. Si vous pensez qu'il vous manque une fonctionnalité, essayez d'utiliser un câble USB avant de signaler.", "controlify.bluetooth_warning.dont_show": "Ne plus afficher", - "controlify.binding.controlify.walk_forward": "Avancer", - "controlify.binding.controlify.walk_backward": "Reculer", - "controlify.binding.controlify.strafe_left": "Se déplacer à gauche", - "controlify.binding.controlify.strafe_right": "Se déplacer à droite", - "controlify.binding.controlify.look_up": "Regarder vers le haut", - "controlify.binding.controlify.look_down": "Regarder vers le bas", - "controlify.binding.controlify.look_left": "Regarder vers la gauche", - "controlify.binding.controlify.look_right": "Regarder vers la droite", - "controlify.binding.controlify.gyro_button": "Bouton Gyroscope", - "controlify.binding.controlify.jump": "Sauter", - "controlify.binding.controlify.sneak": "Accroupir", - "controlify.binding.controlify.attack": "Attaquer", - "controlify.binding.controlify.use": "Utiliser", - "controlify.binding.controlify.sprint": "Courir", - "controlify.binding.controlify.next_slot": "Slot suivant dans la barre d'inventaire", - "controlify.binding.controlify.prev_slot": "Slot précédent dans la barre d'inventaire", - "controlify.binding.controlify.hotbar_load_radial": "Charger la barre d'action en Créatif", - "controlify.binding.controlify.hotbar_save_radial": "Enregistrer la barre d'action en Créatif", - "controlify.binding.controlify.hotbar_item_select_radial": "Selection d'emplacements de la barre d'action Radial", - "controlify.binding.controlify.pause": "Pause", - "controlify.binding.controlify.inventory": "Ouvrir l'inventaire", - "controlify.binding.controlify.change_perspective": "Changer de perspective", - "controlify.binding.controlify.swap_hands": "Changer l’item de main", - "controlify.binding.controlify.open_chat": "Ouvrir le chat", - "controlify.binding.controlify.gui_press": "Presser", - "controlify.binding.controlify.gui_back": "Retour", - "controlify.binding.controlify.gui_next_tab": "Onglet suivant", - "controlify.binding.controlify.gui_prev_tab": "Onglet précédent", - "controlify.binding.controlify.gui_abstract_action_1": "Action abstraite de l'interface 1", - "controlify.binding.controlify.gui_abstract_action_1.desc": "Une action générique qui peut effectuer différentes actions en fonction de l'écran actuel.", - "controlify.binding.controlify.gui_abstract_action_2": "Action abstraite de l'interface 2", - "controlify.binding.controlify.gui_abstract_action_2.desc": "Une action générique qui peut effectuer différentes actions en fonction de l'écran actuel.", - "controlify.binding.controlify.gui_abstract_action_3.desc": "Une action générique qui peut effectuer différentes actions en fonction de l'écran actuel.", - "controlify.binding.controlify.inv_select": "Sélectionner un item", - "controlify.binding.controlify.inv_quick_move": "Déplacer rapidement un item", - "controlify.binding.controlify.inv_take_half": "Prendre la moitié d'un item", - "controlify.binding.controlify.drop": "Lâcher un item", - "controlify.binding.controlify.drop_stack": "Lâcher une pile d'items", - "controlify.binding.controlify.drop_inventory": "Lâcher un item (dans les conteneurs)", - "controlify.binding.controlify.pick_block": "Sélectionner le bloc", - "controlify.binding.controlify.pick_block_nbt": "Sélectionner le bloc (avec NBT)", - "controlify.binding.controlify.take_screenshot": "Prendre une capture d'écran", - "controlify.binding.controlify.toggle_debug_menu": "Activer/désactiver le menu de débogage (F3)", - "controlify.binding.controlify.toggle_hud_visibility": "Activer/désactiver l'affichage de l'interface utilisateur", - "controlify.binding.controlify.show_player_list": "Afficher la liste des joueurs", - "controlify.binding.controlify.radial_menu": "Menu radial", - "controlify.binding.controlify.radial_axis_up": "Menu radial - Haut", - "controlify.binding.controlify.radial_axis_down": "Menu radial - Bas", - "controlify.binding.controlify.radial_axis_left": "Menu radial - Gauche", - "controlify.binding.controlify.radial_axis_right": "Menu radial - Droite", - "controlify.binding.controlify.game_mode_switcher": "Sélecteur de Mode de Jeu", - "controlify.binding.controlify.vmouse_move_up": "Déplacer la souris virtuelle vers le haut", - "controlify.binding.controlify.vmouse_move_down": "Déplacer la souris virtuelle vers le bas", - "controlify.binding.controlify.vmouse_move_left": "Déplacer la souris virtuelle vers la gauche", - "controlify.binding.controlify.vmouse_move_right": "Déplacer la souris virtuelle vers la droite", - "controlify.binding.controlify.vmouse_lclick": "Clic gauche de la souris virtuelle", - "controlify.binding.controlify.vmouse_rclick": "Clic droit de la souris virtuelle", - "controlify.binding.controlify.vmouse_shift_click": "Clic shift de la souris virtuelle", - "controlify.binding.controlify.vmouse_snap_up": "Faire sauter la souris virtuelle vers le haut", - "controlify.binding.controlify.vmouse_snap_down": "Faire sauter la souris virtuelle vers le bas", - "controlify.binding.controlify.vmouse_snap_left": "Faire sauter la souris virtuelle vers la gauche", - "controlify.binding.controlify.vmouse_snap_right": "Faire sauter la souris virtuelle vers la droite", - "controlify.binding.controlify.vmouse_scroll_up": "Faire défiler la souris virtuelle vers le haut", - "controlify.binding.controlify.vmouse_scroll_down": "Faire défiler la souris virtuelle vers le bas", - "controlify.binding.controlify.vmouse_escape": "Touche d'échappement de la souris virtuelle", - "controlify.binding.controlify.vmouse_shift": "Touche shift de la souris virtuelle", - "controlify.binding.controlify.vmouse_toggle": "Activer/désactiver la souris virtuelle", - "controlify.binding.controlify.gui_navi_down": "Navigation dans l'interface - Bas", - "controlify.binding.controlify.gui_navi_up": "Navigation dans l'interface - Haut", - "controlify.binding.controlify.gui_navi_left": "Navigation dans l'interface - Gauche", - "controlify.binding.controlify.gui_navi_right": "Navigation dans l'interface - Droite", - "controlify.binding.controlify.clear_binding": "Effacer la liaison", + "controlify.action.controlify.walk_forward": "Avancer", + "controlify.action.controlify.walk_backward": "Reculer", + "controlify.action.controlify.strafe_left": "Se déplacer à gauche", + "controlify.action.controlify.strafe_right": "Se déplacer à droite", + "controlify.action.controlify.look_up": "Regarder vers le haut", + "controlify.action.controlify.look_down": "Regarder vers le bas", + "controlify.action.controlify.look_left": "Regarder vers la gauche", + "controlify.action.controlify.look_right": "Regarder vers la droite", + "controlify.action.controlify.gyro_button": "Bouton Gyroscope", + "controlify.action.controlify.jump": "Sauter", + "controlify.action.controlify.sneak": "Accroupir", + "controlify.action.controlify.attack": "Attaquer", + "controlify.action.controlify.use": "Utiliser", + "controlify.action.controlify.sprint": "Courir", + "controlify.action.controlify.next_slot": "Slot suivant dans la barre d'inventaire", + "controlify.action.controlify.prev_slot": "Slot précédent dans la barre d'inventaire", + "controlify.action.controlify.hotbar_load_radial": "Charger la barre d'action en Créatif", + "controlify.action.controlify.hotbar_save_radial": "Enregistrer la barre d'action en Créatif", + "controlify.action.controlify.hotbar_item_select_radial": "Selection d'emplacements de la barre d'action Radial", + "controlify.action.controlify.pause": "Pause", + "controlify.action.controlify.inventory": "Ouvrir l'inventaire", + "controlify.action.controlify.change_perspective": "Changer de perspective", + "controlify.action.controlify.swap_hands": "Changer l’item de main", + "controlify.action.controlify.open_chat": "Ouvrir le chat", + "controlify.action.controlify.gui_press": "Presser", + "controlify.action.controlify.gui_back": "Retour", + "controlify.action.controlify.gui_next_tab": "Onglet suivant", + "controlify.action.controlify.gui_prev_tab": "Onglet précédent", + "controlify.action.controlify.gui_abstract_action_1": "Action abstraite de l'interface 1", + "controlify.action.controlify.gui_abstract_action_1.desc": "Une action générique qui peut effectuer différentes actions en fonction de l'écran actuel.", + "controlify.action.controlify.gui_abstract_action_2": "Action abstraite de l'interface 2", + "controlify.action.controlify.gui_abstract_action_2.desc": "Une action générique qui peut effectuer différentes actions en fonction de l'écran actuel.", + "controlify.action.controlify.gui_abstract_action_3.desc": "Une action générique qui peut effectuer différentes actions en fonction de l'écran actuel.", + "controlify.action.controlify.inv_select": "Sélectionner un item", + "controlify.action.controlify.inv_quick_move": "Déplacer rapidement un item", + "controlify.action.controlify.inv_take_half": "Prendre la moitié d'un item", + "controlify.action.controlify.drop": "Lâcher un item", + "controlify.action.controlify.drop_stack": "Lâcher une pile d'items", + "controlify.action.controlify.drop_inventory": "Lâcher un item (dans les conteneurs)", + "controlify.action.controlify.pick_block": "Sélectionner le bloc", + "controlify.action.controlify.pick_block_nbt": "Sélectionner le bloc (avec NBT)", + "controlify.action.controlify.take_screenshot": "Prendre une capture d'écran", + "controlify.action.controlify.toggle_debug_menu": "Activer/désactiver le menu de débogage (F3)", + "controlify.action.controlify.toggle_hud_visibility": "Activer/désactiver l'affichage de l'interface utilisateur", + "controlify.action.controlify.show_player_list": "Afficher la liste des joueurs", + "controlify.action.controlify.radial_menu": "Menu radial", + "controlify.action.controlify.radial_axis_up": "Menu radial - Haut", + "controlify.action.controlify.radial_axis_down": "Menu radial - Bas", + "controlify.action.controlify.radial_axis_left": "Menu radial - Gauche", + "controlify.action.controlify.radial_axis_right": "Menu radial - Droite", + "controlify.action.controlify.game_mode_switcher": "Sélecteur de Mode de Jeu", + "controlify.action.controlify.vmouse_move_up": "Déplacer la souris virtuelle vers le haut", + "controlify.action.controlify.vmouse_move_down": "Déplacer la souris virtuelle vers le bas", + "controlify.action.controlify.vmouse_move_left": "Déplacer la souris virtuelle vers la gauche", + "controlify.action.controlify.vmouse_move_right": "Déplacer la souris virtuelle vers la droite", + "controlify.action.controlify.vmouse_lclick": "Clic gauche de la souris virtuelle", + "controlify.action.controlify.vmouse_rclick": "Clic droit de la souris virtuelle", + "controlify.action.controlify.vmouse_shift_click": "Clic shift de la souris virtuelle", + "controlify.action.controlify.vmouse_snap_up": "Faire sauter la souris virtuelle vers le haut", + "controlify.action.controlify.vmouse_snap_down": "Faire sauter la souris virtuelle vers le bas", + "controlify.action.controlify.vmouse_snap_left": "Faire sauter la souris virtuelle vers la gauche", + "controlify.action.controlify.vmouse_snap_right": "Faire sauter la souris virtuelle vers la droite", + "controlify.action.controlify.vmouse_scroll_up": "Faire défiler la souris virtuelle vers le haut", + "controlify.action.controlify.vmouse_scroll_down": "Faire défiler la souris virtuelle vers le bas", + "controlify.action.controlify.vmouse_escape": "Touche d'échappement de la souris virtuelle", + "controlify.action.controlify.vmouse_shift": "Touche shift de la souris virtuelle", + "controlify.action.controlify.vmouse_toggle": "Activer/désactiver la souris virtuelle", + "controlify.action.controlify.gui_navi_down": "Navigation dans l'interface - Bas", + "controlify.action.controlify.gui_navi_up": "Navigation dans l'interface - Haut", + "controlify.action.controlify.gui_navi_left": "Navigation dans l'interface - Gauche", + "controlify.action.controlify.gui_navi_right": "Navigation dans l'interface - Droite", + "controlify.action.controlify.clear_binding": "Effacer la liaison", "controlify.input.controlify.button/left_stick": "Joystick gauche", "controlify.input.controlify.button/right_stick": "Joystick droit", "controlify.binding_category.gui": "Interface graphique (GUI)", diff --git a/src/main/resources/assets/controlify/lang/hu_hu.json b/src/main/resources/assets/controlify/lang/hu_hu.json index 623b86548..00c90263a 100644 --- a/src/main/resources/assets/controlify/lang/hu_hu.json +++ b/src/main/resources/assets/controlify/lang/hu_hu.json @@ -159,72 +159,72 @@ "controlify.bluetooth_warning.title": "Kontroller csatlakoztatva Bluetoothon keresztül", "controlify.bluetooth_warning.desc": "Előfordulhat, hogy egyes kontrollerek nem támogatnak bizonyos funkciókat Bluetooth-on keresztül. Például a HD Haptikus Visszajelzés Dualsense controllerrel. Ha úgy érzed, hogy hiányzik valami, hibajelentés előtt próbáld meg USB-n keresztül.", "controlify.bluetooth_warning.dont_show": "Ne mutasd újra", - "controlify.binding.controlify.walk_forward": "Gyaloglás Előre", - "controlify.binding.controlify.walk_backward": "Hátrálás", - "controlify.binding.controlify.strafe_left": "Oldalazás Balra", - "controlify.binding.controlify.strafe_right": "Oldalazás Jobbra", - "controlify.binding.controlify.look_up": "Nézés Fel", - "controlify.binding.controlify.look_down": "Nézés Le", - "controlify.binding.controlify.look_left": "Nézés Balra", - "controlify.binding.controlify.look_right": "Nézés Jobbra", - "controlify.binding.controlify.gyro_button": "Giroszkóp Gomb", - "controlify.binding.controlify.jump": "Ugrás", - "controlify.binding.controlify.sneak": "Lopakodás", - "controlify.binding.controlify.attack": "Támadás", - "controlify.binding.controlify.use": "Használ", - "controlify.binding.controlify.sprint": "Futás", - "controlify.binding.controlify.next_slot": "Következő Gyorstár Rekesz", - "controlify.binding.controlify.prev_slot": "Előző Gyorstár Rekesz", - "controlify.binding.controlify.hotbar_load_radial": "Kreatív Gyorstár Betöltése", - "controlify.binding.controlify.hotbar_save_radial": "Kreatív Gyorstár Mentése", - "controlify.binding.controlify.pause": "Játék Szüneteltetése", - "controlify.binding.controlify.inventory": "Eszköztár Megnyitása", - "controlify.binding.controlify.change_perspective": "Nézetváltás", - "controlify.binding.controlify.swap_hands": "Áthelyezés Másik Kézbe", - "controlify.binding.controlify.open_chat": "Chat Megnyitása", - "controlify.binding.controlify.gui_press": "GUI Lenyomás", - "controlify.binding.controlify.gui_back": "GUI Vissza", - "controlify.binding.controlify.gui_next_tab": "GUI Következő Lap", - "controlify.binding.controlify.gui_prev_tab": "GUI Előző Lap", - "controlify.binding.controlify.gui_abstract_action_1": "GUI Absztrakt Cselekvés 1", - "controlify.binding.controlify.gui_abstract_action_1.desc": "Egy általános hozzárendelés amely különböző dolgokat hajt végre az aktuális képernyőtől függően.", - "controlify.binding.controlify.gui_abstract_action_2": "GUI Absztrakt Cselekvés 2", - "controlify.binding.controlify.gui_abstract_action_2.desc": "Egy általános hozzárendelés amely különböző dolgokat hajt végre az aktuális képernyőtől függően.", - "controlify.binding.controlify.gui_abstract_action_3.desc": "Egy általános hozzárendelés amely különböző dolgokat hajt végre az aktuális képernyőtől függően.", - "controlify.binding.controlify.inv_select": "Tárgy Kiválasztása", - "controlify.binding.controlify.inv_quick_move": "Gyors Mozgatás", - "controlify.binding.controlify.inv_take_half": "Fél Elvétele", - "controlify.binding.controlify.drop": "Tárgy Eldobása", - "controlify.binding.controlify.drop_stack": "Stack Eldobása", - "controlify.binding.controlify.drop_inventory": "Tárgy Eldobása (Konténerekben)", - "controlify.binding.controlify.pick_block": "Blokk Kiválasztása", - "controlify.binding.controlify.pick_block_nbt": "Blokk Kiválasztása (NBT-vel)", - "controlify.binding.controlify.take_screenshot": "Képernyőkép Készítése", - "controlify.binding.controlify.toggle_hud_visibility": "HUD Láthatóságának Ki/Bekapcsolása", - "controlify.binding.controlify.show_player_list": "Játékoslista Mutatása", - "controlify.binding.controlify.radial_menu": "Radiális Menü", - "controlify.binding.controlify.radial_axis_up": "Radiális Menü - Fel", - "controlify.binding.controlify.radial_axis_down": "Radiális Menü - Le", - "controlify.binding.controlify.radial_axis_left": "Radiális Menü - Balra", - "controlify.binding.controlify.radial_axis_right": "Radiális Menü - Jobbra", - "controlify.binding.controlify.game_mode_switcher": "Jatékmód Választó", - "controlify.binding.controlify.vmouse_move_up": "Virtuális Egér Mozgatás - Fel", - "controlify.binding.controlify.vmouse_move_down": "Virtuális Egér Mozgatás - Le", - "controlify.binding.controlify.vmouse_move_left": "Virtuális Egér Mozgatás - Balra", - "controlify.binding.controlify.vmouse_move_right": "Virtuális Egér Mozgatás - Jobbra", - "controlify.binding.controlify.vmouse_lclick": "Virtuális Egér - Bal Kattintás", - "controlify.binding.controlify.vmouse_rclick": "Virtuális Egér - Jobb Kattintás", - "controlify.binding.controlify.vmouse_shift_click": "Virtuális Egér Shift-Kattintás", - "controlify.binding.controlify.vmouse_scroll_up": "Virtuális Egér - Görgő Fel", - "controlify.binding.controlify.vmouse_scroll_down": "Virtuális Egér - Görgő Le", - "controlify.binding.controlify.vmouse_escape": "Virtuális Egér - ESC Gomb", - "controlify.binding.controlify.vmouse_shift": "Virtuális Egér - Shift", - "controlify.binding.controlify.vmouse_toggle": "Virtuális Egér Átkapcsolása", - "controlify.binding.controlify.gui_navi_down": "GUI Navigáció - Le", - "controlify.binding.controlify.gui_navi_up": "GUI Navigáció - Fel", - "controlify.binding.controlify.gui_navi_left": "GUI Navigáció - Balra", - "controlify.binding.controlify.gui_navi_right": "GUI Navigáció - Jobbra", - "controlify.binding.controlify.clear_binding": "Billentyűparancsok Törlése", + "controlify.action.controlify.walk_forward": "Gyaloglás Előre", + "controlify.action.controlify.walk_backward": "Hátrálás", + "controlify.action.controlify.strafe_left": "Oldalazás Balra", + "controlify.action.controlify.strafe_right": "Oldalazás Jobbra", + "controlify.action.controlify.look_up": "Nézés Fel", + "controlify.action.controlify.look_down": "Nézés Le", + "controlify.action.controlify.look_left": "Nézés Balra", + "controlify.action.controlify.look_right": "Nézés Jobbra", + "controlify.action.controlify.gyro_button": "Giroszkóp Gomb", + "controlify.action.controlify.jump": "Ugrás", + "controlify.action.controlify.sneak": "Lopakodás", + "controlify.action.controlify.attack": "Támadás", + "controlify.action.controlify.use": "Használ", + "controlify.action.controlify.sprint": "Futás", + "controlify.action.controlify.next_slot": "Következő Gyorstár Rekesz", + "controlify.action.controlify.prev_slot": "Előző Gyorstár Rekesz", + "controlify.action.controlify.hotbar_load_radial": "Kreatív Gyorstár Betöltése", + "controlify.action.controlify.hotbar_save_radial": "Kreatív Gyorstár Mentése", + "controlify.action.controlify.pause": "Játék Szüneteltetése", + "controlify.action.controlify.inventory": "Eszköztár Megnyitása", + "controlify.action.controlify.change_perspective": "Nézetváltás", + "controlify.action.controlify.swap_hands": "Áthelyezés Másik Kézbe", + "controlify.action.controlify.open_chat": "Chat Megnyitása", + "controlify.action.controlify.gui_press": "GUI Lenyomás", + "controlify.action.controlify.gui_back": "GUI Vissza", + "controlify.action.controlify.gui_next_tab": "GUI Következő Lap", + "controlify.action.controlify.gui_prev_tab": "GUI Előző Lap", + "controlify.action.controlify.gui_abstract_action_1": "GUI Absztrakt Cselekvés 1", + "controlify.action.controlify.gui_abstract_action_1.desc": "Egy általános hozzárendelés amely különböző dolgokat hajt végre az aktuális képernyőtől függően.", + "controlify.action.controlify.gui_abstract_action_2": "GUI Absztrakt Cselekvés 2", + "controlify.action.controlify.gui_abstract_action_2.desc": "Egy általános hozzárendelés amely különböző dolgokat hajt végre az aktuális képernyőtől függően.", + "controlify.action.controlify.gui_abstract_action_3.desc": "Egy általános hozzárendelés amely különböző dolgokat hajt végre az aktuális képernyőtől függően.", + "controlify.action.controlify.inv_select": "Tárgy Kiválasztása", + "controlify.action.controlify.inv_quick_move": "Gyors Mozgatás", + "controlify.action.controlify.inv_take_half": "Fél Elvétele", + "controlify.action.controlify.drop": "Tárgy Eldobása", + "controlify.action.controlify.drop_stack": "Stack Eldobása", + "controlify.action.controlify.drop_inventory": "Tárgy Eldobása (Konténerekben)", + "controlify.action.controlify.pick_block": "Blokk Kiválasztása", + "controlify.action.controlify.pick_block_nbt": "Blokk Kiválasztása (NBT-vel)", + "controlify.action.controlify.take_screenshot": "Képernyőkép Készítése", + "controlify.action.controlify.toggle_hud_visibility": "HUD Láthatóságának Ki/Bekapcsolása", + "controlify.action.controlify.show_player_list": "Játékoslista Mutatása", + "controlify.action.controlify.radial_menu": "Radiális Menü", + "controlify.action.controlify.radial_axis_up": "Radiális Menü - Fel", + "controlify.action.controlify.radial_axis_down": "Radiális Menü - Le", + "controlify.action.controlify.radial_axis_left": "Radiális Menü - Balra", + "controlify.action.controlify.radial_axis_right": "Radiális Menü - Jobbra", + "controlify.action.controlify.game_mode_switcher": "Jatékmód Választó", + "controlify.action.controlify.vmouse_move_up": "Virtuális Egér Mozgatás - Fel", + "controlify.action.controlify.vmouse_move_down": "Virtuális Egér Mozgatás - Le", + "controlify.action.controlify.vmouse_move_left": "Virtuális Egér Mozgatás - Balra", + "controlify.action.controlify.vmouse_move_right": "Virtuális Egér Mozgatás - Jobbra", + "controlify.action.controlify.vmouse_lclick": "Virtuális Egér - Bal Kattintás", + "controlify.action.controlify.vmouse_rclick": "Virtuális Egér - Jobb Kattintás", + "controlify.action.controlify.vmouse_shift_click": "Virtuális Egér Shift-Kattintás", + "controlify.action.controlify.vmouse_scroll_up": "Virtuális Egér - Görgő Fel", + "controlify.action.controlify.vmouse_scroll_down": "Virtuális Egér - Görgő Le", + "controlify.action.controlify.vmouse_escape": "Virtuális Egér - ESC Gomb", + "controlify.action.controlify.vmouse_shift": "Virtuális Egér - Shift", + "controlify.action.controlify.vmouse_toggle": "Virtuális Egér Átkapcsolása", + "controlify.action.controlify.gui_navi_down": "GUI Navigáció - Le", + "controlify.action.controlify.gui_navi_up": "GUI Navigáció - Fel", + "controlify.action.controlify.gui_navi_left": "GUI Navigáció - Balra", + "controlify.action.controlify.gui_navi_right": "GUI Navigáció - Jobbra", + "controlify.action.controlify.clear_binding": "Billentyűparancsok Törlése", "controlify.input.controlify.button/left_stick": "Bal Kar", "controlify.input.controlify.button/right_stick": "Jobb Kar", "controlify.binding_category.gui": "Grafikus interfész (GUI)", diff --git a/src/main/resources/assets/controlify/lang/it_it.json b/src/main/resources/assets/controlify/lang/it_it.json index 452154676..4660c5249 100644 --- a/src/main/resources/assets/controlify/lang/it_it.json +++ b/src/main/resources/assets/controlify/lang/it_it.json @@ -200,90 +200,90 @@ "controlify.bluetooth_warning.title": "Controller collegato via Bluetooth", "controlify.bluetooth_warning.desc": "Alcuni funzioni del controller potrebbero mancare quando si è connessi tramite Bluetooth. Per esempio, Haptics HD su DualSense. Se pensi di non avere una funzionalità, prova a collegare il controller tramite USB prima di segnalare il problema.", "controlify.bluetooth_warning.dont_show": "Non mostrare più", - "controlify.binding.controlify.walk_forward": "Cammina Avanti", - "controlify.binding.controlify.walk_backward": "Cammina Indietro", - "controlify.binding.controlify.strafe_left": "Cammina a Sinistra", - "controlify.binding.controlify.strafe_right": "Cammina a Destra", - "controlify.binding.controlify.look_up": "Guarda Su", - "controlify.binding.controlify.look_down": "Guarda Giù", - "controlify.binding.controlify.look_left": "Guarda a Sinistra", - "controlify.binding.controlify.look_right": "Guarda a Destra", - "controlify.binding.controlify.gyro_button": "Pulsante Giroscopio", - "controlify.binding.controlify.jump": "Salta", - "controlify.binding.controlify.sneak": "Movimento Furtivo", - "controlify.binding.controlify.attack": "Attacca", - "controlify.binding.controlify.use": "Usa", - "controlify.binding.controlify.sprint": "Corsa", - "controlify.binding.controlify.next_slot": "Slot Successivo della Barra di Scelta Rapida", - "controlify.binding.controlify.prev_slot": "Slot Precedente della Barra di Scelta Rapida", - "controlify.binding.controlify.hotbar_load_radial": "Carica Barra Scelta Rapida Mod. Creativa", - "controlify.binding.controlify.hotbar_save_radial": "Salva Barra Scelta Rapida Mod. Creativa", - "controlify.binding.controlify.hotbar_item_select_radial": "Selezione Barra Scelta Rapida Radiale", - "controlify.binding.controlify.pause": "Pausa Gioco", - "controlify.binding.controlify.inventory": "Apri Inventario", - "controlify.binding.controlify.change_perspective": "Cambia Prospettiva", - "controlify.binding.controlify.swap_hands": "Scambia Mani", - "controlify.binding.controlify.open_chat": "Apri Chat", - "controlify.binding.controlify.gui_press": "Premi GUI", - "controlify.binding.controlify.gui_back": "Indietro GUI", - "controlify.binding.controlify.gui_next_tab": "Scheda Successiva GUI", - "controlify.binding.controlify.gui_prev_tab": "Scheda Precedente GUI", - "controlify.binding.controlify.gui_abstract_action_1": "Azione Astratta 1 GUI", - "controlify.binding.controlify.gui_abstract_action_1.desc": "Un'associazione generica che può fare cose diverse a seconda della schermata attuale.", - "controlify.binding.controlify.gui_abstract_action_2": "Azione Astratta 2 GUI", - "controlify.binding.controlify.gui_abstract_action_2.desc": "Un'associazione generica che può fare cose diverse a seconda della schermata attuale.", - "controlify.binding.controlify.gui_abstract_action_3": "Azione Astratta 3 GUI", - "controlify.binding.controlify.gui_abstract_action_3.desc": "Un'associazione generica che può fare cose diverse a seconda della schermata attuale.", - "controlify.binding.controlify.inv_select": "Seleziona Oggetto", - "controlify.binding.controlify.inv_quick_move": "Spostamento Rapido", - "controlify.binding.controlify.inv_take_half": "Prendi Metà", - "controlify.binding.controlify.drop": "Lascia Oggetto", - "controlify.binding.controlify.drop_stack": "Lascia Stack", - "controlify.binding.controlify.drop_inventory": "Lascia Oggetto (Nei Contenitori)", - "controlify.binding.controlify.pick_block": "Seleziona Blocco", - "controlify.binding.controlify.pick_block_nbt": "Seleziona Blocco (con NBT)", - "controlify.binding.controlify.take_screenshot": "Cattura Schermata", - "controlify.binding.controlify.debug_radial": "Debug Menu Radiale", - "controlify.binding.controlify.toggle_debug_menu": "Attiva/Disattiva il Menu Debug (F3)", - "controlify.binding.controlify.toggle_debug_menu_fps": "Attiva/Disattiva il Menu Debug (F3) con FPS", - "controlify.binding.controlify.toggle_debug_menu_prof": "Attiva/Disattiva il Menu Debug (F3) con Profilatore", - "controlify.binding.controlify.toggle_debug_menu_net": "Attiva/Disattiva il Menu Debug (F3) con Grafico di Rete", - "controlify.binding.controlify.toggle_debug_menu_chart": "Attiva/Disattiva il Menu Debug (F3) con Grafici", - "controlify.binding.controlify.toggle_hud_visibility": "Attiva/Disattiva HUD", - "controlify.binding.controlify.show_player_list": "Mostra Elenco Giocatori", - "controlify.binding.controlify.radial_menu": "Menù Radiale", - "controlify.binding.controlify.radial_axis_up": "Menu Radiale Su", - "controlify.binding.controlify.radial_axis_down": "Menu Radiale Giù", - "controlify.binding.controlify.radial_axis_left": "Menu Radiale Sinistra", - "controlify.binding.controlify.radial_axis_right": "Menu Radiale Destra", - "controlify.binding.controlify.game_mode_switcher": "Selettore Modalità di Gioco", - "controlify.binding.controlify.vmouse_move_up": "VMouse Muovi Su", - "controlify.binding.controlify.vmouse_move_down": "VMouse Muovi Giù", - "controlify.binding.controlify.vmouse_move_left": "VMouse Muovi Sinistra", - "controlify.binding.controlify.vmouse_move_right": "VMouse Muovi Destra", - "controlify.binding.controlify.vmouse_lclick": "VMouse Click Sinistro", - "controlify.binding.controlify.vmouse_rclick": "VMouse Click Destro", - "controlify.binding.controlify.vmouse_snap_up": "Passo Mouse Su", - "controlify.binding.controlify.vmouse_snap_down": "Passo Mouse Giù", - "controlify.binding.controlify.vmouse_snap_left": "Passo Mouse Sinistra", - "controlify.binding.controlify.vmouse_snap_right": "Passo Mouse Destra", - "controlify.binding.controlify.vmouse_scroll_up": "Scorri VMouse Su", - "controlify.binding.controlify.vmouse_scroll_down": "Scorri VMouse Giù", - "controlify.binding.controlify.vmouse_escape": "VMouse Premi ESC", - "controlify.binding.controlify.vmouse_shift": "VMouse Premi Shift", - "controlify.binding.controlify.vmouse_page_next": "VMouse Pagina Successiva", - "controlify.binding.controlify.vmouse_page_prev": "VMouse Pagina Precedente", - "controlify.binding.controlify.vmouse_page_down": "VMouse Pagina Giù", - "controlify.binding.controlify.vmouse_page_up": "VMouse Pagina Su", - "controlify.binding.controlify.vmouse_toggle": "Attiva/Disattiva Mouse Virtuale", - "controlify.binding.controlify.gui_navi_down": "Naviga Interfaccia Giù", - "controlify.binding.controlify.gui_navi_up": "Naviga Interfaccia Su", - "controlify.binding.controlify.gui_navi_left": "Naviga Interfaccia Sinistra", - "controlify.binding.controlify.gui_navi_right": "Naviga Interfaccia Destra", - "controlify.binding.controlify.clear_binding": "Rimuovi Associazione", - "controlify.binding.voicechat.ptt_hold": "Push-To-Talk (Tieni Premuto)", - "controlify.binding.voicechat.ptt_toggle": "Push-To-Talk (Attiva/Disattiva)", - "controlify.binding.voicechat.mute_microphone": "Silenzia Microfono", + "controlify.action.controlify.walk_forward": "Cammina Avanti", + "controlify.action.controlify.walk_backward": "Cammina Indietro", + "controlify.action.controlify.strafe_left": "Cammina a Sinistra", + "controlify.action.controlify.strafe_right": "Cammina a Destra", + "controlify.action.controlify.look_up": "Guarda Su", + "controlify.action.controlify.look_down": "Guarda Giù", + "controlify.action.controlify.look_left": "Guarda a Sinistra", + "controlify.action.controlify.look_right": "Guarda a Destra", + "controlify.action.controlify.gyro_button": "Pulsante Giroscopio", + "controlify.action.controlify.jump": "Salta", + "controlify.action.controlify.sneak": "Movimento Furtivo", + "controlify.action.controlify.attack": "Attacca", + "controlify.action.controlify.use": "Usa", + "controlify.action.controlify.sprint": "Corsa", + "controlify.action.controlify.next_slot": "Slot Successivo della Barra di Scelta Rapida", + "controlify.action.controlify.prev_slot": "Slot Precedente della Barra di Scelta Rapida", + "controlify.action.controlify.hotbar_load_radial": "Carica Barra Scelta Rapida Mod. Creativa", + "controlify.action.controlify.hotbar_save_radial": "Salva Barra Scelta Rapida Mod. Creativa", + "controlify.action.controlify.hotbar_item_select_radial": "Selezione Barra Scelta Rapida Radiale", + "controlify.action.controlify.pause": "Pausa Gioco", + "controlify.action.controlify.inventory": "Apri Inventario", + "controlify.action.controlify.change_perspective": "Cambia Prospettiva", + "controlify.action.controlify.swap_hands": "Scambia Mani", + "controlify.action.controlify.open_chat": "Apri Chat", + "controlify.action.controlify.gui_press": "Premi GUI", + "controlify.action.controlify.gui_back": "Indietro GUI", + "controlify.action.controlify.gui_next_tab": "Scheda Successiva GUI", + "controlify.action.controlify.gui_prev_tab": "Scheda Precedente GUI", + "controlify.action.controlify.gui_abstract_action_1": "Azione Astratta 1 GUI", + "controlify.action.controlify.gui_abstract_action_1.desc": "Un'associazione generica che può fare cose diverse a seconda della schermata attuale.", + "controlify.action.controlify.gui_abstract_action_2": "Azione Astratta 2 GUI", + "controlify.action.controlify.gui_abstract_action_2.desc": "Un'associazione generica che può fare cose diverse a seconda della schermata attuale.", + "controlify.action.controlify.gui_abstract_action_3": "Azione Astratta 3 GUI", + "controlify.action.controlify.gui_abstract_action_3.desc": "Un'associazione generica che può fare cose diverse a seconda della schermata attuale.", + "controlify.action.controlify.inv_select": "Seleziona Oggetto", + "controlify.action.controlify.inv_quick_move": "Spostamento Rapido", + "controlify.action.controlify.inv_take_half": "Prendi Metà", + "controlify.action.controlify.drop": "Lascia Oggetto", + "controlify.action.controlify.drop_stack": "Lascia Stack", + "controlify.action.controlify.drop_inventory": "Lascia Oggetto (Nei Contenitori)", + "controlify.action.controlify.pick_block": "Seleziona Blocco", + "controlify.action.controlify.pick_block_nbt": "Seleziona Blocco (con NBT)", + "controlify.action.controlify.take_screenshot": "Cattura Schermata", + "controlify.action.controlify.debug_radial": "Debug Menu Radiale", + "controlify.action.controlify.toggle_debug_menu": "Attiva/Disattiva il Menu Debug (F3)", + "controlify.action.controlify.toggle_debug_menu_fps": "Attiva/Disattiva il Menu Debug (F3) con FPS", + "controlify.action.controlify.toggle_debug_menu_prof": "Attiva/Disattiva il Menu Debug (F3) con Profilatore", + "controlify.action.controlify.toggle_debug_menu_net": "Attiva/Disattiva il Menu Debug (F3) con Grafico di Rete", + "controlify.action.controlify.toggle_debug_menu_chart": "Attiva/Disattiva il Menu Debug (F3) con Grafici", + "controlify.action.controlify.toggle_hud_visibility": "Attiva/Disattiva HUD", + "controlify.action.controlify.show_player_list": "Mostra Elenco Giocatori", + "controlify.action.controlify.radial_menu": "Menù Radiale", + "controlify.action.controlify.radial_axis_up": "Menu Radiale Su", + "controlify.action.controlify.radial_axis_down": "Menu Radiale Giù", + "controlify.action.controlify.radial_axis_left": "Menu Radiale Sinistra", + "controlify.action.controlify.radial_axis_right": "Menu Radiale Destra", + "controlify.action.controlify.game_mode_switcher": "Selettore Modalità di Gioco", + "controlify.action.controlify.vmouse_move_up": "VMouse Muovi Su", + "controlify.action.controlify.vmouse_move_down": "VMouse Muovi Giù", + "controlify.action.controlify.vmouse_move_left": "VMouse Muovi Sinistra", + "controlify.action.controlify.vmouse_move_right": "VMouse Muovi Destra", + "controlify.action.controlify.vmouse_lclick": "VMouse Click Sinistro", + "controlify.action.controlify.vmouse_rclick": "VMouse Click Destro", + "controlify.action.controlify.vmouse_snap_up": "Passo Mouse Su", + "controlify.action.controlify.vmouse_snap_down": "Passo Mouse Giù", + "controlify.action.controlify.vmouse_snap_left": "Passo Mouse Sinistra", + "controlify.action.controlify.vmouse_snap_right": "Passo Mouse Destra", + "controlify.action.controlify.vmouse_scroll_up": "Scorri VMouse Su", + "controlify.action.controlify.vmouse_scroll_down": "Scorri VMouse Giù", + "controlify.action.controlify.vmouse_escape": "VMouse Premi ESC", + "controlify.action.controlify.vmouse_shift": "VMouse Premi Shift", + "controlify.action.controlify.vmouse_page_next": "VMouse Pagina Successiva", + "controlify.action.controlify.vmouse_page_prev": "VMouse Pagina Precedente", + "controlify.action.controlify.vmouse_page_down": "VMouse Pagina Giù", + "controlify.action.controlify.vmouse_page_up": "VMouse Pagina Su", + "controlify.action.controlify.vmouse_toggle": "Attiva/Disattiva Mouse Virtuale", + "controlify.action.controlify.gui_navi_down": "Naviga Interfaccia Giù", + "controlify.action.controlify.gui_navi_up": "Naviga Interfaccia Su", + "controlify.action.controlify.gui_navi_left": "Naviga Interfaccia Sinistra", + "controlify.action.controlify.gui_navi_right": "Naviga Interfaccia Destra", + "controlify.action.controlify.clear_binding": "Rimuovi Associazione", + "controlify.action.voicechat.ptt_hold": "Push-To-Talk (Tieni Premuto)", + "controlify.action.voicechat.ptt_toggle": "Push-To-Talk (Attiva/Disattiva)", + "controlify.action.voicechat.mute_microphone": "Silenzia Microfono", "controlify.input.controlify.button/south": "Pulsante Sud", "controlify.input.controlify.button/east": "Pulsante Est", "controlify.input.controlify.button/west": "Pulsante Ovest", @@ -404,13 +404,13 @@ "controlify.radial.debug.overlay_prof": "Mostra Overlay con Profilatore", "controlify.radial.debug.overlay_chart": "Mostra Overlay con Grafici", "controlify.radial.debug.hide_overlay": "Nascondi Overlay", - "controlify.radial.debug.reload_chunks": "Ricarica Chunk", - "controlify.radial.debug.chunk_borders": "Attiva/Disattiva Confini Chunk", - "controlify.radial.debug.advanced_tooltips": "Attiva/Disattiva Suggerimenti Avanzati", - "controlify.radial.debug.entity_hitboxes": "Attiva/Disattiva Hitbox Entità", - "controlify.radial.debug.reload_packs": "Ricarica Pacchetti di Risorse", - "controlify.radial.debug.clear_chat": "Svuota Chat", - "controlify.radial.debug.profile": "Avvia/Ferma Profilazione", + "controlify.action.controlify.debug_reload_chunks": "Ricarica Chunk", + "controlify.action.controlify.debug_toggle_chunk_borders": "Attiva/Disattiva Confini Chunk", + "controlify.action.controlify.debug_toggle_advanced_tooltips": "Attiva/Disattiva Suggerimenti Avanzati", + "controlify.action.controlify.debug_toggle_entity_hitboxes": "Attiva/Disattiva Hitbox Entità", + "controlify.action.controlify.debug_reload_resource_packs": "Ricarica Pacchetti di Risorse", + "controlify.action.controlify.debug_clear_chat": "Svuota Chat", + "controlify.action.controlify.debug_start_stop_profiling": "Avvia/Ferma Profilazione", "controlify.controller_submission.title": "Invio Dati per Controller Sconosciuto", "controlify.controller_submission.message": "Si prega di trasmettere alcune delle informazioni del tuo controller al database di Controlify per aggiungerlo in un aggiornamento futuro.\n\nControlify invierà le seguenti informazioni:\n- Il Vendor ID e Product ID del tuo controller\n- Il GUID del tuo controller\n- Il nome del tuo controller (nella casella sotto)\n- La versione di Controlify attualmente in uso\n\nQuesto processo è completamente anonimo e non terrà in memoria alcun dato personale né informazioni dell'account.", "controlify.controller_submission.operational_checkbox": "Il tuo controller funziona con Controlify?", diff --git a/src/main/resources/assets/controlify/lang/ja_jp.json b/src/main/resources/assets/controlify/lang/ja_jp.json index 812ea8790..a6cf47026 100644 --- a/src/main/resources/assets/controlify/lang/ja_jp.json +++ b/src/main/resources/assets/controlify/lang/ja_jp.json @@ -216,91 +216,91 @@ "controlify.bluetooth_warning.title": "Bluetooth 経由で接続されたコントローラー", "controlify.bluetooth_warning.desc": "Bluetooth経由で接続すると、一部のコントローラに機能が欠落している可能性があります。たとえば、デュアルセンスのHDハプティクスです。 機能が不足していると思われる場合は、報告する前にUSB接続を使用してみてください", "controlify.bluetooth_warning.dont_show": "今後表示しない", - "controlify.binding.controlify.walk_forward": "前に進む", - "controlify.binding.controlify.walk_backward": "後方に進む", - "controlify.binding.controlify.strafe_left": "左に進む", - "controlify.binding.controlify.strafe_right": "右に進む", - "controlify.binding.controlify.look_up": "視点 上", - "controlify.binding.controlify.look_down": "視点 下", - "controlify.binding.controlify.look_left": "視点 左", - "controlify.binding.controlify.look_right": "視点 右", - "controlify.binding.controlify.gyro_button": "ジャイロ ボタン", - "controlify.binding.controlify.jump": "ジャンプ", - "controlify.binding.controlify.sneak": "スニーク", - "controlify.binding.controlify.attack": "攻撃", - "controlify.binding.controlify.use": "使用", - "controlify.binding.controlify.sprint": "スプリント", - "controlify.binding.controlify.next_slot": "次のホットバースロット", - "controlify.binding.controlify.prev_slot": "前のホットバースロット", - "controlify.binding.controlify.hotbar_load_radial": "クリエイティブホットバーを読み込む", - "controlify.binding.controlify.hotbar_save_radial": "クリエイティブホットバーを保存", - "controlify.binding.controlify.hotbar_item_select_radial": "ホットバースロット選択ラジアル", - "controlify.binding.controlify.pause": "ゲームを一時停止", - "controlify.binding.controlify.inventory": "インベントリを開く", - "controlify.binding.controlify.change_perspective": "視点を変更", - "controlify.binding.controlify.swap_hands": "オフハンド切り替え", - "controlify.binding.controlify.open_chat": "チャットを開く", - "controlify.binding.controlify.gui_press": "GUI 押す", - "controlify.binding.controlify.gui_back": "GUI 戻る", - "controlify.binding.controlify.gui_next_tab": "GUI 次のタブ", - "controlify.binding.controlify.gui_prev_tab": "GUI 前のタブ", - "controlify.binding.controlify.gui_abstract_action_1": "GUI抽象アクション 1", - "controlify.binding.controlify.gui_abstract_action_1.desc": "現在の画面に応じて異なることを行う一般的なバインディング", - "controlify.binding.controlify.gui_abstract_action_2": "GUI抽象アクション 2", - "controlify.binding.controlify.gui_abstract_action_2.desc": "現在の画面に応じて異なることを行う一般的なバインディング", - "controlify.binding.controlify.gui_abstract_action_3": "GUI抽象アクション 3", - "controlify.binding.controlify.gui_abstract_action_3.desc": "現在の画面に応じて異なることを行う一般的なバインディング", - "controlify.binding.controlify.inv_select": "アイテムを選択", - "controlify.binding.controlify.inv_quick_move": "クイック移動", - "controlify.binding.controlify.inv_take_half": "半分取る", - "controlify.binding.controlify.drop": "アイテムをドロップ", - "controlify.binding.controlify.drop_stack": "ドロップスタック", - "controlify.binding.controlify.drop_inventory": "アイテムをドロップ(コンテナ内)", - "controlify.binding.controlify.pick_block": "ブロックを選択", - "controlify.binding.controlify.pick_block_nbt": "ブロックを選択 (NBT)", - "controlify.binding.controlify.take_screenshot": "スクリーンショットを撮影", - "controlify.binding.controlify.debug_radial": "デバッグラディアルメニュー", - "controlify.binding.controlify.toggle_debug_menu": "デバッグメニューの切り替え (F3)", - "controlify.binding.controlify.toggle_debug_menu_fps": "デバッグメニュー切り替え(FPS)", - "controlify.binding.controlify.toggle_debug_menu_prof": "デバッグメニュー切り替え(Profiler)", - "controlify.binding.controlify.toggle_debug_menu_net": "デバッグメニュー切り替え(グラフ)", - "controlify.binding.controlify.toggle_debug_menu_chart": "デバッグメニュー切り替え(チャート)", - "controlify.binding.controlify.toggle_hud_visibility": "HUD表示の切り替え", - "controlify.binding.controlify.show_player_list": "プレイヤーリストを表示", - "controlify.binding.controlify.radial_menu": "ラジアルメニュー", - "controlify.binding.controlify.radial_axis_up": "ラジアルメニュー 上", - "controlify.binding.controlify.radial_axis_down": "ラジアルメニュー 下", - "controlify.binding.controlify.radial_axis_left": "ラジアルメニュー 左", - "controlify.binding.controlify.radial_axis_right": "ラジアルメニュー 右", - "controlify.binding.controlify.game_mode_switcher": "ゲームモードの切り替え", - "controlify.binding.controlify.vmouse_move_up": "仮想マウス 移動上", - "controlify.binding.controlify.vmouse_move_down": "仮想マウス 移動下", - "controlify.binding.controlify.vmouse_move_left": "仮想マウス 移動 左", - "controlify.binding.controlify.vmouse_move_right": "仮想マウス 移動 右", - "controlify.binding.controlify.vmouse_lclick": "仮想マウス 左クリック", - "controlify.binding.controlify.vmouse_rclick": "仮想マウス 右クリック", - "controlify.binding.controlify.vmouse_shift_click": "仮想マウス shiftクリック", - "controlify.binding.controlify.vmouse_snap_up": "スナップマウス 上", - "controlify.binding.controlify.vmouse_snap_down": "スナップマウス 下", - "controlify.binding.controlify.vmouse_snap_left": "スナップマウス 左", - "controlify.binding.controlify.vmouse_snap_right": "スナップマウス 右", - "controlify.binding.controlify.vmouse_scroll_up": "仮想マウス 上スクロール", - "controlify.binding.controlify.vmouse_scroll_down": "仮想マウス 下スクロール", - "controlify.binding.controlify.vmouse_escape": "仮想マウス escap", - "controlify.binding.controlify.vmouse_shift": "仮想マウス shift", - "controlify.binding.controlify.vmouse_page_next": "仮想マウス Page Next", - "controlify.binding.controlify.vmouse_page_prev": "仮想マウス Page Prev", - "controlify.binding.controlify.vmouse_page_down": "VMouseページダウン", - "controlify.binding.controlify.vmouse_page_up": "VMouseページアップ", - "controlify.binding.controlify.vmouse_toggle": "仮想マウスの切り替え", - "controlify.binding.controlify.gui_navi_down": "GUI Navi 下", - "controlify.binding.controlify.gui_navi_up": "GUI Navi 上", - "controlify.binding.controlify.gui_navi_left": "GUI Navi 左", - "controlify.binding.controlify.gui_navi_right": "GUI Navi 右", - "controlify.binding.controlify.clear_binding": "バインディングをクリア", - "controlify.binding.voicechat.ptt_hold": "プッシュトゥトーク(ホールド)", - "controlify.binding.voicechat.ptt_toggle": "プッシュトゥトーク(切り替え)", - "controlify.binding.voicechat.mute_microphone": "マイクをミュート", + "controlify.action.controlify.walk_forward": "前に進む", + "controlify.action.controlify.walk_backward": "後方に進む", + "controlify.action.controlify.strafe_left": "左に進む", + "controlify.action.controlify.strafe_right": "右に進む", + "controlify.action.controlify.look_up": "視点 上", + "controlify.action.controlify.look_down": "視点 下", + "controlify.action.controlify.look_left": "視点 左", + "controlify.action.controlify.look_right": "視点 右", + "controlify.action.controlify.gyro_button": "ジャイロ ボタン", + "controlify.action.controlify.jump": "ジャンプ", + "controlify.action.controlify.sneak": "スニーク", + "controlify.action.controlify.attack": "攻撃", + "controlify.action.controlify.use": "使用", + "controlify.action.controlify.sprint": "スプリント", + "controlify.action.controlify.next_slot": "次のホットバースロット", + "controlify.action.controlify.prev_slot": "前のホットバースロット", + "controlify.action.controlify.hotbar_load_radial": "クリエイティブホットバーを読み込む", + "controlify.action.controlify.hotbar_save_radial": "クリエイティブホットバーを保存", + "controlify.action.controlify.hotbar_item_select_radial": "ホットバースロット選択ラジアル", + "controlify.action.controlify.pause": "ゲームを一時停止", + "controlify.action.controlify.inventory": "インベントリを開く", + "controlify.action.controlify.change_perspective": "視点を変更", + "controlify.action.controlify.swap_hands": "オフハンド切り替え", + "controlify.action.controlify.open_chat": "チャットを開く", + "controlify.action.controlify.gui_press": "GUI 押す", + "controlify.action.controlify.gui_back": "GUI 戻る", + "controlify.action.controlify.gui_next_tab": "GUI 次のタブ", + "controlify.action.controlify.gui_prev_tab": "GUI 前のタブ", + "controlify.action.controlify.gui_abstract_action_1": "GUI抽象アクション 1", + "controlify.action.controlify.gui_abstract_action_1.desc": "現在の画面に応じて異なることを行う一般的なバインディング", + "controlify.action.controlify.gui_abstract_action_2": "GUI抽象アクション 2", + "controlify.action.controlify.gui_abstract_action_2.desc": "現在の画面に応じて異なることを行う一般的なバインディング", + "controlify.action.controlify.gui_abstract_action_3": "GUI抽象アクション 3", + "controlify.action.controlify.gui_abstract_action_3.desc": "現在の画面に応じて異なることを行う一般的なバインディング", + "controlify.action.controlify.inv_select": "アイテムを選択", + "controlify.action.controlify.inv_quick_move": "クイック移動", + "controlify.action.controlify.inv_take_half": "半分取る", + "controlify.action.controlify.drop": "アイテムをドロップ", + "controlify.action.controlify.drop_stack": "ドロップスタック", + "controlify.action.controlify.drop_inventory": "アイテムをドロップ(コンテナ内)", + "controlify.action.controlify.pick_block": "ブロックを選択", + "controlify.action.controlify.pick_block_nbt": "ブロックを選択 (NBT)", + "controlify.action.controlify.take_screenshot": "スクリーンショットを撮影", + "controlify.action.controlify.debug_radial": "デバッグラディアルメニュー", + "controlify.action.controlify.toggle_debug_menu": "デバッグメニューの切り替え (F3)", + "controlify.action.controlify.toggle_debug_menu_fps": "デバッグメニュー切り替え(FPS)", + "controlify.action.controlify.toggle_debug_menu_prof": "デバッグメニュー切り替え(Profiler)", + "controlify.action.controlify.toggle_debug_menu_net": "デバッグメニュー切り替え(グラフ)", + "controlify.action.controlify.toggle_debug_menu_chart": "デバッグメニュー切り替え(チャート)", + "controlify.action.controlify.toggle_hud_visibility": "HUD表示の切り替え", + "controlify.action.controlify.show_player_list": "プレイヤーリストを表示", + "controlify.action.controlify.radial_menu": "ラジアルメニュー", + "controlify.action.controlify.radial_axis_up": "ラジアルメニュー 上", + "controlify.action.controlify.radial_axis_down": "ラジアルメニュー 下", + "controlify.action.controlify.radial_axis_left": "ラジアルメニュー 左", + "controlify.action.controlify.radial_axis_right": "ラジアルメニュー 右", + "controlify.action.controlify.game_mode_switcher": "ゲームモードの切り替え", + "controlify.action.controlify.vmouse_move_up": "仮想マウス 移動上", + "controlify.action.controlify.vmouse_move_down": "仮想マウス 移動下", + "controlify.action.controlify.vmouse_move_left": "仮想マウス 移動 左", + "controlify.action.controlify.vmouse_move_right": "仮想マウス 移動 右", + "controlify.action.controlify.vmouse_lclick": "仮想マウス 左クリック", + "controlify.action.controlify.vmouse_rclick": "仮想マウス 右クリック", + "controlify.action.controlify.vmouse_shift_click": "仮想マウス shiftクリック", + "controlify.action.controlify.vmouse_snap_up": "スナップマウス 上", + "controlify.action.controlify.vmouse_snap_down": "スナップマウス 下", + "controlify.action.controlify.vmouse_snap_left": "スナップマウス 左", + "controlify.action.controlify.vmouse_snap_right": "スナップマウス 右", + "controlify.action.controlify.vmouse_scroll_up": "仮想マウス 上スクロール", + "controlify.action.controlify.vmouse_scroll_down": "仮想マウス 下スクロール", + "controlify.action.controlify.vmouse_escape": "仮想マウス escap", + "controlify.action.controlify.vmouse_shift": "仮想マウス shift", + "controlify.action.controlify.vmouse_page_next": "仮想マウス Page Next", + "controlify.action.controlify.vmouse_page_prev": "仮想マウス Page Prev", + "controlify.action.controlify.vmouse_page_down": "VMouseページダウン", + "controlify.action.controlify.vmouse_page_up": "VMouseページアップ", + "controlify.action.controlify.vmouse_toggle": "仮想マウスの切り替え", + "controlify.action.controlify.gui_navi_down": "GUI Navi 下", + "controlify.action.controlify.gui_navi_up": "GUI Navi 上", + "controlify.action.controlify.gui_navi_left": "GUI Navi 左", + "controlify.action.controlify.gui_navi_right": "GUI Navi 右", + "controlify.action.controlify.clear_binding": "バインディングをクリア", + "controlify.action.voicechat.ptt_hold": "プッシュトゥトーク(ホールド)", + "controlify.action.voicechat.ptt_toggle": "プッシュトゥトーク(切り替え)", + "controlify.action.voicechat.mute_microphone": "マイクをミュート", "controlify.input.controlify.button/south": "南面", "controlify.input.controlify.button/east": "東面", "controlify.input.controlify.button/west": "西面", @@ -430,13 +430,13 @@ "controlify.radial.debug.overlay_prof": "プロファイラーオーバレイを表示", "controlify.radial.debug.overlay_chart": "チャートオーバーレイを表示", "controlify.radial.debug.hide_overlay": "オーバレイ非表示", - "controlify.radial.debug.reload_chunks": "チャンクを再読み込み", - "controlify.radial.debug.chunk_borders": "チャンク境界の切り替え", - "controlify.radial.debug.advanced_tooltips": "高度なツールチップの切り替え", - "controlify.radial.debug.entity_hitboxes": "エンティティのヒットボックスの切り替え", - "controlify.radial.debug.reload_packs": "リソースパックを再読み込み", - "controlify.radial.debug.clear_chat": "チャットをクリア", - "controlify.radial.debug.profile": "プロファイリングの開始/停止", + "controlify.action.controlify.debug_reload_chunks": "チャンクを再読み込み", + "controlify.action.controlify.debug_toggle_chunk_borders": "チャンク境界の切り替え", + "controlify.action.controlify.debug_toggle_advanced_tooltips": "高度なツールチップの切り替え", + "controlify.action.controlify.debug_toggle_entity_hitboxes": "エンティティのヒットボックスの切り替え", + "controlify.action.controlify.debug_reload_resource_packs": "リソースパックを再読み込み", + "controlify.action.controlify.debug_clear_chat": "チャットをクリア", + "controlify.action.controlify.debug_start_stop_profiling": "プロファイリングの開始/停止", "controlify.controller_submission.title": "不明なコントローラーを提出", "controlify.controller_submission.message": "今後のアップデートで追加するには、Conrolifyのデータベースにコントローラー情報の一部を送信してください。\n\nControlify が以下の情報を送信します:\n- コントローラーのベンダーと製品ID\n- コントローラーのGUID\n- コントローラーの名前(下記のボックス内)\n- Controlify のバージョン\n\nこれは完全に匿名であり、個人情報またはアカウント情報のいずれも保存しません", "controlify.controller_submission.operational_checkbox": "あなたのコントローラーはConrolifyで動作しますか?", diff --git a/src/main/resources/assets/controlify/lang/ms_my.json b/src/main/resources/assets/controlify/lang/ms_my.json index e7118256a..19b092d35 100644 --- a/src/main/resources/assets/controlify/lang/ms_my.json +++ b/src/main/resources/assets/controlify/lang/ms_my.json @@ -214,91 +214,91 @@ "controlify.bluetooth_warning.title": "Pengawal disambungkan melalui Bluetooth", "controlify.bluetooth_warning.desc": "Sesetengah pengawal mungkin akan kehilangan ciri apabila disambungkan melalui Bluetooth. Contohnya, haptik HD pada DualSense. Jika anda rasa anda kehilangan ciri, cuba gunakan USB sebelum melaporkan.", "controlify.bluetooth_warning.dont_show": "Jangan tunjukkan lagi", - "controlify.binding.controlify.walk_forward": "Jalan ke Hadapan", - "controlify.binding.controlify.walk_backward": "Jalan ke Belakang", - "controlify.binding.controlify.strafe_left": "Jalan ke Kiri", - "controlify.binding.controlify.strafe_right": "Jalan ke Kanan", - "controlify.binding.controlify.look_up": "Lihat ke Atas", - "controlify.binding.controlify.look_down": "Lihat ke Bawah", - "controlify.binding.controlify.look_left": "Lihat ke Kiri", - "controlify.binding.controlify.look_right": "Lihat ke Kanan", - "controlify.binding.controlify.gyro_button": "Butang Giro", - "controlify.binding.controlify.jump": "Lompat", - "controlify.binding.controlify.sneak": "Selinap", - "controlify.binding.controlify.attack": "Serang", - "controlify.binding.controlify.use": "Guna", - "controlify.binding.controlify.sprint": "Lari", - "controlify.binding.controlify.next_slot": "Slot Palang Utama Seterusnya", - "controlify.binding.controlify.prev_slot": "Slot Palang Utama Sebelumnya", - "controlify.binding.controlify.hotbar_load_radial": "Muat Palang Utama Kreatif", - "controlify.binding.controlify.hotbar_save_radial": "Simpan Palang Utama Kreatif", - "controlify.binding.controlify.hotbar_item_select_radial": "Jejari Pemilihan Slot Palang Utama", - "controlify.binding.controlify.pause": "Jeda Permainan", - "controlify.binding.controlify.inventory": "Buka Inventori", - "controlify.binding.controlify.change_perspective": "Ubah Perspektif", - "controlify.binding.controlify.swap_hands": "Tukar Tangan", - "controlify.binding.controlify.open_chat": "Buka Perbualan", - "controlify.binding.controlify.gui_press": "GUI Tekan", - "controlify.binding.controlify.gui_back": "GUI Kembali", - "controlify.binding.controlify.gui_next_tab": "Tab GUI Seterusnya", - "controlify.binding.controlify.gui_prev_tab": "Tab GUI Sebelumnya", - "controlify.binding.controlify.gui_abstract_action_1": "Aksi Abstrak GUI 1", - "controlify.binding.controlify.gui_abstract_action_1.desc": "Ikatan generik yang mungkin melakukan perkara yang berbeza bergantung pada skrin semasa.", - "controlify.binding.controlify.gui_abstract_action_2": "Aksi Abstrak GUI 2", - "controlify.binding.controlify.gui_abstract_action_2.desc": "Ikatan generik yang mungkin melakukan perkara yang berbeza bergantung pada skrin semasa.", - "controlify.binding.controlify.gui_abstract_action_3": "Aksi Abstrak GUI 3", - "controlify.binding.controlify.gui_abstract_action_3.desc": "Ikatan generik yang mungkin melakukan perkara yang berbeza bergantung pada skrin semasa.", - "controlify.binding.controlify.inv_select": "Pilih Item", - "controlify.binding.controlify.inv_quick_move": "Pindah Pantas", - "controlify.binding.controlify.inv_take_half": "Ambil Separuh", - "controlify.binding.controlify.drop": "Lepaskan Item", - "controlify.binding.controlify.drop_stack": "Jatuhkan Timbunan", - "controlify.binding.controlify.drop_inventory": "Jatuhkan Item (Dalam Bekas)", - "controlify.binding.controlify.pick_block": "Ambil Blok", - "controlify.binding.controlify.pick_block_nbt": "Ambil Blok (beserta NBT)", - "controlify.binding.controlify.take_screenshot": "Ambil Tangkap Layar", - "controlify.binding.controlify.debug_radial": "Menu Jejari Nyahpepijat", - "controlify.binding.controlify.toggle_debug_menu": "Togol Menu Nyahpepijat (F3)", - "controlify.binding.controlify.toggle_debug_menu_fps": "Togol Menu Nyahpepijat (F3) dengan BPS", - "controlify.binding.controlify.toggle_debug_menu_prof": "Togol Menu Nyahpepijat (F3) dengan Pemprofil", - "controlify.binding.controlify.toggle_debug_menu_net": "Togol Menu Nyahpepijat (F3) dengan Graf Rangkaian", - "controlify.binding.controlify.toggle_debug_menu_chart": "Togol Menu Nyahpepijat (F3) dengan Carta", - "controlify.binding.controlify.toggle_hud_visibility": "Togol Keterlihatan HUD", - "controlify.binding.controlify.show_player_list": "Tunjukkan Senarai Pemain", - "controlify.binding.controlify.radial_menu": "Menu Jejari", - "controlify.binding.controlify.radial_axis_up": "Menu Jejari Atas", - "controlify.binding.controlify.radial_axis_down": "Menu Jejari Bawah", - "controlify.binding.controlify.radial_axis_left": "Menu Jejari Kiri", - "controlify.binding.controlify.radial_axis_right": "Menu Jejari Kanan", - "controlify.binding.controlify.game_mode_switcher": "Penukar Mod Permainan", - "controlify.binding.controlify.vmouse_move_up": "Tetikus Maya Gerak ke Atas", - "controlify.binding.controlify.vmouse_move_down": "Tetikus Maya Gerak ke Bawah", - "controlify.binding.controlify.vmouse_move_left": "Tetikus Maya Gerak ke Kiri", - "controlify.binding.controlify.vmouse_move_right": "Tetikus Maya Gerak ke Kanan", - "controlify.binding.controlify.vmouse_lclick": "Tetikus Maya Klik Kiri", - "controlify.binding.controlify.vmouse_rclick": "Tetikus Maya Klik Kanan", - "controlify.binding.controlify.vmouse_shift_click": "Tetikus Maya Klik + Shift", - "controlify.binding.controlify.vmouse_snap_up": "Sentapan Tetikus Atas", - "controlify.binding.controlify.vmouse_snap_down": "Sentapan Tetikus Bawah", - "controlify.binding.controlify.vmouse_snap_left": "Sentapan Tetikus Kiri", - "controlify.binding.controlify.vmouse_snap_right": "Sentapan Tetikus Kanan", - "controlify.binding.controlify.vmouse_scroll_up": "Tetikus Maya Tatal ke Atas", - "controlify.binding.controlify.vmouse_scroll_down": "Tetikus Maya Tatal ke Bawah", - "controlify.binding.controlify.vmouse_escape": "Tetikus Vertikal Kekunci Escape", - "controlify.binding.controlify.vmouse_shift": "Tetikus Maya Kekunci Shift", - "controlify.binding.controlify.vmouse_page_next": "Tetikus Maya Halaman Seterusnya", - "controlify.binding.controlify.vmouse_page_prev": "Tetikus Maya Halaman Sebelumnya", - "controlify.binding.controlify.vmouse_page_down": "Tetikus Maya Halaman Bawah", - "controlify.binding.controlify.vmouse_page_up": "Tetikus Maya Halaman Atas", - "controlify.binding.controlify.vmouse_toggle": "Togol Tetikus Maya", - "controlify.binding.controlify.gui_navi_down": "Navigasi GUI Bawah", - "controlify.binding.controlify.gui_navi_up": "Navigasi GUI Atas", - "controlify.binding.controlify.gui_navi_left": "Navigasi GUI Kiri", - "controlify.binding.controlify.gui_navi_right": "Navigasi GUI Kanan", - "controlify.binding.controlify.clear_binding": "Bersihkan Kekunci Pintas", - "controlify.binding.voicechat.ptt_hold": "Tekan Untuk Cakap (Tahan)", - "controlify.binding.voicechat.ptt_toggle": "Tekan Untuk Cakap (Togol)", - "controlify.binding.voicechat.mute_microphone": "Redam Mikrofon", + "controlify.action.controlify.walk_forward": "Jalan ke Hadapan", + "controlify.action.controlify.walk_backward": "Jalan ke Belakang", + "controlify.action.controlify.strafe_left": "Jalan ke Kiri", + "controlify.action.controlify.strafe_right": "Jalan ke Kanan", + "controlify.action.controlify.look_up": "Lihat ke Atas", + "controlify.action.controlify.look_down": "Lihat ke Bawah", + "controlify.action.controlify.look_left": "Lihat ke Kiri", + "controlify.action.controlify.look_right": "Lihat ke Kanan", + "controlify.action.controlify.gyro_button": "Butang Giro", + "controlify.action.controlify.jump": "Lompat", + "controlify.action.controlify.sneak": "Selinap", + "controlify.action.controlify.attack": "Serang", + "controlify.action.controlify.use": "Guna", + "controlify.action.controlify.sprint": "Lari", + "controlify.action.controlify.next_slot": "Slot Palang Utama Seterusnya", + "controlify.action.controlify.prev_slot": "Slot Palang Utama Sebelumnya", + "controlify.action.controlify.hotbar_load_radial": "Muat Palang Utama Kreatif", + "controlify.action.controlify.hotbar_save_radial": "Simpan Palang Utama Kreatif", + "controlify.action.controlify.hotbar_item_select_radial": "Jejari Pemilihan Slot Palang Utama", + "controlify.action.controlify.pause": "Jeda Permainan", + "controlify.action.controlify.inventory": "Buka Inventori", + "controlify.action.controlify.change_perspective": "Ubah Perspektif", + "controlify.action.controlify.swap_hands": "Tukar Tangan", + "controlify.action.controlify.open_chat": "Buka Perbualan", + "controlify.action.controlify.gui_press": "GUI Tekan", + "controlify.action.controlify.gui_back": "GUI Kembali", + "controlify.action.controlify.gui_next_tab": "Tab GUI Seterusnya", + "controlify.action.controlify.gui_prev_tab": "Tab GUI Sebelumnya", + "controlify.action.controlify.gui_abstract_action_1": "Aksi Abstrak GUI 1", + "controlify.action.controlify.gui_abstract_action_1.desc": "Ikatan generik yang mungkin melakukan perkara yang berbeza bergantung pada skrin semasa.", + "controlify.action.controlify.gui_abstract_action_2": "Aksi Abstrak GUI 2", + "controlify.action.controlify.gui_abstract_action_2.desc": "Ikatan generik yang mungkin melakukan perkara yang berbeza bergantung pada skrin semasa.", + "controlify.action.controlify.gui_abstract_action_3": "Aksi Abstrak GUI 3", + "controlify.action.controlify.gui_abstract_action_3.desc": "Ikatan generik yang mungkin melakukan perkara yang berbeza bergantung pada skrin semasa.", + "controlify.action.controlify.inv_select": "Pilih Item", + "controlify.action.controlify.inv_quick_move": "Pindah Pantas", + "controlify.action.controlify.inv_take_half": "Ambil Separuh", + "controlify.action.controlify.drop": "Lepaskan Item", + "controlify.action.controlify.drop_stack": "Jatuhkan Timbunan", + "controlify.action.controlify.drop_inventory": "Jatuhkan Item (Dalam Bekas)", + "controlify.action.controlify.pick_block": "Ambil Blok", + "controlify.action.controlify.pick_block_nbt": "Ambil Blok (beserta NBT)", + "controlify.action.controlify.take_screenshot": "Ambil Tangkap Layar", + "controlify.action.controlify.debug_radial": "Menu Jejari Nyahpepijat", + "controlify.action.controlify.toggle_debug_menu": "Togol Menu Nyahpepijat (F3)", + "controlify.action.controlify.toggle_debug_menu_fps": "Togol Menu Nyahpepijat (F3) dengan BPS", + "controlify.action.controlify.toggle_debug_menu_prof": "Togol Menu Nyahpepijat (F3) dengan Pemprofil", + "controlify.action.controlify.toggle_debug_menu_net": "Togol Menu Nyahpepijat (F3) dengan Graf Rangkaian", + "controlify.action.controlify.toggle_debug_menu_chart": "Togol Menu Nyahpepijat (F3) dengan Carta", + "controlify.action.controlify.toggle_hud_visibility": "Togol Keterlihatan HUD", + "controlify.action.controlify.show_player_list": "Tunjukkan Senarai Pemain", + "controlify.action.controlify.radial_menu": "Menu Jejari", + "controlify.action.controlify.radial_axis_up": "Menu Jejari Atas", + "controlify.action.controlify.radial_axis_down": "Menu Jejari Bawah", + "controlify.action.controlify.radial_axis_left": "Menu Jejari Kiri", + "controlify.action.controlify.radial_axis_right": "Menu Jejari Kanan", + "controlify.action.controlify.game_mode_switcher": "Penukar Mod Permainan", + "controlify.action.controlify.vmouse_move_up": "Tetikus Maya Gerak ke Atas", + "controlify.action.controlify.vmouse_move_down": "Tetikus Maya Gerak ke Bawah", + "controlify.action.controlify.vmouse_move_left": "Tetikus Maya Gerak ke Kiri", + "controlify.action.controlify.vmouse_move_right": "Tetikus Maya Gerak ke Kanan", + "controlify.action.controlify.vmouse_lclick": "Tetikus Maya Klik Kiri", + "controlify.action.controlify.vmouse_rclick": "Tetikus Maya Klik Kanan", + "controlify.action.controlify.vmouse_shift_click": "Tetikus Maya Klik + Shift", + "controlify.action.controlify.vmouse_snap_up": "Sentapan Tetikus Atas", + "controlify.action.controlify.vmouse_snap_down": "Sentapan Tetikus Bawah", + "controlify.action.controlify.vmouse_snap_left": "Sentapan Tetikus Kiri", + "controlify.action.controlify.vmouse_snap_right": "Sentapan Tetikus Kanan", + "controlify.action.controlify.vmouse_scroll_up": "Tetikus Maya Tatal ke Atas", + "controlify.action.controlify.vmouse_scroll_down": "Tetikus Maya Tatal ke Bawah", + "controlify.action.controlify.vmouse_escape": "Tetikus Vertikal Kekunci Escape", + "controlify.action.controlify.vmouse_shift": "Tetikus Maya Kekunci Shift", + "controlify.action.controlify.vmouse_page_next": "Tetikus Maya Halaman Seterusnya", + "controlify.action.controlify.vmouse_page_prev": "Tetikus Maya Halaman Sebelumnya", + "controlify.action.controlify.vmouse_page_down": "Tetikus Maya Halaman Bawah", + "controlify.action.controlify.vmouse_page_up": "Tetikus Maya Halaman Atas", + "controlify.action.controlify.vmouse_toggle": "Togol Tetikus Maya", + "controlify.action.controlify.gui_navi_down": "Navigasi GUI Bawah", + "controlify.action.controlify.gui_navi_up": "Navigasi GUI Atas", + "controlify.action.controlify.gui_navi_left": "Navigasi GUI Kiri", + "controlify.action.controlify.gui_navi_right": "Navigasi GUI Kanan", + "controlify.action.controlify.clear_binding": "Bersihkan Kekunci Pintas", + "controlify.action.voicechat.ptt_hold": "Tekan Untuk Cakap (Tahan)", + "controlify.action.voicechat.ptt_toggle": "Tekan Untuk Cakap (Togol)", + "controlify.action.voicechat.mute_microphone": "Redam Mikrofon", "controlify.input.controlify.button/south": "Muka Selatan", "controlify.input.controlify.button/east": "Muka Timur", "controlify.input.controlify.button/west": "Muka Barat", @@ -428,13 +428,13 @@ "controlify.radial.debug.overlay_prof": "Tunjukkan Tindanan dengan Pemprofil", "controlify.radial.debug.overlay_chart": "Tunjukkan Tindanan dengan Carta", "controlify.radial.debug.hide_overlay": "Sembunyikan Tindanan", - "controlify.radial.debug.reload_chunks": "Muat Semula Cebisan", - "controlify.radial.debug.chunk_borders": "Togol Sempadan Cebisan", - "controlify.radial.debug.advanced_tooltips": "Togol Petua Alat Lanjutan", - "controlify.radial.debug.entity_hitboxes": "Togol Hitbox Entiti", - "controlify.radial.debug.reload_packs": "Muat Semula Pek Sumber", - "controlify.radial.debug.clear_chat": "Bersihkan Perbualan", - "controlify.radial.debug.profile": "Mulakan/Berhentikan Pemprofilan", + "controlify.action.controlify.debug_reload_chunks": "Muat Semula Cebisan", + "controlify.action.controlify.debug_toggle_chunk_borders": "Togol Sempadan Cebisan", + "controlify.action.controlify.debug_toggle_advanced_tooltips": "Togol Petua Alat Lanjutan", + "controlify.action.controlify.debug_toggle_entity_hitboxes": "Togol Hitbox Entiti", + "controlify.action.controlify.debug_reload_resource_packs": "Muat Semula Pek Sumber", + "controlify.action.controlify.debug_clear_chat": "Bersihkan Perbualan", + "controlify.action.controlify.debug_start_stop_profiling": "Mulakan/Berhentikan Pemprofilan", "controlify.controller_submission.title": "Penyerahan Maklumat Pengawal Tidak Diketahui", "controlify.controller_submission.message": "Sila serahkan beberapa maklumat pengawal anda ke pangkalan data Controlify untuk menambahkannya dalam kemas kini masa hadapan.\n\nControlify menghantar maklumat berikut:\n- ID vendor & produk pengawal anda\n- GUID pengawal anda\n- Nama pengawal anda (dalam kotak di bawah)\n- Versi Controlify yang sedang anda gunakan\n\nIni adalah awanama sepenuhnya dan tidak menyimpan sebarang maklumat peribadi atau akaun anda.", "controlify.controller_submission.operational_checkbox": "Adakah pengawal anda berfungsi dengan Controlify?", diff --git a/src/main/resources/assets/controlify/lang/pl_pl.json b/src/main/resources/assets/controlify/lang/pl_pl.json index b0033d7a8..95e9a8092 100644 --- a/src/main/resources/assets/controlify/lang/pl_pl.json +++ b/src/main/resources/assets/controlify/lang/pl_pl.json @@ -213,85 +213,85 @@ "controlify.bluetooth_warning.title": "Kontroler podłączony przez Bluetooth", "controlify.bluetooth_warning.desc": "Niektóre kontrolery mogą nie mieć pełnych funkcji przy połączeniu przez Bluetooth, np. haptyki HD w kontrolerze DualSense. Jeśli brakuje Ci funkcji, spróbuj połączyć się przez USB przed zgłoszeniem.", "controlify.bluetooth_warning.dont_show": "Nie pokazuj ponownie", - "controlify.binding.controlify.walk_forward": "Idź do przodu", - "controlify.binding.controlify.walk_backward": "Idź do tyłu", - "controlify.binding.controlify.strafe_left": "Idź w lewo", - "controlify.binding.controlify.strafe_right": "Idź w prawo", - "controlify.binding.controlify.look_up": "Spójrz w górę", - "controlify.binding.controlify.look_down": "Spójrz w dół", - "controlify.binding.controlify.look_left": "Spójrz w lewo", - "controlify.binding.controlify.look_right": "Spójrz w prawo", - "controlify.binding.controlify.gyro_button": "Żyroskop", - "controlify.binding.controlify.jump": "Skok", - "controlify.binding.controlify.sneak": "Skradanie się", - "controlify.binding.controlify.attack": "Atak", - "controlify.binding.controlify.use": "Użyj", - "controlify.binding.controlify.sprint": "Bieg", - "controlify.binding.controlify.next_slot": "Następne pole paska szybkiego wyboru", - "controlify.binding.controlify.prev_slot": "Poprzednie pole paska szybkiego wyboru", - "controlify.binding.controlify.hotbar_load_radial": "Wczytaj pasek szybkiego wyboru trybu kreatywnego", - "controlify.binding.controlify.hotbar_save_radial": "Zapisz pasek szybkiego wyboru trybu kreatywnego", - "controlify.binding.controlify.hotbar_item_select_radial": "Wybór pola paska szybkiego wyboru", - "controlify.binding.controlify.pause": "Wstrzymaj grę", - "controlify.binding.controlify.inventory": "Otwórz ekwipunek", - "controlify.binding.controlify.change_perspective": "Zmień perspektywę", - "controlify.binding.controlify.swap_hands": "Zamień ręce", - "controlify.binding.controlify.open_chat": "Otwórz czat", - "controlify.binding.controlify.gui_press": "Naciśnij", - "controlify.binding.controlify.gui_back": "Cofnij", - "controlify.binding.controlify.gui_next_tab": "Następna zakładka\n", - "controlify.binding.controlify.gui_prev_tab": "Poprzednia zakładka", - "controlify.binding.controlify.gui_abstract_action_1": "Abstrakcyjna akcja 1", - "controlify.binding.controlify.gui_abstract_action_1.desc": "Uniwersalne przypisanie, które może wykonywać różne czynności w zależności od aktualnego ekranu.", - "controlify.binding.controlify.gui_abstract_action_2": "Abstrakcyjna akcja 2", - "controlify.binding.controlify.gui_abstract_action_2.desc": "Uniwersalne przypisanie, które może wykonywać różne czynności w zależności od aktualnego ekranu.", - "controlify.binding.controlify.gui_abstract_action_3": "Abstrakcyjna akcja 3", - "controlify.binding.controlify.gui_abstract_action_3.desc": "Uniwersalne przypisanie, które może wykonywać różne czynności w zależności od aktualnego ekranu.", - "controlify.binding.controlify.inv_select": "Wybierz przedmiot", - "controlify.binding.controlify.inv_quick_move": "Szybkie przenoszenie", - "controlify.binding.controlify.inv_take_half": "Weź połowę", - "controlify.binding.controlify.drop": "Upuść przedmiot", - "controlify.binding.controlify.drop_stack": "Upuść stos", - "controlify.binding.controlify.drop_inventory": "Upuść przedmiot (w pojemniku)", - "controlify.binding.controlify.pick_block": "Wybierz blok", - "controlify.binding.controlify.pick_block_nbt": "Wybierz blok (z NBT)", - "controlify.binding.controlify.take_screenshot": "Zrób zrzut ekranu", - "controlify.binding.controlify.debug_radial": "Menu radialne debugowania", - "controlify.binding.controlify.toggle_debug_menu": "Przełącz menu debugowania (F3)", - "controlify.binding.controlify.toggle_debug_menu_fps": "Przełącz menu debugowania (F3) z FPS", - "controlify.binding.controlify.toggle_debug_menu_prof": "Przełącz menu debugowania (F3) z profilerem", - "controlify.binding.controlify.toggle_debug_menu_net": "Przełącz menu debugowania (F3) z wykresem sieci", - "controlify.binding.controlify.toggle_debug_menu_chart": "Przełącz menu debugowania (F3) z wykresami", - "controlify.binding.controlify.toggle_hud_visibility": "Przełącz widoczność HUD", - "controlify.binding.controlify.show_player_list": "Pokaż listę graczy", - "controlify.binding.controlify.radial_menu": "Menu radialne", - "controlify.binding.controlify.radial_axis_up": "Menu radialne w górę", - "controlify.binding.controlify.radial_axis_down": "Menu radialne w dół", - "controlify.binding.controlify.radial_axis_left": "Menu radialne w lewo", - "controlify.binding.controlify.radial_axis_right": "Menu radialne w prawo", - "controlify.binding.controlify.game_mode_switcher": "Przełącznik trybu gry", - "controlify.binding.controlify.vmouse_move_up": "Ruch kursora w górę", - "controlify.binding.controlify.vmouse_move_down": "Ruch kursora w dół", - "controlify.binding.controlify.vmouse_move_left": "Ruch kursora w lewo", - "controlify.binding.controlify.vmouse_move_right": "Ruch kursora w prawo", - "controlify.binding.controlify.vmouse_lclick": "Lewy przycisk myszki", - "controlify.binding.controlify.vmouse_rclick": "Prawy przycisk myszki", - "controlify.binding.controlify.vmouse_shift_click": "Shift + Lewy przycisk myszki", - "controlify.binding.controlify.vmouse_snap_up": "Ruch myszki w górę", - "controlify.binding.controlify.vmouse_snap_down": "Ruch myszki w dół", - "controlify.binding.controlify.vmouse_snap_left": "Ruch myszki w lewo", - "controlify.binding.controlify.vmouse_snap_right": "Ruch myszki w prawo", - "controlify.binding.controlify.vmouse_scroll_up": "Przewinięcie w górę", - "controlify.binding.controlify.vmouse_scroll_down": "Przewinięcie w dół", - "controlify.binding.controlify.vmouse_toggle": "Przełącz wirtualną myszkę", - "controlify.binding.controlify.gui_navi_down": "Nawigacja w dół (GUI)", - "controlify.binding.controlify.gui_navi_up": "Nawigacja w górę (GUI)", - "controlify.binding.controlify.gui_navi_left": "Nawigacja w lewo (GUI)", - "controlify.binding.controlify.gui_navi_right": "Nawigacja w prawo (GUI)", - "controlify.binding.controlify.clear_binding": "Wyczyść przypisanie", - "controlify.binding.voicechat.ptt_hold": "Naciśnij i Mów (Przytrzymaj)", - "controlify.binding.voicechat.ptt_toggle": "Naciśnij i Mów (Przełączj)", - "controlify.binding.voicechat.mute_microphone": "Wycisz mikrofon", + "controlify.action.controlify.walk_forward": "Idź do przodu", + "controlify.action.controlify.walk_backward": "Idź do tyłu", + "controlify.action.controlify.strafe_left": "Idź w lewo", + "controlify.action.controlify.strafe_right": "Idź w prawo", + "controlify.action.controlify.look_up": "Spójrz w górę", + "controlify.action.controlify.look_down": "Spójrz w dół", + "controlify.action.controlify.look_left": "Spójrz w lewo", + "controlify.action.controlify.look_right": "Spójrz w prawo", + "controlify.action.controlify.gyro_button": "Żyroskop", + "controlify.action.controlify.jump": "Skok", + "controlify.action.controlify.sneak": "Skradanie się", + "controlify.action.controlify.attack": "Atak", + "controlify.action.controlify.use": "Użyj", + "controlify.action.controlify.sprint": "Bieg", + "controlify.action.controlify.next_slot": "Następne pole paska szybkiego wyboru", + "controlify.action.controlify.prev_slot": "Poprzednie pole paska szybkiego wyboru", + "controlify.action.controlify.hotbar_load_radial": "Wczytaj pasek szybkiego wyboru trybu kreatywnego", + "controlify.action.controlify.hotbar_save_radial": "Zapisz pasek szybkiego wyboru trybu kreatywnego", + "controlify.action.controlify.hotbar_item_select_radial": "Wybór pola paska szybkiego wyboru", + "controlify.action.controlify.pause": "Wstrzymaj grę", + "controlify.action.controlify.inventory": "Otwórz ekwipunek", + "controlify.action.controlify.change_perspective": "Zmień perspektywę", + "controlify.action.controlify.swap_hands": "Zamień ręce", + "controlify.action.controlify.open_chat": "Otwórz czat", + "controlify.action.controlify.gui_press": "Naciśnij", + "controlify.action.controlify.gui_back": "Cofnij", + "controlify.action.controlify.gui_next_tab": "Następna zakładka\n", + "controlify.action.controlify.gui_prev_tab": "Poprzednia zakładka", + "controlify.action.controlify.gui_abstract_action_1": "Abstrakcyjna akcja 1", + "controlify.action.controlify.gui_abstract_action_1.desc": "Uniwersalne przypisanie, które może wykonywać różne czynności w zależności od aktualnego ekranu.", + "controlify.action.controlify.gui_abstract_action_2": "Abstrakcyjna akcja 2", + "controlify.action.controlify.gui_abstract_action_2.desc": "Uniwersalne przypisanie, które może wykonywać różne czynności w zależności od aktualnego ekranu.", + "controlify.action.controlify.gui_abstract_action_3": "Abstrakcyjna akcja 3", + "controlify.action.controlify.gui_abstract_action_3.desc": "Uniwersalne przypisanie, które może wykonywać różne czynności w zależności od aktualnego ekranu.", + "controlify.action.controlify.inv_select": "Wybierz przedmiot", + "controlify.action.controlify.inv_quick_move": "Szybkie przenoszenie", + "controlify.action.controlify.inv_take_half": "Weź połowę", + "controlify.action.controlify.drop": "Upuść przedmiot", + "controlify.action.controlify.drop_stack": "Upuść stos", + "controlify.action.controlify.drop_inventory": "Upuść przedmiot (w pojemniku)", + "controlify.action.controlify.pick_block": "Wybierz blok", + "controlify.action.controlify.pick_block_nbt": "Wybierz blok (z NBT)", + "controlify.action.controlify.take_screenshot": "Zrób zrzut ekranu", + "controlify.action.controlify.debug_radial": "Menu radialne debugowania", + "controlify.action.controlify.toggle_debug_menu": "Przełącz menu debugowania (F3)", + "controlify.action.controlify.toggle_debug_menu_fps": "Przełącz menu debugowania (F3) z FPS", + "controlify.action.controlify.toggle_debug_menu_prof": "Przełącz menu debugowania (F3) z profilerem", + "controlify.action.controlify.toggle_debug_menu_net": "Przełącz menu debugowania (F3) z wykresem sieci", + "controlify.action.controlify.toggle_debug_menu_chart": "Przełącz menu debugowania (F3) z wykresami", + "controlify.action.controlify.toggle_hud_visibility": "Przełącz widoczność HUD", + "controlify.action.controlify.show_player_list": "Pokaż listę graczy", + "controlify.action.controlify.radial_menu": "Menu radialne", + "controlify.action.controlify.radial_axis_up": "Menu radialne w górę", + "controlify.action.controlify.radial_axis_down": "Menu radialne w dół", + "controlify.action.controlify.radial_axis_left": "Menu radialne w lewo", + "controlify.action.controlify.radial_axis_right": "Menu radialne w prawo", + "controlify.action.controlify.game_mode_switcher": "Przełącznik trybu gry", + "controlify.action.controlify.vmouse_move_up": "Ruch kursora w górę", + "controlify.action.controlify.vmouse_move_down": "Ruch kursora w dół", + "controlify.action.controlify.vmouse_move_left": "Ruch kursora w lewo", + "controlify.action.controlify.vmouse_move_right": "Ruch kursora w prawo", + "controlify.action.controlify.vmouse_lclick": "Lewy przycisk myszki", + "controlify.action.controlify.vmouse_rclick": "Prawy przycisk myszki", + "controlify.action.controlify.vmouse_shift_click": "Shift + Lewy przycisk myszki", + "controlify.action.controlify.vmouse_snap_up": "Ruch myszki w górę", + "controlify.action.controlify.vmouse_snap_down": "Ruch myszki w dół", + "controlify.action.controlify.vmouse_snap_left": "Ruch myszki w lewo", + "controlify.action.controlify.vmouse_snap_right": "Ruch myszki w prawo", + "controlify.action.controlify.vmouse_scroll_up": "Przewinięcie w górę", + "controlify.action.controlify.vmouse_scroll_down": "Przewinięcie w dół", + "controlify.action.controlify.vmouse_toggle": "Przełącz wirtualną myszkę", + "controlify.action.controlify.gui_navi_down": "Nawigacja w dół (GUI)", + "controlify.action.controlify.gui_navi_up": "Nawigacja w górę (GUI)", + "controlify.action.controlify.gui_navi_left": "Nawigacja w lewo (GUI)", + "controlify.action.controlify.gui_navi_right": "Nawigacja w prawo (GUI)", + "controlify.action.controlify.clear_binding": "Wyczyść przypisanie", + "controlify.action.voicechat.ptt_hold": "Naciśnij i Mów (Przytrzymaj)", + "controlify.action.voicechat.ptt_toggle": "Naciśnij i Mów (Przełączj)", + "controlify.action.voicechat.mute_microphone": "Wycisz mikrofon", "controlify.input.controlify.button/left_shoulder": "Lewe ramię", "controlify.input.controlify.button/right_shoulder": "Prawe ramię", "controlify.input.controlify.button/left_stick": "Lewy drążek", @@ -387,13 +387,13 @@ "controlify.radial.debug.overlay_prof": "Pokaż nakładkę z profilerem", "controlify.radial.debug.overlay_chart": "Pokaż nakładkę z wykresami", "controlify.radial.debug.hide_overlay": "Ukryj nakładkę", - "controlify.radial.debug.reload_chunks": "Przeładuj chunki", - "controlify.radial.debug.chunk_borders": "Przełącz granice chunków", - "controlify.radial.debug.advanced_tooltips": "Przełącz zaawansowane podpowiedzi", - "controlify.radial.debug.entity_hitboxes": "Przełącz hitboxy obiektów", - "controlify.radial.debug.reload_packs": "Przeładuj paczki zasobów", - "controlify.radial.debug.clear_chat": "Wyczyść czat", - "controlify.radial.debug.profile": "Rozpocznij/Zakończ profilowanie", + "controlify.action.controlify.debug_reload_chunks": "Przeładuj chunki", + "controlify.action.controlify.debug_toggle_chunk_borders": "Przełącz granice chunków", + "controlify.action.controlify.debug_toggle_advanced_tooltips": "Przełącz zaawansowane podpowiedzi", + "controlify.action.controlify.debug_toggle_entity_hitboxes": "Przełącz hitboxy obiektów", + "controlify.action.controlify.debug_reload_resource_packs": "Przeładuj paczki zasobów", + "controlify.action.controlify.debug_clear_chat": "Wyczyść czat", + "controlify.action.controlify.debug_start_stop_profiling": "Rozpocznij/Zakończ profilowanie", "controlify.controller_submission.title": "Nieznane zgłoszenie kontrolera", "controlify.controller_submission.message": "Prosimy o przesłanie niektórych informacji o kontrolerze do bazy danych Controlify, aby mogły zostać dodane w przyszłej aktualizacji.\n\nControlify wysyła następujące informacje:\n- ID producenta i produktu Twojego kontrolera\n- GUID Twojego kontrolera\n- Nazwa Twojego kontrolera (w polu poniżej)\n- Wersja Controlify, z której obecnie korzystasz\n\nJest to całkowicie anonimowe i nie przechowuje żadnych danych osobistych ani danych konta.", "controlify.controller_submission.operational_checkbox": "Czy Twój kontroler działa z Controlify?", diff --git a/src/main/resources/assets/controlify/lang/pt_br.json b/src/main/resources/assets/controlify/lang/pt_br.json index d5f241878..0c3c951a8 100644 --- a/src/main/resources/assets/controlify/lang/pt_br.json +++ b/src/main/resources/assets/controlify/lang/pt_br.json @@ -182,37 +182,37 @@ "controlify.controller_theme.default": "Padrão", "controlify.bluetooth_warning.title": "Controle conectado por Bluetooth", "controlify.bluetooth_warning.dont_show": "Não exibir novamente", - "controlify.binding.controlify.walk_forward": "Andar para frente", - "controlify.binding.controlify.walk_backward": "Andar para trás", - "controlify.binding.controlify.strafe_left": "Andar para a esquerda", - "controlify.binding.controlify.strafe_right": "Andar para a direita", - "controlify.binding.controlify.look_up": "Olhar para cima", - "controlify.binding.controlify.look_down": "Olhar para baixo", - "controlify.binding.controlify.look_left": "Olhar para a esquerda", - "controlify.binding.controlify.look_right": "Olhar para a direita", - "controlify.binding.controlify.jump": "Pular", - "controlify.binding.controlify.sneak": "Agachar", - "controlify.binding.controlify.attack": "Atacar", - "controlify.binding.controlify.use": "Usar", - "controlify.binding.controlify.sprint": "Correr", - "controlify.binding.controlify.next_slot": "Próximo espaço da barra rápida", - "controlify.binding.controlify.prev_slot": "Espaço anterior da barra rápida", - "controlify.binding.controlify.pause": "Pausar jogo", - "controlify.binding.controlify.inventory": "Abrir Inventário", - "controlify.binding.controlify.change_perspective": "Alterar perspectiva", - "controlify.binding.controlify.swap_hands": "Trocar de mão", - "controlify.binding.controlify.open_chat": "Abrir chat", - "controlify.binding.controlify.inv_select": "Selecionar item", - "controlify.binding.controlify.inv_take_half": "Pegar metade", - "controlify.binding.controlify.drop": "Largar item", - "controlify.binding.controlify.drop_stack": "Largar pilha", - "controlify.binding.controlify.drop_inventory": "Largar item (em contêineres)", - "controlify.binding.controlify.pick_block": "Escolher bloco", - "controlify.binding.controlify.pick_block_nbt": "Escolher bloco (com NBT)", - "controlify.binding.controlify.take_screenshot": "Capturar tela", - "controlify.binding.controlify.show_player_list": "Exibir lista de jogadores", - "controlify.binding.controlify.radial_menu": "Menu Radial", - "controlify.binding.voicechat.mute_microphone": "Silenciar microfone", + "controlify.action.controlify.walk_forward": "Andar para frente", + "controlify.action.controlify.walk_backward": "Andar para trás", + "controlify.action.controlify.strafe_left": "Andar para a esquerda", + "controlify.action.controlify.strafe_right": "Andar para a direita", + "controlify.action.controlify.look_up": "Olhar para cima", + "controlify.action.controlify.look_down": "Olhar para baixo", + "controlify.action.controlify.look_left": "Olhar para a esquerda", + "controlify.action.controlify.look_right": "Olhar para a direita", + "controlify.action.controlify.jump": "Pular", + "controlify.action.controlify.sneak": "Agachar", + "controlify.action.controlify.attack": "Atacar", + "controlify.action.controlify.use": "Usar", + "controlify.action.controlify.sprint": "Correr", + "controlify.action.controlify.next_slot": "Próximo espaço da barra rápida", + "controlify.action.controlify.prev_slot": "Espaço anterior da barra rápida", + "controlify.action.controlify.pause": "Pausar jogo", + "controlify.action.controlify.inventory": "Abrir Inventário", + "controlify.action.controlify.change_perspective": "Alterar perspectiva", + "controlify.action.controlify.swap_hands": "Trocar de mão", + "controlify.action.controlify.open_chat": "Abrir chat", + "controlify.action.controlify.inv_select": "Selecionar item", + "controlify.action.controlify.inv_take_half": "Pegar metade", + "controlify.action.controlify.drop": "Largar item", + "controlify.action.controlify.drop_stack": "Largar pilha", + "controlify.action.controlify.drop_inventory": "Largar item (em contêineres)", + "controlify.action.controlify.pick_block": "Escolher bloco", + "controlify.action.controlify.pick_block_nbt": "Escolher bloco (com NBT)", + "controlify.action.controlify.take_screenshot": "Capturar tela", + "controlify.action.controlify.show_player_list": "Exibir lista de jogadores", + "controlify.action.controlify.radial_menu": "Menu Radial", + "controlify.action.voicechat.mute_microphone": "Silenciar microfone", "controlify.input.controlify.button/south": "Face sul", "controlify.input.controlify.button/east": "Face leste", "controlify.input.controlify.button/west": "Face oeste", @@ -299,9 +299,9 @@ "controlify.radial.hotbar_load_hint": "Carregue os espaços da barra rápida.", "controlify.radial.debug.overlay": "Mostrar sobreposição", "controlify.radial.debug.hide_overlay": "Esconder sobreposição", - "controlify.radial.debug.reload_chunks": "Recarregar chunks", - "controlify.radial.debug.reload_packs": "Recarregar pacotes de recursos", - "controlify.radial.debug.clear_chat": "Limpar chat", + "controlify.action.controlify.debug_reload_chunks": "Recarregar chunks", + "controlify.action.controlify.debug_reload_resource_packs": "Recarregar pacotes de recursos", + "controlify.action.controlify.debug_clear_chat": "Limpar chat", "controlify.controller_submission.operational_checkbox": "Seu controle funciona com o Controlify?", "controlify.controller_submission.submit": "Enviar dados", "controlify.controller_submission.skip": "Pular", diff --git a/src/main/resources/assets/controlify/lang/pt_pt.json b/src/main/resources/assets/controlify/lang/pt_pt.json index d695cafc5..342e46291 100644 --- a/src/main/resources/assets/controlify/lang/pt_pt.json +++ b/src/main/resources/assets/controlify/lang/pt_pt.json @@ -205,91 +205,91 @@ "controlify.bluetooth_warning.title": "Comando conectado por Bluetooth", "controlify.bluetooth_warning.desc": "Alguns comandos podem não ter todas as funcionalidades ao serem conectados via Bluetooth. Por exemplo, feedback háptico em HD no DualSense. Se achar que está a faltar alguma funcionalidade, tente utilizar uma ligação USB antes de reportar o problema.", "controlify.bluetooth_warning.dont_show": "Não mostrar de novo", - "controlify.binding.controlify.walk_forward": "Andar para frente", - "controlify.binding.controlify.walk_backward": "Andar para trás", - "controlify.binding.controlify.strafe_left": "Andar de lado para a esquerda", - "controlify.binding.controlify.strafe_right": "Andar de lado para a direita", - "controlify.binding.controlify.look_up": "Olhar para Cima", - "controlify.binding.controlify.look_down": "Olhar para Baixo", - "controlify.binding.controlify.look_left": "Olhar para a Esquerda", - "controlify.binding.controlify.look_right": "Olhar para a Direita", - "controlify.binding.controlify.gyro_button": "Botão do Giroscópio", - "controlify.binding.controlify.jump": "Saltar", - "controlify.binding.controlify.sneak": "Agachar-se", - "controlify.binding.controlify.attack": "Atacar", - "controlify.binding.controlify.use": "Usar", - "controlify.binding.controlify.sprint": "Correr", - "controlify.binding.controlify.next_slot": "Próximo slot na barra rápida", - "controlify.binding.controlify.prev_slot": "Slot anterior na barra rápida", - "controlify.binding.controlify.hotbar_load_radial": "Carregar barra rápida criativa", - "controlify.binding.controlify.hotbar_save_radial": "Gravar barra rápida criativa", - "controlify.binding.controlify.hotbar_item_select_radial": "Selecionar slot da barra rápida no menu radial", - "controlify.binding.controlify.pause": "Pausar jogo", - "controlify.binding.controlify.inventory": "Abrir Inventário", - "controlify.binding.controlify.change_perspective": "Mudar Perspectiva", - "controlify.binding.controlify.swap_hands": "Trocar Mãos", - "controlify.binding.controlify.open_chat": "Abrir Chat", - "controlify.binding.controlify.gui_press": "GUI Pressionar", - "controlify.binding.controlify.gui_back": "GUI Para Trás", - "controlify.binding.controlify.gui_next_tab": "GUI Próxima Aba", - "controlify.binding.controlify.gui_prev_tab": "GUI Aba Anterior", - "controlify.binding.controlify.gui_abstract_action_1": "Ação Abstrata da GUI 1", - "controlify.binding.controlify.gui_abstract_action_1.desc": "Um vínculo genérico que pode realizar diferentes ações dependendo do ecrã atual.", - "controlify.binding.controlify.gui_abstract_action_2": "Ação Abstrata da GUI 2", - "controlify.binding.controlify.gui_abstract_action_2.desc": "Um vínculo genérico que pode realizar diferentes ações dependendo do ecrã atual.", - "controlify.binding.controlify.gui_abstract_action_3": "Ação Abstrata da GUI 3", - "controlify.binding.controlify.gui_abstract_action_3.desc": "Um vínculo genérico que pode realizar diferentes ações dependendo do ecrã atual.", - "controlify.binding.controlify.inv_select": "Seleccionar Item", - "controlify.binding.controlify.inv_quick_move": "Mover rapidamente", - "controlify.binding.controlify.inv_take_half": "Pegar Metade", - "controlify.binding.controlify.drop": "Largar Item", - "controlify.binding.controlify.drop_stack": "Largar Pilha", - "controlify.binding.controlify.drop_inventory": "Largar Item (Em um Container)", - "controlify.binding.controlify.pick_block": "Escolher Bloco", - "controlify.binding.controlify.pick_block_nbt": "Escolher Bloco (com NBT)", - "controlify.binding.controlify.take_screenshot": "Tirar Captura de Ecrã", - "controlify.binding.controlify.debug_radial": "Depurar Menu Radial", - "controlify.binding.controlify.toggle_debug_menu": "Alternar Menu de Depuração (F3)", - "controlify.binding.controlify.toggle_debug_menu_fps": "Alternar Menu de Depuração (F3) com FPS", - "controlify.binding.controlify.toggle_debug_menu_prof": "Alternar Menu de Depuração (F3) com Profiler", - "controlify.binding.controlify.toggle_debug_menu_net": "Alternar Menu de Depuração (F3) com Net Graph", - "controlify.binding.controlify.toggle_debug_menu_chart": "Alternar Menu de Depuração (F3) com Gráficos", - "controlify.binding.controlify.toggle_hud_visibility": "Alternar Visibilidade da HUD", - "controlify.binding.controlify.show_player_list": "Mostrar Lista de Utilizadores", - "controlify.binding.controlify.radial_menu": "Menu Radial", - "controlify.binding.controlify.radial_axis_up": "Menu Radial Cima", - "controlify.binding.controlify.radial_axis_down": "Menu Radial Baixo", - "controlify.binding.controlify.radial_axis_left": "Menu Radial Esquerda", - "controlify.binding.controlify.radial_axis_right": "Menu Radial Direita", - "controlify.binding.controlify.game_mode_switcher": "Alternador de Modo de Jogo", - "controlify.binding.controlify.vmouse_move_up": "Mover para cima do Rato Virtual", - "controlify.binding.controlify.vmouse_move_down": "Mover para baixo do Rato Virtual", - "controlify.binding.controlify.vmouse_move_left": "Mover para a esquerda do Rato Virtual", - "controlify.binding.controlify.vmouse_move_right": "Mover para a direita do Rato Virtual", - "controlify.binding.controlify.vmouse_lclick": "Clique esquerdo do Rato Virtual", - "controlify.binding.controlify.vmouse_rclick": "Clique direito do Rato Virtual", - "controlify.binding.controlify.vmouse_shift_click": "Shift Clique do Rato Virtual", - "controlify.binding.controlify.vmouse_snap_up": "Snap Mouse Cima", - "controlify.binding.controlify.vmouse_snap_down": "Snap Mouse Baixo", - "controlify.binding.controlify.vmouse_snap_left": "Snap Mouse Esquerda", - "controlify.binding.controlify.vmouse_snap_right": "Snap Mouse Direita", - "controlify.binding.controlify.vmouse_scroll_up": "Scroll cima do Rato Virtual", - "controlify.binding.controlify.vmouse_scroll_down": "Scroll baixo do Rato Virtual", - "controlify.binding.controlify.vmouse_escape": "Tecla escape do Rato Virtual", - "controlify.binding.controlify.vmouse_shift": "Tecla shift do Rato Virtual", - "controlify.binding.controlify.vmouse_page_next": "Próxima página do Rato Virtual", - "controlify.binding.controlify.vmouse_page_prev": "Página anterior do Rato Virtual", - "controlify.binding.controlify.vmouse_page_down": "Page Down do Rato Virtual", - "controlify.binding.controlify.vmouse_page_up": "Page Up do Rato Virtual", - "controlify.binding.controlify.vmouse_toggle": "Alternar Rato Virtual", - "controlify.binding.controlify.gui_navi_down": "Navegação GUI Baixo", - "controlify.binding.controlify.gui_navi_up": "Navegação GUI Cima", - "controlify.binding.controlify.gui_navi_left": "Navegação GUI Esquerda", - "controlify.binding.controlify.gui_navi_right": "Navegação GUI Direita", - "controlify.binding.controlify.clear_binding": "Limpar Vínculo", - "controlify.binding.voicechat.ptt_hold": "Push-To-Talk (Segurar)", - "controlify.binding.voicechat.ptt_toggle": "Push-To-Talk (Alternar)", - "controlify.binding.voicechat.mute_microphone": "Silenciar Microfone", + "controlify.action.controlify.walk_forward": "Andar para frente", + "controlify.action.controlify.walk_backward": "Andar para trás", + "controlify.action.controlify.strafe_left": "Andar de lado para a esquerda", + "controlify.action.controlify.strafe_right": "Andar de lado para a direita", + "controlify.action.controlify.look_up": "Olhar para Cima", + "controlify.action.controlify.look_down": "Olhar para Baixo", + "controlify.action.controlify.look_left": "Olhar para a Esquerda", + "controlify.action.controlify.look_right": "Olhar para a Direita", + "controlify.action.controlify.gyro_button": "Botão do Giroscópio", + "controlify.action.controlify.jump": "Saltar", + "controlify.action.controlify.sneak": "Agachar-se", + "controlify.action.controlify.attack": "Atacar", + "controlify.action.controlify.use": "Usar", + "controlify.action.controlify.sprint": "Correr", + "controlify.action.controlify.next_slot": "Próximo slot na barra rápida", + "controlify.action.controlify.prev_slot": "Slot anterior na barra rápida", + "controlify.action.controlify.hotbar_load_radial": "Carregar barra rápida criativa", + "controlify.action.controlify.hotbar_save_radial": "Gravar barra rápida criativa", + "controlify.action.controlify.hotbar_item_select_radial": "Selecionar slot da barra rápida no menu radial", + "controlify.action.controlify.pause": "Pausar jogo", + "controlify.action.controlify.inventory": "Abrir Inventário", + "controlify.action.controlify.change_perspective": "Mudar Perspectiva", + "controlify.action.controlify.swap_hands": "Trocar Mãos", + "controlify.action.controlify.open_chat": "Abrir Chat", + "controlify.action.controlify.gui_press": "GUI Pressionar", + "controlify.action.controlify.gui_back": "GUI Para Trás", + "controlify.action.controlify.gui_next_tab": "GUI Próxima Aba", + "controlify.action.controlify.gui_prev_tab": "GUI Aba Anterior", + "controlify.action.controlify.gui_abstract_action_1": "Ação Abstrata da GUI 1", + "controlify.action.controlify.gui_abstract_action_1.desc": "Um vínculo genérico que pode realizar diferentes ações dependendo do ecrã atual.", + "controlify.action.controlify.gui_abstract_action_2": "Ação Abstrata da GUI 2", + "controlify.action.controlify.gui_abstract_action_2.desc": "Um vínculo genérico que pode realizar diferentes ações dependendo do ecrã atual.", + "controlify.action.controlify.gui_abstract_action_3": "Ação Abstrata da GUI 3", + "controlify.action.controlify.gui_abstract_action_3.desc": "Um vínculo genérico que pode realizar diferentes ações dependendo do ecrã atual.", + "controlify.action.controlify.inv_select": "Seleccionar Item", + "controlify.action.controlify.inv_quick_move": "Mover rapidamente", + "controlify.action.controlify.inv_take_half": "Pegar Metade", + "controlify.action.controlify.drop": "Largar Item", + "controlify.action.controlify.drop_stack": "Largar Pilha", + "controlify.action.controlify.drop_inventory": "Largar Item (Em um Container)", + "controlify.action.controlify.pick_block": "Escolher Bloco", + "controlify.action.controlify.pick_block_nbt": "Escolher Bloco (com NBT)", + "controlify.action.controlify.take_screenshot": "Tirar Captura de Ecrã", + "controlify.action.controlify.debug_radial": "Depurar Menu Radial", + "controlify.action.controlify.toggle_debug_menu": "Alternar Menu de Depuração (F3)", + "controlify.action.controlify.toggle_debug_menu_fps": "Alternar Menu de Depuração (F3) com FPS", + "controlify.action.controlify.toggle_debug_menu_prof": "Alternar Menu de Depuração (F3) com Profiler", + "controlify.action.controlify.toggle_debug_menu_net": "Alternar Menu de Depuração (F3) com Net Graph", + "controlify.action.controlify.toggle_debug_menu_chart": "Alternar Menu de Depuração (F3) com Gráficos", + "controlify.action.controlify.toggle_hud_visibility": "Alternar Visibilidade da HUD", + "controlify.action.controlify.show_player_list": "Mostrar Lista de Utilizadores", + "controlify.action.controlify.radial_menu": "Menu Radial", + "controlify.action.controlify.radial_axis_up": "Menu Radial Cima", + "controlify.action.controlify.radial_axis_down": "Menu Radial Baixo", + "controlify.action.controlify.radial_axis_left": "Menu Radial Esquerda", + "controlify.action.controlify.radial_axis_right": "Menu Radial Direita", + "controlify.action.controlify.game_mode_switcher": "Alternador de Modo de Jogo", + "controlify.action.controlify.vmouse_move_up": "Mover para cima do Rato Virtual", + "controlify.action.controlify.vmouse_move_down": "Mover para baixo do Rato Virtual", + "controlify.action.controlify.vmouse_move_left": "Mover para a esquerda do Rato Virtual", + "controlify.action.controlify.vmouse_move_right": "Mover para a direita do Rato Virtual", + "controlify.action.controlify.vmouse_lclick": "Clique esquerdo do Rato Virtual", + "controlify.action.controlify.vmouse_rclick": "Clique direito do Rato Virtual", + "controlify.action.controlify.vmouse_shift_click": "Shift Clique do Rato Virtual", + "controlify.action.controlify.vmouse_snap_up": "Snap Mouse Cima", + "controlify.action.controlify.vmouse_snap_down": "Snap Mouse Baixo", + "controlify.action.controlify.vmouse_snap_left": "Snap Mouse Esquerda", + "controlify.action.controlify.vmouse_snap_right": "Snap Mouse Direita", + "controlify.action.controlify.vmouse_scroll_up": "Scroll cima do Rato Virtual", + "controlify.action.controlify.vmouse_scroll_down": "Scroll baixo do Rato Virtual", + "controlify.action.controlify.vmouse_escape": "Tecla escape do Rato Virtual", + "controlify.action.controlify.vmouse_shift": "Tecla shift do Rato Virtual", + "controlify.action.controlify.vmouse_page_next": "Próxima página do Rato Virtual", + "controlify.action.controlify.vmouse_page_prev": "Página anterior do Rato Virtual", + "controlify.action.controlify.vmouse_page_down": "Page Down do Rato Virtual", + "controlify.action.controlify.vmouse_page_up": "Page Up do Rato Virtual", + "controlify.action.controlify.vmouse_toggle": "Alternar Rato Virtual", + "controlify.action.controlify.gui_navi_down": "Navegação GUI Baixo", + "controlify.action.controlify.gui_navi_up": "Navegação GUI Cima", + "controlify.action.controlify.gui_navi_left": "Navegação GUI Esquerda", + "controlify.action.controlify.gui_navi_right": "Navegação GUI Direita", + "controlify.action.controlify.clear_binding": "Limpar Vínculo", + "controlify.action.voicechat.ptt_hold": "Push-To-Talk (Segurar)", + "controlify.action.voicechat.ptt_toggle": "Push-To-Talk (Alternar)", + "controlify.action.voicechat.mute_microphone": "Silenciar Microfone", "controlify.input.controlify.button/south": "Face Sul", "controlify.input.controlify.button/east": "Face Este", "controlify.input.controlify.button/west": "Face Oeste", @@ -417,13 +417,13 @@ "controlify.radial.debug.overlay_prof": "Mostrar Overlay com Profiler", "controlify.radial.debug.overlay_chart": "Mostrar Overlay com Gráficos", "controlify.radial.debug.hide_overlay": "Esconder Overlay", - "controlify.radial.debug.reload_chunks": "Recarregar Chunks", - "controlify.radial.debug.chunk_borders": "Alternar Limites dos Chunks", - "controlify.radial.debug.advanced_tooltips": "Alternar Dicas avançadas", - "controlify.radial.debug.entity_hitboxes": "Alternar as Hitboxes das Entidades", - "controlify.radial.debug.reload_packs": "Recarregar Pacotes de Recursos", - "controlify.radial.debug.clear_chat": "Limpar o Chat", - "controlify.radial.debug.profile": "Iniciar/Parar o profiling", + "controlify.action.controlify.debug_reload_chunks": "Recarregar Chunks", + "controlify.action.controlify.debug_toggle_chunk_borders": "Alternar Limites dos Chunks", + "controlify.action.controlify.debug_toggle_advanced_tooltips": "Alternar Dicas avançadas", + "controlify.action.controlify.debug_toggle_entity_hitboxes": "Alternar as Hitboxes das Entidades", + "controlify.action.controlify.debug_reload_resource_packs": "Recarregar Pacotes de Recursos", + "controlify.action.controlify.debug_clear_chat": "Limpar o Chat", + "controlify.action.controlify.debug_start_stop_profiling": "Iniciar/Parar o profiling", "controlify.controller_submission.title": "Submissão de Comando Desconhecido", "controlify.controller_submission.message": "Por favor, envie algumas informações sobre o seu comando para a base de dados do Controlify para que possam ser adicionadas numa atualização futura.\n\nO Controlify envia as seguintes informações:\n- Os IDs de fornecedor e produto do seu comando\n- O GUID do seu comando\n- O nome do seu comando (na caixa abaixo)\n- A versão do Controlify que está a utilizar atualmente\n\nIsto é completamente anónimo e não armazena nenhuma das suas informações pessoais ou de conta.", "controlify.controller_submission.operational_checkbox": "O seu comando funciona com o Controlify?", diff --git a/src/main/resources/assets/controlify/lang/ro_ro.json b/src/main/resources/assets/controlify/lang/ro_ro.json index 9fe51bca5..0a07d5eee 100644 --- a/src/main/resources/assets/controlify/lang/ro_ro.json +++ b/src/main/resources/assets/controlify/lang/ro_ro.json @@ -36,23 +36,23 @@ "controlify.toast.controller_disconnected.description": "'%s' a fost deconectat.", "controlify.bluetooth_warning.title": "Controller conectat prin Bluetooth", "controlify.bluetooth_warning.dont_show": "Nu mai afișa", - "controlify.binding.controlify.jump": "Sari", - "controlify.binding.controlify.sneak": "Furișare", - "controlify.binding.controlify.attack": "Atacă", - "controlify.binding.controlify.use": "Foloseşte", - "controlify.binding.controlify.sprint": "Aleargă", - "controlify.binding.controlify.pause": "Pauză Joc", - "controlify.binding.controlify.inventory": "Deschide Inventarul", - "controlify.binding.controlify.change_perspective": "Schimbă Perspectiva", - "controlify.binding.controlify.swap_hands": "Schimbă Mâinile", - "controlify.binding.controlify.open_chat": "Deschide Chat-ul", - "controlify.binding.controlify.inv_select": "Selectează Obiect", - "controlify.binding.controlify.inv_take_half": "Ia Jumătate", - "controlify.binding.controlify.drop": "Aruncă Obiect", - "controlify.binding.controlify.pick_block": "Alege Bloc", - "controlify.binding.controlify.take_screenshot": "Captură de Ecran", - "controlify.binding.controlify.toggle_hud_visibility": "Comută Vizibilitatea HUD", - "controlify.binding.controlify.radial_menu": "Meniu Radial", + "controlify.action.controlify.jump": "Sari", + "controlify.action.controlify.sneak": "Furișare", + "controlify.action.controlify.attack": "Atacă", + "controlify.action.controlify.use": "Foloseşte", + "controlify.action.controlify.sprint": "Aleargă", + "controlify.action.controlify.pause": "Pauză Joc", + "controlify.action.controlify.inventory": "Deschide Inventarul", + "controlify.action.controlify.change_perspective": "Schimbă Perspectiva", + "controlify.action.controlify.swap_hands": "Schimbă Mâinile", + "controlify.action.controlify.open_chat": "Deschide Chat-ul", + "controlify.action.controlify.inv_select": "Selectează Obiect", + "controlify.action.controlify.inv_take_half": "Ia Jumătate", + "controlify.action.controlify.drop": "Aruncă Obiect", + "controlify.action.controlify.pick_block": "Alege Bloc", + "controlify.action.controlify.take_screenshot": "Captură de Ecran", + "controlify.action.controlify.toggle_hud_visibility": "Comută Vizibilitatea HUD", + "controlify.action.controlify.radial_menu": "Meniu Radial", "controlify.input.controlify.button/back": "Înapoi", "controlify.input.controlify.button/start": "Pornire", "controlify.input.controlify.button/guide": "Ghid", diff --git a/src/main/resources/assets/controlify/lang/ru_ru.json b/src/main/resources/assets/controlify/lang/ru_ru.json index c31432277..e0df19e16 100644 --- a/src/main/resources/assets/controlify/lang/ru_ru.json +++ b/src/main/resources/assets/controlify/lang/ru_ru.json @@ -183,74 +183,74 @@ "controlify.bluetooth_warning.title": "Контроллер подключен через Bluetooth", "controlify.bluetooth_warning.desc": "У некоторых контроллеров могут пропасть дополнительные функции когда они подключены через Bluetooth. Например, HD haptics на DualSence. Если вам кажется что у вас отсутствует какая-нибудь функция, попробуйте подключить геймпад через USB перед тем как жаловаться об этом.", "controlify.bluetooth_warning.dont_show": "Не показывать снова", - "controlify.binding.controlify.walk_forward": "Идти вперёд", - "controlify.binding.controlify.walk_backward": "Идти назад", - "controlify.binding.controlify.strafe_left": "Идти влево", - "controlify.binding.controlify.strafe_right": "Идти вправо", - "controlify.binding.controlify.look_up": "Смотреть вверх", - "controlify.binding.controlify.look_down": "Смотреть вниз", - "controlify.binding.controlify.look_left": "Смотреть налево", - "controlify.binding.controlify.look_right": "Смотреть вправо", - "controlify.binding.controlify.gyro_button": "Кнопка Гироскопа", - "controlify.binding.controlify.jump": "Прыжок", - "controlify.binding.controlify.sneak": "Красться", - "controlify.binding.controlify.attack": "Атаковать", - "controlify.binding.controlify.use": "Использовать", - "controlify.binding.controlify.sprint": "Бег", - "controlify.binding.controlify.next_slot": "Следующий слот", - "controlify.binding.controlify.prev_slot": "Предыдущий слот", - "controlify.binding.controlify.pause": "Приостановить игру", - "controlify.binding.controlify.inventory": "Открыть инвентарь", - "controlify.binding.controlify.change_perspective": "Сменить перспективу", - "controlify.binding.controlify.swap_hands": "Сменить руки", - "controlify.binding.controlify.open_chat": "Открыть чат", - "controlify.binding.controlify.gui_press": "Нажатие в интерфейсе", - "controlify.binding.controlify.gui_back": "Возврат в интерфейсе", - "controlify.binding.controlify.gui_next_tab": "Следующая вкладка в интерфейсе", - "controlify.binding.controlify.gui_prev_tab": "Предыдущая вкладка в интерфейсе", - "controlify.binding.controlify.gui_abstract_action_1": "Абстрактное действие 1 в интерфейсе", - "controlify.binding.controlify.gui_abstract_action_1.desc": "Общая привязка, которая позволяет делать различные действия в зависимости от экрана.", - "controlify.binding.controlify.gui_abstract_action_2": "Абстрактное действие 2 в интерфейсе", - "controlify.binding.controlify.gui_abstract_action_2.desc": "Общая привязка, которая позволяет делать различные действия в зависимости от экрана.", - "controlify.binding.controlify.gui_abstract_action_3.desc": "Общая привязка, которая позволяет делать различные действия в зависимости от экрана.", - "controlify.binding.controlify.inv_select": "Выбрать предмет", - "controlify.binding.controlify.inv_quick_move": "Быстро переместить", - "controlify.binding.controlify.inv_take_half": "Взять половину", - "controlify.binding.controlify.drop": "Выкинуть предмет", - "controlify.binding.controlify.drop_stack": "Выкинуть стак", - "controlify.binding.controlify.drop_inventory": "Выкинуть предмет (в контейнерах)", - "controlify.binding.controlify.pick_block": "Взять блок", - "controlify.binding.controlify.pick_block_nbt": "Взять блок (с NBT)", - "controlify.binding.controlify.take_screenshot": "Сделать скриншот", - "controlify.binding.controlify.toggle_debug_menu": "Переключить экран отладки (F3)", - "controlify.binding.controlify.toggle_hud_visibility": "Переключить видимость интерфейса", - "controlify.binding.controlify.show_player_list": "Показать список игроков", - "controlify.binding.controlify.radial_menu": "Радиальное меню", - "controlify.binding.controlify.radial_axis_up": "Вверх в радиальном меню", - "controlify.binding.controlify.radial_axis_down": "Вниз в радиальном меню", - "controlify.binding.controlify.radial_axis_left": "Влево в радиальном меню", - "controlify.binding.controlify.radial_axis_right": "Вправо в радиальном меню", - "controlify.binding.controlify.vmouse_move_up": "Переместить в. курсор вверх", - "controlify.binding.controlify.vmouse_move_down": "Переместить в. курсор вниз", - "controlify.binding.controlify.vmouse_move_left": "Переместить в. курсор влево", - "controlify.binding.controlify.vmouse_move_right": "Переместить в. курсор вправо", - "controlify.binding.controlify.vmouse_lclick": "Нажатие ЛКМ", - "controlify.binding.controlify.vmouse_rclick": "Нажатие ПКМ", - "controlify.binding.controlify.vmouse_shift_click": "Shift+ЛКМ", - "controlify.binding.controlify.vmouse_snap_up": "Snap Mouse вверх", - "controlify.binding.controlify.vmouse_snap_down": "Snap Mouse вниз", - "controlify.binding.controlify.vmouse_snap_left": "Snap Mouse влево", - "controlify.binding.controlify.vmouse_snap_right": "Snap Mouse вправо", - "controlify.binding.controlify.vmouse_scroll_up": "Прокрутка вверх", - "controlify.binding.controlify.vmouse_scroll_down": "Прокрутка вниз", - "controlify.binding.controlify.vmouse_escape": "Виртуальная Escape", - "controlify.binding.controlify.vmouse_shift": "Виртуальная Shift", - "controlify.binding.controlify.vmouse_toggle": "Переключить виртуальную мышь", - "controlify.binding.controlify.gui_navi_down": "Перемещение по интерфейсу вниз", - "controlify.binding.controlify.gui_navi_up": "Перемещение по интерфейсу вверх", - "controlify.binding.controlify.gui_navi_left": "Перемещение по интерфейсу влево", - "controlify.binding.controlify.gui_navi_right": "Перемещение по интерфейсу вправо", - "controlify.binding.controlify.clear_binding": "Снять привязку", + "controlify.action.controlify.walk_forward": "Идти вперёд", + "controlify.action.controlify.walk_backward": "Идти назад", + "controlify.action.controlify.strafe_left": "Идти влево", + "controlify.action.controlify.strafe_right": "Идти вправо", + "controlify.action.controlify.look_up": "Смотреть вверх", + "controlify.action.controlify.look_down": "Смотреть вниз", + "controlify.action.controlify.look_left": "Смотреть налево", + "controlify.action.controlify.look_right": "Смотреть вправо", + "controlify.action.controlify.gyro_button": "Кнопка Гироскопа", + "controlify.action.controlify.jump": "Прыжок", + "controlify.action.controlify.sneak": "Красться", + "controlify.action.controlify.attack": "Атаковать", + "controlify.action.controlify.use": "Использовать", + "controlify.action.controlify.sprint": "Бег", + "controlify.action.controlify.next_slot": "Следующий слот", + "controlify.action.controlify.prev_slot": "Предыдущий слот", + "controlify.action.controlify.pause": "Приостановить игру", + "controlify.action.controlify.inventory": "Открыть инвентарь", + "controlify.action.controlify.change_perspective": "Сменить перспективу", + "controlify.action.controlify.swap_hands": "Сменить руки", + "controlify.action.controlify.open_chat": "Открыть чат", + "controlify.action.controlify.gui_press": "Нажатие в интерфейсе", + "controlify.action.controlify.gui_back": "Возврат в интерфейсе", + "controlify.action.controlify.gui_next_tab": "Следующая вкладка в интерфейсе", + "controlify.action.controlify.gui_prev_tab": "Предыдущая вкладка в интерфейсе", + "controlify.action.controlify.gui_abstract_action_1": "Абстрактное действие 1 в интерфейсе", + "controlify.action.controlify.gui_abstract_action_1.desc": "Общая привязка, которая позволяет делать различные действия в зависимости от экрана.", + "controlify.action.controlify.gui_abstract_action_2": "Абстрактное действие 2 в интерфейсе", + "controlify.action.controlify.gui_abstract_action_2.desc": "Общая привязка, которая позволяет делать различные действия в зависимости от экрана.", + "controlify.action.controlify.gui_abstract_action_3.desc": "Общая привязка, которая позволяет делать различные действия в зависимости от экрана.", + "controlify.action.controlify.inv_select": "Выбрать предмет", + "controlify.action.controlify.inv_quick_move": "Быстро переместить", + "controlify.action.controlify.inv_take_half": "Взять половину", + "controlify.action.controlify.drop": "Выкинуть предмет", + "controlify.action.controlify.drop_stack": "Выкинуть стак", + "controlify.action.controlify.drop_inventory": "Выкинуть предмет (в контейнерах)", + "controlify.action.controlify.pick_block": "Взять блок", + "controlify.action.controlify.pick_block_nbt": "Взять блок (с NBT)", + "controlify.action.controlify.take_screenshot": "Сделать скриншот", + "controlify.action.controlify.toggle_debug_menu": "Переключить экран отладки (F3)", + "controlify.action.controlify.toggle_hud_visibility": "Переключить видимость интерфейса", + "controlify.action.controlify.show_player_list": "Показать список игроков", + "controlify.action.controlify.radial_menu": "Радиальное меню", + "controlify.action.controlify.radial_axis_up": "Вверх в радиальном меню", + "controlify.action.controlify.radial_axis_down": "Вниз в радиальном меню", + "controlify.action.controlify.radial_axis_left": "Влево в радиальном меню", + "controlify.action.controlify.radial_axis_right": "Вправо в радиальном меню", + "controlify.action.controlify.vmouse_move_up": "Переместить в. курсор вверх", + "controlify.action.controlify.vmouse_move_down": "Переместить в. курсор вниз", + "controlify.action.controlify.vmouse_move_left": "Переместить в. курсор влево", + "controlify.action.controlify.vmouse_move_right": "Переместить в. курсор вправо", + "controlify.action.controlify.vmouse_lclick": "Нажатие ЛКМ", + "controlify.action.controlify.vmouse_rclick": "Нажатие ПКМ", + "controlify.action.controlify.vmouse_shift_click": "Shift+ЛКМ", + "controlify.action.controlify.vmouse_snap_up": "Snap Mouse вверх", + "controlify.action.controlify.vmouse_snap_down": "Snap Mouse вниз", + "controlify.action.controlify.vmouse_snap_left": "Snap Mouse влево", + "controlify.action.controlify.vmouse_snap_right": "Snap Mouse вправо", + "controlify.action.controlify.vmouse_scroll_up": "Прокрутка вверх", + "controlify.action.controlify.vmouse_scroll_down": "Прокрутка вниз", + "controlify.action.controlify.vmouse_escape": "Виртуальная Escape", + "controlify.action.controlify.vmouse_shift": "Виртуальная Shift", + "controlify.action.controlify.vmouse_toggle": "Переключить виртуальную мышь", + "controlify.action.controlify.gui_navi_down": "Перемещение по интерфейсу вниз", + "controlify.action.controlify.gui_navi_up": "Перемещение по интерфейсу вверх", + "controlify.action.controlify.gui_navi_left": "Перемещение по интерфейсу влево", + "controlify.action.controlify.gui_navi_right": "Перемещение по интерфейсу вправо", + "controlify.action.controlify.clear_binding": "Снять привязку", "controlify.input.controlify.button/left_stick": "Левый стик", "controlify.input.controlify.button/right_stick": "Правый стик", "controlify.binding_category.gui": "Интерфейс", diff --git a/src/main/resources/assets/controlify/lang/tr_tr.json b/src/main/resources/assets/controlify/lang/tr_tr.json index 964daa593..e6356a1fb 100644 --- a/src/main/resources/assets/controlify/lang/tr_tr.json +++ b/src/main/resources/assets/controlify/lang/tr_tr.json @@ -204,74 +204,74 @@ "controlify.bluetooth_warning.title": "Kontrolcü Bluetooth ile bağlandı", "controlify.bluetooth_warning.desc": "Bluetooth ile bağlandığında bazı kontrolcülerde eksik özellikler olabilir. Örneğin, DualSense üzerinde HD dokunsallar. Bir özelliği kaçırdığınızı düşünüyorsanız, bildirmeden önce USB kullanmayı deneyin.", "controlify.bluetooth_warning.dont_show": "Tekrar gösterme", - "controlify.binding.controlify.walk_forward": "İleri Yürü", - "controlify.binding.controlify.walk_backward": "Geri Yürü", - "controlify.binding.controlify.look_up": "Yukarı Bak", - "controlify.binding.controlify.look_down": "Aşağı Bak", - "controlify.binding.controlify.look_left": "Sola Bak", - "controlify.binding.controlify.look_right": "Sağa Bak", - "controlify.binding.controlify.gyro_button": "Gyro Butonu", - "controlify.binding.controlify.jump": "Zıpla", - "controlify.binding.controlify.sneak": "Eğil", - "controlify.binding.controlify.attack": "Saldır", - "controlify.binding.controlify.use": "Kullan", - "controlify.binding.controlify.sprint": "Koş", - "controlify.binding.controlify.next_slot": "Sonraki Hotbar Slotu", - "controlify.binding.controlify.prev_slot": "Önceki Hotbar Slotu", - "controlify.binding.controlify.hotbar_load_radial": "Yaratıcı Mod Hotbar'ını Yükle", - "controlify.binding.controlify.hotbar_save_radial": "Yaratıcı Mod Hotbar'ını Kaydet", - "controlify.binding.controlify.hotbar_item_select_radial": "Hotbar Yuva Seçimi Dairesel", - "controlify.binding.controlify.pause": "Oyunu Duraklat", - "controlify.binding.controlify.inventory": "Envanteri Aç", - "controlify.binding.controlify.change_perspective": "Perspektifi Değiştir", - "controlify.binding.controlify.swap_hands": "El Değiştir", - "controlify.binding.controlify.open_chat": "Sohbeti Aç", - "controlify.binding.controlify.gui_abstract_action_1.desc": "Geçerli ekrana bağlı olarak farklı şeyler yapabilen genel bir bağlayıcı.", - "controlify.binding.controlify.gui_abstract_action_2.desc": "Geçerli ekrana bağlı olarak farklı şeyler yapabilen genel bir bağlayıcı.", - "controlify.binding.controlify.gui_abstract_action_3.desc": "Geçerli ekrana bağlı olarak farklı şeyler yapabilen genel bir bağlayıcı.", - "controlify.binding.controlify.inv_select": "Eşya Seç", - "controlify.binding.controlify.inv_quick_move": "Hızlı Hareket", - "controlify.binding.controlify.inv_take_half": "Yarısını Al", - "controlify.binding.controlify.drop": "Eşya Bırak", - "controlify.binding.controlify.drop_stack": "İstif Bırak", - "controlify.binding.controlify.drop_inventory": "Eşya Bırak (Konteynerlerde)", - "controlify.binding.controlify.pick_block": "Blok Seç", - "controlify.binding.controlify.pick_block_nbt": "Blok Seç (NBT ile)", - "controlify.binding.controlify.take_screenshot": "Ekran Görüntüsü Al", - "controlify.binding.controlify.debug_radial": "Dairesel Menü Hata Ayıklama", - "controlify.binding.controlify.toggle_debug_menu": "Hata Ayıklama Menüsünü Değiştir (F3)", - "controlify.binding.controlify.toggle_debug_menu_fps": "Hata Ayıklama Menüsünü (F3) FPS ile Değiştir", - "controlify.binding.controlify.toggle_debug_menu_prof": "Hata Ayıklama Menüsünü (F3) Profil Oluşturucu ile Değiştir", - "controlify.binding.controlify.toggle_debug_menu_net": "Hata Ayıklama Menüsünü (F3) Net Grafik ile Değiştir", - "controlify.binding.controlify.toggle_debug_menu_chart": "Hata Ayıklama Menüsünü (F3) Grafiklerle Değiştir", - "controlify.binding.controlify.toggle_hud_visibility": "HUD Görünürlüğünü Değiştir", - "controlify.binding.controlify.show_player_list": "Oyuncu Listesini Göster", - "controlify.binding.controlify.radial_menu": "Dairesel Menü", - "controlify.binding.controlify.radial_axis_up": "Dairesel Menü Yukarı", - "controlify.binding.controlify.radial_axis_down": "Dairesel Menü Aşağı", - "controlify.binding.controlify.radial_axis_left": "Dairesel Menü Sol", - "controlify.binding.controlify.radial_axis_right": "Dairesel Menü Sağ", - "controlify.binding.controlify.game_mode_switcher": "Oyun Modu Değiştirici", - "controlify.binding.controlify.vmouse_move_up": "SFare Yukarı Hareket Et", - "controlify.binding.controlify.vmouse_move_down": "SFare Aşağı Hareket Et", - "controlify.binding.controlify.vmouse_move_left": "SFare Sola Hareket Et", - "controlify.binding.controlify.vmouse_move_right": "SFare Sağa Hareket Et", - "controlify.binding.controlify.vmouse_lclick": "SFare SolTık", - "controlify.binding.controlify.vmouse_rclick": "SFare SağTık", - "controlify.binding.controlify.vmouse_shift_click": "SFare Shift Tık", - "controlify.binding.controlify.vmouse_scroll_up": "SFare Yukarı Kaydır", - "controlify.binding.controlify.vmouse_scroll_down": "SFare Aşağı Kaydır", - "controlify.binding.controlify.vmouse_escape": "SFare Esc Tuşu", - "controlify.binding.controlify.vmouse_shift": "SFare Shift Tuşu", - "controlify.binding.controlify.vmouse_page_next": "SFare Sonraki Sayfa", - "controlify.binding.controlify.vmouse_page_prev": "SFare Önceki Sayfa", - "controlify.binding.controlify.vmouse_page_down": "SFare Aşağı Sayfa", - "controlify.binding.controlify.vmouse_page_up": "SFare Yukarı Sayfa", - "controlify.binding.controlify.vmouse_toggle": "Sanal Fareyi Değiştir", - "controlify.binding.controlify.clear_binding": "Bağlamayı Temizle", - "controlify.binding.voicechat.ptt_hold": "Bas-Konuş (Basılı Tut)", - "controlify.binding.voicechat.ptt_toggle": "Bas-Konuş (Geçiş)", - "controlify.binding.voicechat.mute_microphone": "Mikrofonu Sessize Al", + "controlify.action.controlify.walk_forward": "İleri Yürü", + "controlify.action.controlify.walk_backward": "Geri Yürü", + "controlify.action.controlify.look_up": "Yukarı Bak", + "controlify.action.controlify.look_down": "Aşağı Bak", + "controlify.action.controlify.look_left": "Sola Bak", + "controlify.action.controlify.look_right": "Sağa Bak", + "controlify.action.controlify.gyro_button": "Gyro Butonu", + "controlify.action.controlify.jump": "Zıpla", + "controlify.action.controlify.sneak": "Eğil", + "controlify.action.controlify.attack": "Saldır", + "controlify.action.controlify.use": "Kullan", + "controlify.action.controlify.sprint": "Koş", + "controlify.action.controlify.next_slot": "Sonraki Hotbar Slotu", + "controlify.action.controlify.prev_slot": "Önceki Hotbar Slotu", + "controlify.action.controlify.hotbar_load_radial": "Yaratıcı Mod Hotbar'ını Yükle", + "controlify.action.controlify.hotbar_save_radial": "Yaratıcı Mod Hotbar'ını Kaydet", + "controlify.action.controlify.hotbar_item_select_radial": "Hotbar Yuva Seçimi Dairesel", + "controlify.action.controlify.pause": "Oyunu Duraklat", + "controlify.action.controlify.inventory": "Envanteri Aç", + "controlify.action.controlify.change_perspective": "Perspektifi Değiştir", + "controlify.action.controlify.swap_hands": "El Değiştir", + "controlify.action.controlify.open_chat": "Sohbeti Aç", + "controlify.action.controlify.gui_abstract_action_1.desc": "Geçerli ekrana bağlı olarak farklı şeyler yapabilen genel bir bağlayıcı.", + "controlify.action.controlify.gui_abstract_action_2.desc": "Geçerli ekrana bağlı olarak farklı şeyler yapabilen genel bir bağlayıcı.", + "controlify.action.controlify.gui_abstract_action_3.desc": "Geçerli ekrana bağlı olarak farklı şeyler yapabilen genel bir bağlayıcı.", + "controlify.action.controlify.inv_select": "Eşya Seç", + "controlify.action.controlify.inv_quick_move": "Hızlı Hareket", + "controlify.action.controlify.inv_take_half": "Yarısını Al", + "controlify.action.controlify.drop": "Eşya Bırak", + "controlify.action.controlify.drop_stack": "İstif Bırak", + "controlify.action.controlify.drop_inventory": "Eşya Bırak (Konteynerlerde)", + "controlify.action.controlify.pick_block": "Blok Seç", + "controlify.action.controlify.pick_block_nbt": "Blok Seç (NBT ile)", + "controlify.action.controlify.take_screenshot": "Ekran Görüntüsü Al", + "controlify.action.controlify.debug_radial": "Dairesel Menü Hata Ayıklama", + "controlify.action.controlify.toggle_debug_menu": "Hata Ayıklama Menüsünü Değiştir (F3)", + "controlify.action.controlify.toggle_debug_menu_fps": "Hata Ayıklama Menüsünü (F3) FPS ile Değiştir", + "controlify.action.controlify.toggle_debug_menu_prof": "Hata Ayıklama Menüsünü (F3) Profil Oluşturucu ile Değiştir", + "controlify.action.controlify.toggle_debug_menu_net": "Hata Ayıklama Menüsünü (F3) Net Grafik ile Değiştir", + "controlify.action.controlify.toggle_debug_menu_chart": "Hata Ayıklama Menüsünü (F3) Grafiklerle Değiştir", + "controlify.action.controlify.toggle_hud_visibility": "HUD Görünürlüğünü Değiştir", + "controlify.action.controlify.show_player_list": "Oyuncu Listesini Göster", + "controlify.action.controlify.radial_menu": "Dairesel Menü", + "controlify.action.controlify.radial_axis_up": "Dairesel Menü Yukarı", + "controlify.action.controlify.radial_axis_down": "Dairesel Menü Aşağı", + "controlify.action.controlify.radial_axis_left": "Dairesel Menü Sol", + "controlify.action.controlify.radial_axis_right": "Dairesel Menü Sağ", + "controlify.action.controlify.game_mode_switcher": "Oyun Modu Değiştirici", + "controlify.action.controlify.vmouse_move_up": "SFare Yukarı Hareket Et", + "controlify.action.controlify.vmouse_move_down": "SFare Aşağı Hareket Et", + "controlify.action.controlify.vmouse_move_left": "SFare Sola Hareket Et", + "controlify.action.controlify.vmouse_move_right": "SFare Sağa Hareket Et", + "controlify.action.controlify.vmouse_lclick": "SFare SolTık", + "controlify.action.controlify.vmouse_rclick": "SFare SağTık", + "controlify.action.controlify.vmouse_shift_click": "SFare Shift Tık", + "controlify.action.controlify.vmouse_scroll_up": "SFare Yukarı Kaydır", + "controlify.action.controlify.vmouse_scroll_down": "SFare Aşağı Kaydır", + "controlify.action.controlify.vmouse_escape": "SFare Esc Tuşu", + "controlify.action.controlify.vmouse_shift": "SFare Shift Tuşu", + "controlify.action.controlify.vmouse_page_next": "SFare Sonraki Sayfa", + "controlify.action.controlify.vmouse_page_prev": "SFare Önceki Sayfa", + "controlify.action.controlify.vmouse_page_down": "SFare Aşağı Sayfa", + "controlify.action.controlify.vmouse_page_up": "SFare Yukarı Sayfa", + "controlify.action.controlify.vmouse_toggle": "Sanal Fareyi Değiştir", + "controlify.action.controlify.clear_binding": "Bağlamayı Temizle", + "controlify.action.voicechat.ptt_hold": "Bas-Konuş (Basılı Tut)", + "controlify.action.voicechat.ptt_toggle": "Bas-Konuş (Geçiş)", + "controlify.action.voicechat.mute_microphone": "Mikrofonu Sessize Al", "controlify.input.controlify.button/south": "Güney Yüzü", "controlify.input.controlify.button/east": "Doğu Yüzü", "controlify.input.controlify.button/west": "Batı Yüzü", @@ -397,13 +397,13 @@ "controlify.radial.debug.overlay_prof": "Profil Oluşturucu ile Yer Paylaşımını Göster", "controlify.radial.debug.overlay_chart": "Grafiklerle Yer Paylaşımını Göster", "controlify.radial.debug.hide_overlay": "Yer Paylaşımını Gizle", - "controlify.radial.debug.reload_chunks": "Yığınları Yeniden Yükle", - "controlify.radial.debug.chunk_borders": "Yığın Sınırlarını Aç/Kapat", - "controlify.radial.debug.advanced_tooltips": "Gelişmiş Araç İpuçlarını Aç / Kapat", - "controlify.radial.debug.entity_hitboxes": "Varlık Hitbox'larını Aç/Kapat", - "controlify.radial.debug.reload_packs": "Kaynak Paketlerini Yeniden Yükle", - "controlify.radial.debug.clear_chat": "Sohbeti Sil", - "controlify.radial.debug.profile": "Profil Oluşturmayı Başlat/Durdur", + "controlify.action.controlify.debug_reload_chunks": "Yığınları Yeniden Yükle", + "controlify.action.controlify.debug_toggle_chunk_borders": "Yığın Sınırlarını Aç/Kapat", + "controlify.action.controlify.debug_toggle_advanced_tooltips": "Gelişmiş Araç İpuçlarını Aç / Kapat", + "controlify.action.controlify.debug_toggle_entity_hitboxes": "Varlık Hitbox'larını Aç/Kapat", + "controlify.action.controlify.debug_reload_resource_packs": "Kaynak Paketlerini Yeniden Yükle", + "controlify.action.controlify.debug_clear_chat": "Sohbeti Sil", + "controlify.action.controlify.debug_start_stop_profiling": "Profil Oluşturmayı Başlat/Durdur", "controlify.controller_submission.title": "Bilinmeyen Kontrolcü Gönderimi", "controlify.controller_submission.operational_checkbox": "Kontrolcünüz Controlify ile çalışıyor mu?", "controlify.controller_submission.submit": "Veri Gönder", diff --git a/src/main/resources/assets/controlify/lang/uk_ua.json b/src/main/resources/assets/controlify/lang/uk_ua.json index 5fc256bf7..cc3fde5d5 100644 --- a/src/main/resources/assets/controlify/lang/uk_ua.json +++ b/src/main/resources/assets/controlify/lang/uk_ua.json @@ -207,68 +207,68 @@ "controlify.bluetooth_warning.title": "Контролер підключений через Bluetooth", "controlify.bluetooth_warning.desc": "У деяких контролерах можуть бути відсутні функції під час підключення через Bluetooth. Наприклад, HD haptics на DualSense. Якщо ви вважаєте, що вам не вистачає функції, спробуйте використовувати USB, перш ніж повідомляти про це.", "controlify.bluetooth_warning.dont_show": "Більше не показувати", - "controlify.binding.controlify.walk_forward": "Йти вперед", - "controlify.binding.controlify.walk_backward": "Йти назад", - "controlify.binding.controlify.strafe_left": "Йти ліворуч", - "controlify.binding.controlify.strafe_right": "Йти праворуч", - "controlify.binding.controlify.look_up": "Дивитися вгору", - "controlify.binding.controlify.look_down": "Дивитися вниз", - "controlify.binding.controlify.look_left": "Дивитися ліворуч", - "controlify.binding.controlify.look_right": "Дивитися праворуч", - "controlify.binding.controlify.gyro_button": "Кнопка гіроскопа", - "controlify.binding.controlify.jump": "Стрибок", - "controlify.binding.controlify.sneak": "Крастися", - "controlify.binding.controlify.attack": "Атакувати", - "controlify.binding.controlify.use": "Використати", - "controlify.binding.controlify.sprint": "Біг", - "controlify.binding.controlify.next_slot": "Наступний слот", - "controlify.binding.controlify.prev_slot": "Попередній слот", - "controlify.binding.controlify.gui_abstract_action_2": "Абстрактна дія 2 в інтерфейсі", - "controlify.binding.controlify.gui_abstract_action_3": "Абстрактна дія 3 в інтерфейсі", - "controlify.binding.controlify.inv_select": "Вибрати предмет", - "controlify.binding.controlify.inv_quick_move": "Швидкий рух", - "controlify.binding.controlify.inv_take_half": "Взяти половину", - "controlify.binding.controlify.drop": "Викинути предмет", - "controlify.binding.controlify.drop_stack": "Викинути стак", - "controlify.binding.controlify.drop_inventory": "Викинути предмет (у контейнерах)", - "controlify.binding.controlify.pick_block": "Вибрати блок", - "controlify.binding.controlify.pick_block_nbt": "Вибрати блок (з NBT)", - "controlify.binding.controlify.take_screenshot": "Зробити знімок екрана", - "controlify.binding.controlify.debug_radial": "Налагодження радіального меню", - "controlify.binding.controlify.toggle_debug_menu": "Перемкнути меню налагодження (F3)", - "controlify.binding.controlify.toggle_debug_menu_fps": "Перемкнути меню налагодження (F3) з FPS", - "controlify.binding.controlify.toggle_hud_visibility": "Перемкнути видимість інтерфейсу", - "controlify.binding.controlify.show_player_list": "Показати список гравців", - "controlify.binding.controlify.radial_menu": "Радіальне меню", - "controlify.binding.controlify.radial_axis_up": "Радіальне меню: угору", - "controlify.binding.controlify.radial_axis_down": "Радіальне меню: униз", - "controlify.binding.controlify.radial_axis_left": "Радіальне меню: ліворуч", - "controlify.binding.controlify.radial_axis_right": "Радіальне меню: праворуч", - "controlify.binding.controlify.game_mode_switcher": "Перемикання режимів гри", - "controlify.binding.controlify.vmouse_move_up": "В. миша: перемістити вгору", - "controlify.binding.controlify.vmouse_move_down": "В. миша: перемістити вниз", - "controlify.binding.controlify.vmouse_move_left": "В. миша: перемістити ліворуч", - "controlify.binding.controlify.vmouse_move_right": "В. миша: перемістити праворуч", - "controlify.binding.controlify.vmouse_lclick": "В. миша: ЛКМ", - "controlify.binding.controlify.vmouse_rclick": "В. миша: ПКМ", - "controlify.binding.controlify.vmouse_shift_click": "В. миша: Shift", - "controlify.binding.controlify.vmouse_snap_up": "В. миша: перестрибнути вгору", - "controlify.binding.controlify.vmouse_snap_down": "В. миша: перестрибнути вниз", - "controlify.binding.controlify.vmouse_snap_left": "В. миша: перестрибнути ліворуч", - "controlify.binding.controlify.vmouse_snap_right": "В. миша: перестрибнути праворуч", - "controlify.binding.controlify.vmouse_scroll_up": "В. миша: прокрутити вгору", - "controlify.binding.controlify.vmouse_scroll_down": "В. миша: прокрутити вниз", - "controlify.binding.controlify.vmouse_escape": "В. миша: Escape", - "controlify.binding.controlify.vmouse_shift": "В. миша: Shift", - "controlify.binding.controlify.vmouse_page_next": "В. миша: наступна сторінка", - "controlify.binding.controlify.vmouse_page_prev": "В. миша: попередня сторінка", - "controlify.binding.controlify.vmouse_page_down": "В. миша: сторінка знизу", - "controlify.binding.controlify.vmouse_page_up": "В. миша: сторінка згори", - "controlify.binding.controlify.vmouse_toggle": "Перемкнути віртуальну мишу", - "controlify.binding.controlify.gui_navi_down": "Інтерфейс: переміщення вниз", - "controlify.binding.controlify.gui_navi_up": "Інтерфейс: переміщення вгору", - "controlify.binding.controlify.gui_navi_left": "Інтерфейс: переміщення ліворуч", - "controlify.binding.controlify.gui_navi_right": "Інтерфейс: переміщення праворуч", + "controlify.action.controlify.walk_forward": "Йти вперед", + "controlify.action.controlify.walk_backward": "Йти назад", + "controlify.action.controlify.strafe_left": "Йти ліворуч", + "controlify.action.controlify.strafe_right": "Йти праворуч", + "controlify.action.controlify.look_up": "Дивитися вгору", + "controlify.action.controlify.look_down": "Дивитися вниз", + "controlify.action.controlify.look_left": "Дивитися ліворуч", + "controlify.action.controlify.look_right": "Дивитися праворуч", + "controlify.action.controlify.gyro_button": "Кнопка гіроскопа", + "controlify.action.controlify.jump": "Стрибок", + "controlify.action.controlify.sneak": "Крастися", + "controlify.action.controlify.attack": "Атакувати", + "controlify.action.controlify.use": "Використати", + "controlify.action.controlify.sprint": "Біг", + "controlify.action.controlify.next_slot": "Наступний слот", + "controlify.action.controlify.prev_slot": "Попередній слот", + "controlify.action.controlify.gui_abstract_action_2": "Абстрактна дія 2 в інтерфейсі", + "controlify.action.controlify.gui_abstract_action_3": "Абстрактна дія 3 в інтерфейсі", + "controlify.action.controlify.inv_select": "Вибрати предмет", + "controlify.action.controlify.inv_quick_move": "Швидкий рух", + "controlify.action.controlify.inv_take_half": "Взяти половину", + "controlify.action.controlify.drop": "Викинути предмет", + "controlify.action.controlify.drop_stack": "Викинути стак", + "controlify.action.controlify.drop_inventory": "Викинути предмет (у контейнерах)", + "controlify.action.controlify.pick_block": "Вибрати блок", + "controlify.action.controlify.pick_block_nbt": "Вибрати блок (з NBT)", + "controlify.action.controlify.take_screenshot": "Зробити знімок екрана", + "controlify.action.controlify.debug_radial": "Налагодження радіального меню", + "controlify.action.controlify.toggle_debug_menu": "Перемкнути меню налагодження (F3)", + "controlify.action.controlify.toggle_debug_menu_fps": "Перемкнути меню налагодження (F3) з FPS", + "controlify.action.controlify.toggle_hud_visibility": "Перемкнути видимість інтерфейсу", + "controlify.action.controlify.show_player_list": "Показати список гравців", + "controlify.action.controlify.radial_menu": "Радіальне меню", + "controlify.action.controlify.radial_axis_up": "Радіальне меню: угору", + "controlify.action.controlify.radial_axis_down": "Радіальне меню: униз", + "controlify.action.controlify.radial_axis_left": "Радіальне меню: ліворуч", + "controlify.action.controlify.radial_axis_right": "Радіальне меню: праворуч", + "controlify.action.controlify.game_mode_switcher": "Перемикання режимів гри", + "controlify.action.controlify.vmouse_move_up": "В. миша: перемістити вгору", + "controlify.action.controlify.vmouse_move_down": "В. миша: перемістити вниз", + "controlify.action.controlify.vmouse_move_left": "В. миша: перемістити ліворуч", + "controlify.action.controlify.vmouse_move_right": "В. миша: перемістити праворуч", + "controlify.action.controlify.vmouse_lclick": "В. миша: ЛКМ", + "controlify.action.controlify.vmouse_rclick": "В. миша: ПКМ", + "controlify.action.controlify.vmouse_shift_click": "В. миша: Shift", + "controlify.action.controlify.vmouse_snap_up": "В. миша: перестрибнути вгору", + "controlify.action.controlify.vmouse_snap_down": "В. миша: перестрибнути вниз", + "controlify.action.controlify.vmouse_snap_left": "В. миша: перестрибнути ліворуч", + "controlify.action.controlify.vmouse_snap_right": "В. миша: перестрибнути праворуч", + "controlify.action.controlify.vmouse_scroll_up": "В. миша: прокрутити вгору", + "controlify.action.controlify.vmouse_scroll_down": "В. миша: прокрутити вниз", + "controlify.action.controlify.vmouse_escape": "В. миша: Escape", + "controlify.action.controlify.vmouse_shift": "В. миша: Shift", + "controlify.action.controlify.vmouse_page_next": "В. миша: наступна сторінка", + "controlify.action.controlify.vmouse_page_prev": "В. миша: попередня сторінка", + "controlify.action.controlify.vmouse_page_down": "В. миша: сторінка знизу", + "controlify.action.controlify.vmouse_page_up": "В. миша: сторінка згори", + "controlify.action.controlify.vmouse_toggle": "Перемкнути віртуальну мишу", + "controlify.action.controlify.gui_navi_down": "Інтерфейс: переміщення вниз", + "controlify.action.controlify.gui_navi_up": "Інтерфейс: переміщення вгору", + "controlify.action.controlify.gui_navi_left": "Інтерфейс: переміщення ліворуч", + "controlify.action.controlify.gui_navi_right": "Інтерфейс: переміщення праворуч", "controlify.input.controlify.button/left_stick": "Лівий стік", "controlify.input.controlify.button/right_stick": "Правий стік", "controlify.input.controlify.axis/left_stick_down": "Лівий стік вниз", diff --git a/src/main/resources/assets/controlify/lang/vi_vn.json b/src/main/resources/assets/controlify/lang/vi_vn.json index fe0d89f3e..c3e3966ee 100644 --- a/src/main/resources/assets/controlify/lang/vi_vn.json +++ b/src/main/resources/assets/controlify/lang/vi_vn.json @@ -279,95 +279,95 @@ "controlify.bluetooth_warning.title": "Đã kết nối tay cầm qua Bluetooth", "controlify.bluetooth_warning.desc": "Một số tay cầm có thể mất các tính năng nhất định khi kết nối qua Bluetooth, chẳng hạn như tính năng haptic HD trên tay cầm DualSense. Nếu bạn thấy mình cũng gặp vấn đề tương tự, hãy thử kết nối qua USB trước khi báo lỗi.", "controlify.bluetooth_warning.dont_show": "Không hiển thị lại", - "controlify.binding.controlify.walk_forward": "Tiến lên phía trước", - "controlify.binding.controlify.walk_backward": "Lùi về phía sau", - "controlify.binding.controlify.strafe_left": "Bước sang trái", - "controlify.binding.controlify.strafe_right": "Bước sang phải", - "controlify.binding.controlify.look_up": "Nhìn lên", - "controlify.binding.controlify.look_down": "Nhìn xuống", - "controlify.binding.controlify.look_left": "Nhìn sang trái", - "controlify.binding.controlify.look_right": "Nhìn sang phải", - "controlify.binding.controlify.gyro_button": "Nút con quay hồi chuyển", - "controlify.binding.controlify.jump": "Nhảy", - "controlify.binding.controlify.sneak": "Đi rón rén", - "controlify.binding.controlify.attack": "Tấn công", - "controlify.binding.controlify.use": "Sử dụng", - "controlify.binding.controlify.sprint": "Chạy", - "controlify.binding.controlify.next_slot": "Vị trí ô vật phẩm tiếp theo", - "controlify.binding.controlify.prev_slot": "Vị trí ô vật phẩm trước", - "controlify.binding.controlify.hotbar_load_radial": "Nạp thanh vật phẩm chế độ sáng tạo", - "controlify.binding.controlify.hotbar_save_radial": "Lưu thanh vật phẩm chế độ sáng tạo", - "controlify.binding.controlify.hotbar_item_select_radial": "Menu xoay thanh vật phẩm", - "controlify.binding.controlify.pause": "Tạm dừng trò chơi", - "controlify.binding.controlify.inventory": "Mở túi đồ", - "controlify.binding.controlify.change_perspective": "Đổi góc nhìn", - "controlify.binding.controlify.swap_hands": "Đổi tay", - "controlify.binding.controlify.open_chat": "Mở màn hình trò chuyện", - "controlify.binding.controlify.gui_press": "Nhấn nút trên giao diện", - "controlify.binding.controlify.gui_back": "Quay lại giao diện trước đó", - "controlify.binding.controlify.gui_next_tab": "Mở tab tiếp theo", - "controlify.binding.controlify.gui_prev_tab": "Mở tab trước đó", - "controlify.binding.controlify.gui_abstract_action_1": "Thao tác giao diện 1", - "controlify.binding.controlify.gui_abstract_action_1.desc": "Một tổ hợp phím chung có thể thực hiện những việc khác nhau tùy thuộc vào màn hình hiện tại.", - "controlify.binding.controlify.gui_abstract_action_2": "Thao tác giao diện 2", - "controlify.binding.controlify.gui_abstract_action_2.desc": "Một tổ hợp phím chung có thể thực hiện những việc khác nhau tùy thuộc vào màn hình hiện tại.", - "controlify.binding.controlify.gui_abstract_action_3": "Thao tác giao diện 3", - "controlify.binding.controlify.gui_abstract_action_3.desc": "Một tổ hợp phím chung có thể thực hiện những việc khác nhau tùy thuộc vào màn hình hiện tại.", - "controlify.binding.controlify.inv_select": "Chọn vật phẩm", - "controlify.binding.controlify.inv_quick_move": "Di chuyển nhanh", - "controlify.binding.controlify.inv_take_half": "Lấy một nửa", - "controlify.binding.controlify.drop": "Thả vật phẩm", - "controlify.binding.controlify.drop_stack": "Thả chồng", - "controlify.binding.controlify.drop_inventory": "Thả vật phẩm (trong menu đồ)", - "controlify.binding.controlify.pick_block": "Chọn khối", - "controlify.binding.controlify.pick_block_nbt": "Chọn khối (kèm NBT)", - "controlify.binding.controlify.take_screenshot": "Chụp ảnh màn hình", - "controlify.binding.controlify.debug_radial": "Menu xoay gỡ lỗi", - "controlify.binding.controlify.toggle_debug_menu": "Bật tắt menu gỡ lỗi (F3)", - "controlify.binding.controlify.toggle_debug_menu_fps": "Bật tắt menu gỡ lỗi (F3) kèm FPS", - "controlify.binding.controlify.toggle_debug_menu_prof": "Bật tắt menu gỡ lỗi (F3) kèm profiler", - "controlify.binding.controlify.toggle_debug_menu_net": "Bật tắt menu gỡ lỗi (F3) kèm biểu đồ mạng", - "controlify.binding.controlify.toggle_debug_menu_chart": "Bật tắt menu gỡ lỗi (F3) kèm biểu đồ", - "controlify.binding.controlify.toggle_hud_visibility": "Ẩn hiện giao diện", - "controlify.binding.controlify.show_player_list": "Hiện danh sách người chơi", - "controlify.binding.controlify.radial_menu": "Menu xoay", - "controlify.binding.controlify.radial_axis_up": "Menu xoay lên", - "controlify.binding.controlify.radial_axis_down": "Menu xoay xuống", - "controlify.binding.controlify.radial_axis_left": "Menu xoay trái", - "controlify.binding.controlify.radial_axis_right": "Menu xoay phải", - "controlify.binding.controlify.game_mode_switcher": "Giao diện đổi chế độ chơi", - "controlify.binding.controlify.vmouse_move_up": "Di chuyển chuột ảo lên", - "controlify.binding.controlify.vmouse_move_down": "Di chuyển chuột ảo xuống", - "controlify.binding.controlify.vmouse_move_left": "Di chuyển chuột ảo sang trái", - "controlify.binding.controlify.vmouse_move_right": "Di chuyển chuột ảo sang phải", - "controlify.binding.controlify.vmouse_lclick": "Chuột ảo nhấp trái", - "controlify.binding.controlify.vmouse_rclick": "Chuột ảo nhấp phải", - "controlify.binding.controlify.vmouse_shift_click": "Giữ Shift và nhấp chuột ảo", - "controlify.binding.controlify.vmouse_snap_up": "Chuyển chuột lên trên", - "controlify.binding.controlify.vmouse_snap_down": "Chuyển chuột xuống dưới", - "controlify.binding.controlify.vmouse_snap_left": "Chuyển chuột sang trái", - "controlify.binding.controlify.vmouse_snap_right": "Chuyển chuột sang phải", - "controlify.binding.controlify.vmouse_scroll_up": "Chuột ảo cuộn lên", - "controlify.binding.controlify.vmouse_scroll_down": "Chuột ảo cuộn xuống", - "controlify.binding.controlify.vmouse_escape": "Chuột ảo phím Esc", - "controlify.binding.controlify.vmouse_shift": "Chuột ảo phím Shift", - "controlify.binding.controlify.vmouse_page_next": "Chuột ảo trang tiếp", - "controlify.binding.controlify.vmouse_page_prev": "Chuột ảo trang trước", - "controlify.binding.controlify.vmouse_page_down": "Chuột ảo xuống trang", - "controlify.binding.controlify.vmouse_page_up": "Chuột ảo trang lên trang", - "controlify.binding.controlify.vmouse_toggle": "Bật tắt chuột ảo", - "controlify.binding.controlify.gui_navi_down": "Điều hướng giao diện xuống", - "controlify.binding.controlify.gui_navi_up": "Điều hướng giao diện lên", - "controlify.binding.controlify.gui_navi_left": "Điều hướng giao diện sang trái", - "controlify.binding.controlify.gui_navi_right": "Điều hướng giao diện sang phải", - "controlify.binding.controlify.gui_secondary_navi_down": "Điều hướng giao diện thứ cấp xuống", - "controlify.binding.controlify.gui_secondary_navi_up": "Điều hướng giao diện thứ cấp lên", - "controlify.binding.controlify.gui_secondary_navi_left": "Điều hướng giao diện thứ cấp sang trái", - "controlify.binding.controlify.gui_secondary_navi_right": "Điều hướng giao diện thứ cấp sang phải", - "controlify.binding.controlify.clear_binding": "Xoá tổ hợp phím", - "controlify.binding.voicechat.ptt_hold": "Nhấn để nói (giữ)", - "controlify.binding.voicechat.ptt_toggle": "Nhấn để nói (đổi)", - "controlify.binding.voicechat.mute_microphone": "Tắt micrô", + "controlify.action.controlify.walk_forward": "Tiến lên phía trước", + "controlify.action.controlify.walk_backward": "Lùi về phía sau", + "controlify.action.controlify.strafe_left": "Bước sang trái", + "controlify.action.controlify.strafe_right": "Bước sang phải", + "controlify.action.controlify.look_up": "Nhìn lên", + "controlify.action.controlify.look_down": "Nhìn xuống", + "controlify.action.controlify.look_left": "Nhìn sang trái", + "controlify.action.controlify.look_right": "Nhìn sang phải", + "controlify.action.controlify.gyro_button": "Nút con quay hồi chuyển", + "controlify.action.controlify.jump": "Nhảy", + "controlify.action.controlify.sneak": "Đi rón rén", + "controlify.action.controlify.attack": "Tấn công", + "controlify.action.controlify.use": "Sử dụng", + "controlify.action.controlify.sprint": "Chạy", + "controlify.action.controlify.next_slot": "Vị trí ô vật phẩm tiếp theo", + "controlify.action.controlify.prev_slot": "Vị trí ô vật phẩm trước", + "controlify.action.controlify.hotbar_load_radial": "Nạp thanh vật phẩm chế độ sáng tạo", + "controlify.action.controlify.hotbar_save_radial": "Lưu thanh vật phẩm chế độ sáng tạo", + "controlify.action.controlify.hotbar_item_select_radial": "Menu xoay thanh vật phẩm", + "controlify.action.controlify.pause": "Tạm dừng trò chơi", + "controlify.action.controlify.inventory": "Mở túi đồ", + "controlify.action.controlify.change_perspective": "Đổi góc nhìn", + "controlify.action.controlify.swap_hands": "Đổi tay", + "controlify.action.controlify.open_chat": "Mở màn hình trò chuyện", + "controlify.action.controlify.gui_press": "Nhấn nút trên giao diện", + "controlify.action.controlify.gui_back": "Quay lại giao diện trước đó", + "controlify.action.controlify.gui_next_tab": "Mở tab tiếp theo", + "controlify.action.controlify.gui_prev_tab": "Mở tab trước đó", + "controlify.action.controlify.gui_abstract_action_1": "Thao tác giao diện 1", + "controlify.action.controlify.gui_abstract_action_1.desc": "Một tổ hợp phím chung có thể thực hiện những việc khác nhau tùy thuộc vào màn hình hiện tại.", + "controlify.action.controlify.gui_abstract_action_2": "Thao tác giao diện 2", + "controlify.action.controlify.gui_abstract_action_2.desc": "Một tổ hợp phím chung có thể thực hiện những việc khác nhau tùy thuộc vào màn hình hiện tại.", + "controlify.action.controlify.gui_abstract_action_3": "Thao tác giao diện 3", + "controlify.action.controlify.gui_abstract_action_3.desc": "Một tổ hợp phím chung có thể thực hiện những việc khác nhau tùy thuộc vào màn hình hiện tại.", + "controlify.action.controlify.inv_select": "Chọn vật phẩm", + "controlify.action.controlify.inv_quick_move": "Di chuyển nhanh", + "controlify.action.controlify.inv_take_half": "Lấy một nửa", + "controlify.action.controlify.drop": "Thả vật phẩm", + "controlify.action.controlify.drop_stack": "Thả chồng", + "controlify.action.controlify.drop_inventory": "Thả vật phẩm (trong menu đồ)", + "controlify.action.controlify.pick_block": "Chọn khối", + "controlify.action.controlify.pick_block_nbt": "Chọn khối (kèm NBT)", + "controlify.action.controlify.take_screenshot": "Chụp ảnh màn hình", + "controlify.action.controlify.debug_radial": "Menu xoay gỡ lỗi", + "controlify.action.controlify.toggle_debug_menu": "Bật tắt menu gỡ lỗi (F3)", + "controlify.action.controlify.toggle_debug_menu_fps": "Bật tắt menu gỡ lỗi (F3) kèm FPS", + "controlify.action.controlify.toggle_debug_menu_prof": "Bật tắt menu gỡ lỗi (F3) kèm profiler", + "controlify.action.controlify.toggle_debug_menu_net": "Bật tắt menu gỡ lỗi (F3) kèm biểu đồ mạng", + "controlify.action.controlify.toggle_debug_menu_chart": "Bật tắt menu gỡ lỗi (F3) kèm biểu đồ", + "controlify.action.controlify.toggle_hud_visibility": "Ẩn hiện giao diện", + "controlify.action.controlify.show_player_list": "Hiện danh sách người chơi", + "controlify.action.controlify.radial_menu": "Menu xoay", + "controlify.action.controlify.radial_axis_up": "Menu xoay lên", + "controlify.action.controlify.radial_axis_down": "Menu xoay xuống", + "controlify.action.controlify.radial_axis_left": "Menu xoay trái", + "controlify.action.controlify.radial_axis_right": "Menu xoay phải", + "controlify.action.controlify.game_mode_switcher": "Giao diện đổi chế độ chơi", + "controlify.action.controlify.vmouse_move_up": "Di chuyển chuột ảo lên", + "controlify.action.controlify.vmouse_move_down": "Di chuyển chuột ảo xuống", + "controlify.action.controlify.vmouse_move_left": "Di chuyển chuột ảo sang trái", + "controlify.action.controlify.vmouse_move_right": "Di chuyển chuột ảo sang phải", + "controlify.action.controlify.vmouse_lclick": "Chuột ảo nhấp trái", + "controlify.action.controlify.vmouse_rclick": "Chuột ảo nhấp phải", + "controlify.action.controlify.vmouse_shift_click": "Giữ Shift và nhấp chuột ảo", + "controlify.action.controlify.vmouse_snap_up": "Chuyển chuột lên trên", + "controlify.action.controlify.vmouse_snap_down": "Chuyển chuột xuống dưới", + "controlify.action.controlify.vmouse_snap_left": "Chuyển chuột sang trái", + "controlify.action.controlify.vmouse_snap_right": "Chuyển chuột sang phải", + "controlify.action.controlify.vmouse_scroll_up": "Chuột ảo cuộn lên", + "controlify.action.controlify.vmouse_scroll_down": "Chuột ảo cuộn xuống", + "controlify.action.controlify.vmouse_escape": "Chuột ảo phím Esc", + "controlify.action.controlify.vmouse_shift": "Chuột ảo phím Shift", + "controlify.action.controlify.vmouse_page_next": "Chuột ảo trang tiếp", + "controlify.action.controlify.vmouse_page_prev": "Chuột ảo trang trước", + "controlify.action.controlify.vmouse_page_down": "Chuột ảo xuống trang", + "controlify.action.controlify.vmouse_page_up": "Chuột ảo trang lên trang", + "controlify.action.controlify.vmouse_toggle": "Bật tắt chuột ảo", + "controlify.action.controlify.gui_navi_down": "Điều hướng giao diện xuống", + "controlify.action.controlify.gui_navi_up": "Điều hướng giao diện lên", + "controlify.action.controlify.gui_navi_left": "Điều hướng giao diện sang trái", + "controlify.action.controlify.gui_navi_right": "Điều hướng giao diện sang phải", + "controlify.action.controlify.gui_secondary_navi_down": "Điều hướng giao diện thứ cấp xuống", + "controlify.action.controlify.gui_secondary_navi_up": "Điều hướng giao diện thứ cấp lên", + "controlify.action.controlify.gui_secondary_navi_left": "Điều hướng giao diện thứ cấp sang trái", + "controlify.action.controlify.gui_secondary_navi_right": "Điều hướng giao diện thứ cấp sang phải", + "controlify.action.controlify.clear_binding": "Xoá tổ hợp phím", + "controlify.action.voicechat.ptt_hold": "Nhấn để nói (giữ)", + "controlify.action.voicechat.ptt_toggle": "Nhấn để nói (đổi)", + "controlify.action.voicechat.mute_microphone": "Tắt micrô", "controlify.input.controlify.button/south": "Nút Face dưới", "controlify.input.controlify.button/east": "Nút Face phải", "controlify.input.controlify.button/west": "Nút Face trái", @@ -543,13 +543,13 @@ "controlify.radial.debug.overlay_prof": "Hiện biểu thị kèm profiler", "controlify.radial.debug.overlay_chart": "Hiện biểu thị kèm biểu đồ", "controlify.radial.debug.hide_overlay": "Ẩn biểu thị", - "controlify.radial.debug.reload_chunks": "Nạp lại đoạn khúc", - "controlify.radial.debug.chunk_borders": "Bật tắt viền đoạn khúc", - "controlify.radial.debug.advanced_tooltips": "Bật tắt chú giải nâng cao", - "controlify.radial.debug.entity_hitboxes": "Bật tắt hộp va chạm thực thể", - "controlify.radial.debug.reload_packs": "Tải lại gói tài nguyên", - "controlify.radial.debug.clear_chat": "Xóa sạch trò chuyện", - "controlify.radial.debug.profile": "Bắt đầu/Kết thúc phân tích", + "controlify.action.controlify.debug_reload_chunks": "Nạp lại đoạn khúc", + "controlify.action.controlify.debug_toggle_chunk_borders": "Bật tắt viền đoạn khúc", + "controlify.action.controlify.debug_toggle_advanced_tooltips": "Bật tắt chú giải nâng cao", + "controlify.action.controlify.debug_toggle_entity_hitboxes": "Bật tắt hộp va chạm thực thể", + "controlify.action.controlify.debug_reload_resource_packs": "Tải lại gói tài nguyên", + "controlify.action.controlify.debug_clear_chat": "Xóa sạch trò chuyện", + "controlify.action.controlify.debug_start_stop_profiling": "Bắt đầu/Kết thúc phân tích", "controlify.controller_submission.title": "Bộ điều khiển không xác định", "controlify.controller_submission.message": "Vui lòng gửi một số thông tin về bộ điều khiển của bạn tới cơ sở dữ liệu của Controlify để được thêm vào trong bản cập nhật trong tương lai.\n\nControlify gửi các thông tin sau:\n- Bên cung cấp tay cầm của bạn & ID sản phẩm\n- GUID tay cầm của bạn\n- Tên tay cầm của bạn (trong khung bên dưới)\n- Phiên bản Controlify bạn đang sử dụng\n\nQuy trình này hoàn toàn ẩn danh và không lưu trữ bất kỳ thông tin cá nhân hoặc tài khoản nào của bạn.", "controlify.controller_submission.operational_checkbox": "Bộ điều khiển của bạn có hoạt động với Controlify không?", diff --git a/src/main/resources/assets/controlify/lang/zh_cn.json b/src/main/resources/assets/controlify/lang/zh_cn.json index 749e3a22c..795a92a94 100644 --- a/src/main/resources/assets/controlify/lang/zh_cn.json +++ b/src/main/resources/assets/controlify/lang/zh_cn.json @@ -279,104 +279,104 @@ "controlify.bluetooth_warning.title": "控制器已通过蓝牙连接", "controlify.bluetooth_warning.desc": "部分控制器通过蓝牙连接时可能会缺少某些功能。例如,DualSense控制器可能无法使用高精细度触感反馈。若发现缺少某些功能,请先尝试使用USB连接,之后再报告问题。", "controlify.bluetooth_warning.dont_show": "不再显示", - "controlify.binding.controlify.walk_forward": "向前移动", - "controlify.binding.controlify.walk_backward": "向后移动", - "controlify.binding.controlify.strafe_left": "向左移动", - "controlify.binding.controlify.strafe_right": "向右移动", - "controlify.binding.controlify.look_up": "向上看", - "controlify.binding.controlify.look_down": "向下看", - "controlify.binding.controlify.look_left": "向左看", - "controlify.binding.controlify.look_right": "向右看", - "controlify.binding.controlify.gyro_button": "陀螺仪按键", - "controlify.binding.controlify.jump": "跳跃", - "controlify.binding.controlify.sneak": "潜行", - "controlify.binding.controlify.attack": "攻击", - "controlify.binding.controlify.use": "使用", - "controlify.binding.controlify.sprint": "疾跑", - "controlify.binding.controlify.next_slot": "下一个快捷栏位", - "controlify.binding.controlify.prev_slot": "上一个快捷栏位", - "controlify.binding.controlify.hotbar_slot_1": "快捷栏1", - "controlify.binding.controlify.hotbar_slot_2": "快捷栏2", - "controlify.binding.controlify.hotbar_slot_3": "快捷栏3", - "controlify.binding.controlify.hotbar_slot_4": "快捷栏4", - "controlify.binding.controlify.hotbar_slot_5": "快捷栏5", - "controlify.binding.controlify.hotbar_slot_6": "快捷栏6", - "controlify.binding.controlify.hotbar_slot_7": "快捷栏7", - "controlify.binding.controlify.hotbar_slot_8": "快捷栏8", - "controlify.binding.controlify.hotbar_slot_9": "快捷栏9", - "controlify.binding.controlify.hotbar_load_radial": "加载创造模式快捷栏", - "controlify.binding.controlify.hotbar_save_radial": "保存创造模式快捷栏", - "controlify.binding.controlify.hotbar_item_select_radial": "快捷栏槽位选择圆形菜单", - "controlify.binding.controlify.pause": "暂停游戏", - "controlify.binding.controlify.inventory": "打开物品栏", - "controlify.binding.controlify.change_perspective": "更改视角", - "controlify.binding.controlify.swap_hands": "交换手", - "controlify.binding.controlify.open_chat": "打开聊天", - "controlify.binding.controlify.gui_press": "GUI按下", - "controlify.binding.controlify.gui_back": "GUI返回", - "controlify.binding.controlify.gui_next_tab": "GUI下一标签页", - "controlify.binding.controlify.gui_prev_tab": "GUI上一标签页", - "controlify.binding.controlify.gui_abstract_action_1": "界面抽象操作1", - "controlify.binding.controlify.gui_abstract_action_1.desc": "一个通用绑定,可以根据当前画面做不同的事情。", - "controlify.binding.controlify.gui_abstract_action_2": "界面抽象操作2", - "controlify.binding.controlify.gui_abstract_action_2.desc": "一个通用绑定,可以根据当前画面做不同的事情。", - "controlify.binding.controlify.gui_abstract_action_3": "界面抽象操作3", - "controlify.binding.controlify.gui_abstract_action_3.desc": "一个通用绑定,可以根据当前画面做不同的事情。", - "controlify.binding.controlify.inv_select": "选择物品", - "controlify.binding.controlify.inv_quick_move": "快速移动", - "controlify.binding.controlify.inv_take_half": "拿取一半", - "controlify.binding.controlify.drop": "丢弃物品", - "controlify.binding.controlify.drop_stack": "丢弃一组", - "controlify.binding.controlify.drop_inventory": "丢弃物品(在容器中)", - "controlify.binding.controlify.pick_block": "选取方块", - "controlify.binding.controlify.pick_block_nbt": "选取方块(含NBT)", - "controlify.binding.controlify.take_screenshot": "屏幕截图", - "controlify.binding.controlify.debug_radial": "调试圆形菜单", - "controlify.binding.controlify.toggle_debug_menu": "切换调试菜单(F3)", - "controlify.binding.controlify.toggle_debug_menu_fps": "切换调试菜单(F3)与帧率", - "controlify.binding.controlify.toggle_debug_menu_prof": "切换调试菜单(F3)与分析器", - "controlify.binding.controlify.toggle_debug_menu_net": "切换调试菜单(F3)与网络图", - "controlify.binding.controlify.toggle_debug_menu_chart": "切换调试菜单(F3)与图表", - "controlify.binding.controlify.toggle_hud_visibility": "切换HUD可见性", - "controlify.binding.controlify.show_player_list": "显示玩家列表", - "controlify.binding.controlify.radial_menu": "圆形菜单", - "controlify.binding.controlify.radial_axis_up": "圆形菜单向上", - "controlify.binding.controlify.radial_axis_down": "圆形菜单向下", - "controlify.binding.controlify.radial_axis_left": "圆形菜单向左", - "controlify.binding.controlify.radial_axis_right": "圆形菜单向右", - "controlify.binding.controlify.game_mode_switcher": "游戏模式切换器", - "controlify.binding.controlify.vmouse_move_up": "虚拟鼠标上移", - "controlify.binding.controlify.vmouse_move_down": "虚拟鼠标下移", - "controlify.binding.controlify.vmouse_move_left": "虚拟鼠标左移", - "controlify.binding.controlify.vmouse_move_right": "虚拟鼠标右移", - "controlify.binding.controlify.vmouse_lclick": "虚拟鼠标左键点击", - "controlify.binding.controlify.vmouse_rclick": "虚拟鼠标右键点击", - "controlify.binding.controlify.vmouse_shift_click": "虚拟鼠标Shift点击", - "controlify.binding.controlify.vmouse_snap_up": "鼠标向上吸附", - "controlify.binding.controlify.vmouse_snap_down": "鼠标向下吸附", - "controlify.binding.controlify.vmouse_snap_left": "鼠标向左吸附", - "controlify.binding.controlify.vmouse_snap_right": "鼠标向右吸附", - "controlify.binding.controlify.vmouse_scroll_up": "虚拟鼠标向上滚动", - "controlify.binding.controlify.vmouse_scroll_down": "虚拟鼠标向下滚动", - "controlify.binding.controlify.vmouse_escape": "虚拟鼠标Esc键", - "controlify.binding.controlify.vmouse_shift": "虚拟鼠标Shift键", - "controlify.binding.controlify.vmouse_page_next": "虚拟鼠标后一页", - "controlify.binding.controlify.vmouse_page_prev": "虚拟鼠标前一页", - "controlify.binding.controlify.vmouse_page_down": "虚拟鼠标向下翻页", - "controlify.binding.controlify.vmouse_page_up": "虚拟鼠标向上翻页", - "controlify.binding.controlify.vmouse_toggle": "开关虚拟鼠标", - "controlify.binding.controlify.gui_navi_down": "GUI导航下移", - "controlify.binding.controlify.gui_navi_up": "GUI导航上移", - "controlify.binding.controlify.gui_navi_left": "GUI导航左移", - "controlify.binding.controlify.gui_navi_right": "GUI导航右移", - "controlify.binding.controlify.gui_secondary_navi_down": "界面二级导航向下", - "controlify.binding.controlify.gui_secondary_navi_up": "界面二级导航向上", - "controlify.binding.controlify.gui_secondary_navi_left": "界面二级导航向左", - "controlify.binding.controlify.gui_secondary_navi_right": "界面二级导航向右", - "controlify.binding.controlify.clear_binding": "清除绑定", - "controlify.binding.voicechat.ptt_hold": "按键说话(按住)", - "controlify.binding.voicechat.ptt_toggle": "按键说话(切换)", - "controlify.binding.voicechat.mute_microphone": "麦克风静音", + "controlify.action.controlify.walk_forward": "向前移动", + "controlify.action.controlify.walk_backward": "向后移动", + "controlify.action.controlify.strafe_left": "向左移动", + "controlify.action.controlify.strafe_right": "向右移动", + "controlify.action.controlify.look_up": "向上看", + "controlify.action.controlify.look_down": "向下看", + "controlify.action.controlify.look_left": "向左看", + "controlify.action.controlify.look_right": "向右看", + "controlify.action.controlify.gyro_button": "陀螺仪按键", + "controlify.action.controlify.jump": "跳跃", + "controlify.action.controlify.sneak": "潜行", + "controlify.action.controlify.attack": "攻击", + "controlify.action.controlify.use": "使用", + "controlify.action.controlify.sprint": "疾跑", + "controlify.action.controlify.next_slot": "下一个快捷栏位", + "controlify.action.controlify.prev_slot": "上一个快捷栏位", + "controlify.action.controlify.hotbar_slot_1": "快捷栏1", + "controlify.action.controlify.hotbar_slot_2": "快捷栏2", + "controlify.action.controlify.hotbar_slot_3": "快捷栏3", + "controlify.action.controlify.hotbar_slot_4": "快捷栏4", + "controlify.action.controlify.hotbar_slot_5": "快捷栏5", + "controlify.action.controlify.hotbar_slot_6": "快捷栏6", + "controlify.action.controlify.hotbar_slot_7": "快捷栏7", + "controlify.action.controlify.hotbar_slot_8": "快捷栏8", + "controlify.action.controlify.hotbar_slot_9": "快捷栏9", + "controlify.action.controlify.hotbar_load_radial": "加载创造模式快捷栏", + "controlify.action.controlify.hotbar_save_radial": "保存创造模式快捷栏", + "controlify.action.controlify.hotbar_item_select_radial": "快捷栏槽位选择圆形菜单", + "controlify.action.controlify.pause": "暂停游戏", + "controlify.action.controlify.inventory": "打开物品栏", + "controlify.action.controlify.change_perspective": "更改视角", + "controlify.action.controlify.swap_hands": "交换手", + "controlify.action.controlify.open_chat": "打开聊天", + "controlify.action.controlify.gui_press": "GUI按下", + "controlify.action.controlify.gui_back": "GUI返回", + "controlify.action.controlify.gui_next_tab": "GUI下一标签页", + "controlify.action.controlify.gui_prev_tab": "GUI上一标签页", + "controlify.action.controlify.gui_abstract_action_1": "界面抽象操作1", + "controlify.action.controlify.gui_abstract_action_1.desc": "一个通用绑定,可以根据当前画面做不同的事情。", + "controlify.action.controlify.gui_abstract_action_2": "界面抽象操作2", + "controlify.action.controlify.gui_abstract_action_2.desc": "一个通用绑定,可以根据当前画面做不同的事情。", + "controlify.action.controlify.gui_abstract_action_3": "界面抽象操作3", + "controlify.action.controlify.gui_abstract_action_3.desc": "一个通用绑定,可以根据当前画面做不同的事情。", + "controlify.action.controlify.inv_select": "选择物品", + "controlify.action.controlify.inv_quick_move": "快速移动", + "controlify.action.controlify.inv_take_half": "拿取一半", + "controlify.action.controlify.drop": "丢弃物品", + "controlify.action.controlify.drop_stack": "丢弃一组", + "controlify.action.controlify.drop_inventory": "丢弃物品(在容器中)", + "controlify.action.controlify.pick_block": "选取方块", + "controlify.action.controlify.pick_block_nbt": "选取方块(含NBT)", + "controlify.action.controlify.take_screenshot": "屏幕截图", + "controlify.action.controlify.debug_radial": "调试圆形菜单", + "controlify.action.controlify.toggle_debug_menu": "切换调试菜单(F3)", + "controlify.action.controlify.toggle_debug_menu_fps": "切换调试菜单(F3)与帧率", + "controlify.action.controlify.toggle_debug_menu_prof": "切换调试菜单(F3)与分析器", + "controlify.action.controlify.toggle_debug_menu_net": "切换调试菜单(F3)与网络图", + "controlify.action.controlify.toggle_debug_menu_chart": "切换调试菜单(F3)与图表", + "controlify.action.controlify.toggle_hud_visibility": "切换HUD可见性", + "controlify.action.controlify.show_player_list": "显示玩家列表", + "controlify.action.controlify.radial_menu": "圆形菜单", + "controlify.action.controlify.radial_axis_up": "圆形菜单向上", + "controlify.action.controlify.radial_axis_down": "圆形菜单向下", + "controlify.action.controlify.radial_axis_left": "圆形菜单向左", + "controlify.action.controlify.radial_axis_right": "圆形菜单向右", + "controlify.action.controlify.game_mode_switcher": "游戏模式切换器", + "controlify.action.controlify.vmouse_move_up": "虚拟鼠标上移", + "controlify.action.controlify.vmouse_move_down": "虚拟鼠标下移", + "controlify.action.controlify.vmouse_move_left": "虚拟鼠标左移", + "controlify.action.controlify.vmouse_move_right": "虚拟鼠标右移", + "controlify.action.controlify.vmouse_lclick": "虚拟鼠标左键点击", + "controlify.action.controlify.vmouse_rclick": "虚拟鼠标右键点击", + "controlify.action.controlify.vmouse_shift_click": "虚拟鼠标Shift点击", + "controlify.action.controlify.vmouse_snap_up": "鼠标向上吸附", + "controlify.action.controlify.vmouse_snap_down": "鼠标向下吸附", + "controlify.action.controlify.vmouse_snap_left": "鼠标向左吸附", + "controlify.action.controlify.vmouse_snap_right": "鼠标向右吸附", + "controlify.action.controlify.vmouse_scroll_up": "虚拟鼠标向上滚动", + "controlify.action.controlify.vmouse_scroll_down": "虚拟鼠标向下滚动", + "controlify.action.controlify.vmouse_escape": "虚拟鼠标Esc键", + "controlify.action.controlify.vmouse_shift": "虚拟鼠标Shift键", + "controlify.action.controlify.vmouse_page_next": "虚拟鼠标后一页", + "controlify.action.controlify.vmouse_page_prev": "虚拟鼠标前一页", + "controlify.action.controlify.vmouse_page_down": "虚拟鼠标向下翻页", + "controlify.action.controlify.vmouse_page_up": "虚拟鼠标向上翻页", + "controlify.action.controlify.vmouse_toggle": "开关虚拟鼠标", + "controlify.action.controlify.gui_navi_down": "GUI导航下移", + "controlify.action.controlify.gui_navi_up": "GUI导航上移", + "controlify.action.controlify.gui_navi_left": "GUI导航左移", + "controlify.action.controlify.gui_navi_right": "GUI导航右移", + "controlify.action.controlify.gui_secondary_navi_down": "界面二级导航向下", + "controlify.action.controlify.gui_secondary_navi_up": "界面二级导航向上", + "controlify.action.controlify.gui_secondary_navi_left": "界面二级导航向左", + "controlify.action.controlify.gui_secondary_navi_right": "界面二级导航向右", + "controlify.action.controlify.clear_binding": "清除绑定", + "controlify.action.voicechat.ptt_hold": "按键说话(按住)", + "controlify.action.voicechat.ptt_toggle": "按键说话(切换)", + "controlify.action.voicechat.mute_microphone": "麦克风静音", "controlify.input.controlify.button/south": "南面", "controlify.input.controlify.button/east": "东面", "controlify.input.controlify.button/west": "西面", @@ -552,13 +552,13 @@ "controlify.radial.debug.overlay_prof": "显示含分析器的叠加界面", "controlify.radial.debug.overlay_chart": "显示含图表的叠加界面", "controlify.radial.debug.hide_overlay": "隐藏叠加界面", - "controlify.radial.debug.reload_chunks": "重新加载区块", - "controlify.radial.debug.chunk_borders": "切换区块边界", - "controlify.radial.debug.advanced_tooltips": "切换高级工具提示", - "controlify.radial.debug.entity_hitboxes": "切换实体判定箱", - "controlify.radial.debug.reload_packs": "重新加载资源包", - "controlify.radial.debug.clear_chat": "清空聊天", - "controlify.radial.debug.profile": "开始/停止分析", + "controlify.action.controlify.debug_reload_chunks": "重新加载区块", + "controlify.action.controlify.debug_toggle_chunk_borders": "切换区块边界", + "controlify.action.controlify.debug_toggle_advanced_tooltips": "切换高级工具提示", + "controlify.action.controlify.debug_toggle_entity_hitboxes": "切换实体判定箱", + "controlify.action.controlify.debug_reload_resource_packs": "重新加载资源包", + "controlify.action.controlify.debug_clear_chat": "清空聊天", + "controlify.action.controlify.debug_start_stop_profiling": "开始/停止分析", "controlify.controller_submission.title": "提交未知手柄", "controlify.controller_submission.message": "请将你的一些手柄信息提交到Controlify的数据库,以便在未来的更新中添加它。\n\nControlify将发送以下信息:\n- 你的手柄的供应商和产品ID\n- 你的手柄的GUID\n- 你的手柄名称(在下面的输入框中)\n- 你当前使用的Controlify版本\n\n这是完全匿名的,不会存储你的任何个人信息或账号信息。", "controlify.controller_submission.operational_checkbox": "你的手柄能与Controlify正常使用吗?", diff --git a/src/main/resources/assets/controlify/lang/zh_tw.json b/src/main/resources/assets/controlify/lang/zh_tw.json index 8e26b8d67..742b5ccc4 100644 --- a/src/main/resources/assets/controlify/lang/zh_tw.json +++ b/src/main/resources/assets/controlify/lang/zh_tw.json @@ -218,91 +218,91 @@ "controlify.bluetooth_warning.title": "控制器已透過藍牙連線", "controlify.bluetooth_warning.desc": "部分控制器透過藍牙連線時可能會缺少部分功能。例如 DualSense 的 HD 震動。如果你認為控制器缺少某項功能,請嘗試使用 USB 連接,然後再回報問題。", "controlify.bluetooth_warning.dont_show": "不再顯示", - "controlify.binding.controlify.walk_forward": "往前走", - "controlify.binding.controlify.walk_backward": "往後走", - "controlify.binding.controlify.strafe_left": "往左走", - "controlify.binding.controlify.strafe_right": "往右走", - "controlify.binding.controlify.look_up": "往上看", - "controlify.binding.controlify.look_down": "往下看", - "controlify.binding.controlify.look_left": "往左看", - "controlify.binding.controlify.look_right": "往右看", - "controlify.binding.controlify.gyro_button": "陀螺儀按鈕", - "controlify.binding.controlify.jump": "跳躍", - "controlify.binding.controlify.sneak": "潛行", - "controlify.binding.controlify.attack": "攻擊", - "controlify.binding.controlify.use": "使用", - "controlify.binding.controlify.sprint": "跑步", - "controlify.binding.controlify.next_slot": "下一個快捷槽儲存槽", - "controlify.binding.controlify.prev_slot": "上一個快捷槽儲存槽", - "controlify.binding.controlify.hotbar_load_radial": "載入創造模式快捷欄", - "controlify.binding.controlify.hotbar_save_radial": "儲存創造模式快捷欄", - "controlify.binding.controlify.hotbar_item_select_radial": "快捷欄選項圓環選單", - "controlify.binding.controlify.pause": "暫停遊戲", - "controlify.binding.controlify.inventory": "開啟物品欄", - "controlify.binding.controlify.change_perspective": "更改視角", - "controlify.binding.controlify.swap_hands": "交換兩手物品", - "controlify.binding.controlify.open_chat": "開啟聊天欄", - "controlify.binding.controlify.gui_press": "介面按下", - "controlify.binding.controlify.gui_back": "介面返回", - "controlify.binding.controlify.gui_next_tab": "介面下一個頁籤", - "controlify.binding.controlify.gui_prev_tab": "介面上一個頁籤", - "controlify.binding.controlify.gui_abstract_action_1": "介面抽象動作 1", - "controlify.binding.controlify.gui_abstract_action_1.desc": "一個通用的綁定,根據目前畫面可能執行不同的操作。", - "controlify.binding.controlify.gui_abstract_action_2": "介面抽象動作 2", - "controlify.binding.controlify.gui_abstract_action_2.desc": "一個通用的綁定,根據目前畫面可能執行不同的操作。", - "controlify.binding.controlify.gui_abstract_action_3": "介面抽象動作 3", - "controlify.binding.controlify.gui_abstract_action_3.desc": "一個通用的綁定,根據目前畫面可能執行不同的操作。", - "controlify.binding.controlify.inv_select": "選擇物品", - "controlify.binding.controlify.inv_quick_move": "快速移動", - "controlify.binding.controlify.inv_take_half": "拿取一半", - "controlify.binding.controlify.drop": "丟棄物品", - "controlify.binding.controlify.drop_stack": "丟棄一組", - "controlify.binding.controlify.drop_inventory": "丟棄物品(在容器中)", - "controlify.binding.controlify.pick_block": "選擇方塊", - "controlify.binding.controlify.pick_block_nbt": "選擇方塊(含 NBT)", - "controlify.binding.controlify.take_screenshot": "擷取螢幕畫面", - "controlify.binding.controlify.debug_radial": "除錯圓環選單", - "controlify.binding.controlify.toggle_debug_menu": "開關除錯畫面(F3)", - "controlify.binding.controlify.toggle_debug_menu_fps": "切換除錯選單 (F3) 並顯示 FPS", - "controlify.binding.controlify.toggle_debug_menu_prof": "切換除錯選單 (F3) 並顯示效能分析器", - "controlify.binding.controlify.toggle_debug_menu_net": "切換除錯選單 (F3) 並顯示網路圖表", - "controlify.binding.controlify.toggle_debug_menu_chart": "切換除錯選單 (F3) 並顯示圖表", - "controlify.binding.controlify.toggle_hud_visibility": "開關 HUD 顯示", - "controlify.binding.controlify.show_player_list": "顯示玩家清單", - "controlify.binding.controlify.radial_menu": "圓環選單", - "controlify.binding.controlify.radial_axis_up": "圓環選單上", - "controlify.binding.controlify.radial_axis_down": "圓環選單下", - "controlify.binding.controlify.radial_axis_left": "圓環選單左", - "controlify.binding.controlify.radial_axis_right": "圓環選單右", - "controlify.binding.controlify.game_mode_switcher": "遊戲模式切換器", - "controlify.binding.controlify.vmouse_move_up": "虛擬滑鼠上移", - "controlify.binding.controlify.vmouse_move_down": "虛擬滑鼠下移", - "controlify.binding.controlify.vmouse_move_left": "虛擬滑鼠左移", - "controlify.binding.controlify.vmouse_move_right": "虛擬滑鼠右移", - "controlify.binding.controlify.vmouse_lclick": "虛擬滑鼠左鍵", - "controlify.binding.controlify.vmouse_rclick": "虛擬滑鼠右鍵", - "controlify.binding.controlify.vmouse_shift_click": "虛擬滑鼠 Shift 鍵", - "controlify.binding.controlify.vmouse_snap_up": "快速移動滑鼠上", - "controlify.binding.controlify.vmouse_snap_down": "快速移動滑鼠下", - "controlify.binding.controlify.vmouse_snap_left": "快速移動滑鼠左", - "controlify.binding.controlify.vmouse_snap_right": "快速移動滑鼠右", - "controlify.binding.controlify.vmouse_scroll_up": "虛擬滑鼠上滑", - "controlify.binding.controlify.vmouse_scroll_down": "虛擬滑鼠下滑", - "controlify.binding.controlify.vmouse_escape": "虛擬滑鼠按鍵 Esc 鍵", - "controlify.binding.controlify.vmouse_shift": "虛擬滑鼠按鍵 Shift 鍵", - "controlify.binding.controlify.vmouse_page_next": "虛擬滑鼠頁面下一頁", - "controlify.binding.controlify.vmouse_page_prev": "虛擬滑鼠頁面上一頁", - "controlify.binding.controlify.vmouse_page_down": "虛擬滑頁面面下移", - "controlify.binding.controlify.vmouse_page_up": "虛擬滑鼠頁面上移", - "controlify.binding.controlify.vmouse_toggle": "切換虛擬滑鼠", - "controlify.binding.controlify.gui_navi_down": "介面導航下", - "controlify.binding.controlify.gui_navi_up": "介面導航上", - "controlify.binding.controlify.gui_navi_left": "介面導航左", - "controlify.binding.controlify.gui_navi_right": "介面導航右", - "controlify.binding.controlify.clear_binding": "清除綁定", - "controlify.binding.voicechat.ptt_hold": "按鍵發話(按住)", - "controlify.binding.voicechat.ptt_toggle": "按鍵發話(切換)", - "controlify.binding.voicechat.mute_microphone": "靜音麥克風", + "controlify.action.controlify.walk_forward": "往前走", + "controlify.action.controlify.walk_backward": "往後走", + "controlify.action.controlify.strafe_left": "往左走", + "controlify.action.controlify.strafe_right": "往右走", + "controlify.action.controlify.look_up": "往上看", + "controlify.action.controlify.look_down": "往下看", + "controlify.action.controlify.look_left": "往左看", + "controlify.action.controlify.look_right": "往右看", + "controlify.action.controlify.gyro_button": "陀螺儀按鈕", + "controlify.action.controlify.jump": "跳躍", + "controlify.action.controlify.sneak": "潛行", + "controlify.action.controlify.attack": "攻擊", + "controlify.action.controlify.use": "使用", + "controlify.action.controlify.sprint": "跑步", + "controlify.action.controlify.next_slot": "下一個快捷槽儲存槽", + "controlify.action.controlify.prev_slot": "上一個快捷槽儲存槽", + "controlify.action.controlify.hotbar_load_radial": "載入創造模式快捷欄", + "controlify.action.controlify.hotbar_save_radial": "儲存創造模式快捷欄", + "controlify.action.controlify.hotbar_item_select_radial": "快捷欄選項圓環選單", + "controlify.action.controlify.pause": "暫停遊戲", + "controlify.action.controlify.inventory": "開啟物品欄", + "controlify.action.controlify.change_perspective": "更改視角", + "controlify.action.controlify.swap_hands": "交換兩手物品", + "controlify.action.controlify.open_chat": "開啟聊天欄", + "controlify.action.controlify.gui_press": "介面按下", + "controlify.action.controlify.gui_back": "介面返回", + "controlify.action.controlify.gui_next_tab": "介面下一個頁籤", + "controlify.action.controlify.gui_prev_tab": "介面上一個頁籤", + "controlify.action.controlify.gui_abstract_action_1": "介面抽象動作 1", + "controlify.action.controlify.gui_abstract_action_1.desc": "一個通用的綁定,根據目前畫面可能執行不同的操作。", + "controlify.action.controlify.gui_abstract_action_2": "介面抽象動作 2", + "controlify.action.controlify.gui_abstract_action_2.desc": "一個通用的綁定,根據目前畫面可能執行不同的操作。", + "controlify.action.controlify.gui_abstract_action_3": "介面抽象動作 3", + "controlify.action.controlify.gui_abstract_action_3.desc": "一個通用的綁定,根據目前畫面可能執行不同的操作。", + "controlify.action.controlify.inv_select": "選擇物品", + "controlify.action.controlify.inv_quick_move": "快速移動", + "controlify.action.controlify.inv_take_half": "拿取一半", + "controlify.action.controlify.drop": "丟棄物品", + "controlify.action.controlify.drop_stack": "丟棄一組", + "controlify.action.controlify.drop_inventory": "丟棄物品(在容器中)", + "controlify.action.controlify.pick_block": "選擇方塊", + "controlify.action.controlify.pick_block_nbt": "選擇方塊(含 NBT)", + "controlify.action.controlify.take_screenshot": "擷取螢幕畫面", + "controlify.action.controlify.debug_radial": "除錯圓環選單", + "controlify.action.controlify.toggle_debug_menu": "開關除錯畫面(F3)", + "controlify.action.controlify.toggle_debug_menu_fps": "切換除錯選單 (F3) 並顯示 FPS", + "controlify.action.controlify.toggle_debug_menu_prof": "切換除錯選單 (F3) 並顯示效能分析器", + "controlify.action.controlify.toggle_debug_menu_net": "切換除錯選單 (F3) 並顯示網路圖表", + "controlify.action.controlify.toggle_debug_menu_chart": "切換除錯選單 (F3) 並顯示圖表", + "controlify.action.controlify.toggle_hud_visibility": "開關 HUD 顯示", + "controlify.action.controlify.show_player_list": "顯示玩家清單", + "controlify.action.controlify.radial_menu": "圓環選單", + "controlify.action.controlify.radial_axis_up": "圓環選單上", + "controlify.action.controlify.radial_axis_down": "圓環選單下", + "controlify.action.controlify.radial_axis_left": "圓環選單左", + "controlify.action.controlify.radial_axis_right": "圓環選單右", + "controlify.action.controlify.game_mode_switcher": "遊戲模式切換器", + "controlify.action.controlify.vmouse_move_up": "虛擬滑鼠上移", + "controlify.action.controlify.vmouse_move_down": "虛擬滑鼠下移", + "controlify.action.controlify.vmouse_move_left": "虛擬滑鼠左移", + "controlify.action.controlify.vmouse_move_right": "虛擬滑鼠右移", + "controlify.action.controlify.vmouse_lclick": "虛擬滑鼠左鍵", + "controlify.action.controlify.vmouse_rclick": "虛擬滑鼠右鍵", + "controlify.action.controlify.vmouse_shift_click": "虛擬滑鼠 Shift 鍵", + "controlify.action.controlify.vmouse_snap_up": "快速移動滑鼠上", + "controlify.action.controlify.vmouse_snap_down": "快速移動滑鼠下", + "controlify.action.controlify.vmouse_snap_left": "快速移動滑鼠左", + "controlify.action.controlify.vmouse_snap_right": "快速移動滑鼠右", + "controlify.action.controlify.vmouse_scroll_up": "虛擬滑鼠上滑", + "controlify.action.controlify.vmouse_scroll_down": "虛擬滑鼠下滑", + "controlify.action.controlify.vmouse_escape": "虛擬滑鼠按鍵 Esc 鍵", + "controlify.action.controlify.vmouse_shift": "虛擬滑鼠按鍵 Shift 鍵", + "controlify.action.controlify.vmouse_page_next": "虛擬滑鼠頁面下一頁", + "controlify.action.controlify.vmouse_page_prev": "虛擬滑鼠頁面上一頁", + "controlify.action.controlify.vmouse_page_down": "虛擬滑頁面面下移", + "controlify.action.controlify.vmouse_page_up": "虛擬滑鼠頁面上移", + "controlify.action.controlify.vmouse_toggle": "切換虛擬滑鼠", + "controlify.action.controlify.gui_navi_down": "介面導航下", + "controlify.action.controlify.gui_navi_up": "介面導航上", + "controlify.action.controlify.gui_navi_left": "介面導航左", + "controlify.action.controlify.gui_navi_right": "介面導航右", + "controlify.action.controlify.clear_binding": "清除綁定", + "controlify.action.voicechat.ptt_hold": "按鍵發話(按住)", + "controlify.action.voicechat.ptt_toggle": "按鍵發話(切換)", + "controlify.action.voicechat.mute_microphone": "靜音麥克風", "controlify.input.controlify.button/south": "南方面", "controlify.input.controlify.button/east": "東方面", "controlify.input.controlify.button/west": "西方面", @@ -433,13 +433,13 @@ "controlify.radial.debug.overlay_prof": "顯示帶有效能分析器的覆蓋層", "controlify.radial.debug.overlay_chart": "顯示帶有圖表的覆蓋層", "controlify.radial.debug.hide_overlay": "隱藏覆蓋層", - "controlify.radial.debug.reload_chunks": "重新載入區塊", - "controlify.radial.debug.chunk_borders": "切換區塊邊界", - "controlify.radial.debug.advanced_tooltips": "切換進階工具提示", - "controlify.radial.debug.entity_hitboxes": "切換實體碰撞箱", - "controlify.radial.debug.reload_packs": "重新載入資源包", - "controlify.radial.debug.clear_chat": "清除聊天欄", - "controlify.radial.debug.profile": "開始或停止分析", + "controlify.action.controlify.debug_reload_chunks": "重新載入區塊", + "controlify.action.controlify.debug_toggle_chunk_borders": "切換區塊邊界", + "controlify.action.controlify.debug_toggle_advanced_tooltips": "切換進階工具提示", + "controlify.action.controlify.debug_toggle_entity_hitboxes": "切換實體碰撞箱", + "controlify.action.controlify.debug_reload_resource_packs": "重新載入資源包", + "controlify.action.controlify.debug_clear_chat": "清除聊天欄", + "controlify.action.controlify.debug_start_stop_profiling": "開始或停止分析", "controlify.controller_submission.title": "未知控制器提交", "controlify.controller_submission.message": "請向 Controlify 的資料庫提交一些你的控制器資訊,以便在將來的更新中增加。\n\nControlify 傳送下列資訊:\n- 你的控制器供應商和產品 ID\n- 你的控制器 GUID\n- 你的控制器名稱(在下方的方框中)\n- 你目前使用的 Controlify 版本\n\n這是完全匿名的,不會儲存任何你的個人或帳號資訊。", "controlify.controller_submission.operational_checkbox": "你的控制器是否在 Controlify 中正常執行?", diff --git a/src/main/resources/controlify.mixins.json b/src/main/resources/controlify.mixins.json index d25e2b00b..b266609d9 100644 --- a/src/main/resources/controlify.mixins.json +++ b/src/main/resources/controlify.mixins.json @@ -26,7 +26,6 @@ "feature.bind.GuiMixin", "feature.bind.HudMixin", "feature.bind.KeyMappingMixin", - "feature.bind.ToggleKeyMappingMixin", "feature.font.FontAccessor", "feature.font.KeybindContentsMixin", "feature.font.TranslatableContentsMixin", diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/impl/ControlifyBootstrap.java b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/ControlifyBootstrap.java index e6bbafe73..53dafd23b 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/impl/ControlifyBootstrap.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/ControlifyBootstrap.java @@ -32,6 +32,7 @@ public ControlifyBootstrap(IEventBus modBus) { () -> (client, parent) -> new ControlifySettingsScreen(parent) ); + PlatformMainUtil.invokeControlifyEntrypoints(); Controlify.instance().preInitialiseControlify(); } if (PlatformMainUtil.getEnv() == Environment.SERVER) { diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/NeoforgePlatformMainImpl.java b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/NeoforgePlatformMainImpl.java index 89c5683b4..d3983d93e 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/NeoforgePlatformMainImpl.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/NeoforgePlatformMainImpl.java @@ -6,7 +6,8 @@ */ package dev.isxander.controlify.neoforge.impl.platform; -import dev.isxander.controlify.impl.api.entrypoint.ControlifyEntrypoint; +import dev.isxander.controlify.api.ControlifyEntrypoint; +import dev.isxander.controlify.impl.utils.CUtil; import dev.isxander.controlify.neoforge.impl.platform.network.C2SNetworkApiNeoforge; import dev.isxander.controlify.neoforge.impl.platform.network.S2CNetworkApiNeoforge; import dev.isxander.controlify.impl.platform.Environment; @@ -93,7 +94,13 @@ public String getControlifyVersion() { @Override public void applyToControlifyEntrypoint(Consumer entrypointConsumer) { - ServiceLoader.load(ControlifyEntrypoint.class).forEach(entrypointConsumer); + ServiceLoader.load(ControlifyEntrypoint.class).stream().forEach(provider -> { + try { + entrypointConsumer.accept(provider.get()); + } catch (Throwable throwable) { + CUtil.LOGGER.error("Failed to load Controlify addon provider: {}", provider.type().getName(), throwable); + } + }); } @Override diff --git a/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/client/NeoforgePlatformClientImpl.java b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/client/NeoforgePlatformClientImpl.java index 0720148c3..bd19f5ce4 100644 --- a/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/client/NeoforgePlatformClientImpl.java +++ b/src/neoforge/java/dev/isxander/controlify/neoforge/impl/platform/client/NeoforgePlatformClientImpl.java @@ -57,7 +57,7 @@ public void registerClientTickStarted(TickEvent event) { @Override public void registerClientTickEnded(TickEvent event) { - NeoForge.EVENT_BUS.addListener(e -> { + NeoForge.EVENT_BUS.addListener(e -> { event.onTick(Minecraft.getInstance()); }); } From a25c6da5650c772e19a878480c77cd45e381fddf Mon Sep 17 00:00:00 2001 From: isxander Date: Thu, 22 Oct 2026 02:55:24 +0100 Subject: [PATCH 14/44] remove deadzone debugging --- .../impl/controllermanager/ControllerCoordinator.java | 5 +---- .../controlify/impl/input/pipeline/DeadzoneStage.java | 10 ---------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerCoordinator.java b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerCoordinator.java index 7b40622be..77b4f044b 100644 --- a/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerCoordinator.java +++ b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerCoordinator.java @@ -209,10 +209,7 @@ private void onControllerAdded(ControllerImpl controller, boolean hotplugged) { } this.inputSubscriptions.put(controller, controller.input().inputPipeline().sourceProcessedPhysicalSignals() .filter(signal -> signal instanceof PhysicalInputSignal) - .subscribe(s -> { - this.controllersWithInput.add(controller); - System.out.println(s); - })); + .subscribe(_ -> this.controllersWithInput.add(controller))); DeviceSettings rememberedDevice = this.config.getSettings().getOrCreateDeviceSettings(controller.uid()); rememberedDevice.name = controller.name(); diff --git a/src/main/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStage.java index 3e7afc594..cc78fb168 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStage.java @@ -1,12 +1,10 @@ package dev.isxander.controlify.impl.input.pipeline; -import dev.isxander.controlify.impl.debug.DebugProperties; import dev.isxander.controlify.impl.input.mapping.DeadzoneGroup; import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; import dev.isxander.controlify.impl.input.signal.PhysicalSignal; import dev.isxander.controlify.api.utils.event.EventSink; import dev.isxander.controlify.api.utils.event.UnaryEventStage; -import dev.isxander.controlify.impl.utils.CUtil; import it.unimi.dsi.fastutil.objects.*; import net.minecraft.resources.Identifier; import net.minecraft.util.Mth; @@ -41,9 +39,6 @@ public void onEvent(@NonNull PhysicalSignal event, @NonNull EventSink Date: Thu, 22 Oct 2026 03:02:53 +0100 Subject: [PATCH 15/44] fix drivers never updating --- .../impl/controllermanager/ControllerCoordinator.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerCoordinator.java b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerCoordinator.java index 77b4f044b..2e245c957 100644 --- a/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerCoordinator.java +++ b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerCoordinator.java @@ -131,6 +131,10 @@ public boolean update(boolean outOfFocus) { this.backend.pollEvents(); + for (var controller : this.controllerManager.getAllControllers()) { + controller.update(); + } + if (!outOfFocus && this.config.getActiveProfile().controllerUid == null && !this.controllersWithInput.contains(this.currentController)) { // The active controller wins regardless of the order of events within the poll. From 78853df8f96279c7033603ef6d830de8122abafe Mon Sep 17 00:00:00 2001 From: isxander Date: Thu, 22 Oct 2026 03:29:02 +0100 Subject: [PATCH 16/44] fix contextual domains loading from bad path; added warning to catch it in the future --- .../impl/contextual/ContextualDomainImpl.java | 14 +- .../dualsense/TriggerEffectInstanceImpl.java | 2 +- .../impl/gui/guide/GuideInstanceImpl.java | 2 +- .../controllers/contextual/guide/in_game.json | 1194 ----------------- 4 files changed, 12 insertions(+), 1200 deletions(-) delete mode 100644 src/main/resources/resourcepacks/legacy_console/assets/controlify/controllers/contextual/guide/in_game.json diff --git a/src/main/java/dev/isxander/controlify/impl/contextual/ContextualDomainImpl.java b/src/main/java/dev/isxander/controlify/impl/contextual/ContextualDomainImpl.java index 33b76a332..06f24348e 100644 --- a/src/main/java/dev/isxander/controlify/impl/contextual/ContextualDomainImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/contextual/ContextualDomainImpl.java @@ -15,6 +15,7 @@ import dev.isxander.controlify.impl.gui.guide.GuideInstanceImpl; import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; import dev.isxander.controlify.impl.utils.CUtil; +import dev.isxander.controlify.impl.utils.log.ControlifyLogger; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.multiplayer.CacheSlot; @@ -37,9 +38,10 @@ import java.util.concurrent.Executor; public final class ContextualDomainImpl implements ContextualDomain, SimpleControlifyReloadListener { - public static final String DIRECTORY = "contextual/facts"; + public static final String DIRECTORY = "controller/contextual/facts"; private static final FileToIdConverter converter = FileToIdConverter.json(DIRECTORY); + private final ControlifyLogger logger; private final Identifier id; private final CacheSlot resolvedFactGraphs = new CacheSlot<>(this::resolveFactGraph); private ContextualStateContributor contributor = ContextualStateContributor.noop(); @@ -48,11 +50,12 @@ public final class ContextualDomainImpl implements Contextual public ContextualDomainImpl(Identifier id) { this.id = Objects.requireNonNull(id, "id"); + this.logger = CUtil.LOGGER.createSubLogger("ContextualDomain " + this.id); this.registerContributor(ContextualStateContributors.COMMON); } @Override - public Identifier id() { + public @NonNull Identifier id() { return this.id; } @@ -64,14 +67,14 @@ public void registerContributor(@NonNull ContextualStateContributor c } @Override - public GuideInstance createGuideInstance(@NonNull Font font) { + public @NonNull GuideInstance createGuideInstance(@NonNull Font font) { RuleSetManager ruleSetManager = RuleSetManager.GUIDE_RULES; return new GuideInstanceImpl<>(this, () -> this.requireRuleEngine(ruleSetManager, "guide"), font); } @Override - public TriggerEffectInstance createTriggerEffectInstance() { + public @NonNull TriggerEffectInstance createTriggerEffectInstance() { RuleSetManager ruleSetManager = RuleSetManager.TRIGGER_EFFECT_RULES; return new TriggerEffectInstanceImpl<>(this, () -> this.requireRuleEngine(ruleSetManager, "trigger effect")); @@ -139,6 +142,9 @@ public CompletableFuture apply(Preparations data, ResourceManager manager, } private synchronized void apply(Preparations data) { + if (this.preparations.facts().isEmpty()) { + this.logger.warn("Contextual domain {} loaded with 0 derived facts. Issue?", this.id); + } this.preparations = data; this.factGraphWithoutLevel = data.factGraphWithoutLevel(); this.resolvedFactGraphs.clear(); diff --git a/src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectInstanceImpl.java b/src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectInstanceImpl.java index 94a7d88fb..37514091a 100644 --- a/src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectInstanceImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/controller/dualsense/TriggerEffectInstanceImpl.java @@ -39,7 +39,7 @@ public TriggerEffectInstanceImpl( } @Override - public void update(C context) { + public void update(@NonNull C context) { var controller = (ControllerImpl) context.controller(); RuleEngine ruleEngine = this.ruleEngineSupplier.get(); diff --git a/src/main/java/dev/isxander/controlify/impl/gui/guide/GuideInstanceImpl.java b/src/main/java/dev/isxander/controlify/impl/gui/guide/GuideInstanceImpl.java index ed864e856..06fa0e318 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/guide/GuideInstanceImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/guide/GuideInstanceImpl.java @@ -46,7 +46,7 @@ public GuideInstanceImpl( } @Override - public void update(C context) { + public void update(@NonNull C context) { RuleEngine ruleEngine = this.ruleEngineSupplier.get(); ContextualState state = this.domain.calculateState(context, ruleEngine.factDependencies()); List guides = ruleEngine.evaluate(state, _ -> true, "guide/" + this.domain.id()); diff --git a/src/main/resources/resourcepacks/legacy_console/assets/controlify/controllers/contextual/guide/in_game.json b/src/main/resources/resourcepacks/legacy_console/assets/controlify/controllers/contextual/guide/in_game.json deleted file mode 100644 index 99a4c520c..000000000 --- a/src/main/resources/resourcepacks/legacy_console/assets/controlify/controllers/contextual/guide/in_game.json +++ /dev/null @@ -1,1194 +0,0 @@ -{ - "replace": true, - "rules": [ - { - "for": "controlify:jump", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:riding_happy_ghast" - ] - }, - "then": { - "translate": "controlify.guide.ingame.fly_up" - } - }, - { - "for": "controlify:jump", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "any_of": [ - "controlify:riding_saddled_camel", - "controlify:riding_saddled_nautilus" - ] - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.dash" - } - }, - { - "for": "controlify:jump", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:riding_saddled_equine" - ] - }, - "then": { - "translate": "key.jump" - } - }, - { - "for": "controlify:jump", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:creative_flying" - ] - }, - "then": { - "translate": "controlify.guide.ingame.fly_up" - } - }, - { - "for": "controlify:jump", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:in_water" - ] - }, - "then": { - "translate": "controlify.guide.ingame.swim_up" - } - }, - { - "for": "controlify:jump", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:on_climbable" - ] - }, - "then": { - "translate": "controlify.guide.ingame.climb_up" - } - }, - { - "for": "controlify:jump", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:on_ground" - ] - }, - "then": { - "translate": "key.jump" - } - }, - { - "for": "controlify:jump", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:can_elytra_fly" - ] - }, - "then": { - "translate": "controlify.guide.ingame.start_elytra" - } - }, - { - "for": "controlify:sneak", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:in_vehicle" - ] - }, - "then": { - "translate": "controlify.guide.ingame.dismount" - } - }, - { - "for": "controlify:sneak", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:creative_flying" - ] - }, - "then": { - "translate": "controlify.guide.ingame.fly_down" - } - }, - { - "for": "controlify:sneak", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:in_water" - ], - "none_of": [ - "controlify:on_ground" - ] - }, - "then": { - "translate": "controlify.guide.ingame.swim_down" - } - }, - { - "for": "controlify:sneak", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:on_climbable" - ] - }, - "then": { - "translate": "controlify.guide.ingame.climb_down" - } - }, - { - "for": "controlify:inventory", - "where": "right", - "if": "controlify:verbosity_reduced", - "then": { - "translate": "controlify.guide.ingame.inventory" - } - }, - { - "for": "controlify:attack", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_entity", - "controlify:is_spectator" - ] - }, - "then": { - "translate": "controlify.guide.ingame.spectate" - } - }, - { - "for": "controlify:attack", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_entity" - ], - "none_of": [ - "controlify:is_spectator" - ] - }, - "then": { - "translate": "controlify.guide.ingame.attack" - } - }, - { - "for": "controlify:attack", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_block", - "controlify:main_hand_is_tool" - ] - }, - "then": { - "translate": "controlify.guide.ingame.mine" - } - }, - { - "for": "controlify:attack", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_block" - ] - }, - "then": { - "translate": "controlify.guide.ingame.break" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_entity", - "controlify:is_spectator" - ] - }, - "then": { - "translate": "controlify.guide.ingame.spectate" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_merchant" - ] - }, - "then": { - "translate": "controlify.guide.ingame.trade" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_chest_boat", - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.open" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_container_minecart" - ] - }, - "then": { - "translate": "controlify.guide.ingame.open" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_rideable" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.ride" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_closed_openable" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.open" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_open_openable" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.close" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_container" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.open" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_button" - ] - }, - "then": { - "translate": "controlify.guide.ingame.press" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_lever" - ] - }, - "then": { - "translate": "controlify.guide.ingame.toggle" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_bell" - ] - }, - "then": { - "translate": "controlify.guide.ingame.ring" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_bed" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.sleep" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_crafting_table" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.craft" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_furnace" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.smelt" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_smoker" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.cook" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_brewing_stand" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.brew" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_enchanting_table" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.enchant" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_smithing_table" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.smith" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_repair_station" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.repair" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_stonecutter" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.cut_stone" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_loom" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.weave" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_cartography_table" - ], - "none_of": [ - "controlify:sneaking" - ] - }, - "then": { - "translate": "controlify.guide.ingame.edit_map" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "items": "#c:tools/bow" - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.shoot" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "predicates": { - "minecraft:charged_projectiles": {} - } - } - } - ], - "none_of": [ - { - "slot": "controlify:main_hand", - "item": { - "components": { - "minecraft:charged_projectiles": [] - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.shoot" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "items": "minecraft:crossbow", - "components": { - "minecraft:charged_projectiles": [] - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.load" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "predicates": { - "minecraft:blocks_attacks": {} - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.block" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "predicates": { - "minecraft:consumable": {} - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.consume" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "any_of": [ - { - "slot": "controlify:main_hand", - "item": { - "items": "#c:tools/trident" - } - }, - { - "slot": "controlify:main_hand", - "item": { - "items": "#minecraft:spears" - } - }, - { - "slot": "controlify:main_hand", - "item": { - "items": "#minecraft:eggs" - } - }, - { - "slot": "controlify:main_hand", - "item": { - "items": [ - "minecraft:snowball", - "minecraft:splash_potion", - "minecraft:lingering_potion", - "minecraft:experience_bottle", - "minecraft:ender_pearl", - "minecraft:wind_charge" - ] - } - } - ] - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.throw" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:fishing_hook_cast", - { - "slot": "controlify:main_hand", - "item": { - "items": "#c:tools/fishing_rod" - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.reel_in" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "items": "#c:tools/fishing_rod" - } - } - ], - "none_of": [ - "controlify:fishing_hook_cast" - ] - }, - "then": { - "translate": "controlify.guide.ingame.cast" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "items": "#c:tools/brush" - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.brush" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "items": "minecraft:spyglass" - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.zoom" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "predicates": { - "minecraft:instrument": {} - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.play" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:main_hand", - "item": { - "predicates": { - "minecraft:equippable": {} - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.equip" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_block", - "controlify:main_hand_is_block_item" - ] - }, - "then": { - "translate": "controlify.guide.ingame.place" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "items": "#c:tools/bow" - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.shoot" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "predicates": { - "minecraft:charged_projectiles": {} - } - } - } - ], - "none_of": [ - { - "slot": "controlify:off_hand", - "item": { - "components": { - "minecraft:charged_projectiles": [] - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.shoot" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "items": "minecraft:crossbow", - "components": { - "minecraft:charged_projectiles": [] - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.load" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "predicates": { - "minecraft:blocks_attacks": {} - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.block" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "predicates": { - "minecraft:consumable": {} - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.consume" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "any_of": [ - { - "slot": "controlify:off_hand", - "item": { - "items": "#c:tools/trident" - } - }, - { - "slot": "controlify:off_hand", - "item": { - "items": "#minecraft:spears" - } - }, - { - "slot": "controlify:off_hand", - "item": { - "items": "#minecraft:eggs" - } - }, - { - "slot": "controlify:off_hand", - "item": { - "items": [ - "minecraft:snowball", - "minecraft:splash_potion", - "minecraft:lingering_potion", - "minecraft:experience_bottle", - "minecraft:ender_pearl", - "minecraft:wind_charge" - ] - } - } - ] - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.throw" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:fishing_hook_cast", - { - "slot": "controlify:off_hand", - "item": { - "items": "#c:tools/fishing_rod" - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.reel_in" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "items": "#c:tools/fishing_rod" - } - } - ], - "none_of": [ - "controlify:fishing_hook_cast" - ] - }, - "then": { - "translate": "controlify.guide.ingame.cast" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "items": "#c:tools/brush" - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.brush" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "items": "minecraft:spyglass" - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.zoom" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "predicates": { - "minecraft:instrument": {} - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.play" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - { - "slot": "controlify:off_hand", - "item": { - "predicates": { - "minecraft:equippable": {} - } - } - } - ] - }, - "then": { - "translate": "controlify.guide.ingame.equip" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_block", - "controlify:off_hand_is_block_item" - ] - }, - "then": { - "translate": "controlify.guide.ingame.place" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_entity" - ], - "none_of": [ - "controlify:is_spectator" - ] - }, - "then": { - "translate": "controlify.guide.ingame.interact" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:looking_at_block" - ] - }, - "then": { - "translate": "controlify.guide.ingame.use" - } - }, - { - "for": "controlify:use", - "where": "right", - "if": { - "all_of": [ - "controlify:verbosity_minimal", - "controlify:has_item_in_either_hand" - ] - }, - "then": { - "translate": "controlify.guide.ingame.use" - } - }, - { - "for": "controlify:drop", - "where": "left", - "if": { - "all_of": [ - "controlify:verbosity_reduced", - "controlify:has_item_in_main_hand" - ] - }, - "then": { - "translate": "controlify.guide.ingame.drop" - } - } - ] -} From 8909d7a94117257f939a14513acae1931db6e460 Mon Sep 17 00:00:00 2001 From: isxander Date: Thu, 22 Oct 2026 03:46:40 +0100 Subject: [PATCH 17/44] make look input including gyro per-frame --- .../config/dto/dfu/ControlifyDataFixer.java | 5 ++- .../config/dto/dfu/ControlifySchemas.java | 7 +++ .../dto/dfu/fixes/RemoveRelativeGyroFix.java | 29 +++++++++++++ .../dto/dfu/fixes/TheHolyMigrationFix.java | 2 +- .../impl/config/dto/profile/GyroConfig.java | 2 - .../config/settings/profile/GyroSettings.java | 5 --- .../screen/ControllerConfigScreenFactory.java | 27 +++++++----- .../impl/ingame/InGameInputHandler.java | 43 ++++++------------- .../impl/utils/DebugOverlayHelper.java | 4 -- .../controlify/controller/default_config.json | 1 - .../assets/controlify/lang/en_us.json | 9 ++-- .../assets/controlify/lang/es_es.json | 6 --- .../assets/controlify/lang/es_mx.json | 6 --- .../assets/controlify/lang/fr_fr.json | 6 --- .../assets/controlify/lang/hu_hu.json | 4 -- .../assets/controlify/lang/it_it.json | 6 --- .../assets/controlify/lang/ja_jp.json | 6 --- .../assets/controlify/lang/ms_my.json | 6 --- .../assets/controlify/lang/pl_pl.json | 6 --- .../assets/controlify/lang/pt_br.json | 6 --- .../assets/controlify/lang/pt_pt.json | 6 --- .../assets/controlify/lang/ro_ro.json | 2 - .../assets/controlify/lang/ru_ru.json | 6 --- .../assets/controlify/lang/tr_tr.json | 6 --- .../assets/controlify/lang/uk_ua.json | 6 --- .../assets/controlify/lang/vi_vn.json | 6 --- .../assets/controlify/lang/zh_cn.json | 6 --- .../assets/controlify/lang/zh_tw.json | 6 --- .../default_bind/default.json | 0 .../default_bind/dualsense.json | 0 .../default_bind/dualshock4.json | 0 .../default_config.json | 0 32 files changed, 72 insertions(+), 158 deletions(-) create mode 100644 src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/RemoveRelativeGyroFix.java rename src/main/resources/resourcepacks/legacy_console/assets/controlify/{controllers => controller}/default_bind/default.json (100%) rename src/main/resources/resourcepacks/legacy_console/assets/controlify/{controllers => controller}/default_bind/dualsense.json (100%) rename src/main/resources/resourcepacks/legacy_console/assets/controlify/{controllers => controller}/default_bind/dualshock4.json (100%) rename src/main/resources/resourcepacks/legacy_console/assets/controlify/{controllers => controller}/default_config.json (100%) diff --git a/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/ControlifyDataFixer.java b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/ControlifyDataFixer.java index 42b750006..2ac7389b3 100644 --- a/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/ControlifyDataFixer.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/ControlifyDataFixer.java @@ -13,7 +13,7 @@ import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; public final class ControlifyDataFixer { - public static final int CURRENT_VERSION = 9; + public static final int CURRENT_VERSION = 10; public static DataFixer getFixer() { return ProductionFixerHolder.FIXER; @@ -55,6 +55,9 @@ private static DataFixer createFixer(GlobalSettings globalDefaults, ProfileSetti var v9 = builder.addSchema(9, ControlifySchemas.V9::new); builder.addFixer(new GyroLowPassFix(v9, profileDefaults)); + var v10 = builder.addSchema(10, ControlifySchemas.V10::new); + builder.addFixer(new RemoveRelativeGyroFix(v10)); + return builder.build().fixer(); } diff --git a/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/ControlifySchemas.java b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/ControlifySchemas.java index b9113575c..c1aa3e2bb 100644 --- a/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/ControlifySchemas.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/ControlifySchemas.java @@ -94,4 +94,11 @@ public V9(int versionKey, Schema parent) { super(versionKey, parent); } } + + public static class V10 extends Schema { + public V10(int versionKey, Schema parent) { + super(versionKey, parent); + } + } + } diff --git a/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/RemoveRelativeGyroFix.java b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/RemoveRelativeGyroFix.java new file mode 100644 index 000000000..ddf5fb05f --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/RemoveRelativeGyroFix.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.config.dto.dfu.fixes; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.DataFix; +import com.mojang.datafixers.TypeRewriteRule; +import com.mojang.datafixers.schemas.Schema; +import dev.isxander.controlify.impl.config.dto.dfu.ControlifyTypeReferences; + +public class RemoveRelativeGyroFix extends DataFix { + public RemoveRelativeGyroFix(Schema outputSchema) { + super(outputSchema, true); + } + + @Override + protected TypeRewriteRule makeRule() { + return fixTypeEverywhereTyped( + "Controlify: remove relative gyro mode", + getInputSchema().getType(ControlifyTypeReferences.PROFILE_CONFIG), + typed -> typed.update(DSL.remainderFinder(), + root -> root.update("gyro", gyro -> gyro.remove("relative_mode"))) + ); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/TheHolyMigrationFix.java b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/TheHolyMigrationFix.java index d0d7725b6..2a1da8887 100644 --- a/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/TheHolyMigrationFix.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/dfu/fixes/TheHolyMigrationFix.java @@ -235,7 +235,7 @@ private Dynamic rewriteGyroConfig(Dynamic old, Dynamic root) { root.createString("look_sensitivity"), old.get("look_sensitivity") .result().orElse(root.createFloat(defaults.lookSensitivity)), root.createString("relative_mode"), old.get("relative_gyro_mode") - .result().orElse(root.createBoolean(defaults.relativeMode)), + .result().orElse(root.createBoolean(false)), root.createString("invert_pitch"), old.get("invert_y") .result().orElse(root.createBoolean(defaults.invertPitch)), root.createString("invert_yaw"), old.get("invert_x") diff --git a/src/main/java/dev/isxander/controlify/impl/config/dto/profile/GyroConfig.java b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/GyroConfig.java index 774d43307..a3f207b27 100644 --- a/src/main/java/dev/isxander/controlify/impl/config/dto/profile/GyroConfig.java +++ b/src/main/java/dev/isxander/controlify/impl/config/dto/profile/GyroConfig.java @@ -14,7 +14,6 @@ public record GyroConfig( float lookSensitivity, - boolean relativeMode, boolean invertPitch, boolean invertYaw, GyroButtonMode buttonMode, @@ -24,7 +23,6 @@ public record GyroConfig( ) { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( Codec.FLOAT.fieldOf("look_sensitivity").forGetter(GyroConfig::lookSensitivity), - Codec.BOOL.fieldOf("relative_mode").forGetter(GyroConfig::relativeMode), Codec.BOOL.fieldOf("invert_pitch").forGetter(GyroConfig::invertPitch), Codec.BOOL.fieldOf("invert_yaw").forGetter(GyroConfig::invertYaw), GyroButtonMode.CODEC.fieldOf("button_mode").forGetter(GyroConfig::buttonMode), diff --git a/src/main/java/dev/isxander/controlify/impl/config/settings/profile/GyroSettings.java b/src/main/java/dev/isxander/controlify/impl/config/settings/profile/GyroSettings.java index c52e6c33e..85d174d82 100644 --- a/src/main/java/dev/isxander/controlify/impl/config/settings/profile/GyroSettings.java +++ b/src/main/java/dev/isxander/controlify/impl/config/settings/profile/GyroSettings.java @@ -12,7 +12,6 @@ public class GyroSettings { public float lookSensitivity; - public boolean relativeMode; public boolean invertPitch; public boolean invertYaw; public GyroButtonMode buttonMode; @@ -22,7 +21,6 @@ public class GyroSettings { public GyroSettings( float lookSensitivity, - boolean relativeMode, boolean invertPitch, boolean invertYaw, GyroButtonMode buttonMode, @@ -31,7 +29,6 @@ public GyroSettings( float lowPassCutoffHz ) { this.lookSensitivity = lookSensitivity; - this.relativeMode = relativeMode; this.invertPitch = invertPitch; this.invertYaw = invertYaw; this.buttonMode = buttonMode; @@ -43,7 +40,6 @@ public GyroSettings( public static GyroSettings fromDTO(GyroConfig dto) { return new GyroSettings( dto.lookSensitivity(), - dto.relativeMode(), dto.invertPitch(), dto.invertYaw(), dto.buttonMode(), @@ -56,7 +52,6 @@ public static GyroSettings fromDTO(GyroConfig dto) { public GyroConfig toDTO() { return new GyroConfig( lookSensitivity, - relativeMode, invertPitch, invertYaw, buttonMode, diff --git a/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerConfigScreenFactory.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerConfigScreenFactory.java index 735c75fb2..b9d370ff3 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerConfigScreenFactory.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerConfigScreenFactory.java @@ -629,17 +629,7 @@ private Optional makeGyroGroup( o.requestSetDefault(); })) .build()); - var relativeModeOpt = Option.createBuilder() - .name(Component.translatable("controlify.gui.gyro_behaviour")) - .description(val -> OptionDescription.createBuilder() - .text(Component.translatable("controlify.gui.gyro_behaviour.tooltip")) - .text(val ? Component.translatable("controlify.gui.gyro_behaviour.relative.tooltip") : Component.translatable("controlify.gui.gyro_behaviour.absolute.tooltip")) - .build()) - .binding(defaults.gyro.relativeMode, () -> settings.gyro.relativeMode, v -> settings.gyro.relativeMode = v) - .controller(opt -> BooleanControllerBuilder.create(opt) - .formatValue(v -> v ? Component.translatable("controlify.gui.gyro_behaviour.relative") : Component.translatable("controlify.gui.gyro_behaviour.absolute"))) - .build(); - gyroGroup.option(relativeModeOpt); + gyroGroup.option(Util.make(() -> { var option = Option.createBuilder() .name(Component.translatable("controlify.gui.gyro_yaw_mode")) @@ -692,6 +682,21 @@ private Optional makeGyroGroup( gyroOptions.add(opt); return opt; })); + gyroGroup.option(Util.make(() -> { + var option = Option.createBuilder() + .name(Component.translatable("controlify.gui.gyro_low_pass_cutoff_hz")) + .description(OptionDescription.of(Component.translatable("controlify.gui.gyro_low_pass_cutoff_hz.tooltip"))) + .binding(defaults.gyro.lowPassCutoffHz, () -> settings.gyro.lowPassCutoffHz, v -> settings.gyro.lowPassCutoffHz = v) + .controller(opt -> FloatSliderControllerBuilder.create(opt) + .range(0f, 100f) + .step(1f) + .formatValue(v -> v == 0 ? CommonComponents.OPTION_OFF + : Component.translatable("controlify.gui.gyro_low_pass_cutoff_hz.value", v.intValue()))) + .available(gyroSensitivity.pendingValue() > 0) + .build(); + gyroOptions.add(option); + return option; + })); gyroGroup.option(Util.make(() -> { var opt = Option.createBuilder() .name(Component.translatable("controlify.gui.flick_stick")) diff --git a/src/main/java/dev/isxander/controlify/impl/ingame/InGameInputHandler.java b/src/main/java/dev/isxander/controlify/impl/ingame/InGameInputHandler.java index c8a3df8a3..7a5c57dc2 100644 --- a/src/main/java/dev/isxander/controlify/impl/ingame/InGameInputHandler.java +++ b/src/main/java/dev/isxander/controlify/impl/ingame/InGameInputHandler.java @@ -64,8 +64,6 @@ private Events() { private final Controlify controlify; private final Minecraft minecraft; - private double lookInputX, lookInputY; // in degrees per tick - private final Vector3f gyroInput = new Vector3f(); private boolean gyroToggledOn; private boolean wasAiming; private Animation flickAnimation; @@ -82,7 +80,6 @@ public InGameInputHandler(ControllerImpl controller) { public void fixedUpdate() { boolean isController = ControllerPlayerMovement.shouldBeControllerInput(); - handlePlayerLookInput(isController); ControllerPlayerMovement.ensureCorrectInput(minecraft.player); if (isController) { @@ -313,12 +310,10 @@ private boolean canSwitchGameMode() { && GameModeCommand.PERMISSION_CHECK.check(minecraft.player.permissions()); } - protected void handlePlayerLookInput(boolean isController) { + public void processPlayerLook(float deltaTime) { LocalPlayer player = this.minecraft.player; - if (!isController || !canProcessLookInput()) { - lookInputX = 0; - lookInputY = 0; + if (!ControllerPlayerMovement.shouldBeControllerInput() || !canProcessLookInput()) { return; } @@ -330,20 +325,22 @@ protected void handlePlayerLookInput(boolean isController) { if (controller.gyro().map(gyro -> gyro.settings().lookSensitivity > 0 && gyro.settings().flickStick).orElse(false)) { handleFlickStick(player); } else { - handleRegularLook(controller.input(), lookImpulse, aiming, player); + handleRegularLook(controller.input(), lookImpulse, aiming, player, deltaTime); } var modifier = new LookInputModifier(new Vector2f((float) lookImpulse.x, (float) lookImpulse.y), controller); Events.LOOK_INPUT_MODIFIER.emit(modifier); lookImpulse.set(modifier.lookInput()); - lookInputX = lookImpulse.x; - lookInputY = lookImpulse.y; + double velX = lookImpulse.x / 0.15; + double velY = lookImpulse.y / 0.15; + player.turn(velX, velY); + minecraft.getTutorial().onMouse(velX, velY); wasAiming = aiming; } - protected void handleRegularLook(InputComponentImpl input, Vector2d impulse, boolean aiming, LocalPlayer player) { + protected void handleRegularLook(InputComponentImpl input, Vector2d impulse, boolean aiming, LocalPlayer player, float deltaTime) { InputSettings settings = input.settings(); // normal look input @@ -391,12 +388,13 @@ protected void handleRegularLook(InputComponentImpl input, Vector2d impulse, boo regularImpulse.x *= settings.sensitivity.hLookSensitivity * 10f; regularImpulse.y *= settings.sensitivity.vLookSensitivity * 10f; - impulse.add(regularImpulse); + impulse.add(regularImpulse.mul(deltaTime)); } protected void handleGyroLook(GyroComponentImpl gyro, Vector2d impulse, boolean aiming) { GyroSettings settings = gyro.settings(); var gyroButton = ControlifyActions.GYRO_BUTTON.on(controller); + var gyroInput = new Vector3f(gyro.getState()); if (settings.buttonMode.equals(GyroButtonMode.ON) && (!gyroButton.latch().isLatchActive() && !aiming)) { gyroInput.set(0); @@ -404,12 +402,6 @@ protected void handleGyroLook(GyroComponentImpl gyro, Vector2d impulse, boolean gyroInput.set(0); } else if(settings.buttonMode.equals(GyroButtonMode.TOGGLE) && (!gyroToggledOn && !aiming)) { gyroInput.set(0); - } else { - if (settings.relativeMode) { - gyroInput.add(new Vector3f(gyro.getState()).mul(0.1f)); - } - else - gyroInput.set(gyro.getState()); } boolean gyroPressed = gyroButton.pulse().consumePulse(); @@ -417,10 +409,9 @@ protected void handleGyroLook(GyroComponentImpl gyro, Vector2d impulse, boolean gyroToggledOn = !gyroToggledOn; } - // convert radians per second into degrees per tick - var thisInput = new Vector3f(gyroInput) + // The latest gyro sample is already accumulated over this frame, in radians. + var thisInput = gyroInput .mul(Mth.RAD_TO_DEG) - .div(20) .mul(settings.lookSensitivity); impulse.y += -thisInput.x() * (settings.invertPitch ? -1 : 1); @@ -457,16 +448,6 @@ protected void handleFlickStick(LocalPlayer player) { .play(); } - public void processPlayerLook(float deltaTime) { - if (minecraft.player != null) { - double velX = lookInputX / 0.15 * deltaTime; - double velY = lookInputY / 0.15 * deltaTime; - //noinspection SuspiciousNameCombination - minecraft.player.turn(velX, velY); - minecraft.getTutorial().onMouse(velX, velY); - } - } - public boolean shouldShowPlayerList() { return this.shouldShowPlayerList; } diff --git a/src/main/java/dev/isxander/controlify/impl/utils/DebugOverlayHelper.java b/src/main/java/dev/isxander/controlify/impl/utils/DebugOverlayHelper.java index f4c7e06bc..e0b7c6b50 100644 --- a/src/main/java/dev/isxander/controlify/impl/utils/DebugOverlayHelper.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/DebugOverlayHelper.java @@ -16,10 +16,6 @@ public final class DebugOverlayHelper { private static final Minecraft mc = Minecraft.getInstance(); - public static boolean isOverlayEnabled() { - return mc.debugEntries.isOverlayVisible(); - } - public static void toggleOverlay() { mc.debugEntries.toggleDebugOverlay(); } diff --git a/src/main/resources/assets/controlify/controller/default_config.json b/src/main/resources/assets/controlify/controller/default_config.json index 53a32fae7..8f9fd38c0 100644 --- a/src/main/resources/assets/controlify/controller/default_config.json +++ b/src/main/resources/assets/controlify/controller/default_config.json @@ -67,7 +67,6 @@ }, "gyro": { "look_sensitivity": 0.0, - "relative_mode": false, "invert_pitch": false, "invert_yaw": false, "button_mode": "on", diff --git a/src/main/resources/assets/controlify/lang/en_us.json b/src/main/resources/assets/controlify/lang/en_us.json index 308460358..499560078 100644 --- a/src/main/resources/assets/controlify/lang/en_us.json +++ b/src/main/resources/assets/controlify/lang/en_us.json @@ -163,12 +163,9 @@ "controlify.gui.group.gyro.no_gyro_steamdeck.tooltip": "Steam Deck's gyroscope does not currently work with Controlify. For now, use Steam Input to emulate gyro to work as a mouse.", "controlify.gui.gyro_look_sensitivity": "Look Sensitivity", "controlify.gui.gyro_look_sensitivity.tooltip": "The percentage of the real-world rotation of your controller to use as look input. For example, rotating your controller 90 degrees, will move the camera 90 degrees, at 100%.\nThe pitch of your controller is used as up/down and the roll and yaw is used for left/right.\nThis sensitivity is completely independent of the regular horizontal and vertical sensitivity settings.", - "controlify.gui.gyro_behaviour": "Gyro Behaviour", - "controlify.gui.gyro_behaviour.tooltip": "How the gyroscope input should be interpreted as look input.", - "controlify.gui.gyro_behaviour.absolute": "Absolute", - "controlify.gui.gyro_behaviour.absolute.tooltip": "Absolute: Equivalent to moving the mouse in a direction.", - "controlify.gui.gyro_behaviour.relative": "Relative", - "controlify.gui.gyro_behaviour.relative.tooltip": "Relative: Equivalent to moving a thumbstick in a direction.", + "controlify.gui.gyro_low_pass_cutoff_hz": "Low-Pass Cutoff", + "controlify.gui.gyro_low_pass_cutoff_hz.tooltip": "Smooths gyro movement to reduce jitter. Lower cutoff frequencies provide more smoothing but add delay. Set to Off to disable filtering.", + "controlify.gui.gyro_low_pass_cutoff_hz.value": "%s Hz", "controlify.gui.gyro_yaw_mode": "Yaw Mode", "controlify.gui.gyro_yaw_mode.tooltip": "Determine how the gyro should move the left/right axis.", "controlify.gui.gyro_yaw_mode.tooltip.yaw_only": "Use the gyroscope's yaw value: rotate the controller left/right, whilst not tilting the controller.", diff --git a/src/main/resources/assets/controlify/lang/es_es.json b/src/main/resources/assets/controlify/lang/es_es.json index 5e420c50f..967937286 100644 --- a/src/main/resources/assets/controlify/lang/es_es.json +++ b/src/main/resources/assets/controlify/lang/es_es.json @@ -120,12 +120,6 @@ "controlify.gui.group.gyro.no_gyro_steamdeck.tooltip": "El giroscopio de Steam Deck no funciona actualmente con Controlify. Por ahora, usa Steam Input para emular el giroscopio para que funcione como un ratón.", "controlify.gui.gyro_look_sensitivity": "Sensibilidad de vista", "controlify.gui.gyro_look_sensitivity.tooltip": "El porcentaje de la rotación del mundo real de tu mando para usar como entrada de vista. Por ejemplo, rotar tu mando 90 grados, moverá la cámara 90 grados, al 100%.\nEl cabeceo de tu mando se usa como arriba/abajo y el alabeo y la guiñada se usan como izquierda/derecha.\nEsta sensibilidad es completamente independiente de los ajustes regulares de sensibilidad horizontal y vertical.", - "controlify.gui.gyro_behaviour": "Comportamiento del giroscopio", - "controlify.gui.gyro_behaviour.tooltip": "Cómo debería interpretarse la entrada del giroscopio como entrada de vista.", - "controlify.gui.gyro_behaviour.absolute": "Absoluto", - "controlify.gui.gyro_behaviour.absolute.tooltip": "Absoluto: Equivalente a mover el ratón en una dirección.", - "controlify.gui.gyro_behaviour.relative": "Relativo", - "controlify.gui.gyro_behaviour.relative.tooltip": "Relativo: Equivalente a mover un stick en una dirección.", "controlify.gui.gyro_yaw_mode": "Modo de guiñada", "controlify.gui.gyro_yaw_mode.tooltip": "Determina cómo el giroscopio debería mover el eje izquierda/derecha.", "controlify.gui.gyro_yaw_mode.tooltip.yaw_only": "Usar el valor de guiñada del giroscopio: rotar el mando izquierda/derecha, sin inclinar el mando.", diff --git a/src/main/resources/assets/controlify/lang/es_mx.json b/src/main/resources/assets/controlify/lang/es_mx.json index adb8fc78e..f8c083485 100644 --- a/src/main/resources/assets/controlify/lang/es_mx.json +++ b/src/main/resources/assets/controlify/lang/es_mx.json @@ -120,12 +120,6 @@ "controlify.gui.group.gyro.no_gyro_steamdeck.tooltip": "El giroscopio de Steam Deck no funciona actualmente con Controlify. Por ahora, utiliza Steam Input para emular el giroscopio y que funcione como un ratón.", "controlify.gui.gyro_look_sensitivity": "Sensibilidad de Giroscopio", "controlify.gui.gyro_look_sensitivity.tooltip": "El porcentaje de la rotación del control en el mundo real que se utiliza como entrada de mira. Por ejemplo, al girar el control 90 grados, la cámara se moverá 90 grados, al 100%.\nEl cabeceo del control se utiliza para arriba/abajo y el balanceo y guiñada se utilizan para izquierda/derecha.\nEsta sensibilidad es completamente independiente de la sensibilidad regular horizontal y vertical.", - "controlify.gui.gyro_behaviour": "Comportamiento del Giroscopio", - "controlify.gui.gyro_behaviour.tooltip": "Cómo se debe interpretar la entrada del giroscopio como entrada de mira.", - "controlify.gui.gyro_behaviour.absolute": "Absoluto", - "controlify.gui.gyro_behaviour.absolute.tooltip": "Absoluto: Equivalente a mover el ratón en una dirección.", - "controlify.gui.gyro_behaviour.relative": "Relativo", - "controlify.gui.gyro_behaviour.relative.tooltip": "Relativo: Equivalente a mover un joystick en una dirección.", "controlify.gui.gyro_yaw_mode": "Modo de desvio", "controlify.gui.gyro_yaw_mode.tooltip": "Determina cómo el giroscopio debe mover el eje izquierda/derecha.", "controlify.gui.gyro_yaw_mode.tooltip.yaw_only": "Utiliza el valor de desvio: rota el control izquierda/derecha sin inclinarlo.", diff --git a/src/main/resources/assets/controlify/lang/fr_fr.json b/src/main/resources/assets/controlify/lang/fr_fr.json index 46b037614..ff513a14d 100644 --- a/src/main/resources/assets/controlify/lang/fr_fr.json +++ b/src/main/resources/assets/controlify/lang/fr_fr.json @@ -105,12 +105,6 @@ "controlify.gui.group.gyro.no_gyro_steamdeck.tooltip": "Le gyroscope du Steam Deck ne fonctionne actuellement pas avec Controlify. Pour l'instant, utilisez Steam Input pour émuler le gyroscope et l'utiliser comme une souris.", "controlify.gui.gyro_look_sensitivity": "Sensibilité de la vision gyroscopique", "controlify.gui.gyro_look_sensitivity.tooltip": "Le pourcentage de l’orientation de votre manette transmis à l’orientation de votre regard en jeu. Par exemple, à 100%, une rotation de votre manette de 90 degrés déplacera la caméra de 90 degrés.\nLe tangage de votre manette est utilisé pour l’axe haut/bas et le roulis et le lacet sont utilisés pour l’axe gauche/droite.\nCette sensibilité est complètement indépendante des paramètres de sensibilité horizontale et verticale habituels.", - "controlify.gui.gyro_behaviour": "Comportement du gyroscope", - "controlify.gui.gyro_behaviour.tooltip": "Comment l'entrée du gyroscope doit être interprétée comme entrée de vision.", - "controlify.gui.gyro_behaviour.absolute": "Absolu", - "controlify.gui.gyro_behaviour.absolute.tooltip": "Absolu : Équivalent à déplacer la souris dans une direction.", - "controlify.gui.gyro_behaviour.relative": "Relatif", - "controlify.gui.gyro_behaviour.relative.tooltip": "Relatif : Équivalent à déplacer un joystick dans une direction.", "controlify.gui.gyro_yaw_mode": "Mode de rotation", "controlify.gui.gyro_yaw_mode.tooltip": "Détermine comment le gyroscope doit déplacer l'axe gauche/droite.", "controlify.gui.gyro_yaw_mode.tooltip.yaw_only": "Utilise la valeur de la rotation du gyroscope : pivote le contrôleur de gauche à droite, sans incliner le contrôleur.", diff --git a/src/main/resources/assets/controlify/lang/hu_hu.json b/src/main/resources/assets/controlify/lang/hu_hu.json index 00c90263a..d3c5ba528 100644 --- a/src/main/resources/assets/controlify/lang/hu_hu.json +++ b/src/main/resources/assets/controlify/lang/hu_hu.json @@ -94,10 +94,6 @@ "controlify.gui.group.gyro": "Giroszkóp", "controlify.gui.group.gyro.no_gyro.tooltip": "Ez a kontroller nem támogatja a giroszkópot. Egy DualSense™ vagy más kompatibilis kontrollerre van szükséged a funkció használatához.", "controlify.gui.group.gyro.no_gyro_steamdeck.tooltip": "A Steam Deck giroszkópja jelenleg nem működik a Controlify-al. Egyenlőre használd a Steam Bevitelt az egér giroszkóppal való emulálásához.", - "controlify.gui.gyro_behaviour.absolute": "Abszolút", - "controlify.gui.gyro_behaviour.absolute.tooltip": "Abszolút: Egyenlő az egér mozgatásával egy irányban.", - "controlify.gui.gyro_behaviour.relative": "Relatív", - "controlify.gui.gyro_behaviour.relative.tooltip": "Relatív: Egyenlő az analóg kar mozgatásával egy irányban.", "controlify.gui.gyro_yaw_mode.tooltip": "Határozd meg hogy a giroszkóp hogyan mozgatja a jobb és bal tengelyt.", "controlify.gui.gyro_invert_x": "X Megfordítása", "controlify.gui.gyro_invert_y": "Y Megfordítása", diff --git a/src/main/resources/assets/controlify/lang/it_it.json b/src/main/resources/assets/controlify/lang/it_it.json index 4660c5249..8726d708e 100644 --- a/src/main/resources/assets/controlify/lang/it_it.json +++ b/src/main/resources/assets/controlify/lang/it_it.json @@ -110,12 +110,6 @@ "controlify.gui.group.gyro.no_gyro_steamdeck.tooltip": "Il giroscopio dello Steam Deck non funziona attualmente con Controlify. Per ora, usa Steam Input per emulare il mouse usando il giroscopio.", "controlify.gui.gyro_look_sensitivity": "Sensibilità Vista", "controlify.gui.gyro_look_sensitivity.tooltip": "La percentuale della rotazione reale del controller da usare come input di vista. Per esempio, ruotando il tuo controller di 90 gradi muoverai la telecamera di 90 gradi, al 100%.\nLa rotazione verticale del tuo controller sarà usata come su/giù, mentre la rotazione laterale e orizzontale saranno usate per sinistra/destra.\nQuesta sensibilità è completamente indipendente dalle normali impostazioni di sensibilità della vista orizzontale e verticale.", - "controlify.gui.gyro_behaviour": "Comportamento Giroscopio", - "controlify.gui.gyro_behaviour.tooltip": "Come l'input del giroscopio deve essere interpretato in quanto a input della vista.", - "controlify.gui.gyro_behaviour.absolute": "Assoluto", - "controlify.gui.gyro_behaviour.absolute.tooltip": "Assoluto: Equivalente a muovere il mouse in una direzione.", - "controlify.gui.gyro_behaviour.relative": "Relativo", - "controlify.gui.gyro_behaviour.relative.tooltip": "Relativo: Equivalente a muovere un joystick in una direzione.", "controlify.gui.gyro_yaw_mode": "Modalità Rotazione", "controlify.gui.gyro_yaw_mode.tooltip": "Determina come il giroscopio deve trattare l'asse sinistro/destro.", "controlify.gui.gyro_yaw_mode.tooltip.yaw_only": "Usa il valore di \"Imbardata\" del giroscopio: Ruotando il controller a sinistra/destra, senza valutare l'inclinazione laterale del controller.", diff --git a/src/main/resources/assets/controlify/lang/ja_jp.json b/src/main/resources/assets/controlify/lang/ja_jp.json index a6cf47026..96dd787be 100644 --- a/src/main/resources/assets/controlify/lang/ja_jp.json +++ b/src/main/resources/assets/controlify/lang/ja_jp.json @@ -115,12 +115,6 @@ "controlify.gui.group.gyro.no_gyro_steamdeck.tooltip": "Steam Deckのジャイロは現在、Conrolifyでは動作しません。現時点では、Steam入力を使用してジャイロをエミュレートしてマウスとして動作します", "controlify.gui.gyro_look_sensitivity": "視点感度", "controlify.gui.gyro_look_sensitivity.tooltip": "ジャイロ入力として使用するコントローラーの現実世界の回転の割合。たとえば、コントローラーを 90 度回転させると、視点感度 100% で 90 度動きます。\\nコントローラーのピッチはアップ/ダウンとして使用され、ロールとヨーは左/右として使用されます。この感度は、通常の水平および垂直の感度設定とはまったく独立しています", - "controlify.gui.gyro_behaviour": "ジャイロの動作", - "controlify.gui.gyro_behaviour.tooltip": "ジャイロスコープ入力を視点操作時にどのように解釈するか", - "controlify.gui.gyro_behaviour.absolute": "絶対的", - "controlify.gui.gyro_behaviour.absolute.tooltip": "絶対:マウスをある方向に動かすのと同じです", - "controlify.gui.gyro_behaviour.relative": "相対的", - "controlify.gui.gyro_behaviour.relative.tooltip": "相対:サムスティックをある方向に動かすことに相当します", "controlify.gui.gyro_yaw_mode": "ヨーモード", "controlify.gui.gyro_yaw_mode.tooltip": "ジャイロが左/右の軸をどのように動かすかを決定します", "controlify.gui.gyro_yaw_mode.tooltip.yaw_only": "ジャイロスコープのヨー値を使用:コントローラーを傾けずに、コントローラーを左右に回転させます", diff --git a/src/main/resources/assets/controlify/lang/ms_my.json b/src/main/resources/assets/controlify/lang/ms_my.json index 19b092d35..be8e4cc91 100644 --- a/src/main/resources/assets/controlify/lang/ms_my.json +++ b/src/main/resources/assets/controlify/lang/ms_my.json @@ -112,12 +112,6 @@ "controlify.gui.group.gyro.no_gyro_steamdeck.tooltip": "Giroskop Steam Deck tidak berfungsi dengan Controlify pada masa ini. Buat masa ini, gunakan Steam Input untuk meniru giro untuk berfungsi sebagai tetikus.", "controlify.gui.gyro_look_sensitivity": "Sensitiviti Penglihatan", "controlify.gui.gyro_look_sensitivity.tooltip": "Peratusan putaran dunia sebenar pengawal anda untuk digunakan sebagai input melihat. Sebagai contoh, memutarkan pengawal anda 90 darjah akan menggerakkan kamera 90 darjah pada tetapan 100%.\nAnggul pengawal anda digunakan sebagai pergerakan atas/bawah manakala gulingan dan rewang pengawal anda digunakan sebagai pergerakan kiri/kanan.\nSensitiviti ini adalah bebas sepenuhnya daripada tetapan sensitiviti mendatar dan menegak biasa.", - "controlify.gui.gyro_behaviour": "Perilaku Giro", - "controlify.gui.gyro_behaviour.tooltip": "Bagaimana input giroskop harus ditafsirkan sebagai input penglihatan.", - "controlify.gui.gyro_behaviour.absolute": "Mutlak", - "controlify.gui.gyro_behaviour.absolute.tooltip": "Mutlak: Setara dengan menggerakkan tetikus ke suatu arah.", - "controlify.gui.gyro_behaviour.relative": "Relatif", - "controlify.gui.gyro_behaviour.relative.tooltip": "Relatif: Setara dengan menggerakkan kayu bedil ke suatu arah.", "controlify.gui.gyro_yaw_mode": "Mod Rewang", "controlify.gui.gyro_yaw_mode.tooltip": "Tentukan bagaimana giro harus menggerakkan paksi kiri/kanan.", "controlify.gui.gyro_yaw_mode.tooltip.yaw_only": "Gunakan nilai rewang giroskop: putar pengawal ke kiri/kanan, sambil tidak menyengetkan pengawal.", diff --git a/src/main/resources/assets/controlify/lang/pl_pl.json b/src/main/resources/assets/controlify/lang/pl_pl.json index 95e9a8092..a1da81723 100644 --- a/src/main/resources/assets/controlify/lang/pl_pl.json +++ b/src/main/resources/assets/controlify/lang/pl_pl.json @@ -112,12 +112,6 @@ "controlify.gui.group.gyro.no_gyro_steamdeck.tooltip": "Żyroskop Steam Decka obecnie nie działa z Controlify. Na razie użyj Steam Input, aby emulować żyroskop, aby działał jak mysz.", "controlify.gui.gyro_look_sensitivity": "Czułość widzenia", "controlify.gui.gyro_look_sensitivity.tooltip": "Procent rzeczywistego obrotu twojego kontrolera używanego jako wejście do patrzenia. Na przykład, obracając kontroler o 90 stopni, przesuniesz kamerę o 90 stopni przy 100%.\nKąt nachylenia kontrolera jest używany jako ruch w górę/w dół, a obrót i przechylenie są używane do ruchu w lewo/prawo.\nTa czułość jest całkowicie niezależna od regularnych ustawień czułości poziomej i pionowej.", - "controlify.gui.gyro_behaviour": "Zachowanie żyroskopu", - "controlify.gui.gyro_behaviour.tooltip": "Jak wejście z żyroskopu powinno być interpretowane jako wejście do patrzenia.", - "controlify.gui.gyro_behaviour.absolute": "Absolutne", - "controlify.gui.gyro_behaviour.absolute.tooltip": "Absolutne: Równoznaczne z przesuwaniem myszy w danym kierunku.", - "controlify.gui.gyro_behaviour.relative": "Relatywne", - "controlify.gui.gyro_behaviour.relative.tooltip": "Relatywne: Równoznaczne z przesuwaniem drążka w danym kierunku.", "controlify.gui.gyro_yaw_mode": "Tryb Yaw", "controlify.gui.gyro_yaw_mode.tooltip": "Określ, jak żyroskop powinien poruszać osią lewo/prawo.", "controlify.gui.gyro_yaw_mode.tooltip.yaw_only": "Użyj wartości Yaw z żyroskopu: obracaj kontroler w lewo/prawo, nie przechylając kontrolera.", diff --git a/src/main/resources/assets/controlify/lang/pt_br.json b/src/main/resources/assets/controlify/lang/pt_br.json index 0c3c951a8..0d3463ce9 100644 --- a/src/main/resources/assets/controlify/lang/pt_br.json +++ b/src/main/resources/assets/controlify/lang/pt_br.json @@ -114,12 +114,6 @@ "controlify.gui.group.gyro.no_gyro_steamdeck.tooltip": "O giroscópio do Steam não funciona com Controle. Por enquanto, use a entrada Steam para emular o giroscópio para funcionar como um mouse.", "controlify.gui.gyro_look_sensitivity": "Sensibilidade Visual", "controlify.gui.gyro_look_sensitivity.tooltip": "A porcentagem de rotação do mundo real de seu controle para usar como entrada de aparência. Por exemplo, girar o controlador 90 graus, moverá a câmera 90 graus, em 100%.\nO ajuste de seu controle é usado como para cima/baixo e a rolagem e guinada são usados para esquerda/direita.\nEsta sensibilidade é completamente independente das configurações regulares de sensibilidade horizontal e vertical.", - "controlify.gui.gyro_behaviour": "Comportamento do Giroscópio", - "controlify.gui.gyro_behaviour.tooltip": "Como a entrada do giroscópio deve ser interpretada como uma entrada.", - "controlify.gui.gyro_behaviour.absolute": "Absoluto", - "controlify.gui.gyro_behaviour.absolute.tooltip": "Absoluto: Equivalente a mover o mouse em uma direção.", - "controlify.gui.gyro_behaviour.relative": "Relativo", - "controlify.gui.gyro_behaviour.relative.tooltip": "Relativo: Equivalente a mover um analógico em uma direção.", "controlify.gui.gyro_yaw_mode": "Modo Guinada", "controlify.gui.gyro_yaw_mode.tooltip": "Determine como o giroscópio deve mover o eixo esquerda/direita.", "controlify.gui.gyro_yaw_mode.tooltip.yaw_only": "Use o valor de guinada do giroscópio: gire o controle para a esquerda/direita, sem incliná-lo.", diff --git a/src/main/resources/assets/controlify/lang/pt_pt.json b/src/main/resources/assets/controlify/lang/pt_pt.json index 342e46291..41bade0fb 100644 --- a/src/main/resources/assets/controlify/lang/pt_pt.json +++ b/src/main/resources/assets/controlify/lang/pt_pt.json @@ -110,12 +110,6 @@ "controlify.gui.group.gyro.no_gyro_steamdeck.tooltip": "O giroscópio do Steam Deck não funciona atualmente com o Controlify. Por enquanto, utilize o Steam Input para emular o giroscópio a funcionar como um rato.", "controlify.gui.gyro_look_sensitivity": "Sensibilidade da Câmara", "controlify.gui.gyro_look_sensitivity.tooltip": "A percentagem da rotação real do seu comando a utilizar como entrada de câmara. Por exemplo, ao rodar o seu comando 90 graus, a câmara mover-se-á 90 graus, a 100%. A inclinação do seu comando é utilizada para cima/baixo, enquanto a rotação lateral e a guinada são utilizadas para esquerda/direita. Esta sensibilidade é completamente independente das definições de sensibilidade horizontal e vertical regulares.", - "controlify.gui.gyro_behaviour": "Comportamento de Giroscópio", - "controlify.gui.gyro_behaviour.tooltip": "Como a entrada do giroscópio deve ser interpretada como a câmara.", - "controlify.gui.gyro_behaviour.absolute": "Absoluto", - "controlify.gui.gyro_behaviour.absolute.tooltip": "Absoluto: Equivalente a mover o rato numa direção.", - "controlify.gui.gyro_behaviour.relative": "Relativo", - "controlify.gui.gyro_behaviour.relative.tooltip": "Relativo: Equivalente a mover um joystick numa direção.", "controlify.gui.gyro_yaw_mode": "Modo Guinada", "controlify.gui.gyro_yaw_mode.tooltip": "Determine como o giroscópio deve mover o eixo esquerda/direita.", "controlify.gui.gyro_yaw_mode.tooltip.yaw_only": "Utilize o valor de guinada do giroscópio: rode o comando para a esquerda/direita, sem o inclinar.", diff --git a/src/main/resources/assets/controlify/lang/ro_ro.json b/src/main/resources/assets/controlify/lang/ro_ro.json index 0a07d5eee..5cae287c4 100644 --- a/src/main/resources/assets/controlify/lang/ro_ro.json +++ b/src/main/resources/assets/controlify/lang/ro_ro.json @@ -21,8 +21,6 @@ "controlify.config.category.advanced": "Avansat", "controlify.gui.group.vibration": "Vibrație", "controlify.gui.allow_vibrations": "Permite Vibrarea", - "controlify.gui.gyro_behaviour.absolute": "Absolut", - "controlify.gui.gyro_behaviour.relative": "Relativ", "controlify.gui.group.advanced": "Avansat", "controlify.gui.test_vibration": "Testare Vibrație", "controlify.gui.radial_menu": "Meniu Radial", diff --git a/src/main/resources/assets/controlify/lang/ru_ru.json b/src/main/resources/assets/controlify/lang/ru_ru.json index e0df19e16..1292cdd21 100644 --- a/src/main/resources/assets/controlify/lang/ru_ru.json +++ b/src/main/resources/assets/controlify/lang/ru_ru.json @@ -98,12 +98,6 @@ "controlify.gui.group.gyro.no_gyro_steamdeck.tooltip": "Гироскоп Steam Deck в настоящее время не работает с Controlify. Пока что используйте Steam Input для эмуляции гироскопа в качестве мыши.", "controlify.gui.gyro_look_sensitivity": "Чувствительность обзора", "controlify.gui.gyro_look_sensitivity.tooltip": "Процент реального вращения контроллера, используемого для обзора. Например, при 100% при повороте контроллера на 90 градусов камера переместится на 90 градусов.\nУгол тангажа вашего контроллера используется как вверх/вниз, а крен и рысканье - как влево/вправо.\nЭта чувствительность совершенно не зависит от обычных настроек чувствительности по горизонтали и вертикали.", - "controlify.gui.gyro_behaviour": "Поведение гироскопа", - "controlify.gui.gyro_behaviour.tooltip": "Как интерпретировать данные гироскопа в качестве ввода обзора.", - "controlify.gui.gyro_behaviour.absolute": "Абсолютное", - "controlify.gui.gyro_behaviour.absolute.tooltip": "Абсолютное: эквивалентно перемещению мыши в определённом направлении.", - "controlify.gui.gyro_behaviour.relative": "Относительное", - "controlify.gui.gyro_behaviour.relative.tooltip": "Относительное: Эквивалентно перемещению стика в определённом направлении.", "controlify.gui.gyro_yaw_mode.tooltip": "Определяет, как гироскоп должен двигаться по левой/правой стороной.", "controlify.gui.gyro_invert_x": "Обратить X", "controlify.gui.gyro_invert_x.tooltip": "Инверсия направления обзора гироскопа при вращении влево/вправо.", diff --git a/src/main/resources/assets/controlify/lang/tr_tr.json b/src/main/resources/assets/controlify/lang/tr_tr.json index e6356a1fb..19f5b87a6 100644 --- a/src/main/resources/assets/controlify/lang/tr_tr.json +++ b/src/main/resources/assets/controlify/lang/tr_tr.json @@ -109,12 +109,6 @@ "controlify.gui.group.gyro.no_gyro_steamdeck.tooltip": "Steam Deck'in jiroskopu şu anda Controlify ile çalışmıyor. Şimdilik, jiroskopu fare gibi çalışacak şekilde taklit etmek için Steam Input'u kullanın.", "controlify.gui.gyro_look_sensitivity": "Bakış Hassasiyeti", "controlify.gui.gyro_look_sensitivity.tooltip": "Kontrolcünüzün gerçek dünyadaki dönüşünün, bakış girdisi olarak kullanılacak yüzdesi. Örneğin, kontrolcünüzü 90 derece döndürmek, kamerayı %100 oranında 90 derece hareket ettirecektir.\nKontrolcünüzün eğimi yukarı/aşağı olarak, yuvarlanma ve sapma ise sol/sağ için kullanılır.\nBu hassasiyet, normal yatay ve dikey hassasiyet ayarlarından tamamen bağımsızdır.", - "controlify.gui.gyro_behaviour": "Gyro Davranışı", - "controlify.gui.gyro_behaviour.tooltip": "Jiroskop girdisinin bakış girdisi olarak nasıl yorumlanması gerektiği.", - "controlify.gui.gyro_behaviour.absolute": "Mutlak", - "controlify.gui.gyro_behaviour.absolute.tooltip": "Mutlak: Fareyi bir yönde hareket ettirmeye eşdeğerdir.", - "controlify.gui.gyro_behaviour.relative": "Göreceli", - "controlify.gui.gyro_behaviour.relative.tooltip": "Göreceli: Kontrol çubuğunu bir yönde hareket ettirmeye eşdeğerdir.", "controlify.gui.gyro_yaw_mode.tooltip": "Gyro'nun sol/sağ ekseni nasıl hareket ettirmesi gerektiğini belirleyin.", "controlify.gui.gyro_yaw_mode.tooltip.roll_only": "Jiroskopun yuvarlanma değerini kullanın: kontrolcüyü bir yandan diğer yana eğin.", "controlify.gui.gyro_invert_x": "X'i Ters Çevir", diff --git a/src/main/resources/assets/controlify/lang/uk_ua.json b/src/main/resources/assets/controlify/lang/uk_ua.json index cc3fde5d5..1723002ff 100644 --- a/src/main/resources/assets/controlify/lang/uk_ua.json +++ b/src/main/resources/assets/controlify/lang/uk_ua.json @@ -111,12 +111,6 @@ "controlify.gui.group.gyro.no_gyro_steamdeck.tooltip": "Гіроскоп Steam Deck наразі не працює з Controlify. Ви можете використовувати Steam Input для емуляції гіроскопа в якости миші.", "controlify.gui.gyro_look_sensitivity": "Чутливість зору", "controlify.gui.gyro_look_sensitivity.tooltip": "Відсоток реального повороту вашого контролера, який буде використовуватися як вхідні дані для огляду. Наприклад, якщо повернути контролер на 90 градусів, камера повернеться на 90 градусів, тобто на 100%.\nНахил вашого контролера використовується як вгору/вниз, а крен і рискання — як вліво/вправо.\nЦя чутливість повністю незалежна від звичайних налаштувань горизонтальної та вертикальної чутливості.", - "controlify.gui.gyro_behaviour": "Поведінка гіроскопа", - "controlify.gui.gyro_behaviour.tooltip": "Як інтерпретувати дані гіроскопа як введення огляду.", - "controlify.gui.gyro_behaviour.absolute": "Абсолютне", - "controlify.gui.gyro_behaviour.absolute.tooltip": "Абсолютне: еквівалентно переміщенню миші в певному напрямку.", - "controlify.gui.gyro_behaviour.relative": "Відносне", - "controlify.gui.gyro_behaviour.relative.tooltip": "Відносне: еквівалентно переміщенню стіку в певному напрямку.", "controlify.gui.gyro_yaw_mode": "Режим рискання", "controlify.gui.gyro_yaw_mode.tooltip": "Визначте, як гіроскоп повинен рухатися по лівій/правій осі.", "controlify.gui.gyro_yaw_mode.tooltip.yaw_only": "Використовуйте значення рискання гіроскопа: повернути контролер вліво/вправо, не нахиляючи його при цьому.", diff --git a/src/main/resources/assets/controlify/lang/vi_vn.json b/src/main/resources/assets/controlify/lang/vi_vn.json index c3e3966ee..34ed3dfec 100644 --- a/src/main/resources/assets/controlify/lang/vi_vn.json +++ b/src/main/resources/assets/controlify/lang/vi_vn.json @@ -160,12 +160,6 @@ "controlify.gui.group.gyro.no_gyro_steamdeck.tooltip": "Con quay hồi chuyển của Steam Deck hiện không hoạt động với Controlify. Hiện tại, hãy sử dụng Steam Input để mô phỏng con quay hồi chuyển dưới dạng đầu vào chuột.", "controlify.gui.gyro_look_sensitivity": "Độ nhạy nhìn", "controlify.gui.gyro_look_sensitivity.tooltip": "Tỷ lệ phần trăm xoay trong thế giới thực của bộ điều khiển của bạn để sử dụng làm đầu vào giao diện. Ví dụ: xoay bộ điều khiển của bạn 90 độ, camera sẽ di chuyển 90 độ, ở mức 100%.\nĐộ cao của bộ điều khiển được sử dụng làm hướng lên/xuống, đồng thời Roll và Yaw được sử dụng cho trái/phải.\nĐộ nhạy này hoàn toàn độc lập của cài đặt độ nhạy ngang và dọc thông thường.", - "controlify.gui.gyro_behaviour": "Hành vi con quay hồi chuyển", - "controlify.gui.gyro_behaviour.tooltip": "Làm thế nào đầu vào con quay hồi chuyển nên được hiểu là đầu vào nhìn.", - "controlify.gui.gyro_behaviour.absolute": "Tuyệt đối", - "controlify.gui.gyro_behaviour.absolute.tooltip": "Tuyệt đối: Tương đương với việc di chuyển chuột theo một hướng.", - "controlify.gui.gyro_behaviour.relative": "Tương đối", - "controlify.gui.gyro_behaviour.relative.tooltip": "Tương đối: Tương đương với việc di chuyển cần điều khiển theo một hướng.", "controlify.gui.gyro_yaw_mode": "Chế độ xoay trái phải", "controlify.gui.gyro_yaw_mode.tooltip": "Chọn cách con quay hồi chuyển di chuyển trục trái/phải.", "controlify.gui.gyro_yaw_mode.tooltip.yaw_only": "Sử dụng giá trị xoay trái phải của con quay hồi chuyển: xoay tay cầm trái phải, không nghiêng tay cầm.", diff --git a/src/main/resources/assets/controlify/lang/zh_cn.json b/src/main/resources/assets/controlify/lang/zh_cn.json index 795a92a94..a60aacdb9 100644 --- a/src/main/resources/assets/controlify/lang/zh_cn.json +++ b/src/main/resources/assets/controlify/lang/zh_cn.json @@ -160,12 +160,6 @@ "controlify.gui.group.gyro.no_gyro_steamdeck.tooltip": "Steam Deck的陀螺仪目前无法与Controlify一起使用。对于现在,需使用Steam输入来模拟陀螺仪以充当鼠标。", "controlify.gui.gyro_look_sensitivity": "视觉灵敏度", "controlify.gui.gyro_look_sensitivity.tooltip": "基于陀螺仪的旋转,视角移动的程度。", - "controlify.gui.gyro_behaviour": "陀螺仪行为", - "controlify.gui.gyro_behaviour.tooltip": "陀螺仪的行为输入将如何转换为视觉输入。", - "controlify.gui.gyro_behaviour.absolute": "绝对", - "controlify.gui.gyro_behaviour.absolute.tooltip": "绝对:相当于在一个方向上移动鼠标。", - "controlify.gui.gyro_behaviour.relative": "相对", - "controlify.gui.gyro_behaviour.relative.tooltip": "相对的: 相当于在一个方向上移动一个类比摇杆。", "controlify.gui.gyro_yaw_mode": "偏航模式", "controlify.gui.gyro_yaw_mode.tooltip": "确定陀螺仪应如何沿左右轴移动。", "controlify.gui.gyro_yaw_mode.tooltip.yaw_only": "使用陀螺仪的偏航值:左右旋转控制器,同时不倾斜控制器。", diff --git a/src/main/resources/assets/controlify/lang/zh_tw.json b/src/main/resources/assets/controlify/lang/zh_tw.json index 742b5ccc4..eea827a81 100644 --- a/src/main/resources/assets/controlify/lang/zh_tw.json +++ b/src/main/resources/assets/controlify/lang/zh_tw.json @@ -115,12 +115,6 @@ "controlify.gui.group.gyro.no_gyro_steamdeck.tooltip": "Steam Deck 的陀螺儀目前無法與 Controlify 配合使用。目前,請使用 Steam Input 模擬陀螺儀以作為滑鼠操作。", "controlify.gui.gyro_look_sensitivity": "方向靈敏度", "controlify.gui.gyro_look_sensitivity.tooltip": "將控制器的真實旋轉百分比用作方向輸入。例如,將控制器旋轉 90 度,將使相機移動 90 度,百分之百。\n控制器的俯仰用於上和下,橫滾和偏航用於左和右。\n此敏感度完全獨立於常規水平和垂直敏感度設定。", - "controlify.gui.gyro_behaviour": "陀螺儀行為", - "controlify.gui.gyro_behaviour.tooltip": "陀螺儀輸入應該如何被解釋為方向輸入。", - "controlify.gui.gyro_behaviour.absolute": "絕對", - "controlify.gui.gyro_behaviour.absolute.tooltip": "絕對:相當於在某個方向上移動滑鼠。", - "controlify.gui.gyro_behaviour.relative": "相對", - "controlify.gui.gyro_behaviour.relative.tooltip": "相對:相當於在某個方向上移動搖桿。", "controlify.gui.gyro_yaw_mode": "偏航模式", "controlify.gui.gyro_yaw_mode.tooltip": "決定陀螺儀應該如何移動左右軸。", "controlify.gui.gyro_yaw_mode.tooltip.yaw_only": "使用陀螺儀的偏航值:左右旋轉控制器,同時不傾斜控制器。", diff --git a/src/main/resources/resourcepacks/legacy_console/assets/controlify/controllers/default_bind/default.json b/src/main/resources/resourcepacks/legacy_console/assets/controlify/controller/default_bind/default.json similarity index 100% rename from src/main/resources/resourcepacks/legacy_console/assets/controlify/controllers/default_bind/default.json rename to src/main/resources/resourcepacks/legacy_console/assets/controlify/controller/default_bind/default.json diff --git a/src/main/resources/resourcepacks/legacy_console/assets/controlify/controllers/default_bind/dualsense.json b/src/main/resources/resourcepacks/legacy_console/assets/controlify/controller/default_bind/dualsense.json similarity index 100% rename from src/main/resources/resourcepacks/legacy_console/assets/controlify/controllers/default_bind/dualsense.json rename to src/main/resources/resourcepacks/legacy_console/assets/controlify/controller/default_bind/dualsense.json diff --git a/src/main/resources/resourcepacks/legacy_console/assets/controlify/controllers/default_bind/dualshock4.json b/src/main/resources/resourcepacks/legacy_console/assets/controlify/controller/default_bind/dualshock4.json similarity index 100% rename from src/main/resources/resourcepacks/legacy_console/assets/controlify/controllers/default_bind/dualshock4.json rename to src/main/resources/resourcepacks/legacy_console/assets/controlify/controller/default_bind/dualshock4.json diff --git a/src/main/resources/resourcepacks/legacy_console/assets/controlify/controllers/default_config.json b/src/main/resources/resourcepacks/legacy_console/assets/controlify/controller/default_config.json similarity index 100% rename from src/main/resources/resourcepacks/legacy_console/assets/controlify/controllers/default_config.json rename to src/main/resources/resourcepacks/legacy_console/assets/controlify/controller/default_config.json From 09a185b4f04d49c3e3a6ba31621ef60a12d21224 Mon Sep 17 00:00:00 2001 From: isxander Date: Thu, 22 Oct 2026 03:47:11 +0100 Subject: [PATCH 18/44] add unit test for deadzone stage --- .../input/pipeline/DeadzoneStageTest.java | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/test/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStageTest.java diff --git a/src/test/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStageTest.java b/src/test/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStageTest.java new file mode 100644 index 000000000..d0f22b778 --- /dev/null +++ b/src/test/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStageTest.java @@ -0,0 +1,81 @@ +package dev.isxander.controlify.impl.input.pipeline; + +import dev.isxander.controlify.impl.input.mapping.DeadzoneGroup; +import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; +import dev.isxander.controlify.impl.input.signal.PhysicalSignal; +import net.minecraft.resources.Identifier; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class DeadzoneStageTest { + private static final Identifier UP = id("axis/right_stick_up"); + private static final Identifier DOWN = id("axis/right_stick_down"); + private static final Identifier LEFT = id("axis/right_stick_left"); + private static final Identifier RIGHT = id("axis/right_stick_right"); + + @Test + void movementInsideRadialDeadzoneNeverActuatesButtons() { + var stage = stage(0.12f).andThen(new InputDomainExpansionStage(0.5f, 0.49f, 1f, 0f)); + List output = new ArrayList<>(); + + // Jitter in both dimensions, including releasing the previous half-axis on sign changes. + for (int i = 0; i < 100; i++) { + stage.onEvent(new PhysicalInputSignal.AxisMoved(i, UP, 0.10f), output::add); + stage.onEvent(new PhysicalInputSignal.AxisMoved(i, LEFT, 0.01f), output::add); + stage.onEvent(new PhysicalInputSignal.AxisMoved(i, UP, 0f), output::add); + stage.onEvent(new PhysicalInputSignal.AxisMoved(i, DOWN, 0.08f), output::add); + stage.onEvent(new PhysicalInputSignal.AxisMoved(i, LEFT, 0f), output::add); + stage.onEvent(new PhysicalInputSignal.AxisMoved(i, RIGHT, 0.02f), output::add); + stage.onEvent(new PhysicalInputSignal.AxisMoved(i, DOWN, 0f), output::add); + stage.onEvent(new PhysicalInputSignal.AxisMoved(i, RIGHT, 0f), output::add); + } + + assertTrue(output.isEmpty(), () -> "Unexpected output within deadzone: " + output); + } + + @Test + void returningInsideDeadzoneReleasesEveryActiveAxis() { + var stage = stage(0.12f); + List output = new ArrayList<>(); + stage.onEvent(new PhysicalInputSignal.AxisMoved(1, UP, 0.8f), output::add); + stage.onEvent(new PhysicalInputSignal.AxisMoved(2, LEFT, 0.3f), output::add); + stage.onEvent(new PhysicalInputSignal.AxisMoved(3, LEFT, 0.01f), output::add); + output.clear(); + + stage.onEvent(new PhysicalInputSignal.AxisMoved(4, UP, 0.1f), output::add); + + assertEquals(List.of( + new PhysicalInputSignal.AxisMoved(4, UP, 0f), + new PhysicalInputSignal.AxisMoved(4, LEFT, 0f) + ), output); + } + + @Test + void rescalesRadialMagnitudeAndPreservesDirection() { + var stage = stage(0.2f); + List output = new ArrayList<>(); + stage.onEvent(new PhysicalInputSignal.AxisMoved(1, UP, 0.3f), output::add); + output.clear(); + stage.onEvent(new PhysicalInputSignal.AxisMoved(2, RIGHT, 0.4f), output::add); + + assertEquals(2, output.size()); + assertEquals(0.225f, ((PhysicalInputSignal.AxisMoved) output.get(0)).value(), 0.000001f); + assertEquals(0.3f, ((PhysicalInputSignal.AxisMoved) output.get(1)).value(), 0.000001f); + } + + private static DeadzoneStage stage(float threshold) { + var stage = new DeadzoneStage(Set.of(new DeadzoneGroup(id("right_stick"), List.of(UP, DOWN, LEFT, RIGHT)))); + stage.setDeadzoneSupplier(_ -> threshold); + return stage; + } + + private static Identifier id(String path) { + return Identifier.fromNamespaceAndPath("controlify", path); + } +} From 80dbabcdae904e9b96b1eea0e7c759e37c87e51b Mon Sep 17 00:00:00 2001 From: isxander Date: Thu, 22 Oct 2026 03:47:44 +0100 Subject: [PATCH 19/44] bump version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index f357c4d94..dea177773 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -mod.version=3.5.3 +mod.version=4.0.0-test.1 mod.id=controlify mod.name=Controlify mod.description=The most advanced controller mod for Minecraft. From ac82e561163df45983fb86f650e902ba88882acd Mon Sep 17 00:00:00 2001 From: isxander Date: Thu, 22 Oct 2026 04:03:54 +0100 Subject: [PATCH 20/44] fix game tests --- .../controller/ControlifyGameTestContext.java | 20 ++++- .../controller/TestControllerContext.java | 19 ++++- .../controller/VirtualControllerBuilder.java | 10 ++- ...java => SDLControllerBackendAccessor.java} | 6 +- .../mixin/SDLControllerBackendMixin.java | 41 ++++++++++ .../mixin/SDLControllerManagerMixin.java | 48 ------------ .../mixin/SDLEventHandlerAccessor.java | 2 +- .../gametest/tests/ButtonGuideTests.java | 45 ++++++----- .../tests/ResetControlifySettingsTests.java | 3 +- .../gametest/tests/RumbleTests.java | 6 ++ .../tests/VirtualControllerInputTests.java | 76 +++++++++++-------- .../resources/controlify_test.mixins.json | 4 +- src/gametest/resources/fabric.mod.json | 1 + .../contextual/trigger_effect/in_game.json | 0 .../contextual/trigger_effect/in_game.json | 0 .../contextual/trigger_effect/in_game.json | 0 .../controller/contextual/guide/in_game.json | 53 +++++++++++++ .../guide_override_test/pack.mcmeta | 9 +++ .../keyboard_layout/full/en_gb.json | 0 .../keyboard_layout/full/en_us.json | 0 .../api/contextual/GuideVerbosity.java | 3 +- .../impl/ingame/InGameInputHandler.java | 2 + .../mixins/core/InputConstantsMixin.java | 2 +- .../mixins/core/KeyboardHandlerMixin.java | 4 +- .../mixins/core/MouseHandlerMixin.java | 6 +- .../mixins/core/SDLEventHandlerMixin.java | 2 +- 26 files changed, 237 insertions(+), 125 deletions(-) rename src/gametest/java/dev/isxander/controlify/gametest/mixin/{SDLControllerManagerAccessor.java => SDLControllerBackendAccessor.java} (81%) create mode 100644 src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerBackendMixin.java delete mode 100644 src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerManagerMixin.java rename src/gametest/resources/resourcepacks/adaptive_trigger_high_priority/assets/controlify/{ => controller}/contextual/trigger_effect/in_game.json (100%) rename src/gametest/resources/resourcepacks/adaptive_trigger_low_priority/assets/controlify/{ => controller}/contextual/trigger_effect/in_game.json (100%) rename src/gametest/resources/resourcepacks/adaptive_trigger_server_tag/assets/controlify/{ => controller}/contextual/trigger_effect/in_game.json (100%) create mode 100644 src/gametest/resources/resourcepacks/guide_override_test/assets/controlify/controller/contextual/guide/in_game.json create mode 100644 src/gametest/resources/resourcepacks/guide_override_test/pack.mcmeta rename src/gametest/resources/resourcepacks/keyboard_layout_test/assets/controlify/{ => controller}/keyboard_layout/full/en_gb.json (100%) rename src/gametest/resources/resourcepacks/keyboard_layout_test/assets/controlify/{ => controller}/keyboard_layout/full/en_us.json (100%) diff --git a/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/ControlifyGameTestContext.java b/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/ControlifyGameTestContext.java index e9f14ca2b..9cdc3d477 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/ControlifyGameTestContext.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/ControlifyGameTestContext.java @@ -11,10 +11,9 @@ import dev.isxander.controlify.impl.config.settings.GlobalSettings; import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.controller.gyro.GyroState; import dev.isxander.controlify.impl.controllermanager.ControllerManager; import dev.isxander.controlify.gametest.mixin.ControlifySettingsAccessor; -import dev.isxander.controlify.gametest.mixin.SDLControllerManagerAccessor; +import dev.isxander.controlify.gametest.mixin.SDLControllerBackendAccessor; import dev.isxander.sdl.Sdl; import net.fabricmc.fabric.api.client.gametest.v1.context.ClientGameTestContext; import org.joml.Vector3f; @@ -29,7 +28,7 @@ public class ControlifyGameTestContext { public ControlifyGameTestContext(ClientGameTestContext context) { this.context = context; - this.sdl = ((SDLControllerManagerAccessor) Controlify.instance().getControllerManager().orElseThrow()).controlify_test$getSdl(); + this.sdl = ((SDLControllerBackendAccessor) Controlify.instance().getControllerCoordinator().orElseThrow().backend()).controlify_test$getSdl(); } public VirtualControllerBuilder virtualControllerBuilder() { @@ -64,7 +63,7 @@ public void resetSettings() { settings.putProfileSettings(activeProfileIndex, profileDefaults); var connectedControllers = controlify.getControllerManager() - .map(ControllerManager::getConnectedControllers) + .map(ControllerManager::getAllControllers) .orElseGet(List::of); var connectedUids = connectedControllers.stream() .map(ControllerImpl::uid) @@ -83,10 +82,23 @@ public void resetSettings() { device.mapping = null; controller.setSettings(profileDefaults); + controller.input().reloadActions(); + bindDefaultInputs(controller); } controlify.applyControllerSelection(false); config.markDirty(); }); } + + static void bindDefaultInputs(ControllerImpl controller) { + // New profiles can leave actions unbound; each virtual device test starts with its default layout. + controller.input().getAllActions().forEach(action -> { + action.setBoundInput(action.defaultInput()); + if (action.defaultInput() != null) { + controller.settings().input.bindings.bindings.put(action.spec().id(), action.defaultInput()); + } + }); + } + } diff --git a/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/TestControllerContext.java b/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/TestControllerContext.java index df83e1073..4ce2d4df5 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/TestControllerContext.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/TestControllerContext.java @@ -47,7 +47,7 @@ public TestControllerContext( public ControllerImpl getControllerImpl() { return Controlify.instance().getControllerManager().orElseThrow() - .getConnectedControllers().stream() + .getAllControllers().stream() .filter(c -> c.info().ucid().equals(new SDLUniqueControllerID(this.joystickId))) .findAny() .orElseThrow(); @@ -91,13 +91,19 @@ public String describeButton(int button) { } public void holdAxis(int axis, float state) { + // SDL's raw joystick triggers span [-1, 1], while tests use [0, 1]. + if (axis == SdlGamepad.SDL_GAMEPAD_AXIS_LEFT_TRIGGER || axis == SdlGamepad.SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) { + state = state * 2f - 1f; + } short stateShort = CUtil.mapFloatToShort(state); - this.sdl.joystick().SDL_SetJoystickVirtualAxis( + if (!this.sdl.joystick().SDL_SetJoystickVirtualAxis( this.joystickHandle, this.inputs.joystickAxis(axis), stateShort - ); + )) { + throw SDLException.useSDLError(this.sdl, "Failed to set virtual axis " + axis + " to " + state); + } } public void releaseAxis(int axis) { @@ -108,6 +114,13 @@ public void tapAxis(int axis, float state) { this.holdAxis(axis, state); context.waitTick(); this.releaseAxis(axis); + context.waitTick(); + } + + public void releaseAllInputs() { + this.inputs.buttons().forEach(this::releaseButton); + this.inputs.axes().forEach(this::releaseAxis); + this.context.waitTick(); } public void sendSensorData(int sensorType, float... data) { diff --git a/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/VirtualControllerBuilder.java b/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/VirtualControllerBuilder.java index b5f2fb1e6..c17088e09 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/VirtualControllerBuilder.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/framework/controller/VirtualControllerBuilder.java @@ -127,9 +127,11 @@ public TestControllerContext attachWithDriver(@Nullable GamepadEffectDriv throw SDLException.useSDLError(sdl, "Failed to open virtual joystick"); } - // Allow Controlify to poll and add controller - this.context.waitTick(); - - return new TestControllerContext<>(this.context, this.sdl, handle, jid, this.inputs, state); + var controller = new TestControllerContext<>(this.context, this.sdl, handle, jid, this.inputs, state); + // Center sticks and fully release triggers before Controlify receives the device. + // This also lets Controlify poll and add the controller. + controller.releaseAllInputs(); + this.context.runOnClient(_ -> ControlifyGameTestContext.bindDefaultInputs(controller.getControllerImpl())); + return controller; } } diff --git a/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerManagerAccessor.java b/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerBackendAccessor.java similarity index 81% rename from src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerManagerAccessor.java rename to src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerBackendAccessor.java index 0451fdda8..b748b72d3 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerManagerAccessor.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerBackendAccessor.java @@ -6,13 +6,13 @@ */ package dev.isxander.controlify.gametest.mixin; -import dev.isxander.controlify.impl.controllermanager.SDLControllerManager; +import dev.isxander.controlify.impl.controllermanager.SDLControllerBackend; import dev.isxander.sdl.Sdl; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(SDLControllerManager.class) -public interface SDLControllerManagerAccessor { +@Mixin(SDLControllerBackend.class) +public interface SDLControllerBackendAccessor { @Accessor("sdl") Sdl controlify_test$getSdl(); } diff --git a/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerBackendMixin.java b/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerBackendMixin.java new file mode 100644 index 000000000..c9fd27f9e --- /dev/null +++ b/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerBackendMixin.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.gametest.mixin; + +import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import dev.isxander.controlify.impl.controller.ControllerInfo; +import dev.isxander.controlify.impl.controllermanager.SDLControllerBackend; +import dev.isxander.controlify.impl.controllermanager.SDLUniqueControllerID; +import dev.isxander.sdl.Sdl; +import dev.isxander.sdl.SdlJoystickId; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(SDLControllerBackend.class) +public class SDLControllerBackendMixin { + @Shadow + @Final + private Sdl sdl; + + /// Only load virtual joysticks in test environments + @WrapMethod(method = "createAndAddController(Ldev/isxander/controlify/impl/controller/ControllerInfo;Z)V") + private void preventNonVirtual( + ControllerInfo info, + boolean hotplugged, + Operation original + ) { + SdlJoystickId jid = ((SDLUniqueControllerID) info.ucid()).jid(); + + if (!sdl.joystick().SDL_IsJoystickVirtual(jid)) { + return; + } + + original.call(info, hotplugged); + } +} diff --git a/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerManagerMixin.java b/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerManagerMixin.java deleted file mode 100644 index 461803a2c..000000000 --- a/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLControllerManagerMixin.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.gametest.mixin; - -import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.controllermanager.SDLControllerManager; -import dev.isxander.controlify.impl.controllermanager.SDLUniqueControllerID; -import dev.isxander.controlify.impl.controllermanager.UniqueControllerID; -import dev.isxander.controlify.impl.hid.ControllerHIDInfo; -import dev.isxander.controlify.impl.utils.log.ControlifyLogger; -import dev.isxander.sdl.Sdl; -import dev.isxander.sdl.SdlJoystickId; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; - -import java.util.Optional; - -@Mixin(SDLControllerManager.class) -public class SDLControllerManagerMixin { - @Shadow - @Final - private Sdl sdl; - - /// Only load virtual joysticks in test environments - @WrapMethod(method = "createController") - private Optional preventNonVirtual( - UniqueControllerID ucid, - ControllerHIDInfo hidInfo, - ControlifyLogger controllerLogger, - Operation> original - ) { - SdlJoystickId jid = ((SDLUniqueControllerID) ucid).jid(); - - if (!sdl.joystick().SDL_IsJoystickVirtual(jid)) { - controllerLogger.warn("Suppressing creation of controller as the test environment permits only virtual controllers"); - return Optional.empty(); - } - - return original.call(ucid, hidInfo, controllerLogger); - } -} diff --git a/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLEventHandlerAccessor.java b/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLEventHandlerAccessor.java index e96a6caec..debc6c03b 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLEventHandlerAccessor.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/mixin/SDLEventHandlerAccessor.java @@ -19,6 +19,6 @@ public interface SDLEventHandlerAccessor { void controlify_test$handleMouseMotionEvent(SDL_Event event); } //?} else { -/*@Mixin(dev.isxander.controlify.utils.DummyMixinTarget.class) +/*@Mixin(dev.isxander.controlify.impl.utils.DummyMixinTarget.class) public interface SDLEventHandlerAccessor { } *///?} diff --git a/src/gametest/java/dev/isxander/controlify/gametest/tests/ButtonGuideTests.java b/src/gametest/java/dev/isxander/controlify/gametest/tests/ButtonGuideTests.java index d81060868..914b4acb6 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/ButtonGuideTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/ButtonGuideTests.java @@ -15,10 +15,12 @@ import dev.isxander.controlify.gametest.framework.controller.ControlifyGameTestContext; import dev.isxander.controlify.impl.gui.guide.GuideInstanceImpl; import dev.isxander.controlify.impl.utils.CUtil; +import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.gametest.v1.FabricClientGameTest; import net.fabricmc.fabric.api.client.gametest.v1.context.ClientGameTestContext; import net.minecraft.commands.arguments.EntityAnchorArgument; import net.minecraft.core.BlockPos; +import net.minecraft.resources.Identifier; import net.minecraft.world.phys.EntityHitResult; import java.util.List; @@ -26,7 +28,14 @@ import java.util.stream.Collectors; @SuppressWarnings({"UnstableApiUsage", "unchecked"}) -public class ButtonGuideTests implements FabricClientGameTest { +public class ButtonGuideTests implements FabricClientGameTest, ClientModInitializer { + private static final Identifier OVERRIDE_PACK = CTestUtil.id("guide_override_test"); + + @Override + public void onInitializeClient() { + CTestUtil.registerBuiltinResourcePack(OVERRIDE_PACK); + } + @Override public void runTest(ClientGameTestContext context) { var controlify = new ControlifyGameTestContext(context); @@ -58,8 +67,8 @@ public void runTest(ClientGameTestContext context) { assertFact(state, "looking_at_merchant"); var matchingRules = ruleEngine.evaluate(state); - assertBindingRule(matchingRules, "attack"); - assertBindingRule(matchingRules, "use"); + assertActionRule(matchingRules, "attack"); + assertActionRule(matchingRules, "use"); var guide = (GuideInstanceImpl) domain.createGuideInstance(minecraft.font); guide.update(inGameContext); @@ -68,19 +77,19 @@ public void runTest(ClientGameTestContext context) { } }); - try (var _ = CTestUtil.applyResourcePack(context, CUtil.rl("legacy_console"))) { + try (var _ = CTestUtil.applyResourcePack(context, OVERRIDE_PACK)) { context.runOnClient(minecraft -> { var ruleEngine = RuleSetManager.GUIDE_RULES .getRuleEngine(minecraft.level, CUtil.rl("in_game")) .orElseThrow(); - Set expectedBindings = Set.of("jump", "sneak", "drop", "inventory", "attack", "use"); - Set actualBindings = ruleEngine.rules().stream() - .map(rule -> rule.binding().actionId()) + Set expectedActions = Set.of("jump", "sneak", "drop", "inventory", "attack", "use"); + Set actualActions = ruleEngine.rules().stream() + .map(rule -> rule.action().actionId()) .map(id -> id.getPath()) .collect(Collectors.toSet()); - if (!actualBindings.equals(expectedBindings)) { - throw new AssertionError("Expected the Legacy Console guide rules to replace the defaults. Bindings: " + actualBindings); + if (!actualActions.equals(expectedActions)) { + throw new AssertionError("Expected the test guide rules to replace the defaults. Actions: " + actualActions); } assertRuleLocation(ruleEngine, "jump", GuideLocation.LEFT); @@ -103,28 +112,28 @@ private static void assertFact(ContextualState state, String path) { } } - private static void assertBindingRule( + private static void assertActionRule( List rules, - String bindingPath + String actionPath ) { - var bindingId = CUtil.rl(bindingPath); - if (rules.stream().noneMatch(rule -> rule.binding().actionId().equals(bindingId))) { - throw new AssertionError("Expected a matching guide rule for '" + bindingId + "': " + rules); + var actionId = CUtil.rl(actionPath); + if (rules.stream().noneMatch(rule -> rule.action().actionId().equals(actionId))) { + throw new AssertionError("Expected a matching guide rule for '" + actionId + "': " + rules); } } private static void assertRuleLocation( RuleEngine ruleEngine, - String bindingPath, + String actionPath, GuideLocation expectedLocation ) { - var bindingId = CUtil.rl(bindingPath); + var actionId = CUtil.rl(actionPath); List rules = ruleEngine.rules().stream() - .filter(rule -> rule.binding().actionId().equals(bindingId)) + .filter(rule -> rule.action().actionId().equals(actionId)) .toList(); if (rules.isEmpty() || rules.stream().anyMatch(rule -> rule.location() != expectedLocation)) { throw new AssertionError( - "Expected all guide rules for '" + bindingId + "' to be on " + expectedLocation + ": " + rules + "Expected all guide rules for '" + actionId + "' to be on " + expectedLocation + ": " + rules ); } } 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 e420f23dc..b81f0b192 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/ResetControlifySettingsTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/ResetControlifySettingsTests.java @@ -7,7 +7,6 @@ package dev.isxander.controlify.gametest.tests; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.impl.controller.gyro.GyroState; import dev.isxander.controlify.gametest.framework.controller.ControlifyGameTestContext; import net.fabricmc.fabric.api.client.gametest.v1.FabricClientGameTest; import net.fabricmc.fabric.api.client.gametest.v1.context.ClientGameTestContext; @@ -40,7 +39,7 @@ public void runTest(ClientGameTestContext context) { if (cachedDeviceSettings != settings.getOrCreateDeviceSettings(ControllerImpl.uid())) { throw new AssertionError("Connected controller device settings reference was replaced"); } - if (!cachedDeviceSettings.gyroCalibration.offset.equals(new GyroState())) { + if (!cachedDeviceSettings.gyroCalibration.offset.equals(new Vector3f())) { throw new AssertionError("Device settings were not reset"); } if (ControllerImpl.settings() != controlify.config().getActiveProfile()) { 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 9b577ea55..f4de656aa 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/RumbleTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/RumbleTests.java @@ -324,6 +324,12 @@ private void cleanupTest( TestControllerContext controller, TestUnitWorldContext world ) { + controller.releaseAllInputs(); + context.runOnClient(client -> { + if (client.options.keyAttack.isDown() || client.options.keyUse.isDown()) { + throw new AssertionError("Attack or use remained held after releasing the virtual controller"); + } + }); // Wait for rumble to stop context.waitFor(_ -> !controller.state().hasRumble()); } 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 c8972a983..0e13b20e3 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/VirtualControllerInputTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/VirtualControllerInputTests.java @@ -7,7 +7,7 @@ package dev.isxander.controlify.gametest.tests; import dev.isxander.controlify.impl.input.mapping.GamepadInputs; -import dev.isxander.controlify.impl.controller.input.InputComponentImpl; +import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; import dev.isxander.controlify.gametest.framework.CTestUtil; import dev.isxander.controlify.gametest.framework.controller.ControlifyGameTestContext; import dev.isxander.controlify.gametest.framework.controller.TestControllerContext; @@ -18,6 +18,8 @@ import net.minecraft.resources.Identifier; import java.util.Map; +import java.util.HashSet; +import java.util.Set; //? if >=26.3 { import com.mojang.blaze3d.platform.SDLEventHandler; @@ -58,40 +60,49 @@ public void runTest(ClientGameTestContext context) { try (var controller = controlify.virtualControllerBuilder() .withXbox() .attach()) { - var ControllerImpl = controller.getControllerImpl(); - var input = ControllerImpl.input().orElseThrow(); - - // Fabric cancels real mouse input during tests. Controlify must not - // change input mode before that cancellation takes effect. - //? if >=26.3 { - context.runOnClient(client -> { - Controlify.instance().setInputMode(InputMode.CONTROLLER); - try (var event = SDL_Event.calloc()) { - var handler = new SDLEventHandler(client, client.getWindow()); - ((SDLEventHandlerAccessor) handler).controlify_test$handleMouseMotionEvent(event); - } - if (Controlify.instance().currentInputMode() != InputMode.CONTROLLER) { - throw new AssertionError("Cancelled SDL mouse input changed Controlify input mode"); + var input = controller.getControllerImpl().input(); + // This test observes physical events without triggering menus or gameplay. + context.runOnClient(_ -> input.getAllActions().forEach(action -> action.setBoundInput(null))); + Set pressedButtons = new HashSet<>(); + try (var subscription = context.computeOnClient(_ -> input.inputPipeline().sourceRawPhysicalSignals().subscribe(signal -> { + if (signal instanceof PhysicalInputSignal.ButtonChanged button) { + if (button.state()) pressedButtons.add(button.input()); + else pressedButtons.remove(button.input()); } - }); - //?} + }))) { - context.waitTick(); + // Fabric cancels real mouse input during tests. Controlify must not + // change input mode before that cancellation takes effect. + //? if >=26.3 { + context.runOnClient(client -> { + Controlify.instance().setInputMode(InputMode.CONTROLLER); + try (var event = SDL_Event.calloc()) { + var handler = new SDLEventHandler(client, client.getWindow()); + ((SDLEventHandlerAccessor) handler).controlify_test$handleMouseMotionEvent(event); + } + if (Controlify.instance().currentInputMode() != InputMode.CONTROLLER) { + throw new AssertionError("Cancelled SDL mouse input changed Controlify input mode"); + } + }); + //?} - for (var expected : XINPUT_BUTTONS.entrySet()) { - controller.holdButton(expected.getKey()); - waitForButton(context, controller, input, expected, true); + context.waitTick(); - for (var other : XINPUT_BUTTONS.values()) { - if (!other.equals(expected.getValue()) && input.rawStateNow().isButtonDown(other)) { - throw new AssertionError( - "Pressed " + expected.getValue() + " but also received " + other - ); + for (var expected : XINPUT_BUTTONS.entrySet()) { + controller.holdButton(expected.getKey()); + waitForButton(context, controller, pressedButtons, expected, true); + + for (var other : XINPUT_BUTTONS.values()) { + if (!other.equals(expected.getValue()) && context.computeOnClient(_ -> pressedButtons.contains(other))) { + throw new AssertionError( + "Pressed " + expected.getValue() + " but also received " + other + ); + } } - } - controller.releaseButton(expected.getKey()); - waitForButton(context, controller, input, expected, false); + controller.releaseButton(expected.getKey()); + waitForButton(context, controller, pressedButtons, expected, false); + } } } @@ -102,15 +113,14 @@ public void runTest(ClientGameTestContext context) { private static void waitForButton( ClientGameTestContext context, TestControllerContext controller, - InputComponentImpl input, + Set pressedButtons, Map.Entry expected, boolean down ) { try { - context.waitFor(_ -> input.rawStateNow().isButtonDown(expected.getValue()) == down, 10); + context.waitFor(_ -> pressedButtons.contains(expected.getValue()) == down, 10); } catch (AssertionError failure) { - var pressed = context.computeOnClient(_ -> input.rawStateNow().getButtons().stream() - .filter(input.rawStateNow()::isButtonDown).toList()); + var pressed = context.computeOnClient(_ -> Set.copyOf(pressedButtons)); throw new AssertionError("Timed out waiting for " + expected.getValue() + " to be " + (down ? "pressed" : "released") + "; Controlify pressed=" + pressed + "; " + controller.describeButton(expected.getKey()), failure); diff --git a/src/gametest/resources/controlify_test.mixins.json b/src/gametest/resources/controlify_test.mixins.json index 008de9104..7e971ad9d 100644 --- a/src/gametest/resources/controlify_test.mixins.json +++ b/src/gametest/resources/controlify_test.mixins.json @@ -14,8 +14,8 @@ }, "mixins": [ "ControlifySettingsAccessor", - "SDLControllerManagerAccessor", - "SDLControllerManagerMixin" + "SDLControllerBackendAccessor", + "SDLControllerBackendMixin" ], "client": [ "RecipeBookPageAccessor", diff --git a/src/gametest/resources/fabric.mod.json b/src/gametest/resources/fabric.mod.json index 469ed2fda..c417bd96c 100644 --- a/src/gametest/resources/fabric.mod.json +++ b/src/gametest/resources/fabric.mod.json @@ -23,6 +23,7 @@ "dev.isxander.controlify.gametest.tests.ButtonGuideTests" ], "client": [ + "dev.isxander.controlify.gametest.tests.ButtonGuideTests", "dev.isxander.controlify.gametest.tests.KeyboardLayoutTests", "dev.isxander.controlify.gametest.tests.AdaptiveTriggerTests" ] diff --git a/src/gametest/resources/resourcepacks/adaptive_trigger_high_priority/assets/controlify/contextual/trigger_effect/in_game.json b/src/gametest/resources/resourcepacks/adaptive_trigger_high_priority/assets/controlify/controller/contextual/trigger_effect/in_game.json similarity index 100% rename from src/gametest/resources/resourcepacks/adaptive_trigger_high_priority/assets/controlify/contextual/trigger_effect/in_game.json rename to src/gametest/resources/resourcepacks/adaptive_trigger_high_priority/assets/controlify/controller/contextual/trigger_effect/in_game.json diff --git a/src/gametest/resources/resourcepacks/adaptive_trigger_low_priority/assets/controlify/contextual/trigger_effect/in_game.json b/src/gametest/resources/resourcepacks/adaptive_trigger_low_priority/assets/controlify/controller/contextual/trigger_effect/in_game.json similarity index 100% rename from src/gametest/resources/resourcepacks/adaptive_trigger_low_priority/assets/controlify/contextual/trigger_effect/in_game.json rename to src/gametest/resources/resourcepacks/adaptive_trigger_low_priority/assets/controlify/controller/contextual/trigger_effect/in_game.json diff --git a/src/gametest/resources/resourcepacks/adaptive_trigger_server_tag/assets/controlify/contextual/trigger_effect/in_game.json b/src/gametest/resources/resourcepacks/adaptive_trigger_server_tag/assets/controlify/controller/contextual/trigger_effect/in_game.json similarity index 100% rename from src/gametest/resources/resourcepacks/adaptive_trigger_server_tag/assets/controlify/contextual/trigger_effect/in_game.json rename to src/gametest/resources/resourcepacks/adaptive_trigger_server_tag/assets/controlify/controller/contextual/trigger_effect/in_game.json diff --git a/src/gametest/resources/resourcepacks/guide_override_test/assets/controlify/controller/contextual/guide/in_game.json b/src/gametest/resources/resourcepacks/guide_override_test/assets/controlify/controller/contextual/guide/in_game.json new file mode 100644 index 000000000..d9e222f52 --- /dev/null +++ b/src/gametest/resources/resourcepacks/guide_override_test/assets/controlify/controller/contextual/guide/in_game.json @@ -0,0 +1,53 @@ +{ + "replace": true, + "rules": [ + { + "for": "controlify:jump", + "where": "left", + "if": true, + "then": { + "text": "jump" + } + }, + { + "for": "controlify:sneak", + "where": "left", + "if": true, + "then": { + "text": "sneak" + } + }, + { + "for": "controlify:drop", + "where": "left", + "if": true, + "then": { + "text": "drop" + } + }, + { + "for": "controlify:inventory", + "where": "right", + "if": true, + "then": { + "text": "inventory" + } + }, + { + "for": "controlify:attack", + "where": "right", + "if": true, + "then": { + "text": "attack" + } + }, + { + "for": "controlify:use", + "where": "right", + "if": true, + "then": { + "text": "use" + } + } + ] +} diff --git a/src/gametest/resources/resourcepacks/guide_override_test/pack.mcmeta b/src/gametest/resources/resourcepacks/guide_override_test/pack.mcmeta new file mode 100644 index 000000000..e4e49a77b --- /dev/null +++ b/src/gametest/resources/resourcepacks/guide_override_test/pack.mcmeta @@ -0,0 +1,9 @@ +{ + "pack": { + "description": "Guide override test pack", + "supported_formats": [34, 10000], + "pack_format": 64, + "min_format": 34, + "max_format": [10000, 0] + } +} 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/controller/keyboard_layout/full/en_gb.json similarity index 100% rename from src/gametest/resources/resourcepacks/keyboard_layout_test/assets/controlify/keyboard_layout/full/en_gb.json rename to src/gametest/resources/resourcepacks/keyboard_layout_test/assets/controlify/controller/keyboard_layout/full/en_gb.json 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/controller/keyboard_layout/full/en_us.json similarity index 100% rename from src/gametest/resources/resourcepacks/keyboard_layout_test/assets/controlify/keyboard_layout/full/en_us.json rename to src/gametest/resources/resourcepacks/keyboard_layout_test/assets/controlify/controller/keyboard_layout/full/en_us.json diff --git a/src/main/java/dev/isxander/controlify/api/contextual/GuideVerbosity.java b/src/main/java/dev/isxander/controlify/api/contextual/GuideVerbosity.java index 8baba51d3..1aeb626f3 100644 --- a/src/main/java/dev/isxander/controlify/api/contextual/GuideVerbosity.java +++ b/src/main/java/dev/isxander/controlify/api/contextual/GuideVerbosity.java @@ -10,7 +10,6 @@ import dev.isxander.yacl3.api.NameableEnum; import net.minecraft.network.chat.Component; import net.minecraft.util.StringRepresentable; -import org.jspecify.annotations.NonNull; public enum GuideVerbosity implements NameableEnum, StringRepresentable { FULL(3), @@ -35,7 +34,7 @@ public Component getDisplayName() { } @Override - public @NonNull String getSerializedName() { + public String getSerializedName() { return name().toLowerCase(); } } diff --git a/src/main/java/dev/isxander/controlify/impl/ingame/InGameInputHandler.java b/src/main/java/dev/isxander/controlify/impl/ingame/InGameInputHandler.java index 7a5c57dc2..ff2d4f775 100644 --- a/src/main/java/dev/isxander/controlify/impl/ingame/InGameInputHandler.java +++ b/src/main/java/dev/isxander/controlify/impl/ingame/InGameInputHandler.java @@ -39,6 +39,8 @@ import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; import net.minecraft.server.commands.GameModeCommand; import net.minecraft.util.Mth; +//? if <26.3 +//import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.Vec2; 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 e3fdb464b..f3eb1e7a3 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/InputConstantsMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/InputConstantsMixin.java @@ -23,7 +23,7 @@ private static void ensureMouseVisible(CallbackInfo ci) { } } //?} else { -/*@Mixin(dev.isxander.controlify.utils.DummyMixinTarget.class) +/*@Mixin(dev.isxander.controlify.impl.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 3d73b72db..38a9e5682 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/KeyboardHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/KeyboardHandlerMixin.java @@ -12,8 +12,8 @@ /*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; -import dev.isxander.controlify.InputMode; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.api.InputMode; import net.minecraft.client.KeyboardHandler; import net.minecraft.client.Minecraft; import org.lwjgl.glfw.*; 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 2743b8b16..544131a43 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/MouseHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/MouseHandlerMixin.java @@ -20,7 +20,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; //? if <26.3 { -/*import org.lwjgl.glfw.GLFWCursorPosCallbackI; +/*import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.mojang.blaze3d.platform.Window; +import dev.isxander.controlify.api.InputMode; +import org.lwjgl.glfw.GLFWCursorPosCallbackI; import org.lwjgl.glfw.GLFWDropCallbackI; import org.lwjgl.glfw.GLFWMouseButtonCallbackI; import org.lwjgl.glfw.GLFWScrollCallbackI; 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 06dfcecbd..5c3589a68 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/SDLEventHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/SDLEventHandlerMixin.java @@ -63,6 +63,6 @@ private void onMouseInput(CallbackInfo ci) { } } //?} else { -/*@Mixin(dev.isxander.controlify.utils.DummyMixinTarget.class) +/*@Mixin(dev.isxander.controlify.impl.utils.DummyMixinTarget.class) public class SDLEventHandlerMixin { } *///?} From 9a9b24a621b7cd6b0caf4ecd31eca863a111bef3 Mon Sep 17 00:00:00 2001 From: isxander Date: Thu, 22 Oct 2026 04:06:23 +0100 Subject: [PATCH 21/44] spotless --- .../dev/isxander/controlify/api/ControlifyApi.java | 6 ++++++ .../isxander/controlify/api/ControlifyEntrypoint.java | 6 ++++++ .../dev/isxander/controlify/api/ControlifyEvents.java | 6 ++++++ .../dev/isxander/controlify/api/action/Action.java | 6 ++++++ .../isxander/controlify/api/action/ActionAccessor.java | 6 ++++++ .../controlify/api/action/ActionContextRegistry.java | 6 ++++++ .../isxander/controlify/api/action/ActionEvents.java | 6 ++++++ .../controlify/api/action/ActionProxyManager.java | 6 ++++++ .../dev/isxander/controlify/api/action/ActionSpec.java | 6 ++++++ .../controlify/api/action/ActionSpecBuilder.java | 6 ++++++ .../controlify/api/action/ActionSpecRegistry.java | 6 ++++++ .../controlify/api/action/BuiltinActionContexts.java | 6 ++++++ .../dev/isxander/controlify/api/action/Channel.java | 6 ++++++ .../controlify/api/contextual/ContextualEvents.java | 6 ++++++ .../isxander/controlify/api/controller/Controller.java | 6 ++++++ .../controlify/api/controller/gyro/GyroComponent.java | 6 ++++++ .../api/controller/input/InputComponent.java | 6 ++++++ .../api/controller/rumble/ContinuousRumbleEffect.java | 6 ++++++ .../api/controller/rumble/FiniteRumbleEffect.java | 6 ++++++ .../api/controller/rumble/RumbleComponent.java | 6 ++++++ .../controlify/api/controller/rumble/RumbleEffect.java | 6 ++++++ .../api/controller/rumble/RumbleEffectInstance.java | 6 ++++++ .../api/controllermanager/ControllerEvents.java | 6 ++++++ .../isxander/controlify/api/ingame/InGameEvents.java | 6 ++++++ .../controlify/api/ingame/LookInputModifier.java | 6 ++++++ .../isxander/controlify/api/input/LogicalInput.java | 6 ++++++ .../isxander/controlify/api/utils/event/EventSink.java | 6 ++++++ .../controlify/api/utils/event/EventSource.java | 6 ++++++ .../controlify/api/utils/event/EventStage.java | 6 ++++++ .../controlify/api/utils/event/UnaryEventStage.java | 6 ++++++ .../java/dev/isxander/controlify/impl/Controlify.java | 2 +- .../isxander/controlify/impl/action/Accumulator.java | 6 ++++++ .../controlify/impl/action/ActionAccessorImpl.java | 6 ++++++ .../isxander/controlify/impl/action/ActionContext.java | 6 ++++++ .../impl/action/ActionContextRegistryImpl.java | 6 ++++++ .../isxander/controlify/impl/action/ActionGraph.java | 6 ++++++ .../isxander/controlify/impl/action/ActionImpl.java | 6 ++++++ .../controlify/impl/action/ActionSpecBuilderImpl.java | 6 ++++++ .../controlify/impl/action/ActionSpecRegistryImpl.java | 6 ++++++ .../isxander/controlify/impl/action/ActionState.java | 6 ++++++ .../dev/isxander/controlify/impl/action/Activator.java | 6 ++++++ .../controlify/impl/action/KeyMappingHandle.java | 6 ++++++ .../impl/action/activator/ActivatorConfig.java | 6 ++++++ .../impl/action/activator/ActivatorConfigType.java | 6 ++++++ .../impl/action/activator/CompoundActivator.java | 6 ++++++ .../impl/action/activator/ContinuousActivator.java | 6 ++++++ .../impl/action/activator/DoubleTapPulseActivator.java | 6 ++++++ .../impl/action/activator/GuiPressPulseActivator.java | 6 ++++++ .../impl/action/activator/HoldLatchActivator.java | 6 ++++++ .../impl/action/activator/HoldPulseActivator.java | 6 ++++++ .../impl/action/activator/NoopActivator.java | 6 ++++++ .../impl/action/activator/PressPulseActivator.java | 6 ++++++ .../impl/action/activator/ReleasePulseActivator.java | 6 ++++++ .../action/activator/RepeatLatchPulseActivator.java | 6 ++++++ .../impl/action/activator/TapPulseActivator.java | 6 ++++++ .../impl/action/activator/ToggleLatchActivator.java | 6 ++++++ .../impl/action/proxy/ActionProxyManagerImpl.java | 6 ++++++ .../controlify/impl/action/proxy/KeyMappingProxy.java | 6 ++++++ .../impl/action/radial/RadialIconManager.java | 2 +- .../impl/config/dto/dfu/fixes/GyroLowPassFix.java | 6 ++++++ .../impl/controller/ControllerComponent.java | 6 ++++++ .../controlify/impl/controller/ControllerImpl.java | 6 ++++++ .../controller/SingleValueControllerComponent.java | 6 ++++++ .../impl/controller/input/InputComponentImpl.java | 6 ++++++ .../controller/rumble/ContinuousRumbleEffectImpl.java | 6 ++++++ .../impl/controller/rumble/FiniteRumbleEffectImpl.java | 6 ++++++ .../controller/rumble/RumbleEffectInstanceImpl.java | 6 ++++++ .../impl/controllermanager/ControllerBackend.java | 6 ++++++ .../impl/controllermanager/ControllerManager.java | 6 ++++++ .../impl/controllermanager/SDLControllerBackend.java | 6 ++++++ .../impl/controllermanager/SDLUniqueControllerID.java | 6 ++++++ .../controlify/impl/driver/sdl/SDLControllerEvent.java | 6 ++++++ .../impl/driver/sdl/SDLControllerEventStage.java | 6 ++++++ .../impl/driver/sdl/SDLInputConversions.java | 10 ++++++++-- .../controlify/impl/driver/sdl/SDLToPhysicalStage.java | 6 ++++++ .../dev/isxander/controlify/impl/input/SensorType.java | 6 ++++++ .../impl/input/mapping/ControllerMappingManager.java | 6 ++++++ .../controlify/impl/input/mapping/GestureInputs.java | 6 ++++++ .../controlify/impl/input/pipeline/DeadzoneStage.java | 6 ++++++ .../impl/input/pipeline/GestureRecogniserStage.java | 6 ++++++ .../impl/input/pipeline/InputDomainExpansionStage.java | 6 ++++++ .../controlify/impl/input/pipeline/InputPipeline.java | 6 ++++++ .../controlify/impl/input/pipeline/MappingStage.java | 6 ++++++ .../impl/input/pipeline/PhysicalToLogicalStage.java | 6 ++++++ .../impl/input/pipeline/SensorAccumulatorStage.java | 6 ++++++ .../impl/input/pipeline/SensorProcessorStage.java | 6 ++++++ .../controlify/impl/input/signal/LogicalSignal.java | 6 ++++++ .../impl/input/signal/PhysicalInputSignal.java | 6 ++++++ .../impl/input/signal/PhysicalSensorSignal.java | 6 ++++++ .../controlify/impl/input/signal/PhysicalSignal.java | 6 ++++++ .../controlify/impl/utils/SetupWizardManager.java | 6 ++++++ .../isxander/controlify/impl/utils/event/EventBus.java | 6 ++++++ .../isxander/controlify/impl/utils/event/EventTap.java | 6 ++++++ .../mixins/feature/bind/KeyMappingMixin.java | 6 ++++++ .../impl/input/pipeline/DeadzoneStageTest.java | 6 ++++++ 95 files changed, 562 insertions(+), 4 deletions(-) diff --git a/src/main/java/dev/isxander/controlify/api/ControlifyApi.java b/src/main/java/dev/isxander/controlify/api/ControlifyApi.java index 7f6fd17e7..84f091ad0 100644 --- a/src/main/java/dev/isxander/controlify/api/ControlifyApi.java +++ b/src/main/java/dev/isxander/controlify/api/ControlifyApi.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.api; import dev.isxander.controlify.api.contextual.BuiltinContextualDomains; diff --git a/src/main/java/dev/isxander/controlify/api/ControlifyEntrypoint.java b/src/main/java/dev/isxander/controlify/api/ControlifyEntrypoint.java index e455a6296..8e784591b 100644 --- a/src/main/java/dev/isxander/controlify/api/ControlifyEntrypoint.java +++ b/src/main/java/dev/isxander/controlify/api/ControlifyEntrypoint.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.api; /// Registers addon event listeners before Controlify starts client setup. diff --git a/src/main/java/dev/isxander/controlify/api/ControlifyEvents.java b/src/main/java/dev/isxander/controlify/api/ControlifyEvents.java index be9fc65b6..9bc4900eb 100644 --- a/src/main/java/dev/isxander/controlify/api/ControlifyEvents.java +++ b/src/main/java/dev/isxander/controlify/api/ControlifyEvents.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.api; import dev.isxander.controlify.api.utils.event.EventSource; diff --git a/src/main/java/dev/isxander/controlify/api/action/Action.java b/src/main/java/dev/isxander/controlify/api/action/Action.java index 05046ecd4..0a00420fa 100644 --- a/src/main/java/dev/isxander/controlify/api/action/Action.java +++ b/src/main/java/dev/isxander/controlify/api/action/Action.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.api.action; import dev.isxander.controlify.api.input.LogicalInput; diff --git a/src/main/java/dev/isxander/controlify/api/action/ActionAccessor.java b/src/main/java/dev/isxander/controlify/api/action/ActionAccessor.java index b4c9248e6..ed1603d4d 100644 --- a/src/main/java/dev/isxander/controlify/api/action/ActionAccessor.java +++ b/src/main/java/dev/isxander/controlify/api/action/ActionAccessor.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.api.action; import com.mojang.serialization.Codec; diff --git a/src/main/java/dev/isxander/controlify/api/action/ActionContextRegistry.java b/src/main/java/dev/isxander/controlify/api/action/ActionContextRegistry.java index bbd90fd5e..febe97cf3 100644 --- a/src/main/java/dev/isxander/controlify/api/action/ActionContextRegistry.java +++ b/src/main/java/dev/isxander/controlify/api/action/ActionContextRegistry.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.api.action; import net.minecraft.client.Minecraft; diff --git a/src/main/java/dev/isxander/controlify/api/action/ActionEvents.java b/src/main/java/dev/isxander/controlify/api/action/ActionEvents.java index 6461f9a55..91b9d37b4 100644 --- a/src/main/java/dev/isxander/controlify/api/action/ActionEvents.java +++ b/src/main/java/dev/isxander/controlify/api/action/ActionEvents.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.api.action; import dev.isxander.controlify.api.utils.event.EventSource; diff --git a/src/main/java/dev/isxander/controlify/api/action/ActionProxyManager.java b/src/main/java/dev/isxander/controlify/api/action/ActionProxyManager.java index a9c1c8440..9bdad7886 100644 --- a/src/main/java/dev/isxander/controlify/api/action/ActionProxyManager.java +++ b/src/main/java/dev/isxander/controlify/api/action/ActionProxyManager.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.api.action; import net.minecraft.client.KeyMapping; diff --git a/src/main/java/dev/isxander/controlify/api/action/ActionSpec.java b/src/main/java/dev/isxander/controlify/api/action/ActionSpec.java index e3e81b9fc..4fc4929e8 100644 --- a/src/main/java/dev/isxander/controlify/api/action/ActionSpec.java +++ b/src/main/java/dev/isxander/controlify/api/action/ActionSpec.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.api.action; import dev.isxander.controlify.api.controller.Controller; diff --git a/src/main/java/dev/isxander/controlify/api/action/ActionSpecBuilder.java b/src/main/java/dev/isxander/controlify/api/action/ActionSpecBuilder.java index 9d59c97e1..0dea77fa7 100644 --- a/src/main/java/dev/isxander/controlify/api/action/ActionSpecBuilder.java +++ b/src/main/java/dev/isxander/controlify/api/action/ActionSpecBuilder.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.api.action; import dev.isxander.controlify.api.controller.Controller; diff --git a/src/main/java/dev/isxander/controlify/api/action/ActionSpecRegistry.java b/src/main/java/dev/isxander/controlify/api/action/ActionSpecRegistry.java index c5eea397f..aec0179be 100644 --- a/src/main/java/dev/isxander/controlify/api/action/ActionSpecRegistry.java +++ b/src/main/java/dev/isxander/controlify/api/action/ActionSpecRegistry.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.api.action; /// Receives action definitions during {@link ActionEvents#REGISTER_ACTIONS}. diff --git a/src/main/java/dev/isxander/controlify/api/action/BuiltinActionContexts.java b/src/main/java/dev/isxander/controlify/api/action/BuiltinActionContexts.java index 517a1d29d..ae00f9766 100644 --- a/src/main/java/dev/isxander/controlify/api/action/BuiltinActionContexts.java +++ b/src/main/java/dev/isxander/controlify/api/action/BuiltinActionContexts.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.api.action; import dev.isxander.controlify.impl.utils.CUtil; diff --git a/src/main/java/dev/isxander/controlify/api/action/Channel.java b/src/main/java/dev/isxander/controlify/api/action/Channel.java index 120106d3f..a232e1671 100644 --- a/src/main/java/dev/isxander/controlify/api/action/Channel.java +++ b/src/main/java/dev/isxander/controlify/api/action/Channel.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.api.action; /// A channel is a way of interpreting the output state of a given {@link Action}. diff --git a/src/main/java/dev/isxander/controlify/api/contextual/ContextualEvents.java b/src/main/java/dev/isxander/controlify/api/contextual/ContextualEvents.java index 49d6a1f52..ec98eedc4 100644 --- a/src/main/java/dev/isxander/controlify/api/contextual/ContextualEvents.java +++ b/src/main/java/dev/isxander/controlify/api/contextual/ContextualEvents.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.api.contextual; import dev.isxander.controlify.api.utils.event.EventSource; diff --git a/src/main/java/dev/isxander/controlify/api/controller/Controller.java b/src/main/java/dev/isxander/controlify/api/controller/Controller.java index e072f8331..f00e3c2a7 100644 --- a/src/main/java/dev/isxander/controlify/api/controller/Controller.java +++ b/src/main/java/dev/isxander/controlify/api/controller/Controller.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.api.controller; import dev.isxander.controlify.api.controller.rumble.RumbleComponent; diff --git a/src/main/java/dev/isxander/controlify/api/controller/gyro/GyroComponent.java b/src/main/java/dev/isxander/controlify/api/controller/gyro/GyroComponent.java index d75ac23db..4e4e238ea 100644 --- a/src/main/java/dev/isxander/controlify/api/controller/gyro/GyroComponent.java +++ b/src/main/java/dev/isxander/controlify/api/controller/gyro/GyroComponent.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.api.controller.gyro; import org.joml.Vector3fc; diff --git a/src/main/java/dev/isxander/controlify/api/controller/input/InputComponent.java b/src/main/java/dev/isxander/controlify/api/controller/input/InputComponent.java index 8c6987d33..983f55015 100644 --- a/src/main/java/dev/isxander/controlify/api/controller/input/InputComponent.java +++ b/src/main/java/dev/isxander/controlify/api/controller/input/InputComponent.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.api.controller.input; import dev.isxander.controlify.api.action.Action; diff --git a/src/main/java/dev/isxander/controlify/api/controller/rumble/ContinuousRumbleEffect.java b/src/main/java/dev/isxander/controlify/api/controller/rumble/ContinuousRumbleEffect.java index 71a6e0972..377b81b3f 100644 --- a/src/main/java/dev/isxander/controlify/api/controller/rumble/ContinuousRumbleEffect.java +++ b/src/main/java/dev/isxander/controlify/api/controller/rumble/ContinuousRumbleEffect.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.api.controller.rumble; import net.minecraft.world.phys.Vec3; diff --git a/src/main/java/dev/isxander/controlify/api/controller/rumble/FiniteRumbleEffect.java b/src/main/java/dev/isxander/controlify/api/controller/rumble/FiniteRumbleEffect.java index 11a2b75a4..e69e12e3f 100644 --- a/src/main/java/dev/isxander/controlify/api/controller/rumble/FiniteRumbleEffect.java +++ b/src/main/java/dev/isxander/controlify/api/controller/rumble/FiniteRumbleEffect.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.api.controller.rumble; import java.util.Objects; diff --git a/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleComponent.java b/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleComponent.java index 11a514bb9..8abb94df6 100644 --- a/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleComponent.java +++ b/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleComponent.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.api.controller.rumble; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleEffect.java b/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleEffect.java index 1b71b55a1..539d30ed8 100644 --- a/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleEffect.java +++ b/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleEffect.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.api.controller.rumble; import dev.isxander.controlify.impl.controller.rumble.FiniteRumbleEffectImpl; diff --git a/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleEffectInstance.java b/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleEffectInstance.java index 5a0a7f4cc..eaba4c0da 100644 --- a/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleEffectInstance.java +++ b/src/main/java/dev/isxander/controlify/api/controller/rumble/RumbleEffectInstance.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.api.controller.rumble; /// A handle on a specific instance of a queued, playing, or played rumble diff --git a/src/main/java/dev/isxander/controlify/api/controllermanager/ControllerEvents.java b/src/main/java/dev/isxander/controlify/api/controllermanager/ControllerEvents.java index 11199afae..2508b1e30 100644 --- a/src/main/java/dev/isxander/controlify/api/controllermanager/ControllerEvents.java +++ b/src/main/java/dev/isxander/controlify/api/controllermanager/ControllerEvents.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.api.controllermanager; import dev.isxander.controlify.api.ControlifyApi; diff --git a/src/main/java/dev/isxander/controlify/api/ingame/InGameEvents.java b/src/main/java/dev/isxander/controlify/api/ingame/InGameEvents.java index a11f745a0..dbe31e19e 100644 --- a/src/main/java/dev/isxander/controlify/api/ingame/InGameEvents.java +++ b/src/main/java/dev/isxander/controlify/api/ingame/InGameEvents.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.api.ingame; import dev.isxander.controlify.api.utils.event.EventSource; diff --git a/src/main/java/dev/isxander/controlify/api/ingame/LookInputModifier.java b/src/main/java/dev/isxander/controlify/api/ingame/LookInputModifier.java index dfc6f2486..7c1327c9e 100644 --- a/src/main/java/dev/isxander/controlify/api/ingame/LookInputModifier.java +++ b/src/main/java/dev/isxander/controlify/api/ingame/LookInputModifier.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.api.ingame; import dev.isxander.controlify.api.controller.Controller; diff --git a/src/main/java/dev/isxander/controlify/api/input/LogicalInput.java b/src/main/java/dev/isxander/controlify/api/input/LogicalInput.java index 7ec5d1ffa..a5a561807 100644 --- a/src/main/java/dev/isxander/controlify/api/input/LogicalInput.java +++ b/src/main/java/dev/isxander/controlify/api/input/LogicalInput.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.api.input; import com.mojang.datafixers.util.Either; diff --git a/src/main/java/dev/isxander/controlify/api/utils/event/EventSink.java b/src/main/java/dev/isxander/controlify/api/utils/event/EventSink.java index 01095d82a..8d10a374f 100644 --- a/src/main/java/dev/isxander/controlify/api/utils/event/EventSink.java +++ b/src/main/java/dev/isxander/controlify/api/utils/event/EventSink.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.api.utils.event; @FunctionalInterface diff --git a/src/main/java/dev/isxander/controlify/api/utils/event/EventSource.java b/src/main/java/dev/isxander/controlify/api/utils/event/EventSource.java index ca9944318..b22bba996 100644 --- a/src/main/java/dev/isxander/controlify/api/utils/event/EventSource.java +++ b/src/main/java/dev/isxander/controlify/api/utils/event/EventSource.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.api.utils.event; import java.util.function.Predicate; diff --git a/src/main/java/dev/isxander/controlify/api/utils/event/EventStage.java b/src/main/java/dev/isxander/controlify/api/utils/event/EventStage.java index 39e83e0b5..a0ed347e7 100644 --- a/src/main/java/dev/isxander/controlify/api/utils/event/EventStage.java +++ b/src/main/java/dev/isxander/controlify/api/utils/event/EventStage.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.api.utils.event; @FunctionalInterface diff --git a/src/main/java/dev/isxander/controlify/api/utils/event/UnaryEventStage.java b/src/main/java/dev/isxander/controlify/api/utils/event/UnaryEventStage.java index e078ad8b0..c054141d9 100644 --- a/src/main/java/dev/isxander/controlify/api/utils/event/UnaryEventStage.java +++ b/src/main/java/dev/isxander/controlify/api/utils/event/UnaryEventStage.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.api.utils.event; @FunctionalInterface diff --git a/src/main/java/dev/isxander/controlify/impl/Controlify.java b/src/main/java/dev/isxander/controlify/impl/Controlify.java index 883cb99f6..dbad581b5 100644 --- a/src/main/java/dev/isxander/controlify/impl/Controlify.java +++ b/src/main/java/dev/isxander/controlify/impl/Controlify.java @@ -351,7 +351,7 @@ public void fixedUpdate() { } boolean outOfFocus = !config().getSettings().globalSettings().outOfFocusInput - && !this.minecraft.isWindowActive(); + && !this.minecraft.isWindowActive(); this.getCurrentController().ifPresent(currentController -> { wrapControllerError( diff --git a/src/main/java/dev/isxander/controlify/impl/action/Accumulator.java b/src/main/java/dev/isxander/controlify/impl/action/Accumulator.java index f5bfbc438..cdcf89dc2 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/Accumulator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/Accumulator.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.impl.action; /// Accepts action state. diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionAccessorImpl.java b/src/main/java/dev/isxander/controlify/impl/action/ActionAccessorImpl.java index 9316baabe..91e3f21a9 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionAccessorImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionAccessorImpl.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.impl.action; import dev.isxander.controlify.api.action.Action; diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionContext.java b/src/main/java/dev/isxander/controlify/impl/action/ActionContext.java index f5bc999e8..1e3e6908e 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionContext.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionContext.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.impl.action; import net.minecraft.client.Minecraft; diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionContextRegistryImpl.java b/src/main/java/dev/isxander/controlify/impl/action/ActionContextRegistryImpl.java index 972a49b4d..6037051fc 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionContextRegistryImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionContextRegistryImpl.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.impl.action; import dev.isxander.controlify.api.action.ActionContextRegistry; diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionGraph.java b/src/main/java/dev/isxander/controlify/impl/action/ActionGraph.java index c81699a6a..f5f463395 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionGraph.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionGraph.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.impl.action; import dev.isxander.controlify.api.input.LogicalInput; diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionImpl.java b/src/main/java/dev/isxander/controlify/impl/action/ActionImpl.java index a9453da1e..e03b59d72 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionImpl.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.impl.action; import dev.isxander.controlify.api.action.Action; diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionSpecBuilderImpl.java b/src/main/java/dev/isxander/controlify/impl/action/ActionSpecBuilderImpl.java index 75ec6a311..3cb42306e 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionSpecBuilderImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionSpecBuilderImpl.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.impl.action; import dev.isxander.controlify.api.action.ActionSpec; diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionSpecRegistryImpl.java b/src/main/java/dev/isxander/controlify/impl/action/ActionSpecRegistryImpl.java index abfc5054a..f2f31b53f 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionSpecRegistryImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionSpecRegistryImpl.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.impl.action; import dev.isxander.controlify.api.action.ActionAccessor; diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionState.java b/src/main/java/dev/isxander/controlify/impl/action/ActionState.java index 499b60103..abbc4df94 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionState.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionState.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.impl.action; import dev.isxander.controlify.api.action.Channel; diff --git a/src/main/java/dev/isxander/controlify/impl/action/Activator.java b/src/main/java/dev/isxander/controlify/impl/action/Activator.java index 92855b478..6f4aa6803 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/Activator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/Activator.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.impl.action; import dev.isxander.controlify.api.action.Action; diff --git a/src/main/java/dev/isxander/controlify/impl/action/KeyMappingHandle.java b/src/main/java/dev/isxander/controlify/impl/action/KeyMappingHandle.java index e4856f9f7..61f2ffcf5 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/KeyMappingHandle.java +++ b/src/main/java/dev/isxander/controlify/impl/action/KeyMappingHandle.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.impl.action; /// Applies emulated output without changing Minecraft's keyboard toggle policy. diff --git a/src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfig.java b/src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfig.java index 5dfcecb8b..2aa66fbeb 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfig.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfig.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.impl.action.activator; import com.mojang.serialization.Codec; diff --git a/src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfigType.java b/src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfigType.java index 31fd78a74..dc4e7b70a 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfigType.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/ActivatorConfigType.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.impl.action.activator; import com.mojang.serialization.MapCodec; diff --git a/src/main/java/dev/isxander/controlify/impl/action/activator/CompoundActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/CompoundActivator.java index c90c9d9f7..80e542a9b 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/activator/CompoundActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/CompoundActivator.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.impl.action.activator; import com.mojang.serialization.MapCodec; diff --git a/src/main/java/dev/isxander/controlify/impl/action/activator/ContinuousActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/ContinuousActivator.java index 4bc2daa1d..93a443dff 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/activator/ContinuousActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/ContinuousActivator.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.impl.action.activator; import dev.isxander.controlify.impl.action.Accumulator; diff --git a/src/main/java/dev/isxander/controlify/impl/action/activator/DoubleTapPulseActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/DoubleTapPulseActivator.java index 92d136552..b4ab77d50 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/activator/DoubleTapPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/DoubleTapPulseActivator.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.impl.action.activator; import dev.isxander.controlify.impl.action.Accumulator; diff --git a/src/main/java/dev/isxander/controlify/impl/action/activator/GuiPressPulseActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/GuiPressPulseActivator.java index 4fb6e5317..22ab501b8 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/activator/GuiPressPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/GuiPressPulseActivator.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.impl.action.activator; import dev.isxander.controlify.impl.action.Accumulator; diff --git a/src/main/java/dev/isxander/controlify/impl/action/activator/HoldLatchActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/HoldLatchActivator.java index e40dcbe21..0b654b9c0 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/activator/HoldLatchActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/HoldLatchActivator.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.impl.action.activator; import dev.isxander.controlify.impl.action.Accumulator; diff --git a/src/main/java/dev/isxander/controlify/impl/action/activator/HoldPulseActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/HoldPulseActivator.java index 6b817b9c3..1b60ae7b5 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/activator/HoldPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/HoldPulseActivator.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.impl.action.activator; import dev.isxander.controlify.impl.action.Accumulator; diff --git a/src/main/java/dev/isxander/controlify/impl/action/activator/NoopActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/NoopActivator.java index 1fc73f482..2f24b8d06 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/activator/NoopActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/NoopActivator.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.impl.action.activator; import dev.isxander.controlify.impl.action.Accumulator; diff --git a/src/main/java/dev/isxander/controlify/impl/action/activator/PressPulseActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/PressPulseActivator.java index fa5f093fe..7964278cb 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/activator/PressPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/PressPulseActivator.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.impl.action.activator; import dev.isxander.controlify.impl.action.Accumulator; diff --git a/src/main/java/dev/isxander/controlify/impl/action/activator/ReleasePulseActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/ReleasePulseActivator.java index 698b0889f..655812293 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/activator/ReleasePulseActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/ReleasePulseActivator.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.impl.action.activator; import dev.isxander.controlify.impl.action.Accumulator; diff --git a/src/main/java/dev/isxander/controlify/impl/action/activator/RepeatLatchPulseActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/RepeatLatchPulseActivator.java index 0f8c50896..3712256f6 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/activator/RepeatLatchPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/RepeatLatchPulseActivator.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.impl.action.activator; import com.mojang.serialization.Codec; diff --git a/src/main/java/dev/isxander/controlify/impl/action/activator/TapPulseActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/TapPulseActivator.java index 94679b10e..81cc44d6d 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/activator/TapPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/TapPulseActivator.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.impl.action.activator; import dev.isxander.controlify.impl.action.Accumulator; diff --git a/src/main/java/dev/isxander/controlify/impl/action/activator/ToggleLatchActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/ToggleLatchActivator.java index fe8012e29..7a3977f05 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/activator/ToggleLatchActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/ToggleLatchActivator.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.impl.action.activator; import com.mojang.serialization.MapCodec; diff --git a/src/main/java/dev/isxander/controlify/impl/action/proxy/ActionProxyManagerImpl.java b/src/main/java/dev/isxander/controlify/impl/action/proxy/ActionProxyManagerImpl.java index 279168d66..fa6e2b737 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/proxy/ActionProxyManagerImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/action/proxy/ActionProxyManagerImpl.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.impl.action.proxy; import dev.isxander.controlify.api.action.ActionProxyManager; diff --git a/src/main/java/dev/isxander/controlify/impl/action/proxy/KeyMappingProxy.java b/src/main/java/dev/isxander/controlify/impl/action/proxy/KeyMappingProxy.java index 267b2034c..ddd38e4b7 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/proxy/KeyMappingProxy.java +++ b/src/main/java/dev/isxander/controlify/impl/action/proxy/KeyMappingProxy.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.impl.action.proxy; import dev.isxander.controlify.api.action.Action; diff --git a/src/main/java/dev/isxander/controlify/impl/action/radial/RadialIconManager.java b/src/main/java/dev/isxander/controlify/impl/action/radial/RadialIconManager.java index d89e4eba7..b1cc5c6e2 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/radial/RadialIconManager.java +++ b/src/main/java/dev/isxander/controlify/impl/action/radial/RadialIconManager.java @@ -36,7 +36,7 @@ public class RadialIconManager implements SimpleControlifyReloadListener GamepadInputs.RIGHT_STICK_AXIS_DOWN; case (SDL_GAMEPAD_AXIS_RIGHTY << 1) | 1 -> GamepadInputs.RIGHT_STICK_AXIS_UP; case (SDL_GAMEPAD_AXIS_LEFT_TRIGGER << 1), - (SDL_GAMEPAD_AXIS_LEFT_TRIGGER << 1) | 1 -> GamepadInputs.LEFT_TRIGGER_AXIS; + (SDL_GAMEPAD_AXIS_LEFT_TRIGGER << 1) | 1 -> GamepadInputs.LEFT_TRIGGER_AXIS; case (SDL_GAMEPAD_AXIS_RIGHT_TRIGGER << 1), - (SDL_GAMEPAD_AXIS_RIGHT_TRIGGER << 1) | 1 -> GamepadInputs.RIGHT_TRIGGER_AXIS; + (SDL_GAMEPAD_AXIS_RIGHT_TRIGGER << 1) | 1 -> GamepadInputs.RIGHT_TRIGGER_AXIS; default -> throw new IllegalArgumentException("Unknown axis"); }) .toArray(Identifier[]::new); diff --git a/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLToPhysicalStage.java b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLToPhysicalStage.java index 3e52d77f9..d4d04025d 100644 --- a/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLToPhysicalStage.java +++ b/src/main/java/dev/isxander/controlify/impl/driver/sdl/SDLToPhysicalStage.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.impl.driver.sdl; import dev.isxander.controlify.impl.input.mapping.JoystickInputs; diff --git a/src/main/java/dev/isxander/controlify/impl/input/SensorType.java b/src/main/java/dev/isxander/controlify/impl/input/SensorType.java index 4087817a6..5a3f099f2 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/SensorType.java +++ b/src/main/java/dev/isxander/controlify/impl/input/SensorType.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.impl.input; public enum SensorType { diff --git a/src/main/java/dev/isxander/controlify/impl/input/mapping/ControllerMappingManager.java b/src/main/java/dev/isxander/controlify/impl/input/mapping/ControllerMappingManager.java index c4c7f5290..d4ea9f337 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/mapping/ControllerMappingManager.java +++ b/src/main/java/dev/isxander/controlify/impl/input/mapping/ControllerMappingManager.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.impl.input.mapping; import com.google.gson.JsonElement; diff --git a/src/main/java/dev/isxander/controlify/impl/input/mapping/GestureInputs.java b/src/main/java/dev/isxander/controlify/impl/input/mapping/GestureInputs.java index ba67cfafc..4871f7861 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/mapping/GestureInputs.java +++ b/src/main/java/dev/isxander/controlify/impl/input/mapping/GestureInputs.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.impl.input.mapping; import dev.isxander.controlify.impl.utils.CUtil; diff --git a/src/main/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStage.java index cc78fb168..38afe6d01 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStage.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.impl.input.pipeline; import dev.isxander.controlify.impl.input.mapping.DeadzoneGroup; diff --git a/src/main/java/dev/isxander/controlify/impl/input/pipeline/GestureRecogniserStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/GestureRecogniserStage.java index e3bb3bee9..2f1d66fba 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/pipeline/GestureRecogniserStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/GestureRecogniserStage.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.impl.input.pipeline; import dev.isxander.controlify.impl.input.mapping.GestureInputs; diff --git a/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputDomainExpansionStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputDomainExpansionStage.java index 315ec3d23..3712d6118 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputDomainExpansionStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputDomainExpansionStage.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.impl.input.pipeline; import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; diff --git a/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputPipeline.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputPipeline.java index 2a4bce81b..1cba6c27d 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputPipeline.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/InputPipeline.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.impl.input.pipeline; import dev.isxander.controlify.impl.input.mapping.ControllerMapping; diff --git a/src/main/java/dev/isxander/controlify/impl/input/pipeline/MappingStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/MappingStage.java index 13121c633..4b4735f2f 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/pipeline/MappingStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/MappingStage.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.impl.input.pipeline; import dev.isxander.controlify.impl.input.mapping.ControllerMapping; diff --git a/src/main/java/dev/isxander/controlify/impl/input/pipeline/PhysicalToLogicalStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/PhysicalToLogicalStage.java index 449a505e0..cc7b9009c 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/pipeline/PhysicalToLogicalStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/PhysicalToLogicalStage.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.impl.input.pipeline; import dev.isxander.controlify.api.input.LogicalInput; diff --git a/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorAccumulatorStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorAccumulatorStage.java index b53677a41..ccf8c3b7a 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorAccumulatorStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorAccumulatorStage.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.impl.input.pipeline; import dev.isxander.controlify.impl.input.SensorType; diff --git a/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorProcessorStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorProcessorStage.java index e7e5c69f5..515164bf5 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorProcessorStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/SensorProcessorStage.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.impl.input.pipeline; import dev.isxander.controlify.impl.input.SensorType; diff --git a/src/main/java/dev/isxander/controlify/impl/input/signal/LogicalSignal.java b/src/main/java/dev/isxander/controlify/impl/input/signal/LogicalSignal.java index d8bf4ae14..192d8aa6c 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/signal/LogicalSignal.java +++ b/src/main/java/dev/isxander/controlify/impl/input/signal/LogicalSignal.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.impl.input.signal; import dev.isxander.controlify.api.input.LogicalInput; diff --git a/src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalInputSignal.java b/src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalInputSignal.java index d8d19fd9e..f46414932 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalInputSignal.java +++ b/src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalInputSignal.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.impl.input.signal; import net.minecraft.resources.Identifier; diff --git a/src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalSensorSignal.java b/src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalSensorSignal.java index 87389424c..07c384b14 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalSensorSignal.java +++ b/src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalSensorSignal.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.impl.input.signal; import dev.isxander.controlify.impl.input.SensorType; diff --git a/src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalSignal.java b/src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalSignal.java index 37485d8f2..59f730942 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalSignal.java +++ b/src/main/java/dev/isxander/controlify/impl/input/signal/PhysicalSignal.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.impl.input.signal; public sealed interface PhysicalSignal permits PhysicalInputSignal, PhysicalSensorSignal, PhysicalSignal.Frame { diff --git a/src/main/java/dev/isxander/controlify/impl/utils/SetupWizardManager.java b/src/main/java/dev/isxander/controlify/impl/utils/SetupWizardManager.java index 87422053c..7f418137a 100644 --- a/src/main/java/dev/isxander/controlify/impl/utils/SetupWizardManager.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/SetupWizardManager.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.impl.utils; import dev.isxander.controlify.impl.gui.screen.DontInterruptScreen; diff --git a/src/main/java/dev/isxander/controlify/impl/utils/event/EventBus.java b/src/main/java/dev/isxander/controlify/impl/utils/event/EventBus.java index 11e379ce7..57e85aa02 100644 --- a/src/main/java/dev/isxander/controlify/impl/utils/event/EventBus.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/event/EventBus.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.impl.utils.event; import dev.isxander.controlify.api.utils.event.EventSink; diff --git a/src/main/java/dev/isxander/controlify/impl/utils/event/EventTap.java b/src/main/java/dev/isxander/controlify/impl/utils/event/EventTap.java index e61698f2d..ab78bce23 100644 --- a/src/main/java/dev/isxander/controlify/impl/utils/event/EventTap.java +++ b/src/main/java/dev/isxander/controlify/impl/utils/event/EventTap.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.impl.utils.event; import dev.isxander.controlify.api.utils.event.EventSink; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/bind/KeyMappingMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/bind/KeyMappingMixin.java index 055824d02..8182164ed 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/bind/KeyMappingMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/bind/KeyMappingMixin.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.feature.bind; import dev.isxander.controlify.impl.action.KeyMappingHandle; diff --git a/src/test/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStageTest.java b/src/test/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStageTest.java index d0f22b778..20ea41cfa 100644 --- a/src/test/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStageTest.java +++ b/src/test/java/dev/isxander/controlify/impl/input/pipeline/DeadzoneStageTest.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.impl.input.pipeline; import dev.isxander.controlify.impl.input.mapping.DeadzoneGroup; From 8650878d1e690bc409ed30765384826800b3d724 Mon Sep 17 00:00:00 2001 From: isxander Date: Thu, 22 Oct 2026 23:56:24 +0100 Subject: [PATCH 22/44] screenop 2 --- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle.kts | 7 +- .../fabric/impl/ControlifyBootstrap.java | 1 + .../gametest/tests/KeyboardLayoutTests.java | 2 +- .../tests/RecipeBookNavigationTests.java | 4 +- .../controlify/api/action/Channel.java | 8 + .../controlify/api/gui/ButtonGuideApi.java | 24 +- .../api/gui/keyboard/InputTarget.java | 50 ++ .../api/gui/keyboard/KeyboardLayouts.java | 19 + .../api/gui/keyboard/KeyboardPositioner.java | 62 ++ .../api/gui/keyboard/package-info.java | 4 + .../api/screenop/ElementBehaviour.java | 35 ++ .../api/screenop/ElementBehaviourFactory.java | 8 + .../screenop/ElementBehaviourRegistry.java | 27 + .../api/screenop/ElementBehaviours.java | 107 ++++ .../screenop/ElementKeyboardBehaviour.java | 40 ++ .../api/screenop/ISnapBehaviour.java | 27 - .../api/screenop/ScreenBehaviour.java | 40 ++ .../api/screenop/ScreenBehaviourFactory.java | 8 + .../api/screenop/ScreenBehaviourRegistry.java | 31 + .../api/screenop/ScreenBehaviours.java | 44 ++ .../api/screenop/ScreenContext.java | 53 ++ .../api/screenop/ScreenopEvents.java | 21 + .../controlify/api/screenop/SnapPoint.java | 2 +- .../controlify/api/screenop/VirtualMouse.java | 33 ++ .../api/screenop/VirtualMouseMode.java | 25 + .../controlify/api/screenop/package-info.java | 29 + .../isxander/controlify/impl/Controlify.java | 67 +-- .../action/ActionContextRegistryImpl.java | 12 +- .../controlify/impl/action/ActionGraph.java | 6 +- .../controlify/impl/action/ActionState.java | 9 + .../activator/RepeatLatchPulseActivator.java | 2 +- .../impl/action/proxy/KeyMappingProxy.java | 4 +- .../impl/compatibility/ControlifyCompat.java | 10 + .../mixins/BaseOptionElementWidgetMixin.java | 26 +- .../BaseOptionElementComponentProcessor.java | 27 - .../iris/screenop/IrisScreenBehaviours.java | 17 + .../iris/screenop/package-info.java | 4 + .../mixins/SodiumVideoOptionsScreenMixin.java | 19 +- .../SimpleVoiceChatCompat.java | 4 +- .../mixins/CycleControlElementMixin.java | 19 +- .../mixins/SliderControlElementMixin.java | 21 +- .../mixins/TickBoxControlElementMixin.java | 16 +- .../mixins/VideoSettingsScreenMixin.java | 36 +- .../screenop/CycleControlProcessor.java | 38 -- .../screenop/SliderControlProcessor.java | 24 - ...cessor.java => SodiumScreenBehaviour.java} | 47 +- .../screenop/SodiumScreenBehaviours.java | 40 ++ .../screenop/TickBoxControlProcessor.java | 34 -- .../sodium/screenop/package-info.java | 4 + .../mixins/CyclingControllerElementMixin.java | 18 +- .../yacl/mixins/OptionListWidgetMixin.java | 16 +- .../mixins/SliderControllerElementMixin.java | 18 +- .../mixins/StringControllerElementMixin.java | 17 +- .../mixins/YACLScreenCategoryTabMixin.java | 6 +- .../yacl/mixins/YACLScreenMixin.java | 15 +- ...ngControllerElementComponentProcessor.java | 44 -- ...or.java => OptionListWidgetBehaviour.java} | 52 +- ...erControllerElementComponentProcessor.java | 42 -- ... => StringControllerElementBehaviour.java} | 32 +- ...rocessor.java => YACLScreenBehaviour.java} | 36 +- .../yacl/screenop/YACLScreenBehaviours.java | 24 + .../yacl/screenop/package-info.java | 4 + .../controlify/impl/config/ConfigManager.java | 6 +- .../ControllerCoordinator.java | 19 + .../impl/gui/ButtonGuideApiImpl.java | 24 + .../impl/gui/controllers/BindController.java | 19 +- .../keyboard/CommonKeyboardHints.java | 2 +- .../gui/keyboard/DelegatingInputTarget.java | 57 ++ .../impl/gui/keyboard/EditBoxInputTarget.java | 60 ++ .../keyboard/FallbackKeyboardLayout.java | 10 +- .../{screenop => gui}/keyboard/KeyWidget.java | 81 +-- .../keyboard/KeyboardLayout.java | 35 +- .../keyboard/KeyboardLayoutManager.java | 9 +- .../keyboard/KeyboardLayoutWithId.java | 5 +- .../keyboard/KeyboardOverlayScreen.java | 72 +-- .../keyboard/KeyboardWidget.java | 49 +- .../impl/gui/screen/BindConsumerScreen.java | 34 +- .../gui/screen/ControlifySettingsScreen.java | 56 +- .../screen/ControllerMappingMakerScreen.java | 46 +- .../gui/screen/ProfileSelectionScreen.java | 39 +- .../impl/gui/screen/RadialMenuEditScreen.java | 68 +-- .../impl/gui/screen/RadialMenuScreen.java | 62 +- .../impl/ingame/ControllerPlayerMovement.java | 6 +- .../impl/ingame/InGameInputHandler.java | 82 +-- .../pipeline/PhysicalToLogicalStage.java | 2 +- .../impl/input/signal/LogicalSignal.java | 2 +- .../impl/screenop/BehaviourChain.java | 51 ++ .../impl/screenop/BehaviourRegistry.java | 62 ++ .../screenop/BuiltinScreenBehaviours.java | 210 +++++++ .../impl/screenop/ComponentProcessor.java | 38 -- .../screenop/ComponentProcessorProvider.java | 27 - .../screenop/CompositeElementBehaviour.java | 90 +++ .../screenop/CompositeScreenBehaviour.java | 94 ++++ .../screenop/ConditionalElementBehaviour.java | 62 ++ .../screenop/ConditionalScreenBehaviour.java | 85 +++ .../controlify/impl/screenop/CustomFocus.java | 13 - .../ElementBehaviourRegistryImpl.java | 52 ++ .../controlify/impl/screenop/Registry.java | 56 -- .../screenop/ScreenBehaviourRegistryImpl.java | 90 +++ .../impl/screenop/ScreenContextHolder.java | 16 + .../impl/screenop/ScreenContextImpl.java | 304 ++++++++++ .../ScreenControllerEventListener.java | 13 - .../impl/screenop/ScreenProcessor.java | 333 ----------- .../impl/screenop/ScreenProcessorFactory.java | 41 -- .../screenop/ScreenProcessorProvider.java | 23 - .../impl/screenop/ScreenRuntime.java | 81 +++ .../impl/screenop/ScreenWidgets.java | 47 ++ .../impl/screenop/VirtualMouseState.java | 389 +++++++++++++ .../behaviours/element/ButtonBehaviour.java | 43 ++ .../behaviours/element/CycleBehaviour.java | 49 ++ .../element/DefaultContainerBehaviour.java | 31 + .../element/DefaultEditBoxBehaviour.java | 45 ++ .../element/DefaultWidgetBehaviour.java | 29 + .../element/LayoutElementSnapBehaviour.java | 34 ++ .../behaviours/element/SliderBehaviour.java | 90 +++ .../behaviours/element/package-info.java | 4 + .../screen/BackButtonBehaviour.java | 29 + .../screen/FocusInputBehaviour.java | 69 +++ .../screen/FocusManagementBehaviour.java | 76 +++ .../screen/FocusedElementBehaviour.java | 29 + .../screen/ScreenSnapPointBehaviour.java | 29 + .../screen/TabNavigationBehaviour.java | 49 ++ .../behaviours/screen/package-info.java | 4 + .../BundleItemSlotControllerAction.java | 87 +++ .../vanilla/ChatScreenBehaviour.java} | 59 +- .../vanilla/ContainerScreenBehaviour.java | 120 ++++ .../vanilla/CreateWorldScreenBehaviour.java | 33 ++ ...CreativeModeInventoryScreenBehaviour.java} | 52 +- .../vanilla/DoneButtonGuideBehaviour.java | 28 + .../vanilla/ItemSlotControllerAction.java | 2 +- .../JoinMultiplayerScreenBehaviour.java} | 51 +- .../vanilla/PauseScreenBehaviour.java} | 46 +- .../vanilla/RecipeBookScreenBehaviour.java | 87 +++ .../vanilla/SelectWorldScreenBehaviour.java} | 27 +- .../vanilla/ServerAddressBehaviour.java | 75 +++ .../vanilla/ServerAddressScreenAccess.java | 7 + .../vanilla/SignEditingBehaviour.java} | 54 +- .../vanilla/TitleScreenBehaviour.java | 71 +++ .../behaviours/vanilla/package-info.java | 4 + .../virtualmouse/CursorClickBehaviour.java | 30 + .../virtualmouse/CursorModeBehaviour.java | 78 +++ .../virtualmouse/CursorMovementBehaviour.java | 59 ++ .../virtualmouse/CursorRenderBehaviour.java | 63 +++ .../virtualmouse/CursorScrollBehaviour.java | 34 ++ .../behaviours/virtualmouse/package-info.java | 4 + .../AbstractSliderComponentProcessor.java | 37 -- .../AbstractButtonComponentProcessor.java | 37 -- .../AbstractContainerScreenProcessor.java | 168 ------ .../vanilla/AddServerLikeScreenProcessor.java | 84 --- .../BundleItemSlotControllerAction.java | 87 --- .../vanilla/CreateWorldScreenProcessor.java | 31 - .../vanilla/EditBoxComponentProcessor.java | 129 ----- ...nguageSelectionListComponentProcessor.java | 43 -- .../vanilla/OptionsScreenProcessor.java | 39 -- .../vanilla/OptionsSubScreenProcessor.java | 39 -- .../vanilla/RecipeBookScreenProcessor.java | 91 --- ...rSelectionListEntryComponentProcessor.java | 25 - .../vanilla/SliderComponentProcessor.java | 69 --- .../compat/vanilla/TitleScreenProcessor.java | 66 --- .../WorldListEntryComponentProcessor.java | 28 - .../keyboard/ComponentKeyboardBehaviour.java | 42 -- .../impl/screenop/keyboard/InputTarget.java | 92 --- .../screenop/keyboard/KeyboardLayouts.java | 42 -- .../impl/screenop/package-info.java | 4 + .../virtualmouse/VirtualMouseBehaviour.java | 23 - .../virtualmouse/VirtualMouseHandler.java | 531 ------------------ .../mixins/core/MinecraftMixin.java | 8 +- .../screenop/ContainerEventHandlerMixin.java | 25 - .../mixins/feature/screenop/GuiMixin.java | 10 +- .../feature/screenop}/MixinInputTarget.java | 4 +- .../mixins/feature/screenop/ScreenMixin.java | 32 +- .../impl/bundle/BundleMouseActionsMixin.java | 4 +- .../impl/chat/ChatComponentMixin.java | 2 +- .../impl/chat}/ChatKeyboardDucky.java | 2 +- .../screenop/impl/chat/ChatScreenMixin.java | 42 +- .../impl/chat/CommandSuggestionsMixin.java | 11 +- .../AbstractContainerScreenMixin.java | 61 +- .../AbstractRecipeBookScreenMixin.java | 22 +- .../CreativeModeInventoryScreenMixin.java | 61 +- .../impl/elements/AbstractButtonMixin.java | 25 - .../AbstractContainerEventHandlerMixin.java | 24 - .../elements/AbstractSliderButtonMixin.java | 45 -- .../impl/elements/AbstractWidgetMixin.java | 8 +- ...ontainerObjectSelectionListEntryMixin.java | 24 - .../screenop/impl/elements/EditBoxMixin.java | 42 +- .../outofgame/CreateWorldScreenMixin.java | 25 +- .../DirectJoinServerScreenMixin.java | 32 +- .../outofgame/JoinMultiplayerScreenMixin.java | 37 +- .../LanguageSelectionListEntryMixin.java | 44 +- .../outofgame/ManageServerScreenMixin.java | 33 +- .../impl/outofgame/OptionsScreenMixin.java | 20 +- .../impl/outofgame/OptionsSubScreenMixin.java | 22 +- .../impl/outofgame/PauseScreenMixin.java | 19 +- .../outofgame/SelectWorldScreenMixin.java | 17 +- .../ServerSelectionListEntryMixin.java | 27 - .../impl/outofgame/TitleScreenMixin.java | 25 - .../WorldSelectionListEntryMixin.java | 24 - .../sign/AbstractSignEditScreenMixin.java | 61 +- .../screenop/runtime/ScreenContextMixin.java | 58 ++ .../screenop/runtime/SliderAccessor.java | 20 + .../mixins/feature/virtualmouse/GuiMixin.java | 7 +- .../feature/virtualmouse/MinecraftMixin.java | 7 +- .../AbstractContainerScreenMixin.java | 50 -- .../AbstractRecipeBookScreenMixin.java | 32 -- .../CreativeModeInventoryScreenMixin.java | 33 +- .../snapping/EnchantmentScreenMixin.java | 72 ++- .../snapping/LoomScreenMixin.java | 123 ++-- .../virtualmouse/snapping/ScreenMixin.java | 43 -- .../snapping/StonecutterScreenMixin.java | 79 ++- src/main/resources/controlify.mixins.json | 13 +- .../neoforge/impl/ControlifyBootstrap.java | 5 +- 212 files changed, 5248 insertions(+), 4065 deletions(-) create mode 100644 src/main/java/dev/isxander/controlify/api/gui/keyboard/InputTarget.java create mode 100644 src/main/java/dev/isxander/controlify/api/gui/keyboard/KeyboardLayouts.java create mode 100644 src/main/java/dev/isxander/controlify/api/gui/keyboard/KeyboardPositioner.java create mode 100644 src/main/java/dev/isxander/controlify/api/gui/keyboard/package-info.java create mode 100644 src/main/java/dev/isxander/controlify/api/screenop/ElementBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/api/screenop/ElementBehaviourFactory.java create mode 100644 src/main/java/dev/isxander/controlify/api/screenop/ElementBehaviourRegistry.java create mode 100644 src/main/java/dev/isxander/controlify/api/screenop/ElementBehaviours.java create mode 100644 src/main/java/dev/isxander/controlify/api/screenop/ElementKeyboardBehaviour.java delete mode 100644 src/main/java/dev/isxander/controlify/api/screenop/ISnapBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/api/screenop/ScreenBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/api/screenop/ScreenBehaviourFactory.java create mode 100644 src/main/java/dev/isxander/controlify/api/screenop/ScreenBehaviourRegistry.java create mode 100644 src/main/java/dev/isxander/controlify/api/screenop/ScreenBehaviours.java create mode 100644 src/main/java/dev/isxander/controlify/api/screenop/ScreenContext.java create mode 100644 src/main/java/dev/isxander/controlify/api/screenop/ScreenopEvents.java create mode 100644 src/main/java/dev/isxander/controlify/api/screenop/VirtualMouse.java create mode 100644 src/main/java/dev/isxander/controlify/api/screenop/VirtualMouseMode.java create mode 100644 src/main/java/dev/isxander/controlify/api/screenop/package-info.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/compatibility/iris/screenop/BaseOptionElementComponentProcessor.java create mode 100644 src/main/java/dev/isxander/controlify/impl/compatibility/iris/screenop/IrisScreenBehaviours.java create mode 100644 src/main/java/dev/isxander/controlify/impl/compatibility/iris/screenop/package-info.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/CycleControlProcessor.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SliderControlProcessor.java rename src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/{SodiumGuiScreenProcessor.java => SodiumScreenBehaviour.java} (57%) create mode 100644 src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumScreenBehaviours.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/TickBoxControlProcessor.java create mode 100644 src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/package-info.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/CyclingControllerElementComponentProcessor.java rename src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/{OptionListWidgetComponentProcessor.java => OptionListWidgetBehaviour.java} (54%) delete mode 100644 src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/SliderControllerElementComponentProcessor.java rename src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/{StringControllerElementComponentProcessor.java => StringControllerElementBehaviour.java} (61%) rename src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/{YACLScreenProcessor.java => YACLScreenBehaviour.java} (57%) create mode 100644 src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/YACLScreenBehaviours.java create mode 100644 src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/package-info.java create mode 100644 src/main/java/dev/isxander/controlify/impl/gui/ButtonGuideApiImpl.java rename src/main/java/dev/isxander/controlify/impl/{screenop => gui}/keyboard/CommonKeyboardHints.java (94%) create mode 100644 src/main/java/dev/isxander/controlify/impl/gui/keyboard/DelegatingInputTarget.java create mode 100644 src/main/java/dev/isxander/controlify/impl/gui/keyboard/EditBoxInputTarget.java rename src/main/java/dev/isxander/controlify/impl/{screenop => gui}/keyboard/FallbackKeyboardLayout.java (90%) rename src/main/java/dev/isxander/controlify/impl/{screenop => gui}/keyboard/KeyWidget.java (82%) rename src/main/java/dev/isxander/controlify/impl/{screenop => gui}/keyboard/KeyboardLayout.java (90%) rename src/main/java/dev/isxander/controlify/impl/{screenop => gui}/keyboard/KeyboardLayoutManager.java (96%) rename src/main/java/dev/isxander/controlify/impl/{screenop => gui}/keyboard/KeyboardLayoutWithId.java (80%) rename src/main/java/dev/isxander/controlify/impl/{screenop => gui}/keyboard/KeyboardOverlayScreen.java (65%) rename src/main/java/dev/isxander/controlify/impl/{screenop => gui}/keyboard/KeyboardWidget.java (85%) create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/BehaviourChain.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/BehaviourRegistry.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/BuiltinScreenBehaviours.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/ComponentProcessor.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/ComponentProcessorProvider.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/CompositeElementBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/CompositeScreenBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/ConditionalElementBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/ConditionalScreenBehaviour.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/CustomFocus.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/ElementBehaviourRegistryImpl.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/Registry.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/ScreenBehaviourRegistryImpl.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/ScreenContextHolder.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/ScreenContextImpl.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/ScreenControllerEventListener.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessor.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessorFactory.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessorProvider.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/ScreenRuntime.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/ScreenWidgets.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/VirtualMouseState.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/ButtonBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/CycleBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/DefaultContainerBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/DefaultEditBoxBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/DefaultWidgetBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/LayoutElementSnapBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/SliderBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/package-info.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/BackButtonBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/FocusInputBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/FocusManagementBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/FocusedElementBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/ScreenSnapPointBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/TabNavigationBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/package-info.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/BundleItemSlotControllerAction.java rename src/main/java/dev/isxander/controlify/impl/screenop/{compat/vanilla/ChatScreenProcessor.java => behaviours/vanilla/ChatScreenBehaviour.java} (69%) create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/ContainerScreenBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/CreateWorldScreenBehaviour.java rename src/main/java/dev/isxander/controlify/impl/screenop/{compat/vanilla/CreativeModeInventoryScreenProcessor.java => behaviours/vanilla/CreativeModeInventoryScreenBehaviour.java} (51%) create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/DoneButtonGuideBehaviour.java rename src/main/java/dev/isxander/controlify/impl/screenop/{compat => behaviours}/vanilla/ItemSlotControllerAction.java (87%) rename src/main/java/dev/isxander/controlify/impl/screenop/{compat/vanilla/JoinMultiplayerScreenProcessor.java => behaviours/vanilla/JoinMultiplayerScreenBehaviour.java} (65%) rename src/main/java/dev/isxander/controlify/impl/screenop/{compat/vanilla/PauseScreenProcessor.java => behaviours/vanilla/PauseScreenBehaviour.java} (57%) create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/RecipeBookScreenBehaviour.java rename src/main/java/dev/isxander/controlify/impl/screenop/{compat/vanilla/SelectWorldScreenProcessor.java => behaviours/vanilla/SelectWorldScreenBehaviour.java} (61%) create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/ServerAddressBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/ServerAddressScreenAccess.java rename src/main/java/dev/isxander/controlify/impl/screenop/{compat/vanilla/AbstractSignEditScreenProcessor.java => behaviours/vanilla/SignEditingBehaviour.java} (69%) create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/TitleScreenBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/package-info.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/virtualmouse/CursorClickBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/virtualmouse/CursorModeBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/virtualmouse/CursorMovementBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/virtualmouse/CursorRenderBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/virtualmouse/CursorScrollBehaviour.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/behaviours/virtualmouse/package-info.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/compat/AbstractSliderComponentProcessor.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractButtonComponentProcessor.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AbstractContainerScreenProcessor.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/AddServerLikeScreenProcessor.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/BundleItemSlotControllerAction.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/CreateWorldScreenProcessor.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/EditBoxComponentProcessor.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/LanguageSelectionListComponentProcessor.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/OptionsScreenProcessor.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/OptionsSubScreenProcessor.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/RecipeBookScreenProcessor.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ServerSelectionListEntryComponentProcessor.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/SliderComponentProcessor.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/TitleScreenProcessor.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/WorldListEntryComponentProcessor.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/keyboard/ComponentKeyboardBehaviour.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/keyboard/InputTarget.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayouts.java create mode 100644 src/main/java/dev/isxander/controlify/impl/screenop/package-info.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/virtualmouse/VirtualMouseBehaviour.java delete mode 100644 src/main/java/dev/isxander/controlify/impl/virtualmouse/VirtualMouseHandler.java delete mode 100644 src/main/java/dev/isxander/controlify/mixins/feature/screenop/ContainerEventHandlerMixin.java rename src/main/java/dev/isxander/controlify/{impl/screenop/keyboard => mixins/feature/screenop}/MixinInputTarget.java (93%) rename src/main/java/dev/isxander/controlify/{impl/screenop/keyboard => mixins/feature/screenop/impl/chat}/ChatKeyboardDucky.java (85%) delete mode 100644 src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/AbstractButtonMixin.java delete mode 100644 src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/AbstractContainerEventHandlerMixin.java delete mode 100644 src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/AbstractSliderButtonMixin.java delete mode 100644 src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/elements/ContainerObjectSelectionListEntryMixin.java delete mode 100644 src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/ServerSelectionListEntryMixin.java delete mode 100644 src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/TitleScreenMixin.java delete mode 100644 src/main/java/dev/isxander/controlify/mixins/feature/screenop/impl/outofgame/WorldSelectionListEntryMixin.java create mode 100644 src/main/java/dev/isxander/controlify/mixins/feature/screenop/runtime/ScreenContextMixin.java create mode 100644 src/main/java/dev/isxander/controlify/mixins/feature/screenop/runtime/SliderAccessor.java delete mode 100644 src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/AbstractContainerScreenMixin.java delete mode 100644 src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/AbstractRecipeBookScreenMixin.java delete mode 100644 src/main/java/dev/isxander/controlify/mixins/feature/virtualmouse/snapping/ScreenMixin.java diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 683f1763c..fd35d1463 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Tue Jul 21 16:53:14 BST 2026 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index 96f0aab63..27a272756 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,7 +15,10 @@ pluginManagement { } } exclusiveContent { - forRepository { maven("https://maven.kikugie.dev/releases") } + forRepositories( + maven("https://maven.kikugie.dev/releases"), + maven("https://maven.kikugie.dev/snapshots") + ) filter { includeGroupAndSubgroups("dev.kikugie") } } exclusiveContent { @@ -28,7 +31,7 @@ pluginManagement { } plugins { - id("dev.kikugie.stonecutter") version "0.9.7" + id("dev.kikugie.stonecutter") version "0.10-beta.1" id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" } diff --git a/src/fabric/java/dev/isxander/controlify/fabric/impl/ControlifyBootstrap.java b/src/fabric/java/dev/isxander/controlify/fabric/impl/ControlifyBootstrap.java index 0e2be3af3..3aa16b45a 100644 --- a/src/fabric/java/dev/isxander/controlify/fabric/impl/ControlifyBootstrap.java +++ b/src/fabric/java/dev/isxander/controlify/fabric/impl/ControlifyBootstrap.java @@ -20,6 +20,7 @@ public class ControlifyBootstrap implements ClientModInitializer, ModInitializer @Override public void onInitializeClient() { PlatformClientUtil.IMPL = new FabricPlatformClientImpl(); + dev.isxander.controlify.impl.screenop.BuiltinScreenBehaviours.initialize(); PlatformMainUtil.invokeControlifyEntrypoints(); Controlify.instance().preInitialiseControlify(); } 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 623dda7ee..82218a188 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/KeyboardLayoutTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/KeyboardLayoutTests.java @@ -8,7 +8,7 @@ import dev.isxander.controlify.gametest.framework.CTestUtil; import dev.isxander.controlify.gametest.framework.controller.ControlifyGameTestContext; -import dev.isxander.controlify.impl.screenop.keyboard.KeyboardWidget; +import dev.isxander.controlify.impl.gui.keyboard.KeyboardWidget; import dev.isxander.controlify.impl.utils.MinecraftUtil; import dev.isxander.sdl.SdlGamepad; import net.fabricmc.api.ClientModInitializer; diff --git a/src/gametest/java/dev/isxander/controlify/gametest/tests/RecipeBookNavigationTests.java b/src/gametest/java/dev/isxander/controlify/gametest/tests/RecipeBookNavigationTests.java index df6b882fc..0811d9080 100644 --- a/src/gametest/java/dev/isxander/controlify/gametest/tests/RecipeBookNavigationTests.java +++ b/src/gametest/java/dev/isxander/controlify/gametest/tests/RecipeBookNavigationTests.java @@ -12,7 +12,7 @@ import dev.isxander.controlify.gametest.framework.controller.ControlifyGameTestContext; import dev.isxander.controlify.gametest.mixin.RecipeBookPageAccessor; import dev.isxander.controlify.mixins.feature.virtualmouse.snapping.RecipeBookComponentAccessor; -import dev.isxander.controlify.impl.screenop.compat.vanilla.RecipeBookScreenProcessor; +import dev.isxander.controlify.impl.screenop.behaviours.vanilla.RecipeBookScreenBehaviour; import dev.isxander.controlify.impl.utils.MinecraftUtil; import dev.isxander.sdl.SdlGamepad; import net.fabricmc.fabric.api.client.gametest.v1.FabricClientGameTest; @@ -56,7 +56,7 @@ public void runTest(ClientGameTestContext context) { context.waitForScreen(CraftingScreen.class); var recipeBook = context.computeOnClient(_ -> - ((RecipeBookScreenProcessor.RecipeBookScreenAccessor) MinecraftUtil.getScreen()) + ((RecipeBookScreenBehaviour.RecipeBookScreenAccessor) MinecraftUtil.getScreen()) .controlify$getRecipeBookComponent()); var component = (RecipeBookComponentAccessor) recipeBook; var page = context.computeOnClient(_ -> diff --git a/src/main/java/dev/isxander/controlify/api/action/Channel.java b/src/main/java/dev/isxander/controlify/api/action/Channel.java index a232e1671..4aaaae01f 100644 --- a/src/main/java/dev/isxander/controlify/api/action/Channel.java +++ b/src/main/java/dev/isxander/controlify/api/action/Channel.java @@ -16,7 +16,15 @@ non-sealed interface Continuous extends Channel { /// A channel which gives single pulses, for example a button press causing a jump. non-sealed interface Pulse extends Channel { + /// Consumes one available pulse + /// Typically you would use this with a `while` statement. + /// @return true if there was a pulse to consume boolean consumePulse(); + + /// Consumes all available pulses + /// Typically you would use this with an `if` statement. + /// @return true if there was a pulse to consume + boolean consumeAllPulses(); } /// A channel which gives a stateful digital output of the action, for example a physical switch's state. diff --git a/src/main/java/dev/isxander/controlify/api/gui/ButtonGuideApi.java b/src/main/java/dev/isxander/controlify/api/gui/ButtonGuideApi.java index eab5c61ca..e96b34a00 100644 --- a/src/main/java/dev/isxander/controlify/api/gui/ButtonGuideApi.java +++ b/src/main/java/dev/isxander/controlify/api/gui/ButtonGuideApi.java @@ -7,7 +7,8 @@ package dev.isxander.controlify.api.gui; import dev.isxander.controlify.api.action.ActionAccessor; -import dev.isxander.controlify.impl.gui.ButtonGuideRenderer; +import dev.isxander.controlify.api.screenop.ElementBehaviour; +import dev.isxander.controlify.api.screenop.ScreenBehaviour; import net.minecraft.client.gui.screens.Screen; import java.util.function.Supplier; @@ -16,31 +17,26 @@ * Adds a guide to a button. This does not invoke the button press on binding trigger, only renders the guide. * This should be called every time a button is initialised, like in {@link Screen#init()} */ -public final class ButtonGuideApi { - - private ButtonGuideApi() { - } +public interface ButtonGuideApi { /** * Makes the button render the image of the binding specified. * This does not invoke the button press on binding trigger, only renders the guide. - * Custom behaviour should be handled inside a {@link dev.isxander.controlify.impl.screenop.ScreenProcessor} or {@link dev.isxander.controlify.impl.screenop.ComponentProcessor} + * Custom behaviour should be handled inside a {@link ScreenBehaviour} or {@link ElementBehaviour}. * * @param button button to render the guide for * @param action the action binding to render * @param renderPredicate whether the guide should be rendered */ - public static void addGuideToButton( + void addGuideToButton( T button, ActionAccessor action, - ButtonGuidePredicate renderPredicate) { - ButtonGuideRenderer.registerActionForButton(button, () -> action, renderPredicate); - } + ButtonGuidePredicate renderPredicate + ); - public static void addGuideToButton( + void addGuideToButton( T button, Supplier action, - ButtonGuidePredicate renderPredicate) { - ButtonGuideRenderer.registerActionForButton(button, action, renderPredicate); - } + ButtonGuidePredicate renderPredicate + ); } diff --git a/src/main/java/dev/isxander/controlify/api/gui/keyboard/InputTarget.java b/src/main/java/dev/isxander/controlify/api/gui/keyboard/InputTarget.java new file mode 100644 index 000000000..f0205f964 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/gui/keyboard/InputTarget.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.api.gui.keyboard; + +/** + * Represents a target/consumer for keyboard input. + * A target accepts input from the on-screen keyboard. + * It can have various capabilities, all of which are optional to implement. + */ +public interface InputTarget { + InputTarget EMPTY = new InputTarget() { + }; + + default boolean supportsCharInput() { + return false; + } + + default boolean acceptChar(char ch, int modifiers) { + return false; + } + + default boolean supportsKeyCodeInput() { + return false; + } + + default boolean acceptKeyCode(int keycode, int scancode, int modifiers) { + return false; + } + + default boolean supportsCopying() { + return false; + } + + default boolean copy() { + return false; + } + + default boolean supportsCursorMovement() { + return false; + } + + default boolean moveCursor(int amount) { + return false; + } + +} diff --git a/src/main/java/dev/isxander/controlify/api/gui/keyboard/KeyboardLayouts.java b/src/main/java/dev/isxander/controlify/api/gui/keyboard/KeyboardLayouts.java new file mode 100644 index 000000000..fc144e8bc --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/gui/keyboard/KeyboardLayouts.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.api.gui.keyboard; + +import net.minecraft.resources.Identifier; + +/// Resource-backed keyboard layout identifiers. Custom layouts use their own identifier. +public final class KeyboardLayouts { + public static final Identifier FULL = Identifier.fromNamespaceAndPath("controlify", "full"); + public static final Identifier SIMPLE = Identifier.fromNamespaceAndPath("controlify", "simple"); + public static final Identifier SERVER_IP = Identifier.fromNamespaceAndPath("controlify", "server_ip"); + + private KeyboardLayouts() { + } +} diff --git a/src/main/java/dev/isxander/controlify/api/gui/keyboard/KeyboardPositioner.java b/src/main/java/dev/isxander/controlify/api/gui/keyboard/KeyboardPositioner.java new file mode 100644 index 000000000..077d08e11 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/gui/keyboard/KeyboardPositioner.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.api.gui.keyboard; + +import net.minecraft.client.gui.navigation.ScreenAxis; +import net.minecraft.client.gui.navigation.ScreenRectangle; + +import java.util.function.Supplier; + +@FunctionalInterface +public interface KeyboardPositioner { + ScreenRectangle positionKeyboard(int screenWidth, int screenHeight); + + static KeyboardPositioner aboveOrBelowWidgetPositioner(int desiredKeyboardWidth, int desiredKeyboardHeight, int padding, Supplier widgetRectSupplier) { + return (screenWidth, screenHeight) -> { + ScreenRectangle widgetRect = widgetRectSupplier.get(); + + int keyboardWidth = Math.min(desiredKeyboardWidth, screenWidth); + int keyboardLeft = Math.clamp( + widgetRect.getCenterInAxis(ScreenAxis.HORIZONTAL) - keyboardWidth / 2, + 0, screenWidth - keyboardWidth + ); + + int spaceBelow = screenHeight - widgetRect.bottom() - padding; + int spaceAbove = widgetRect.top() - padding; + + // Determine the best vertical position for the keyboard. + // 1. Prefer placing the keyboard below the widget, if it fits. + // 2. If there's not enough space below, check space above. + // 3. If neither fits fully, pick the side with more space and shrink the height. + int keyboardHeight; + boolean above; + if (spaceBelow >= desiredKeyboardHeight) { + keyboardHeight = desiredKeyboardHeight; + above = false; + } else if (spaceAbove >= desiredKeyboardHeight) { + keyboardHeight = desiredKeyboardHeight; + above = true; + } else if (spaceBelow >= spaceAbove) { + keyboardHeight = spaceBelow; + above = false; + } else { + keyboardHeight = spaceAbove; + above = true; + } + int keyboardTop = above + ? widgetRect.top() - keyboardHeight - padding + : widgetRect.bottom() + padding; + + return new ScreenRectangle( + keyboardLeft, + keyboardTop, + keyboardWidth, + keyboardHeight + ); + }; + } +} diff --git a/src/main/java/dev/isxander/controlify/api/gui/keyboard/package-info.java b/src/main/java/dev/isxander/controlify/api/gui/keyboard/package-info.java new file mode 100644 index 000000000..ba2725ad8 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/gui/keyboard/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package dev.isxander.controlify.api.gui.keyboard; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/api/screenop/ElementBehaviour.java b/src/main/java/dev/isxander/controlify/api/screenop/ElementBehaviour.java new file mode 100644 index 000000000..642ff5b78 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/screenop/ElementBehaviour.java @@ -0,0 +1,35 @@ +package dev.isxander.controlify.api.screenop; + +import dev.isxander.controlify.api.controller.Controller; + +import java.util.Optional; +import java.util.function.Consumer; + +/// Stateful, per-element sidecar within one screen. Continuations are synchronous +/// and may be called at most once. Omitting one stops the remaining stage. +public interface ElementBehaviour { + default void handleNavigation(ScreenContext screen, Controller controller, Runnable next) { + next.run(); + } + + default void handleInteraction(ScreenContext screen, Controller controller, Runnable next) { + next.run(); + } + + /// A lifecycle notification, including focus changes made by other elements. + default void onFocusChanged(ScreenContext screen, boolean focused) { + } + + /// Empty inherits the next policy. DoNothing explicitly suppresses the popup + /// and ordinary activation, for elements whose screen already supplies a keyboard. + default Optional keyboardBehaviour(ScreenContext screen) { + return Optional.empty(); + } + + /// Points use scaled screen coordinates. The continuation supplies the underlying + /// behaviour: widget centres or child traversal for containers. Omitting it can + /// replace an entire container's snap points, including its descendants. + default void collectSnapPoints(ScreenContext screen, Consumer output, Runnable next) { + next.run(); + } +} diff --git a/src/main/java/dev/isxander/controlify/api/screenop/ElementBehaviourFactory.java b/src/main/java/dev/isxander/controlify/api/screenop/ElementBehaviourFactory.java new file mode 100644 index 000000000..72410e876 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/screenop/ElementBehaviourFactory.java @@ -0,0 +1,8 @@ +package dev.isxander.controlify.api.screenop; + +import net.minecraft.client.gui.components.events.GuiEventListener; + +@FunctionalInterface +public interface ElementBehaviourFactory { + ElementBehaviour create(T element); +} diff --git a/src/main/java/dev/isxander/controlify/api/screenop/ElementBehaviourRegistry.java b/src/main/java/dev/isxander/controlify/api/screenop/ElementBehaviourRegistry.java new file mode 100644 index 000000000..3aa6373be --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/screenop/ElementBehaviourRegistry.java @@ -0,0 +1,27 @@ +package dev.isxander.controlify.api.screenop; + +import net.minecraft.client.gui.components.events.GuiEventListener; + +public interface ElementBehaviourRegistry { + /// Adds a factory within this screen; registering the same type again composes + /// another behaviour instead of replacing it. Higher priorities run first, then + /// more specific types, then newest registrations. Registrations close after the event. + /// The default priority is zero; positive and negative priorities are both supported. + void register(Class elementClass, int priority, ElementBehaviourFactory factory); + + default void register(Class elementClass, ElementBehaviourFactory factory) { + register(elementClass, 0, factory); + } + + > void register(Class elementClass, int priority); + + default > void register(Class elementClass) { + register(elementClass, 0); + } + + void registerUnchecked(Class elementClass, int priority); + + default void registerUnchecked(Class elementClass) { + registerUnchecked(elementClass, 0); + } +} diff --git a/src/main/java/dev/isxander/controlify/api/screenop/ElementBehaviours.java b/src/main/java/dev/isxander/controlify/api/screenop/ElementBehaviours.java new file mode 100644 index 000000000..ffc968cb4 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/screenop/ElementBehaviours.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.api.screenop; + +import dev.isxander.controlify.impl.screenop.CompositeElementBehaviour; +import dev.isxander.controlify.impl.screenop.ConditionalElementBehaviour; +import dev.isxander.controlify.impl.screenop.behaviours.element.ButtonBehaviour; +import dev.isxander.controlify.impl.screenop.behaviours.element.CycleBehaviour; +import dev.isxander.controlify.impl.screenop.behaviours.element.LayoutElementSnapBehaviour; +import dev.isxander.controlify.impl.screenop.behaviours.element.SliderBehaviour; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.layouts.LayoutElement; + +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; +import java.util.function.ObjIntConsumer; +import java.util.function.Predicate; + +/// Reusable controller handling, independent of the element's widget implementation. +/// Factories create a separate stateful behaviour for each registered element. +public final class ElementBehaviours { + private ElementBehaviours() { + } + + /// Runs members in order. The last next() continues into the enclosing chain. + /// Focus notifications reach every member; the first present keyboard policy wins. + public static ElementBehaviour chain(List behaviours) { + return new CompositeElementBehaviour(behaviours); + } + + public static ElementBehaviour chain(ElementBehaviour... behaviours) { + return chain(List.of(behaviours)); + } + + /// False skips this behaviour and continues the enclosing chain. The condition + /// is checked on each call, while focus notifications always reach the behaviour. + public static ElementBehaviour conditional(BooleanSupplier condition, ElementBehaviour behaviour) { + return new ConditionalElementBehaviour(condition, behaviour); + } + + /// Participates while the widget is active and visible, checking both on each + /// call. Otherwise continues the enclosing chain. Focus notifications always + /// reach the behaviour so it can reset state when a disabled widget loses focus. + public static ElementBehaviourFactory enabledWidget(ElementBehaviourFactory factory) { + return widget -> conditional(() -> widget.active && widget.visible, factory.create(widget)); + } + + /// Confines cancellation to this branch, then continues the enclosing chain on + /// normal return. Exceptions propagate and changing screens still stops input. + /// Keyboard policies and focus notifications retain their ordinary semantics. + public static ElementBehaviour branch(ElementBehaviour behaviour) { + return CompositeElementBehaviour.branch(behaviour); + } + + /// Activates once per invocation, draining additional press pulses. Plays the + /// standard click sound and navigation haptic. Wrap with conditional to supply + /// an element-specific enabled/visible check. + public static ElementBehaviourFactory button(Consumer press) { + return element -> new ButtonBehaviour(() -> press.accept(element)); + } + + /// Uses the element's own click sound instead of the standard one. + public static ElementBehaviourFactory button(Consumer press, Consumer sound) { + return element -> new ButtonBehaviour(() -> press.accept(element), () -> sound.accept(element)); + } + + /// Press toggles editing; Back exits editing before closing the screen. Primary + /// horizontal navigation adjusts while editing; secondary horizontal navigation + /// adjusts directly. Each pulse calls adjust with -1 or +1. Losing focus exits + /// editing. The behaviour owns its editing state. + public static ElementBehaviourFactory slider(ObjIntConsumer adjust) { + return element -> new SliderBehaviour(direction -> adjust.accept(element, direction)); + } + + /// Secondary horizontal navigation cycles with -1 or +1; activation cycles + /// forward. Every pulse is applied, including repeated activation pulses. + public static ElementBehaviourFactory cycle(ObjIntConsumer cycle) { + return element -> new CycleBehaviour(direction -> cycle.accept(element, direction)); + } + + /// Shares an existing editing state with the element, for example when its + /// rendering or keyboard handling also depends on that state. Adjustment must + /// work regardless of editing, since secondary navigation can adjust directly. + public static ElementBehaviourFactory slider( + ObjIntConsumer adjust, + Predicate isEditing, + BiConsumer setEditing + ) { + return element -> new SliderBehaviour( + direction -> adjust.accept(element, direction), + () -> isEditing.test(element), + editing -> setEditing.accept(element, editing) + ); + } + + /// Adds a centre snap point using live layout geometry, then continues. + public static ElementBehaviour snapPoint(LayoutElement element) { + return new LayoutElementSnapBehaviour(element); + } +} diff --git a/src/main/java/dev/isxander/controlify/api/screenop/ElementKeyboardBehaviour.java b/src/main/java/dev/isxander/controlify/api/screenop/ElementKeyboardBehaviour.java new file mode 100644 index 000000000..9a77e734d --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/screenop/ElementKeyboardBehaviour.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.api.screenop; + +import dev.isxander.controlify.api.gui.keyboard.InputTarget; +import dev.isxander.controlify.api.gui.keyboard.KeyboardPositioner; +import net.minecraft.resources.Identifier; + +/** + * Describes whether and how to open a keyboard when an element is pressed. + * An absent policy is represented by Optional.empty(), allowing an underlying + * element behaviour to supply one. + */ +public sealed interface ElementKeyboardBehaviour { + + /** + * Indicates that some other support has been provided, for example by the screen itself + * and a separate keyboard widget that supports it. + */ + record DoNothing() implements ElementKeyboardBehaviour { + } + + /** + * Custom support for this element for a popup keyboard. + * + * @param layout the keyboard layout to use + * @param inputTarget the input target for the keyboard + * @param positioner the positioner for the overlaid keyboard + */ + record Handled( + Identifier layout, + InputTarget inputTarget, + KeyboardPositioner positioner + ) implements ElementKeyboardBehaviour { + } +} diff --git a/src/main/java/dev/isxander/controlify/api/screenop/ISnapBehaviour.java b/src/main/java/dev/isxander/controlify/api/screenop/ISnapBehaviour.java deleted file mode 100644 index d84226301..000000000 --- a/src/main/java/dev/isxander/controlify/api/screenop/ISnapBehaviour.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.api.screenop; - -import java.util.Set; -import java.util.function.Consumer; - -/** - * An interface to implement by gui components to define snap points for virtual mouse snapping. - * Can also be implemented in a mixin to improve compatibility. - */ -public interface ISnapBehaviour { - default void controlify$collectSnapPoints(Consumer consumer) { - for (SnapPoint point : getSnapPoints()) { - consumer.accept(point); - } - } - - @Deprecated - default Set getSnapPoints() { - return Set.of(); - } -} diff --git a/src/main/java/dev/isxander/controlify/api/screenop/ScreenBehaviour.java b/src/main/java/dev/isxander/controlify/api/screenop/ScreenBehaviour.java new file mode 100644 index 000000000..a5f37fcde --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/screenop/ScreenBehaviour.java @@ -0,0 +1,40 @@ +package dev.isxander.controlify.api.screenop; + +import dev.isxander.controlify.api.controller.Controller; +import net.minecraft.client.gui.GuiGraphicsExtractor; + +import java.util.function.Consumer; + +/// Stateful sidecar created once per screen instance. Continuations are synchronous +/// and may be called at most once. Navigation completes before interaction is routed. +public interface ScreenBehaviour { + default void handleNavigation(Controller controller, Runnable next) { + next.run(); + } + + default void handleInteraction(Controller controller, Runnable next) { + next.run(); + } + + default void onWidgetRebuild(Runnable next) { + next.run(); + } + + default void extractRenderState(GuiGraphicsExtractor graphics, float tickDelta, Controller controller, Runnable next) { + next.run(); + } + + default void collectSnapPoints(Consumer output, Runnable next) { + next.run(); + } + + /// Per-frame update before GUI mouse processing. Delta is in realtime ticks. + default void update(float delta, Runnable next) { + next.run(); + } + + /// Lifetime notification, broadcast independently of input propagation. A screen + /// context can become active repeatedly as overlays open and close. + default void onScreenActiveChanged(boolean active) { + } +} diff --git a/src/main/java/dev/isxander/controlify/api/screenop/ScreenBehaviourFactory.java b/src/main/java/dev/isxander/controlify/api/screenop/ScreenBehaviourFactory.java new file mode 100644 index 000000000..72b02a99c --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/screenop/ScreenBehaviourFactory.java @@ -0,0 +1,8 @@ +package dev.isxander.controlify.api.screenop; + +import net.minecraft.client.gui.screens.Screen; + +@FunctionalInterface +public interface ScreenBehaviourFactory { + ScreenBehaviour create(ScreenContext screen); +} diff --git a/src/main/java/dev/isxander/controlify/api/screenop/ScreenBehaviourRegistry.java b/src/main/java/dev/isxander/controlify/api/screenop/ScreenBehaviourRegistry.java new file mode 100644 index 000000000..f63b8a8f2 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/screenop/ScreenBehaviourRegistry.java @@ -0,0 +1,31 @@ +package dev.isxander.controlify.api.screenop; + +import net.minecraft.client.gui.screens.Screen; + +public interface ScreenBehaviourRegistry { + /// Adds a screen behaviour factory. Higher priorities run first, then more specific + /// classes, then newest registrations. The default priority is zero; positive and + /// negative priorities are both supported. Factories run once per screen context, + /// after construction. + void register(Class screenClass, int priority, ScreenBehaviourFactory factory); + + default void register(Class screenClass, ScreenBehaviourFactory factory) { + register(screenClass, 0, factory); + } + + /// Registers a screen behaviour factory provided by the screen itself. + > void register(Class screenClass, int priority); + + default > void register(Class screenClass) { + register(screenClass, 0); + } + + /// Registers a screen behaviour factory provided by the screen itself. + /// The given screen must implement {@link ScreenBehaviourFactory} but this is not + /// in the type bound to allow the factory to be implemented by mixins. + void registerUnchecked(Class screenClass, int priority); + + default void registerUnchecked(Class screenClass) { + registerUnchecked(screenClass, 0); + } +} diff --git a/src/main/java/dev/isxander/controlify/api/screenop/ScreenBehaviours.java b/src/main/java/dev/isxander/controlify/api/screenop/ScreenBehaviours.java new file mode 100644 index 000000000..1be158b57 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/screenop/ScreenBehaviours.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.api.screenop; + +import dev.isxander.controlify.impl.screenop.CompositeScreenBehaviour; +import dev.isxander.controlify.impl.screenop.ConditionalScreenBehaviour; + +import java.util.List; +import java.util.function.BooleanSupplier; + +/// Helpers for composing stateful screen behaviours. Construct them once in a +/// screen factory so their state survives between invocations. +public final class ScreenBehaviours { + private ScreenBehaviours() { + } + + /// Runs members in order. The last next() continues into the enclosing chain. + /// Activation notifications always reach every member. + public static ScreenBehaviour chain(List behaviours) { + return new CompositeScreenBehaviour(behaviours); + } + + public static ScreenBehaviour chain(ScreenBehaviour... behaviours) { + return chain(List.of(behaviours)); + } + + /// Evaluates the condition on each call. False skips this behaviour and continues + /// the enclosing chain. Activation notifications still reach the behaviour. + public static ScreenBehaviour conditional(BooleanSupplier condition, ScreenBehaviour behaviour) { + return new ConditionalScreenBehaviour(condition, behaviour); + } + + /// Confines cancellation to the supplied behaviour, usually a chain. Once that + /// invocation returns normally, the enclosing chain continues even if the branch + /// omitted next(). Exceptions propagate and changing screens still stops input. + /// Work after an inner next() finishes before the enclosing chain resumes. + public static ScreenBehaviour branch(ScreenBehaviour behaviour) { + return CompositeScreenBehaviour.branch(behaviour); + } +} diff --git a/src/main/java/dev/isxander/controlify/api/screenop/ScreenContext.java b/src/main/java/dev/isxander/controlify/api/screenop/ScreenContext.java new file mode 100644 index 000000000..a6fcd2dae --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/screenop/ScreenContext.java @@ -0,0 +1,53 @@ +package dev.isxander.controlify.api.screenop; + +import dev.isxander.controlify.api.InputMode; +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.gui.ButtonGuideApi; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public interface ScreenContext { + T screen(); + + /// Resolves and caches the effective behaviour chain by element identity within + /// this screen. Available after element registration completes. + ElementBehaviour elementBehaviour(GuiEventListener element); + + InputMode inputMode(); + + VirtualMouse virtualMouse(); + + default boolean isVirtualMouseEnabled() { + return virtualMouse().isEnabled(); + } + + /// Immutable snapshot, deepest focused element first. + List focusPath(); + + void clearFocus(); + + void setInitialFocus(); + + /// Reports a navigation step to the input system, plays navigation haptics and, + /// when extra UI sounds are enabled, the focus-change sound. + void notifyNavigation(Controller controller); + + void playNavigationHaptic(Controller controller); + + /// Plays the focus-change sound directly, regardless of the extra UI sounds setting. + void playFocusChangeSound(); + + void playClickSound(); + + ButtonGuideApi buttonGuide(); + + /// Opens the on-screen keyboard for a supported element. True means handled (including + /// intentional suppression); false allows ordinary activation to continue. + boolean tryOpenKeyboard( + Controller controller, + @Nullable GuiEventListener component + ); +} diff --git a/src/main/java/dev/isxander/controlify/api/screenop/ScreenopEvents.java b/src/main/java/dev/isxander/controlify/api/screenop/ScreenopEvents.java new file mode 100644 index 000000000..2df4a5996 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/screenop/ScreenopEvents.java @@ -0,0 +1,21 @@ +package dev.isxander.controlify.api.screenop; + +import dev.isxander.controlify.api.utils.event.EventSource; +import dev.isxander.controlify.impl.screenop.ScreenBehaviourRegistryImpl; + +public final class ScreenopEvents { + + /// Fires once during client initialization. Registrations close after dispatch. + public static final EventSource REGISTER_SCREEN_BEHAVIOURS = ScreenBehaviourRegistryImpl.Events.REGISTER_SCREEN_BEHAVIOURS; + + /// Fires once for each new screen context, after its screen factories have run. + /// Register factories here; widgets may not have been initialized yet. The registry + /// closes after dispatch and is retained across widget rebuilds. + public static final EventSource REGISTER_ELEMENT_BEHAVIOURS = ScreenBehaviourRegistryImpl.Events.REGISTER_ELEMENT_BEHAVIOURS; + + public record RegisterElementBehavioursEvent(ScreenContext screenContext, ElementBehaviourRegistry registry) { + } + + private ScreenopEvents() { + } +} diff --git a/src/main/java/dev/isxander/controlify/api/screenop/SnapPoint.java b/src/main/java/dev/isxander/controlify/api/screenop/SnapPoint.java index 5c234db53..450374ab1 100644 --- a/src/main/java/dev/isxander/controlify/api/screenop/SnapPoint.java +++ b/src/main/java/dev/isxander/controlify/api/screenop/SnapPoint.java @@ -13,7 +13,7 @@ * Defines a point on the screen that the virtual mouse can snap to. * * @param position the position on the screen where the cursor will snap to - * @param range how far away from the snap point the cursor can be and still snap to it + * @param range how far away from the snap point the cursor can be and still snap to it */ public record SnapPoint(Vector2ic position, int range) { public SnapPoint(int x, int y, int range) { diff --git a/src/main/java/dev/isxander/controlify/api/screenop/VirtualMouse.java b/src/main/java/dev/isxander/controlify/api/screenop/VirtualMouse.java new file mode 100644 index 000000000..eb4504931 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/screenop/VirtualMouse.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.api.screenop; + +import dev.isxander.controlify.api.controller.Controller; +import net.minecraft.client.gui.navigation.ScreenDirection; + +/// The shared cursor for one screen. Mode selects which cursor behaviours run; +/// enabled state also reflects the current input mode and screen activation. +public interface VirtualMouse { + VirtualMouseMode mode(); + + void setMode(VirtualMouseMode mode); + + boolean isEnabled(); + + int getCurrentX(float deltaTime); + + int getCurrentY(float deltaTime); + + void snapToClosestPoint(); + + void snapInDirection(ScreenDirection direction); + + /// Explicit click fallback, including when automatic click emulation is disabled. + void emulateClicks(Controller controller); + + void preventScrollingThisTick(); +} diff --git a/src/main/java/dev/isxander/controlify/api/screenop/VirtualMouseMode.java b/src/main/java/dev/isxander/controlify/api/screenop/VirtualMouseMode.java new file mode 100644 index 000000000..1ceb9a279 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/screenop/VirtualMouseMode.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.api.screenop; + +/// Screen-level cursor policy, independent of the middleware implementing it. +public enum VirtualMouseMode { + /// Uses the saved screen preference and permits the user to toggle it. + DEFAULT, + ENABLED, + DISABLED, + CURSOR_ONLY, + CURSOR_SCROLL; + + public boolean emulatesClicks() { + return this == DEFAULT || this == ENABLED; + } + + public boolean scrolls() { + return emulatesClicks() || this == CURSOR_SCROLL; + } +} diff --git a/src/main/java/dev/isxander/controlify/api/screenop/package-info.java b/src/main/java/dev/isxander/controlify/api/screenop/package-info.java new file mode 100644 index 000000000..9874b8874 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/api/screenop/package-info.java @@ -0,0 +1,29 @@ +/// Screen and element sidecars. Register screen factories during +/// ScreenopEvents.REGISTER_SCREEN_BEHAVIOURS and element factories during the +/// per-screen REGISTER_ELEMENT_BEHAVIOURS event. Screen behaviours compose cursor +/// handling, focused-element dispatch and default controls. Focused-element input +/// travels from the deepest element outward before continuing through screen +/// behaviours. Navigation completes before interaction resolves its focus path. +/// +/// VirtualMouseMode is a screen-level policy configured through the context's +/// virtual mouse. Live predicates select cursor or focus behaviours without +/// reconstructing their state. Frame updates run before GUI mouse processing; +/// activation notifications reach all behaviours independently of continuations. +/// +/// ScreenBehaviours.chain and ElementBehaviours.chain compose an explicit ordered +/// group without assigning priorities to its individual members. Its final next() +/// continues into the enclosing chain. Priorities order independently registered +/// groups; each group controls the order of its own members. +/// Branches confine cancellation to an inner group and then resume the enclosing +/// chain on normal return. Conditional skips a group when its predicate is false; +/// it is sufficient for ordinary visibility and availability checks. +/// +/// ElementBehaviours.button and slider supply shared input handling through +/// callbacks, so custom controls can behave consistently with vanilla controls. +/// +/// Continuations control propagation within an invocation, not action lifetime. +/// Consume or drain action pulses explicitly when rejecting input. +@NullMarked +package dev.isxander.controlify.api.screenop; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/impl/Controlify.java b/src/main/java/dev/isxander/controlify/impl/Controlify.java index dbad581b5..de68672e2 100644 --- a/src/main/java/dev/isxander/controlify/impl/Controlify.java +++ b/src/main/java/dev/isxander/controlify/impl/Controlify.java @@ -43,14 +43,14 @@ import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; import dev.isxander.controlify.impl.platform.main.PlatformMainUtil; import dev.isxander.controlify.impl.platform.network.SidedNetworkApi; -import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.impl.screenop.keyboard.KeyboardLayoutManager; +import dev.isxander.controlify.impl.screenop.ScreenBehaviourRegistryImpl; +import dev.isxander.controlify.impl.gui.keyboard.KeyboardLayoutManager; import dev.isxander.controlify.impl.server.*; import dev.isxander.controlify.impl.server.packets.*; import dev.isxander.controlify.impl.sound.ControlifyClientSounds; import dev.isxander.controlify.impl.utils.*; import dev.isxander.controlify.impl.utils.event.EventBus; -import dev.isxander.controlify.impl.virtualmouse.VirtualMouseHandler; +import dev.isxander.controlify.impl.screenop.ScreenRuntime; import dev.isxander.controlify.impl.wireless.LowBatteryNotifier; import dev.isxander.controlify.mixins.feature.virtualmouse.MouseHandlerAccessor; import dev.isxander.yacl3.gui.YACLScreen; @@ -63,6 +63,7 @@ import org.jetbrains.annotations.Nullable; import org.jspecify.annotations.NonNull; import org.spongepowered.asm.mixin.MixinEnvironment; + import static dev.isxander.controlify.impl.utils.ControllerUtils.wrapControllerError; import java.io.IOException; @@ -89,7 +90,6 @@ private Events() { private @Nullable InGameInputHandler inGameInputHandler; public @Nullable InGameButtonGuide inGameButtonGuide; - private VirtualMouseHandler virtualMouseHandler; // Asset reloaders / managers private InputFontMapper inputFontMapper; @@ -225,7 +225,7 @@ private void registerBuiltinPack(String id) { /** * Called once Minecraft has completely loaded. * (When the loading overlay starts to fade). - * + *

* This is where controllers are usually initialised, as long * as one or more controllers are connected. */ @@ -236,7 +236,6 @@ public void initializeControlify() { config().loadOrDefault(); this.inGameInputHandler = null; // set when the current controller changes - this.virtualMouseHandler = new VirtualMouseHandler(); this.triggerEffectManager = new TriggerEffectManager(); ActionContextRegistryImpl.INSTANCE.registerBuiltinContexts(); @@ -244,8 +243,10 @@ public void initializeControlify() { PlatformClientUtil.registerPostScreenRender((screen, graphics, mouseX, mouseY, tickDelta) -> this.getCurrentController().ifPresent(controller -> { - virtualMouseHandler().renderVirtualMouse(graphics); - ScreenProcessorProvider.provide(screen).render(controller, graphics, tickDelta); + var context = ScreenRuntime.current(); + if (context != null && context.screen() == screen) { + context.extractRenderState(graphics, tickDelta, controller); + } })); this.controllerCoordinator = new ControllerCoordinator(this, this.setupWizardManager); @@ -253,6 +254,8 @@ public void initializeControlify() { // Built-in compatibility must subscribe before addon-facing lifecycle events. ControlifyCompat.init(); Events.INITIALIZED.emit(this); + ScreenBehaviourRegistryImpl.INSTANCE.initialize(); + ScreenRuntime.onScreenChanged(); try { try { ActionContextRegistryImpl.Events.REGISTER_CONTEXTS.emit(ActionContextRegistryImpl.INSTANCE); @@ -319,7 +322,7 @@ public void update(float tickDelta) { } boolean outOfFocus = !config().getSettings().globalSettings().outOfFocusInput - && !this.minecraft.isWindowActive(); + && !this.minecraft.isWindowActive(); boolean givingInput = this.controllerCoordinator.update(outOfFocus); this.setupWizardManager.tick(); @@ -339,6 +342,13 @@ public void update(float tickDelta) { this.mouseUsedThisFrame = false; } + /// Called once per frame, after minecraft has done its full tick + /// {@link fixedUpdate} may have been called multiple times between + /// {@link update} and {@link postUpdate}, or it may not have been called. + public void postUpdate(float tickDelta) { + this.controllerCoordinator.postUpdate(); + } + /// Called at 20tps public void fixedUpdate() { LowBatteryNotifier.tick(); @@ -350,8 +360,10 @@ public void fixedUpdate() { } } + this.controllerCoordinator.fixedUpdate(); + boolean outOfFocus = !config().getSettings().globalSettings().outOfFocusInput - && !this.minecraft.isWindowActive(); + && !this.minecraft.isWindowActive(); this.getCurrentController().ifPresent(currentController -> { wrapControllerError( @@ -410,7 +422,10 @@ private void fixedUpdateActiveController(ControllerImpl controller, boolean outO if (this.currentInputMode().isController()) { if (MinecraftUtil.getScreen() != null) { - ScreenProcessorProvider.provide(MinecraftUtil.getScreen()).onControllerFixedUpdate(controller); + var context = ScreenRuntime.current(); + if (context != null) { + context.handleControllerInput(controller); + } } } } @@ -493,7 +508,7 @@ public boolean setInputMode(@NotNull InputMode newInputMode) { lastInputSwitchTime = Blaze3D.getTime(); if (!minecraft.mouseHandler.isMouseGrabbed()) { - if (newInputMode == InputMode.KEYBOARD_MOUSE && virtualMouseHandler().isVirtualMouseEnabled()) { + if (newInputMode == InputMode.KEYBOARD_MOUSE && ScreenRuntime.virtualMouse().map(mouse -> mouse.isEnabled()).orElse(false)) { moveCursorToVirtualMouse(); } hideMouse(newInputMode.isController(), true); @@ -511,12 +526,7 @@ public boolean setInputMode(@NotNull InputMode newInputMode) { } } - // notify current screen of input mode change - if (MinecraftUtil.getScreen() != null) { - ScreenProcessorProvider.provide(MinecraftUtil.getScreen()).onInputModeChanged(newInputMode); - } - - this.virtualMouseHandler.onInputModeChanged(newInputMode); + ScreenRuntime.virtualMouse().ifPresent(mouse -> mouse.synchronizeMode()); // notify event listeners of input mode change Events.INPUT_MODE_CHANGED.emit(new ControlifyEvents.InputModeChanged(this, newInputMode)); @@ -555,10 +565,6 @@ public Optional inGameButtonGuide() { return Optional.ofNullable(inGameButtonGuide); } - public VirtualMouseHandler virtualMouseHandler() { - return virtualMouseHandler; - } - public @NotNull InputMode currentInputMode() { return currentInputMode; } @@ -568,7 +574,7 @@ public void hideMouse(boolean hide, boolean moveMouse) { if (MinecraftUtil.getScreen() != null) { var mouseHandlerAccessor = (MouseHandlerAccessor) minecraft.mouseHandler; - if (hide && !virtualMouseHandler().isVirtualMouseEnabled() && !virtualMouseHandler().requiresVirtualMouse() && moveMouse) { + if (hide && !ScreenRuntime.virtualMouse().map(mouse -> mouse.isEnabled()).orElse(false) && !ScreenRuntime.virtualMouse().map(mouse -> mouse.isRequired()).orElse(false) && 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 @@ -580,9 +586,9 @@ public void hideMouse(boolean hide, boolean moveMouse) { public void showCursorTemporarily() { if (currentInputMode() == InputMode.MIXED && !minecraft.mouseHandler.isMouseGrabbed()) { mouseUsedThisFrame = true; - if (virtualMouseHandler().isVirtualMouseEnabled()) { + if (ScreenRuntime.virtualMouse().map(mouse -> mouse.isEnabled()).orElse(false)) { moveCursorToVirtualMouse(); - virtualMouseHandler().disableVirtualMouse(); + ScreenRuntime.virtualMouse().ifPresent(mouse -> mouse.setSuspended(true)); } hideMouse(false, false); showMouseTicks = 20 * 2; @@ -590,11 +596,8 @@ public void showCursorTemporarily() { } private void moveCursorToVirtualMouse() { - CursorUtils.setPosition( - minecraft.getWindow(), - virtualMouseHandler().getCurrentX(0), - virtualMouseHandler().getCurrentY(0) - ); + ScreenRuntime.virtualMouse().ifPresent(mouse -> CursorUtils.setPosition( + minecraft.getWindow(), mouse.getCurrentX(0), mouse.getCurrentY(0))); } public void endTemporaryCursor() { @@ -608,9 +611,7 @@ public void endTemporaryCursor() { private void restoreVirtualCursor() { hideMouse(true, false); - if (virtualMouseHandler().requiresVirtualMouse()) { - virtualMouseHandler().enableVirtualMouse(); - } + ScreenRuntime.virtualMouse().ifPresent(mouse -> mouse.setSuspended(false)); } public InputFontMapper inputFontMapper() { diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionContextRegistryImpl.java b/src/main/java/dev/isxander/controlify/impl/action/ActionContextRegistryImpl.java index 6037051fc..992b4a4b5 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionContextRegistryImpl.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionContextRegistryImpl.java @@ -10,10 +10,10 @@ import dev.isxander.controlify.api.action.BuiltinActionContexts; import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.gui.screen.RadialMenuScreen; -import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; +import dev.isxander.controlify.impl.screenop.ScreenRuntime; import dev.isxander.controlify.impl.utils.MinecraftUtil; import dev.isxander.controlify.impl.utils.event.EventBus; -import dev.isxander.controlify.impl.virtualmouse.VirtualMouseBehaviour; +import dev.isxander.controlify.api.screenop.VirtualMouseMode; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.resources.Identifier; @@ -63,17 +63,15 @@ public void registerBuiltinContexts() { this.register(BuiltinActionContexts.ANY_SCREEN, mc -> MinecraftUtil.getScreen() != null); this.register(BuiltinActionContexts.REGULAR_SCREEN, mc -> MinecraftUtil.getScreen() != null - && !Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled()); + && !ScreenRuntime.virtualMouse().map(mouse -> mouse.isEnabled()).orElse(false)); this.register(BuiltinActionContexts.CONTAINER, mc -> MinecraftUtil.getScreen() instanceof AbstractContainerScreen); this.register(BuiltinActionContexts.V_MOUSE_CURSOR, mc -> MinecraftUtil.getScreen() != null - && ScreenProcessorProvider.provide(MinecraftUtil.getScreen()).virtualMouseBehaviour().hasCursor() - && Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled()); + && ScreenRuntime.virtualMouse().map(mouse -> mouse.isEnabled()).orElse(false)); this.register(BuiltinActionContexts.V_MOUSE_COMPAT, mc -> MinecraftUtil.getScreen() != null - && ScreenProcessorProvider.provide(MinecraftUtil.getScreen()).virtualMouseBehaviour() == VirtualMouseBehaviour.ENABLED - && Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled()); + && ScreenRuntime.virtualMouse().map(mouse -> mouse.isEnabled() && mouse.mode().emulatesClicks()).orElse(false)); this.register(BuiltinActionContexts.RADIAL_MENU, mc -> MinecraftUtil.getScreen() instanceof RadialMenuScreen); } diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionGraph.java b/src/main/java/dev/isxander/controlify/impl/action/ActionGraph.java index f5f463395..f85e82259 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionGraph.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionGraph.java @@ -79,15 +79,15 @@ public void setCurrentContexts(Set contexts) { @Override public void accept(@NonNull LogicalSignal event) { switch (event) { - case LogicalSignal.Tick tick -> this.processTick(tick); + case LogicalSignal.Frame frame -> this.processFrame(frame); case LogicalSignal.InputSignal inputSignal -> this.processInput(inputSignal); case LogicalSignal.SensorInterval _ -> {} // ignore sensor data } } - private void processTick(LogicalSignal.Tick tick) { + private void processFrame(LogicalSignal.Frame frame) { for (ActionImpl action : this.allActions) { - action.activator().onSignal(tick, action.state()); + action.activator().onSignal(frame, action.state()); } } diff --git a/src/main/java/dev/isxander/controlify/impl/action/ActionState.java b/src/main/java/dev/isxander/controlify/impl/action/ActionState.java index abbc4df94..71dbfefb7 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/ActionState.java +++ b/src/main/java/dev/isxander/controlify/impl/action/ActionState.java @@ -32,6 +32,15 @@ public boolean consumePulse() { return false; } + @Override + public boolean consumeAllPulses() { + if (this.pulses > 0) { + this.pulses = 0; + return true; + } + return false; + } + @Override public void toggleLatch() { this.setLatch(!this.isLatchActive()); diff --git a/src/main/java/dev/isxander/controlify/impl/action/activator/RepeatLatchPulseActivator.java b/src/main/java/dev/isxander/controlify/impl/action/activator/RepeatLatchPulseActivator.java index 3712256f6..3667758fa 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/activator/RepeatLatchPulseActivator.java +++ b/src/main/java/dev/isxander/controlify/impl/action/activator/RepeatLatchPulseActivator.java @@ -43,7 +43,7 @@ public void onSignal(LogicalSignal signal, Accumulator acc) { this.latchActivator.onSignal(signal, this.holdRepeatAccumulator); - if (signal instanceof LogicalSignal.Tick) { + if (signal instanceof LogicalSignal.Frame) { while (down && signal.timeNanos() >= nextDue) { acc.firePulse(); nextDue += repeatDelayNs; diff --git a/src/main/java/dev/isxander/controlify/impl/action/proxy/KeyMappingProxy.java b/src/main/java/dev/isxander/controlify/impl/action/proxy/KeyMappingProxy.java index ddd38e4b7..e26d8a1a8 100644 --- a/src/main/java/dev/isxander/controlify/impl/action/proxy/KeyMappingProxy.java +++ b/src/main/java/dev/isxander/controlify/impl/action/proxy/KeyMappingProxy.java @@ -44,9 +44,7 @@ public void accept(@NonNull ControllerImpl controller) { && (minecraft.isWindowActive() || Controlify.instance().config().getSettings().globalSettings().outOfFocusInput); if (!enabled || resolvedAction == null) { if (resolvedAction != null) { - while (resolvedAction.pulse().consumePulse()) { - // Discard ineligible input instead of replaying it when the controller becomes active. - } + resolvedAction.pulse().consumeAllPulses(); } this.release(controller); return; diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/ControlifyCompat.java b/src/main/java/dev/isxander/controlify/impl/compatibility/ControlifyCompat.java index 87a47cd7f..35bad5ea9 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/ControlifyCompat.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/ControlifyCompat.java @@ -23,6 +23,16 @@ public class ControlifyCompat { public static final String SIMPLE_VOICE_CHAT = "voicechat"; public static final String FANCY_MENU = "fancymenu"; + public static void initializeScreenBehaviours() { + dev.isxander.controlify.impl.compatibility.yacl.screenop.YACLScreenBehaviours.initialize(); + //? if sodium { + wrapCompatCall("sodium", dev.isxander.controlify.impl.compatibility.sodium.screenop.SodiumScreenBehaviours::initialize); + //?} + //? if iris { + wrapCompatCall("iris", dev.isxander.controlify.impl.compatibility.iris.screenop.IrisScreenBehaviours::initialize); + //?} + } + public static void init() { //? if simple_voice_chat { try { diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/iris/mixins/BaseOptionElementWidgetMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/iris/mixins/BaseOptionElementWidgetMixin.java index 10803cdd8..23ea3720e 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/iris/mixins/BaseOptionElementWidgetMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/iris/mixins/BaseOptionElementWidgetMixin.java @@ -8,30 +8,32 @@ //? if iris { -import dev.isxander.controlify.impl.compatibility.iris.screenop.BaseOptionElementComponentProcessor; -import dev.isxander.controlify.impl.screenop.ComponentProcessor; -import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.api.screenop.*; import net.irisshaders.iris.gui.NavigationController; import net.irisshaders.iris.gui.element.widget.BaseOptionElementWidget; +import net.minecraft.client.gui.components.events.GuiEventListener; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @Mixin(BaseOptionElementWidget.class) -public abstract class BaseOptionElementWidgetMixin implements ComponentProcessorProvider { - @Shadow public abstract boolean applyPreviousValue(); - @Shadow public abstract boolean applyNextValue(); +public abstract class BaseOptionElementWidgetMixin implements ElementBehaviourFactory { + @Shadow + public abstract boolean applyPreviousValue(); - @Shadow protected NavigationController navigation; - @Unique private final BaseOptionElementComponentProcessor processor - = new BaseOptionElementComponentProcessor(this::cycle); + @Shadow + public abstract boolean applyNextValue(); + + @Shadow + protected NavigationController navigation; @Override - public ComponentProcessor componentProcessor() { - return processor; + public ElementBehaviour create(GuiEventListener element) { + return ElementBehaviours.slider((GuiEventListener widget, int direction) -> this.cycle(direction < 0)).create((GuiEventListener) (Object) this); } - @Unique private void cycle(boolean reverse) { + @Unique + private void cycle(boolean reverse) { boolean needsUpdate = reverse ? applyPreviousValue() : applyNextValue(); if (needsUpdate) { navigation.refresh(); diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/iris/screenop/BaseOptionElementComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/iris/screenop/BaseOptionElementComponentProcessor.java deleted file mode 100644 index 70ece565d..000000000 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/iris/screenop/BaseOptionElementComponentProcessor.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.compatibility.iris.screenop; - -//? if iris { - -import dev.isxander.controlify.impl.screenop.compat.AbstractSliderComponentProcessor; - -import java.util.function.Consumer; - -public class BaseOptionElementComponentProcessor extends AbstractSliderComponentProcessor { - private final Consumer cycleMethod; - - public BaseOptionElementComponentProcessor(Consumer cycleMethod) { - this.cycleMethod = cycleMethod; - } - - @Override - protected void incrementSlider(boolean reverse) { - this.cycleMethod.accept(reverse); - } -} -//?} diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/iris/screenop/IrisScreenBehaviours.java b/src/main/java/dev/isxander/controlify/impl/compatibility/iris/screenop/IrisScreenBehaviours.java new file mode 100644 index 000000000..808d00ce0 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/iris/screenop/IrisScreenBehaviours.java @@ -0,0 +1,17 @@ +package dev.isxander.controlify.impl.compatibility.iris.screenop; + +//? if iris { + +import dev.isxander.controlify.api.screenop.ScreenopEvents; +import net.irisshaders.iris.gui.element.widget.BaseOptionElementWidget; + +public final class IrisScreenBehaviours { + private IrisScreenBehaviours() { + } + + public static void initialize() { + ScreenopEvents.REGISTER_ELEMENT_BEHAVIOURS.subscribe(event -> + event.registry().registerUnchecked(BaseOptionElementWidget.class)); + } +} +//?} diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/iris/screenop/package-info.java b/src/main/java/dev/isxander/controlify/impl/compatibility/iris/screenop/package-info.java new file mode 100644 index 000000000..9a1c057be --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/iris/screenop/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package dev.isxander.controlify.impl.compatibility.iris.screenop; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/rso/mixins/SodiumVideoOptionsScreenMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/rso/mixins/SodiumVideoOptionsScreenMixin.java index 3ee55bd84..6739763aa 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/rso/mixins/SodiumVideoOptionsScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/rso/mixins/SodiumVideoOptionsScreenMixin.java @@ -8,10 +8,10 @@ //? if reeses_sodium_options { /*import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -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; +import dev.isxander.controlify.impl.compatibility.sodium.screenop.SodiumScreenBehaviour; +import dev.isxander.controlify.impl.compatibility.sodium.screenop.SodiumScreenOperations; +import dev.isxander.controlify.api.screenop.*; +import dev.isxander.controlify.impl.screenop.ScreenContextImpl; import me.flashyreese.mods.reeses_sodium_options.client.gui.SodiumVideoOptionsScreen; import me.flashyreese.mods.reeses_sodium_options.client.gui.frame.AbstractFrame; import me.flashyreese.mods.reeses_sodium_options.client.gui.frame.tab.Tab; @@ -33,7 +33,7 @@ import net.caffeinemc.mods.sodium.client.gui.options.control.ControlElement; @Mixin(SodiumVideoOptionsScreen.class) -public abstract class SodiumVideoOptionsScreenMixin extends Screen implements ScreenProcessorProvider, SodiumScreenOperations { +public abstract class SodiumVideoOptionsScreenMixin extends Screen implements ScreenBehaviourFactory, SodiumScreenOperations { @Shadow private FlatButtonWidget applyButton; @Shadow @@ -41,9 +41,6 @@ public abstract class SodiumVideoOptionsScreenMixin extends Screen implements Sc @Shadow private FlatButtonWidget undoButton; - @Unique private final SodiumGuiScreenProcessor controlify$screenProcessor - = new SodiumGuiScreenProcessor((SodiumVideoOptionsScreen) (Object) this, this); - @Unique private TabFrame tabFrame; protected SodiumVideoOptionsScreenMixin(Component title) { @@ -52,7 +49,7 @@ protected SodiumVideoOptionsScreenMixin(Component title) { @Inject(method = "init", at = @At("RETURN")) private void notifyProcessorRebuild(CallbackInfo ci) { - controlify$screenProcessor.onRebuildGUI(); + ScreenContextImpl.get(this).onWidgetRebuild(); focusOnFirstControl(); } @@ -92,8 +89,8 @@ private Runnable setInitialFocusOnTabChange(Runnable onSetTab) { } @Override - public ScreenProcessor screenProcessor() { - return controlify$screenProcessor; + public ScreenBehaviour create(ScreenContext context) { + return new SodiumScreenBehaviour(context, this); } @Override diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/SimpleVoiceChatCompat.java b/src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/SimpleVoiceChatCompat.java index 7eadf3980..2d0f232d8 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/SimpleVoiceChatCompat.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/simplevoicechat/SimpleVoiceChatCompat.java @@ -89,8 +89,8 @@ private static void tick(Minecraft minecraft) { if (!controlify.currentInputMode().isController() || (!minecraft.isWindowActive() && !controlify.config().getSettings().globalSettings().outOfFocusInput)) { // Discard ineligible toggle presses so they cannot replay when focus/input returns. - while (pttToggle.pulse().consumePulse()) {} - while (whisperToggle.pulse().consumePulse()) {} + pttToggle.pulse().consumeAllPulses(); + whisperToggle.pulse().consumeAllPulses(); clearState(); return; } diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/CycleControlElementMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/CycleControlElementMixin.java index 62575c030..ee0a31a4b 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/CycleControlElementMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/CycleControlElementMixin.java @@ -8,23 +8,20 @@ //? if sodium { -import dev.isxander.controlify.impl.compatibility.sodium.screenop.CycleControlProcessor; -import dev.isxander.controlify.impl.screenop.ComponentProcessor; -import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.api.screenop.*; +import net.minecraft.client.gui.components.events.GuiEventListener; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; @Mixin(targets = "net.caffeinemc.mods.sodium.client.gui.options.control.CyclingControl$CyclingControlElement") -public abstract class CycleControlElementMixin implements ComponentProcessorProvider { - @Shadow protected abstract void cycleControl(boolean reverse); - - @Unique private final ComponentProcessor controlify$componentProcessor - = new CycleControlProcessor(this::cycleControl); +public abstract class CycleControlElementMixin implements ElementBehaviourFactory { + @Shadow + protected abstract void cycleControl(boolean reverse); @Override - public ComponentProcessor componentProcessor() { - return controlify$componentProcessor; + public ElementBehaviour create(GuiEventListener element) { + return ElementBehaviours.cycle((GuiEventListener widget, int direction) -> this.cycleControl(direction < 0)).create((GuiEventListener) (Object) this); } + } //?} diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/SliderControlElementMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/SliderControlElementMixin.java index 13f9e9e1d..c920e43f0 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/SliderControlElementMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/SliderControlElementMixin.java @@ -8,34 +8,33 @@ //? if sodium { -import dev.isxander.controlify.impl.compatibility.sodium.screenop.SliderControlProcessor; -import dev.isxander.controlify.impl.screenop.ComponentProcessor; -import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.api.screenop.*; import net.caffeinemc.mods.sodium.client.config.structure.IntegerOption; import net.caffeinemc.mods.sodium.client.gui.ColorTheme; import net.caffeinemc.mods.sodium.client.gui.options.control.AbstractOptionList; import net.caffeinemc.mods.sodium.client.gui.options.control.ControlElement; import net.caffeinemc.mods.sodium.client.util.Dim2i; +import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.util.Mth; import org.spongepowered.asm.mixin.*; @Mixin(targets = "net.caffeinemc.mods.sodium.client.gui.options.control.SliderControl$SliderControlElement") -public abstract class SliderControlElementMixin extends ControlElement implements ComponentProcessorProvider { - @Shadow @Final private IntegerOption option; - - @Unique private final ComponentProcessor controlify$componentProcessor - = new SliderControlProcessor(this::controlify$incrementSlider); +public abstract class SliderControlElementMixin extends ControlElement implements ElementBehaviourFactory { + @Shadow + @Final + private IntegerOption option; public SliderControlElementMixin(AbstractOptionList list, Dim2i dim, ColorTheme theme) { super(list, dim, theme); } @Override - public ComponentProcessor componentProcessor() { - return controlify$componentProcessor; + public ElementBehaviour create(GuiEventListener element) { + return ElementBehaviours.slider((GuiEventListener widget, int direction) -> this.controlify$incrementSlider(direction < 0)).create((GuiEventListener) (Object) this); } - @Unique private void controlify$incrementSlider(boolean reverse) { + @Unique + private void controlify$incrementSlider(boolean reverse) { var range = option.getSteppedValidator(); option.modifyValue(Mth.clamp( option.getValidatedValue() + (reverse ? -range.step() : range.step()), diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/TickBoxControlElementMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/TickBoxControlElementMixin.java index 60c3297c4..a292495cb 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/TickBoxControlElementMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/TickBoxControlElementMixin.java @@ -8,24 +8,20 @@ //? if sodium { -import dev.isxander.controlify.impl.compatibility.sodium.screenop.TickBoxControlProcessor; -import dev.isxander.controlify.impl.screenop.ComponentProcessor; -import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.api.screenop.*; +import net.minecraft.client.gui.components.events.GuiEventListener; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; @Mixin(targets = "net.caffeinemc.mods.sodium.client.gui.options.control.TickBoxControl$TickBoxControlElement") -public abstract class TickBoxControlElementMixin implements ComponentProcessorProvider { +public abstract class TickBoxControlElementMixin implements ElementBehaviourFactory { @Shadow protected abstract void toggleControl(); - @Unique private final ComponentProcessor controlify$componentProcessor - = new TickBoxControlProcessor(this::toggleControl); - @Override - public ComponentProcessor componentProcessor() { - return controlify$componentProcessor; + public ElementBehaviour create(GuiEventListener element) { + return ElementBehaviours.cycle((GuiEventListener widget, int direction) -> this.toggleControl()).create((GuiEventListener) (Object) this); } + } //?} diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/VideoSettingsScreenMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/VideoSettingsScreenMixin.java index 9c29763bf..42a7fbfaf 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/VideoSettingsScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/mixins/VideoSettingsScreenMixin.java @@ -8,10 +8,9 @@ //? if sodium { -import dev.isxander.controlify.impl.compatibility.sodium.screenop.SodiumGuiScreenProcessor; +import dev.isxander.controlify.api.screenop.*; +import dev.isxander.controlify.impl.compatibility.sodium.screenop.SodiumScreenBehaviour; import dev.isxander.controlify.impl.compatibility.sodium.screenop.SodiumScreenOperations; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; -import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; import net.caffeinemc.mods.sodium.client.config.ConfigManager; import net.caffeinemc.mods.sodium.client.config.structure.Page; import net.caffeinemc.mods.sodium.client.gui.VideoSettingsScreen; @@ -19,6 +18,7 @@ import net.caffeinemc.mods.sodium.client.gui.widgets.OptionListWidget; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; +import org.jspecify.annotations.NonNull; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -27,21 +27,24 @@ import java.util.List; @Mixin(VideoSettingsScreen.class) -public abstract class VideoSettingsScreenMixin extends Screen implements ScreenProcessorProvider, SodiumScreenOperations { +public abstract class VideoSettingsScreenMixin extends Screen implements ScreenBehaviourFactory, SodiumScreenOperations { //? if >=26.2 { - @Shadow private net.caffeinemc.mods.sodium.client.gui.widgets.KeyBoundButtonWidget applyButton; - @Shadow private net.caffeinemc.mods.sodium.client.gui.widgets.KeyBoundButtonWidget closeButton; - @Shadow private net.caffeinemc.mods.sodium.client.gui.widgets.KeyBoundButtonWidget undoButton; + @Shadow + private net.caffeinemc.mods.sodium.client.gui.widgets.KeyBoundButtonWidget applyButton; + @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 closeButton; @Shadow private FlatButtonWidget undoButton; *///?} - @Shadow private OptionListWidget optionList; + @Shadow + private OptionListWidget optionList; - @Unique private Page controlify$currentPage = null; - @Unique private final SodiumGuiScreenProcessor controlify$screenProcessor - = new SodiumGuiScreenProcessor((VideoSettingsScreen) (Object) this, this); + @Unique + private Page controlify$currentPage = null; protected VideoSettingsScreenMixin(Component title) { super(title); @@ -52,7 +55,6 @@ protected VideoSettingsScreenMixin(Component title) { if (optionList != null && !optionList.getControls().isEmpty()) { this.setInitialFocus(optionList.getControls().get(0)); } - controlify$screenProcessor.onRebuildGUI(); } @Inject(method = "onSectionFocused", at = @At("HEAD")) @@ -61,8 +63,8 @@ protected VideoSettingsScreenMixin(Component title) { } @Override - public ScreenProcessor screenProcessor() { - return controlify$screenProcessor; + public ScreenBehaviour create(@NonNull ScreenContext context) { + return new SodiumScreenBehaviour(context, this); } @Override @@ -83,13 +85,15 @@ public ScreenProcessor screenProcessor() { jumpToPage(pages.get(nextIndex)); } - @Unique private List controlify$getAllPages() { + @Unique + private List controlify$getAllPages() { return ConfigManager.CONFIG.getModOptions().stream() .flatMap(mod -> mod.pages().stream()) .toList(); } - @Shadow public abstract void jumpToPage(Page page); + @Shadow + public abstract void jumpToPage(Page page); @Override public FlatButtonWidget controlify$getApplyButton() { diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/CycleControlProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/CycleControlProcessor.java deleted file mode 100644 index 2e6984f70..000000000 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/CycleControlProcessor.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.compatibility.sodium.screenop; - -import dev.isxander.controlify.impl.action.ControlifyActions; -import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.screenop.ComponentProcessor; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; - -import java.util.function.Consumer; - -public class CycleControlProcessor implements ComponentProcessor { - private final Consumer cycleMethod; - - public CycleControlProcessor(Consumer cycleMethod) { - this.cycleMethod = cycleMethod; - } - - @Override - public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { - if (ControlifyActions.GUI_SECONDARY_NAVI_RIGHT.on(controller).pulse().consumePulse() - || ControlifyActions.GUI_PRESS.on(controller).pulse().consumePulse() - ) { - cycleMethod.accept(false); - return true; - } - if (ControlifyActions.GUI_SECONDARY_NAVI_LEFT.on(controller).pulse().consumePulse()) { - cycleMethod.accept(true); - return true; - } - - return false; - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SliderControlProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SliderControlProcessor.java deleted file mode 100644 index 19247d025..000000000 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SliderControlProcessor.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.compatibility.sodium.screenop; - -import dev.isxander.controlify.impl.screenop.compat.AbstractSliderComponentProcessor; - -import java.util.function.Consumer; - -public class SliderControlProcessor extends AbstractSliderComponentProcessor { - private final Consumer cycleMethod; - - public SliderControlProcessor(Consumer cycleMethod) { - this.cycleMethod = cycleMethod; - } - - @Override - protected void incrementSlider(boolean reverse) { - cycleMethod.accept(reverse); - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumScreenBehaviour.java similarity index 57% rename from src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumScreenBehaviour.java index 619868b49..c8dec3df7 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumGuiScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumScreenBehaviour.java @@ -8,66 +8,67 @@ //? if sodium { -import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.controller.Controller; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.api.screenop.*; import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.compatibility.sodium.mixins.FlatButtonWidgetAccessor; -import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; -import net.minecraft.client.gui.screens.Screen; -public class SodiumGuiScreenProcessor extends ScreenProcessor { +public final class SodiumScreenBehaviour implements ScreenBehaviour { private final SodiumScreenOperations operations; - public SodiumGuiScreenProcessor(Screen screen, SodiumScreenOperations operations) { - super(screen); - this.operations = operations; - } + private final ScreenContext context; - @Override - protected void handleComponentNavigation(ControllerImpl controller) { - super.handleComponentNavigation(controller); + public SodiumScreenBehaviour(ScreenContext context, SodiumScreenOperations operations) { + this.context = context; + this.operations = operations; } @Override - protected void handleButtons(ControllerImpl controller) { - if (ControlifyActions.GUI_ABSTRACT_ACTION_1.on(controller).pulse().consumePulse()) { + public void handleInteraction(Controller controller, Runnable next) { + if (ControlifyActions.GUI_ABSTRACT_ACTION_1.on(controller).pulse().consumeAllPulses()) { ((FlatButtonWidgetAccessor) operations.controlify$getApplyButton()).invokeDoAction(); } - if (ControlifyActions.GUI_ABSTRACT_ACTION_2.on(controller).pulse().consumePulse()) { + if (ControlifyActions.GUI_ABSTRACT_ACTION_2.on(controller).pulse().consumeAllPulses()) { ((FlatButtonWidgetAccessor) operations.controlify$getUndoButton()).invokeDoAction(); } - if (ControlifyActions.GUI_NEXT_TAB.on(controller).pulse().consumePulse()) { + next.run(); + } + + @Override + public void handleNavigation(Controller controller, Runnable next) { + while (ControlifyActions.GUI_NEXT_TAB.on(controller).pulse().consumePulse()) { operations.controlify$nextPage(); } - if (ControlifyActions.GUI_PREV_TAB.on(controller).pulse().consumePulse()) { + while (ControlifyActions.GUI_PREV_TAB.on(controller).pulse().consumePulse()) { operations.controlify$prevPage(); } - super.handleButtons(controller); + next.run(); } - public void onRebuildGUI() { - ButtonGuideApi.addGuideToButton( + @Override + public void onWidgetRebuild(Runnable next) { + context.buttonGuide().addGuideToButton( operations.controlify$getApplyButton(), ControlifyActions.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always() ); - ButtonGuideApi.addGuideToButton( + context.buttonGuide().addGuideToButton( operations.controlify$getUndoButton(), ControlifyActions.GUI_ABSTRACT_ACTION_2, ButtonGuidePredicate.always() ); - ButtonGuideApi.addGuideToButton( + context.buttonGuide().addGuideToButton( operations.controlify$getCloseButton(), ControlifyActions.GUI_BACK, ButtonGuidePredicate.always() ); - super.onWidgetRebuild(); + next.run(); } } //?} diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumScreenBehaviours.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumScreenBehaviours.java new file mode 100644 index 000000000..1c9197e92 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/SodiumScreenBehaviours.java @@ -0,0 +1,40 @@ +package dev.isxander.controlify.impl.compatibility.sodium.screenop; + +//? if sodium { + +import dev.isxander.controlify.api.screenop.ElementBehaviourRegistry; +import dev.isxander.controlify.api.screenop.ScreenopEvents; +import net.caffeinemc.mods.sodium.client.gui.VideoSettingsScreen; +import net.minecraft.client.gui.components.events.GuiEventListener; + +public final class SodiumScreenBehaviours { + private SodiumScreenBehaviours() { + } + + public static void initialize() { + ScreenopEvents.REGISTER_SCREEN_BEHAVIOURS.subscribe(registry -> { + registry.registerUnchecked(VideoSettingsScreen.class); + //? if reeses_sodium_options { + /*if (dev.isxander.controlify.impl.platform.main.PlatformMainUtil.isModLoaded("reeses-sodium-options")) { + registry.registerUnchecked(me.flashyreese.mods.reeses_sodium_options.client.gui.SodiumVideoOptionsScreen.class); + } + *///?} + }); + ScreenopEvents.REGISTER_ELEMENT_BEHAVIOURS.subscribe(event -> { + registerControl(event.registry(), "CyclingControl$CyclingControlElement"); + registerControl(event.registry(), "SliderControl$SliderControlElement"); + registerControl(event.registry(), "TickBoxControl$TickBoxControlElement"); + }); + } + + private static void registerControl(ElementBehaviourRegistry registry, String name) { + try { + var type = Class.forName("net.caffeinemc.mods.sodium.client.gui.options.control." + name) + .asSubclass(GuiEventListener.class); + registry.registerUnchecked(type); + } catch (ClassNotFoundException exception) { + throw new IllegalStateException("Sodium control class is unavailable: " + name, exception); + } + } +} +//?} diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/TickBoxControlProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/TickBoxControlProcessor.java deleted file mode 100644 index bde32e5fb..000000000 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/TickBoxControlProcessor.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.compatibility.sodium.screenop; - -import dev.isxander.controlify.impl.action.ControlifyActions; -import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.screenop.ComponentProcessor; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; - -public class TickBoxControlProcessor implements ComponentProcessor { - private final Runnable toggleMethod; - - public TickBoxControlProcessor(Runnable toggleMethod) { - this.toggleMethod = toggleMethod; - } - - @Override - public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { - if (ControlifyActions.GUI_PRESS.on(controller).pulse().consumePulse()) { - toggleMethod.run(); - return true; - } - if (ControlifyActions.GUI_SECONDARY_NAVI_RIGHT.on(controller).pulse().consumePulse() || ControlifyActions.GUI_SECONDARY_NAVI_LEFT.on(controller).pulse().consumePulse()) { - toggleMethod.run(); - return true; - } - - return false; - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/package-info.java b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/package-info.java new file mode 100644 index 000000000..1b70ae342 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/sodium/screenop/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package dev.isxander.controlify.impl.compatibility.sodium.screenop; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/CyclingControllerElementMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/CyclingControllerElementMixin.java index 2bd77edb5..739e66eaa 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/CyclingControllerElementMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/CyclingControllerElementMixin.java @@ -6,20 +6,20 @@ */ package dev.isxander.controlify.impl.compatibility.yacl.mixins; -import dev.isxander.controlify.impl.screenop.ComponentProcessor; -import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; -import dev.isxander.controlify.impl.compatibility.yacl.screenop.CyclingControllerElementComponentProcessor; +import dev.isxander.controlify.api.screenop.*; import dev.isxander.yacl3.gui.controllers.cycling.CyclingControllerElement; +import net.minecraft.client.gui.components.events.GuiEventListener; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; @Mixin(CyclingControllerElement.class) -public class CyclingControllerElementMixin implements ComponentProcessorProvider { - @Unique private final CyclingControllerElementComponentProcessor controlify$processor - = new CyclingControllerElementComponentProcessor((CyclingControllerElement) (Object) this); +public class CyclingControllerElementMixin implements ElementBehaviourFactory { @Override - public ComponentProcessor componentProcessor() { - return controlify$processor; + public ElementBehaviour create(GuiEventListener element) { + return ElementBehaviours.conditional( + () -> ((dev.isxander.controlify.impl.compatibility.yacl.mixins.ControllerWidgetAccessor) this).getControl().option().available(), + ElementBehaviours.cycle((CyclingControllerElement widget, int direction) -> widget.cycleValue(direction)).create((CyclingControllerElement) (Object) this) + ); } + } diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/OptionListWidgetMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/OptionListWidgetMixin.java index d47ac7454..1caa35313 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/OptionListWidgetMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/OptionListWidgetMixin.java @@ -6,20 +6,18 @@ */ package dev.isxander.controlify.impl.compatibility.yacl.mixins; -import dev.isxander.controlify.impl.compatibility.yacl.screenop.OptionListWidgetComponentProcessor; -import dev.isxander.controlify.impl.screenop.ComponentProcessor; -import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.api.screenop.*; +import dev.isxander.controlify.impl.compatibility.yacl.screenop.OptionListWidgetBehaviour; import dev.isxander.yacl3.gui.OptionListWidget; +import net.minecraft.client.gui.components.events.GuiEventListener; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; @Mixin(OptionListWidget.class) -public class OptionListWidgetMixin implements ComponentProcessorProvider { - @Unique private final OptionListWidgetComponentProcessor controlify$processor = - new OptionListWidgetComponentProcessor((OptionListWidget) (Object) this); +public class OptionListWidgetMixin implements ElementBehaviourFactory { @Override - public ComponentProcessor componentProcessor() { - return this.controlify$processor; + public ElementBehaviour create(GuiEventListener element) { + return new OptionListWidgetBehaviour((OptionListWidget) (Object) this); } + } diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/SliderControllerElementMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/SliderControllerElementMixin.java index 8b0f11aa7..9e77a2790 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/SliderControllerElementMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/SliderControllerElementMixin.java @@ -6,20 +6,20 @@ */ package dev.isxander.controlify.impl.compatibility.yacl.mixins; -import dev.isxander.controlify.impl.screenop.ComponentProcessor; -import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; -import dev.isxander.controlify.impl.compatibility.yacl.screenop.SliderControllerElementComponentProcessor; +import dev.isxander.controlify.api.screenop.*; import dev.isxander.yacl3.gui.controllers.slider.SliderControllerElement; +import net.minecraft.client.gui.components.events.GuiEventListener; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; @Mixin(SliderControllerElement.class) -public class SliderControllerElementMixin implements ComponentProcessorProvider { - @Unique private final SliderControllerElementComponentProcessor controlify$processor - = new SliderControllerElementComponentProcessor((SliderControllerElement) (Object) this); +public class SliderControllerElementMixin implements ElementBehaviourFactory { @Override - public ComponentProcessor componentProcessor() { - return controlify$processor; + public ElementBehaviour create(GuiEventListener element) { + return ElementBehaviours.conditional( + () -> ((dev.isxander.controlify.impl.compatibility.yacl.mixins.ControllerWidgetAccessor) this).getControl().option().available(), + ElementBehaviours.slider((SliderControllerElement widget, int direction) -> widget.incrementValue(direction)).create((SliderControllerElement) (Object) this) + ); } + } diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/StringControllerElementMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/StringControllerElementMixin.java index ae449df55..a6f1bde93 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/StringControllerElementMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/StringControllerElementMixin.java @@ -6,21 +6,18 @@ */ package dev.isxander.controlify.impl.compatibility.yacl.mixins; -import dev.isxander.controlify.impl.compatibility.yacl.screenop.StringControllerElementComponentProcessor; -import dev.isxander.controlify.impl.screenop.ComponentProcessor; -import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; +import dev.isxander.controlify.api.screenop.*; +import dev.isxander.controlify.impl.compatibility.yacl.screenop.StringControllerElementBehaviour; import dev.isxander.yacl3.gui.controllers.string.StringControllerElement; +import net.minecraft.client.gui.components.events.GuiEventListener; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; @Mixin(StringControllerElement.class) -public class StringControllerElementMixin implements ComponentProcessorProvider { - @Unique private final ComponentProcessor componentProcessor = new StringControllerElementComponentProcessor( - (StringControllerElement) (Object) this - ); +public class StringControllerElementMixin implements ElementBehaviourFactory { @Override - public ComponentProcessor componentProcessor() { - return componentProcessor; + public ElementBehaviour create(GuiEventListener element) { + return new StringControllerElementBehaviour((StringControllerElement) (Object) this); } + } diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenCategoryTabMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenCategoryTabMixin.java index cf2ea1151..d39401624 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenCategoryTabMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenCategoryTabMixin.java @@ -6,7 +6,6 @@ */ package dev.isxander.controlify.impl.compatibility.yacl.mixins; -import dev.isxander.controlify.api.gui.ButtonGuideApi; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.yacl3.gui.YACLScreen; @@ -20,11 +19,12 @@ @Mixin(YACLScreen.CategoryTab.class) public class YACLScreenCategoryTabMixin { - @Shadow @Final + @Shadow + @Final public Button saveFinishedButton; @Inject(method = "", at = @At("RETURN"), require = 0) private void onConstructCategory(CallbackInfo ci) { - ButtonGuideApi.addGuideToButton(saveFinishedButton, ControlifyActions.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always()); + dev.isxander.controlify.impl.gui.ButtonGuideApiImpl.INSTANCE.addGuideToButton(saveFinishedButton, ControlifyActions.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always()); } } diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenMixin.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenMixin.java index c77e766e2..9dc19afb6 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenMixin.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/mixins/YACLScreenMixin.java @@ -6,19 +6,16 @@ */ package dev.isxander.controlify.impl.compatibility.yacl.mixins; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; -import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; -import dev.isxander.controlify.impl.compatibility.yacl.screenop.YACLScreenProcessor; +import dev.isxander.controlify.api.screenop.*; +import dev.isxander.controlify.impl.compatibility.yacl.screenop.YACLScreenBehaviour; import dev.isxander.yacl3.gui.YACLScreen; +import org.jspecify.annotations.NonNull; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; @Mixin(YACLScreen.class) -public class YACLScreenMixin implements ScreenProcessorProvider { - @Unique private final YACLScreenProcessor controlify$processor = new YACLScreenProcessor((YACLScreen) (Object) this); - +public class YACLScreenMixin implements ScreenBehaviourFactory { @Override - public ScreenProcessor screenProcessor() { - return controlify$processor; + public ScreenBehaviour create(@NonNull ScreenContext context) { + return new YACLScreenBehaviour(context); } } diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/CyclingControllerElementComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/CyclingControllerElementComponentProcessor.java deleted file mode 100644 index 19d6cdf4e..000000000 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/CyclingControllerElementComponentProcessor.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.compatibility.yacl.screenop; - -import dev.isxander.controlify.impl.action.ControlifyActions; -import dev.isxander.controlify.impl.compatibility.yacl.mixins.ControllerWidgetAccessor; -import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.screenop.ComponentProcessor; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; -import dev.isxander.yacl3.gui.controllers.cycling.CyclingControllerElement; - -public class CyclingControllerElementComponentProcessor implements ComponentProcessor { - private final CyclingControllerElement cyclingController; - - public CyclingControllerElementComponentProcessor(CyclingControllerElement cyclingController) { - this.cyclingController = cyclingController; - } - - @Override - public boolean overrideControllerNavigation(ScreenProcessor screen, ControllerImpl controller) { - boolean left = ControlifyActions.GUI_SECONDARY_NAVI_LEFT.on(controller).pulse().consumePulse(); - boolean right = ControlifyActions.GUI_SECONDARY_NAVI_RIGHT.on(controller).pulse().consumePulse(); - - if (!((ControllerWidgetAccessor) cyclingController).getControl().option().available()) { - return false; - } - - if (left) { - cyclingController.cycleValue(-1); - - return true; - } else if (right) { - cyclingController.cycleValue(1); - - return true; - } else { - return false; - } - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/OptionListWidgetComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/OptionListWidgetBehaviour.java similarity index 54% rename from src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/OptionListWidgetComponentProcessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/OptionListWidgetBehaviour.java index 07eb6c32a..2299ead65 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/OptionListWidgetComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/OptionListWidgetBehaviour.java @@ -6,20 +6,20 @@ */ package dev.isxander.controlify.impl.compatibility.yacl.screenop; +import dev.isxander.controlify.api.screenop.*; +import dev.isxander.controlify.api.controller.Controller; import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.compatibility.yacl.mixins.GroupSeparatorEntryAccessor; import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.screenop.ComponentProcessor; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.yacl3.gui.OptionListWidget; import org.jetbrains.annotations.Nullable; import java.util.List; -public class OptionListWidgetComponentProcessor implements ComponentProcessor { +public class OptionListWidgetBehaviour implements ElementBehaviour { private final OptionListWidget optionListWidget; - public OptionListWidgetComponentProcessor(OptionListWidget optionListWidget) { + public OptionListWidgetBehaviour(OptionListWidget optionListWidget) { this.optionListWidget = optionListWidget; } @@ -27,29 +27,37 @@ public OptionListWidgetComponentProcessor(OptionListWidget optionListWidget) { * Allows navigating between groups using secondary navigation up/down. */ @Override - public boolean overrideControllerNavigation(ScreenProcessor screen, ControllerImpl controller) { - boolean up = ControlifyActions.GUI_SECONDARY_NAVI_UP.on(controller).pulse().consumePulse(); - boolean down = ControlifyActions.GUI_SECONDARY_NAVI_DOWN.on(controller).pulse().consumePulse(); - if (up || down) { - OptionListWidget.GroupSeparatorEntry nextGroup = findNextGroup(optionListWidget, up ? -1 : 1); - if (nextGroup != null) { - if (nextGroup.isExpanded()) { - var childEntries = ((GroupSeparatorEntryAccessor) nextGroup).getChildEntries(); - if (!childEntries.isEmpty()) { - var childEntry = childEntries.get(0); - var buttonWithinChild = childEntry.children().get(0); - optionListWidget.setFocused(childEntries.get(0)); - childEntry.setFocused(buttonWithinChild); - } else { - optionListWidget.setFocused(nextGroup); - } + public void handleNavigation(ScreenContext screen, Controller controller, Runnable next) { + boolean handled = false; + while (ControlifyActions.GUI_SECONDARY_NAVI_UP.on(controller).pulse().consumePulse()) { + handled |= moveGroup(-1); + } + while (ControlifyActions.GUI_SECONDARY_NAVI_DOWN.on(controller).pulse().consumePulse()) { + handled |= moveGroup(1); + } + if (!handled) { + next.run(); + } + } + + private boolean moveGroup(int direction) { + OptionListWidget.GroupSeparatorEntry nextGroup = findNextGroup(optionListWidget, direction); + if (nextGroup != null) { + if (nextGroup.isExpanded()) { + var childEntries = ((GroupSeparatorEntryAccessor) nextGroup).getChildEntries(); + if (!childEntries.isEmpty()) { + var childEntry = childEntries.get(0); + var buttonWithinChild = childEntry.children().get(0); + optionListWidget.setFocused(childEntries.get(0)); + childEntry.setFocused(buttonWithinChild); } else { optionListWidget.setFocused(nextGroup); } - return true; + } else { + optionListWidget.setFocused(nextGroup); } + return true; } - return false; } diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/SliderControllerElementComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/SliderControllerElementComponentProcessor.java deleted file mode 100644 index ae549db7b..000000000 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/SliderControllerElementComponentProcessor.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.compatibility.yacl.screenop; - -import dev.isxander.controlify.impl.action.ControlifyActions; -import dev.isxander.controlify.impl.compatibility.yacl.mixins.ControllerWidgetAccessor; -import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.screenop.ComponentProcessor; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; -import dev.isxander.yacl3.gui.controllers.slider.SliderControllerElement; - -public class SliderControllerElementComponentProcessor implements ComponentProcessor { - private final SliderControllerElement slider; - - public SliderControllerElementComponentProcessor(SliderControllerElement element) { - this.slider = element; - } - - @Override - public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { - var left = ControlifyActions.GUI_SECONDARY_NAVI_LEFT.on(controller).pulse().consumePulse(); - var right = ControlifyActions.GUI_SECONDARY_NAVI_RIGHT.on(controller).pulse().consumePulse(); - - if (!((ControllerWidgetAccessor) slider).getControl().option().available()) { - return false; - } - - if (left) { - slider.incrementValue(-1); - } else if (right) { - slider.incrementValue(1); - } else { - return false; - } - - return true; - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/StringControllerElementComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/StringControllerElementBehaviour.java similarity index 61% rename from src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/StringControllerElementComponentProcessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/StringControllerElementBehaviour.java index 28355cb28..f389d99f4 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/StringControllerElementComponentProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/StringControllerElementBehaviour.java @@ -7,36 +7,36 @@ package dev.isxander.controlify.impl.compatibility.yacl.screenop; import com.mojang.blaze3d.platform.InputConstants; +import dev.isxander.controlify.api.gui.keyboard.InputTarget; +import dev.isxander.controlify.api.gui.keyboard.KeyboardLayouts; +import dev.isxander.controlify.api.gui.keyboard.KeyboardPositioner; +import dev.isxander.controlify.api.screenop.*; +import dev.isxander.controlify.api.screenop.ElementKeyboardBehaviour; import dev.isxander.controlify.impl.compatibility.yacl.mixins.StringControllerElementAccessor; -import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.screenop.ComponentProcessor; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; -import dev.isxander.controlify.impl.screenop.keyboard.ComponentKeyboardBehaviour; -import dev.isxander.controlify.impl.screenop.keyboard.InputTarget; -import dev.isxander.controlify.impl.screenop.keyboard.KeyboardLayouts; -import dev.isxander.controlify.impl.screenop.keyboard.KeyboardOverlayScreen; import dev.isxander.yacl3.gui.controllers.string.StringControllerElement; import net.minecraft.client.input.CharacterEvent; import net.minecraft.client.input.KeyEvent; -public class StringControllerElementComponentProcessor implements ComponentProcessor { +import java.util.Optional; + +public class StringControllerElementBehaviour implements ElementBehaviour { private final StringControllerElement element; - public StringControllerElementComponentProcessor(StringControllerElement element) { + public StringControllerElementBehaviour(StringControllerElement element) { this.element = element; } @Override - public ComponentKeyboardBehaviour getKeyboardBehaviour(ScreenProcessor screen, ControllerImpl controller) { - return new ComponentKeyboardBehaviour.Handled( - KeyboardLayouts.simple(), + public Optional keyboardBehaviour(ScreenContext screen) { + return Optional.of(new ElementKeyboardBehaviour.Handled( + KeyboardLayouts.SIMPLE, new StringInputTarget(), - KeyboardOverlayScreen.aboveOrBelowWidgetPositioner( - (int) (screen.screen.width * 0.8f), (int) (screen.screen.height * 0.4f), + KeyboardPositioner.aboveOrBelowWidgetPositioner( + (int) (screen.screen().width * 0.8f), (int) (screen.screen().height * 0.4f), 1, element::getRectangle ) - ); + )); } private class StringInputTarget implements InputTarget { @@ -68,7 +68,7 @@ public boolean supportsCursorMovement() { @Override public boolean moveCursor(int amount) { int keycode = amount > 0 ? InputConstants.KEY_RIGHT : InputConstants.KEY_LEFT; - for (int i = 0; i < amount; i++) { + for (int i = 0; i < Math.abs(amount); i++) { element.keyPressed(new KeyEvent(keycode, 0, 0)); } return true; diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/YACLScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/YACLScreenBehaviour.java similarity index 57% rename from src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/YACLScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/YACLScreenBehaviour.java index 47e5b741e..53b5c2850 100644 --- a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/YACLScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/YACLScreenBehaviour.java @@ -6,33 +6,43 @@ */ package dev.isxander.controlify.impl.compatibility.yacl.screenop; +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.screenop.*; import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.compatibility.yacl.mixins.YACLScreenCategoryTabAccessor; -import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.yacl3.gui.OptionListWidget; import dev.isxander.yacl3.gui.WidgetAndType; import dev.isxander.yacl3.gui.YACLScreen; -public class YACLScreenProcessor extends ScreenProcessor { - public YACLScreenProcessor(YACLScreen screen) { - super(screen); +public final class YACLScreenBehaviour implements ScreenBehaviour { + private final ScreenContext context; + private final YACLScreen screen; + + public YACLScreenBehaviour(ScreenContext context) { + this.context = context; + this.screen = context.screen(); } @Override - protected void handleButtons(ControllerImpl controller) { - if (ControlifyActions.GUI_ABSTRACT_ACTION_1.on(controller).pulse().consumePulse()) { + public void handleInteraction(Controller controller, Runnable next) { + if (ControlifyActions.GUI_ABSTRACT_ACTION_1.on(controller).pulse().consumeAllPulses()) { if (screen.tabManager.getCurrentTab() instanceof YACLScreen.CategoryTab categoryTab) { - ((YACLScreenCategoryTabAccessor) categoryTab).getSaveFinishedButton().onPress(null); + ((YACLScreenCategoryTabAccessor) categoryTab).getSaveFinishedButton().onPress(new net.minecraft.client.input.KeyEvent(com.mojang.blaze3d.platform.InputConstants.KEY_RETURN, 0, 0)); } - playClackSound(); + context.playClickSound(); } - super.handleButtons(controller); + next.run(); } @Override - protected void onTabChanged(ControllerImpl controller) { + public void handleNavigation(Controller controller, Runnable next) { + var previous = screen.tabManager.getCurrentTab(); + next.run(); + if (dev.isxander.controlify.impl.utils.MinecraftUtil.getScreen() != screen + || screen.tabManager.getCurrentTab() == previous) { + return; + } if (screen.tabManager.getCurrentTab() instanceof YACLScreen.CategoryTab categoryTab) { WidgetAndType optionListHolder = ((YACLScreenCategoryTabAccessor) categoryTab).getOptionList(); OptionListWidget optionList = optionListHolder.getType(); @@ -52,7 +62,9 @@ protected void onTabChanged(ControllerImpl controller) { OptionListWidget.Entry firstEntry = optionList.children().get(0); optionList.setFocused(firstEntry); - firstEntry.setFocused(firstEntry.children().get(0)); + if (!firstEntry.children().isEmpty()) { + firstEntry.setFocused(firstEntry.children().get(0)); + } } } } diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/YACLScreenBehaviours.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/YACLScreenBehaviours.java new file mode 100644 index 000000000..ee437054d --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/YACLScreenBehaviours.java @@ -0,0 +1,24 @@ +package dev.isxander.controlify.impl.compatibility.yacl.screenop; + +import dev.isxander.controlify.api.screenop.ScreenopEvents; +import dev.isxander.yacl3.gui.OptionListWidget; +import dev.isxander.yacl3.gui.YACLScreen; +import dev.isxander.yacl3.gui.controllers.cycling.CyclingControllerElement; +import dev.isxander.yacl3.gui.controllers.slider.SliderControllerElement; +import dev.isxander.yacl3.gui.controllers.string.StringControllerElement; + +public final class YACLScreenBehaviours { + private YACLScreenBehaviours() { + } + + public static void initialize() { + ScreenopEvents.REGISTER_SCREEN_BEHAVIOURS.subscribe(registry -> registry.registerUnchecked(YACLScreen.class)); + ScreenopEvents.REGISTER_ELEMENT_BEHAVIOURS.subscribe(event -> { + var registry = event.registry(); + registry.registerUnchecked(OptionListWidget.class); + registry.registerUnchecked(CyclingControllerElement.class); + registry.registerUnchecked(SliderControllerElement.class); + registry.registerUnchecked(StringControllerElement.class); + }); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/package-info.java b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/package-info.java new file mode 100644 index 000000000..97aa660c7 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/compatibility/yacl/screenop/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package dev.isxander.controlify.impl.compatibility.yacl.screenop; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/impl/config/ConfigManager.java b/src/main/java/dev/isxander/controlify/impl/config/ConfigManager.java index 860a0abd3..da5f03f49 100644 --- a/src/main/java/dev/isxander/controlify/impl/config/ConfigManager.java +++ b/src/main/java/dev/isxander/controlify/impl/config/ConfigManager.java @@ -431,7 +431,8 @@ private void makeBackup(Path path) throws IOException { do { backup = path.resolveSibling(path.getFileName() + ".backup" + (index == 0 ? "" : index)); index++; - } while (Files.exists(backup)); + } + while (Files.exists(backup)); Files.copy(path, backup); } @@ -441,7 +442,8 @@ private void archiveLegacy() throws IOException { do { backup = legacyPath.resolveSibling(legacyPath.getFileName() + ".backup" + (index == 0 ? "" : index)); index++; - } while (Files.exists(backup)); + } + while (Files.exists(backup)); Files.move(legacyPath, backup); } diff --git a/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerCoordinator.java b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerCoordinator.java index 2e245c957..8a02226ac 100644 --- a/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerCoordinator.java +++ b/src/main/java/dev/isxander/controlify/impl/controllermanager/ControllerCoordinator.java @@ -63,6 +63,8 @@ private Events() { private boolean hasDiscoveredControllers; private boolean closed; + private boolean requireActionPulseConsume; + public ControllerCoordinator(Controlify controlify, SetupWizardManager setupWizardManager) { this.controlify = controlify; this.config = controlify.config(); @@ -145,6 +147,23 @@ public boolean update(boolean outOfFocus) { return !outOfFocus && this.controllersWithInput.contains(this.currentController); } + public void fixedUpdate() { + this.requireActionPulseConsume = true; + } + + public void postUpdate() { + // after a FixedUpdate + if (this.requireActionPulseConsume) { + this.requireActionPulseConsume = false; + + for (var controller : this.controllerManager.getAllControllers()) { + for (var action : controller.input().getAllActions()) { + action.state().consumeAllPulses(); + } + } + } + } + public void setCurrentController(@Nullable ControllerImpl controller, boolean changeInputMode) { if (this.closed || this.currentController == controller) { return; diff --git a/src/main/java/dev/isxander/controlify/impl/gui/ButtonGuideApiImpl.java b/src/main/java/dev/isxander/controlify/impl/gui/ButtonGuideApiImpl.java new file mode 100644 index 000000000..1bb73972a --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/gui/ButtonGuideApiImpl.java @@ -0,0 +1,24 @@ +package dev.isxander.controlify.impl.gui; + +import dev.isxander.controlify.api.action.ActionAccessor; +import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.gui.ButtonGuidePredicate; + +import java.util.function.Supplier; + +public final class ButtonGuideApiImpl implements ButtonGuideApi { + public static final ButtonGuideApi INSTANCE = new ButtonGuideApiImpl(); + + private ButtonGuideApiImpl() { + } + + @Override + public void addGuideToButton(T button, ActionAccessor action, ButtonGuidePredicate renderPredicate) { + addGuideToButton(button, () -> action, renderPredicate); + } + + @Override + public void addGuideToButton(T button, Supplier action, ButtonGuidePredicate renderPredicate) { + ButtonGuideRenderer.registerActionForButton(button, action, renderPredicate); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/gui/controllers/BindController.java b/src/main/java/dev/isxander/controlify/impl/gui/controllers/BindController.java index 7ab3b1f69..6b013bb52 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/controllers/BindController.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/controllers/BindController.java @@ -8,13 +8,12 @@ import com.mojang.blaze3d.platform.InputConstants; import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.api.screenop.*; import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.controllermanager.ControllerType; import dev.isxander.controlify.impl.gui.screen.BindConsumerScreen; -import dev.isxander.controlify.impl.screenop.ComponentProcessor; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.utils.MinecraftUtil; import dev.isxander.yacl3.api.Controller; import dev.isxander.yacl3.api.Option; @@ -62,7 +61,7 @@ public BindControllerElement provideWidget(YACLScreen yaclScreen, Dimension implements ComponentProcessor { + public static class BindControllerElement extends ControllerWidget implements ElementBehaviour { public boolean awaitingControllerInput = false; private final Component awaitingText = Component.translatable("controlify.gui.bind_input_awaiting").withStyle(ChatFormatting.ITALIC); @@ -73,7 +72,7 @@ public BindControllerElement(BindController control, YACLScreen screen, Dimensio @Override protected void extractValueText(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { if (awaitingControllerInput) { - graphics.text(textRenderer, awaitingText, getDimension().xLimit() - textRenderer.width(awaitingText) - getXPadding(), (int)(getDimension().centerY() - textRenderer.lineHeight / 2f), 0xFFFFFFFF, true); + graphics.text(textRenderer, awaitingText, getDimension().xLimit() - textRenderer.width(awaitingText) - getXPadding(), (int) (getDimension().centerY() - textRenderer.lineHeight / 2f), 0xFFFFFFFF, true); } else { var bind = control.option().pendingValue(); if (bind == null) { @@ -84,7 +83,7 @@ protected void extractValueText(GuiGraphicsExtractor graphics, int mouseX, int m .getComponentFromInput((control.controller == null ? ControllerType.DEFAULT.id() : control.controller.info().type().id()), bind.orElse(null)); int width = textRenderer.width(text); - graphics.text(textRenderer, text, getDimension().xLimit() - width - 1, (int)(getDimension().centerY() - textRenderer.lineHeight / 2f + 1), -1, false); + graphics.text(textRenderer, text, getDimension().xLimit() - width - 1, (int) (getDimension().centerY() - textRenderer.lineHeight / 2f + 1), -1, false); } } @@ -117,17 +116,17 @@ private void openConsumerScreen() { } @Override - public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { + public void handleInteraction(ScreenContext screen, dev.isxander.controlify.api.controller.Controller controller, Runnable next) { if (controller != control.controller) { - return true; + return; } - if (ControlifyActions.GUI_PRESS.on(controller).pulse().consumePulse()) { + if (ControlifyActions.GUI_PRESS.on(controller).pulse().consumeAllPulses()) { openConsumerScreen(); - return true; + return; } - return false; + next.run(); } @Override diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/CommonKeyboardHints.java b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/CommonKeyboardHints.java similarity index 94% rename from src/main/java/dev/isxander/controlify/impl/screenop/keyboard/CommonKeyboardHints.java rename to src/main/java/dev/isxander/controlify/impl/gui/keyboard/CommonKeyboardHints.java index e740b8732..5bf6414d4 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/CommonKeyboardHints.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/CommonKeyboardHints.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.impl.screenop.keyboard; +package dev.isxander.controlify.impl.gui.keyboard; import dev.isxander.controlify.api.font.ActionGlyphs; import dev.isxander.controlify.impl.action.ControlifyActions; diff --git a/src/main/java/dev/isxander/controlify/impl/gui/keyboard/DelegatingInputTarget.java b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/DelegatingInputTarget.java new file mode 100644 index 000000000..1288e8bf8 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/DelegatingInputTarget.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.gui.keyboard; + +import dev.isxander.controlify.api.gui.keyboard.InputTarget; + +public class DelegatingInputTarget implements InputTarget { + private final InputTarget target; + + public DelegatingInputTarget(InputTarget target) { + this.target = target; + } + + @Override + public boolean supportsCharInput() { + return this.target.supportsCharInput(); + } + + @Override + public boolean acceptChar(char ch, int modifiers) { + return this.target.acceptChar(ch, modifiers); + } + + @Override + public boolean supportsKeyCodeInput() { + return this.target.supportsKeyCodeInput(); + } + + @Override + public boolean acceptKeyCode(int keycode, int scancode, int modifiers) { + return this.target.acceptKeyCode(keycode, scancode, modifiers); + } + + @Override + public boolean supportsCopying() { + return this.target.supportsCopying(); + } + + @Override + public boolean copy() { + return this.target.copy(); + } + + @Override + public boolean supportsCursorMovement() { + return this.target.supportsCursorMovement(); + } + + @Override + public boolean moveCursor(int amount) { + return this.target.moveCursor(amount); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/gui/keyboard/EditBoxInputTarget.java b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/EditBoxInputTarget.java new file mode 100644 index 000000000..9be153296 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/EditBoxInputTarget.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.gui.keyboard; + +import dev.isxander.controlify.api.gui.keyboard.InputTarget; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.input.CharacterEvent; +import net.minecraft.client.input.KeyEvent; + +public record EditBoxInputTarget(EditBox editBox) implements InputTarget { + + @Override + public boolean supportsCharInput() { + return true; + } + + @Override + public boolean acceptChar(char ch, int modifiers) { + this.editBox.charTyped(new CharacterEvent(ch)); + return true; + } + + @Override + public boolean supportsKeyCodeInput() { + return true; + } + + @Override + public boolean acceptKeyCode(int keycode, int scancode, int modifiers) { + this.editBox.keyPressed(new KeyEvent(keycode, scancode, modifiers)); + return true; + } + + @Override + public boolean supportsCursorMovement() { + return true; + } + + @Override + public boolean moveCursor(int amount) { + this.editBox.moveCursor(amount, false); + return true; + } + + @Override + public boolean supportsCopying() { + return true; + } + + @Override + public boolean copy() { + Minecraft.getInstance().keyboardHandler.setClipboard(this.editBox.getValue()); + return true; + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/FallbackKeyboardLayout.java b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/FallbackKeyboardLayout.java similarity index 90% rename from src/main/java/dev/isxander/controlify/impl/screenop/keyboard/FallbackKeyboardLayout.java rename to src/main/java/dev/isxander/controlify/impl/gui/keyboard/FallbackKeyboardLayout.java index 888f411c7..2c1813b4b 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/FallbackKeyboardLayout.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/FallbackKeyboardLayout.java @@ -4,19 +4,20 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.impl.screenop.keyboard; +package dev.isxander.controlify.impl.gui.keyboard; import dev.isxander.controlify.api.action.ActionAccessor; import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.utils.CUtil; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; -import static dev.isxander.controlify.impl.screenop.keyboard.KeyboardLayout.Key; -import static dev.isxander.controlify.impl.screenop.keyboard.KeyboardLayout.KeyFunction.*; import java.util.List; import java.util.Optional; +import static dev.isxander.controlify.impl.gui.keyboard.KeyboardLayout.Key; +import static dev.isxander.controlify.impl.gui.keyboard.KeyboardLayout.KeyFunction.*; + /** * A code-defined fallback keyboard layout if the resource-pack driven ones * are not available or the layout required is not defined. @@ -77,12 +78,15 @@ public final class FallbackKeyboardLayout { private static Key k(SpecialFunc.Action action, float width, @Nullable ActionAccessor shortcutBinding) { return new Key(new SpecialFunc(action), width, Optional.ofNullable(shortcutBinding)); } + private static Key k(String string) { return new Key(new StringFunc(string)); } + private static Key k(String regular, String shifted) { return new Key(new StringFunc(regular), new StringFunc(shifted)); } + private static Key k(String string, float width) { return new Key(new StringFunc(string), width); } diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyWidget.java b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/KeyWidget.java similarity index 82% rename from src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyWidget.java rename to src/main/java/dev/isxander/controlify/impl/gui/keyboard/KeyWidget.java index b06786da8..63a217f4a 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyWidget.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/KeyWidget.java @@ -4,17 +4,17 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.impl.screenop.keyboard; +package dev.isxander.controlify.impl.gui.keyboard; import com.mojang.blaze3d.platform.InputConstants; import dev.isxander.controlify.api.action.Action; +import dev.isxander.controlify.api.controller.Controller; import dev.isxander.controlify.api.font.ActionGlyphs; +import dev.isxander.controlify.api.gui.keyboard.InputTarget; +import dev.isxander.controlify.api.screenop.ElementBehaviour; +import dev.isxander.controlify.api.screenop.ScreenContext; import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.action.ControlifyActions; -import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.screenop.ComponentProcessor; -import dev.isxander.controlify.impl.screenop.ScreenControllerEventListener; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.utils.CUtil; import dev.isxander.controlify.impl.utils.HoldRepeatHelper; import net.minecraft.client.Minecraft; @@ -31,7 +31,7 @@ * Represents a single key widget within a {@link KeyboardWidget}. * It handles rendering, input processing, and interaction with the keyboard layout. */ -public class KeyWidget extends AbstractWidget implements ComponentProcessor, ScreenControllerEventListener { +public class KeyWidget extends AbstractWidget implements ElementBehaviour { public static final Identifier SPRITE = CUtil.rl("keyboard/key"); public static final Identifier SPRITE_PRESSED = CUtil.rl("keyboard/key_pressed"); @@ -45,6 +45,7 @@ public class KeyWidget extends AbstractWidget implements ComponentProcessor, Scr private boolean buttonPressed; private boolean shortcutPressed; private boolean ignoreHeldPress; + private boolean newlyFocused = true; private final HoldRepeatHelper focusedRepeatHelper = new HoldRepeatHelper(10, 2); private final HoldRepeatHelper shortcutRepeatHelper = new HoldRepeatHelper(10, 2); @@ -113,16 +114,24 @@ protected void extractWidgetRenderState(@NonNull GuiGraphicsExtractor graphics, } @Override - public void onFocusGained(ScreenProcessor screen, ControllerImpl controller) { - this.ignoreHeldPress = ControlifyActions.GUI_PRESS.on(controller).latch().isLatchActive(); + public void onFocusChanged(@NonNull ScreenContext screen, boolean focused) { + this.newlyFocused = focused; this.buttonPressed = false; this.focusedRepeatHelper.reset(); } @Override - public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { + public void handleInteraction(@NonNull ScreenContext screen, @NonNull Controller controller, @NonNull Runnable next) { + if (screen.isVirtualMouseEnabled()) { + next.run(); + return; + } Action guiPress = ControlifyActions.GUI_PRESS.on(controller); boolean held = guiPress.latch().isLatchActive(); + if (this.newlyFocused) { + this.ignoreHeldPress = held; + this.newlyFocused = false; + } if (!held) { this.ignoreHeldPress = false; } @@ -130,19 +139,16 @@ public boolean overrideControllerButtons(ScreenProcessor screen, ControllerIm if (this.focusedRepeatHelper.shouldAction(this.buttonPressed)) { onPress(); } - while (guiPress.pulse().consumePulse()) { - // The keyboard handles this action through its latch, including local repetition. - } - return false; + guiPress.pulse().consumeAllPulses(); + next.run(); } - @Override - public void onControllerInput(ControllerImpl controller) { + public void handleShortcut(Controller controller) { this.key.shortcutBinding().ifPresent(accessor -> { Action shortcut = accessor.on(controller); this.shortcutPressed = shortcut.latch().isLatchActive(); boolean repeat = this.shortcutRepeatHelper.shouldAction(this.shortcutPressed); - boolean pressed = shortcut.pulse().consumePulse(); + boolean pressed = shortcut.pulse().consumeAllPulses(); if (pressed || repeat) { onPress(); } @@ -171,16 +177,15 @@ private void onPress() { KeyboardLayout.KeyFunction keyFunction = this.getKeyFunction(); InputTarget inputConsumer = this.keyboard.getInputTarget(); - ScreenProcessor.playClackSound(); + playDownSound(Minecraft.getInstance().getSoundManager()); boolean wasShiftAction = false; switch (keyFunction) { - case KeyboardLayout.KeyFunction.StringFunc stringKey -> - insertText(stringKey.string(), inputConsumer); + case KeyboardLayout.KeyFunction.StringFunc stringKey -> insertText(stringKey.string(), inputConsumer); case KeyboardLayout.KeyFunction.CodeFunc codeKey -> - codeKey.codes().forEach(code -> inputConsumer.acceptKeyCode(code.keycode(), code.scancode(), code.modifier())); + codeKey.codes().forEach(code -> inputConsumer.acceptKeyCode(code.keycode(), code.scancode(), code.modifier())); case KeyboardLayout.KeyFunction.SpecialFunc specialKey -> { switch (specialKey.action()) { @@ -219,8 +224,7 @@ private void onPress() { } } - case KeyboardLayout.KeyFunction.ChangeLayoutFunc func -> - this.changeLayout(func.layout()); + case KeyboardLayout.KeyFunction.ChangeLayoutFunc func -> this.changeLayout(func.layout()); } if (!wasShiftAction && this.keyboard.isShifted() && !this.keyboard.isShiftLocked()) { @@ -256,22 +260,26 @@ public KeyboardLayout.KeyFunction getKeyFunction() { public boolean isVisuallyPressed() { return this.mousePressed - || this.buttonPressed - || this.shortcutPressed - || this.isShiftKeyAndShifting() - || this.isShiftLockKeyAndShiftLocked(); + || this.buttonPressed + || this.shortcutPressed + || this.isShiftKeyAndShifting() + || this.isShiftLockKeyAndShiftLocked(); } private boolean isShiftKeyAndShifting() { return this.keyboard.isShifted() && !this.keyboard.isShiftLocked() - && this.getKeyFunction() instanceof KeyboardLayout.KeyFunction.SpecialFunc(KeyboardLayout.KeyFunction.SpecialFunc.Action action) - && action == KeyboardLayout.KeyFunction.SpecialFunc.Action.SHIFT; + && this.getKeyFunction() instanceof KeyboardLayout.KeyFunction.SpecialFunc( + KeyboardLayout.KeyFunction.SpecialFunc.Action action + ) + && action == KeyboardLayout.KeyFunction.SpecialFunc.Action.SHIFT; } private boolean isShiftLockKeyAndShiftLocked() { return this.keyboard.isShiftLocked() - && this.getKeyFunction() instanceof KeyboardLayout.KeyFunction.SpecialFunc(KeyboardLayout.KeyFunction.SpecialFunc.Action action) - && action == KeyboardLayout.KeyFunction.SpecialFunc.Action.SHIFT_LOCK; + && this.getKeyFunction() instanceof KeyboardLayout.KeyFunction.SpecialFunc( + KeyboardLayout.KeyFunction.SpecialFunc.Action action + ) + && action == KeyboardLayout.KeyFunction.SpecialFunc.Action.SHIFT_LOCK; } private boolean supportsAction(boolean shifted) { @@ -282,13 +290,12 @@ private boolean supportsAction(boolean shifted) { return switch (this.getKey().getFunction(shifted)) { case KeyboardLayout.KeyFunction.StringFunc ignored -> supportsCharInput; case KeyboardLayout.KeyFunction.CodeFunc ignored -> supportsKeyCodeInput; - case KeyboardLayout.KeyFunction.SpecialFunc specialFunc -> - switch (specialFunc.action()) { - case ENTER, BACKSPACE, LEFT_ARROW, RIGHT_ARROW, UP_ARROW, DOWN_ARROW -> supportsKeyCodeInput; - case TAB, PASTE -> supportsCharInput; - case COPY_ALL -> supportsCopying; - case SHIFT, SHIFT_LOCK, PREVIOUS_LAYOUT -> true; - }; + case KeyboardLayout.KeyFunction.SpecialFunc specialFunc -> switch (specialFunc.action()) { + case ENTER, BACKSPACE, LEFT_ARROW, RIGHT_ARROW, UP_ARROW, DOWN_ARROW -> supportsKeyCodeInput; + case TAB, PASTE -> supportsCharInput; + case COPY_ALL -> supportsCopying; + case SHIFT, SHIFT_LOCK, PREVIOUS_LAYOUT -> true; + }; case KeyboardLayout.KeyFunction.ChangeLayoutFunc ignored -> true; }; } diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayout.java b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/KeyboardLayout.java similarity index 90% rename from src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayout.java rename to src/main/java/dev/isxander/controlify/impl/gui/keyboard/KeyboardLayout.java index 8a6e01b1f..6e6153f2f 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayout.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/KeyboardLayout.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.impl.screenop.keyboard; +package dev.isxander.controlify.impl.gui.keyboard; import com.mojang.datafixers.util.Either; import com.mojang.datafixers.util.Pair; @@ -32,8 +32,9 @@ * A keyboard layout consists of keys within rows, it is a column-aligned layout, * meaning keys cannot span multiple columns. * A keyboard layout has a single shift-layer, defined by the {@link Key}. + * * @param width the unit width of the keyboard layout. all rows must sum to this width. - * @param keys the rows of keys in the keyboard layout, row-major order. + * @param keys the rows of keys in the keyboard layout, row-major order. */ public record KeyboardLayout(float width, List> keys) { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( @@ -44,9 +45,9 @@ public record KeyboardLayout(float width, List> keys) { .fieldOf("keys").forGetter(KeyboardLayout::keys) ).apply(instance, KeyboardLayout::new)).validate( layout -> - validateRowWidths(layout) - ? DataResult.success(layout) - : DataResult.error(() -> "Row widths do not match the specified row width: " + layout.width()) + validateRowWidths(layout) + ? DataResult.success(layout) + : DataResult.error(() -> "Row widths do not match the specified row width: " + layout.width()) ); public static boolean validateRowWidths(KeyboardLayout layout) { @@ -78,13 +79,14 @@ public static KeyboardLayout of(float width, List... keys) { *

* Each key function provides the display name of the key with {@link KeyFunction#displayName()}. * - * @param regular the regular key function, which is used when the shift is not enabled. - * @param shifted the shifted key function, which is used when the shift is enabled - * @param width the unit width of the key, which is multiplied by the keyboard width to determine the actual pixel width of the key + * @param regular the regular key function, which is used when the shift is not enabled. + * @param shifted the shifted key function, which is used when the shift is enabled + * @param width the unit width of the key, which is multiplied by the keyboard width to determine the actual pixel width of the key * @param shortcutBinding an optional shortcut binding for the key, used to display a shortcut in the UI - * @param identifier an optional identifier for the key, used when changing layouts to focus a specific key with a matching identifier + * @param identifier an optional identifier for the key, used when changing layouts to focus a specific key with a matching identifier */ - public record Key(KeyFunction regular, KeyFunction shifted, float width, Optional shortcutBinding, @Nullable String identifier) { + public record Key(KeyFunction regular, KeyFunction shifted, float width, Optional shortcutBinding, + @Nullable String identifier) { private static final Codec WIDTH_CODEC = Codec.floatRange(0.1f, Float.MAX_VALUE); private static final Codec PAIR_CODEC = Codec.withAlternative( @@ -149,6 +151,7 @@ public sealed interface KeyFunction { /** * Returns the display name of the key function. * Used to display the key in the UI. + * * @return the display name of the key function */ Component displayName(); @@ -156,6 +159,7 @@ public sealed interface KeyFunction { /** * Creates a shifted version of this key function. * May return itself if the function does not support shifting. + * * @return the shifted key function */ default KeyFunction createShifted() { @@ -169,7 +173,8 @@ public DataResult encode(KeyFunction input, DynamicOps ops, T prefix) case StringFunc stringKey -> StringFunc.CODEC.encode(stringKey, ops, prefix); case CodeFunc codeKey -> CodeFunc.CODEC.encode(codeKey, ops, prefix); case SpecialFunc specialKey -> SpecialFunc.CODEC.encode(specialKey, ops, prefix); - case ChangeLayoutFunc changeLayoutKey -> ChangeLayoutFunc.CODEC.encode(changeLayoutKey, ops, prefix); + case ChangeLayoutFunc changeLayoutKey -> + ChangeLayoutFunc.CODEC.encode(changeLayoutKey, ops, prefix); }; } @@ -189,7 +194,7 @@ public DataResult> decode(DynamicOps ops, T input) { *

* This function supports {@link #createShifted()} which uses {@link String#toUpperCase()} to create a shifted version of the string. * - * @param string the string to insert when the key is pressed + * @param string the string to insert when the key is pressed * @param manualDisplayName an optional manual display name for the key, if not provided, the string itself is used when returning {@link #displayName()} */ record StringFunc(String string, @Nullable Component manualDisplayName) implements KeyFunction { @@ -225,7 +230,7 @@ public KeyFunction createShifted() { *

* This function does not support {@link #createShifted()} as key codes cannot be automatically upper-cased. * - * @param codes the list of key codes to insert when the key is pressed + * @param codes the list of key codes to insert when the key is pressed * @param displayName the display name of the key function, used to display the key in the UI */ record CodeFunc(List codes, Component displayName) implements KeyFunction { @@ -237,7 +242,7 @@ record CodeFunc(List codes, Component displayName) implements KeyFuncti /** * A key code is a combination of a keycode, scancode, and modifier. * - * @param keycode is the logical key code, platform agnostic. use {@link com.mojang.blaze3d.platform.InputConstants} to get the key code. + * @param keycode is the logical key code, platform agnostic. use {@link com.mojang.blaze3d.platform.InputConstants} to get the key code. * @param scancode is the physical key code, platform specific. usually leaving blank is fine since no one ever looks at it. * @param modifier is the modifier bitset, which can be used to specify additional key modifiers like shift, ctrl, alt, etc. * use {@link org.lwjgl.glfw.GLFW#GLFW_MOD_SHIFT} etc @@ -318,7 +323,7 @@ public Component displayName() { *

* This function does not support {@link #createShifted()} as layouts cannot be automatically upper-cased. * - * @param layout the layout id to switch to when the key is pressed. + * @param layout the layout id to switch to when the key is pressed. * @param displayName the display name of the key function, used to display the key in the UI */ record ChangeLayoutFunc(Identifier layout, Component displayName) implements KeyFunction { diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayoutManager.java b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/KeyboardLayoutManager.java similarity index 96% rename from src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayoutManager.java rename to src/main/java/dev/isxander/controlify/impl/gui/keyboard/KeyboardLayoutManager.java index e022092e3..6d7be86ef 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayoutManager.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/KeyboardLayoutManager.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.impl.screenop.keyboard; +package dev.isxander.controlify.impl.gui.keyboard; import com.google.gson.JsonElement; import com.google.gson.JsonParser; @@ -12,11 +12,11 @@ import dev.isxander.controlify.impl.platform.client.resource.SimpleControlifyReloadListener; import dev.isxander.controlify.impl.utils.CUtil; import dev.isxander.controlify.impl.utils.log.ControlifyLogger; -import net.minecraft.util.Util; import net.minecraft.client.Minecraft; import net.minecraft.resources.Identifier; import net.minecraft.server.packs.resources.Resource; import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.util.Util; import java.io.BufferedReader; import java.util.Map; @@ -88,7 +88,7 @@ public KeyboardLayoutWithId getLayout(Identifier layoutId, String languageCode) return Optional.ofNullable(this.layouts.get(key)) .or(() -> Optional.ofNullable(this.layouts.get(key.withDefaultLanguage()))) .map(layout -> new KeyboardLayoutWithId(layout, layoutId)) - .orElse(KeyboardLayouts.fallback()); + .orElse(new KeyboardLayoutWithId(FallbackKeyboardLayout.QWERTY, FallbackKeyboardLayout.ID)); } public KeyboardLayoutWithId getLayout(Identifier layout) { @@ -124,7 +124,8 @@ private static Map listMatchingResources(ResourceManager r return resourceManager.listResources(PREFIX, path -> path.getPath().endsWith(".json")); } - public record Preparations(Map layouts) {} + public record Preparations(Map layouts) { + } private record KeyboardLayoutKey(String languageCode, Identifier layoutId) { public static final String DEFAULT_LANGUAGE = "en_us"; diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayoutWithId.java b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/KeyboardLayoutWithId.java similarity index 80% rename from src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayoutWithId.java rename to src/main/java/dev/isxander/controlify/impl/gui/keyboard/KeyboardLayoutWithId.java index 3a180ec4b..e27dd8647 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardLayoutWithId.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/KeyboardLayoutWithId.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.impl.screenop.keyboard; +package dev.isxander.controlify.impl.gui.keyboard; import net.minecraft.resources.Identifier; @@ -12,8 +12,9 @@ * Represents a keyboard layout with an associated ID. * Layouts themselves do not have IDs, but this record * couples a layout with its resource pack ID. + * * @param layout the keyboard layout - * @param id the resource location ID of the layout + * @param id the resource location ID of the layout */ public record KeyboardLayoutWithId(KeyboardLayout layout, Identifier id) { } diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardOverlayScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/KeyboardOverlayScreen.java similarity index 65% rename from src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardOverlayScreen.java rename to src/main/java/dev/isxander/controlify/impl/gui/keyboard/KeyboardOverlayScreen.java index 54fcd4979..dc1753588 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardOverlayScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/KeyboardOverlayScreen.java @@ -4,20 +4,21 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.impl.screenop.keyboard; +package dev.isxander.controlify.impl.gui.keyboard; import com.mojang.blaze3d.platform.InputConstants; +import dev.isxander.controlify.api.gui.keyboard.InputTarget; +import dev.isxander.controlify.api.gui.keyboard.KeyboardPositioner; import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.mixins.feature.screenop.GuiMixin; import dev.isxander.controlify.impl.utils.MinecraftUtil; +import dev.isxander.controlify.mixins.feature.screenop.GuiMixin; import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.client.gui.navigation.ScreenAxis; import net.minecraft.client.gui.navigation.ScreenRectangle; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.input.MouseButtonEvent; +import net.minecraft.resources.Identifier; import org.jspecify.annotations.NonNull; -import java.util.function.Supplier; /** * A screen with a single widget: the keyboard widget. @@ -29,12 +30,12 @@ * instead of adding and removing widgets, where focus to the keyboard could * be lost without the keyboard closing. * - * @see GuiMixin#preventRemovingOldScreen(Screen, Screen) - * this mixin prevents calling removed() on the underlying screen when this overlay is presented, since it will be restored + * The GUI mixin prevents calling removed() on the underlying screen when this + * overlay is presented, since the underlying screen will be restored. */ public class KeyboardOverlayScreen extends Screen { private final Screen backgroundScreen; - private final KeyboardLayoutWithId initialKeyboardLayout; + private final Identifier initialKeyboardLayout; private final InputTarget inputTarget; private final KeyboardPositioner keyboardPositioner; @@ -42,7 +43,7 @@ public class KeyboardOverlayScreen extends Screen { public KeyboardOverlayScreen( Screen backgroundScreen, - KeyboardLayoutWithId initialKeyboardLayout, + Identifier initialKeyboardLayout, InputTarget inputTarget, KeyboardPositioner keyboardPositioner ) { @@ -57,7 +58,7 @@ public KeyboardOverlayScreen( protected void init() { KeyboardLayoutWithId layout = this.keyboardWidget != null ? Controlify.instance().keyboardLayoutManager().getLayout(this.keyboardWidget.getCurrentLayoutId()) - : this.initialKeyboardLayout; + : Controlify.instance().keyboardLayoutManager().getLayout(this.initialKeyboardLayout); ScreenRectangle keyboardRect = this.keyboardPositioner.positionKeyboard(this.width, this.height); @@ -125,14 +126,14 @@ public void onClose() { // so the screen is not reinitialised MinecraftUtil.forceSetScreen(this.backgroundScreen); - Controlify.instance().virtualMouseHandler().onScreenChanged(); + dev.isxander.controlify.impl.screenop.ScreenRuntime.onScreenChanged(); } /** * An input target that captures {@link InputConstants#KEY_RETURN} and {@link InputConstants#KEY_ESCAPE} * key presses to close the keyboard overlay screen. */ - private class WrappedInputTarget extends InputTarget.Delegated { + private class WrappedInputTarget extends DelegatingInputTarget { public WrappedInputTarget(InputTarget target) { super(target); } @@ -149,53 +150,4 @@ public boolean acceptKeyCode(int keycode, int scancode, int modifiers) { } } - @FunctionalInterface - public interface KeyboardPositioner { - ScreenRectangle positionKeyboard(int screenWidth, int screenHeight); - } - - public static KeyboardPositioner aboveOrBelowWidgetPositioner(int desiredKeyboardWidth, int desiredKeyboardHeight, int padding, Supplier widgetRectSupplier) { - return (screenWidth, screenHeight) -> { - ScreenRectangle widgetRect = widgetRectSupplier.get(); - - int keyboardWidth = Math.min(desiredKeyboardWidth, screenWidth); - int keyboardLeft = Math.clamp( - widgetRect.getCenterInAxis(ScreenAxis.HORIZONTAL) - keyboardWidth / 2, - 0, screenWidth - keyboardWidth - ); - - int spaceBelow = screenHeight - widgetRect.bottom() - padding; - int spaceAbove = widgetRect.top() - padding; - - // Determine the best vertical position for the keyboard. - // 1. Prefer placing the keyboard below the widget, if it fits. - // 2. If there's not enough space below, check space above. - // 3. If neither fits fully, pick the side with more space and shrink the height. - int keyboardHeight; - boolean above; - if (spaceBelow >= desiredKeyboardHeight) { - keyboardHeight = desiredKeyboardHeight; - above = false; - } else if (spaceAbove >= desiredKeyboardHeight) { - keyboardHeight = desiredKeyboardHeight; - above = true; - } else if (spaceBelow >= spaceAbove) { - keyboardHeight = spaceBelow; - above = false; - } else { - keyboardHeight = spaceAbove; - above = true; - } - int keyboardTop = above - ? widgetRect.top() - keyboardHeight - padding - : widgetRect.bottom() + padding; - - return new ScreenRectangle( - keyboardLeft, - keyboardTop, - keyboardWidth, - keyboardHeight - ); - }; - } } diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardWidget.java b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/KeyboardWidget.java similarity index 85% rename from src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardWidget.java rename to src/main/java/dev/isxander/controlify/impl/gui/keyboard/KeyboardWidget.java index 2cd950f21..00c14eca2 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/keyboard/KeyboardWidget.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/keyboard/KeyboardWidget.java @@ -4,14 +4,16 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.impl.screenop.keyboard; +package dev.isxander.controlify.impl.gui.keyboard; import com.google.common.collect.ImmutableList; +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.gui.keyboard.InputTarget; +import dev.isxander.controlify.api.screenop.ElementBehaviour; +import dev.isxander.controlify.api.screenop.ScreenContext; import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.screenop.ComponentProcessor; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; import dev.isxander.controlify.impl.utils.HoldRepeatHelper; import net.minecraft.client.gui.ComponentPath; import net.minecraft.client.gui.GuiGraphicsExtractor; @@ -38,7 +40,7 @@ * arranging the keys based on a {@link KeyboardLayout} as well as coordinating inputs and global * state between keys. */ -public class KeyboardWidget extends AbstractWidget implements ContainerEventHandler, ComponentProcessor { +public class KeyboardWidget extends AbstractWidget implements ContainerEventHandler, ElementBehaviour { private Identifier currentLayout; private @Nullable Identifier previousLayout; @@ -82,9 +84,9 @@ public void updateLayout(KeyboardLayoutWithId layout, @Nullable String identifie oldLayoutChangerToFocus != null, k -> { boolean isOldLayout = k.getKeyFunction() instanceof KeyboardLayout.KeyFunction.ChangeLayoutFunc changeLayoutKey - && changeLayoutKey.layout().equals(oldLayoutChangerToFocus); + && changeLayoutKey.layout().equals(oldLayoutChangerToFocus); boolean isPrevLayout = k.getKeyFunction() instanceof KeyboardLayout.KeyFunction.SpecialFunc specialFunc - && specialFunc.action() == KeyboardLayout.KeyFunction.SpecialFunc.Action.PREVIOUS_LAYOUT; + && specialFunc.action() == KeyboardLayout.KeyFunction.SpecialFunc.Action.PREVIOUS_LAYOUT; return isOldLayout || isPrevLayout; }) ); @@ -147,22 +149,18 @@ protected void extractWidgetRenderState(@NonNull GuiGraphicsExtractor graphics, } @Override - public boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { + public void handleNavigation(@NonNull ScreenContext screen, @NonNull Controller controller, @NonNull Runnable next) { InputTarget inputTarget = this.getInputTarget(); - var settings = controller.settings().generic; + var settings = ((ControllerImpl) controller).settings().generic; boolean supportsCursor = inputTarget.supportsCursorMovement(); boolean forward = this.forwardCursorRepeat.shouldAction(supportsCursor - && ControlifyActions.GUI_NEXT_TAB.on(controller).latch().isLatchActive()); + && ControlifyActions.GUI_NEXT_TAB.on(controller).latch().isLatchActive()); boolean backward = this.backwardCursorRepeat.shouldAction(supportsCursor - && ControlifyActions.GUI_PREV_TAB.on(controller).latch().isLatchActive()); + && ControlifyActions.GUI_PREV_TAB.on(controller).latch().isLatchActive()); boolean movedCursor = false; if (supportsCursor) { - while (ControlifyActions.GUI_NEXT_TAB.on(controller).pulse().consumePulse()) { - // Cursor movement uses held state so tab pulses must not also navigate the screen. - } - while (ControlifyActions.GUI_PREV_TAB.on(controller).pulse().consumePulse()) { - // Cursor movement uses held state so tab pulses must not also navigate the screen. - } + ControlifyActions.GUI_NEXT_TAB.on(controller).pulse().consumeAllPulses(); + ControlifyActions.GUI_PREV_TAB.on(controller).pulse().consumeAllPulses(); if (forward) { movedCursor = inputTarget.moveCursor(1); } else if (backward) { @@ -170,18 +168,22 @@ public boolean overrideControllerButtons(ScreenProcessor screen, ControllerIm } } if (movedCursor) { - ScreenProcessor.playFocusChangeSound(); + screen.playFocusChangeSound(); if (settings.keyboard.hintCursor && settings.guide.showScreenGuides) { settings.keyboard.hintCursor = false; Controlify.instance().config().saveSafely(); } } - this.keys.forEach(key -> key.onControllerInput(controller)); + next.run(); + } - // Suppress default Enter handling while the keyboard processes the shared GUI action. - boolean pressed = ControlifyActions.GUI_PRESS.on(controller).pulse().consumePulse(); - return movedCursor || pressed; + @Override + public void handleInteraction(@NonNull ScreenContext screen, @NonNull Controller controller, @NonNull Runnable next) { + this.keys.forEach(key -> key.handleShortcut(controller)); + // The focused key handles activation; the screen must not also send Enter. + ControlifyActions.GUI_PRESS.on(controller).pulse().consumeAllPulses(); + next.run(); } public void setShifted(boolean shifted) { @@ -276,7 +278,8 @@ public void setFocused(@Nullable GuiEventListener focused) { this.focused = (KeyWidget) focused; } - @Nullable @Override + @Nullable + @Override public ComponentPath nextFocusPath(@NonNull FocusNavigationEvent event) { return ContainerEventHandler.super.nextFocusPath(event); } @@ -285,10 +288,12 @@ public ComponentPath nextFocusPath(@NonNull FocusNavigationEvent event) { public boolean mouseClicked(@NonNull MouseButtonEvent mouseButtonEvent, boolean doubleClick) { return ContainerEventHandler.super.mouseClicked(mouseButtonEvent, doubleClick); } + @Override public boolean mouseReleased(@NonNull MouseButtonEvent mouseButtonEvent) { return ContainerEventHandler.super.mouseReleased(mouseButtonEvent); } + @Override public boolean mouseDragged(@NonNull MouseButtonEvent mouseButtonEvent, double dx, double dy) { return ContainerEventHandler.super.mouseDragged(mouseButtonEvent, dx, dy); diff --git a/src/main/java/dev/isxander/controlify/impl/gui/screen/BindConsumerScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/BindConsumerScreen.java index 675ceb042..a81ca9abd 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/screen/BindConsumerScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/BindConsumerScreen.java @@ -6,13 +6,13 @@ */ package dev.isxander.controlify.impl.gui.screen; +import dev.isxander.controlify.api.controller.Controller; import dev.isxander.controlify.api.input.LogicalInput; +import dev.isxander.controlify.api.screenop.*; import dev.isxander.controlify.api.utils.event.EventSource; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.gui.controllers.BindController; import dev.isxander.controlify.impl.input.signal.LogicalSignal; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; -import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; import dev.isxander.controlify.impl.utils.MinecraftUtil; import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.utils.Dimension; @@ -25,14 +25,13 @@ import java.util.Optional; -public class BindConsumerScreen extends Screen implements ScreenProcessorProvider { +public class BindConsumerScreen extends Screen implements ScreenBehaviourFactory { private final ControllerImpl controller; private EventSource.Subscription inputSubscription; private LogicalInput capturedInput; private final Option> option; private final Screen backgroundScreen; private final BindController.BindControllerElement widgetToFocus; - private final ScreenProcessorImpl screenProcessor = new ScreenProcessorImpl(this); private int ticksTillClose; private int ticksTillInput; @@ -53,7 +52,7 @@ protected void init() { } inputSubscription = controller.input().inputPipeline().sourceLogicalSignals().subscribe(signal -> { if (ticksTillInput == 0 && ticksTillClose == 0 && capturedInput == null - && signal instanceof LogicalSignal.Tapped tap) { + && signal instanceof LogicalSignal.Tapped tap) { capturedInput = tap.input(); } }); @@ -188,18 +187,19 @@ private void returnToBackground() { } @Override - public ScreenProcessor screenProcessor() { - return screenProcessor; - } - - private static class ScreenProcessorImpl extends ScreenProcessor { - public ScreenProcessorImpl(BindConsumerScreen screen) { - super(screen); - } + public ScreenBehaviour create(@NonNull ScreenContext context) { + context.virtualMouse().setMode(VirtualMouseMode.DISABLED); + return new ScreenBehaviour() { + @Override + public void handleNavigation(@NonNull Controller controller, @NonNull Runnable next) { + // Physical input is captured by the screen's dedicated subscription. + } - @Override - public void onControllerFixedUpdate(ControllerImpl controller) { - // prevent all other controller input logic - } + @Override + public void handleInteraction(@NonNull Controller controller, @NonNull Runnable next) { + // Do not also activate controls while capturing physical input. + } + }; } + } diff --git a/src/main/java/dev/isxander/controlify/impl/gui/screen/ControlifySettingsScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/ControlifySettingsScreen.java index adabf74c3..b428ad2e0 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/screen/ControlifySettingsScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/ControlifySettingsScreen.java @@ -7,8 +7,9 @@ package dev.isxander.controlify.impl.gui.screen; import com.google.common.collect.ImmutableList; -import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.controller.Controller; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.api.screenop.*; import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.config.settings.device.DeviceSettings; @@ -16,7 +17,6 @@ import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.controllermanager.ControllerType; import dev.isxander.controlify.impl.gui.components.PlainTextWidget; -import dev.isxander.controlify.impl.screenop.ScreenControllerEventListener; import dev.isxander.controlify.impl.utils.CUtil; import dev.isxander.controlify.impl.utils.MinecraftUtil; import dev.isxander.controlify.mixins.feature.ui.AbstractSelectionListAccessor; @@ -43,7 +43,7 @@ import java.util.List; import java.util.Optional; -public class ControlifySettingsScreen extends Screen implements ScreenControllerEventListener { +public class ControlifySettingsScreen extends Screen implements ScreenBehaviourFactory { private final @Nullable Screen parent; private Button globalSettingsButton, doneButton, controllerNotDetectedButton; @@ -82,7 +82,7 @@ protected void init() { Button.builder( Component.translatable("controlify.gui.carousel.controller_not_detected_btn"), 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"))) .build() @@ -126,28 +126,28 @@ protected void init() { int activeProfileIndex = Controlify.instance().config().getActiveProfileIndex(); if (Controlify.instance().config().getSettings().globalSettings().showSplitscreenAd && false) { ProfileSlotEntry profileSlotEntry = new ProfileSlotEntry( - activeProfileIndex, - mainPaneX + slotPaddingX, mainPaneY + slotPaddingY, - profileSlotWidth / 2 - (slotPaddingX / 2), profileSlotHeight + activeProfileIndex, + mainPaneX + slotPaddingX, mainPaneY + slotPaddingY, + profileSlotWidth / 2 - (slotPaddingX / 2), profileSlotHeight ); this.addRenderableWidget(profileSlotEntry); SplitscreenAdvertisementSlotEntry splitscreenAdEntry = new SplitscreenAdvertisementSlotEntry( - mainPaneX + slotPaddingX + profileSlotWidth / 2 + (slotPaddingX / 2), mainPaneY + slotPaddingY, - profileSlotWidth / 2 - (slotPaddingX / 2), profileSlotHeight + mainPaneX + slotPaddingX + profileSlotWidth / 2 + (slotPaddingX / 2), mainPaneY + slotPaddingY, + profileSlotWidth / 2 - (slotPaddingX / 2), profileSlotHeight ); this.addRenderableWidget(splitscreenAdEntry); } else { ProfileSlotEntry profileSlotEntry = new ProfileSlotEntry( - activeProfileIndex, - mainPaneX + slotPaddingX, mainPaneY + slotPaddingY, - profileSlotWidth, profileSlotHeight + activeProfileIndex, + mainPaneX + slotPaddingX, mainPaneY + slotPaddingY, + profileSlotWidth, profileSlotHeight ); this.addRenderableWidget(profileSlotEntry); } } - ButtonGuideApi.addGuideToButton(globalSettingsButton, ControlifyActions.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always()); - ButtonGuideApi.addGuideToButton(doneButton, ControlifyActions.GUI_BACK, ButtonGuidePredicate.always()); + dev.isxander.controlify.impl.screenop.ScreenContextImpl.get(this).buttonGuide().addGuideToButton(globalSettingsButton, ControlifyActions.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always()); + dev.isxander.controlify.impl.screenop.ScreenContextImpl.get(this).buttonGuide().addGuideToButton(doneButton, ControlifyActions.GUI_BACK, ButtonGuidePredicate.always()); } @Override @@ -187,11 +187,9 @@ public void extractBackground(@NonNull GuiGraphicsExtractor graphics, int mouseX ); } - @Override - public void onControllerInput(ControllerImpl controller) { - if (ControlifyActions.GUI_ABSTRACT_ACTION_1.on(controller).pulse().consumePulse()) { - // Button impl does not use NonNull parameter - globalSettingsButton.onPress(null); + private void onControllerInput(ControllerImpl controller) { + if (ControlifyActions.GUI_ABSTRACT_ACTION_1.on(controller).pulse().consumeAllPulses()) { + globalSettingsButton.onPress(new net.minecraft.client.input.KeyEvent(com.mojang.blaze3d.platform.InputConstants.KEY_RETURN, 0, 0)); } } @@ -354,10 +352,10 @@ private void updateProfileInfo(boolean force) { } if (!force - && profileName.equals(displayedProfileName) - && controllerName.equals(displayedControllerName) - && controllerIcon.equals(displayedControllerIcon) - && controllerConnected == displayedControllerConnected) { + && profileName.equals(displayedProfileName) + && controllerName.equals(displayedControllerName) + && controllerIcon.equals(displayedControllerIcon) + && controllerConnected == displayedControllerConnected) { return; } @@ -478,4 +476,16 @@ public void extractRenderState(@NonNull GuiGraphicsExtractor graphics, int mouse public void updateNarration(@NonNull NarrationElementOutput narrationElementOutput) { } } + + @Override + public ScreenBehaviour create(@NonNull ScreenContext context) { + return new ScreenBehaviour() { + @Override + public void handleInteraction(@NonNull Controller controller, @NonNull Runnable next) { + onControllerInput((ControllerImpl) controller); + next.run(); + } + }; + } + } diff --git a/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerMappingMakerScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerMappingMakerScreen.java index 96cf51252..d1833012e 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerMappingMakerScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/ControllerMappingMakerScreen.java @@ -6,6 +6,8 @@ */ package dev.isxander.controlify.impl.gui.screen; +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.screenop.*; import dev.isxander.controlify.api.utils.event.EventSource; import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.config.settings.device.DeviceSettings; @@ -17,8 +19,6 @@ import dev.isxander.controlify.impl.input.mapping.MappingEntry; import dev.isxander.controlify.impl.input.signal.PhysicalInputSignal; import dev.isxander.controlify.impl.input.signal.PhysicalSignal; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; -import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; import dev.isxander.controlify.impl.utils.CUtil; import dev.isxander.controlify.impl.utils.ColorUtils; import dev.isxander.controlify.impl.utils.MinecraftUtil; @@ -38,7 +38,7 @@ import java.util.Map; import java.util.Set; -public class ControllerMappingMakerScreen extends Screen implements ScreenProcessorProvider, DontInterruptScreen { +public class ControllerMappingMakerScreen extends Screen implements ScreenBehaviourFactory, DontInterruptScreen { private static final float AXIS_PRESS_THRESHOLD = 0.5f; private static final float AXIS_RELEASE_THRESHOLD = 0.25f; @@ -47,7 +47,6 @@ public class ControllerMappingMakerScreen extends Screen implements ScreenProces private final Set activeInputs = new HashSet<>(); private EventSource.Subscription inputSubscription; private final ControllerMapping.Builder mappingBuilder = new ControllerMapping.Builder(); - private final ScreenProcessor screenProcessor = new ScreenProcessorImpl(this); private int delayTillNextStage = 20; @@ -100,18 +99,18 @@ public static ControllerMappingMakerScreen createGamepadMapping(ControllerImpl c protected void init() { addRenderableWidget( this.goBackButton = Button.builder( - Component.translatable("controlify.gui.mapping_maker.go_back"), - button -> goBackStage() - ) + Component.translatable("controlify.gui.mapping_maker.go_back"), + button -> goBackStage() + ) .bounds(width / 2 - 152, height - 60, 150, 20) .tooltip(Tooltip.create(Component.translatable("controlify.gui.mapping_maker.go_back.tooltip"))) .build() ); addRenderableWidget( Button.builder( - Component.translatable("controlify.gui.mapping_maker.no_map"), - button -> mapAsNone() - ) + Component.translatable("controlify.gui.mapping_maker.no_map"), + button -> mapAsNone() + ) .bounds(width / 2 + 2, height - 60, 150, 20) .tooltip(Tooltip.create(Component.translatable("controlify.gui.mapping_maker.no_map.tooltip"))) .build() @@ -183,7 +182,7 @@ private void onPhysicalSignal(PhysicalSignal signal) { return; } if (!this.activeInputs.add(input) || this.delayTillNextStage >= 0 - || this.currentStage == null || this.currentStage.isSatisfied()) { + || this.currentStage == null || this.currentStage.isSatisfied()) { return; } // MappingStage requires each physical source to have a single destination. @@ -316,8 +315,19 @@ private static Component axis(String axisName, String direction) { } @Override - public ScreenProcessor screenProcessor() { - return screenProcessor; + public ScreenBehaviour create(@NonNull ScreenContext context) { + context.virtualMouse().setMode(VirtualMouseMode.DISABLED); + return new ScreenBehaviour() { + @Override + public void handleNavigation(@NonNull Controller controller, @NonNull Runnable next) { + // Physical input is captured by the screen's dedicated subscription. + } + + @Override + public void handleInteraction(@NonNull Controller controller, @NonNull Runnable next) { + // Do not also activate controls while capturing physical input. + } + }; } public static class MappingStage { @@ -374,14 +384,4 @@ public void setSatisfied(boolean satisfied) { } } - private static class ScreenProcessorImpl extends ScreenProcessor { - public ScreenProcessorImpl(ControllerMappingMakerScreen screen) { - super(screen); - } - - @Override - public void onControllerFixedUpdate(ControllerImpl controller) { - // Captured physical inputs must not also activate screen controls. - } - } } diff --git a/src/main/java/dev/isxander/controlify/impl/gui/screen/ProfileSelectionScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/ProfileSelectionScreen.java index 3e40c66c0..2322fdd4e 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/screen/ProfileSelectionScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/ProfileSelectionScreen.java @@ -6,15 +6,15 @@ */ package dev.isxander.controlify.impl.gui.screen; -import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.controller.Controller; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.api.screenop.*; import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.config.settings.device.DeviceSettings; import dev.isxander.controlify.impl.config.settings.profile.ProfileSettings; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.controllermanager.ControllerType; -import dev.isxander.controlify.impl.screenop.ScreenControllerEventListener; import dev.isxander.controlify.impl.utils.CUtil; import dev.isxander.controlify.impl.utils.MinecraftUtil; import net.minecraft.ChatFormatting; @@ -43,7 +43,7 @@ import java.util.ArrayList; import java.util.List; -public class ProfileSelectionScreen extends Screen implements ScreenControllerEventListener { +public class ProfileSelectionScreen extends Screen implements ScreenBehaviourFactory { private static final int CARD_WIDTH = 128; private static final int CARD_HEIGHT = 104; private static final int CARD_SPACING = 6; @@ -100,7 +100,7 @@ protected void init() { scrollable.setScrollbarSpacing(0); //?} else { /*ScrollableLayout scrollable = new ScrollableLayout(this.minecraft, scrollContent, this.layout.getContentHeight()); - *///?} + *///?} scrollable.setMinWidth(columns * CARD_WIDTH + Math.max(0, columns - 1) * CARD_SPACING); scrollable.setMaxHeight(this.layout.getContentHeight()); scrollable.setMinHeight(this.layout.getContentHeight()); @@ -146,10 +146,10 @@ protected void init() { ); this.layout.addToFooter(footer); - ButtonGuideApi.addGuideToButton(this.rememberButton, ControlifyActions.GUI_PRESS, ButtonGuidePredicate.always()); - ButtonGuideApi.addGuideToButton(this.onceButton, ControlifyActions.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always()); - ButtonGuideApi.addGuideToButton(this.deleteButton, ControlifyActions.GUI_ABSTRACT_ACTION_2, ButtonGuidePredicate.always()); - ButtonGuideApi.addGuideToButton(backButton, ControlifyActions.GUI_BACK, ButtonGuidePredicate.always()); + dev.isxander.controlify.impl.screenop.ScreenContextImpl.get(this).buttonGuide().addGuideToButton(this.rememberButton, ControlifyActions.GUI_PRESS, ButtonGuidePredicate.always()); + dev.isxander.controlify.impl.screenop.ScreenContextImpl.get(this).buttonGuide().addGuideToButton(this.onceButton, ControlifyActions.GUI_ABSTRACT_ACTION_1, ButtonGuidePredicate.always()); + dev.isxander.controlify.impl.screenop.ScreenContextImpl.get(this).buttonGuide().addGuideToButton(this.deleteButton, ControlifyActions.GUI_ABSTRACT_ACTION_2, ButtonGuidePredicate.always()); + dev.isxander.controlify.impl.screenop.ScreenContextImpl.get(this).buttonGuide().addGuideToButton(backButton, ControlifyActions.GUI_BACK, ButtonGuidePredicate.always()); this.layout.visitWidgets(this::addRenderableWidget); this.repositionElements(); @@ -199,13 +199,12 @@ public void extractRenderState(@NonNull GuiGraphicsExtractor graphics, int mouse ); } - @Override - public void onControllerInput(ControllerImpl controller) { + private void onControllerInput(ControllerImpl controller) { if (this.selectedProfileIndex >= 0 - && ControlifyActions.GUI_ABSTRACT_ACTION_1.on(controller).pulse().consumePulse()) { + && ControlifyActions.GUI_ABSTRACT_ACTION_1.on(controller).pulse().consumeAllPulses()) { this.switchProfile(this.selectedProfileIndex, false); } else if (this.deleteButton.active - && ControlifyActions.GUI_ABSTRACT_ACTION_2.on(controller).pulse().consumePulse()) { + && ControlifyActions.GUI_ABSTRACT_ACTION_2.on(controller).pulse().consumeAllPulses()) { this.confirmDeleteProfile(); } } @@ -239,7 +238,7 @@ private void updateActionButtons() { } if (this.deleteButton != null) { this.deleteButton.active = hasProfile - && this.selectedProfileIndex != Controlify.instance().config().getActiveProfileIndex(); + && this.selectedProfileIndex != Controlify.instance().config().getActiveProfileIndex(); } } @@ -270,7 +269,7 @@ private void createProfile() { private void confirmDeleteProfile() { if (this.selectedProfileIndex < 0 - || this.selectedProfileIndex == Controlify.instance().config().getActiveProfileIndex()) { + || this.selectedProfileIndex == Controlify.instance().config().getActiveProfileIndex()) { return; } @@ -487,4 +486,16 @@ protected void updateWidgetNarration(@NonNull NarrationElementOutput output) { private record ControllerDisplay(Component name, Identifier icon) { } + + @Override + public ScreenBehaviour create(@NonNull ScreenContext context) { + return new ScreenBehaviour() { + @Override + public void handleInteraction(@NonNull Controller controller, @NonNull Runnable next) { + onControllerInput((ControllerImpl) controller); + next.run(); + } + }; + } + } diff --git a/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuEditScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuEditScreen.java index a9b9713d0..26f516e0e 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuEditScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuEditScreen.java @@ -8,18 +8,17 @@ import com.mojang.blaze3d.platform.InputConstants; import dev.isxander.controlify.api.action.Action; -import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.controller.Controller; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.api.screenop.*; +import dev.isxander.controlify.api.screenop.VirtualMouseMode; import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.action.radial.RadialIcon; import dev.isxander.controlify.impl.action.radial.RadialIconExtractor; import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.controller.haptic.HapticEffects; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; -import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; import dev.isxander.controlify.impl.utils.MinecraftUtil; -import dev.isxander.controlify.impl.virtualmouse.VirtualMouseBehaviour; import net.minecraft.ChatFormatting; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.AbstractButton; @@ -51,7 +50,7 @@ import java.util.function.Function; import java.util.stream.Collectors; -public class RadialMenuEditScreen extends Screen implements ScreenProcessorProvider { +public class RadialMenuEditScreen extends Screen implements ScreenBehaviourFactory { private static final int COLUMN_GAP = 8; private static final int COLUMN_PADDING = 4; private static final int COLUMN_HEADER_HEIGHT = 24; @@ -65,7 +64,6 @@ public class RadialMenuEditScreen extends Screen implements ScreenProcessorProvi private final RadialMenuEditModel model; private final List candidates; private final Map candidatesById; - private final Processor processor = new Processor(this); private HeaderAndFooterLayout layout; private ContainerEventHandler availableContainer; @@ -116,7 +114,7 @@ protected void init() { Button done = footer.addChild(Button.builder(CommonComponents.GUI_DONE, _ -> commitAndClose()) .width(FOOTER_BUTTON_WIDTH) .build()); - ButtonGuideApi.addGuideToButton(done, ControlifyActions.GUI_BACK, ButtonGuidePredicate.always()); + dev.isxander.controlify.impl.screenop.ScreenContextImpl.get(this).buttonGuide().addGuideToButton(done, ControlifyActions.GUI_BACK, ButtonGuidePredicate.always()); layout.addToFooter(footer); layout.visitWidgets(this::addRenderableWidget); @@ -246,7 +244,7 @@ private ScrollableLayout createScrollable(Layout content, int width) { scrollable.setScrollbarSpacing(0); //?} else { /*ScrollableLayout scrollable = new ScrollableLayout(minecraft, content, height); - *///?} + *///?} scrollable.setMinWidth(width); scrollable.setMaxHeight(height); scrollable.setMinHeight(height); @@ -430,8 +428,9 @@ private void commitAndClose() { } @Override - public ScreenProcessor screenProcessor() { - return processor; + public ScreenBehaviour create(@NonNull ScreenContext context) { + context.virtualMouse().setMode(VirtualMouseMode.DISABLED); + return new Behaviour(context); } private Component trim(Component text, int maxWidth) { @@ -648,50 +647,55 @@ protected void updateWidgetNarration(@NonNull NarrationElementOutput output) { } } - public static class Processor extends ScreenProcessor { - public Processor(RadialMenuEditScreen screen) { - super(screen); + private static final class Behaviour implements ScreenBehaviour { + private final RadialMenuEditScreen screen; + private final ScreenContext context; + + private Behaviour(ScreenContext context) { + this.context = context; + this.screen = context.screen(); } @Override - protected void handleComponentNavigation(ControllerImpl controller) { + public void handleNavigation(@NonNull Controller input, @NonNull Runnable next) { + var controller = (ControllerImpl) input; if (!screen.model.isCarrying()) { - super.handleComponentNavigation(controller); + next.run(); return; } boolean moved = false; - if (ControlifyActions.GUI_NAVI_LEFT.on(controller).pulse().consumePulse()) { - moved = screen.moveCarried(ScreenDirection.LEFT); - } else if (ControlifyActions.GUI_NAVI_RIGHT.on(controller).pulse().consumePulse()) { - moved = screen.moveCarried(ScreenDirection.RIGHT); - } else if (ControlifyActions.GUI_NAVI_UP.on(controller).pulse().consumePulse()) { - moved = screen.moveCarried(ScreenDirection.UP); - } else if (ControlifyActions.GUI_NAVI_DOWN.on(controller).pulse().consumePulse()) { - moved = screen.moveCarried(ScreenDirection.DOWN); + while (ControlifyActions.GUI_NAVI_LEFT.on(controller).pulse().consumePulse()) { + moved |= screen.moveCarried(ScreenDirection.LEFT); + } + while (ControlifyActions.GUI_NAVI_RIGHT.on(controller).pulse().consumePulse()) { + moved |= screen.moveCarried(ScreenDirection.RIGHT); + } + while (ControlifyActions.GUI_NAVI_UP.on(controller).pulse().consumePulse()) { + moved |= screen.moveCarried(ScreenDirection.UP); + } + while (ControlifyActions.GUI_NAVI_DOWN.on(controller).pulse().consumePulse()) { + moved |= screen.moveCarried(ScreenDirection.DOWN); } if (moved) { - playFocusChangeSound(); + context.playFocusChangeSound(); controller.hdHaptics().ifPresent(haptics -> haptics.playHaptic(HapticEffects.NAVIGATE)); } } @Override - protected void handleButtons(ControllerImpl controller) { + public void handleInteraction(@NonNull Controller input, @NonNull Runnable next) { + var controller = (ControllerImpl) input; if (screen.model.isCarrying()) { - if (ControlifyActions.GUI_PRESS.on(controller).pulse().consumePulse()) { + if (ControlifyActions.GUI_PRESS.on(controller).pulse().consumeAllPulses()) { screen.dropCarried(); - playClackSound(); + context.playClickSound(); return; } } - super.handleButtons(controller); + next.run(); } - @Override - public VirtualMouseBehaviour virtualMouseBehaviour() { - return VirtualMouseBehaviour.DISABLED; - } } } diff --git a/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuScreen.java b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuScreen.java index 3d52618ed..60af48744 100644 --- a/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuScreen.java +++ b/src/main/java/dev/isxander/controlify/impl/gui/screen/RadialMenuScreen.java @@ -6,6 +6,8 @@ */ package dev.isxander.controlify.impl.gui.screen; +import dev.isxander.controlify.api.screenop.*; +import dev.isxander.controlify.api.controller.Controller; import dev.isxander.controlify.api.action.ActionAccessor; import dev.isxander.controlify.api.font.ActionGlyphs; import dev.isxander.controlify.impl.action.ControlifyActions; @@ -14,15 +16,12 @@ import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.controller.haptic.HapticEffects; import dev.isxander.controlify.impl.gui.screen.RadialItems.RadialPage; -import dev.isxander.controlify.impl.screenop.ScreenControllerEventListener; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; -import dev.isxander.controlify.impl.screenop.ScreenProcessorProvider; import dev.isxander.controlify.impl.sound.ControlifyClientSounds; import dev.isxander.controlify.impl.utils.CUtil; import dev.isxander.controlify.impl.utils.MinecraftUtil; import dev.isxander.controlify.impl.utils.animation.api.Animation; import dev.isxander.controlify.impl.utils.animation.api.EasingFunction; -import dev.isxander.controlify.impl.virtualmouse.VirtualMouseBehaviour; +import dev.isxander.controlify.api.screenop.VirtualMouseMode; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.TextAlignment; import net.minecraft.client.gui.components.MultiLineLabel; @@ -46,7 +45,7 @@ import java.util.Collections; import java.util.List; -public class RadialMenuScreen extends Screen implements ScreenControllerEventListener, ScreenProcessorProvider { +public class RadialMenuScreen extends Screen implements ScreenBehaviourFactory { private final ControllerImpl controller; private final @Nullable Screen parent; private final Component footerText; @@ -61,8 +60,6 @@ public class RadialMenuScreen extends Screen implements ScreenControllerEventLis private int idleTicks; private final int idleTicksTimeout; - private final Processor processor = new Processor(this); - /** * Creates a single-page radial menu. Existing special-purpose radial menus use this path. */ @@ -119,7 +116,7 @@ private void buildCurrentPage() { int centerX = this.width / 2; int centerY = this.height / 2; - float buttonDiameter = (float)Math.sqrt(32 * 32 + 32 * 32) + 8; + float buttonDiameter = (float) Math.sqrt(32 * 32 + 32 * 32) + 8; float circumference = buttonDiameter * items.length; radialRadius = Math.max(circumference / Mth.TWO_PI, 43); @@ -149,14 +146,13 @@ private void changePage(int direction) { controller.hdHaptics().ifPresent(haptics -> haptics.playHaptic(HapticEffects.NAVIGATE)); } - @Override - public void onControllerInput(ControllerImpl controller) { + private void onControllerInput(ControllerImpl controller) { if (this.controller != controller) { return; } if (openAction == null) { - if (ControlifyActions.GUI_BACK.on(controller).pulse().consumePulse()) { + if (ControlifyActions.GUI_BACK.on(controller).pulse().consumeAllPulses()) { onClose(); } return; @@ -177,13 +173,13 @@ public void onControllerInput(ControllerImpl controller) { } float x = ControlifyActions.RADIAL_AXIS_RIGHT.on(controller).continuous().getContinuous() - - ControlifyActions.RADIAL_AXIS_LEFT.on(controller).continuous().getContinuous(); + - ControlifyActions.RADIAL_AXIS_LEFT.on(controller).continuous().getContinuous(); float y = ControlifyActions.RADIAL_AXIS_DOWN.on(controller).continuous().getContinuous() - - ControlifyActions.RADIAL_AXIS_UP.on(controller).continuous().getContinuous(); + - ControlifyActions.RADIAL_AXIS_UP.on(controller).continuous().getContinuous(); float threshold = controller.input().settings().buttonActivationThreshold; if (Math.abs(x) >= threshold || Math.abs(y) >= threshold) { - float angle = Mth.wrapDegrees(Mth.RAD_TO_DEG * (float)Mth.atan2(y, x) - 90f) + 180f; + float angle = Mth.wrapDegrees(Mth.RAD_TO_DEG * (float) Mth.atan2(y, x) - 90f) + 180f; float each = 360f / buttons.length; int newSelected = Mth.floor((angle + each / 2f) / each) % buttons.length; @@ -281,8 +277,9 @@ public boolean isPauseScreen() { } @Override - public ScreenProcessor screenProcessor() { - return processor; + public ScreenBehaviour create(@NonNull ScreenContext context) { + context.virtualMouse().setMode(VirtualMouseMode.DISABLED); + return new Behaviour(context); } private void playClickSound() { @@ -310,7 +307,7 @@ private RadialButton(RadialItem item, float x, float y) { setX(x); setY(y); this.item = item; - this.name = MultiLineLabel.create(font, item.name(), (int)(radialRadius * 2 - 32)); + this.name = MultiLineLabel.create(font, item.name(), (int) (radialRadius * 2 - 32)); } @Override @@ -340,12 +337,12 @@ private boolean invoke() { } private void setX(float x) { - this.x = (int)x; + this.x = (int) x; this.translateX = x - this.x; } private void setY(float y) { - this.y = (int)y; + this.y = (int) y; this.translateY = y - this.y; } @@ -375,23 +372,30 @@ public void updateNarration(NarrationElementOutput builder) { } } - public static class Processor extends ScreenProcessor { - public Processor(RadialMenuScreen screen) { - super(screen); + private static final class Behaviour implements ScreenBehaviour { + private final RadialMenuScreen screen; + private final ScreenContext context; + + private Behaviour(ScreenContext context) { + this.context = context; + this.screen = context.screen(); } @Override - protected void handleTabNavigation(ControllerImpl controller) { - if (ControlifyActions.GUI_NEXT_TAB.on(controller).pulse().consumePulse()) { + public void handleNavigation(@NonNull Controller input, @NonNull Runnable next) { + var controller = (ControllerImpl) input; + screen.onControllerInput(controller); + if (MinecraftUtil.getScreen() != screen) { + return; + } + while (ControlifyActions.GUI_NEXT_TAB.on(controller).pulse().consumePulse()) { screen.changePage(1); - } else if (ControlifyActions.GUI_PREV_TAB.on(controller).pulse().consumePulse()) { + } + while (ControlifyActions.GUI_PREV_TAB.on(controller).pulse().consumePulse()) { screen.changePage(-1); } + next.run(); } - @Override - public VirtualMouseBehaviour virtualMouseBehaviour() { - return VirtualMouseBehaviour.DISABLED; - } } } diff --git a/src/main/java/dev/isxander/controlify/impl/ingame/ControllerPlayerMovement.java b/src/main/java/dev/isxander/controlify/impl/ingame/ControllerPlayerMovement.java index 26fa61961..86f063bc5 100644 --- a/src/main/java/dev/isxander/controlify/impl/ingame/ControllerPlayerMovement.java +++ b/src/main/java/dev/isxander/controlify/impl/ingame/ControllerPlayerMovement.java @@ -63,18 +63,18 @@ public void tick() { // this over-complication is so exiting a GUI with the button still held doesn't trigger a jump. Action jump = ControlifyActions.JUMP.on(controller); - jumping = jump.pulse().consumePulse() || jump.latch().isLatchActive(); + jumping = jump.pulse().consumeAllPulses() || jump.latch().isLatchActive(); Action sneak = ControlifyActions.SNEAK.on(controller); if (player.getAbilities().flying || (player.isInWater() && !player.onGround()) || player.getVehicle() != null || !controller.settings().generic.toggleSneak) { - if (sneak.pulse().consumePulse()) { + if (sneak.pulse().consumeAllPulses()) { shiftKeyDown = true; } if (!sneak.latch().isLatchActive()) { shiftKeyDown = false; } } else { - if (sneak.pulse().consumePulse()) { + while (sneak.pulse().consumePulse()) { shiftKeyDown = !shiftKeyDown; } } diff --git a/src/main/java/dev/isxander/controlify/impl/ingame/InGameInputHandler.java b/src/main/java/dev/isxander/controlify/impl/ingame/InGameInputHandler.java index ff2d4f775..9b6e77ff0 100644 --- a/src/main/java/dev/isxander/controlify/impl/ingame/InGameInputHandler.java +++ b/src/main/java/dev/isxander/controlify/impl/ingame/InGameInputHandler.java @@ -95,16 +95,16 @@ protected void handleKeybinds() { return; } - if (ControlifyActions.PAUSE.on(controller).pulse().consumePulse()) { + if (ControlifyActions.PAUSE.on(controller).pulse().consumeAllPulses()) { minecraft.pauseGame(false); } if (minecraft.player != null) { Inventory inventory = minecraft.player.getInventory(); - if (ControlifyActions.NEXT_SLOT.on(controller).pulse().consumePulse()) { + while (ControlifyActions.NEXT_SLOT.on(controller).pulse().consumePulse()) { inventory.setSelectedSlot((inventory.getSelectedSlot() + 1) % Inventory.getSelectionSize()); } - if (ControlifyActions.PREV_SLOT.on(controller).pulse().consumePulse()) { + while (ControlifyActions.PREV_SLOT.on(controller).pulse().consumePulse()) { inventory.setSelectedSlot((inventory.getSelectedSlot() - 1 + Inventory.getSelectionSize()) % Inventory.getSelectionSize()); } @@ -120,24 +120,27 @@ protected void handleKeybinds() { ControlifyActions.HOTBAR_SLOT_9 ); for (int i = 0; i < hotbarSlots.size(); i++) { - if (hotbarSlots.get(i).on(controller).pulse().consumePulse()) { + if (hotbarSlots.get(i).on(controller).pulse().consumeAllPulses()) { inventory.setSelectedSlot(i); } } if (!minecraft.player.isSpectator()) { - if (ControlifyActions.DROP_STACK.on(controller).pulse().consumePulse()) { + if (ControlifyActions.DROP_STACK.on(controller).pulse().consumeAllPulses()) { dropItem(minecraft.player, true); - } else if (ControlifyActions.DROP_INGAME.on(controller).pulse().consumePulse()) { - dropItem(minecraft.player, false); + ControlifyActions.DROP_INGAME.on(controller).pulse().consumeAllPulses(); + } else { + while (ControlifyActions.DROP_INGAME.on(controller).pulse().consumePulse()) { + dropItem(minecraft.player, false); + } } - if (ControlifyActions.SWAP_HANDS.on(controller).pulse().consumePulse()) { + while (ControlifyActions.SWAP_HANDS.on(controller).pulse().consumePulse()) { minecraft.player.connection.send(new ServerboundPlayerActionPacket(ServerboundPlayerActionPacket.Action.SWAP_ITEM_WITH_OFFHAND, BlockPos.ZERO, Direction.DOWN)); } } - if (ControlifyActions.INVENTORY.on(controller).pulse().consumePulse()) { + if (ControlifyActions.INVENTORY.on(controller).pulse().consumeAllPulses()) { if (minecraft.gameMode.isServerControlledInventory()) { minecraft.player.sendOpenInventory(); } else { @@ -146,7 +149,7 @@ protected void handleKeybinds() { } } - if (ControlifyActions.CHANGE_PERSPECTIVE.on(controller).pulse().consumePulse()) { + while (ControlifyActions.CHANGE_PERSPECTIVE.on(controller).pulse().consumePulse()) { CameraType cameraType = minecraft.options.getCameraType(); minecraft.options.setCameraType(minecraft.options.getCameraType().cycle()); if (cameraType.isFirstPerson() != minecraft.options.getCameraType().isFirstPerson()) { @@ -157,7 +160,7 @@ protected void handleKeybinds() { //minecraft.levelRenderer.needsUpdate(); } } - if (ControlifyActions.TOGGLE_HUD_VISIBILITY.on(controller).pulse().consumePulse()) { + while (ControlifyActions.TOGGLE_HUD_VISIBILITY.on(controller).pulse().consumePulse()) { //? if >=26.2 { minecraft.gui.hud.toggle(); //?} else { @@ -165,44 +168,44 @@ protected void handleKeybinds() { *///?} } - if (ControlifyActions.SHOW_PLAYER_LIST.on(controller).pulse().consumePulse()) { + while (ControlifyActions.SHOW_PLAYER_LIST.on(controller).pulse().consumePulse()) { shouldShowPlayerList = !shouldShowPlayerList; } - if (ControlifyActions.TOGGLE_DEBUG_MENU.on(controller).pulse().consumePulse()) { + while (ControlifyActions.TOGGLE_DEBUG_MENU.on(controller).pulse().consumePulse()) { DebugOverlayHelper.toggleOverlay(); } - if (ControlifyActions.TOGGLE_DEBUG_MENU_FPS.on(controller).pulse().consumePulse()) { + while (ControlifyActions.TOGGLE_DEBUG_MENU_FPS.on(controller).pulse().consumePulse()) { DebugOverlayHelper.toggleFpsOverlay(); } - if (ControlifyActions.TOGGLE_DEBUG_MENU_NET.on(controller).pulse().consumePulse()) { + while (ControlifyActions.TOGGLE_DEBUG_MENU_NET.on(controller).pulse().consumePulse()) { DebugOverlayHelper.toggleNetworkOverlay(); } - if (ControlifyActions.TOGGLE_DEBUG_MENU_PROF.on(controller).pulse().consumePulse()) { + while (ControlifyActions.TOGGLE_DEBUG_MENU_PROF.on(controller).pulse().consumePulse()) { DebugOverlayHelper.toggleProfilerOverlay(); } - if (ControlifyActions.DEBUG_RELOAD_CHUNKS.on(controller).pulse().consumePulse()) { + if (ControlifyActions.DEBUG_RELOAD_CHUNKS.on(controller).pulse().consumeAllPulses()) { DebugOverlayHelper.reloadChunks(); } - if (ControlifyActions.DEBUG_TOGGLE_CHUNK_BORDERS.on(controller).pulse().consumePulse()) { + while (ControlifyActions.DEBUG_TOGGLE_CHUNK_BORDERS.on(controller).pulse().consumePulse()) { DebugOverlayHelper.toggleChunkBorders(); } - if (ControlifyActions.DEBUG_TOGGLE_ADVANCED_TOOLTIPS.on(controller).pulse().consumePulse()) { + while (ControlifyActions.DEBUG_TOGGLE_ADVANCED_TOOLTIPS.on(controller).pulse().consumePulse()) { DebugOverlayHelper.toggleAdvancedTooltips(); } - if (ControlifyActions.DEBUG_TOGGLE_ENTITY_HITBOXES.on(controller).pulse().consumePulse()) { + while (ControlifyActions.DEBUG_TOGGLE_ENTITY_HITBOXES.on(controller).pulse().consumePulse()) { DebugOverlayHelper.toggleEntityHitboxes(); } - if (ControlifyActions.DEBUG_RELOAD_RESOURCE_PACKS.on(controller).pulse().consumePulse()) { + if (ControlifyActions.DEBUG_RELOAD_RESOURCE_PACKS.on(controller).pulse().consumeAllPulses()) { DebugOverlayHelper.reloadResourcePacks(); } - if (ControlifyActions.DEBUG_CLEAR_CHAT.on(controller).pulse().consumePulse()) { + if (ControlifyActions.DEBUG_CLEAR_CHAT.on(controller).pulse().consumeAllPulses()) { DebugOverlayHelper.clearChat(); } - if (ControlifyActions.DEBUG_START_STOP_PROFILING.on(controller).pulse().consumePulse()) { + while (ControlifyActions.DEBUG_START_STOP_PROFILING.on(controller).pulse().consumePulse()) { DebugOverlayHelper.startStopProfiling(); } - if (ControlifyActions.DEBUG_RADIAL.on(controller).pulse().consumePulse()) { + if (ControlifyActions.DEBUG_RADIAL.on(controller).pulse().consumeAllPulses()) { MinecraftUtil.setScreen(new RadialMenuScreen( controller, ControlifyActions.DEBUG_RADIAL, @@ -212,7 +215,7 @@ protected void handleKeybinds() { )); } - if (ControlifyActions.TAKE_SCREENSHOT.on(controller).pulse().consumePulse()) { + if (ControlifyActions.TAKE_SCREENSHOT.on(controller).pulse().consumeAllPulses()) { // get file before it takes and writes the screenshot (which changes the next name) File screenshotFile = ScreenshotAccessor.controlify$invokeGetFile( new File(minecraft.gameDirectory, "screenshots") @@ -241,14 +244,14 @@ protected void handleKeybinds() { ); } - if (ControlifyActions.PICK_BLOCK.on(controller).pulse().consumePulse()) { + if (ControlifyActions.PICK_BLOCK.on(controller).pulse().consumeAllPulses()) { ((PickBlockAccessor) minecraft).controlify$pickBlock(); } - if (ControlifyActions.PICK_BLOCK_NBT.on(controller).pulse().consumePulse()) { + if (ControlifyActions.PICK_BLOCK_NBT.on(controller).pulse().consumeAllPulses()) { ((PickBlockAccessor) minecraft).controlify$pickBlockWithNbt(); } - if (ControlifyActions.RADIAL_MENU.on(controller).pulse().consumePulse()) { + if (ControlifyActions.RADIAL_MENU.on(controller).pulse().consumeAllPulses()) { var bonusPages = new ArrayList(); if (canSwitchGameMode()) { bonusPages.add(RadialItems.createGameModes()); @@ -264,7 +267,7 @@ protected void handleKeybinds() { )); } - if (ControlifyActions.GAME_MODE_SWITCHER.on(controller).pulse().consumePulse() && canSwitchGameMode()) { + if (ControlifyActions.GAME_MODE_SWITCHER.on(controller).pulse().consumeAllPulses() && canSwitchGameMode()) { MinecraftUtil.setScreen(new RadialMenuScreen( controller, ControlifyActions.GAME_MODE_SWITCHER, @@ -274,7 +277,7 @@ protected void handleKeybinds() { ); } - if (ControlifyActions.HOTBAR_SLOT_SELECT.on(controller).pulse().consumePulse()) { + if (ControlifyActions.HOTBAR_SLOT_SELECT.on(controller).pulse().consumeAllPulses()) { MinecraftUtil.setScreen(new RadialMenuScreen( controller, ControlifyActions.HOTBAR_SLOT_SELECT, @@ -285,7 +288,7 @@ protected void handleKeybinds() { } if (minecraft.player != null && minecraft.player.hasInfiniteMaterials()) { - if (ControlifyActions.HOTBAR_LOAD_RADIAL.on(controller).pulse().consumePulse()) { + if (ControlifyActions.HOTBAR_LOAD_RADIAL.on(controller).pulse().consumeAllPulses()) { MinecraftUtil.setScreen(new RadialMenuScreen( controller, ControlifyActions.HOTBAR_LOAD_RADIAL, @@ -294,7 +297,7 @@ protected void handleKeybinds() { null )); } - if (ControlifyActions.HOTBAR_SAVE_RADIAL.on(controller).pulse().consumePulse()) { + if (ControlifyActions.HOTBAR_SAVE_RADIAL.on(controller).pulse().consumeAllPulses()) { MinecraftUtil.setScreen(new RadialMenuScreen( controller, ControlifyActions.HOTBAR_SAVE_RADIAL, @@ -406,9 +409,10 @@ protected void handleGyroLook(GyroComponentImpl gyro, Vector2d impulse, boolean gyroInput.set(0); } - boolean gyroPressed = gyroButton.pulse().consumePulse(); - if (settings.buttonMode.equals(GyroButtonMode.TOGGLE) && gyroPressed) { - gyroToggledOn = !gyroToggledOn; + while (gyroButton.pulse().consumePulse()) { + if (settings.buttonMode.equals(GyroButtonMode.TOGGLE)) { + gyroToggledOn = !gyroToggledOn; + } } // The latest gyro sample is already accumulated over this frame, in radians. @@ -432,10 +436,10 @@ protected void handleFlickStick(LocalPlayer player) { float flickAngle = Mth.wrapDegrees((float) Mth.atan2(y, x) * Mth.RAD_TO_DEG + 90f); - if (!ControlifyActions.LOOK_DOWN.on(controller).pulse().consumePulse() - & !ControlifyActions.LOOK_UP.on(controller).pulse().consumePulse() - & !ControlifyActions.LOOK_LEFT.on(controller).pulse().consumePulse() - & !ControlifyActions.LOOK_RIGHT.on(controller).pulse().consumePulse() + if (!ControlifyActions.LOOK_DOWN.on(controller).pulse().consumeAllPulses() + & !ControlifyActions.LOOK_UP.on(controller).pulse().consumeAllPulses() + & !ControlifyActions.LOOK_LEFT.on(controller).pulse().consumeAllPulses() + & !ControlifyActions.LOOK_RIGHT.on(controller).pulse().consumeAllPulses() ) { return; } diff --git a/src/main/java/dev/isxander/controlify/impl/input/pipeline/PhysicalToLogicalStage.java b/src/main/java/dev/isxander/controlify/impl/input/pipeline/PhysicalToLogicalStage.java index cc7b9009c..ebddf61e4 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/pipeline/PhysicalToLogicalStage.java +++ b/src/main/java/dev/isxander/controlify/impl/input/pipeline/PhysicalToLogicalStage.java @@ -89,7 +89,7 @@ public void onEvent(@NonNull PhysicalSignal event, @NonNull EventSink timeNanos - time > this.doubleTapMaxNanos); - downstream.accept(new LogicalSignal.Tick(timeNanos, deltaTimeNanos)); + downstream.accept(new LogicalSignal.Frame(timeNanos, deltaTimeNanos)); } case PhysicalSensorSignal sensorSignal -> this.sensorAccumulatorStage.onEvent(sensorSignal, downstream); diff --git a/src/main/java/dev/isxander/controlify/impl/input/signal/LogicalSignal.java b/src/main/java/dev/isxander/controlify/impl/input/signal/LogicalSignal.java index 192d8aa6c..8d3b12ab6 100644 --- a/src/main/java/dev/isxander/controlify/impl/input/signal/LogicalSignal.java +++ b/src/main/java/dev/isxander/controlify/impl/input/signal/LogicalSignal.java @@ -17,7 +17,7 @@ public sealed interface LogicalSignal { /// Submitted once per frame. /// Allows bindings to accumulate state between ticks. - record Tick(long timeNanos, long deltaTimeNanos) implements LogicalSignal {} + record Frame(long timeNanos, long deltaTimeNanos) implements LogicalSignal {} sealed interface InputSignal extends LogicalSignal { LogicalInput input(); diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/BehaviourChain.java b/src/main/java/dev/isxander/controlify/impl/screenop/BehaviourChain.java new file mode 100644 index 000000000..8bb043464 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/BehaviourChain.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.impl.screenop; + +import java.util.List; +import java.util.function.BiConsumer; + +final class BehaviourChain { + private BehaviourChain() { + } + + static void run(List behaviours, BiConsumer invoke, Runnable terminal) { + run(behaviours, 0, invoke, terminal); + } + + private static void run(List behaviours, int index, BiConsumer invoke, Runnable terminal) { + if (index == behaviours.size()) { + terminal.run(); + return; + } + var next = new Continuation(() -> run(behaviours, index + 1, invoke, terminal)); + try { + invoke.accept(behaviours.get(index), next); + } finally { + next.active = false; + } + } + + private static final class Continuation implements Runnable { + private final Runnable operation; + private boolean active = true; + private boolean used; + + private Continuation(Runnable operation) { + this.operation = operation; + } + + @Override + public void run() { + if (!active || used) { + throw new IllegalStateException("A behaviour continuation must be called synchronously and at most once"); + } + used = true; + operation.run(); + } + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/BehaviourRegistry.java b/src/main/java/dev/isxander/controlify/impl/screenop/BehaviourRegistry.java new file mode 100644 index 000000000..34305c981 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/BehaviourRegistry.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; + +/// Orders matching registrations by descending priority, then specificity, then +/// newest registration first. +/// Explicit assignability ordering also handles interfaces without relying on a +/// comparator whose ordering would be inconsistent for unrelated interfaces. +final class BehaviourRegistry { + private final List> registrations = new ArrayList<>(); + private boolean locked; + + void register(Class type, int priority, F factory) { + if (locked) { + throw new IllegalStateException("Behaviour registration has closed"); + } + registrations.add(new Registration<>(Objects.requireNonNull(type), priority, Objects.requireNonNull(factory))); + } + + List factories(T object) { + List> remaining = new ArrayList<>(); + for (int i = registrations.size() - 1; i >= 0; i--) { + var registration = registrations.get(i); + if (registration.type.isInstance(object)) { + remaining.add(registration); + } + } + // Stable sorting retains newest-first order within a priority. + remaining.sort(Comparator.>comparingInt(Registration::priority).reversed()); + List ordered = new ArrayList<>(remaining.size()); + while (!remaining.isEmpty()) { + for (int i = 0; i < remaining.size(); i++) { + var candidate = remaining.get(i); + boolean hasMoreSpecific = remaining.stream().anyMatch(other -> + candidate.priority == other.priority && candidate.type != other.type + && candidate.type.isAssignableFrom(other.type)); + if (!hasMoreSpecific) { + ordered.add(candidate.factory); + remaining.remove(i); + break; + } + } + } + return List.copyOf(ordered); + } + + void lock() { + locked = true; + } + + private record Registration(Class type, int priority, F factory) { + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/BuiltinScreenBehaviours.java b/src/main/java/dev/isxander/controlify/impl/screenop/BuiltinScreenBehaviours.java new file mode 100644 index 000000000..d3e923a84 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/BuiltinScreenBehaviours.java @@ -0,0 +1,210 @@ +package dev.isxander.controlify.impl.screenop; + +import com.mojang.blaze3d.platform.InputConstants; +import dev.isxander.controlify.api.gui.keyboard.*; +import dev.isxander.controlify.api.screenop.*; +import dev.isxander.controlify.impl.gui.controllers.BindController; +import dev.isxander.controlify.impl.gui.keyboard.*; +import dev.isxander.controlify.impl.gui.screen.*; +import dev.isxander.controlify.impl.screenop.behaviours.element.*; +import dev.isxander.controlify.impl.screenop.behaviours.screen.*; +import dev.isxander.controlify.impl.screenop.behaviours.vanilla.*; +import dev.isxander.controlify.impl.screenop.behaviours.virtualmouse.*; +import dev.isxander.controlify.mixins.feature.screenop.impl.container.CreativeModeInventoryScreenMixin; +import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.*; +import dev.isxander.controlify.mixins.feature.screenop.runtime.SliderAccessor; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.*; +import net.minecraft.client.gui.components.events.*; +import net.minecraft.client.gui.screens.*; +import net.minecraft.client.gui.screens.inventory.*; +import net.minecraft.client.gui.screens.multiplayer.*; +import net.minecraft.client.gui.screens.options.*; +import net.minecraft.client.gui.screens.worldselection.*; +import net.minecraft.client.input.KeyEvent; + +import java.util.Optional; + +/// Built-ins use the same registration events as addons. Priorities order whole +/// groups; explicit chains define ordering within each group. +public final class BuiltinScreenBehaviours { + private BuiltinScreenBehaviours() { + } + + public static void initialize() { + ScreenopEvents.REGISTER_SCREEN_BEHAVIOURS.subscribe(BuiltinScreenBehaviours::registerScreens); + ScreenopEvents.REGISTER_ELEMENT_BEHAVIOURS.subscribe(BuiltinScreenBehaviours::registerElements); + dev.isxander.controlify.impl.compatibility.ControlifyCompat.initializeScreenBehaviours(); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private static void registerScreens(ScreenBehaviourRegistry registry) { + registry.register(Screen.class, 100, BuiltinScreenBehaviours::createCursorAndFocusBehaviours); + registry.register(Screen.class, -100, BuiltinScreenBehaviours::createDefaultControls); + registry.register(TitleScreen.class, TitleScreenBehaviour::new); + registry.register(SelectWorldScreen.class, SelectWorldScreenBehaviour::new); + registry.>register((Class) AbstractContainerScreen.class, context -> { + ((ScreenContextImpl) context).virtualMouse().setDefaultMode(VirtualMouseMode.CURSOR_SCROLL); + return new ContainerScreenBehaviour(context); + }); + registry.>register((Class) AbstractRecipeBookScreen.class, RecipeBookScreenBehaviour::new); + registry.register(CreativeModeInventoryScreen.class, CreativeModeInventoryScreenBehaviour::new); + registry.registerUnchecked(OptionsScreen.class); + registry.registerUnchecked(OptionsSubScreen.class); + registry.registerUnchecked(PauseScreen.class); + registry.registerUnchecked(JoinMultiplayerScreen.class); + registry.registerUnchecked(DirectJoinServerScreen.class); + registry.registerUnchecked(ManageServerScreen.class); + registry.registerUnchecked(CreateWorldScreen.class); + registry.registerUnchecked(ChatScreen.class); + registry.registerUnchecked(AbstractSignEditScreen.class); + registry.register(BindConsumerScreen.class, 200); + registry.register(ControllerMappingMakerScreen.class, 200); + registry.register(RadialMenuScreen.class); + registry.register(RadialMenuEditScreen.class, 200); + registry.register(ControlifySettingsScreen.class); + registry.register(ProfileSelectionScreen.class); + registry.registerUnchecked(EnchantmentScreen.class); + registry.registerUnchecked(LoomScreen.class); + registry.registerUnchecked(StonecutterScreen.class); + } + + private static void registerElements(ScreenopEvents.RegisterElementBehavioursEvent event) { + var registry = event.registry(); + var context = event.screenContext(); + + // Snap eligibility -> custom behaviours (zero by default) -> traversal -> centre. + registry.register(AbstractWidget.class, 100, DefaultWidgetBehaviour::new); + registry.register(ContainerEventHandler.class, -100, DefaultContainerBehaviour::new); + registry.register(AbstractWidget.class, -200, ElementBehaviours::snapPoint); + registry.register(AbstractButton.class, ElementBehaviours.enabledWidget(ElementBehaviours.button( + button -> button.onPress(new KeyEvent(InputConstants.KEY_RETURN, 0, 0)), + button -> button.playDownSound(Minecraft.getInstance().getSoundManager()) + ))); + registry.register(AbstractSliderButton.class, ElementBehaviours.enabledWidget(ElementBehaviours.slider( + BuiltinScreenBehaviours::adjustSlider, + slider -> ((SliderAccessor) slider).controlify$canChangeValue(), + (slider, editing) -> ((SliderAccessor) slider).controlify$setCanChangeValue(editing) + ))); + registry.register(EditBox.class, DefaultEditBoxBehaviour::new); + registry.register(KeyWidget.class, key -> key); + registry.register(KeyboardWidget.class, keyboard -> keyboard); + + registry.register(BindController.BindControllerElement.class, element -> element); + registry.register(WorldSelectionList.WorldListEntry.class, ElementBehaviours.button(entry -> { + if (context.screen() instanceof SelectWorldScreen screen) { + screen.setFocused(((SelectWorldScreenAccessor) screen).controlify$getPlayWorldButton()); + } + })); + registry.register(ServerSelectionList.Entry.class, entry -> { + if (entry instanceof ServerSelectionList.LANHeader) { + return ElementBehaviours.chain(); + } + return ElementBehaviours.button(ignored -> { + if (context.screen() instanceof JoinMultiplayerScreen screen) { + screen.setFocused(((JoinMultiplayerScreenAccessor) screen).controlify$getJoinButton()); + } + }).create(entry); + }); + registerLanguageEntries(registry); + if (context.screen() instanceof ChatScreen) { + registry.register(EditBox.class, 10, box -> new ElementBehaviour() { + @Override + public Optional keyboardBehaviour(ScreenContext screen) { + return Optional.of(new ElementKeyboardBehaviour.DoNothing()); + } + }); + } + if (context.screen() instanceof ServerAddressScreenAccess address) { + registry.register(EditBox.class, 10, box -> ElementBehaviours.conditional( + () -> box == address.controlify$getAddressField(), + new DefaultEditBoxBehaviour(box, KeyboardLayouts.SERVER_IP, new EditBoxInputTarget(box)) + )); + } + if (context.screen() instanceof CreativeModeInventoryScreen screen) { + var access = (CreativeModeInventoryScreenMixin) screen; + registry.register(EditBox.class, 10, box -> { + var input = new DelegatingInputTarget(new EditBoxInputTarget(box)) { + @Override + public boolean acceptChar(char ch, int modifiers) { + boolean handled = super.acceptChar(ch, modifiers); + access.controlify$refreshSearchResults(); + return handled; + } + + @Override + public boolean acceptKeyCode(int keycode, int scancode, int modifiers) { + boolean handled = super.acceptKeyCode(keycode, scancode, modifiers); + access.controlify$refreshSearchResults(); + return handled; + } + }; + return ElementBehaviours.conditional(() -> box == access.controlify$getSearchBox(), + new DefaultEditBoxBehaviour(box, KeyboardLayouts.SIMPLE, input)); + }); + } + } + + private static void registerLanguageEntries(ElementBehaviourRegistry registry) { + try { + var type = Class.forName("net.minecraft.client.gui.screens.options.LanguageSelectScreen$LanguageSelectionList$Entry") + .asSubclass(GuiEventListener.class); + registry.registerUnchecked(type); + } catch (ClassNotFoundException exception) { + throw new IllegalStateException("Language selection entry class is unavailable", exception); + } + } + + private static void adjustSlider(AbstractSliderButton slider, int direction) { + var accessor = (SliderAccessor) slider; + boolean editing = accessor.controlify$canChangeValue(); + try { + // Secondary navigation adjusts without entering the widget's editing mode. + accessor.controlify$setCanChangeValue(true); + int key = direction < 0 ? InputConstants.KEY_LEFT : InputConstants.KEY_RIGHT; + slider.keyPressed(new KeyEvent(key, 0, 0)); + } finally { + accessor.controlify$setCanChangeValue(editing && slider.isFocused()); + } + } + + private static ScreenBehaviour createCursorAndFocusBehaviours(ScreenContext screen) { + var context = (ScreenContextImpl) screen; + var cursor = ScreenBehaviours.chain( + new CursorMovementBehaviour(context), + ScreenBehaviours.conditional( + () -> context.virtualMouse().mode().scrolls(), + new CursorScrollBehaviour(context) + ), + new CursorRenderBehaviour(context) + ); + + return ScreenBehaviours.chain( + new CursorModeBehaviour(context), + ScreenBehaviours.conditional(context::isVirtualMouseEnabled, cursor), + new FocusManagementBehaviour(context), + ScreenBehaviours.conditional( + () -> !context.isVirtualMouseEnabled(), + new FocusedElementBehaviour(context) + ) + ); + } + + private static ScreenBehaviour createDefaultControls(ScreenContext screen) { + var context = (ScreenContextImpl) screen; + return ScreenBehaviours.chain( + ScreenBehaviours.conditional( + () -> !context.isVirtualMouseEnabled(), + new FocusInputBehaviour(context) + ), + new TabNavigationBehaviour(context), + ScreenBehaviours.conditional( + () -> context.isVirtualMouseEnabled() && context.virtualMouse().mode().emulatesClicks(), + new CursorClickBehaviour(context) + ), + new BackButtonBehaviour(context), + new ScreenSnapPointBehaviour(context) + ); + } + +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/ComponentProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/ComponentProcessor.java deleted file mode 100644 index 1c563aad9..000000000 --- a/src/main/java/dev/isxander/controlify/impl/screenop/ComponentProcessor.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.screenop; - -import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.screenop.keyboard.ComponentKeyboardBehaviour; - -public interface ComponentProcessor extends ComponentProcessorProvider { - ComponentProcessor EMPTY = new ComponentProcessor(){}; - - default boolean overrideControllerNavigation(ScreenProcessor screen, ControllerImpl controller) { - return false; - } - - default boolean overrideControllerButtons(ScreenProcessor screen, ControllerImpl controller) { - return false; - } - - default void onFocusGained(ScreenProcessor screen, ControllerImpl controller) { - } - - default boolean shouldKeepFocusOnKeyboardMode(ScreenProcessor screen) { - return false; - } - - default ComponentKeyboardBehaviour getKeyboardBehaviour(ScreenProcessor screen, ControllerImpl controller) { - return ComponentKeyboardBehaviour.UNDEFINED; - } - - @Override - default ComponentProcessor componentProcessor() { - return this; - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/ComponentProcessorProvider.java b/src/main/java/dev/isxander/controlify/impl/screenop/ComponentProcessorProvider.java deleted file mode 100644 index 847162b9d..000000000 --- a/src/main/java/dev/isxander/controlify/impl/screenop/ComponentProcessorProvider.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.screenop; - -import net.minecraft.client.gui.components.events.GuiEventListener; - -public interface ComponentProcessorProvider { - ComponentProcessor componentProcessor(); - - static ComponentProcessor provide(GuiEventListener component) { - if (component == null) { - return ComponentProcessor.EMPTY; - } - - if (component instanceof ComponentProcessorProvider provider) - return provider.componentProcessor(); - - return REGISTRY.get(component).orElse(ComponentProcessor.EMPTY); - - } - - Registry REGISTRY = new Registry<>(); -} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/CompositeElementBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/CompositeElementBehaviour.java new file mode 100644 index 000000000..1082d6e68 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/CompositeElementBehaviour.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop; + +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.screenop.ElementBehaviour; +import dev.isxander.controlify.api.screenop.ElementKeyboardBehaviour; +import dev.isxander.controlify.api.screenop.ScreenContext; +import dev.isxander.controlify.api.screenop.SnapPoint; +import dev.isxander.controlify.impl.utils.MinecraftUtil; +import org.jspecify.annotations.NonNull; + +import java.util.List; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public final class CompositeElementBehaviour implements ElementBehaviour { + private final List behaviours; + private final boolean branch; + + public CompositeElementBehaviour(List behaviours) { + this(behaviours, false); + } + + private CompositeElementBehaviour(List behaviours, boolean branch) { + this.behaviours = List.copyOf(behaviours); + this.branch = branch; + } + + public static ElementBehaviour branch(ElementBehaviour behaviour) { + return new CompositeElementBehaviour(List.of(behaviour), true); + } + + private void run(BiConsumer invoke, Runnable next) { + if (branch) { + BehaviourChain.run(behaviours, invoke, () -> { + }); + next.run(); + } else { + BehaviourChain.run(behaviours, invoke, next); + } + } + + @Override + public void handleNavigation(@NonNull ScreenContext screen, @NonNull Controller controller, @NonNull Runnable next) { + run((behaviour, continuation) -> { + if (MinecraftUtil.getScreen() == screen.screen()) { + behaviour.handleNavigation(screen, controller, continuation); + } + }, () -> { + if (MinecraftUtil.getScreen() == screen.screen()) { + next.run(); + } + }); + } + + @Override + public void handleInteraction(@NonNull ScreenContext screen, @NonNull Controller controller, @NonNull Runnable next) { + run((behaviour, continuation) -> { + if (MinecraftUtil.getScreen() == screen.screen()) { + behaviour.handleInteraction(screen, controller, continuation); + } + }, () -> { + if (MinecraftUtil.getScreen() == screen.screen()) { + next.run(); + } + }); + } + + @Override + public void onFocusChanged(@NonNull ScreenContext screen, boolean focused) { + behaviours.forEach(behaviour -> behaviour.onFocusChanged(screen, focused)); + } + + @Override + public Optional keyboardBehaviour(@NonNull ScreenContext screen) { + return behaviours.stream().map(behaviour -> behaviour.keyboardBehaviour(screen)) + .flatMap(Optional::stream).findFirst(); + } + + @Override + public void collectSnapPoints(@NonNull ScreenContext screen, @NonNull Consumer output, @NonNull Runnable next) { + run((behaviour, continuation) -> behaviour.collectSnapPoints(screen, output, continuation), next); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/CompositeScreenBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/CompositeScreenBehaviour.java new file mode 100644 index 000000000..710ffc718 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/CompositeScreenBehaviour.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop; + +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.screenop.ScreenBehaviour; +import dev.isxander.controlify.api.screenop.SnapPoint; +import dev.isxander.controlify.impl.utils.MinecraftUtil; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import org.jspecify.annotations.NonNull; + +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public final class CompositeScreenBehaviour implements ScreenBehaviour { + private final List behaviours; + private final boolean branch; + + public CompositeScreenBehaviour(List behaviours) { + this(behaviours, false); + } + + private CompositeScreenBehaviour(List behaviours, boolean branch) { + this.behaviours = List.copyOf(behaviours); + this.branch = branch; + } + + public static ScreenBehaviour branch(ScreenBehaviour behaviour) { + return new CompositeScreenBehaviour(List.of(behaviour), true); + } + + private void run(BiConsumer invoke, Runnable next) { + if (branch) { + BehaviourChain.run(behaviours, invoke, () -> { + }); + next.run(); + } else { + BehaviourChain.run(behaviours, invoke, next); + } + } + + private void input(BiConsumer invoke, Runnable next) { + var screen = MinecraftUtil.getScreen(); + run((behaviour, continuation) -> { + if (MinecraftUtil.getScreen() == screen) { + invoke.accept(behaviour, continuation); + } + }, () -> { + if (MinecraftUtil.getScreen() == screen) { + next.run(); + } + }); + } + + @Override + public void handleNavigation(@NonNull Controller controller, @NonNull Runnable next) { + input((behaviour, continuation) -> behaviour.handleNavigation(controller, continuation), next); + } + + @Override + public void handleInteraction(@NonNull Controller controller, @NonNull Runnable next) { + input((behaviour, continuation) -> behaviour.handleInteraction(controller, continuation), next); + } + + @Override + public void onWidgetRebuild(@NonNull Runnable next) { + run(ScreenBehaviour::onWidgetRebuild, next); + } + + @Override + public void extractRenderState(@NonNull GuiGraphicsExtractor graphics, float tickDelta, @NonNull Controller controller, @NonNull Runnable next) { + run((behaviour, continuation) -> behaviour.extractRenderState(graphics, tickDelta, controller, continuation), next); + } + + @Override + public void collectSnapPoints(@NonNull Consumer output, @NonNull Runnable next) { + run((behaviour, continuation) -> behaviour.collectSnapPoints(output, continuation), next); + } + + @Override + public void update(float delta, Runnable next) { + input((behaviour, continuation) -> behaviour.update(delta, continuation), next); + } + + @Override + public void onScreenActiveChanged(boolean active) { + behaviours.forEach(behaviour -> behaviour.onScreenActiveChanged(active)); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/ConditionalElementBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/ConditionalElementBehaviour.java new file mode 100644 index 000000000..3cb5d7852 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/ConditionalElementBehaviour.java @@ -0,0 +1,62 @@ +package dev.isxander.controlify.impl.screenop; + +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.screenop.ElementBehaviour; +import dev.isxander.controlify.api.screenop.ElementKeyboardBehaviour; +import dev.isxander.controlify.api.screenop.ScreenContext; +import dev.isxander.controlify.api.screenop.SnapPoint; + +import java.util.Optional; +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; + +public class ConditionalElementBehaviour implements ElementBehaviour { + private final BooleanSupplier enabled; + private final ElementBehaviour behaviour; + + public ConditionalElementBehaviour(BooleanSupplier enabled, ElementBehaviour behaviour) { + this.enabled = enabled; + this.behaviour = behaviour; + } + + @Override + public void handleNavigation(ScreenContext screen, Controller controller, Runnable next) { + if (this.enabled.getAsBoolean()) { + this.behaviour.handleNavigation(screen, controller, next); + } else { + next.run(); + } + } + + @Override + public void handleInteraction(ScreenContext screen, Controller controller, Runnable next) { + if (this.enabled.getAsBoolean()) { + this.behaviour.handleInteraction(screen, controller, next); + } else { + next.run(); + } + } + + @Override + public void onFocusChanged(ScreenContext screen, boolean focused) { + this.behaviour.onFocusChanged(screen, focused); + } + + @Override + public Optional keyboardBehaviour(ScreenContext screen) { + if (this.enabled.getAsBoolean()) { + return this.behaviour.keyboardBehaviour(screen); + } else { + return Optional.empty(); + } + } + + @Override + public void collectSnapPoints(ScreenContext screen, Consumer output, Runnable next) { + if (this.enabled.getAsBoolean()) { + this.behaviour.collectSnapPoints(screen, output, next); + } else { + next.run(); + } + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/ConditionalScreenBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/ConditionalScreenBehaviour.java new file mode 100644 index 000000000..16e575317 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/ConditionalScreenBehaviour.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop; + +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.screenop.*; +import net.minecraft.client.gui.GuiGraphicsExtractor; + +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; + +/// Retains a stateful behaviour while deciding whether it participates in each call. +/// Lifetime notifications always reach the behaviour, including deactivation. +public final class ConditionalScreenBehaviour implements ScreenBehaviour { + private final BooleanSupplier enabled; + private final ScreenBehaviour behaviour; + + public ConditionalScreenBehaviour(BooleanSupplier enabled, ScreenBehaviour behaviour) { + this.enabled = enabled; + this.behaviour = behaviour; + } + + @Override + public void handleNavigation(Controller controller, Runnable next) { + if (enabled.getAsBoolean()) { + behaviour.handleNavigation(controller, next); + } else { + next.run(); + } + } + + @Override + public void handleInteraction(Controller controller, Runnable next) { + if (enabled.getAsBoolean()) { + behaviour.handleInteraction(controller, next); + } else { + next.run(); + } + } + + @Override + public void onWidgetRebuild(Runnable next) { + if (enabled.getAsBoolean()) { + behaviour.onWidgetRebuild(next); + } else { + next.run(); + } + } + + @Override + public void update(float delta, Runnable next) { + if (enabled.getAsBoolean()) { + behaviour.update(delta, next); + } else { + next.run(); + } + } + + @Override + public void extractRenderState(GuiGraphicsExtractor graphics, float tickDelta, Controller controller, Runnable next) { + if (enabled.getAsBoolean()) { + behaviour.extractRenderState(graphics, tickDelta, controller, next); + } else { + next.run(); + } + } + + @Override + public void collectSnapPoints(Consumer output, Runnable next) { + if (enabled.getAsBoolean()) { + behaviour.collectSnapPoints(output, next); + } else { + next.run(); + } + } + + @Override + public void onScreenActiveChanged(boolean active) { + behaviour.onScreenActiveChanged(active); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/CustomFocus.java b/src/main/java/dev/isxander/controlify/impl/screenop/CustomFocus.java deleted file mode 100644 index 715e199b0..000000000 --- a/src/main/java/dev/isxander/controlify/impl/screenop/CustomFocus.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.screenop; - -import net.minecraft.client.gui.components.events.GuiEventListener; - -public interface CustomFocus { - GuiEventListener getCustomFocus(); -} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/ElementBehaviourRegistryImpl.java b/src/main/java/dev/isxander/controlify/impl/screenop/ElementBehaviourRegistryImpl.java new file mode 100644 index 000000000..4b436ed4b --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/ElementBehaviourRegistryImpl.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop; + +import dev.isxander.controlify.api.screenop.ElementBehaviour; +import dev.isxander.controlify.api.screenop.ElementBehaviourFactory; +import dev.isxander.controlify.api.screenop.ElementBehaviourRegistry; +import dev.isxander.controlify.impl.screenop.behaviours.element.*; +import net.minecraft.client.gui.components.events.GuiEventListener; + +import java.util.List; +import java.util.Objects; + +final class ElementBehaviourRegistryImpl implements ElementBehaviourRegistry { + private final BehaviourRegistry> registry = new BehaviourRegistry<>(); + + @Override + public void register(Class elementClass, int priority, ElementBehaviourFactory factory) { + registry.register(elementClass, priority, factory); + } + + @Override + public > void register(Class elementClass, int priority) { + register(elementClass, priority, element -> element.create(element)); + } + + @Override + @SuppressWarnings("unchecked") + public void registerUnchecked(Class elementClass, int priority) { + if (!ElementBehaviourFactory.class.isAssignableFrom(elementClass)) { + throw new IllegalArgumentException(elementClass.getName() + " must implement ElementBehaviourFactory"); + } + register(elementClass, priority, element -> ((ElementBehaviourFactory) element).create(element)); + } + + List create(GuiEventListener element) { + return registry.factories(element).stream().map(factory -> create(factory, element)).toList(); + } + + @SuppressWarnings("unchecked") + private static ElementBehaviour create(ElementBehaviourFactory factory, GuiEventListener element) { + return Objects.requireNonNull(factory.create((T) element), "Element behaviour factory returned null"); + } + + void lock() { + registry.lock(); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/Registry.java b/src/main/java/dev/isxander/controlify/impl/screenop/Registry.java deleted file mode 100644 index bcb1132ff..000000000 --- a/src/main/java/dev/isxander/controlify/impl/screenop/Registry.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.screenop; - -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import org.jetbrains.annotations.ApiStatus; - -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; - -@SuppressWarnings("unchecked") -public class Registry { - private final Map, Function> registry; - private final Map cache; - - public Registry() { - this.registry = new Object2ObjectOpenHashMap<>(); - this.cache = new Object2ObjectOpenHashMap<>(); - } - - /** - * Registers a constructor for a class - * - * @param clazz the class to bind the constructor to - * @param constructor function to build the object from the class - * @param type of class - */ - public void register(Class clazz, Function constructor) { - registry.put(clazz, (Function) constructor); - } - - Optional get(T object) { - U cached = this.cache.get(object); - if (cached != null) - return Optional.of(cached); - - Class clazz = (Class) object.getClass(); - Function constructor = registry.get(clazz); - if (constructor == null) - return Optional.empty(); - - U constructed = constructor.apply(object); - this.cache.put(object, constructed); - return Optional.of(constructed); - } - - @ApiStatus.Internal - public void clearCache() { - this.cache.clear(); - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/ScreenBehaviourRegistryImpl.java b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenBehaviourRegistryImpl.java new file mode 100644 index 000000000..2d1c07770 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenBehaviourRegistryImpl.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop; + +import dev.isxander.controlify.api.screenop.ScreenBehaviour; +import dev.isxander.controlify.api.screenop.ScreenBehaviourFactory; +import dev.isxander.controlify.api.screenop.ScreenBehaviourRegistry; +import dev.isxander.controlify.api.screenop.ScreenContext; +import dev.isxander.controlify.api.screenop.ScreenopEvents; +import dev.isxander.controlify.impl.screenop.behaviours.screen.*; +import dev.isxander.controlify.impl.screenop.behaviours.virtualmouse.*; +import dev.isxander.controlify.impl.utils.event.EventBus; +import net.minecraft.client.gui.screens.Screen; + +import java.util.List; +import java.util.Objects; + +public final class ScreenBehaviourRegistryImpl implements ScreenBehaviourRegistry { + public static final class Events { + public static final EventBus REGISTER_SCREEN_BEHAVIOURS = new EventBus<>(); + public static final EventBus REGISTER_ELEMENT_BEHAVIOURS = new EventBus<>(); + + private Events() { + } + } + + public static final ScreenBehaviourRegistryImpl INSTANCE = new ScreenBehaviourRegistryImpl(); + + private final BehaviourRegistry> registry = new BehaviourRegistry<>(); + private boolean initialized; + private boolean ready; + + private ScreenBehaviourRegistryImpl() { + } + + public void initialize() { + if (initialized) { + throw new IllegalStateException("Screen behaviours have already been initialized"); + } + initialized = true; + try { + Events.REGISTER_SCREEN_BEHAVIOURS.emit(this); + ready = true; + } finally { + registry.lock(); + } + } + + @Override + public void register(Class screenClass, int priority, ScreenBehaviourFactory factory) { + registry.register(screenClass, priority, factory); + } + + @Override + public > void register(Class screenClass, int priority) { + register(screenClass, priority, context -> context.screen().create(context)); + } + + @Override + @SuppressWarnings("unchecked") + public void registerUnchecked(Class screenClass, int priority) { + if (!ScreenBehaviourFactory.class.isAssignableFrom(screenClass)) { + throw new IllegalArgumentException(screenClass.getName() + " must implement ScreenBehaviourFactory"); + } + register(screenClass, priority, context -> ((ScreenBehaviourFactory) context.screen()).create(context)); + } + + public boolean isReady() { + return ready; + } + + List create(ScreenContext context) { + if (!ready) { + throw new IllegalStateException("Screen contexts are available after REGISTER_SCREEN_BEHAVIOURS completes"); + } + return registry.factories(context.screen()).stream() + .map(factory -> create(factory, context)) + .toList(); + } + + @SuppressWarnings("unchecked") + private static ScreenBehaviour create(ScreenBehaviourFactory factory, ScreenContext context) { + // The registry selected this factory using its registered screen class. + return Objects.requireNonNull(factory.create((ScreenContext) context), "Screen behaviour factory returned null"); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/ScreenContextHolder.java b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenContextHolder.java new file mode 100644 index 000000000..35fb9a6ed --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenContextHolder.java @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop; + +import org.jetbrains.annotations.Nullable; + +/// Storage only. All sidecar selection is performed through the registries. +public interface ScreenContextHolder { + ScreenContextImpl controlify$screenContext(); + + @Nullable ScreenContextImpl controlify$existingScreenContext(); +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/ScreenContextImpl.java b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenContextImpl.java new file mode 100644 index 000000000..c59025800 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenContextImpl.java @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop; + +import dev.isxander.controlify.api.InputMode; +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.screenop.*; +import dev.isxander.controlify.api.screenop.ElementKeyboardBehaviour; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.controller.haptic.HapticEffects; +import dev.isxander.controlify.impl.gui.keyboard.KeyboardOverlayScreen; +import dev.isxander.controlify.impl.sound.ControlifyClientSounds; +import dev.isxander.controlify.impl.utils.MinecraftUtil; +import dev.isxander.controlify.mixins.feature.screenop.ScreenAccessor; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.ComponentPath; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.events.ContainerEventHandler; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.resources.sounds.SimpleSoundInstance; +import net.minecraft.sounds.SoundEvents; +import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.NonNull; + +import java.util.*; + +/// One screen's state and dispatch. Default UI behaviour lives in the registered +/// composed screen and element behaviours, rather than in this context. +/// The screen retains this instance for its lifetime. +public final class ScreenContextImpl implements ScreenContext { + private final T screen; + private final VirtualMouseState virtualMouse; + private final ElementBehaviourRegistryImpl elements = new ElementBehaviourRegistryImpl(); + private final Map elementCache = new IdentityHashMap<>(); + private final Set constructingElements = identitySet(); + private final ScreenBehaviour behaviour; + private Set focusedElements = identitySet(); + private boolean elementsReady; + private boolean widgetsObserved; + + /// Resolves the same context across rebuilds and reopening the same screen. + @SuppressWarnings("unchecked") + public static ScreenContextImpl get(T screen) { + return (ScreenContextImpl) ((ScreenContextHolder) screen).controlify$screenContext(); + } + + public ScreenContextImpl(T screen) { + this.screen = Objects.requireNonNull(screen); + this.virtualMouse = new VirtualMouseState(this); + this.behaviour = ScreenBehaviours.chain(ScreenBehaviourRegistryImpl.INSTANCE.create(this)); + try { + ScreenBehaviourRegistryImpl.Events.REGISTER_ELEMENT_BEHAVIOURS.emit( + new ScreenopEvents.RegisterElementBehavioursEvent(this, elements)); + } finally { + elements.lock(); + } + elementsReady = true; + } + + @Override + public T screen() { + return screen; + } + + @Override + public ElementBehaviour elementBehaviour(GuiEventListener element) { + Objects.requireNonNull(element); + if (!elementsReady) { + throw new IllegalStateException("Element behaviours are available after REGISTER_ELEMENT_BEHAVIOURS completes"); + } + var cached = elementCache.get(element); + if (cached != null) { + return cached; + } + if (!constructingElements.add(element)) { + throw new IllegalStateException("Element behaviour factory recursively resolved its own element"); + } + try { + var chain = ElementBehaviours.chain(elements.create(element)); + elementCache.put(element, chain); + return chain; + } finally { + constructingElements.remove(element); + } + } + + @Override + public InputMode inputMode() { + return Controlify.instance().currentInputMode(); + } + + @Override + public VirtualMouseState virtualMouse() { + return virtualMouse; + } + + @Override + public List focusPath() { + List path = new ArrayList<>(); + Set visited = identitySet(); + GuiEventListener focused = screen.getFocused(); + while (focused != null && visited.add(focused)) { + path.addFirst(focused); + focused = focused instanceof ContainerEventHandler container ? container.getFocused() : null; + } + return List.copyOf(path); + } + + @Override + public void clearFocus() { + ((ScreenAccessor) screen).controlify$invokeClearFocus(); + synchronizeFocus(); + } + + @Override + public void setInitialFocus() { + ((ScreenAccessor) screen).invokeSetInitialFocus(); + synchronizeFocus(); + } + + @Override + public void notifyNavigation(Controller controller) { + if (controller instanceof ControllerImpl impl) { + impl.input().onGuiNavigate(); + } + if (Controlify.instance().config().getSettings().globalSettings().extraUiSounds) { + playFocusChangeSound(); + } + playNavigationHaptic(controller); + } + + @Override + public void playNavigationHaptic(Controller controller) { + if (controller instanceof ControllerImpl impl) { + impl.hdHaptics().ifPresent(haptics -> haptics.playHaptic(HapticEffects.NAVIGATE)); + } + } + + @Override + public void playFocusChangeSound() { + Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(ControlifyClientSounds.SCREEN_FOCUS_CHANGE.get(), 1f)); + } + + @Override + public void playClickSound() { + Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1f)); + } + + @Override + public ButtonGuideApi buttonGuide() { + return dev.isxander.controlify.impl.gui.ButtonGuideApiImpl.INSTANCE; + } + + public void changeFocus(ComponentPath path) { + ((ScreenAccessor) screen).controlify$invokeChangeFocus(path); + synchronizeFocus(); + } + + @Override + public boolean tryOpenKeyboard(@NonNull Controller controller, @Nullable GuiEventListener element) { + if (element == null || element instanceof AbstractWidget widget && (!widget.active || !widget.visible)) { + return false; + } + var keyboard = elementBehaviour(element).keyboardBehaviour(this); + if (keyboard.isEmpty()) { + return false; + } + switch (keyboard.get()) { + case ElementKeyboardBehaviour.DoNothing() -> { + } + case ElementKeyboardBehaviour.Handled(var layout, var target, var positioner) -> { + if (((ControllerImpl) controller).settings().generic.keyboard.showOnScreenKeyboard) { + MinecraftUtil.setScreen(new KeyboardOverlayScreen(screen, layout, target, positioner)); + } + } + } + return true; + } + + /// Entry point for the host's fixed update. + public void handleControllerInput(Controller controller) { + if (!isCurrentScreen()) { + return; + } + observeWidgets(); + pruneElements(); + synchronizeFocus(); + behaviour.handleNavigation(controller, () -> { + }); + if (isCurrentScreen()) { + synchronizeFocus(); + behaviour.handleInteraction(controller, () -> { + }); + synchronizeFocus(); + } + } + + public void dispatchElements(Controller controller, boolean navigation, Runnable next) { + List path = focusPath().stream().map(this::elementBehaviour).toList(); + BehaviourChain.run(path, (element, continuation) -> { + if (isCurrentScreen()) { + if (navigation) { + element.handleNavigation(this, controller, continuation); + } else { + element.handleInteraction(this, controller, continuation); + } + } + }, next); + } + + public void onWidgetRebuild() { + widgetsObserved = true; + pruneElements(); + behaviour.onWidgetRebuild(() -> { + }); + synchronizeFocus(); + } + + public void extractRenderState(GuiGraphicsExtractor graphics, float tickDelta, Controller controller) { + observeWidgets(); + behaviour.extractRenderState(graphics, tickDelta, controller, () -> { + }); + synchronizeFocus(); + } + + public Set collectSnapPoints() { + Set points = new LinkedHashSet<>(); + behaviour.collectSnapPoints(points::add, () -> { + }); + return Collections.unmodifiableSet(points); + } + + public void update(float delta) { + observeWidgets(); + behaviour.update(delta, () -> { + }); + synchronizeFocus(); + } + + public void onScreenActiveChanged(boolean active) { + behaviour.onScreenActiveChanged(active); + if (active) { + observeWidgets(); + } + synchronizeFocus(); + } + + private void observeWidgets() { + if (!widgetsObserved) { + onWidgetRebuild(); + } + } + + public void synchronizeFocus() { + if (!elementsReady) { + return; + } + Set previous = focusedElements; + Set current = identitySet(); + current.addAll(focusPath()); + focusedElements = current; + for (GuiEventListener element : previous) { + if (!current.contains(element)) { + elementBehaviour(element).onFocusChanged(this, false); + } + } + for (GuiEventListener element : current) { + if (!previous.contains(element)) { + elementBehaviour(element).onFocusChanged(this, true); + } + } + } + + private void pruneElements() { + Set attached = identitySet(); + ArrayDeque remaining = new ArrayDeque<>(screen.children()); + while (!remaining.isEmpty()) { + GuiEventListener element = remaining.removeFirst(); + if (attached.add(element) && element instanceof ContainerEventHandler container) { + remaining.addAll(container.children()); + } + } + attached.addAll(focusPath()); + // Notify lost focus while the old sidecar is still cached. + synchronizeFocus(); + elementCache.keySet().removeIf(element -> !attached.contains(element)); + } + + public boolean isCurrentScreen() { + return MinecraftUtil.getScreen() == screen; + } + + private static Set identitySet() { + return Collections.newSetFromMap(new IdentityHashMap<>()); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/ScreenControllerEventListener.java b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenControllerEventListener.java deleted file mode 100644 index 4aec4936d..000000000 --- a/src/main/java/dev/isxander/controlify/impl/screenop/ScreenControllerEventListener.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.screenop; - -import dev.isxander.controlify.impl.controller.ControllerImpl; - -public interface ScreenControllerEventListener { - default void onControllerInput(ControllerImpl controller) {} -} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessor.java deleted file mode 100644 index 3fa0fc8d4..000000000 --- a/src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessor.java +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.screenop; - -import com.mojang.blaze3d.platform.InputConstants; -import dev.isxander.controlify.api.InputMode; -import dev.isxander.controlify.api.action.Action; -import dev.isxander.controlify.impl.Controlify; -import dev.isxander.controlify.impl.action.ControlifyActions; -import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.controller.haptic.HapticEffects; -import dev.isxander.controlify.impl.controller.input.InputComponentImpl; -import dev.isxander.controlify.impl.input.mapping.GamepadInputs; -import dev.isxander.controlify.impl.screenop.keyboard.*; -import dev.isxander.controlify.impl.sound.ControlifyClientSounds; -import dev.isxander.controlify.impl.utils.MinecraftUtil; -import dev.isxander.controlify.impl.virtualmouse.VirtualMouseBehaviour; -import dev.isxander.controlify.impl.virtualmouse.VirtualMouseHandler; -import dev.isxander.controlify.mixins.feature.screenop.ScreenAccessor; -import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.TabNavigationBarAccessor; -import net.minecraft.client.InputType; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.client.gui.components.AbstractWidget; -import net.minecraft.client.gui.components.events.GuiEventListener; -import net.minecraft.client.gui.components.tabs.Tab; -import net.minecraft.client.gui.components.tabs.TabNavigationBar; -import net.minecraft.client.gui.navigation.FocusNavigationEvent; -import net.minecraft.client.gui.navigation.ScreenDirection; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.input.KeyEvent; -import net.minecraft.client.resources.sounds.SimpleSoundInstance; -import net.minecraft.network.chat.Component; -import net.minecraft.sounds.SoundEvents; -import org.jetbrains.annotations.Nullable; - -import java.util.*; -import java.util.function.Supplier; - -public class ScreenProcessor { - public final T screen; - protected static final Minecraft minecraft = Minecraft.getInstance(); - - private final List eventListeners = new ArrayList<>(); - - public ScreenProcessor(T screen) { - this.screen = screen; - if (screen instanceof ScreenControllerEventListener eventListener) { - eventListeners.add(eventListener); - } - } - - public void onControllerFixedUpdate(ControllerImpl controller) { - Controlify.instance().virtualMouseHandler().handleControllerInput(controller); - - if (!Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled()) { - if (!handleComponentNavOverride(controller)) { - handleComponentNavigation(controller); - } - - if (!handleComponentButtonOverride(controller)) { - handleButtons(controller); - } - } else { - handleScreenVMouse(controller, Controlify.instance().virtualMouseHandler()); - } - - handleTabNavigation(controller); - - eventListeners.forEach(listener -> listener.onControllerInput(controller)); - } - - public void render(ControllerImpl controller, GuiGraphicsExtractor graphics, float tickDelta) { - var vmouse = Controlify.instance().virtualMouseHandler(); - this.render(controller, graphics, tickDelta, vmouse.isVirtualMouseEnabled() ? Optional.of(vmouse) : Optional.empty()); - } - - public void onInputModeChanged(InputMode mode) { - switch (mode) { - case KEYBOARD_MOUSE -> { - boolean shouldKeepFocus = getFocusTree().stream() - .anyMatch(component -> ComponentProcessorProvider.provide(component).shouldKeepFocusOnKeyboardMode(this)); - - if (!shouldKeepFocus) { - ((ScreenAccessor) screen).controlify$invokeClearFocus(); - } - } - case CONTROLLER, MIXED -> { - if (!Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled()) { - ((ScreenAccessor) screen).invokeSetInitialFocus(); - } - } - } - } - - protected void handleComponentNavigation(ControllerImpl controller) { - if (screen.getFocused() == null) { - ((ScreenAccessor) screen).invokeSetInitialFocus(); - } - - var focuses = List.copyOf(getFocusTree()); - - Action guiNaviRight = ControlifyActions.GUI_NAVI_RIGHT.on(controller); - Action guiNaviLeft = ControlifyActions.GUI_NAVI_LEFT.on(controller); - Action guiNaviUp = ControlifyActions.GUI_NAVI_UP.on(controller); - Action guiNaviDown = ControlifyActions.GUI_NAVI_DOWN.on(controller); - - Supplier navigationFunc = null; - if (guiNaviRight.pulse().consumePulse()) { - navigationFunc = this.createScreenNavigationFunc(ScreenDirection.RIGHT); - } else if (guiNaviLeft.pulse().consumePulse()) { - navigationFunc = this.createScreenNavigationFunc(ScreenDirection.LEFT); - } else if (guiNaviUp.pulse().consumePulse()) { - navigationFunc = this.createScreenNavigationFunc(ScreenDirection.UP); - } else if (guiNaviDown.pulse().consumePulse()) { - navigationFunc = this.createScreenNavigationFunc(ScreenDirection.DOWN); - } - - if (navigationFunc != null) { - // ScrollableLayout only reveals newly focused children for keyboard-style navigation. - // Set this at the point of navigation as the controller input mode may not have been - // established yet when a newly opened screen built its widgets. - minecraft.setLastInputType(InputType.KEYBOARD_ARROW); - if (navigationFunc.get()) { - - controller.input().onGuiNavigate(); - - if (Controlify.instance().config().getSettings().globalSettings().extraUiSounds) { - playFocusChangeSound(); - } - controller.hdHaptics().ifPresent(haptics -> haptics.playHaptic(HapticEffects.NAVIGATE)); - - var newFocusTree = getFocusTree(); - while (!newFocusTree.isEmpty() && !focuses.contains(newFocusTree.peek())) { - ComponentProcessorProvider.provide(newFocusTree.poll()).onFocusGained(this, controller); - } - } - } - } - - protected @Nullable Supplier createScreenNavigationFunc(ScreenDirection direction) { - var event = new FocusNavigationEvent.ArrowNavigation(direction); - var path = screen.nextFocusPath(event); - if (path == null) { - return null; - } - - return () -> { - ((ScreenAccessor) screen).controlify$invokeChangeFocus(path); - return true; - }; - } - - protected void handleButtons(ControllerImpl controller) { - boolean vmouseEnabled = Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled(); - InputComponentImpl input = controller.input(); -// boolean touchpadPressed = input.stateNow().isButtonDown(GamepadInputs.TOUCHPAD_1_BUTTON); -// boolean prevTouchpadPressed = input.stateThen().isButtonDown(GamepadInputs.TOUCHPAD_1_BUTTON); - boolean touchpadPressed = false; - boolean prevTouchpadPressed = false; - - if (ControlifyActions.GUI_PRESS.on(controller).pulse().consumePulse() || (vmouseEnabled && touchpadPressed && !prevTouchpadPressed)) { - if (!this.tryOpenKeyboard(controller, screen.getFocused())) { - screen.keyPressed(new KeyEvent(InputConstants.KEY_RETURN, 0, 0)); - } - } - if (screen.shouldCloseOnEsc() && ControlifyActions.GUI_BACK.on(controller).pulse().consumePulse()) { - playClackSound(); - screen.onClose(); - } - } - - protected void handleScreenVMouse(ControllerImpl controller, VirtualMouseHandler vmouse) { - Minecraft.getInstance().setLastInputType(InputType.MOUSE); - } - - protected boolean handleComponentButtonOverride(ControllerImpl controller) { - var focusTree = getFocusTree(); - while (!focusTree.isEmpty()) { - var focused = focusTree.poll(); - var processor = ComponentProcessorProvider.provide(focused); - if (processor.overrideControllerButtons(this, controller)) { - return true; - } - } - - return false; - } - - protected boolean handleComponentNavOverride(ControllerImpl controller) { - var focusTree = getFocusTree(); - while (!focusTree.isEmpty()) { - var focused = focusTree.poll(); - var processor = ComponentProcessorProvider.provide(focused); - if (processor.overrideControllerNavigation(this, controller)) { - return true; - } - } - return false; - } - - protected void handleTabNavigation(ControllerImpl controller) { - var nextTab = ControlifyActions.GUI_NEXT_TAB.on(controller).pulse().consumePulse(); - var prevTab = ControlifyActions.GUI_PREV_TAB.on(controller).pulse().consumePulse(); - - if (nextTab || prevTab) { - screen.children().stream() - .filter(child -> child instanceof TabNavigationBar) - .map(TabNavigationBar.class::cast) - .findAny() - .ifPresent(navBar -> { - var accessor = (TabNavigationBarAccessor) navBar; - List tabs = accessor.controlify$getTabs(); - int currentIndex = tabs.indexOf(accessor.controlify$getTabManager().getCurrentTab()); - - int newIndex = currentIndex + (prevTab ? -1 : 1); - if (newIndex < 0) { - newIndex = tabs.size() - 1; - } - if (newIndex >= tabs.size()) { - newIndex = 0; - } - - navBar.selectTab(newIndex, true); - onTabChanged(controller); - }); - } - } - - protected void onTabChanged(ControllerImpl controller) { - } - - public void onWidgetRebuild() { - if (Controlify.instance().currentInputMode().isController() && !Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled()) { - // setting this input type causes the vanilla screen to call setInitialFocus - // doing it again would cause the second widget to be focused instead of the first - minecraft.setLastInputType(InputType.KEYBOARD_ARROW); - } - } - - public void onVirtualMouseToggled(boolean enabled) { - if (enabled) { - ((ScreenAccessor) screen).controlify$invokeClearFocus(); - } else { - ((ScreenAccessor) screen).invokeSetInitialFocus(); - } - } - - protected void render(ControllerImpl controller, GuiGraphicsExtractor graphics, float tickDelta, Optional vmouse) { - } - - public VirtualMouseBehaviour virtualMouseBehaviour() { - return VirtualMouseBehaviour.DEFAULT; - } - - public void addEventListener(ScreenControllerEventListener listener) { - eventListeners.add(listener); - } - - public boolean tryOpenKeyboard(ControllerImpl controller, @Nullable GuiEventListener element) { - var componentProcessor = ComponentProcessorProvider.provide(element); - ComponentKeyboardBehaviour behaviour = componentProcessor.getKeyboardBehaviour(this, controller); - - switch (behaviour) { - case ComponentKeyboardBehaviour.DoNothing() -> { - // prevent pressing enter on select when handled - return true; - } - case ComponentKeyboardBehaviour.Undefined() -> { - return false; - } - case ComponentKeyboardBehaviour.Handled( - KeyboardLayoutWithId layout, - InputTarget inputTarget, - KeyboardOverlayScreen.KeyboardPositioner positioner - ) -> { - if (controller.settings().generic.keyboard.showOnScreenKeyboard) { - MinecraftUtil.setScreen(new KeyboardOverlayScreen(screen, layout, inputTarget, positioner)); - } - return true; - } - } - } - - protected Queue getFocusTree() { - if (screen.getFocused() == null) { - return new ArrayDeque<>(); - } - - var tree = new ArrayDeque(); - var focused = screen.getFocused(); - tree.add(focused); - while (focused instanceof CustomFocus customFocus) { - focused = customFocus.getCustomFocus(); - - if (focused != null) { - tree.addFirst(focused); - } - } - - return tree; - } - - protected final Optional getWidget(Component message) { - return screen.children().stream() - .filter(child -> child instanceof AbstractWidget) - .map(AbstractWidget.class::cast) - .filter(widget -> widget.getMessage().equals(message)) - .findAny(); - } - - protected final Optional getWidget(String translationKey) { - String translatedName = Component.translatable(translationKey).getString(); - - return screen.children().stream() - .filter(child -> child instanceof AbstractWidget) - .map(AbstractWidget.class::cast) - .filter(widget -> widget.getMessage().getString().equals(translatedName)) - .findAny(); - } - - public static void playClackSound() { - minecraft.getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F)); - } - - public static void playFocusChangeSound() { - minecraft.getSoundManager().play(SimpleSoundInstance.forUI(ControlifyClientSounds.SCREEN_FOCUS_CHANGE.get(), 1.0F)); - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessorFactory.java b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessorFactory.java deleted file mode 100644 index 567acfdbe..000000000 --- a/src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessorFactory.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.screenop; - -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.minecraft.client.gui.screens.Screen; - -import java.util.Map; -import java.util.function.Function; - -@SuppressWarnings("unchecked") -public final class ScreenProcessorFactory { - private static final Map, Factory> factories = new Object2ObjectOpenHashMap<>(); - - private ScreenProcessorFactory() { - } - - public static ScreenProcessor createForScreen(T screen) { - Class screenClass = screen.getClass(); - while (!factories.containsKey(screenClass) && screenClass != Screen.class) { - screenClass = (Class) screenClass.getSuperclass(); - } - - return ((Factory) factories.getOrDefault(screenClass, ScreenProcessorFactory::createDefault)).apply(screen); - } - - public static void registerProvider(Class screenClass, Factory factory) { - factories.put(screenClass, factory); - } - - private static ScreenProcessor createDefault(T screen) { - return new ScreenProcessor<>(screen); - } - - public interface Factory extends Function> { - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessorProvider.java b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessorProvider.java deleted file mode 100644 index d2c52c79f..000000000 --- a/src/main/java/dev/isxander/controlify/impl/screenop/ScreenProcessorProvider.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2026 isXander - * This file is part of Controlify. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ -package dev.isxander.controlify.impl.screenop; - -import net.minecraft.client.gui.screens.Screen; -import org.jetbrains.annotations.NotNull; - - -public interface ScreenProcessorProvider { - ScreenProcessor screenProcessor(); - - static ScreenProcessor provide(@NotNull Screen screen) { - return ((ScreenProcessorProvider) screen).screenProcessor(); - } - - static void registerProvider(@NotNull Class screenClass, @NotNull ScreenProcessorFactory.Factory factory) { - ScreenProcessorFactory.registerProvider(screenClass, factory); - } -} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/ScreenRuntime.java b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenRuntime.java new file mode 100644 index 000000000..336432d3a --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenRuntime.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop; + +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.utils.MinecraftUtil; +import dev.isxander.controlify.mixins.feature.virtualmouse.MouseHandlerAccessor; +import net.minecraft.client.Minecraft; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +/// Host integration only: activates sidecars and dispatches frames. Cursor input +/// and rendering live in the registered screen behaviours. +public final class ScreenRuntime { + private static @Nullable ScreenContextImpl active; + private static boolean changing; + + private ScreenRuntime() { + } + + public static @Nullable ScreenContextImpl current() { + if (!ScreenBehaviourRegistryImpl.INSTANCE.isReady()) { + return null; + } + if ((active == null ? null : active.screen()) != MinecraftUtil.getScreen()) { + onScreenChanged(); + } + return active; + } + + public static Optional virtualMouse() { + return Optional.ofNullable(current()).map(ScreenContextImpl::virtualMouse); + } + + public static void onScreenChanged() { + if (changing || !ScreenBehaviourRegistryImpl.INSTANCE.isReady()) { + return; + } + if (active != null && active.screen() == MinecraftUtil.getScreen()) { + return; + } + changing = true; + try { + var previous = active; + active = null; + boolean hadCursor = previous != null && previous.virtualMouse().isEnabled(); + if (previous != null) { + previous.onScreenActiveChanged(false); + } + var screen = MinecraftUtil.getScreen(); + if (screen != null) { + active = ScreenContextImpl.get(screen); + active.onScreenActiveChanged(true); + if (Controlify.instance().currentInputMode().isController()) { + Controlify.instance().hideMouse(true, !active.virtualMouse().isEnabled()); + } + } else if (hadCursor) { + var mouse = Minecraft.getInstance().mouseHandler; + ((MouseHandlerAccessor) mouse).controlify$setMouseGrabbed(false); + mouse.grabMouse(); + } + } finally { + changing = false; + } + if ((active == null ? null : active.screen()) != MinecraftUtil.getScreen()) { + onScreenChanged(); + } + } + + public static void update() { + var context = current(); + if (context != null) { + context.update(Minecraft.getInstance().getDeltaTracker().getRealtimeDeltaTicks()); + } + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/ScreenWidgets.java b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenWidgets.java new file mode 100644 index 000000000..9ead79e01 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/ScreenWidgets.java @@ -0,0 +1,47 @@ +package dev.isxander.controlify.impl.screenop; + +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.events.ContainerEventHandler; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.contents.TranslatableContents; + +import java.util.ArrayDeque; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.Optional; +import java.util.function.Predicate; + +/// Finds live widgets after a screen has rebuilt them, including nested layouts. +public final class ScreenWidgets { + private ScreenWidgets() { + } + + public static Optional find(Screen screen, String translationKey) { + return find(screen, widget -> widget.getMessage().getContents() instanceof TranslatableContents contents + && contents.getKey().equals(translationKey)); + } + + public static Optional find(Screen screen, Component message) { + return find(screen, widget -> widget.getMessage().equals(message)); + } + + private static Optional find(Screen screen, Predicate predicate) { + var pending = new ArrayDeque(screen.children()); + var visited = Collections.newSetFromMap(new IdentityHashMap()); + while (!pending.isEmpty()) { + var element = pending.removeFirst(); + if (!visited.add(element)) { + continue; + } + if (element instanceof AbstractWidget widget && predicate.test(widget)) { + return Optional.of(widget); + } + if (element instanceof ContainerEventHandler container) { + pending.addAll(container.children()); + } + } + return Optional.empty(); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/VirtualMouseState.java b/src/main/java/dev/isxander/controlify/impl/screenop/VirtualMouseState.java new file mode 100644 index 000000000..5b57399ea --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/VirtualMouseState.java @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop; + +import com.mojang.blaze3d.platform.InputConstants; +import com.mojang.datafixers.util.Pair; +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.screenop.*; +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.action.ControlifyActions; +import dev.isxander.controlify.mixins.feature.virtualmouse.MouseHandlerAccessor; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.navigation.ScreenAxis; +import net.minecraft.client.gui.navigation.ScreenDirection; +import net.minecraft.client.input.MouseButtonInfo; +import net.minecraft.util.Mth; +import org.jetbrains.annotations.Nullable; +import org.joml.*; + +import java.lang.Math; +import java.util.*; + +/// Shared cursor state and operations. Input interpretation, frame scheduling and +/// rendering are separate screen behaviours. +public final class VirtualMouseState implements VirtualMouse { + private final ScreenContextImpl context; + private final Minecraft minecraft = Minecraft.getInstance(); + private VirtualMouseMode mode = VirtualMouseMode.DEFAULT; + private boolean modeConfigured; + private boolean active; + private boolean enabled; + private boolean suspended; + private double targetX; + private double targetY; + private double currentX; + private double currentY; + private double scrollX; + private double scrollY; + private boolean leftClickDown; + private boolean rightClickDown; + private Set snapPoints = Set.of(); + private @Nullable SnapPoint lastSnappedPoint; + private final Vector2d previousMoveImpulse = new Vector2d(); + + VirtualMouseState(ScreenContextImpl context) { + this.context = context; + } + + @Override + public VirtualMouseMode mode() { + return mode; + } + + @Override + public void setMode(VirtualMouseMode mode) { + Objects.requireNonNull(mode); + modeConfigured = true; + if (this.mode != mode) { + releaseClicks(); + if (!mode.scrolls()) { + preventScrollingThisTick(); + } + } + this.mode = mode; + synchronizeMode(); + } + + /// A built-in default must not replace a mode explicitly selected by a more + /// specific factory, including an explicit selection of DEFAULT. + void setDefaultMode(VirtualMouseMode mode) { + if (!modeConfigured) { + setMode(mode); + } + } + + @Override + public boolean isEnabled() { + return enabled; + } + + public boolean isRequired() { + if (!Controlify.instance().currentInputMode().isController() || Controlify.instance().getCurrentController().isEmpty()) { + return false; + } + return switch (mode) { + case DISABLED -> false; + case ENABLED, CURSOR_ONLY, CURSOR_SCROLL -> true; + case DEFAULT -> Controlify.instance().config().getSettings().globalSettings().virtualMouseScreens + .stream().anyMatch(type -> type.isInstance(context.screen())); + }; + } + + public void activate() { + active = true; + suspended = false; + initializePosition(); + synchronizeMode(); + } + + public void deactivate() { + active = false; + setEnabled(false); + snapPoints = Set.of(); + lastSnappedPoint = null; + } + + public void setSuspended(boolean suspended) { + this.suspended = suspended; + synchronizeMode(); + } + + public void synchronizeMode() { + setEnabled(active && !suspended && isRequired()); + } + + private void setEnabled(boolean enabled) { + if (this.enabled == enabled) { + return; + } + this.enabled = enabled; + if (enabled) { + initializePosition(); + } else { + releaseClicks(); + scrollX = scrollY = 0; + previousMoveImpulse.zero(); + targetX = currentX; + targetY = currentY; + lastSnappedPoint = null; + } + } + + private void initializePosition() { + double x = minecraft.mouseHandler.xpos(), y = minecraft.mouseHandler.ypos(); + if (x == -50 && y == -50) { + x = minecraft.getWindow().getScreenWidth() / 2.0; + y = minecraft.getWindow().getScreenHeight() / 2.0; + } + currentX = targetX = x; + currentY = targetY = y; + } + + public Vector2d previousMoveImpulse() { + return new Vector2d(previousMoveImpulse); + } + + public void setMoveImpulse(Vector2d impulse) { + previousMoveImpulse.set(impulse); + } + + public void releaseUnlatchedClicks(Controller controller) { + if (leftClickDown && !ControlifyActions.VMOUSE_LCLICK.on(controller).latch().isLatchActive()) { + leftClickDown = false; + simulateMousePress(InputConstants.MOUSE_BUTTON_LEFT, InputConstants.RELEASE, 0); + } + if (rightClickDown && !ControlifyActions.VMOUSE_RCLICK.on(controller).latch().isLatchActive()) { + rightClickDown = false; + simulateMousePress(InputConstants.MOUSE_BUTTON_RIGHT, InputConstants.RELEASE, 0); + } + } + + public void refreshSnapPoints() { + snapPoints = context.collectSnapPoints(); + } + + public Set snapPoints() { + return snapPoints; + } + + public @Nullable SnapPoint lastSnappedPoint() { + return lastSnappedPoint; + } + + public double x() { + return currentX; + } + + public double y() { + return currentY; + } + + public void move(double x, double y) { + targetX = Mth.clamp(targetX + x, 0, minecraft.getWindow().getWidth()); + targetY = Mth.clamp(targetY + y, 0, minecraft.getWindow().getHeight()); + } + + public void updatePosition(float delta) { + double newX = Mth.lerp(Math.clamp(delta, 0, 1), currentX, targetX); + double newY = Mth.lerp(Math.clamp(delta, 0, 1), currentY, targetY); + if (newX != currentX || newY != currentY) { + double relX = newX - currentX, relY = newY - currentY; + currentX = newX; + currentY = newY; + ((MouseHandlerAccessor) minecraft.mouseHandler).controlify$invokeOnMove( + minecraft.getWindow().handle(), newX, newY /*? if >=26.3 {*/, relX, relY/*?}*/); + } + } + + public void addScroll(double y) { + scrollY += y; + } + + public void updateScroll(float delta) { + double y = scrollY * Math.clamp(delta, 0, 1), x = scrollX * Math.clamp(delta, 0, 1); + scrollY -= y; + scrollX -= x; + if (Math.abs(scrollY) < 0.01) { + scrollY = 0; + } + if (Math.abs(scrollX) < 0.01) { + scrollX = 0; + } + if (x != 0 || y != 0) { + ((MouseHandlerAccessor) minecraft.mouseHandler).controlify$invokeOnScroll(minecraft.getWindow().handle(), x, y); + } + } + + private void simulateMousePress(int button, int action, int modifiers) { + ((MouseHandlerAccessor) minecraft.mouseHandler).controlify$invokeOnButton( + minecraft.getWindow().handle(), new MouseButtonInfo(button, modifiers), action); + } + + public void releaseClicks() { + boolean left = leftClickDown, right = rightClickDown; + leftClickDown = rightClickDown = false; + if (left) { + simulateMousePress(InputConstants.MOUSE_BUTTON_LEFT, InputConstants.RELEASE, 0); + } + if (right) { + simulateMousePress(InputConstants.MOUSE_BUTTON_RIGHT, InputConstants.RELEASE, 0); + } + } + + @Override + public void emulateClicks(Controller controller) { + if (!isEnabled() || !context.isCurrentScreen()) { + return; + } + + if (ControlifyActions.VMOUSE_LCLICK.on(controller).pulse().consumeAllPulses()) { + leftClickDown = true; + this.simulateMousePress(InputConstants.MOUSE_BUTTON_LEFT, InputConstants.PRESS, 0); + } else if (leftClickDown && !ControlifyActions.VMOUSE_LCLICK.on(controller).latch().isLatchActive()) { + this.simulateMousePress(InputConstants.MOUSE_BUTTON_LEFT, InputConstants.RELEASE, 0); + leftClickDown = false; + } + + if (!isEnabled() || !context.isCurrentScreen()) { + return; + } + if (ControlifyActions.VMOUSE_RCLICK.on(controller).pulse().consumeAllPulses()) { + rightClickDown = true; + this.simulateMousePress(InputConstants.MOUSE_BUTTON_RIGHT, InputConstants.PRESS, 0); + } else if (rightClickDown && !ControlifyActions.VMOUSE_RCLICK.on(controller).latch().isLatchActive()) { + this.simulateMousePress(InputConstants.MOUSE_BUTTON_RIGHT, InputConstants.RELEASE, 0); + rightClickDown = false; + } + + while (isEnabled() && context.isCurrentScreen() && ControlifyActions.VMOUSE_SHIFT_CLICK.on(controller).pulse().consumePulse()) { + this.simulateMousePress(InputConstants.MOUSE_BUTTON_LEFT, InputConstants.PRESS, InputConstants.MOD_SHIFT); + this.simulateMousePress(InputConstants.MOUSE_BUTTON_LEFT, InputConstants.RELEASE, InputConstants.MOD_SHIFT); + } + } + + public void snapToClosestPoint() { + if (!enabled || !context.isCurrentScreen()) { + return; + } + refreshSnapPoints(); + var window = minecraft.getWindow(); + var scaleFactor = new Vector2d((double) window.getGuiScaledWidth() / (double) window.getScreenWidth(), (double) window.getGuiScaledHeight() / (double) window.getScreenHeight()); + var target = new Vector2d(targetX, targetY).mul(scaleFactor); + + if (lastSnappedPoint != null) { + if (lastSnappedPoint.position().distanceSquared(new Vector2i(target, RoundingMode.FLOOR)) > (long) lastSnappedPoint.range() * lastSnappedPoint.range()) { + lastSnappedPoint = null; + } + } + + var closestSnapPoint = snapPoints.stream() + .filter(snapPoint -> !snapPoint.equals(lastSnappedPoint)) // don't snap to the point currently over snapped point + .map(snapPoint -> new Pair<>(snapPoint, snapPoint.position().distanceSquared(new Vector2i(target, RoundingMode.FLOOR)))) // map with distance to current pos + .filter(point -> point.getSecond() <= (long) point.getFirst().range() * point.getFirst().range()) // filter out of range options + .min(Comparator.comparingLong(Pair::getSecond)) // find the closest point + .orElse(new Pair<>(null, Long.MAX_VALUE)).getFirst(); // retrieve point + + if (closestSnapPoint != null) { + snapToPoint(closestSnapPoint, scaleFactor); + } + } + + public void snapInDirection(ScreenDirection direction) { + if (!enabled || !context.isCurrentScreen()) { + return; + } + refreshSnapPoints(); + var window = minecraft.getWindow(); + var scaleFactor = new Vector2d((double) window.getGuiScaledWidth() / (double) window.getScreenWidth(), (double) window.getGuiScaledHeight() / (double) window.getScreenHeight()); + var target = new Vector2d(targetX, targetY).mul(scaleFactor); + + Optional closestSnapPoint = findOrthogonalSnapPoint(lastSnappedPoint, direction, target, snapPoints) + .or(() -> { + Vector2d orthogonalTarget = new Vector2d(target); + switch (direction) { + case UP -> orthogonalTarget.y = window.getGuiScaledHeight(); + case DOWN -> orthogonalTarget.y = 0; + case LEFT -> orthogonalTarget.x = window.getGuiScaledWidth(); + case RIGHT -> orthogonalTarget.x = 0; + } + + return findOrthogonalSnapPoint(lastSnappedPoint, direction, orthogonalTarget, snapPoints); + }); + + closestSnapPoint.ifPresent(snapPoint -> { + snapToPoint(snapPoint, scaleFactor); + }); + } + + private static Optional findOrthogonalSnapPoint(SnapPoint from, ScreenDirection direction, Vector2d target, Collection snapPoints) { + return snapPoints.stream() + .filter(snapPoint -> !snapPoint.equals(from)) // don't snap to the point currently over snapped point + .map(snapPoint -> new Pair<>(snapPoint, new Vector2d(snapPoint.position().x() - target.x(), snapPoint.position().y() - target.y()))) // map with distance to current pos + // filter points that are not in the correct direction + .filter(pair -> { + Vector2d dist = pair.getSecond(); + + double axis = direction.getAxis() == ScreenAxis.HORIZONTAL ? dist.x : dist.y; + double positive = direction.isPositive() ? 1 : -1; + + return axis * positive > 0; + }) + .filter(pair -> { + SnapPoint snapPoint = pair.getFirst(); + Vector2d dist = pair.getSecond(); + + // distance in the correct orthogonal direction + double distance = Math.abs(direction.getAxis() == ScreenAxis.HORIZONTAL ? dist.x : dist.y); + // distance in the incorrect orthogonal direction + double deviation = Math.abs(direction.getAxis() == ScreenAxis.HORIZONTAL ? dist.y : dist.x); + + // punish deviation significantly + pair.getSecond().set(distance, deviation * 4); + + // reject if the deviation is double the correct direction away + return distance >= snapPoint.range() && (deviation < distance * 2); + }) + // pick the closest point + .min(Comparator.comparingDouble(pair -> { + Vector2d distDev = pair.getSecond(); + // x = dist, y = deviation + return distDev.x + distDev.y; + })) + .map(Pair::getFirst); + } + + public void snapToPoint(SnapPoint snapPoint, Vector2dc scaleFactor) { + lastSnappedPoint = snapPoint; + + 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, newX, newY /*? if >=26.3 {*/, relX, relY/*?}*/); + + currentX = targetX = newX; + currentY = targetY = newY; + } + + @Override + public int getCurrentX(float deltaTime) { + return (int) Mth.lerp(deltaTime, currentX, targetX); + } + + @Override + public int getCurrentY(float deltaTime) { + return (int) Mth.lerp(deltaTime, currentY, targetY); + } + + @Override + public void preventScrollingThisTick() { + scrollX = scrollY = 0; + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/ButtonBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/ButtonBehaviour.java new file mode 100644 index 000000000..df198b78c --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/ButtonBehaviour.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop.behaviours.element; + +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.screenop.ElementBehaviour; +import dev.isxander.controlify.api.screenop.ScreenContext; +import dev.isxander.controlify.impl.action.ControlifyActions; + +import java.util.function.Consumer; + +public final class ButtonBehaviour implements ElementBehaviour { + private final Runnable press; + private final Consumer> sound; + + public ButtonBehaviour(Runnable press) { + this(press, ScreenContext::playClickSound); + } + + public ButtonBehaviour(Runnable press, Runnable sound) { + this(press, screen -> sound.run()); + } + + private ButtonBehaviour(Runnable press, Consumer> sound) { + this.press = press; + this.sound = sound; + } + + @Override + public void handleInteraction(ScreenContext screen, Controller controller, Runnable next) { + if (!screen.isVirtualMouseEnabled() && ControlifyActions.GUI_PRESS.on(controller).pulse().consumeAllPulses()) { + sound.accept(screen); + screen.playNavigationHaptic(controller); + press.run(); + return; + } + next.run(); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/CycleBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/CycleBehaviour.java new file mode 100644 index 000000000..d572b5d8b --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/CycleBehaviour.java @@ -0,0 +1,49 @@ +package dev.isxander.controlify.impl.screenop.behaviours.element; + +import dev.isxander.controlify.api.action.ActionAccessor; +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.screenop.ElementBehaviour; +import dev.isxander.controlify.api.screenop.ScreenContext; +import dev.isxander.controlify.impl.action.ControlifyActions; +import dev.isxander.controlify.impl.utils.MinecraftUtil; + +import java.util.function.IntConsumer; + +/// Discrete options share secondary navigation and repeated activation semantics. +public final class CycleBehaviour implements ElementBehaviour { + private final IntConsumer cycle; + + public CycleBehaviour(IntConsumer cycle) { + this.cycle = cycle; + } + + @Override + public void handleNavigation(ScreenContext screen, Controller controller, Runnable next) { + if (screen.isVirtualMouseEnabled()) { + next.run(); + return; + } + boolean handled = consume(screen, controller, ControlifyActions.GUI_SECONDARY_NAVI_LEFT, -1); + handled |= consume(screen, controller, ControlifyActions.GUI_SECONDARY_NAVI_RIGHT, 1); + if (!handled) { + next.run(); + } + } + + @Override + public void handleInteraction(ScreenContext screen, Controller controller, Runnable next) { + if (screen.isVirtualMouseEnabled() || !consume(screen, controller, ControlifyActions.GUI_PRESS, 1)) { + next.run(); + } + } + + private boolean consume(ScreenContext screen, Controller controller, ActionAccessor action, int direction) { + boolean handled = false; + while (MinecraftUtil.getScreen() == screen.screen() && action.on(controller).pulse().consumePulse()) { + cycle.accept(direction); + screen.playNavigationHaptic(controller); + handled = true; + } + return handled; + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/DefaultContainerBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/DefaultContainerBehaviour.java new file mode 100644 index 000000000..d14f0af62 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/DefaultContainerBehaviour.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop.behaviours.element; + +import dev.isxander.controlify.api.screenop.ElementBehaviour; +import dev.isxander.controlify.api.screenop.ScreenContext; +import dev.isxander.controlify.api.screenop.SnapPoint; +import net.minecraft.client.gui.components.events.ContainerEventHandler; + +import java.util.List; +import java.util.function.Consumer; + +public final class DefaultContainerBehaviour implements ElementBehaviour { + private final ContainerEventHandler container; + + public DefaultContainerBehaviour(ContainerEventHandler container) { + this.container = container; + } + + @Override + public void collectSnapPoints(ScreenContext screen, Consumer output, Runnable next) { + for (var child : List.copyOf(container.children())) { + screen.elementBehaviour(child).collectSnapPoints(screen, output, () -> {}); + } + // Child targets replace the generic centre target of the container itself. + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/DefaultEditBoxBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/DefaultEditBoxBehaviour.java new file mode 100644 index 000000000..95c677daf --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/DefaultEditBoxBehaviour.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop.behaviours.element; + +import dev.isxander.controlify.api.gui.keyboard.InputTarget; +import dev.isxander.controlify.api.gui.keyboard.KeyboardLayouts; +import dev.isxander.controlify.api.gui.keyboard.KeyboardPositioner; +import dev.isxander.controlify.api.screenop.ElementBehaviour; +import dev.isxander.controlify.api.screenop.ElementKeyboardBehaviour; +import dev.isxander.controlify.api.screenop.ScreenContext; +import dev.isxander.controlify.impl.gui.keyboard.EditBoxInputTarget; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; + +import java.util.Optional; + +public final class DefaultEditBoxBehaviour implements ElementBehaviour { + private final EditBox editBox; + private final Identifier layout; + private final InputTarget inputTarget; + + public DefaultEditBoxBehaviour(EditBox editBox) { + this(editBox, KeyboardLayouts.SIMPLE, new EditBoxInputTarget(editBox)); + } + + public DefaultEditBoxBehaviour(EditBox editBox, Identifier layout, InputTarget inputTarget) { + this.editBox = editBox; + this.layout = layout; + this.inputTarget = inputTarget; + } + + @Override + public @NonNull Optional keyboardBehaviour(@NonNull ScreenContext screen) { + return Optional.of(new ElementKeyboardBehaviour.Handled( + layout, inputTarget, + KeyboardPositioner.aboveOrBelowWidgetPositioner( + (int) (screen.screen().width * 0.8f), (int) (screen.screen().height * 0.4f), + 1, editBox::getRectangle))); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/DefaultWidgetBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/DefaultWidgetBehaviour.java new file mode 100644 index 000000000..aebceb44d --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/DefaultWidgetBehaviour.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop.behaviours.element; + +import dev.isxander.controlify.api.screenop.ElementBehaviour; +import dev.isxander.controlify.api.screenop.ScreenContext; +import dev.isxander.controlify.api.screenop.SnapPoint; +import net.minecraft.client.gui.components.AbstractWidget; + +import java.util.function.Consumer; + +public final class DefaultWidgetBehaviour implements ElementBehaviour { + private final AbstractWidget widget; + + public DefaultWidgetBehaviour(AbstractWidget widget) { + this.widget = widget; + } + + @Override + public void collectSnapPoints(ScreenContext screen, Consumer output, Runnable next) { + if (widget.visible && widget.active) { + next.run(); + } + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/LayoutElementSnapBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/LayoutElementSnapBehaviour.java new file mode 100644 index 000000000..ecbd70df6 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/LayoutElementSnapBehaviour.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop.behaviours.element; + +import dev.isxander.controlify.api.screenop.ElementBehaviour; +import dev.isxander.controlify.api.screenop.ScreenContext; +import dev.isxander.controlify.api.screenop.SnapPoint; +import net.minecraft.client.gui.layouts.LayoutElement; + +import java.util.function.Consumer; + +/// A centre target derived solely from layout geometry. Registration determines +/// which interactive element types use this behaviour. +public final class LayoutElementSnapBehaviour implements ElementBehaviour { + private final LayoutElement element; + + public LayoutElementSnapBehaviour(LayoutElement element) { + this.element = element; + } + + @Override + public void collectSnapPoints(ScreenContext screen, Consumer output, Runnable next) { + output.accept(new SnapPoint( + element.getX() + element.getWidth() / 2, + element.getY() + element.getHeight() / 2, + Math.min(element.getWidth(), element.getHeight()) / 2 + 10 + )); + next.run(); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/SliderBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/SliderBehaviour.java new file mode 100644 index 000000000..078bc8499 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/SliderBehaviour.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop.behaviours.element; + +import dev.isxander.controlify.api.action.ActionAccessor; +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.screenop.ElementBehaviour; +import dev.isxander.controlify.api.screenop.ScreenContext; +import dev.isxander.controlify.impl.action.ControlifyActions; +import dev.isxander.controlify.impl.utils.MinecraftUtil; + +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; +import java.util.function.IntConsumer; + +public final class SliderBehaviour implements ElementBehaviour { + private final IntConsumer adjust; + private final BooleanSupplier isEditing; + private final Consumer setEditing; + private boolean editing; + + public SliderBehaviour(IntConsumer adjust) { + this.adjust = adjust; + this.isEditing = () -> editing; + this.setEditing = editing -> this.editing = editing; + } + + public SliderBehaviour(IntConsumer adjust, BooleanSupplier isEditing, Consumer setEditing) { + this.adjust = adjust; + this.isEditing = isEditing; + this.setEditing = setEditing; + } + + @Override + public void handleNavigation(ScreenContext screen, Controller controller, Runnable next) { + if (screen.isVirtualMouseEnabled()) { + next.run(); + return; + } + + boolean handled = adjustPulses(screen, controller, ControlifyActions.GUI_SECONDARY_NAVI_LEFT, -1); + handled |= adjustPulses(screen, controller, ControlifyActions.GUI_SECONDARY_NAVI_RIGHT, 1); + if (isEditing.getAsBoolean()) { + handled |= adjustPulses(screen, controller, ControlifyActions.GUI_NAVI_LEFT, -1); + handled |= adjustPulses(screen, controller, ControlifyActions.GUI_NAVI_RIGHT, 1); + } + if (!handled) { + next.run(); + } + } + + private boolean adjustPulses(ScreenContext screen, Controller controller, ActionAccessor action, int direction) { + boolean handled = false; + while (MinecraftUtil.getScreen() == screen.screen() && action.on(controller).pulse().consumePulse()) { + adjust.accept(direction); + screen.playNavigationHaptic(controller); + handled = true; + } + return handled; + } + + @Override + public void handleInteraction(ScreenContext screen, Controller controller, Runnable next) { + if (screen.isVirtualMouseEnabled()) { + next.run(); + return; + } + if (isEditing.getAsBoolean() && ControlifyActions.GUI_BACK.on(controller).pulse().consumeAllPulses()) { + // Back wins if both were pressed, so Press cannot immediately reopen editing. + ControlifyActions.GUI_PRESS.on(controller).pulse().consumeAllPulses(); + setEditing.accept(false); + return; + } + if (ControlifyActions.GUI_PRESS.on(controller).pulse().consumeAllPulses()) { + setEditing.accept(!isEditing.getAsBoolean()); + screen.playNavigationHaptic(controller); + return; + } + next.run(); + } + + @Override + public void onFocusChanged(ScreenContext screen, boolean focused) { + setEditing.accept(false); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/package-info.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/package-info.java new file mode 100644 index 000000000..f5d4fb63b --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/element/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package dev.isxander.controlify.impl.screenop.behaviours.element; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/BackButtonBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/BackButtonBehaviour.java new file mode 100644 index 000000000..efe2f16db --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/BackButtonBehaviour.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop.behaviours.screen; + +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.screenop.*; +import dev.isxander.controlify.impl.action.ControlifyActions; +import dev.isxander.controlify.impl.screenop.ScreenContextImpl; + +public final class BackButtonBehaviour implements ScreenBehaviour { + private final ScreenContextImpl context; + + public BackButtonBehaviour(ScreenContextImpl context) { + this.context = context; + } + + @Override + public void handleInteraction(Controller controller, Runnable next) { + if (context.screen().shouldCloseOnEsc() && ControlifyActions.GUI_BACK.on(controller).pulse().consumeAllPulses()) { + context.playClickSound(); + context.screen().onClose(); + } + next.run(); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/FocusInputBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/FocusInputBehaviour.java new file mode 100644 index 000000000..db47db630 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/FocusInputBehaviour.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop.behaviours.screen; + +import com.mojang.blaze3d.platform.InputConstants; +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.screenop.*; +import dev.isxander.controlify.impl.action.ControlifyActions; +import dev.isxander.controlify.impl.screenop.ScreenContextImpl; +import net.minecraft.client.InputType; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.navigation.FocusNavigationEvent; +import net.minecraft.client.gui.navigation.ScreenDirection; +import net.minecraft.client.input.KeyEvent; + +import java.util.List; + +/// Navigation and activation through the focused element path. +public final class FocusInputBehaviour implements ScreenBehaviour { + private final ScreenContextImpl context; + + public FocusInputBehaviour(ScreenContextImpl context) { + this.context = context; + } + + @Override + public void handleNavigation(Controller controller, Runnable next) { + if (context.screen().getFocused() == null) { + context.setInitialFocus(); + } + for (ScreenDirection direction : List.of(ScreenDirection.RIGHT, ScreenDirection.LEFT, ScreenDirection.UP, ScreenDirection.DOWN)) { + var action = switch (direction) { + case RIGHT -> ControlifyActions.GUI_NAVI_RIGHT; + case LEFT -> ControlifyActions.GUI_NAVI_LEFT; + case UP -> ControlifyActions.GUI_NAVI_UP; + case DOWN -> ControlifyActions.GUI_NAVI_DOWN; + }; + while (context.isCurrentScreen() && action.on(controller).pulse().consumePulse()) { + var path = context.screen().nextFocusPath(new FocusNavigationEvent.ArrowNavigation(direction)); + if (path != null) { + Minecraft.getInstance().setLastInputType(InputType.KEYBOARD_ARROW); + context.changeFocus(path); + context.notifyNavigation(controller); + } + } + } + next.run(); + } + + @Override + public void handleInteraction(Controller controller, Runnable next) { + if (ControlifyActions.GUI_PRESS.on(controller).pulse().consumeAllPulses()) { + var focused = context.focusPath(); + // A widget can be disabled or hidden while it still holds focus. Do not + // reactivate it through the generic keyboard path after its behaviour skips. + boolean unavailable = focused.stream().anyMatch(element -> + element instanceof AbstractWidget widget && (!widget.active || !widget.visible)); + if (!unavailable && !context.tryOpenKeyboard(controller, focused.isEmpty() ? null : focused.getFirst())) { + context.screen().keyPressed(new KeyEvent(InputConstants.KEY_RETURN, 0, 0)); + } + } + next.run(); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/FocusManagementBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/FocusManagementBehaviour.java new file mode 100644 index 000000000..411d2f8fd --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/FocusManagementBehaviour.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop.behaviours.screen; + +import dev.isxander.controlify.api.InputMode; +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.screenop.*; +import dev.isxander.controlify.impl.screenop.ScreenContextImpl; +import net.minecraft.client.InputType; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.EditBox; +import org.jetbrains.annotations.Nullable; + +public final class FocusManagementBehaviour implements ScreenBehaviour { + private final ScreenContextImpl context; + private @Nullable InputMode previousMode; + private boolean previousVirtualMouse; + + public FocusManagementBehaviour(ScreenContextImpl context) { + this.context = context; + } + + @Override + public void update(float delta, Runnable next) { + synchronizeInputMode(); + next.run(); + } + + @Override + public void handleNavigation(Controller controller, Runnable next) { + synchronizeInputMode(); + next.run(); + } + + @Override + public void onScreenActiveChanged(boolean active) { + if (active) { + previousMode = null; + synchronizeInputMode(); + } + } + + @Override + public void onWidgetRebuild(Runnable next) { + if (context.inputMode().isController() && !context.isVirtualMouseEnabled()) { + Minecraft.getInstance().setLastInputType(InputType.KEYBOARD_ARROW); + } + synchronizeInputMode(); + next.run(); + } + + private void synchronizeInputMode() { + if (!context.isCurrentScreen()) { + return; + } + InputMode mode = context.inputMode(); + boolean mouse = context.isVirtualMouseEnabled(); + if (mode == previousMode && mouse == previousVirtualMouse) { + return; + } + previousMode = mode; + previousVirtualMouse = mouse; + if (mouse || mode == InputMode.KEYBOARD_MOUSE) { + if (mouse || context.focusPath().stream().noneMatch(EditBox.class::isInstance)) { + context.clearFocus(); + } + } else { + context.setInitialFocus(); + } + } + +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/FocusedElementBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/FocusedElementBehaviour.java new file mode 100644 index 000000000..0d2bf7cc4 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/FocusedElementBehaviour.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop.behaviours.screen; + +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.screenop.*; +import dev.isxander.controlify.impl.screenop.ScreenContextImpl; + +public final class FocusedElementBehaviour implements ScreenBehaviour { + private final ScreenContextImpl context; + + public FocusedElementBehaviour(ScreenContextImpl context) { + this.context = context; + } + + @Override + public void handleNavigation(Controller controller, Runnable next) { + context.dispatchElements(controller, true, next); + } + + @Override + public void handleInteraction(Controller controller, Runnable next) { + context.dispatchElements(controller, false, next); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/ScreenSnapPointBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/ScreenSnapPointBehaviour.java new file mode 100644 index 000000000..ac473ff3c --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/ScreenSnapPointBehaviour.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop.behaviours.screen; + +import dev.isxander.controlify.api.screenop.*; +import dev.isxander.controlify.impl.screenop.ScreenContextImpl; + +import java.util.List; +import java.util.function.Consumer; + +public final class ScreenSnapPointBehaviour implements ScreenBehaviour { + private final ScreenContextImpl context; + + public ScreenSnapPointBehaviour(ScreenContextImpl context) { + this.context = context; + } + + @Override + public void collectSnapPoints(Consumer output, Runnable next) { + for (var element : List.copyOf(context.screen().children())) { + context.elementBehaviour(element).collectSnapPoints(context, output, () -> {}); + } + next.run(); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/TabNavigationBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/TabNavigationBehaviour.java new file mode 100644 index 000000000..45f931abf --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/TabNavigationBehaviour.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop.behaviours.screen; + +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.screenop.*; +import dev.isxander.controlify.impl.action.ControlifyActions; +import dev.isxander.controlify.impl.screenop.ScreenContextImpl; +import dev.isxander.controlify.mixins.feature.screenop.impl.outofgame.TabNavigationBarAccessor; +import net.minecraft.client.gui.components.tabs.TabNavigationBar; + +import java.util.Optional; + +public final class TabNavigationBehaviour implements ScreenBehaviour { + private final ScreenContextImpl context; + + public TabNavigationBehaviour(ScreenContextImpl context) { + this.context = context; + } + + @Override + public void handleNavigation(Controller controller, Runnable next) { + for (int direction : new int[]{1, -1}) { + var action = direction == 1 ? ControlifyActions.GUI_NEXT_TAB : ControlifyActions.GUI_PREV_TAB; + while (context.isCurrentScreen() && action.on(controller).pulse().consumePulse()) { + Optional navBar = context.screen().children() + .stream() + .filter(TabNavigationBar.class::isInstance) + .map(TabNavigationBar.class::cast) + .findFirst(); + if (navBar.isEmpty()) { + break; + } + var accessor = (TabNavigationBarAccessor) navBar.get(); + var tabs = accessor.controlify$getTabs(); + if (!tabs.isEmpty()) { + int current = tabs.indexOf(accessor.controlify$getTabManager().getCurrentTab()); + navBar.get().selectTab(Math.floorMod(current + direction, tabs.size()), true); + context.synchronizeFocus(); + } + } + } + next.run(); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/package-info.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/package-info.java new file mode 100644 index 000000000..8117096d4 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/screen/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package dev.isxander.controlify.impl.screenop.behaviours.screen; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/BundleItemSlotControllerAction.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/BundleItemSlotControllerAction.java new file mode 100644 index 000000000..7b015f666 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/BundleItemSlotControllerAction.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop.behaviours.vanilla; + +import dev.isxander.controlify.impl.Controlify; +import dev.isxander.controlify.impl.action.ControlifyActions; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import net.minecraft.client.gui.navigation.ScreenDirection; +import net.minecraft.util.Mth; +import net.minecraft.world.item.BundleItem; +import net.minecraft.world.item.ItemStack; + +public class BundleItemSlotControllerAction { + public static boolean onControllerInput( + ItemStack stack, + int hoveredSlotIndex, + ControllerImpl controller, + SelectedBundleItemConsumer consumer + ) { + int uniqueItems = BundleItem.getNumberOfItemsToShow(stack); + + if (uniqueItems > 0) { + dev.isxander.controlify.impl.screenop.ScreenRuntime.virtualMouse().ifPresent(mouse -> mouse.preventScrollingThisTick()); + + boolean handled = false; + for (var direction : ScreenDirection.values()) { + var action = switch (direction) { + case UP -> ControlifyActions.GUI_SECONDARY_NAVI_UP; + case DOWN -> ControlifyActions.GUI_SECONDARY_NAVI_DOWN; + case LEFT -> ControlifyActions.GUI_SECONDARY_NAVI_LEFT; + case RIGHT -> ControlifyActions.GUI_SECONDARY_NAVI_RIGHT; + }; + while (action.on(controller).pulse().consumePulse()) { + int offsetX = direction == ScreenDirection.LEFT ? -1 + : direction == ScreenDirection.RIGHT ? 1 : 0; + int offsetY = direction == ScreenDirection.UP ? -1 + : direction == ScreenDirection.DOWN ? 1 : 0; + handled |= moveSelection(stack, hoveredSlotIndex, consumer, uniqueItems, offsetX, offsetY); + } + } + return handled; + } + + return false; + } + + private static boolean moveSelection(ItemStack stack, int hoveredSlotIndex, SelectedBundleItemConsumer consumer, int uniqueItems, int offsetX, int offsetY) { + int currentIndex = BundleItem.getSelectedItemIndex(stack); + + if (currentIndex == -1) { + consumer.accept(stack, hoveredSlotIndex, 0); + return true; + } + + /* + Bundle grid is organised where the first row is the incomplete row and is RTL, + - - 0 1 + 2 3 4 5 + */ + int rowSize = BundleItem.MAX_SHOWN_GRID_ITEMS_X; + int colSize = Math.min(Mth.ceil(uniqueItems / (float) rowSize), BundleItem.MAX_SHOWN_GRID_ITEMS_Y); + int incompleteRowSize = uniqueItems % rowSize; + int emptySlots = (rowSize - incompleteRowSize) % rowSize; + + int gridX = (currentIndex + emptySlots) % rowSize; + int gridY = (currentIndex + emptySlots) / rowSize; + + int newGridX = (gridX + offsetX + rowSize) % rowSize; + int newGridY = (gridY + offsetY + colSize) % colSize; + + if (newGridY >= 0 && newGridY < colSize) { + int newIndex = Math.max(newGridX + newGridY * rowSize - emptySlots, 0); + + consumer.accept(stack, hoveredSlotIndex, newIndex); + } + + return true; + } + + public interface SelectedBundleItemConsumer { + void accept(ItemStack stack, int hoveredSlot, int selectedIndex); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ChatScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/ChatScreenBehaviour.java similarity index 69% rename from src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ChatScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/ChatScreenBehaviour.java index 1af393317..fc9cfd349 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ChatScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/ChatScreenBehaviour.java @@ -4,29 +4,30 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.impl.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.behaviours.vanilla; +import dev.isxander.controlify.api.controller.Controller; import dev.isxander.controlify.api.font.ActionGlyphs; +import dev.isxander.controlify.api.screenop.*; import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.action.ControlifyActions; import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.screenop.ComponentProcessorProvider; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; -import dev.isxander.controlify.impl.screenop.keyboard.CommonKeyboardHints; -import dev.isxander.controlify.impl.screenop.keyboard.ComponentKeyboardBehaviour; -import dev.isxander.controlify.impl.screenop.keyboard.KeyboardWidget; +import dev.isxander.controlify.impl.gui.keyboard.CommonKeyboardHints; +import dev.isxander.controlify.impl.gui.keyboard.KeyboardWidget; import dev.isxander.controlify.impl.utils.LazyComponentDims; -import dev.isxander.controlify.impl.virtualmouse.VirtualMouseHandler; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.ChatScreen; import net.minecraft.network.chat.Component; import org.jetbrains.annotations.Nullable; -import java.util.Optional; import java.util.function.Supplier; -public class ChatScreenProcessor extends ScreenProcessor { +public final class ChatScreenBehaviour implements ScreenBehaviour { + private final ScreenContext context; + private final ChatScreen screen; + private final Minecraft minecraft = Minecraft.getInstance(); private final Supplier inputSupplier; private final Supplier<@Nullable KeyboardWidget> keyboardSupplier; @@ -39,53 +40,56 @@ public class ChatScreenProcessor extends ScreenProcessor { ActionGlyphs.action(ControlifyActions.GUI_SECONDARY_NAVI_RIGHT.actionId())) ); - public ChatScreenProcessor( - ChatScreen screen, + public ChatScreenBehaviour( + ScreenContext context, Supplier inputSupplier, Supplier<@Nullable KeyboardWidget> keyboardSupplier, Supplier<@Nullable CmdSuggestionsController> suggestionsController ) { - super(screen); + this.context = context; + this.screen = context.screen(); this.inputSupplier = inputSupplier; this.keyboardSupplier = keyboardSupplier; this.suggestionsController = suggestionsController; } @Override - protected void handleButtons(ControllerImpl controller) { - super.handleButtons(controller); + public void handleInteraction(Controller input, Runnable next) { + var controller = (ControllerImpl) input; CmdSuggestionsController suggestionsController = this.suggestionsController.get(); if (suggestionsController != null) { - if (ControlifyActions.GUI_SECONDARY_NAVI_DOWN.on(controller).pulse().consumePulse()) { + while (ControlifyActions.GUI_SECONDARY_NAVI_DOWN.on(controller).pulse().consumePulse()) { if (suggestionsController.controlify$cycle(1)) { - playFocusChangeSound(); + context.playFocusChangeSound(); this.clearCommandSuggesterHint(controller); } } - if (ControlifyActions.GUI_SECONDARY_NAVI_UP.on(controller).pulse().consumePulse()) { + while (ControlifyActions.GUI_SECONDARY_NAVI_UP.on(controller).pulse().consumePulse()) { if (suggestionsController.controlify$cycle(-1)) { - playFocusChangeSound(); + context.playFocusChangeSound(); this.clearCommandSuggesterHint(controller); } } - if (ControlifyActions.GUI_SECONDARY_NAVI_RIGHT.on(controller).pulse().consumePulse()) { + while (ControlifyActions.GUI_SECONDARY_NAVI_RIGHT.on(controller).pulse().consumePulse()) { if (suggestionsController.controlify$useSuggestion()) { - playClackSound(); + context.playClickSound(); this.clearCommandSuggesterHint(controller); } } } + next.run(); } @Override - protected void render(ControllerImpl controller, GuiGraphicsExtractor graphics, float tickDelta, Optional vmouse) { + public void extractRenderState(GuiGraphicsExtractor graphics, float tickDelta, Controller input, Runnable next) { + var controller = (ControllerImpl) input; var settings = controller.settings().generic; if (this.keyboardSupplier.get() != null && settings.guide.showScreenGuides) { @@ -111,18 +115,7 @@ protected void render(ControllerImpl controller, GuiGraphicsExtractor graphics, } } } - } - - @Override - public void onWidgetRebuild() { - super.onWidgetRebuild(); - - // ensure that the default edit box keyboard behaviour is disabled - EditBox input = this.inputSupplier.get(); - if (input != null) { - var processor = (EditBoxComponentProcessor) ComponentProcessorProvider.provide(input); - processor.setKeyboardBehaviour(new ComponentKeyboardBehaviour.DoNothing()); - } + next.run(); } private void clearCommandSuggesterHint(ControllerImpl controller) { diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/ContainerScreenBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/ContainerScreenBehaviour.java new file mode 100644 index 000000000..1a976f171 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/ContainerScreenBehaviour.java @@ -0,0 +1,120 @@ +package dev.isxander.controlify.impl.screenop.behaviours.vanilla; + +import dev.isxander.controlify.api.contextual.ContainerContext; +import dev.isxander.controlify.api.contextual.GuideInstance; +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.screenop.ScreenBehaviour; +import dev.isxander.controlify.api.screenop.ScreenContext; +import dev.isxander.controlify.api.screenop.SnapPoint; +import dev.isxander.controlify.impl.action.ControlifyActions; +import dev.isxander.controlify.impl.contextual.ContextualDomains; +import dev.isxander.controlify.impl.controller.ControllerImpl; +import dev.isxander.controlify.impl.gui.guide.GuideRenderer; +import dev.isxander.controlify.impl.utils.MinecraftUtil; +import dev.isxander.controlify.mixins.feature.guide.screen.AbstractContainerScreenAccessor; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.world.inventory.ContainerInput; +import net.minecraft.world.inventory.Slot; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Consumer; + +/// Inventory interaction, slot snap targets and the contextual inventory guide. +/// Recipe books and creative tabs add independent behaviours to this one. +public final class ContainerScreenBehaviour implements ScreenBehaviour { + private final ScreenContext> context; + private final AbstractContainerScreen screen; + private final Access access; + private final AbstractContainerScreenAccessor geometry; + private final GuideInstance guide; + private final GuideRenderer.Renderable guideRenderer; + private @Nullable Slot previousSlot; + + public ContainerScreenBehaviour(ScreenContext> context) { + this.context = context; + this.screen = context.screen(); + this.access = (Access) screen; + this.geometry = (AbstractContainerScreenAccessor) screen; + this.guide = ContextualDomains.INSTANCE.container().createGuideInstance(Minecraft.getInstance().font); + this.guideRenderer = (GuideRenderer.Renderable) guide.renderable(true, false); + } + + @Override + public void handleInteraction(Controller controller, Runnable next) { + if (!context.isVirtualMouseEnabled()) { + next.run(); + return; + } + var slot = geometry.controlify$getHoveredSlot(); + if (slot != null) { + if (slot.hasItem() && access.controlify$handleItemSlotActions((ControllerImpl) controller)) { + return; + } + if (ControlifyActions.INV_SELECT.on(controller).pulse().consumeAllPulses()) { + access.controlify$clickSlot(slot, slot.index, 0, ContainerInput.PICKUP); + context.playNavigationHaptic(controller); + } + while (MinecraftUtil.getScreen() == screen && ControlifyActions.INV_QUICK_MOVE.on(controller).pulse().consumePulse()) { + access.controlify$clickSlot(slot, slot.index, 0, ContainerInput.QUICK_MOVE); + context.playNavigationHaptic(controller); + } + if (ControlifyActions.INV_TAKE_HALF.on(controller).pulse().consumeAllPulses()) { + access.controlify$clickSlot(slot, slot.index, 1, ContainerInput.PICKUP); + context.playNavigationHaptic(controller); + } + } else { + context.virtualMouse().emulateClicks(controller); + } + if (MinecraftUtil.getScreen() == screen && !screen.getMenu().getCarried().isEmpty() + && ControlifyActions.DROP_INVENTORY.on(controller).pulse().consumeAllPulses()) { + access.controlify$clickSlot(null, -999, 0, ContainerInput.PICKUP); + context.playNavigationHaptic(controller); + } + next.run(); + } + + @Override + public void extractRenderState(GuiGraphicsExtractor graphics, float tickDelta, Controller controller, Runnable next) { + next.run(); + if (MinecraftUtil.getScreen() != screen || !context.inputMode().isController()) { + return; + } + var slot = geometry.controlify$getHoveredSlot(); + if (slot != previousSlot) { + context.playNavigationHaptic(controller); + previousSlot = slot; + } + var settings = ((ControllerImpl) controller).settings().generic.guide; + if (!settings.showScreenGuides) { + return; + } + var mouse = context.virtualMouse(); + guide.update(new ContainerContext( + Minecraft.getInstance().player, slot, screen.getMenu().getCarried(), + geometry.controlify$invokeHasClickedOutside(mouse.getCurrentX(1f), mouse.getCurrentY(1f), + geometry.controlify$getLeftPos(), geometry.controlify$getTopPos()), + (ControllerImpl) controller, settings.verbosity + )); + guideRenderer.setGuiScale(settings.screenGuiScale); + guideRenderer.extractRenderState(graphics, (int) mouse.getCurrentX(tickDelta), (int) mouse.getCurrentY(tickDelta), tickDelta); + } + + @Override + public void collectSnapPoints(Consumer output, Runnable next) { + for (var slot : screen.getMenu().slots) { + if (slot.isActive()) { + output.accept(new SnapPoint(geometry.controlify$getLeftPos() + slot.x + 8, + geometry.controlify$getTopPos() + slot.y + 8, 17)); + } + } + next.run(); + } + + public interface Access { + void controlify$clickSlot(@Nullable Slot slot, int slotId, int button, ContainerInput input); + + boolean controlify$handleItemSlotActions(ControllerImpl controller); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/CreateWorldScreenBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/CreateWorldScreenBehaviour.java new file mode 100644 index 000000000..073d43d87 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/CreateWorldScreenBehaviour.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2026 isXander + * This file is part of Controlify. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ +package dev.isxander.controlify.impl.screenop.behaviours.vanilla; + +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.screenop.*; +import dev.isxander.controlify.impl.action.ControlifyActions; +import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; + +public final class CreateWorldScreenBehaviour implements ScreenBehaviour { + private final ScreenContext context; + + private final Runnable onCreateButton; + + public CreateWorldScreenBehaviour(ScreenContext context, Runnable onCreateButton) { + this.context = context; + this.onCreateButton = onCreateButton; + } + + @Override + public void handleInteraction(Controller controller, Runnable next) { + if (ControlifyActions.GUI_ABSTRACT_ACTION_1.on(controller).pulse().consumeAllPulses()) { + context.playClickSound(); + this.onCreateButton.run(); + return; + } + next.run(); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/CreativeModeInventoryScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/CreativeModeInventoryScreenBehaviour.java similarity index 51% rename from src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/CreativeModeInventoryScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/CreativeModeInventoryScreenBehaviour.java index ca8794c40..fc9fbfcd9 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/CreativeModeInventoryScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/CreativeModeInventoryScreenBehaviour.java @@ -4,41 +4,48 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.impl.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.behaviours.vanilla; +import dev.isxander.controlify.api.controller.Controller; +import dev.isxander.controlify.api.screenop.*; import dev.isxander.controlify.impl.Controlify; import dev.isxander.controlify.impl.action.ControlifyActions; -import dev.isxander.controlify.impl.controller.ControllerImpl; import dev.isxander.controlify.impl.platform.client.CreativeTabHelper; import dev.isxander.controlify.impl.platform.client.PlatformClientUtil; -import dev.isxander.controlify.impl.virtualmouse.VirtualMouseHandler; import net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen; -import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.CreativeModeTab; import java.util.List; -import java.util.function.Predicate; -import java.util.function.Supplier; -public class CreativeModeInventoryScreenProcessor extends AbstractContainerScreenProcessor { +public final class CreativeModeInventoryScreenBehaviour implements ScreenBehaviour { private final CreativeTabHelper tabHelper; - public CreativeModeInventoryScreenProcessor(CreativeModeInventoryScreen screen, Supplier hoveredSlot, ClickSlotFunction clickSlotFunction, Predicate itemSlotAction) { - super(screen, hoveredSlot, clickSlotFunction, itemSlotAction); - this.tabHelper = PlatformClientUtil.createCreativeTabHelper(screen); + private final ScreenContext context; + + public CreativeModeInventoryScreenBehaviour(ScreenContext context) { + this.context = context; + this.tabHelper = PlatformClientUtil.createCreativeTabHelper(context.screen()); } @Override - public void onWidgetRebuild() { - super.onWidgetRebuild(); - Controlify.instance().virtualMouseHandler().snapToClosestPoint(); + public void onWidgetRebuild(Runnable next) { + next.run(); + context.virtualMouse().snapToClosestPoint(); } @SuppressWarnings("UnreachableCode") @Override - protected void handleScreenVMouse(ControllerImpl controller, VirtualMouseHandler vmouse) { + public void handleNavigation(Controller controller, Runnable next) { + if (!context.isVirtualMouseEnabled()) { + next.run(); + return; + } List tabs = tabHelper.getTabsForPage(tabHelper.getCurrentPage()); - if (ControlifyActions.GUI_NEXT_TAB.on(controller).pulse().consumePulse()) { + if (tabs.isEmpty()) { + next.run(); + return; + } + while (ControlifyActions.GUI_NEXT_TAB.on(controller).pulse().consumePulse()) { int newIndex = tabs.indexOf(tabHelper.getSelectedTab()) + 1; if (newIndex >= tabs.size()) { newIndex = 0; @@ -54,7 +61,7 @@ protected void handleScreenVMouse(ControllerImpl controller, VirtualMouseHandler tabHelper.setSelectedTab(tabs.get(newIndex)); } - if (ControlifyActions.GUI_PREV_TAB.on(controller).pulse().consumePulse()) { + while (ControlifyActions.GUI_PREV_TAB.on(controller).pulse().consumePulse()) { int newIndex = tabs.indexOf(tabHelper.getSelectedTab()) - 1; if (newIndex < 0) { int newPage = tabHelper.getCurrentPage() - 1; @@ -70,6 +77,17 @@ protected void handleScreenVMouse(ControllerImpl controller, VirtualMouseHandler tabHelper.setSelectedTab(tabs.get(newIndex)); } - super.handleScreenVMouse(controller, vmouse); + next.run(); } + + @Override + public void collectSnapPoints(java.util.function.Consumer output, Runnable next) { + ((Access) context.screen()).controlify$collectTabSnapPoints(tabHelper.getTabsForPage(tabHelper.getCurrentPage()), output); + next.run(); + } + + public interface Access { + void controlify$collectTabSnapPoints(List tabs, java.util.function.Consumer output); + } + } diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/DoneButtonGuideBehaviour.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/DoneButtonGuideBehaviour.java new file mode 100644 index 000000000..ab6230388 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/DoneButtonGuideBehaviour.java @@ -0,0 +1,28 @@ +package dev.isxander.controlify.impl.screenop.behaviours.vanilla; + +import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.api.screenop.ScreenBehaviour; +import dev.isxander.controlify.api.screenop.ScreenContext; +import dev.isxander.controlify.impl.action.ControlifyActions; +import net.minecraft.client.gui.components.Button; + +import java.util.function.Supplier; + +public final class DoneButtonGuideBehaviour implements ScreenBehaviour { + private final ScreenContext context; + private final Supplier<@org.jetbrains.annotations.Nullable Button> button; + + public DoneButtonGuideBehaviour(ScreenContext context, Supplier<@org.jetbrains.annotations.Nullable Button> button) { + this.context = context; + this.button = button; + } + + @Override + public void onWidgetRebuild(Runnable next) { + var done = button.get(); + if (done != null) { + context.buttonGuide().addGuideToButton(done, ControlifyActions.GUI_BACK, ButtonGuidePredicate.always()); + } + next.run(); + } +} diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ItemSlotControllerAction.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/ItemSlotControllerAction.java similarity index 87% rename from src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ItemSlotControllerAction.java rename to src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/ItemSlotControllerAction.java index d3f59852b..ae5c50118 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/ItemSlotControllerAction.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/ItemSlotControllerAction.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.impl.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.behaviours.vanilla; import dev.isxander.controlify.impl.controller.ControllerImpl; import net.minecraft.client.gui.ItemSlotMouseAction; diff --git a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/JoinMultiplayerScreenProcessor.java b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/JoinMultiplayerScreenBehaviour.java similarity index 65% rename from src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/JoinMultiplayerScreenProcessor.java rename to src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/JoinMultiplayerScreenBehaviour.java index 2d723b2ad..5485d2372 100644 --- a/src/main/java/dev/isxander/controlify/impl/screenop/compat/vanilla/JoinMultiplayerScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/impl/screenop/behaviours/vanilla/JoinMultiplayerScreenBehaviour.java @@ -4,33 +4,36 @@ * * SPDX-License-Identifier: LGPL-3.0-or-later */ -package dev.isxander.controlify.impl.screenop.compat.vanilla; +package dev.isxander.controlify.impl.screenop.behaviours.vanilla; -import dev.isxander.controlify.api.gui.ButtonGuideApi; +import dev.isxander.controlify.api.controller.Controller; import dev.isxander.controlify.api.gui.ButtonGuidePredicate; +import dev.isxander.controlify.api.screenop.*; import dev.isxander.controlify.impl.action.ControlifyActions; -import dev.isxander.controlify.impl.controller.ControllerImpl; -import dev.isxander.controlify.impl.screenop.ScreenProcessor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen; import net.minecraft.client.gui.screens.multiplayer.ServerSelectionList; import java.util.function.Supplier; -public class JoinMultiplayerScreenProcessor extends ScreenProcessor { +public final class JoinMultiplayerScreenBehaviour implements ScreenBehaviour { + private final ScreenContext context; + private final JoinMultiplayerScreen screen; + private final Supplier listSupplier; private final Supplier