Skip to content

Instantly share code, notes, and snippets.

@apples
Last active August 10, 2024 11:06
Show Gist options
  • Save apples/63ecb59e0dadbb0ea60a710e618b6504 to your computer and use it in GitHub Desktop.
Save apples/63ecb59e0dadbb0ea60a710e618b6504 to your computer and use it in GitHub Desktop.
Shallow resource loader designed to load PackedScene files without loading their external resource dependencies.
@tool
extends RefCounted
## ShallowResourceLoader
##
## Shallowly loads a resource without loading external dependencies.
## Dependencies are replaced by an instance of PlaceholderExternalResource.
## Dependencies which have already been loaded and which are in the cache are not replaced.
## Placeholder resources will be stored in the cache using the resource_path they are replacing.
##
## For scenes, other instanced scenes are also loaded recursively, unless recursive_scenes is false.
## Each instanced scene is loaded shallowly, and will be cached using its original path.
##
## The returned resource itself will not be cached.
##
## The typical use case for this is to shallowly load a [class PackedScene],
## and then use [method PackedScene.get_state] to examine it,
## while avoiding loading all of its resource dependencies such as models and textures.
## You could use this method to load other Resource types, but may get type errors.
## Similar to load(), but does not load any additional external resources.
## [param resource_path]: The path of the Resource to load.
## [param recursive_scenes]: When true, external scenes are also loaded recursively. Note: these WILL be cached.
## Returns the loaded Resource. Note: the resource's resource_path will be empty, and it will not be cached.
func load_without_dependencies(resource_path: String, recursive_scenes: bool = true) -> Resource:
# We need to store the placeholders or they'll be removed from the cache.
var placeholder_instances: Array[Resource] = []
# get_dependencies also performs a shallow load of the resource, but unfortunately there's no way to access the result.
for dep: String in ResourceLoader.get_dependencies(resource_path):
var dep_path := dep.get_slice("::", 2)
# If the resource is already loaded and cached, we can just use that.
if ResourceLoader.has_cached(dep_path):
continue
var placeholder: Resource
# Unfortunately we need to use EditorInterface in order to check if something is a scene.
# This means that this code cannot be used in an exported build.
# We could just check the file extension instead, it should generally be a .tscn, .scn, or .escn file.
if recursive_scenes and EditorInterface.get_resource_filesystem().get_file_type(dep_path) == "PackedScene":
placeholder = load_without_dependencies(dep_path)
else:
placeholder = PlaceholderExternalResource.new()
# take_over_path isn't really necessary here, since we know the resource isn't in the cache,
# but I figure it makes the intent clear: we want to add the placeholder to the cache.
placeholder.take_over_path(dep_path)
placeholder_instances.append(placeholder)
# Loading the resource with CACHE_MODE_IGNORE ensure that it doesn't get added to the cache.
# This is kind of a moot effort, since all the external resources (or their placeholders) are still cached.
return ResourceLoader.load(resource_path, "", ResourceLoader.CACHE_MODE_IGNORE)
## A placeholder representing an external resource.
class PlaceholderExternalResource extends Resource:
func _to_string() -> String:
return "<Placeholder for: %s>" % resource_path
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment