godot: Input.parse_input_event(input_event) crashes Godot on edge buttons
Godot version
4.0.2
System information
Windows 10, Windows 11
Issue description
Currently in my project there are 4 buttons in the main menu. They can be easily navigated with the ui input actions from a controller. However I want to trigger them programmatically as well for other reasons.
The above picture is my implementation of this. It works perfectly except for when a command is pressed to move in a direction that doesn’t have any more buttons.
So if you press “up” while at the first button the game crashes (image below).
Same for pressing “down” while on the last button. Otherwise it works fine.
Godot --verbose logs don’t log anything since it becomes non-responsive immediately. It seems to me it might be calling itself recursively somehow.
Steps to reproduce
-
In any scene, add a vbox container and at least 2 buttons.
-
Then add a script to the base node and this code:
if Input.is_action_just_pressed("move_down") or\
Input.is_action_just_pressed("move_up") or \
Input.is_action_just_pressed("move_left") or \
Input.is_action_just_pressed("move_right"):
var input_event := InputEventAction.new()
if Input.is_action_just_pressed("move_down"):
input_event.action = "ui_down"
elif Input.is_action_just_pressed("move_up"):
input_event.action = "ui_up"
elif Input.is_action_just_pressed("move_left"):
input_event.action = "ui_left"
elif Input.is_action_just_pressed("move_right"):
input_event.action = "ui_right"
input_event.pressed = true
Input.parse_input_event(input_event)
- Run the scene (make sure one of the buttons is already focused) and try navigating with the input actions and watch the game crash.
Minimal reproduction project
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 27 (22 by maintainers)
I can confirm this on 4.1.dev 64eeb04d2. However, I don’t get a crash but an infinite loop. Getting a backtrace from gdb during the infinite loop returns:
PS: In the MRP, remember to use WASD instead of arrow keys to navigate the UI (otherwise, built-in actions are used).
To clarify, my suggestion for the correct code in this case is:
The class reference should probably be amended to mention what’s been said on this issue 🙂
The previous action can be released manually.
This is expected behavior as it is incorrect usage of the provided APIs.
_unhandled_input()is called once per input event but the user code is not checking if the current event is the one that is relevant to test against so it is always emitting a new event. This means the code will run in a loop forever and maybe hit a non-recursive mutex lock depending on the engine’s version.State records are kept forever, why should that change?
Currently,
action_stateis a mapping of the states of all actions that have occurred. And it’s used in variousis_*_pressed()functions. The results of these functions should vary with input events. This means thataction_statecan rebuild the mapping before a new event propagates.And if it is treated as a mapping of the state of the corresponding actions during an input event (An event maps multiple actions‘ states), it will work fine and avoid such issues, because we can clear
action_statewhen the current event switches to the next event.It does happen if you release the event immediately after in any way.
The freeze happens immediately as soon as an event is parsed in a direction with no more ui elements, regardless of if it’s released or not.