Jump to content

[1.16.5]A keypress to close them all


Cratthorax

Recommended Posts

Yeah, good Morning,

feels like waging the war against Java code, to end all wars. This time around the battlefield is custom keypress, and closing the various instances of all menus. The reason? Because I'm an arrow key player, that has to reach all across the keyboard in order to close menus, which can't be closed with the inventory key, or will never close if the recipe GUI is opened.

I pretty much tried all available solutions, including using dedicated KeyPressEvents (vanilla and Forge variants), and made sure to not reach across logical sides aka dividing all necessary functions into client and server events. I also made sure to have the events registered on the proper Bus, but for the life of me couldn't figure out why Minecraft.getInstance().displayGuiScreen((Screen)null); does not close all menus. I had at one point the idea it happens because some GUIS do not respond to client ticks, but figured that can't be true, because it would do so in the Main Minecraft .class's

I did found traces in MC .class various GUI functions, as well as in the IngameMenuScreen .class, but either it is hardcoded to not allow using a single key to close all of the menus(which ESC actually does), I am to stupid stupid to figue how it does it, or the ESC function is so deeply nested and addressed by various instances, that I simply cant't be allowed to put them all into one keypress function. I'm using the client tick event as recommended on the Mod bus(not Forge bus) like this:

 
    @SubscribeEvent
    public static void onClientTick(TickEvent.ClientTickEvent event) {
        
        //example on vanilla key hack
        // dito line82
        /*if(REGISTRY == null) {
            
            REGISTRY = InputMappings.Input.class.getDeclaredField("REGISTRY");
            REGISTRY.setAccessible(true);
        }
        
        if(event.phase.equals(TickEvent.Phase.END)) {
            
            @SuppressWarnings("unchecked")
            Map<String, InputMappings> keyBinds = (Map<String, InputMappings>) REGISTRY.get(null);
            
            for (String bind : keyBinds.keySet()) {
                
                keyBinds.get(bind);
                if(InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 256)) {
                    System.out.println(keyBinds.get(bind));
                    break
                }
            }
        }*/
        if(event.phase.equals(TickEvent.Phase.END)) {
            if(RelEqtMain.EXITMENU.isPressed()) {
                
                Minecraft.getInstance().displayGuiScreen((Screen)null);
            }
        }
    }

 

Any help would be extremely appreciated. Or at least a hint on why it actually does not address any single GUI instance, with a custom keypress, while it does so in the original game. You can figure this out inside the KeyboardListener.class:

 
                  if (key == 256) {
                     boolean flag2 = InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 292);
                     this.mc.displayInGameMenu(flag2);
                  }

 

I should note that I actually managed to open any of the GUI screens on custom keypress, but how does the game figure out if it is inside any of the various GUI Screen instances, and should close any of them with pressing ESC?

Edit: I figure mimic an ESC keypress would be an even easier solution. Can you do this on custom keypress?

Edited by Cratthorax
Link to comment
Share on other sites

Works like a charm in every thinkable GUI instance...

@SubscribeEvent
public static void guiScreenKeyEvent(GuiScreenEvent.KeyboardKeyPressedEvent.Pre event) {
	if(event.getKeyCode() == RelEqtMain.EXITMENU.getKey().getKeyCode()) {
    	Minecraft.getInstance().displayGuiScreen((Screen)null);
    }
}
Edited by Cratthorax
Link to comment
Share on other sites

I keep that in mind and //commented if I ever need it on any of my custom keybinds. But a simple exit function doesn't really need that.

I would still love it more to just mimic the ESC key pressed when hitting my custom button, because I could evade all sorts of bugs I don't even know coming yet. Messing with all of the GUI instances can be a messy f. That's specially true for any Screen that has has the Recipe Book GUI imported.

Link to comment
Share on other sites

I stand corrected. It works in all GUI instances, except those that are utilizing the recipeBookGui. I've spend whole yesterday to create a way to close any of those GUI's with my keypress without crashing. But it can't be done. I ended up doing this:

                if(guiScreen instanceof InventoryScreen || guiScreen instanceof CraftingScreen || guiScreen instanceof AbstractFurnaceScreen) {
                    KeyBinding.onTick(Minecraft.getInstance().gameSettings.keyBindInventory.getKey());
                    return;
                }

