Jelajahi Sumber

Merge pull request #22 from architectury/feature/screen-open-event

Add GuiEvent.SET_SCREEN
Max 4 tahun lalu
induk
melakukan
721db319f9

+ 11 - 0
common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java

@@ -28,6 +28,7 @@ import net.minecraft.client.gui.components.AbstractWidget;
 import net.minecraft.client.gui.components.events.GuiEventListener;
 import net.minecraft.client.gui.components.events.GuiEventListener;
 import net.minecraft.client.gui.screens.Screen;
 import net.minecraft.client.gui.screens.Screen;
 import net.minecraft.world.InteractionResult;
 import net.minecraft.world.InteractionResult;
+import net.minecraft.world.InteractionResultHolder;
 
 
 import java.util.List;
 import java.util.List;
 
 
@@ -50,6 +51,11 @@ public interface GuiEvent {
     Event<ScreenRenderPre> RENDER_PRE = EventFactory.createInteractionResult();
     Event<ScreenRenderPre> RENDER_PRE = EventFactory.createInteractionResult();
     Event<ScreenRenderPost> RENDER_POST = EventFactory.createInteractionResult();
     Event<ScreenRenderPost> RENDER_POST = EventFactory.createInteractionResult();
     
     
+    /**
+     * Invoked during Minecraft#setScreen, equivalent to forge's {@code GuiOpenEvent}.
+     */
+    Event<SetScreen> SET_SCREEN = EventFactory.createInteractionResultHolder();
+    
     @Environment(EnvType.CLIENT)
     @Environment(EnvType.CLIENT)
     interface RenderHud {
     interface RenderHud {
         void renderHud(PoseStack matrices, float tickDelta);
         void renderHud(PoseStack matrices, float tickDelta);
@@ -79,4 +85,9 @@ public interface GuiEvent {
     interface ScreenRenderPost {
     interface ScreenRenderPost {
         void render(Screen screen, PoseStack matrices, int mouseX, int mouseY, float delta);
         void render(Screen screen, PoseStack matrices, int mouseX, int mouseY, float delta);
     }
     }
+    
+    @Environment(EnvType.CLIENT)
+    interface SetScreen {
+        InteractionResultHolder<Screen> modifyScreen(Screen screen);
+    }
 }
 }

+ 95 - 4
fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java

@@ -19,27 +19,44 @@
 
 
 package me.shedaniel.architectury.mixin.fabric.client;
 package me.shedaniel.architectury.mixin.fabric.client;
 
 
+import me.shedaniel.architectury.event.events.GuiEvent;
 import me.shedaniel.architectury.event.events.InteractionEvent;
 import me.shedaniel.architectury.event.events.InteractionEvent;
 import me.shedaniel.architectury.event.events.client.ClientPlayerEvent;
 import me.shedaniel.architectury.event.events.client.ClientPlayerEvent;
 import net.minecraft.client.Minecraft;
 import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.screens.ConnectScreen;
 import net.minecraft.client.gui.screens.Screen;
 import net.minecraft.client.gui.screens.Screen;
+import net.minecraft.client.gui.screens.TitleScreen;
+import net.minecraft.client.main.GameConfig;
 import net.minecraft.client.player.LocalPlayer;
 import net.minecraft.client.player.LocalPlayer;
 import net.minecraft.world.InteractionHand;
 import net.minecraft.world.InteractionHand;
+import net.minecraft.world.InteractionResultHolder;
 import net.minecraft.world.item.ItemStack;
 import net.minecraft.world.item.ItemStack;
 import net.minecraft.world.phys.HitResult;
 import net.minecraft.world.phys.HitResult;
 import org.jetbrains.annotations.Nullable;
 import org.jetbrains.annotations.Nullable;
 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.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.*;
 import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
 import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
 import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
 import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
 
 
+import java.io.File;
+
+@Unique
 @Mixin(Minecraft.class)
 @Mixin(Minecraft.class)
-public class MixinMinecraft {
+public abstract class MixinMinecraft {
+    // @formatter:off
     @Shadow @Nullable public LocalPlayer player;
     @Shadow @Nullable public LocalPlayer player;
-    
+
     @Shadow @Nullable public HitResult hitResult;
     @Shadow @Nullable public HitResult hitResult;
+
+    @Shadow public abstract void setScreen(@Nullable Screen screen);
+
+    private boolean setScreenCancelled;
+
+    private String hostname;
+    private int port;
+    // @formatter:on
     
     
     @Inject(method = "clearLevel(Lnet/minecraft/client/gui/screens/Screen;)V",
     @Inject(method = "clearLevel(Lnet/minecraft/client/gui/screens/Screen;)V",
             at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/chat/NarratorChatListener;clear()V"))
             at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/chat/NarratorChatListener;clear()V"))
@@ -59,4 +76,78 @@ public class MixinMinecraft {
     private void leftClickAir(CallbackInfo ci) {
     private void leftClickAir(CallbackInfo ci) {
         InteractionEvent.CLIENT_LEFT_CLICK_AIR.invoker().click(player, InteractionHand.MAIN_HAND);
         InteractionEvent.CLIENT_LEFT_CLICK_AIR.invoker().click(player, InteractionHand.MAIN_HAND);
     }
     }
+    
+    @ModifyVariable(
+            method = "setScreen",
+            at = @At(value = "INVOKE",
+                    target = "Lnet/minecraft/client/player/LocalPlayer;respawn()V",
+                    shift = At.Shift.BY,
+                    by = 2),
+            argsOnly = true
+    )
+    public Screen modifyScreen(Screen screen) {
+        Screen old = screen;
+        InteractionResultHolder<Screen> event = GuiEvent.SET_SCREEN.invoker().modifyScreen(screen);
+        switch (event.getResult()) {
+            case FAIL:
+                setScreenCancelled = true;
+                return old;
+            case SUCCESS:
+                screen = event.getObject();
+                if (old != null && screen != old) {
+                    old.removed();
+                }
+            default:
+                setScreenCancelled = false;
+                return screen;
+        }
+    }
+    
+    @Inject(
+            method = "setScreen",
+            at = @At(value = "INVOKE",
+                    target = "Lnet/minecraft/client/player/LocalPlayer;respawn()V",
+                    shift = At.Shift.BY,
+                    by = 3),
+            cancellable = true
+    )
+    public void cancelSetScreen(@Nullable Screen screen, CallbackInfo ci) {
+        if (setScreenCancelled) {
+            ci.cancel();
+        }
+    }
+    
+    @Redirect(
+            method = "<init>",
+            at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;setScreen(Lnet/minecraft/client/gui/screens/Screen;)V"),
+            slice = @Slice(
+                    from = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;resizeDisplay()V"),
+                    to = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/LoadingOverlay;registerTextures(Lnet/minecraft/client/Minecraft;)V")
+            )
+    )
+    public void minecraftWhy(Minecraft mc, Screen screen) {
+    }
+    
+    @Inject(
+            method = "<init>",
+            at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;resizeDisplay()V"),
+            locals = LocalCapture.CAPTURE_FAILHARD
+    )
+    public void saveLocals(GameConfig gc, CallbackInfo ci, File f, String string2, int j) {
+        hostname = string2;
+        port = j;
+    }
+    
+    @SuppressWarnings({"UnresolvedMixinReference", "ConstantConditions"})
+    @Inject(
+            method = {"method_29338", "lambda$null$1"}, // <init>.lambda$null$1
+            at = @At("RETURN")
+    )
+    public void registerMainScreens(CallbackInfo ci) {
+        if (hostname != null) {
+            setScreen(new ConnectScreen(new TitleScreen(), (Minecraft) ((Object) this), hostname, port));
+        } else {
+            setScreen(new TitleScreen(true));
+        }
+    }
 }
 }

+ 1 - 0
fabric/src/main/resources/architectury.mixins.json

@@ -40,6 +40,7 @@
     "PlayerAttackInvoker"
     "PlayerAttackInvoker"
   ],
   ],
   "injectors": {
   "injectors": {
+    "maxShiftBy": 5,
     "defaultRequire": 1
     "defaultRequire": 1
   }
   }
 }
 }

+ 14 - 1
forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java

@@ -27,6 +27,7 @@ import me.shedaniel.architectury.impl.TooltipEventColorContextImpl;
 import me.shedaniel.architectury.impl.TooltipEventPositionContextImpl;
 import me.shedaniel.architectury.impl.TooltipEventPositionContextImpl;
 import net.minecraft.client.Minecraft;
 import net.minecraft.client.Minecraft;
 import net.minecraft.client.gui.components.events.GuiEventListener;
 import net.minecraft.client.gui.components.events.GuiEventListener;
+import net.minecraft.client.gui.screens.Screen;
 import net.minecraft.client.multiplayer.ClientLevel;
 import net.minecraft.client.multiplayer.ClientLevel;
 import net.minecraft.network.chat.Component;
 import net.minecraft.network.chat.Component;
 import net.minecraft.world.InteractionResult;
 import net.minecraft.world.InteractionResult;
@@ -124,6 +125,18 @@ public class EventHandlerImplClient {
         }
         }
     }
     }
     
     
