Drafted CallableNode class. Will use later down the line for a much cleaner implementation (or possibly outright elision) of what was previously the UI autoload, which should hopefully fix all those errors about awaiters having disappeared by the time a signal emits or a coroutine returns (if I was interpreting them correctly; I can't remember what they actually said, but it was something to that effect).
This commit is contained in:
parent
60fee556cd
commit
48a84ac85b
|
@ -0,0 +1,120 @@
|
||||||
|
class_name CallableNode extends Node
|
||||||
|
## A node that can be called like a function.
|
||||||
|
|
||||||
|
## Positional arguments the node was invoked with.
|
||||||
|
var args: Array = []
|
||||||
|
## Named arguments ("options") the node was invoked with.
|
||||||
|
var opts: Dictionary = {}
|
||||||
|
## Node's return value.
|
||||||
|
var result: Variant = null
|
||||||
|
|
||||||
|
## Calls the CallableNode, waits for it to finish, and returns its result.
|
||||||
|
##
|
||||||
|
## When a CallableNode is called, the following sequence of events occurs:
|
||||||
|
## 1. if the CallableNode is in the tree, it is removed;
|
||||||
|
## 2. the CallableNode is passed the given args and opts;
|
||||||
|
## 3. the CallableNode is requested to call _ready,
|
||||||
|
## and then added as a child to the parent;
|
||||||
|
## 4. if the CallableNode's process_mode is PROCESS_MODE_INHERIT,
|
||||||
|
## the inherited process mode of the CallableNode is resolved,
|
||||||
|
## and its process_mode is set directly to that;
|
||||||
|
## 5. the parent's process mode is set to PROCESS_MODE_DISABLED;
|
||||||
|
## 6. the function yields until the CallableNode is exiting the tree;
|
||||||
|
## 7. steps 2-5 are undone;
|
||||||
|
## 8. The CallableNode's result property is returned.
|
||||||
|
func invoke_existing(
|
||||||
|
parent: Node,
|
||||||
|
p_args: Array = [],
|
||||||
|
p_opts: Dictionary = {}
|
||||||
|
) -> Variant:
|
||||||
|
var prev_args := args
|
||||||
|
var prev_opts := opts
|
||||||
|
var prev_process_mode := process_mode
|
||||||
|
var parent_prev_process_mode := parent.process_mode
|
||||||
|
# step 1
|
||||||
|
if is_inside_tree():
|
||||||
|
get_parent().remove_child(self)
|
||||||
|
# step 2
|
||||||
|
args = p_args
|
||||||
|
opts = p_opts
|
||||||
|
# step 3
|
||||||
|
request_ready()
|
||||||
|
parent.add_child(self)
|
||||||
|
# step 4
|
||||||
|
var process_mode_resolve := self
|
||||||
|
while (
|
||||||
|
process_mode_resolve and
|
||||||
|
process_mode_resolve.process_mode == Node.PROCESS_MODE_INHERIT
|
||||||
|
):
|
||||||
|
process_mode_resolve = process_mode_resolve.get_parent()
|
||||||
|
if process_mode_resolve:
|
||||||
|
process_mode = process_mode_resolve.process_mode
|
||||||
|
else:
|
||||||
|
process_mode = Node.PROCESS_MODE_PAUSABLE
|
||||||
|
# step 5
|
||||||
|
parent.process_mode = Node.PROCESS_MODE_DISABLED
|
||||||
|
# step 6
|
||||||
|
if is_inside_tree():
|
||||||
|
await tree_exiting
|
||||||
|
# step 7
|
||||||
|
args = prev_args
|
||||||
|
opts = prev_opts
|
||||||
|
process_mode = prev_process_mode
|
||||||
|
parent.process_mode = parent_prev_process_mode
|
||||||
|
# step 8
|
||||||
|
return result
|
||||||
|
|
||||||
|
## Invokes an ad-hoc CallableNode.
|
||||||
|
##
|
||||||
|
## Can accept CallableNode, PackedScene, GDScript, or resource path String.
|
||||||
|
## (If and only if CallableNode is given directly,
|
||||||
|
## its lifetime is not managed by the call to invoke;
|
||||||
|
## in this case, the call is equivalent
|
||||||
|
## to calling invoke_existing on the given node.)
|
||||||
|
static func invoke(
|
||||||
|
what: Variant,
|
||||||
|
parent: Node,
|
||||||
|
args: Array = [],
|
||||||
|
opts: Dictionary = {}
|
||||||
|
) -> Variant:
|
||||||
|
if what is CallableNode:
|
||||||
|
return await what.invoke_existing(parent, args, opts)
|
||||||
|
elif what is PackedScene:
|
||||||
|
var inst := what.instantiate() as Node
|
||||||
|
var cn := inst as CallableNode
|
||||||
|
if cn:
|
||||||
|
var result = await cn.invoke_existing(parent, args, opts)
|
||||||
|
cn.queue_free()
|
||||||
|
return result
|
||||||
|
elif inst:
|
||||||
|
push_error(
|
||||||
|
"Tried to invoke a PackedScene " +
|
||||||
|
"whose root is not a CallableNode: " +
|
||||||
|
inst.scene_file_path
|
||||||
|
)
|
||||||
|
inst.free()
|
||||||
|
return null
|
||||||
|
else:
|
||||||
|
return null
|
||||||
|
elif what is GDScript:
|
||||||
|
var inst = what.new()
|
||||||
|
var cn := inst as CallableNode
|
||||||
|
if cn:
|
||||||
|
var result = await cn.invoke_existing(parent, args, opts)
|
||||||
|
cn.queue_free()
|
||||||
|
return result
|
||||||
|
elif inst:
|
||||||
|
push_error(
|
||||||
|
"Tried to invoke a GDScript not derived from CallableNode: " +
|
||||||
|
what.resource_path
|
||||||
|
)
|
||||||
|
if inst is Object and not (inst is RefCounted):
|
||||||
|
inst.free()
|
||||||
|
return null
|
||||||
|
else:
|
||||||
|
return null
|
||||||
|
elif what is String:
|
||||||
|
return await CallableNode.invoke(load(what), parent, args, opts)
|
||||||
|
else:
|
||||||
|
push_error("No way to infer a CallableNode from value: %s" % what)
|
||||||
|
return null
|
Loading…
Reference in New Issue