[MP] Convert _spawn_custom to a Callable property.
Renamed to "spawn_function". Allow both custom spawn and auto spawn list to co-exist. This makes it possible to implement custom spawn without being forced to attach a script to MultiplayerSpawner directly.
This commit is contained in:
parent
f5f7d11ac4
commit
566c48f193
3 changed files with 23 additions and 28 deletions
|
@ -5,20 +5,12 @@
|
||||||
</brief_description>
|
</brief_description>
|
||||||
<description>
|
<description>
|
||||||
Spawnable scenes can be configured in the editor or through code (see [method add_spawnable_scene]).
|
Spawnable scenes can be configured in the editor or through code (see [method add_spawnable_scene]).
|
||||||
Also supports custom node spawns through [method spawn], calling [method _spawn_custom] on all peers.
|
Also supports custom node spawns through [method spawn], calling [member spawn_function] on all peers.
|
||||||
Internally, [MultiplayerSpawner] uses [method MultiplayerAPI.object_configuration_add] to notify spawns passing the spawned node as the [code]object[/code] and itself as the [code]configuration[/code], and [method MultiplayerAPI.object_configuration_remove] to notify despawns in a similar way.
|
Internally, [MultiplayerSpawner] uses [method MultiplayerAPI.object_configuration_add] to notify spawns passing the spawned node as the [code]object[/code] and itself as the [code]configuration[/code], and [method MultiplayerAPI.object_configuration_remove] to notify despawns in a similar way.
|
||||||
</description>
|
</description>
|
||||||
<tutorials>
|
<tutorials>
|
||||||
</tutorials>
|
</tutorials>
|
||||||
<methods>
|
<methods>
|
||||||
<method name="_spawn_custom" qualifiers="virtual">
|
|
||||||
<return type="Node" />
|
|
||||||
<param index="0" name="data" type="Variant" />
|
|
||||||
<description>
|
|
||||||
Method called on all peers when a custom spawn was requested by the authority using [method spawn]. Should return a [Node] that is not in the scene tree.
|
|
||||||
[b]Note:[/b] Spawned nodes should [b]not[/b] be added to the scene with [method Node.add_child]. This is done automatically.
|
|
||||||
</description>
|
|
||||||
</method>
|
|
||||||
<method name="add_spawnable_scene">
|
<method name="add_spawnable_scene">
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<param index="0" name="path" type="String" />
|
<param index="0" name="path" type="String" />
|
||||||
|
@ -49,12 +41,16 @@
|
||||||
<return type="Node" />
|
<return type="Node" />
|
||||||
<param index="0" name="data" type="Variant" default="null" />
|
<param index="0" name="data" type="Variant" default="null" />
|
||||||
<description>
|
<description>
|
||||||
Requests a custom spawn, with [code]data[/code] passed to [method _spawn_custom] on all peers. Returns the locally spawned node instance already inside the scene tree, and added as a child of the node pointed by [member spawn_path].
|
Requests a custom spawn, with [code]data[/code] passed to [member spawn_function] on all peers. Returns the locally spawned node instance already inside the scene tree, and added as a child of the node pointed by [member spawn_path].
|
||||||
[b]Note:[/b] Spawnable scenes are spawned automatically. [method spawn] is only needed for custom spawns.
|
[b]Note:[/b] Spawnable scenes are spawned automatically. [method spawn] is only needed for custom spawns.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
</methods>
|
</methods>
|
||||||
<members>
|
<members>
|
||||||
|
<member name="spawn_function" type="Callable" setter="set_spawn_function" getter="get_spawn_function">
|
||||||
|
Method called on all peers when for every custom [method spawn] requested by the authority. Will receive the [code]data[/code] parameter, and should return a [Node] that is not in the scene tree.
|
||||||
|
[b]Note:[/b] The returned node should [b]not[/b] be added to the scene with [method Node.add_child]. This is done automatically.
|
||||||
|
</member>
|
||||||
<member name="spawn_limit" type="int" setter="set_spawn_limit" getter="get_spawn_limit" default="0">
|
<member name="spawn_limit" type="int" setter="set_spawn_limit" getter="get_spawn_limit" default="0">
|
||||||
Maximum nodes that is allowed to be spawned by this spawner. Includes both spawnable scenes and custom spawns.
|
Maximum nodes that is allowed to be spawned by this spawner. Includes both spawnable scenes and custom spawns.
|
||||||
When set to [code]0[/code] (the default), there is no limit.
|
When set to [code]0[/code] (the default), there is no limit.
|
||||||
|
|
|
@ -93,13 +93,6 @@ PackedStringArray MultiplayerSpawner::get_configuration_warnings() const {
|
||||||
if (spawn_path.is_empty() || !has_node(spawn_path)) {
|
if (spawn_path.is_empty() || !has_node(spawn_path)) {
|
||||||
warnings.push_back(RTR("A valid NodePath must be set in the \"Spawn Path\" property in order for MultiplayerSpawner to be able to spawn Nodes."));
|
warnings.push_back(RTR("A valid NodePath must be set in the \"Spawn Path\" property in order for MultiplayerSpawner to be able to spawn Nodes."));
|
||||||
}
|
}
|
||||||
bool has_scenes = get_spawnable_scene_count() > 0;
|
|
||||||
// Can't check if method is overridden in placeholder scripts.
|
|
||||||
bool has_placeholder_script = get_script_instance() && get_script_instance()->is_placeholder();
|
|
||||||
if (!has_scenes && !GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom) && !has_placeholder_script) {
|
|
||||||
warnings.push_back(RTR("A list of PackedScenes must be set in the \"Auto Spawn List\" property in order for MultiplayerSpawner to automatically spawn them remotely when added as child of \"spawn_path\"."));
|
|
||||||
warnings.push_back(RTR("Alternatively, a Script implementing the function \"_spawn_custom\" must be set for this MultiplayerSpawner, and \"spawn\" must be called explicitly in code."));
|
|
||||||
}
|
|
||||||
return warnings;
|
return warnings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,7 +155,9 @@ void MultiplayerSpawner::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_spawn_limit", "limit"), &MultiplayerSpawner::set_spawn_limit);
|
ClassDB::bind_method(D_METHOD("set_spawn_limit", "limit"), &MultiplayerSpawner::set_spawn_limit);
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "spawn_limit", PROPERTY_HINT_RANGE, "0,1024,1,or_greater"), "set_spawn_limit", "get_spawn_limit");
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "spawn_limit", PROPERTY_HINT_RANGE, "0,1024,1,or_greater"), "set_spawn_limit", "get_spawn_limit");
|
||||||
|
|
||||||
GDVIRTUAL_BIND(_spawn_custom, "data");
|
ClassDB::bind_method(D_METHOD("get_spawn_function"), &MultiplayerSpawner::get_spawn_function);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_spawn_function", "spawn_function"), &MultiplayerSpawner::set_spawn_function);
|
||||||
|
ADD_PROPERTY(PropertyInfo(Variant::CALLABLE, "spawn_function", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_spawn_function", "get_spawn_function");
|
||||||
|
|
||||||
ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
|
ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
|
||||||
ADD_SIGNAL(MethodInfo("spawned", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
|
ADD_SIGNAL(MethodInfo("spawned", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
|
||||||
|
@ -183,7 +178,7 @@ void MultiplayerSpawner::_update_spawn_node() {
|
||||||
Node *node = spawn_path.is_empty() && is_inside_tree() ? nullptr : get_node_or_null(spawn_path);
|
Node *node = spawn_path.is_empty() && is_inside_tree() ? nullptr : get_node_or_null(spawn_path);
|
||||||
if (node) {
|
if (node) {
|
||||||
spawn_node = node->get_instance_id();
|
spawn_node = node->get_instance_id();
|
||||||
if (get_spawnable_scene_count() && !GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom)) {
|
if (get_spawnable_scene_count()) {
|
||||||
node->connect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added));
|
node->connect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -298,23 +293,26 @@ Node *MultiplayerSpawner::instantiate_scene(int p_id) {
|
||||||
|
|
||||||
Node *MultiplayerSpawner::instantiate_custom(const Variant &p_data) {
|
Node *MultiplayerSpawner::instantiate_custom(const Variant &p_data) {
|
||||||
ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!");
|
ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!");
|
||||||
Node *node = nullptr;
|
ERR_FAIL_COND_V_MSG(!spawn_function.is_valid(), nullptr, "Custom spawn requires a valid 'spawn_function'.");
|
||||||
if (GDVIRTUAL_CALL(_spawn_custom, p_data, node)) {
|
const Variant *argv[1] = { &p_data };
|
||||||
return node;
|
Variant ret;
|
||||||
}
|
Callable::CallError ce;
|
||||||
ERR_FAIL_V_MSG(nullptr, "Method '_spawn_custom' is not implemented on this peer.");
|
spawn_function.callp(argv, 1, ret, ce);
|
||||||
|
ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, nullptr, "Failed to call spawn function.");
|
||||||
|
ERR_FAIL_COND_V_MSG(ret.get_type() != Variant::OBJECT, nullptr, "The spawn function must return a Node.");
|
||||||
|
return Object::cast_to<Node>(ret.operator Object *());
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *MultiplayerSpawner::spawn(const Variant &p_data) {
|
Node *MultiplayerSpawner::spawn(const Variant &p_data) {
|
||||||
ERR_FAIL_COND_V(!is_inside_tree() || !get_multiplayer()->has_multiplayer_peer() || !is_multiplayer_authority(), nullptr);
|
ERR_FAIL_COND_V(!is_inside_tree() || !get_multiplayer()->has_multiplayer_peer() || !is_multiplayer_authority(), nullptr);
|
||||||
ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!");
|
ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!");
|
||||||
ERR_FAIL_COND_V_MSG(!GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom), nullptr, "Custom spawn requires the '_spawn_custom' virtual method to be implemented via script.");
|
ERR_FAIL_COND_V_MSG(!spawn_function.is_valid(), nullptr, "Custom spawn requires the 'spawn_function' property to be a valid callable.");
|
||||||
|
|
||||||
Node *parent = get_spawn_node();
|
Node *parent = get_spawn_node();
|
||||||
ERR_FAIL_COND_V_MSG(!parent, nullptr, "Cannot find spawn node.");
|
ERR_FAIL_COND_V_MSG(!parent, nullptr, "Cannot find spawn node.");
|
||||||
|
|
||||||
Node *node = instantiate_custom(p_data);
|
Node *node = instantiate_custom(p_data);
|
||||||
ERR_FAIL_COND_V_MSG(!node, nullptr, "The '_spawn_custom' implementation must return a valid Node.");
|
ERR_FAIL_COND_V_MSG(!node, nullptr, "The 'spawn_function' callable must return a valid node.");
|
||||||
|
|
||||||
_track(node, p_data);
|
_track(node, p_data);
|
||||||
parent->add_child(node, true);
|
parent->add_child(node, true);
|
||||||
|
|
|
@ -71,6 +71,7 @@ private:
|
||||||
ObjectID spawn_node;
|
ObjectID spawn_node;
|
||||||
HashMap<ObjectID, SpawnInfo> tracked_nodes;
|
HashMap<ObjectID, SpawnInfo> tracked_nodes;
|
||||||
uint32_t spawn_limit = 0;
|
uint32_t spawn_limit = 0;
|
||||||
|
Callable spawn_function;
|
||||||
|
|
||||||
void _update_spawn_node();
|
void _update_spawn_node();
|
||||||
void _track(Node *p_node, const Variant &p_argument, int p_scene_id = INVALID_ID);
|
void _track(Node *p_node, const Variant &p_argument, int p_scene_id = INVALID_ID);
|
||||||
|
@ -106,6 +107,8 @@ public:
|
||||||
void set_spawn_path(const NodePath &p_path);
|
void set_spawn_path(const NodePath &p_path);
|
||||||
uint32_t get_spawn_limit() const { return spawn_limit; }
|
uint32_t get_spawn_limit() const { return spawn_limit; }
|
||||||
void set_spawn_limit(uint32_t p_limit) { spawn_limit = p_limit; }
|
void set_spawn_limit(uint32_t p_limit) { spawn_limit = p_limit; }
|
||||||
|
void set_spawn_function(Callable p_spawn_function) { spawn_function = p_spawn_function; }
|
||||||
|
Callable get_spawn_function() const { return spawn_function; }
|
||||||
|
|
||||||
const Variant get_spawn_argument(const ObjectID &p_id) const;
|
const Variant get_spawn_argument(const ObjectID &p_id) const;
|
||||||
int find_spawnable_scene_index_from_object(const ObjectID &p_id) const;
|
int find_spawnable_scene_index_from_object(const ObjectID &p_id) const;
|
||||||
|
@ -114,8 +117,6 @@ public:
|
||||||
Node *instantiate_custom(const Variant &p_data);
|
Node *instantiate_custom(const Variant &p_data);
|
||||||
Node *instantiate_scene(int p_idx);
|
Node *instantiate_scene(int p_idx);
|
||||||
|
|
||||||
GDVIRTUAL1R(Node *, _spawn_custom, const Variant &);
|
|
||||||
|
|
||||||
MultiplayerSpawner() {}
|
MultiplayerSpawner() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue