Merge pull request #71534 from Faless/mp/4.x_nested_spawn
[MP] Fix nested spawning during "ready".
This commit is contained in:
commit
8a00992ab6
@ -199,10 +199,6 @@ void MultiplayerSpawner::_notification(int p_what) {
|
||||
Node *node = Object::cast_to<Node>(ObjectDB::get_instance(E.key));
|
||||
ERR_CONTINUE(!node);
|
||||
node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit));
|
||||
// This is unlikely, but might still crash the engine.
|
||||
if (node->is_connected(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready))) {
|
||||
node->disconnect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready));
|
||||
}
|
||||
get_multiplayer()->object_configuration_remove(node, this);
|
||||
}
|
||||
tracked_nodes.clear();
|
||||
@ -244,11 +240,11 @@ void MultiplayerSpawner::_track(Node *p_node, const Variant &p_argument, int p_s
|
||||
if (!tracked_nodes.has(oid)) {
|
||||
tracked_nodes[oid] = SpawnInfo(p_argument.duplicate(true), p_scene_id);
|
||||
p_node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit).bind(p_node->get_instance_id()), CONNECT_ONE_SHOT);
|
||||
p_node->connect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready).bind(p_node->get_instance_id()), CONNECT_ONE_SHOT);
|
||||
_spawn_notify(p_node->get_instance_id());
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerSpawner::_node_ready(ObjectID p_id) {
|
||||
void MultiplayerSpawner::_spawn_notify(ObjectID p_id) {
|
||||
get_multiplayer()->object_configuration_add(ObjectDB::get_instance(p_id), this);
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ private:
|
||||
void _track(Node *p_node, const Variant &p_argument, int p_scene_id = INVALID_ID);
|
||||
void _node_added(Node *p_node);
|
||||
void _node_exit(ObjectID p_id);
|
||||
void _node_ready(ObjectID p_id);
|
||||
void _spawn_notify(ObjectID p_id);
|
||||
|
||||
Vector<String> _get_spawnable_scenes() const;
|
||||
void _set_spawnable_scenes(const Vector<String> &p_scenes);
|
||||
|
@ -125,6 +125,20 @@ void SceneReplicationInterface::on_reset() {
|
||||
}
|
||||
|
||||
void SceneReplicationInterface::on_network_process() {
|
||||
// Prevent endless stalling in case of unforseen spawn errors.
|
||||
if (spawn_queue.size()) {
|
||||
ERR_PRINT("An error happened during last spawn, this usually means the 'ready' signal was not emitted by the spawned node.");
|
||||
for (const ObjectID &oid : spawn_queue) {
|
||||
Node *node = get_id_as<Node>(oid);
|
||||
ERR_CONTINUE(!node);
|
||||
if (node->is_connected(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready))) {
|
||||
node->disconnect(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready));
|
||||
}
|
||||
}
|
||||
spawn_queue.clear();
|
||||
}
|
||||
|
||||
// Process timed syncs.
|
||||
uint64_t msec = OS::get_singleton()->get_ticks_msec();
|
||||
for (KeyValue<int, PeerInfo> &E : peers_info) {
|
||||
const HashSet<ObjectID> to_sync = E.value.sync_nodes;
|
||||
@ -144,19 +158,41 @@ Error SceneReplicationInterface::on_spawn(Object *p_obj, Variant p_config) {
|
||||
// Track node.
|
||||
const ObjectID oid = node->get_instance_id();
|
||||
TrackedNode &tobj = _track(oid);
|
||||
|
||||
// Spawn state needs to be callected after "ready", but the spawn order follows "enter_tree".
|
||||
ERR_FAIL_COND_V(tobj.spawner != ObjectID(), ERR_ALREADY_IN_USE);
|
||||
tobj.spawner = spawner->get_instance_id();
|
||||
spawned_nodes.insert(oid);
|
||||
|
||||
if (multiplayer->has_multiplayer_peer() && spawner->is_multiplayer_authority()) {
|
||||
if (tobj.net_id == 0) {
|
||||
tobj.net_id = ++last_net_id;
|
||||
}
|
||||
_update_spawn_visibility(0, oid);
|
||||
}
|
||||
spawn_queue.insert(oid);
|
||||
node->connect(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready).bind(oid), Node::CONNECT_ONE_SHOT);
|
||||
return OK;
|
||||
}
|
||||
|
||||
void SceneReplicationInterface::_node_ready(const ObjectID &p_oid) {
|
||||
ERR_FAIL_COND(!spawn_queue.has(p_oid)); // Bug.
|
||||
|
||||
// If we are a nested spawn, we need to wait until the parent is ready.
|
||||
if (p_oid != *(spawn_queue.begin())) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const ObjectID &oid : spawn_queue) {
|
||||
ERR_CONTINUE(!tracked_nodes.has(oid));
|
||||
|
||||
TrackedNode &tobj = tracked_nodes[oid];
|
||||
MultiplayerSpawner *spawner = get_id_as<MultiplayerSpawner>(tobj.spawner);
|
||||
ERR_CONTINUE(!spawner);
|
||||
|
||||
spawned_nodes.insert(oid);
|
||||
if (multiplayer->has_multiplayer_peer() && spawner->is_multiplayer_authority()) {
|
||||
if (tobj.net_id == 0) {
|
||||
tobj.net_id = ++last_net_id;
|
||||
}
|
||||
_update_spawn_visibility(0, oid);
|
||||
}
|
||||
}
|
||||
spawn_queue.clear();
|
||||
}
|
||||
|
||||
Error SceneReplicationInterface::on_despawn(Object *p_obj, Variant p_config) {
|
||||
Node *node = Object::cast_to<Node>(p_obj);
|
||||
ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
|
||||
|
@ -51,7 +51,6 @@ private:
|
||||
|
||||
bool operator==(const ObjectID &p_other) { return id == p_other; }
|
||||
|
||||
_FORCE_INLINE_ MultiplayerSpawner *get_spawner() const { return spawner.is_valid() ? Object::cast_to<MultiplayerSpawner>(ObjectDB::get_instance(spawner)) : nullptr; }
|
||||
TrackedNode() {}
|
||||
TrackedNode(const ObjectID &p_id) { id = p_id; }
|
||||
TrackedNode(const ObjectID &p_id, uint32_t p_net_id) {
|
||||
@ -75,7 +74,10 @@ private:
|
||||
HashSet<ObjectID> spawned_nodes;
|
||||
HashSet<ObjectID> sync_nodes;
|
||||
|
||||
// Pending spawn information.
|
||||
// Pending local spawn information (handles spawning nested nodes during ready).
|
||||
HashSet<ObjectID> spawn_queue;
|
||||
|
||||
// Pending remote spawn information.
|
||||
ObjectID pending_spawn;
|
||||
int pending_spawn_remote = 0;
|
||||
const uint8_t *pending_buffer = nullptr;
|
||||
@ -89,6 +91,7 @@ private:
|
||||
|
||||
TrackedNode &_track(const ObjectID &p_id);
|
||||
void _untrack(const ObjectID &p_id);
|
||||
void _node_ready(const ObjectID &p_oid);
|
||||
|
||||
void _send_sync(int p_peer, const HashSet<ObjectID> p_synchronizers, uint16_t p_sync_net_time, uint64_t p_msec);
|
||||
Error _make_spawn_packet(Node *p_node, MultiplayerSpawner *p_spawner, int &r_len);
|
||||
|
Loading…
Reference in New Issue
Block a user