127 lines
3.7 KiB
GDScript
127 lines
3.7 KiB
GDScript
class_name ResourceGroupBackgroundLoader
|
|
|
|
var _open:Array[String] = []
|
|
var _callback:Callable
|
|
var _cancelled:bool = false
|
|
var _total:int = 0
|
|
var _finished:int = 0
|
|
|
|
|
|
## A loaded resource. This will be given as argument
|
|
## to the callback function
|
|
class ResourceLoadingInfo:
|
|
## Whether loading of this resource was successful
|
|
var success:bool
|
|
## The path from which the resource was loaded
|
|
var path:String
|
|
## The loaded resource. Is null when success is false
|
|
var resource:Resource
|
|
## The overall progress of the background loading, can
|
|
## be used to drive a progress indicator
|
|
var progress:float
|
|
## Whether this has been the last resource
|
|
## of the
|
|
var last:bool
|
|
|
|
## Ctor. Takes an array of paths to load.
|
|
func _init(paths:Array[String], callback:Callable):
|
|
_callback = callback
|
|
|
|
# copy all into the open array and reverse the order
|
|
# so we can use the array as a stack
|
|
_open.append_array(paths)
|
|
_open.reverse()
|
|
|
|
# make note of the total amount of things
|
|
_total = _open.size()
|
|
|
|
# start loading
|
|
_call_deferred(_next)
|
|
|
|
|
|
## Cancels the currently running background loading process
|
|
## as soon as possible. If the process is already finished,
|
|
## nothing will happen. This function will immediately return.
|
|
func cancel():
|
|
_cancelled = true
|
|
|
|
|
|
## Checks if the background loading process is done.
|
|
func is_done() -> bool:
|
|
return _open.is_empty() or _cancelled
|
|
|
|
|
|
## Looks like call_deferred does not work properly when not in a node
|
|
## context (it doesn't seem to defer there...), so we roll our own here.
|
|
func _call_deferred(callable:Callable, args:Array = []):
|
|
await Engine.get_main_loop().process_frame
|
|
callable.callv(args)
|
|
|
|
## Starts the loading process for the next file in background.
|
|
func _next():
|
|
# if we're done or the process was cancelled, stop it here.
|
|
if _open.is_empty() or _cancelled:
|
|
return
|
|
|
|
# fetch the next path and ask the resource loader to load it.
|
|
var path = _open.pop_back()
|
|
var result = ResourceLoader.load_threaded_request(path)
|
|
if result != OK:
|
|
push_warning("Unable to load path ", path , " return code ", result)
|
|
|
|
|
|
_call_deferred(_fetch, [path])
|
|
|
|
## Tries to fetch a file currently being loaded in background.
|
|
func _fetch(path:String):
|
|
var status = ResourceLoader.load_threaded_get_status(path)
|
|
match (status):
|
|
ResourceLoader.THREAD_LOAD_INVALID_RESOURCE:
|
|
push_warning("Loading resource at ", path , " failed. Invalid resource. Ignoring and moving on.")
|
|
_failed(path)
|
|
|
|
ResourceLoader.THREAD_LOAD_FAILED:
|
|
push_warning("Loading resource at ", path , " failed. Ignoring and moving on.")
|
|
_failed(path)
|
|
|
|
# if its ready, ship it to the callback and move on
|
|
ResourceLoader.THREAD_LOAD_LOADED:
|
|
var resource = ResourceLoader.load_threaded_get(path)
|
|
_succeeded(path, resource)
|
|
|
|
# we're still loading, so try again next frame
|
|
ResourceLoader.THREAD_LOAD_IN_PROGRESS:
|
|
_call_deferred(_fetch, [path])
|
|
|
|
|
|
# Called when a path loading ultimately failed. Informs the callback
|
|
# and moves on.
|
|
func _failed(path:String):
|
|
_finished += 1
|
|
_call_callback(false, path, null)
|
|
_call_deferred(_next)
|
|
|
|
## Called when a path loading ultimately succeeded. Informs the callback
|
|
## and moves on.
|
|
func _succeeded(path:String, resource:Resource):
|
|
_finished += 1
|
|
_call_callback(true, path, resource)
|
|
_call_deferred(_next)
|
|
|
|
## Called when an operation is finished. Will invoke the callback.
|
|
func _call_callback(success:bool, path:String, resource:Resource):
|
|
# don't call the callback anymore if the process was cancelled
|
|
if _cancelled:
|
|
return
|
|
|
|
var progress:float = 1.0 if _total == 0 else float(_finished) / float(_total)
|
|
|
|
var result = ResourceLoadingInfo.new()
|
|
result.success = success
|
|
result.path = path
|
|
result.resource = resource
|
|
result.progress = progress
|
|
result.last = _open.is_empty()
|
|
|
|
_callback.call(result)
|