What is the complete behavior of Node.rpc() and friends?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By gau_veldt
:warning: Old Version Published before Godot 3 was released.

Naturally these are new to 2.2 and thus no documentation has been made yet so hopefully I am asking this question fresh enough that the implementors of the networking in 2.2 still know the code they recently wrote:

I am trying to understand the behavior of the new Node.rpc() call and friends…

SomeNode.rpc("foo") is a broadcasted message?
From what has been responded on FB this should broadcast a method call to every connected peer’s /root/nodepath/to/SomeNode.foo() (assuming SomeNode.gd declares foo with remote keyword)?

SomeNode.rpc_id(1000,"foo")
Similar to above but only generates a call to peer 1000’s /root/nodepath/to/SomeNode.foo()?

Finally, no return values? So two-way methods require providing the caller id’s as arguments in eg: SomeNode.rpc_id(1,"foo",1000,arg1,arg2,...,argN) {assume peer 1000 is asking server (1) a foo related question here} and “return” a value with self.rpc_id(1000,"foo_response",1,retval)?

My worry here is the call/return is completely non-atomic and something affecting foo’s computation could happen between foo() call on server and peer 1000 receiving foo_response().

:bust_in_silhouette: Reply From: Karol Walasek

That’s right, NodePath determines which nodes get called. rpc() is a broadcast message, the function to call on each peer is determined by remote/sync/master/slave. rpc_id() also correct, it’s a direct rpc call on a peer.

Computing via a network is an example of something called distributed computing, where instances work in parallel and communicate through asynchronous channels. If you really need to you can lock an objects state after an rpc() with a simple flag value (something like a mutex?), but this should not be required if the object is completely in slave or master mode.
Physics synchronisation is another subject. Usually if you rpc() position and velocity it should be enough. Sometimes you might want to disable collisions on slaves and additionaly receive collision events (simulate “bumping” locally). It really depends on your own project.

As for return values, your approach is the one to use. You can do some tricks here and even use a yield keyword to stop your method execution state.

var response_data
signal reponse_received

remote func foo_response(data):
  response_data = data
  emit_signal("response_received")

func foo():
  rpc("example_request")
  yield(self, "response_received")
  print("Received "+str(response_data))    
  • I’m not sure if yield returns signal’s data with it so an additional variable is needed.

Do the nodetypes matter or just the name of the node for .rpc() and friends? So on the calling end maybe it’s just a proxy Node at the calling end to refer to a real node (actual render object, such as a Spatial) at the peer?

gau_veldt | 2016-08-30 01:57

A: As far as I can see the object may be any type of Node. It’s not restricted to the same object type as other peers, only that the NodePath matches up.

gau_veldt | 2016-09-01 19:40

Great, thanks for the tip.

Karol Walasek | 2016-09-03 17:18