0 votes

Hello,
i'm trying to achieve an "arrow on the knee" effect (arrow stick to the character/obstacle when they hit something) in a 2D multiplayer game. I created a base class for all projectiles, and gave them the sticky property. The projectiles are always owned by the server, therefore the below code get called in the server projectile node

func _on_Hit_area_entered(area):
    if is_network_master():
        if area.get_parent().has_method("receive_damage"):
            area.get_parent().receive_damage(damage*Spt.base_prj_damage)
        if sticky:
            stick(area.get_parent())

stick() freezes the projectile in position, and place it as child of the obstacle, while communicating everything to the clients (using my custom rpc_list method storee in an autoload).

func stick(obj):
    set_process(false)
    var pos=global_position-obj.global_position
    Spt.rpc_list(self, match_node.match_list.keys(), false,false, "puppet_stick", str(obj.get_path()), pos)
    get_parent().remove_child(self)
    obj.add_child(self)
    position=pos  

remote func puppet_stick(path, pos):
    set_process(false)
    var obj=get_node(path)
    get_parent().remove_child(self)
    obj.add_child(self)
    position=pos

it wors flowlessly, but everytime the projectile hit something, the error debugger print 2 "red" errors, that says

_clear monitoring: This function cant be used during the in/out signal

areasetshapedisabled: Can't change this state while flushing queries. Use call deferred() or setdeferred() to change monitoring state instead.

and after roughly 50-100 of this message, the server crashes without notices.
I would gladly use call deferred or set deferred, but i have no idea where!

Godot version 3.2.3
in Engine by (1,213 points)
edited by

1 Answer

0 votes

in your code

you disabled your collison using

"collisionShape".disabled = true

You will have to change that to

"collisionShape.set_deffered("disabled",false)
by (181 points)

Deferred has one f and two rs. I don't usually correct people's spelling but in this case it matters.

It is not sure he has that in code. I remember getting that error on queuing bodies free. I guess shape disabling happens automatically on deparenting, so remove child and add child might need to be calles deferred ?

thank you Inces, that was it! i changed remove_child and add_child in call deferred and now it works without crashes, however it still prints errors sometimes, saying that some random projectile cannot be removed as they are not child of the node, and not added as they already are child of the node.
here the modified code (in the server)

func stick(obj):
    $Hit.set_deferred("monitoring",false)
    set_process(false)
    var pos=global_position-obj.global_position
    Spt.rpc_list(self, match_node.match_list.keys(), false,false, "puppet_stick", str(obj.get_path()), pos)
    get_parent().call_deferred("remove_child",self)
    obj.call_deferred("add_child", self)
    position=pos

and these are the 2 new errors (on projectile 88):

E 0:01:35.766 removechild: Cannot remove child node 88 as it is not a child of this node.
E 0:01:35.768 add
child: Can't add child '88' to 'Obstacle', already has a parent 'Obstacle'.

it only happens sometimes, so i guess it is due to some "internal" lag caused by the call deferred, but i dont really understand why since the call is only executed once, i even added the set_deferred("monitoring", false) on the area2D to be sure not to emit additional area_entered signals that would result in a second stick() call

Nice :)

It seems there are situations in your code that allow to run stick() twice in a row, or stick() function is triggered again when the arrow is already settled. Are You sure arrows don't collide with each other, for example when several arrows hit one place ?

Generally call deferred yields the function until idle frame. It is possible, that during this yield some additional influence happens on this function, what results in doubled command of add or remove child after this idle frame. Try to examine code having that in mind

no, the projectile only have a collision mask, no collision layer, so they cannot collide with each other.
Maybe it is called when the arrow re-appears as the child of the collider, but by then monitoring should already be disabled (and even if it was the case, why only some projectile and not all of them?)

i honestly dont know why stick is called multiple times, but i worked around the issue by adding a simple active=true

func _on_Hit_area_entered(area):
    if is_network_master() and active:
        active=false
        if area.get_parent().has_method("receive_damage"):
            area.get_parent().receive_damage(damage*Spt.base_prj_damage)
        if sticky:
            call_deferred("stick", area.get_parent())

this will also prevent multiple damages.
it's a lazy fix, but it works for now

(ps: if you wanna turn the comment into an answer i'll select it :) )

This may be good fix

I think I see why the previous code did that. $Hit was setting deferred the monitoring before process was turned to false, it is possible that monitoring was still active during waiting for idle frame, thus activated twice. Maybe :). Deferring calls can become a hindrance when it come to timing multiple calls :)

Now You have put whole stick function into deferred call, so this should do better :)

sadly, i put stick() in call deferred just for the sake of elegance, but without checking the active property it still prints the same errors sometimes :(

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.