So it would execute the init() process which resulted in resetting the affected GUI, and allow for closing it with the regular inventory keyBind. Hacky but working.

Edited by Cratthorax
Link to comment
Share on other sites

Because the ClientTick listens for my custom menu enter/exit keypress only outside GUIs, and the other event does inside GUIs. The problem is, the tick is quicker than the GUI key event, and will respond to my if instanceof Screen == null, BEFORE it is properly exited in the GUI key event.

I'm fiddling around with a counter. Makes it easy inside the tick event. I'd start it once I press my custom key in the GUI key event, and then inside the tick I will ask him to not execute my menu open condition, if the counter is over a certain threshold. But damn, is my computer quick...:D...needs some fine tuning.:

...
if(guiScreen != null) {
  ++guiTick;
  return;
} else {
  if(guiTick >= 10) {
    --guiTick;
  } else {
    guiTick = 0;
  }
}

if(guiTick != 0)
  return;
...
inside the GUI key event it's set to 10

 

Link to comment
Share on other sites

I spare you the whole class since it is huge...but I'll give you those two events. The tick is a private static int.

@SubscribeEvent
public static void onClientTick(TickEvent.ClientTickEvent event) {

  if(event.phase.equals(TickEvent.Phase.END)) {
    
    System.out.println(guiTick);
    Minecraft minecraft = Minecraft.getInstance();
    Screen guiScreen = minecraft.currentScreen;
    PlayerEntity player = minecraft.player;

    if(player == null)
      return;

    if(guiScreen != null) {
      ++guiTick;
      return;
    } else {
      if(guiTick >= 10) {
        --guiTick;
      } else {
        guiTick = 0;
      }
    }

    if(guiTick != 0)
      return;

    if(RelEqtMain.EXITMENU.isKeyDown() && !(guiScreen instanceof IngameMenuScreen)) {

      boolean flag = InputMappings.isKeyDown(minecraft.getMainWindow().getHandle(), 292);
      minecraft.displayInGameMenu(flag);
    }
  }
}

@SubscribeEvent
public static void guiKeyEventPre(GuiScreenEvent.KeyboardKeyPressedEvent.Pre event) {

  //You should use KeyBinding#isActiveAndMatches instead, otherwise modifiers like CTRL will not work correctly.
  Minecraft minecraft = Minecraft.getInstance();
  PlayerEntity player = minecraft.player;
  Screen guiScreen = event.getGui();

  if(player == null)
    return;

  if(guiScreen instanceof InventoryScreen || guiScreen instanceof CraftingScreen 
     || guiScreen instanceof FurnaceScreen || guiScreen instanceof BlastFurnaceScreen) {

    if(event.getKeyCode() == RelEqtMain.EXITMENU.getKey().getKeyCode()) {
		
      //dirty hack, since the recipe book crashes all instances of screens that use it
      KeyBinding.onTick(minecraft.gameSettings.keyBindInventory.getKey());
    }
  }

  if(guiScreen instanceof IngameMenuScreen) {
    if(event.getKeyCode() == RelEqtMain.EXITMENU.getKey().getKeyCode()) {

      //guiScreen.keyPressed(256, 256, 0);
      guiTick = 10;
      minecraft.displayGuiScreen((Screen)null);
    }
  }
}

Link to comment
Share on other sites

I did. It's even worth since it would execute the keypress numerous times. Also, in an older thread you recommended against using it, and instead use either the tick, or the GUI key event.

I guess it's a special case, since I basically just try to replace the ESC key function with a custom keybind. The problem here is, stupid Mojang has given all possible keys options to rebind, and address them in java with, for example, minecraft.gameSettings.keyBindUseItem.getKey(). But the ESC menu key is addressed in java code with its keybind number(256). This also creates the issue of the Recipe Book crashing the game, when I try to execute ESC key on my custom key, since the Minecraft.class, respectively the KeyBindListener.class, would only monitor for this explicit keycode, and not its game setting equivalent.

Maybe you Forge guys can give that ESC key proper game setting options?

Edited by Cratthorax
Link to comment
Share on other sites

With a tick of 20 this finally did the trick. I might add a config option, because different users have different computers, and the tick rate might not be the same for all of them.

if(guiScreen != null) {
  guiTick = 20;
  return;
} else {
  if(guiTick > 0) {
    --guiTick;
  }
}

if(guiTick != 0)
  return;

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.