+    @SubscribeEvent
+    public static void event(GuiOpenEvent event) {
+        InteractionResultHolder<Screen> result = GuiEvent.SET_SCREEN.invoker().modifyScreen(event.getGui());
+        switch (result.getResult()) {
+            case FAIL:
+                event.setCanceled(true);
+                return;
+            case SUCCESS:
+                event.setGui(result.getObject());
+        }
+    }
+    
     @SubscribeEvent
     @SubscribeEvent
     public static void event(GuiScreenEvent.DrawScreenEvent.Pre event) {
     public static void event(GuiScreenEvent.DrawScreenEvent.Pre event) {
         if (GuiEvent.RENDER_PRE.invoker().render(event.getGui(), event.getMatrixStack(), event.getMouseX(), event.getMouseY(), event.getRenderPartialTicks()) == InteractionResult.FAIL) {
         if (GuiEvent.RENDER_PRE.invoker().render(event.getGui(), event.getMatrixStack(), event.getMouseX(), event.getMouseY(), event.getRenderPartialTicks()) == InteractionResult.FAIL) {
@@ -300,7 +313,7 @@ public class EventHandlerImplClient {
         public static void event(net.minecraftforge.client.event.TextureStitchEvent.Post event) {
         public static void event(net.minecraftforge.client.event.TextureStitchEvent.Post event) {
             TextureStitchEvent.POST.invoker().stitch(event.getMap());
             TextureStitchEvent.POST.invoker().stitch(event.getMap());
         }
         }
-    
+        
         @SubscribeEvent
         @SubscribeEvent
         public static void event(FMLClientSetupEvent event) {
         public static void event(FMLClientSetupEvent event) {
             ClientLifecycleEvent.CLIENT_SETUP.invoker().stateChanged(event.getMinecraftSupplier().get());
             ClientLifecycleEvent.CLIENT_SETUP.invoker().stateChanged(event.getMinecraftSupplier().get());

+ 4 - 0
testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java

@@ -208,6 +208,10 @@ public class DebugEvents {
         ClientPlayerEvent.CLIENT_PLAYER_RESPAWN.register((oldPlayer, newPlayer) -> {
         ClientPlayerEvent.CLIENT_PLAYER_RESPAWN.register((oldPlayer, newPlayer) -> {
             SINK.accept(newPlayer.getScoreboardName() + " respawned (client)");
             SINK.accept(newPlayer.getScoreboardName() + " respawned (client)");
         });
         });
+        GuiEvent.SET_SCREEN.register((screen -> {
+            SINK.accept("Screen has been changed to " + toSimpleName(screen));
+            return InteractionResultHolder.pass(screen);
+        }));
         GuiEvent.INIT_PRE.register((screen, widgets, children) -> {
         GuiEvent.INIT_PRE.register((screen, widgets, children) -> {
             SINK.accept(toSimpleName(screen) + " initializes");
             SINK.accept(toSimpleName(screen) + " initializes");
             return InteractionResult.PASS;
             return InteractionResult.PASS;