1
0
forked from Clones/Controlify
Files
Controlify/src/main/java/dev/isxander/controlify/Controlify.java
2023-02-17 01:17:32 +00:00

268 lines
11 KiB
Java

package dev.isxander.controlify;
import com.mojang.blaze3d.Blaze3D;
import com.mojang.logging.LogUtils;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.controller.ControllerState;
import dev.isxander.controlify.gui.screen.ControllerDeadzoneCalibrationScreen;
import dev.isxander.controlify.screenop.ScreenProcessorProvider;
import dev.isxander.controlify.config.ControlifyConfig;
import dev.isxander.controlify.controller.hid.ControllerHIDService;
import dev.isxander.controlify.event.ControlifyEvents;
import dev.isxander.controlify.ingame.guide.InGameButtonGuide;
import dev.isxander.controlify.ingame.InGameInputHandler;
import dev.isxander.controlify.mixins.feature.virtualmouse.MouseHandlerAccessor;
import dev.isxander.controlify.virtualmouse.VirtualMouseHandler;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.toasts.SystemToast;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import org.lwjgl.glfw.GLFW;
import org.slf4j.Logger;
import java.util.ArrayDeque;
import java.util.Queue;
public class Controlify {
public static final Logger LOGGER = LogUtils.getLogger();
private static Controlify instance = null;
private Controller<?, ?> currentController;
private InGameInputHandler inGameInputHandler;
public InGameButtonGuide inGameButtonGuide;
private VirtualMouseHandler virtualMouseHandler;
private InputMode currentInputMode;
private ControllerHIDService controllerHIDService;
private final ControlifyConfig config = new ControlifyConfig();
private final Queue<Controller<?, ?>> calibrationQueue = new ArrayDeque<>();
private int consecutiveInputSwitches = 0;
private double lastInputSwitchTime = 0;
public void initializeControllers() {
LOGGER.info("Discovering and initializing controllers...");
Minecraft minecraft = Minecraft.getInstance();
config().load();
controllerHIDService = new ControllerHIDService();
// find already connected controllers
for (int i = 0; i <= GLFW.GLFW_JOYSTICK_LAST; i++) {
if (GLFW.glfwJoystickPresent(i)) {
int jid = i;
controllerHIDService.awaitNextController(device -> {
setCurrentController(Controller.createOrGet(jid, device));
LOGGER.info("Controller found: " + currentController.name());
if (!config().loadOrCreateControllerData(currentController)) {
calibrationQueue.add(currentController);
}
});
}
}
controllerHIDService.start();
// listen for new controllers
GLFW.glfwSetJoystickCallback((jid, event) -> {
if (event == GLFW.GLFW_CONNECTED) {
controllerHIDService.awaitNextController(device -> {
setCurrentController(Controller.createOrGet(jid, device));
LOGGER.info("Controller connected: " + currentController.name());
this.setCurrentInputMode(InputMode.CONTROLLER);
if (!config().loadOrCreateControllerData(currentController)) {
calibrationQueue.add(currentController);
}
minecraft.getToasts().addToast(SystemToast.multiline(
minecraft,
SystemToast.SystemToastIds.PERIODIC_NOTIFICATION,
Component.translatable("controlify.toast.controller_connected.title"),
Component.translatable("controlify.toast.controller_connected.description", currentController.name())
));
});
} else if (event == GLFW.GLFW_DISCONNECTED) {
var controller = Controller.CONTROLLERS.remove(jid);
if (controller != null) {
setCurrentController(Controller.CONTROLLERS.values().stream().findFirst().orElse(null));
LOGGER.info("Controller disconnected: " + controller.name());
this.setCurrentInputMode(currentController == null ? InputMode.KEYBOARD_MOUSE : InputMode.CONTROLLER);
minecraft.getToasts().addToast(SystemToast.multiline(
minecraft,
SystemToast.SystemToastIds.PERIODIC_NOTIFICATION,
Component.translatable("controlify.toast.controller_disconnected.title"),
Component.translatable("controlify.toast.controller_disconnected.description", controller.name())
));
}
}
});
ClientTickEvents.START_CLIENT_TICK.register(this::tick);
}
public void initializeControlify() {
this.inGameInputHandler = new InGameInputHandler(Controller.DUMMY); // initialize with dummy controller before connection in case of no controller
this.virtualMouseHandler = new VirtualMouseHandler();
}
public void tick(Minecraft client) {
var minecraft = Minecraft.getInstance();
if (minecraft.getOverlay() == null) {
if (!calibrationQueue.isEmpty()) {
Screen screen = minecraft.screen;
while (!calibrationQueue.isEmpty()) {
screen = new ControllerDeadzoneCalibrationScreen(calibrationQueue.poll(), screen);
}
minecraft.setScreen(screen);
minecraft.getToasts().addToast(SystemToast.multiline(
minecraft,
SystemToast.SystemToastIds.PERIODIC_NOTIFICATION,
Component.translatable("controlify.toast.controller_calibration.title"),
Component.translatable("controlify.toast.controller_calibration.description")
));
}
}
for (var controller : Controller.CONTROLLERS.values()) {
controller.updateState();
}
ControllerState state = currentController == null ? ControllerState.EMPTY : currentController.state();
if (!config().globalSettings().outOfFocusInput && !client.isWindowActive())
state = ControllerState.EMPTY;
if (state.hasAnyInput())
this.setCurrentInputMode(InputMode.CONTROLLER);
if (consecutiveInputSwitches > 20) {
LOGGER.warn("Controlify detected current controller to be constantly giving input and has been disabled.");
minecraft.getToasts().addToast(SystemToast.multiline(
minecraft,
SystemToast.SystemToastIds.PERIODIC_NOTIFICATION,
Component.translatable("controlify.toast.faulty_input.title"),
Component.translatable("controlify.toast.faulty_input.description")
));
this.setCurrentController(null);
consecutiveInputSwitches = 0;
}
if (currentController == null) {
this.setCurrentInputMode(InputMode.KEYBOARD_MOUSE);
return;
}
if (client.screen != null) {
ScreenProcessorProvider.provide(client.screen).onControllerUpdate(currentController);
} else {
this.inGameInputHandler().inputTick();
}
this.virtualMouseHandler().handleControllerInput(currentController);
ControlifyEvents.CONTROLLER_STATE_UPDATED.invoker().onControllerStateUpdate(currentController);
}
public ControlifyConfig config() {
return config;
}
public Controller<?, ?> currentController() {
if (currentController == null)
return Controller.DUMMY;
return currentController;
}
public void setCurrentController(Controller<?, ?> controller) {
if (controller == null)
controller = Controller.DUMMY;
if (this.currentController == controller) return;
this.currentController = controller;
this.inGameInputHandler = new InGameInputHandler(controller);
if (Minecraft.getInstance().player != null) {
this.inGameButtonGuide = new InGameButtonGuide(controller, Minecraft.getInstance().player);
}
}
public InGameInputHandler inGameInputHandler() {
return inGameInputHandler;
}
public InGameButtonGuide inGameButtonGuide() {
return inGameButtonGuide;
}
public VirtualMouseHandler virtualMouseHandler() {
return virtualMouseHandler;
}
public ControllerHIDService controllerHIDService() {
return controllerHIDService;
}
public InputMode currentInputMode() {
return currentInputMode;
}
public void setCurrentInputMode(InputMode currentInputMode) {
if (this.currentInputMode == currentInputMode) return;
this.currentInputMode = currentInputMode;
var minecraft = Minecraft.getInstance();
if (!minecraft.mouseHandler.isMouseGrabbed())
hideMouse(currentInputMode == InputMode.CONTROLLER, true);
if (minecraft.screen != null) {
ScreenProcessorProvider.provide(minecraft.screen).onInputModeChanged(currentInputMode);
}
if (Minecraft.getInstance().player != null) {
if (currentInputMode == InputMode.KEYBOARD_MOUSE)
this.inGameButtonGuide = null;
else
this.inGameButtonGuide = new InGameButtonGuide(this.currentController != null ? currentController : Controller.DUMMY, Minecraft.getInstance().player);
}
if (Blaze3D.getTime() - lastInputSwitchTime < 20) {
consecutiveInputSwitches++;
} else {
consecutiveInputSwitches = 0;
}
lastInputSwitchTime = Blaze3D.getTime();
ControlifyEvents.INPUT_MODE_CHANGED.invoker().onInputModeChanged(currentInputMode);
}
public void hideMouse(boolean hide, boolean moveMouse) {
var minecraft = Minecraft.getInstance();
GLFW.glfwSetInputMode(
minecraft.getWindow().getWindow(),
GLFW.GLFW_CURSOR,
hide
? GLFW.GLFW_CURSOR_HIDDEN
: GLFW.GLFW_CURSOR_NORMAL
);
if (minecraft.screen != null) {
var mouseHandlerAccessor = (MouseHandlerAccessor) minecraft.mouseHandler;
if (hide && !virtualMouseHandler().isVirtualMouseEnabled() && moveMouse) {
// stop mouse hovering over last element before hiding cursor but don't actually move it
// so when the user switches back to mouse it will be in the same place
mouseHandlerAccessor.invokeOnMove(minecraft.getWindow().getWindow(), -50, -50);
}
}
}
public static Controlify instance() {
if (instance == null) instance = new Controlify();
return instance;
}
}