How does MultiplayerSpawner spawn() work?

Godot Version

v4.4.1

Question

Hello everyone, I am trying to figure out how the spawn function works in Godot. I am referring to a custom spawn function to be clear.

The doc states:

Requests a custom spawn, with data passed to spawn_function on all peers. Returns the locally spawned node instance already inside the scene tree, and added as a child of the node pointed by spawn_path.

I guess the key take-away is that it returns the locally spawned node instance and this is clashing with the idea that I had in mind: to load resources on a different thread. The way I am approaching this, the returned Node always seems to be null and I am sure it has to do with the await call.

I have it set-up along the lines of::

class_name ThreadLoader
extends Node

signal load_done

static var thread: Thread = Thread.new()

func load_threaded(resource: String) -> void:
	var out: Node = load(resource).instantiate()
	call_deferred("emit_signal", "load_done", out)

func _exit_tree() -> void:
	thread.wait_to_finish()

In my CustomMultiplayerSpawner I have a custom_spawn_function which does the following:

class_name CustomMultiplayerSpawner
extends MultiplayerSpawner

var threaded_loader: ThreadLoader

func _ready() -> void:
	thread_loader = ThreadLoader.new()
	spawn_function = custom_spawn_function

func custom_spawn_function(resource_path: String) -> Node:
	thread_loader .load_threaded(resource_path)
	var node: Node = await thread_loader .load_done
	return node

If I load the resource inside the custom function and return it, everything works. If I load it using another thread, it does not work. I also made sure - using breakpoints - that the node I am returning is not null and indeed it’s not, it loads correctly using the other thread.

So I was wondering what I am doing wrong. Is there a race condition I am not taking into consideration? Shouldn’t the method await the load_done signal before returning? Why is that not the case?

1 Like

You should avoid await and threads for the multiplayer spawn function, internally I highly doubt Godot is awaiting the custom spawn function, do you get any errors?

If you want to load resources on another thread you can use ResourceLoader.load_threaded_request, in this case I suppose you can strip your instanced object of all it’s resources and load them once they are ready, with palceholders until the thread is finished.

1 Like

Yes, I get the following error:

E 0:00:03:119   Level.gd:20 @ _ready(): The 'spawn_function' callable must return a valid node.

and as you mentioned it is linked to the use of await. Infact the error presents itself even if you load a resource on the main thread but force an await before returning.

I use this method for level loading, but I don’t really like that it’s bound to the _process function - at least as far as my knowledge goes -, I would prefer an event-based threaded loading where my main thread is notified once the loading is done without me checking at every frame.

I’d recommend just loading the object, if you can predict you’ll need certain resources well before then you can make use of threaded load requests, but it shouldn’t be used aggresively to avoid frame hiccups, especially in a multiplayer setting it’s better to guarantee the nodes/resources are loaded when the next frame comes around, even if it means slowing the frame.

1 Like