Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reparenting an agent sets its HSM to inactive #128

Closed
andmish opened this issue Jun 2, 2024 · 6 comments · Fixed by #131
Closed

Reparenting an agent sets its HSM to inactive #128

andmish opened this issue Jun 2, 2024 · 6 comments · Fixed by #131
Labels
bug Something isn't working

Comments

@andmish
Copy link

andmish commented Jun 2, 2024

Hello!
In running project (Godot 4.2.2 | LimboAI 1.0.2), reparenting an agent (that has LimboHSM) will set that HSM to inactive.
It was working correctly in 1.0.1. So something happened between 1.0.1 >> 1.0.2

Let's say we have such scene:
2024-06-02 13_53_52

Agent has such script:

extends Node3D
@onready var states: LimboHSM = $States
@onready var move: LimboHSM = $States/Move
@onready var new_parent = $"../NewParent"

func _ready():	
	#states.initial_state = move

	states.initialize(self)
	states.set_active(true)
	
	await get_tree().create_timer(2.0).timeout
	reparent(new_parent)
	print(states.is_active())
	### prints "false"

After 2 seconds HSM will be inactive. Basically, reparenting disables AIs 🙂


Other minor issue related to LimboHSM

World has such script for reloading:

func _input(event: InputEvent) -> void:
	if event is InputEventKey and event.pressed:
		if event.keycode == KEY_R:
			get_tree().reload_current_scene()

Now if we alter Agent's script (so initial state now is another LimboHSM child):

func _ready():	
	states.initial_state = move

	states.initialize(self)
	states.set_active(true)
	
	#await get_tree().create_timer(2.0).timeout
	#reparent(new_parent)
	#print(states.is_active())
	### prints "false"

and press R to reload the scene, we will get this error:

E 0:00:03:0596   LimboHSM::_exit: Condition "active_state == nullptr" is true.
  <C++ Source>   limboai\hsm\limbo_hsm.cpp:73 @ LimboHSM::_exit()

It happens only if the current substate is LimboHSM (not LimboState). So if the agent's current state is Idle reloading won't print an error.
Now if we uncomment everything:

func _ready():	
	states.initial_state = move

	states.initialize(self)
	states.set_active(true)
	
	await get_tree().create_timer(2.0).timeout
	reparent(new_parent)
	print(states.is_active())

it makes it inactive and also prints this error again (15 line is reparent(new_parent)):

E 0:00:04:0100   test_hsm.tscn::GDScript_gd7pe:15 @ _ready(): Condition "active_state == nullptr" is true.
  <C++ Source>   limboai\hsm\limbo_hsm.cpp:73 @ LimboHSM::_exit()
  <Stack Trace>  test_hsm.tscn::GDScript_gd7pe:15 @ _ready()
@limbonaut limbonaut added the bug Something isn't working label Jun 2, 2024
@limbonaut
Copy link
Owner

The reason why it happens is these lines: https://github.com/limbonaut/limboai/blob/2c8e0d2da08184051b2729b2eb6c977ab76027f2/hsm/limbo_state.cpp#L188C1-L192C11

It is done in order for a state or a behavior tree in a BTState to release resources they may acquire on _enter. Like an instance of a pathfinder object, which a game may provide a limited number of. However, this is done with the intention to release resources when a state node is removed from a scene, and in your case it's reparenting that triggers this code path.

@limbonaut
Copy link
Owner

The second issue, I can see what happens. When NOTIFICATION_EXIT_TREE is sent, first is the inner HSM that receives this notification, and then the root HSM, which tries to exit its active state, but it's no longer active, so it trips the sanity check and prints the error message.

@limbonaut
Copy link
Owner

limbonaut commented Jun 3, 2024

@andmish
I opened PR that should fix both issues. Could you check if this build fixes those issues for you? https://github.com/limbonaut/limboai/actions/runs/9350197568?pr=131 (scroll down to Artifacts section)

@andmish
Copy link
Author

andmish commented Jun 3, 2024

@limbonaut I was checking notifications and found NOTIFICATION_PREDELETE that goes before exits, so I intended to ask you about it, but looks like you already know everything 😅

I've tested it, no issues, after reparenting an agent its hsm is still active and no error on reloading scene now. Big thanks! 🤗

@andmish andmish closed this as completed Jun 3, 2024
@telemooon
Copy link

telemooon commented Jun 4, 2024

Thank you both so so much! I was having this exact issue and was trying to get around it by repeatedly calling hsm.set_active(true). This definitely saved a lot of headache. Reparenting doesn't deactivate the state machine now, can also confirm working.

@limbonaut
Copy link
Owner

I had to revert the fix in #226 due to a potential crash on exit in LimboHSM. I implemented an alternative solution in that PR, but it's not ideal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants