Skip to content

Commit

Permalink
refactor: use new promises
Browse files Browse the repository at this point in the history
  • Loading branch information
kuruk-mm committed Nov 10, 2023
1 parent 900bcf1 commit c0126b3
Show file tree
Hide file tree
Showing 13 changed files with 144 additions and 82 deletions.
12 changes: 0 additions & 12 deletions godot/.godot/global_script_class_cache.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ list=Array[Dictionary]([{
"language": &"GDScript",
"path": "res://src/tool/avatar_renderer/avatar_renderer_helper.gd"
}, {
"base": &"RefCounted",
"class": &"Awaiter",
"icon": "",
"language": &"GDScript",
"path": "res://src/utils/Awaiter.gd"
}, {
"base": &"Node",
"class": &"Comms",
"icon": "",
Expand Down Expand Up @@ -71,12 +65,6 @@ list=Array[Dictionary]([{
"language": &"GDScript",
"path": "res://src/utils/Promise.gd"
}, {
"base": &"RefCounted",
"class": &"PromiseError",
"icon": "",
"language": &"GDScript",
"path": "res://src/utils/PromiseError.gd"
}, {
"base": &"DclRealm",
"class": &"Realm",
"icon": "",
Expand Down
2 changes: 1 addition & 1 deletion godot/src/decentraland_components/audio_source.gd
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func _refresh_data():
last_loaded_audio_clip, content_mapping
)
var res = await promise.co_awaiter()
if res is PromiseError:
if res is Promise.Error:
self.stop()
self.stream = null
printerr("Error on fetch audio: ", res.get_error())
Expand Down
2 changes: 1 addition & 1 deletion godot/src/decentraland_components/avatar.gd
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ func fetch_wearables_dependencies():
for file_name in content_to_fetch:
async_calls.push_back(_fetch_texture_or_gltf(file_name, content_mapping))

await Awaiter.co_all(async_calls)
await Promise.co_all(async_calls)

load_wearables()

Expand Down
2 changes: 1 addition & 1 deletion godot/src/decentraland_components/video_player.gd
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func request_video(file_hash):

var promise = Global.content_manager.fetch_video(file_hash, content_mapping)
var res = await promise.co_awaiter()
if res is PromiseError:
if res is Promise.Error:
printerr("Error on fetching video: ", res.get_error())
else:
_on_video_loaded(file_hash)
Expand Down
12 changes: 6 additions & 6 deletions godot/src/logic/content_thread.gd
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func _co_process_loading_wearable(
)

var content_result = await promise.co_awaiter()
if content_result is PromiseError:
if content_result is Promise.Error:
printerr("Failing on loading wearable ", url, " reason: ", content_result.get_error())
return

Expand Down Expand Up @@ -184,7 +184,7 @@ func _co_process_loading_gltf(content: Dictionary, content_cache_map: Dictionary
var request_promise = _http_requester.request_file(file_hash_path, absolute_file_path)

var content_result = await request_promise.co_awaiter()
if content_result is PromiseError:
if content_result is Promise.Error:
printerr(
"Failing on loading gltf ", file_hash_path, " reason: ", content_result.get_error()
)
Expand Down Expand Up @@ -227,7 +227,7 @@ func _co_process_loading_gltf(content: Dictionary, content_cache_map: Dictionary

content["gltf_mappings"] = mappings

await Awaiter.co_all(promises_dependencies)
await Promise.co_all(promises_dependencies)

# final processing
var new_gltf := GLTFDocument.new()
Expand Down Expand Up @@ -272,7 +272,7 @@ func _co_process_loading_texture(
var promise: Promise = _http_requester.request_file(file_hash_path, absolute_file_path)

var content_result = await promise.co_awaiter()
if content_result is PromiseError:
if content_result is Promise.Error:
printerr(
"Failing on loading gltf ", file_hash_path, " reason: ", content_result.get_error()
)
Expand Down Expand Up @@ -319,7 +319,7 @@ func _co_process_loading_audio(

var promise: Promise = _http_requester.request_file(file_hash_path, absolute_file_path)
var content_result = await promise.co_awaiter()
if content_result is PromiseError:
if content_result is Promise.Error:
printerr(
"Failing on loading wearable ",
file_hash_path,
Expand Down Expand Up @@ -380,7 +380,7 @@ func _co_process_loading_video(
base_url + file_hash, absolute_file_path
)
var content_result = await promise.co_awaiter()
if content_result is PromiseError:
if content_result is Promise.Error:
printerr(
"Failing on loading wearable ",
file_hash_path,
Expand Down
2 changes: 1 addition & 1 deletion godot/src/logic/realm.gd
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func set_realm(new_realm_string: String) -> void:
)

var res = await promise.co_awaiter()
if res is PromiseError:
if res is Promise.Error:
printerr(
"Rejected request change realm to: ",
new_realm_string,
Expand Down
6 changes: 3 additions & 3 deletions godot/src/logic/scene_fetcher.gd
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ func load_scene(scene_entity_id: String, entity: Dictionary):
)

var res = await promise.co_awaiter()
if res is PromiseError:
if res is Promise.Error:
printerr(
"Scene ",
scene_entity_id,
Expand All @@ -244,7 +244,7 @@ func load_scene(scene_entity_id: String, entity: Dictionary):
local_main_js_path.replace("user:/", OS.get_user_data_dir())
)
var res = await promise.co_awaiter()
if res is PromiseError:
if res is Promise.Error:
printerr(
"Scene ",
scene_entity_id,
Expand All @@ -263,7 +263,7 @@ func load_scene(scene_entity_id: String, entity: Dictionary):
)

var res = await promise.co_awaiter()
if res is PromiseError:
if res is Promise.Error:
printerr(
"Scene ",
scene_entity_id,
Expand Down
2 changes: 1 addition & 1 deletion godot/src/ui/components/wearable_button/wearable_button.gd
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func set_wearable(wearable: Dictionary):
}
var promise = Global.content_manager.fetch_texture(wearable_thumbnail, content_mapping)
var res = await promise.co_awaiter()
if res is PromiseError:
if res is Promise.Error:
printerr("Fetch texture error on ", wearable_thumbnail)
else:
texture_rect_preview.texture = res
2 changes: 1 addition & 1 deletion godot/src/ui/components/wearable_item/wearable_item.gd
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func set_wearable(wearable: Dictionary):
wearable_thumbnail, content_mapping
)
var res = await promise.co_awaiter()
if res is PromiseError:
if res is Promise.Error:
printerr("Fetch texture error on ", wearable_thumbnail)
else:
texture_rect_preview.texture = res
Expand Down
2 changes: 1 addition & 1 deletion godot/src/ui/components/wearable_panel/wearable_panel.gd
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func set_wearable(wearable: Dictionary, _wearable_id: String):
}
var promise = Global.content_manager.fetch_texture(wearable_thumbnail, content_mapping)
var res = await promise.co_awaiter()
if res is PromiseError:
if res is Promise.Error:
printerr("Fetch texture error on ", wearable_thumbnail)
else:
texture_rect_preview.texture = res
Expand Down
35 changes: 0 additions & 35 deletions godot/src/utils/Awaiter.gd

This file was deleted.

133 changes: 128 additions & 5 deletions godot/src/utils/Promise.gd
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
extends RefCounted
# Promises for GDScript
# Every function that must be awaited has an `co_` prefix

class_name Promise

signal _on_resolved
Expand All @@ -8,11 +10,13 @@ var _data: Variant = null


func resolve():
_on_resolved.emit()
if is_resolved(): return
_resolved = true
_on_resolved.emit()


func resolve_with_data(data):
if is_resolved(): return
_data = data
resolve()

Expand All @@ -22,10 +26,12 @@ func get_data():


func reject(reason: String):
_data = PromiseError.create(reason)
printerr("Promise rejected, reason: ", reason)
if is_resolved(): return
_data = Promise.Error.create(reason)
resolve()

func is_rejected() -> bool:
return _data is Promise.Error

func is_resolved() -> bool:
return _resolved
Expand All @@ -34,4 +40,121 @@ func is_resolved() -> bool:
func co_awaiter() -> Variant:
if !_resolved:
await _on_resolved
return _data
if _data is Promise: # Chain promises
return _data.co_awaiter()
else:
return _data

class Error:
var _error_description: String = ""

static func create(description: String) -> Promise.Error:
var error = Promise.Error.new()
error._error_description = description
return error

func get_error() -> String:
return _error_description

# Internal helper function
static func _co_call_and_get_promise(f) -> Promise:
if f is Promise:
return f
elif f is Callable:
var res = await f.call()
if res is Promise:
return res
else:
printerr("Func doesn't return a Promise")
return null
else:
printerr("Func is not a callable nor promise")
return null

class AllAwaiter:
var _mask: int
var _promise: Promise = Promise.new()
var results: Array = []

func _init(funcs: Array) -> void:
var size := funcs.size()
if size == 0: # inmediate resolve, no funcs to await...
_promise.resolve()
return

results.resize(size)
results.fill(null) # by default, the return will be null
assert(size < 64)
_mask = (1 << size) - 1
for i in size:
_call_func(i, funcs[i])

func _call_func(i: int, f) -> void:
@warning_ignore("redundant_await")
var promise = await Promise._co_call_and_get_promise(f)
var data = await promise.co_awaiter()
results[i] = data

_mask &= ~(1 << i)

if not _mask and not _promise.is_resolved():
_promise.resolve_with_data(results)

class AnyAwaiter:
var _promise: Promise = Promise.new()

func _init(funcs: Array) -> void:
var size := funcs.size()
if size == 0: # inmediate resolve, no funcs to await...
_promise.resolve()
return
for i in size:
_call_func(i, funcs[i])

func _call_func(i: int, f) -> void:
@warning_ignore("redundant_await")
var promise: Promise = await Promise._co_call_and_get_promise(f)
var res = await promise.co_awaiter()

# Promise.co_any ignores promises with errors
if !promise.is_rejected() and not _promise.is_resolved():
_promise.resolve_with_data(res)

class RaceAwaiter:
var _promise: Promise = Promise.new()

func _init(funcs: Array) -> void:
var size := funcs.size()
if size == 0: # inmediate resolve, no funcs to await...
_promise.resolve()
return
for i in size:
_call_func(i, funcs[i])

func _call_func(i: int, f) -> void:
@warning_ignore("redundant_await")
var promise: Promise = await Promise._co_call_and_get_promise(f)
var res = await promise.co_awaiter()

# Promise.co_race doesn't ignore on error, you get the first one, with or without an error
if not _promise.is_resolved():
_promise.resolve_with_data(res)

# `co_all` is a static function that takes an array of functions (`funcs`)
# and returns an array. It awaits the resolution of all the given functions.
# Each function in the array is expected to be a coroutine or a function
# that returns a promise.
static func co_all(funcs: Array) -> Array:
return await AllAwaiter.new(funcs)._promise.co_awaiter()

# `co_any` is a static function similar to `co_all`, but it resolves as soon as any of the
# functions in the provided array resolves. It returns the result of the first function
# that resolves. It ignores the rejections (differently from co_race)
static func co_any(funcs: Array) -> Variant:
return await AnyAwaiter.new(funcs)._promise.co_awaiter()

# `co_race` is another static function that takes an array of functions and returns
# a variant. It behaves like a race condition, returning the result of the function
# that completes first, even if it fails (differently from co_any)
static func co_race(funcs: Array) -> Variant:
return await RaceAwaiter.new(funcs)._promise.co_awaiter()
14 changes: 0 additions & 14 deletions godot/src/utils/PromiseError.gd

This file was deleted.

0 comments on commit c0126b3

Please sign in to comment.