forked from Clones/Controlify
✏️ Make rumble effects merge together rather than pausing one another when playing in unison (plays the stronger magnitude for both motors)
This commit is contained in:
@ -8,6 +8,8 @@ import dev.isxander.controlify.ControllerManager;
|
|||||||
import dev.isxander.controlify.bindings.ControllerBindings;
|
import dev.isxander.controlify.bindings.ControllerBindings;
|
||||||
import dev.isxander.controlify.hid.ControllerHIDService;
|
import dev.isxander.controlify.hid.ControllerHIDService;
|
||||||
import dev.isxander.controlify.rumble.RumbleCapable;
|
import dev.isxander.controlify.rumble.RumbleCapable;
|
||||||
|
import dev.isxander.controlify.rumble.RumbleSource;
|
||||||
|
import dev.isxander.controlify.rumble.RumbleState;
|
||||||
import dev.isxander.controlify.utils.Log;
|
import dev.isxander.controlify.utils.Log;
|
||||||
import org.apache.commons.lang3.SerializationUtils;
|
import org.apache.commons.lang3.SerializationUtils;
|
||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
@ -128,6 +130,14 @@ public abstract class AbstractController<S extends ControllerState, C extends Co
|
|||||||
return Optional.of(hidInfo);
|
return Optional.of(hidInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RumbleState applyRumbleSourceStrength(RumbleState state, RumbleSource source) {
|
||||||
|
float strengthMod = config().getRumbleStrength(source);
|
||||||
|
if (source != RumbleSource.MASTER)
|
||||||
|
strengthMod *= config().getRumbleStrength(RumbleSource.MASTER);
|
||||||
|
return state.mul(strengthMod);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
@ -7,6 +7,7 @@ import dev.isxander.controlify.hid.ControllerHIDService;
|
|||||||
import dev.isxander.controlify.rumble.RumbleCapable;
|
import dev.isxander.controlify.rumble.RumbleCapable;
|
||||||
import dev.isxander.controlify.rumble.RumbleManager;
|
import dev.isxander.controlify.rumble.RumbleManager;
|
||||||
import dev.isxander.controlify.rumble.RumbleSource;
|
import dev.isxander.controlify.rumble.RumbleSource;
|
||||||
|
import dev.isxander.controlify.rumble.RumbleState;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -54,7 +55,7 @@ public interface Controller<S extends ControllerState, C extends ControllerConfi
|
|||||||
private final ControllerBindings<ControllerState> bindings = new ControllerBindings<>(this);
|
private final ControllerBindings<ControllerState> bindings = new ControllerBindings<>(this);
|
||||||
private final RumbleManager rumbleManager = new RumbleManager(new RumbleCapable() {
|
private final RumbleManager rumbleManager = new RumbleManager(new RumbleCapable() {
|
||||||
@Override
|
@Override
|
||||||
public boolean setRumble(float strongMagnitude, float weakMagnitude, RumbleSource source) {
|
public boolean setRumble(float strongMagnitude, float weakMagnitude) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,6 +63,11 @@ public interface Controller<S extends ControllerState, C extends ControllerConfi
|
|||||||
public boolean supportsRumble() {
|
public boolean supportsRumble() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RumbleState applyRumbleSourceStrength(RumbleState state, RumbleSource source) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
private final ControllerConfig config = new ControllerConfig() {
|
private final ControllerConfig config = new ControllerConfig() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -8,7 +8,6 @@ import dev.isxander.controlify.hid.ControllerHIDService;
|
|||||||
import dev.isxander.controlify.debug.DebugProperties;
|
import dev.isxander.controlify.debug.DebugProperties;
|
||||||
import dev.isxander.controlify.driver.*;
|
import dev.isxander.controlify.driver.*;
|
||||||
import dev.isxander.controlify.rumble.RumbleManager;
|
import dev.isxander.controlify.rumble.RumbleManager;
|
||||||
import dev.isxander.controlify.rumble.RumbleSource;
|
|
||||||
import dev.isxander.controlify.utils.ControllerUtils;
|
import dev.isxander.controlify.utils.ControllerUtils;
|
||||||
import dev.isxander.controlify.utils.Log;
|
import dev.isxander.controlify.utils.Log;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
@ -116,16 +115,9 @@ public class GamepadController extends AbstractController<GamepadState, GamepadC
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean setRumble(float strongMagnitude, float weakMagnitude, RumbleSource source) {
|
public boolean setRumble(float strongMagnitude, float weakMagnitude) {
|
||||||
if (!supportsRumble() || !config().allowVibrations) return false;
|
if (!supportsRumble() || !config().allowVibrations) return false;
|
||||||
|
|
||||||
var strengthMod = config().getRumbleStrength(source);
|
|
||||||
if (source != RumbleSource.MASTER)
|
|
||||||
strengthMod *= config().getRumbleStrength(RumbleSource.MASTER);
|
|
||||||
|
|
||||||
strongMagnitude *= strengthMod;
|
|
||||||
weakMagnitude *= strengthMod;
|
|
||||||
|
|
||||||
return drivers.rumbleDriver().rumble(Math.min(strongMagnitude, 1), Math.min(weakMagnitude, 1));
|
return drivers.rumbleDriver().rumble(Math.min(strongMagnitude, 1), Math.min(weakMagnitude, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import dev.isxander.controlify.controller.joystick.mapping.RPJoystickMapping;
|
|||||||
import dev.isxander.controlify.rumble.RumbleCapable;
|
import dev.isxander.controlify.rumble.RumbleCapable;
|
||||||
import dev.isxander.controlify.rumble.RumbleManager;
|
import dev.isxander.controlify.rumble.RumbleManager;
|
||||||
import dev.isxander.controlify.rumble.RumbleSource;
|
import dev.isxander.controlify.rumble.RumbleSource;
|
||||||
|
import dev.isxander.controlify.rumble.RumbleState;
|
||||||
import dev.isxander.controlify.utils.Log;
|
import dev.isxander.controlify.utils.Log;
|
||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
@ -143,7 +144,7 @@ public class CompoundJoystickController implements JoystickController<JoystickCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean setRumble(float strongMagnitude, float weakMagnitude, RumbleSource source) {
|
public boolean setRumble(float strongMagnitude, float weakMagnitude) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,6 +153,11 @@ public class CompoundJoystickController implements JoystickController<JoystickCo
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RumbleState applyRumbleSourceStrength(RumbleState state, RumbleSource source) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RumbleManager rumbleManager() {
|
public RumbleManager rumbleManager() {
|
||||||
return this.rumbleManager;
|
return this.rumbleManager;
|
||||||
|
@ -91,16 +91,9 @@ public class SingleJoystickController extends AbstractController<JoystickState,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean setRumble(float strongMagnitude, float weakMagnitude, RumbleSource source) {
|
public boolean setRumble(float strongMagnitude, float weakMagnitude) {
|
||||||
if (!supportsRumble()) return false;
|
if (!supportsRumble()) return false;
|
||||||
|
|
||||||
var strengthMod = config().getRumbleStrength(source);
|
|
||||||
if (source != RumbleSource.MASTER)
|
|
||||||
strengthMod *= config().getRumbleStrength(RumbleSource.MASTER);
|
|
||||||
|
|
||||||
strongMagnitude *= strengthMod;
|
|
||||||
weakMagnitude *= strengthMod;
|
|
||||||
|
|
||||||
// the duration doesn't matter because we are not updating the joystick state,
|
// the duration doesn't matter because we are not updating the joystick state,
|
||||||
// so there is never any SDL check to stop the rumble after the desired time.
|
// so there is never any SDL check to stop the rumble after the desired time.
|
||||||
if (!SDL.SDL_JoystickRumbleTriggers(ptrJoystick, (int)(strongMagnitude * 65535.0F), (int)(weakMagnitude * 65535.0F), 1)) {
|
if (!SDL.SDL_JoystickRumbleTriggers(ptrJoystick, (int)(strongMagnitude * 65535.0F), (int)(weakMagnitude * 65535.0F), 1)) {
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package dev.isxander.controlify.rumble;
|
package dev.isxander.controlify.rumble;
|
||||||
|
|
||||||
public interface RumbleCapable {
|
public interface RumbleCapable {
|
||||||
boolean setRumble(float strongMagnitude, float weakMagnitude, RumbleSource source);
|
boolean setRumble(float strongMagnitude, float weakMagnitude);
|
||||||
|
|
||||||
boolean supportsRumble();
|
boolean supportsRumble();
|
||||||
|
|
||||||
|
RumbleState applyRumbleSourceStrength(RumbleState state, RumbleSource source);
|
||||||
}
|
}
|
||||||
|
@ -30,38 +30,41 @@ public class RumbleManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void tick() {
|
public void tick() {
|
||||||
RumbleEffectInstance effect;
|
|
||||||
do {
|
|
||||||
effect = effectQueue.peek();
|
|
||||||
|
|
||||||
// if we have no effects, break out of loop and get the null check
|
|
||||||
if (effect == null)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// if the effect is finished, remove and set null, so we loop again
|
|
||||||
if (effect.effect().isFinished()) {
|
|
||||||
effectQueue.remove(effect);
|
|
||||||
effect = null;
|
|
||||||
}
|
|
||||||
} while (effect == null);
|
|
||||||
|
|
||||||
if (effect == null) {
|
|
||||||
controller.setRumble(0f, 0f, RumbleSource.MASTER);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
effectQueue.removeIf(e -> e.effect().isFinished());
|
effectQueue.removeIf(e -> e.effect().isFinished());
|
||||||
effectQueue.forEach(e -> e.effect().tick());
|
effectQueue.forEach(e -> e.effect().tick());
|
||||||
|
|
||||||
if (silent) {
|
if (effectQueue.isEmpty()) {
|
||||||
if (!wasSilent) {
|
clearRumble();
|
||||||
controller.setRumble(0f, 0f, RumbleSource.MASTER);
|
return;
|
||||||
wasSilent = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
RumbleState state = effect.effect().currentState();
|
|
||||||
controller.setRumble(state.strong(), state.weak(), effect.source());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float strong = 0f, weak = 0f;
|
||||||
|
for (RumbleEffectInstance effect : effectQueue) {
|
||||||
|
RumbleState effectState = controller.applyRumbleSourceStrength(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.setRumble(state.strong(), state.weak());
|
||||||
|
wasSilent = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearRumble() {
|
||||||
|
if (wasSilent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
controller.setRumble(0f, 0f);
|
||||||
|
wasSilent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearEffects() {
|
public void clearEffects() {
|
||||||
|
@ -3,6 +3,10 @@ package dev.isxander.controlify.rumble;
|
|||||||
public record RumbleState(float strong, float weak) {
|
public record RumbleState(float strong, float weak) {
|
||||||
public static final RumbleState NONE = new RumbleState(0.0F, 0.0F);
|
public static final RumbleState NONE = new RumbleState(0.0F, 0.0F);
|
||||||
|
|
||||||
|
public boolean isZero() {
|
||||||
|
return strong == 0.0F && weak == 0.0F;
|
||||||
|
}
|
||||||
|
|
||||||
public RumbleState mul(float multiplier) {
|
public RumbleState mul(float multiplier) {
|
||||||
return new RumbleState(strong * multiplier, weak * multiplier);
|
return new RumbleState(strong * multiplier, weak * multiplier);
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import dev.isxander.controlify.controller.joystick.mapping.UnmappedJoystickMappi
|
|||||||
import dev.isxander.controlify.rumble.RumbleCapable;
|
import dev.isxander.controlify.rumble.RumbleCapable;
|
||||||
import dev.isxander.controlify.rumble.RumbleManager;
|
import dev.isxander.controlify.rumble.RumbleManager;
|
||||||
import dev.isxander.controlify.rumble.RumbleSource;
|
import dev.isxander.controlify.rumble.RumbleSource;
|
||||||
|
import dev.isxander.controlify.rumble.RumbleState;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -41,7 +42,7 @@ public class FakeController implements JoystickController<JoystickConfig> {
|
|||||||
this.config = new JoystickConfig(this);
|
this.config = new JoystickConfig(this);
|
||||||
this.rumbleManager = new RumbleManager(new RumbleCapable() {
|
this.rumbleManager = new RumbleManager(new RumbleCapable() {
|
||||||
@Override
|
@Override
|
||||||
public boolean setRumble(float strongMagnitude, float weakMagnitude, RumbleSource source) {
|
public boolean setRumble(float strongMagnitude, float weakMagnitude) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +50,11 @@ public class FakeController implements JoystickController<JoystickConfig> {
|
|||||||
public boolean supportsRumble() {
|
public boolean supportsRumble() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RumbleState applyRumbleSourceStrength(RumbleState state, RumbleSource source) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
this.config.deadzonesCalibrated = true;
|
this.config.deadzonesCalibrated = true;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user