1
0
forked from Clones/Controlify

🐛 Fix issue with some modded GUIs crashing with controlify (closes #174)

This commit is contained in:
Xander
2023-10-25 17:09:27 +01:00
parent 36fadf1445
commit 7e23b018c1
3 changed files with 49 additions and 2 deletions

View File

@ -3,12 +3,16 @@ package dev.isxander.controlify.mixins.core;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import dev.isxander.controlify.Controlify; import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.ControllerManager; import dev.isxander.controlify.ControllerManager;
import dev.isxander.controlify.api.ControlifyApi;
import dev.isxander.controlify.controller.Controller; import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.utils.Animator; import dev.isxander.controlify.utils.Animator;
import dev.isxander.controlify.utils.MouseMinecraftCallNotifier;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.MouseHandler;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.server.packs.resources.ReloadInstance; import net.minecraft.server.packs.resources.ReloadInstance;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
@ -21,8 +25,30 @@ public abstract class MinecraftMixin {
@Shadow public abstract void setScreen(@Nullable Screen screen); @Shadow public abstract void setScreen(@Nullable Screen screen);
@Shadow public abstract float getDeltaFrameTime(); @Shadow public abstract float getDeltaFrameTime();
@Shadow @Final public MouseHandler mouseHandler;
@Unique private boolean initNextTick = false; @Unique private boolean initNextTick = false;
// Ideally, this would be done in MouseHandler#releaseMouse, but moving
// the mouse before the screen init is bad, because some mods (e.g. PuzzleLib)
// have custom mouse events that call into screens, events that have not been
// initialised yet in Screen#init. Causing NPEs and many strange issues.
@Inject(method = "setScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MouseHandler;releaseMouse()V", shift = At.Shift.BEFORE))
private void notifyInjectionToNotRun(Screen screen, CallbackInfo ci) {
((MouseMinecraftCallNotifier) mouseHandler).imFromMinecraftSetScreen();
}
/**
* Without this, the mouse would be left in the middle of the
* screen, hovering over whatever is there which would look wrong
* as there is a focus as well.
*/
@Inject(method = "setScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/Screen;init(Lnet/minecraft/client/Minecraft;II)V", shift = At.Shift.AFTER))
private void hideMouseAfterRelease(Screen screen, CallbackInfo ci) {
if (ControlifyApi.get().currentInputMode().isController()) {
Controlify.instance().hideMouse(true, true);
}
}
@ModifyExpressionValue(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/packs/resources/ReloadableResourceManager;createReload(Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;Ljava/util/concurrent/CompletableFuture;Ljava/util/List;)Lnet/minecraft/server/packs/resources/ReloadInstance;")) @ModifyExpressionValue(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/packs/resources/ReloadableResourceManager;createReload(Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;Ljava/util/concurrent/CompletableFuture;Ljava/util/List;)Lnet/minecraft/server/packs/resources/ReloadInstance;"))
private ReloadInstance onInputInitialized(ReloadInstance resourceReload) { private ReloadInstance onInputInitialized(ReloadInstance resourceReload) {
// Controllers need to be initialized extremely late due to the data-driven nature of controllers. // Controllers need to be initialized extremely late due to the data-driven nature of controllers.

View File

@ -3,19 +3,23 @@ package dev.isxander.controlify.mixins.core;
import dev.isxander.controlify.Controlify; import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.InputMode; import dev.isxander.controlify.InputMode;
import dev.isxander.controlify.api.ControlifyApi; import dev.isxander.controlify.api.ControlifyApi;
import dev.isxander.controlify.utils.MouseMinecraftCallNotifier;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.MouseHandler; import net.minecraft.client.MouseHandler;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MouseHandler.class) @Mixin(MouseHandler.class)
public class MouseHandlerMixin { public class MouseHandlerMixin implements MouseMinecraftCallNotifier {
@Shadow @Final private Minecraft minecraft; @Shadow @Final private Minecraft minecraft;
@Unique private boolean calledFromMinecraftSetScreen = false;
// method_22686 is lambda for GLFW mouse click hook - do it outside of the `onPress` method due to fake inputs // method_22686 is lambda for GLFW mouse click hook - do it outside of the `onPress` method due to fake inputs
@Inject(method = "method_22686", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MouseHandler;onPress(JIII)V")) @Inject(method = "method_22686", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MouseHandler;onPress(JIII)V"))
private void onMouseClickInput(long window, int button, int action, int modifiers, CallbackInfo ci) { private void onMouseClickInput(long window, int button, int action, int modifiers, CallbackInfo ci) {
@ -34,6 +38,7 @@ public class MouseHandlerMixin {
onMouse(window); onMouse(window);
} }
@Unique
private void onMouse(long window) { private void onMouse(long window) {
if (window == minecraft.getWindow().getWindow()) { if (window == minecraft.getWindow().getWindow()) {
if (Controlify.instance().currentInputMode() != InputMode.MIXED) { if (Controlify.instance().currentInputMode() != InputMode.MIXED) {
@ -49,8 +54,19 @@ public class MouseHandlerMixin {
*/ */
@Inject(method = "releaseMouse", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/InputConstants;grabOrReleaseMouse(JIDD)V")) @Inject(method = "releaseMouse", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/InputConstants;grabOrReleaseMouse(JIDD)V"))
private void moveMouseIfNecessary(CallbackInfo ci) { private void moveMouseIfNecessary(CallbackInfo ci) {
if (ControlifyApi.get().currentInputMode().isController()) { if (!calledFromMinecraftSetScreen && ControlifyApi.get().currentInputMode().isController()) {
Controlify.instance().hideMouse(true, true); Controlify.instance().hideMouse(true, true);
} }
} }
// shift after RETURN to escape the if statement scope
@Inject(method = "releaseMouse", at = @At(value = "RETURN"))
private void resetCalledFromMinecraftSetScreen(CallbackInfo ci) {
calledFromMinecraftSetScreen = false;
}
@Override
public void imFromMinecraftSetScreen() {
calledFromMinecraftSetScreen = true;
}
} }

View File

@ -0,0 +1,5 @@
package dev.isxander.controlify.utils;
public interface MouseMinecraftCallNotifier {
void imFromMinecraftSetScreen();
}