diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java index d606eeb4..e8a61a92 100644 --- a/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java +++ b/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.screens.Screen; import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionResultHolder; import java.util.List; @@ -50,6 +51,11 @@ public interface GuiEvent { Event RENDER_PRE = EventFactory.createInteractionResult(); Event RENDER_POST = EventFactory.createInteractionResult(); + /** + * Invoked during Minecraft#setScreen, equivalent to forge's {@code GuiOpenEvent}. + */ + Event SET_SCREEN = EventFactory.createInteractionResultHolder(); + @Environment(EnvType.CLIENT) interface RenderHud { void renderHud(PoseStack matrices, float tickDelta); @@ -79,4 +85,9 @@ public interface GuiEvent { interface ScreenRenderPost { void render(Screen screen, PoseStack matrices, int mouseX, int mouseY, float delta); } + + @Environment(EnvType.CLIENT) + interface SetScreen { + InteractionResultHolder modifyScreen(Screen screen); + } } diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java index 5eaea2be..b1c5dd28 100644 --- a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java @@ -19,27 +19,44 @@ 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.client.ClientPlayerEvent; 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.TitleScreen; +import net.minecraft.client.main.GameConfig; import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; import net.minecraft.world.item.ItemStack; import net.minecraft.world.phys.HitResult; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; 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.LocalCapture; +import java.io.File; + +@Unique @Mixin(Minecraft.class) -public class MixinMinecraft { +public abstract class MixinMinecraft { + // @formatter:off @Shadow @Nullable public LocalPlayer player; - + @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", 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) { 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 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 = "", + 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 = "", + 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"}, // .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)); + } + } } diff --git a/fabric/src/main/resources/architectury.mixins.json b/fabric/src/main/resources/architectury.mixins.json index bd47a887..09801fd6 100644 --- a/fabric/src/main/resources/architectury.mixins.json +++ b/fabric/src/main/resources/architectury.mixins.json @@ -40,6 +40,7 @@ "PlayerAttackInvoker" ], "injectors": { + "maxShiftBy": 5, "defaultRequire": 1 } } diff --git a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java index b394d242..ac3eefc1 100644 --- a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java +++ b/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 net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.network.chat.Component; import net.minecraft.world.InteractionResult; @@ -124,6 +125,18 @@ public class EventHandlerImplClient { } } + @SubscribeEvent + public static void event(GuiOpenEvent event) { + InteractionResultHolder result = GuiEvent.SET_SCREEN.invoker().modifyScreen(event.getGui()); + switch (result.getResult()) { + case FAIL: + event.setCanceled(true); + return; + case SUCCESS: + event.setGui(result.getObject()); + } + } + @SubscribeEvent 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) { @@ -300,7 +313,7 @@ public class EventHandlerImplClient { public static void event(net.minecraftforge.client.event.TextureStitchEvent.Post event) { TextureStitchEvent.POST.invoker().stitch(event.getMap()); } - + @SubscribeEvent public static void event(FMLClientSetupEvent event) { ClientLifecycleEvent.CLIENT_SETUP.invoker().stateChanged(event.getMinecraftSupplier().get()); diff --git a/testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java b/testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java index 2447d448..3f8e67b4 100644 --- a/testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java +++ b/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) -> { 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) -> { SINK.accept(toSimpleName(screen) + " initializes"); return InteractionResult.PASS;