Lazy loading scenes & resources from a server

I’m going to have a game that has many different scenes that can be loaded remotely from a server on demand. I plan to fetch the .tscn files from the server and then use that to immediately start the scene. Each scene will contain several external resources and I’d like to lazy load these as needed.

My initial thought process is to set up empty placeholder resources for each external resource reference in the scene. Then when they’re loaded from the server, we can replace those placeholders with the loaded resources. It’s my understanding that I would need to have placeholder resources or else the scene wouldn’t be able to load since the reference to the external resource path doesn’t yet exist.

Is there a simpler way of handling these placeholders than manually creating resources at runtime, saving them at the expected path, and then replacing them with the real ones?

Additionally, I’d need to consider name collisions as 2 different scenes might have a different player.res resource so I’d have to ensure each resource path is unique if they all have to be loaded into the game’s filesystem.

The ideal solution here would be to allow the scene to be instantiated with missing external resources and simply just not show those resources until they’re loaded in. I’d also ideally like to avoid having to save everything to the game’s filesystem (res://) since it’s just extra work when the resources will already be in memory after being loaded from the server.

Appreciate any advice on how to set this system up.

Just got this working, so I will share what I found. Please consider contributing more back so others can also learn from examples as you figure things out :slight_smile:

Note: I’m using Godot 4.3 Beta 1 Web Export

First create your packed scene that you want to have loaded and when exporting select the “Export PCK/ZIP” option. Reference: Exporting packs, patches, and mods — Godot Engine (stable) documentation in English. This is the file that you will upload to your server.

Within your game that you will be shipping include code similar to the code below. This will make the http request to download the packed scene, put it into a file and then load that and instantiate the scene. Note: Exporting packs, patches, and mods — Godot Engine (stable) documentation in English States ,“By default, if you import a file with the same file path/name as one you already have in your project, the imported one will replace it.” which may be what you actually want.

My Code:

extends Node2D


func _ready():
	# Create an HTTP request node and connect its completion signal.
	var http_request = HTTPRequest.new()
	add_child(http_request)
	http_request.request_completed.connect(self._http_request_completed)

	var error = http_request.request("https://your.server.com/path/to/mod.pck")
	if error != OK:
		push_error("An error occurred in the HTTP request.")



func _http_request_completed(result, response_code, headers, body):
	if result == HTTPRequest.RESULT_SUCCESS:
		# Write the PCK data to a temporary file
		var temp_path = "res://mod.pck" # Note "user://mod.pck" also works
		var file = FileAccess.open(temp_path, FileAccess.WRITE)
		file.store_buffer(body)
		file.close()
		# Load the resource pack
		var success = ProjectSettings.load_resource_pack(temp_path)

		if success:
			print("Resource pack loaded successfully")
			# Now you can use the assets from the resource pack
			var imported_scene: PackedScene = load("res://mod_scene.tscn")
			# Create an instance of the PackedScene
			var instance = imported_scene.instantiate()
			# Add the instance to the scene tree
			add_child(instance)
		else:
			print("Failed to load resource pack")
	else:
		print("HTTP request failed with response code:", response_code)

References:

EDIT: I found that this only works when exporting project in single threaded mode for web exports Thread support causes NS_BINDING_ABORTED for the download.