Merge pull request #106837 from reduz/unique-node-ids2
Add unique Node IDs to support base and instantiated scene refactorings
This commit is contained in:
@ -351,6 +351,7 @@ void ResourceUID::clear() {
|
|||||||
unique_ids.clear();
|
unique_ids.clear();
|
||||||
changed = false;
|
changed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceUID::_bind_methods() {
|
void ResourceUID::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("id_to_text", "id"), &ResourceUID::id_to_text);
|
ClassDB::bind_method(D_METHOD("id_to_text", "id"), &ResourceUID::id_to_text);
|
||||||
ClassDB::bind_method(D_METHOD("text_to_id", "text_id"), &ResourceUID::text_to_id);
|
ClassDB::bind_method(D_METHOD("text_to_id", "text_id"), &ResourceUID::text_to_id);
|
||||||
|
|||||||
@ -2082,6 +2082,14 @@ Node *Node::find_parent(const String &p_pattern) const {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Node::set_unique_scene_id(int32_t p_unique_id) {
|
||||||
|
data.unique_scene_id = p_unique_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t Node::get_unique_scene_id() const {
|
||||||
|
return data.unique_scene_id;
|
||||||
|
}
|
||||||
|
|
||||||
Window *Node::get_window() const {
|
Window *Node::get_window() const {
|
||||||
ERR_THREAD_GUARD_V(nullptr);
|
ERR_THREAD_GUARD_V(nullptr);
|
||||||
Viewport *vp = get_viewport();
|
Viewport *vp = get_viewport();
|
||||||
|
|||||||
@ -132,6 +132,9 @@ public:
|
|||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
static SafeNumeric<uint64_t> total_node_count;
|
static SafeNumeric<uint64_t> total_node_count;
|
||||||
#endif
|
#endif
|
||||||
|
enum {
|
||||||
|
UNIQUE_SCENE_ID_UNASSIGNED = 0
|
||||||
|
};
|
||||||
|
|
||||||
void _update_process(bool p_enable, bool p_for_children);
|
void _update_process(bool p_enable, bool p_for_children);
|
||||||
|
|
||||||
@ -281,6 +284,8 @@ private:
|
|||||||
mutable bool is_translation_domain_inherited : 1;
|
mutable bool is_translation_domain_inherited : 1;
|
||||||
mutable bool is_translation_domain_dirty : 1;
|
mutable bool is_translation_domain_dirty : 1;
|
||||||
|
|
||||||
|
int32_t unique_scene_id = UNIQUE_SCENE_ID_UNASSIGNED;
|
||||||
|
|
||||||
mutable NodePath *path_cache = nullptr;
|
mutable NodePath *path_cache = nullptr;
|
||||||
|
|
||||||
} data;
|
} data;
|
||||||
@ -527,6 +532,9 @@ public:
|
|||||||
Node *get_parent() const;
|
Node *get_parent() const;
|
||||||
Node *find_parent(const String &p_pattern) const;
|
Node *find_parent(const String &p_pattern) const;
|
||||||
|
|
||||||
|
void set_unique_scene_id(int32_t p_unique_id);
|
||||||
|
int32_t get_unique_scene_id() const;
|
||||||
|
|
||||||
Window *get_window() const;
|
Window *get_window() const;
|
||||||
Window *get_non_popup_window() const;
|
Window *get_non_popup_window() const;
|
||||||
Window *get_last_exclusive_window() const;
|
Window *get_last_exclusive_window() const;
|
||||||
|
|||||||
@ -124,6 +124,23 @@ Ref<Resource> SceneState::get_remap_resource(const Ref<Resource> &p_resource, Ha
|
|||||||
return remap_resource;
|
return remap_resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Node *_find_node_by_id(Node *p_owner, Node *p_node, int32_t p_id) {
|
||||||
|
if (p_owner == p_node || p_node->get_owner() == p_owner) {
|
||||||
|
if (p_node->get_unique_scene_id() == p_id) {
|
||||||
|
return p_node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < p_node->get_child_count(); i++) {
|
||||||
|
Node *found = _find_node_by_id(p_owner, p_node->get_child(i), p_id);
|
||||||
|
if (found) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
Node *SceneState::instantiate(GenEditState p_edit_state) const {
|
Node *SceneState::instantiate(GenEditState p_edit_state) const {
|
||||||
// Nodes where instantiation failed (because something is missing.)
|
// Nodes where instantiation failed (because something is missing.)
|
||||||
List<Node *> stray_instances;
|
List<Node *> stray_instances;
|
||||||
@ -133,6 +150,9 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
|
|||||||
if (p_id & FLAG_ID_IS_PATH) { \
|
if (p_id & FLAG_ID_IS_PATH) { \
|
||||||
NodePath np = node_paths[p_id & FLAG_MASK]; \
|
NodePath np = node_paths[p_id & FLAG_MASK]; \
|
||||||
p_name = ret_nodes[0]->get_node_or_null(np); \
|
p_name = ret_nodes[0]->get_node_or_null(np); \
|
||||||
|
if (!p_name) { \
|
||||||
|
p_name = _recover_node_path_index(ret_nodes[0], p_id & FLAG_MASK); \
|
||||||
|
} \
|
||||||
} else { \
|
} else { \
|
||||||
ERR_FAIL_INDEX_V(p_id & FLAG_MASK, nc, nullptr); \
|
ERR_FAIL_INDEX_V(p_id & FLAG_MASK, nc, nullptr); \
|
||||||
p_name = ret_nodes[p_id & FLAG_MASK]; \
|
p_name = ret_nodes[p_id & FLAG_MASK]; \
|
||||||
@ -165,6 +185,8 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
|
|||||||
|
|
||||||
LocalVector<DeferredNodePathProperties> deferred_node_paths;
|
LocalVector<DeferredNodePathProperties> deferred_node_paths;
|
||||||
|
|
||||||
|
bool deep_search_warned = false;
|
||||||
|
|
||||||
for (int i = 0; i < nc; i++) {
|
for (int i = 0; i < nc; i++) {
|
||||||
const NodeData &n = nd[i];
|
const NodeData &n = nd[i];
|
||||||
|
|
||||||
@ -249,6 +271,30 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
|
|||||||
// Get the node from somewhere, it likely already exists from another instance.
|
// Get the node from somewhere, it likely already exists from another instance.
|
||||||
if (parent) {
|
if (parent) {
|
||||||
node = parent->_get_child_by_name(snames[n.name]);
|
node = parent->_get_child_by_name(snames[n.name]);
|
||||||
|
if (i < ids.size()) {
|
||||||
|
if (!node) {
|
||||||
|
// Can't get by name, try to fetch by ID. This is slow, but should be fixed after re-save.
|
||||||
|
int32_t id = ids[i];
|
||||||
|
if (id != Node::UNIQUE_SCENE_ID_UNASSIGNED) {
|
||||||
|
if (!deep_search_warned) {
|
||||||
|
WARN_PRINT(vformat("%sA node in the scene this one inherits from has been removed or moved, so a recovery process needs to take place. Please re-save this scene to avoid the cost of this process next time.", !get_path().is_empty() ? get_path() + ": " : ""));
|
||||||
|
deep_search_warned = true;
|
||||||
|
}
|
||||||
|
Node *base = parent;
|
||||||
|
while (base != ret_nodes[0] && !base->is_instance()) {
|
||||||
|
base = base->get_parent();
|
||||||
|
}
|
||||||
|
node = _find_node_by_id(base, base, id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ids[i] != node->get_unique_scene_id()) {
|
||||||
|
// This may be a scene that did not originally have ids and
|
||||||
|
// was saved before the parent, so force the id to match the
|
||||||
|
// parent scene node id.
|
||||||
|
ids.write[i] = node->get_unique_scene_id();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
if (!node) {
|
if (!node) {
|
||||||
WARN_PRINT(String("Node '" + String(ret_nodes[0]->get_path_to(parent)) + "/" + String(snames[n.name]) + "' was modified from inside an instance, but it has vanished.").ascii().get_data());
|
WARN_PRINT(String("Node '" + String(ret_nodes[0]->get_path_to(parent)) + "/" + String(snames[n.name]) + "' was modified from inside an instance, but it has vanished.").ascii().get_data());
|
||||||
@ -297,6 +343,9 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (node) {
|
if (node) {
|
||||||
|
if (i < ids.size()) {
|
||||||
|
node->set_unique_scene_id(ids[i]);
|
||||||
|
}
|
||||||
// may not have found the node (part of instantiated scene and removed)
|
// may not have found the node (part of instantiated scene and removed)
|
||||||
// if found all is good, otherwise ignore
|
// if found all is good, otherwise ignore
|
||||||
|
|
||||||
@ -734,7 +783,7 @@ static int _vm_get_variant(const Variant &p_variant, HashMap<Variant, int, Varia
|
|||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map) {
|
Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map, HashSet<int32_t> &ids_saved) {
|
||||||
// this function handles all the work related to properly packing scenes, be it
|
// this function handles all the work related to properly packing scenes, be it
|
||||||
// instantiated or inherited.
|
// instantiated or inherited.
|
||||||
// given the complexity of this process, an attempt will be made to properly
|
// given the complexity of this process, an attempt will be made to properly
|
||||||
@ -1014,14 +1063,14 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Has
|
|||||||
// below condition is true for all nodes of the scene being saved, and ones in subscenes
|
// below condition is true for all nodes of the scene being saved, and ones in subscenes
|
||||||
// that hold changes
|
// that hold changes
|
||||||
|
|
||||||
bool save_node = nd.properties.size() || nd.groups.size(); // some local properties or groups exist
|
bool save_node = p_node == p_owner; // owner is always saved
|
||||||
save_node = save_node || p_node == p_owner; // owner is always saved
|
|
||||||
save_node = save_node || (p_node->get_owner() == p_owner && instantiated_by_owner); //part of scene and not instanced
|
save_node = save_node || (p_node->get_owner() == p_owner && instantiated_by_owner); //part of scene and not instanced
|
||||||
|
bool save_data = nd.properties.size() || nd.groups.size(); // some local properties or groups exist
|
||||||
|
|
||||||
int idx = nodes.size();
|
int idx = nodes.size();
|
||||||
int parent_node = NO_PARENT_SAVED;
|
int parent_node = NO_PARENT_SAVED;
|
||||||
|
|
||||||
if (save_node) {
|
if (save_node || save_data) {
|
||||||
//don't save the node if nothing and subscene
|
//don't save the node if nothing and subscene
|
||||||
|
|
||||||
node_map[p_node] = idx;
|
node_map[p_node] = idx;
|
||||||
@ -1041,13 +1090,39 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Has
|
|||||||
nd.parent = p_parent_idx;
|
nd.parent = p_parent_idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t unique_scene_id = p_node->get_unique_scene_id();
|
||||||
|
if (save_node && (unique_scene_id == Node::UNIQUE_SCENE_ID_UNASSIGNED || ids_saved.has(unique_scene_id))) {
|
||||||
|
// Unassigned or clash somehow.
|
||||||
|
// Clashes will always happen with instantiated scenes, so it is normal
|
||||||
|
// to expect them to be resolved.
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
uint32_t data = ResourceUID::get_singleton()->create_id();
|
||||||
|
unique_scene_id = data & 0x7FFFFFFF; // keep positive.
|
||||||
|
|
||||||
|
if (unique_scene_id == Node::UNIQUE_SCENE_ID_UNASSIGNED) {
|
||||||
|
unique_scene_id = 1;
|
||||||
|
}
|
||||||
|
if (ids_saved.has(unique_scene_id)) {
|
||||||
|
// While there is one in a four billion chance for a clash, the scenario where one scene is instantiated multiple times is common, so it must reassign the local id.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_node->set_unique_scene_id(unique_scene_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
ids_saved.insert(unique_scene_id);
|
||||||
|
ids.push_back(unique_scene_id);
|
||||||
|
|
||||||
parent_node = idx;
|
parent_node = idx;
|
||||||
nodes.push_back(nd);
|
nodes.push_back(nd);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < p_node->get_child_count(); i++) {
|
for (int i = 0; i < p_node->get_child_count(); i++) {
|
||||||
Node *c = p_node->get_child(i);
|
Node *c = p_node->get_child(i);
|
||||||
Error err = _parse_node(p_owner, c, parent_node, name_map, variant_map, node_map, nodepath_map);
|
Error err = _parse_node(p_owner, c, parent_node, name_map, variant_map, node_map, nodepath_map, ids_saved);
|
||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -1273,6 +1348,7 @@ Error SceneState::pack(Node *p_scene) {
|
|||||||
HashMap<Variant, int, VariantHasher, VariantComparator> variant_map;
|
HashMap<Variant, int, VariantHasher, VariantComparator> variant_map;
|
||||||
HashMap<Node *, int> node_map;
|
HashMap<Node *, int> node_map;
|
||||||
HashMap<Node *, int> nodepath_map;
|
HashMap<Node *, int> nodepath_map;
|
||||||
|
HashSet<int32_t> ids_saved;
|
||||||
|
|
||||||
// If using scene inheritance, pack the scene it inherits from.
|
// If using scene inheritance, pack the scene it inherits from.
|
||||||
if (scene->get_scene_inherited_state().is_valid()) {
|
if (scene->get_scene_inherited_state().is_valid()) {
|
||||||
@ -1284,7 +1360,7 @@ Error SceneState::pack(Node *p_scene) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Instanced, only direct sub-scenes are supported of course.
|
// Instanced, only direct sub-scenes are supported of course.
|
||||||
Error err = _parse_node(scene, scene, -1, name_map, variant_map, node_map, nodepath_map);
|
Error err = _parse_node(scene, scene, -1, name_map, variant_map, node_map, nodepath_map, ids_saved);
|
||||||
if (err) {
|
if (err) {
|
||||||
clear();
|
clear();
|
||||||
ERR_FAIL_V(err);
|
ERR_FAIL_V(err);
|
||||||
@ -1310,8 +1386,31 @@ Error SceneState::pack(Node *p_scene) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
node_paths.resize(nodepath_map.size());
|
node_paths.resize(nodepath_map.size());
|
||||||
|
id_paths.resize(nodepath_map.size());
|
||||||
for (const KeyValue<Node *, int> &E : nodepath_map) {
|
for (const KeyValue<Node *, int> &E : nodepath_map) {
|
||||||
node_paths.write[E.value] = scene->get_path_to(E.key);
|
node_paths.write[E.value] = scene->get_path_to(E.key);
|
||||||
|
|
||||||
|
// Build a path of IDs to reach the node.
|
||||||
|
PackedInt32Array id_path;
|
||||||
|
bool id_path_valid = false;
|
||||||
|
Node *base = E.key;
|
||||||
|
while (base && base->get_unique_scene_id() != Node::UNIQUE_SCENE_ID_UNASSIGNED) {
|
||||||
|
id_path.push_back(base->get_unique_scene_id());
|
||||||
|
base = base->get_owner();
|
||||||
|
if (base == p_scene) {
|
||||||
|
id_path_valid = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!id_path_valid) {
|
||||||
|
id_path.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse it since we went from node to owner, and we seek from owner to node.
|
||||||
|
id_path.reverse();
|
||||||
|
|
||||||
|
id_paths.write[E.value] = id_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Engine::get_singleton()->is_editor_hint()) {
|
if (Engine::get_singleton()->is_editor_hint()) {
|
||||||
@ -1340,6 +1439,8 @@ void SceneState::clear() {
|
|||||||
node_path_cache.clear();
|
node_path_cache.clear();
|
||||||
node_paths.clear();
|
node_paths.clear();
|
||||||
editable_instances.clear();
|
editable_instances.clear();
|
||||||
|
ids.clear();
|
||||||
|
id_paths.clear();
|
||||||
base_scene_idx = -1;
|
base_scene_idx = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1366,6 +1467,9 @@ Error SceneState::copy_from(const Ref<SceneState> &p_scene_state) {
|
|||||||
for (const NodePath &E : p_scene_state->node_paths) {
|
for (const NodePath &E : p_scene_state->node_paths) {
|
||||||
node_paths.append(E);
|
node_paths.append(E);
|
||||||
}
|
}
|
||||||
|
for (const PackedInt32Array &E : p_scene_state->id_paths) {
|
||||||
|
id_paths.append(E);
|
||||||
|
}
|
||||||
for (const NodePath &E : p_scene_state->editable_instances) {
|
for (const NodePath &E : p_scene_state->editable_instances) {
|
||||||
editable_instances.append(E);
|
editable_instances.append(E);
|
||||||
}
|
}
|
||||||
@ -1389,6 +1493,7 @@ int SceneState::find_node_by_path(const NodePath &p_node) const {
|
|||||||
ERR_FAIL_COND_V_MSG(node_path_cache.is_empty(), -1, "This operation requires the node cache to have been built.");
|
ERR_FAIL_COND_V_MSG(node_path_cache.is_empty(), -1, "This operation requires the node cache to have been built.");
|
||||||
|
|
||||||
if (!node_path_cache.has(p_node)) {
|
if (!node_path_cache.has(p_node)) {
|
||||||
|
// If not in this scene state, find node path by scene inheritance.
|
||||||
if (get_base_scene_state().is_valid()) {
|
if (get_base_scene_state().is_valid()) {
|
||||||
int idx = get_base_scene_state()->find_node_by_path(p_node);
|
int idx = get_base_scene_state()->find_node_by_path(p_node);
|
||||||
if (idx != -1) {
|
if (idx != -1) {
|
||||||
@ -1610,15 +1715,30 @@ void SceneState::set_bundled_scene(const Dictionary &p_dictionary) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (p_dictionary.has("node_ids")) {
|
||||||
|
ids = p_dictionary["node_ids"];
|
||||||
|
}
|
||||||
|
|
||||||
Array np;
|
Array np;
|
||||||
if (p_dictionary.has("node_paths")) {
|
if (p_dictionary.has("node_paths")) {
|
||||||
np = p_dictionary["node_paths"];
|
np = p_dictionary["node_paths"];
|
||||||
}
|
}
|
||||||
|
|
||||||
node_paths.resize(np.size());
|
node_paths.resize(np.size());
|
||||||
for (int i = 0; i < np.size(); i++) {
|
for (int i = 0; i < np.size(); i++) {
|
||||||
node_paths.write[i] = np[i];
|
node_paths.write[i] = np[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Array idp;
|
||||||
|
if (p_dictionary.has("id_paths") && ids.size()) {
|
||||||
|
idp = p_dictionary["id_paths"];
|
||||||
|
}
|
||||||
|
|
||||||
|
id_paths.resize(idp.size());
|
||||||
|
for (int i = 0; i < idp.size(); i++) {
|
||||||
|
id_paths.write[i] = idp[i];
|
||||||
|
}
|
||||||
|
|
||||||
Array ei;
|
Array ei;
|
||||||
if (p_dictionary.has("editable_instances")) {
|
if (p_dictionary.has("editable_instances")) {
|
||||||
ei = p_dictionary["editable_instances"];
|
ei = p_dictionary["editable_instances"];
|
||||||
@ -1678,6 +1798,7 @@ Dictionary SceneState::get_bundled_scene() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
d["nodes"] = rnodes;
|
d["nodes"] = rnodes;
|
||||||
|
d["node_ids"] = ids;
|
||||||
|
|
||||||
Vector<int> rconns;
|
Vector<int> rconns;
|
||||||
d["conn_count"] = connections.size();
|
d["conn_count"] = connections.size();
|
||||||
@ -1705,6 +1826,13 @@ Dictionary SceneState::get_bundled_scene() const {
|
|||||||
}
|
}
|
||||||
d["node_paths"] = rnode_paths;
|
d["node_paths"] = rnode_paths;
|
||||||
|
|
||||||
|
Array rid_paths;
|
||||||
|
rid_paths.resize(id_paths.size());
|
||||||
|
for (int i = 0; i < id_paths.size(); i++) {
|
||||||
|
rid_paths[i] = id_paths[i];
|
||||||
|
}
|
||||||
|
d["id_paths"] = rid_paths;
|
||||||
|
|
||||||
Array reditable_instances;
|
Array reditable_instances;
|
||||||
reditable_instances.resize(editable_instances.size());
|
reditable_instances.resize(editable_instances.size());
|
||||||
for (int i = 0; i < editable_instances.size(); i++) {
|
for (int i = 0; i < editable_instances.size(); i++) {
|
||||||
@ -1785,6 +1913,74 @@ Vector<StringName> SceneState::get_node_groups(int p_idx) const {
|
|||||||
return groups;
|
return groups;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node *SceneState::_recover_node_path_index(Node *p_base, int p_idx) const {
|
||||||
|
// ID paths are only used for recovery, since they are slower to traverse.
|
||||||
|
// This function attempts to recover a node by using IDs in case the path
|
||||||
|
// has disappeared.
|
||||||
|
if (p_idx >= id_paths.size()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PackedInt32Array &id_path = id_paths[p_idx & FLAG_MASK];
|
||||||
|
|
||||||
|
Vector<StringName> full_path;
|
||||||
|
const SceneState *ss = this;
|
||||||
|
for (int i = 0; i < id_path.size(); i++) {
|
||||||
|
int idx = ss->ids.find(id_path[i]);
|
||||||
|
if (idx == -1) {
|
||||||
|
// Not found, but may belong to a base scene, so search.
|
||||||
|
while (ss && idx == -1 && ss->base_scene_idx >= 0) {
|
||||||
|
Ref<PackedScene> sdata = ss->variants[ss->base_scene_idx];
|
||||||
|
if (sdata.is_null()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
Ref<SceneState> ssd = sdata->get_state();
|
||||||
|
if (!ssd.is_valid()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
ss = ssd.ptr();
|
||||||
|
idx = ss->ids.find(id_path[i]);
|
||||||
|
if (idx != -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (idx == -1) {
|
||||||
|
//No luck.
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ERR_FAIL_COND_V(idx >= ss->nodes.size(), nullptr); // Should be a node.
|
||||||
|
|
||||||
|
NodePath so_far = ss->get_node_path(idx);
|
||||||
|
for (int j = 0; j < so_far.get_name_count(); j++) {
|
||||||
|
full_path.push_back(so_far.get_name(j));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == id_path.size() - 1) {
|
||||||
|
break; // Do not go further, we have the path.
|
||||||
|
}
|
||||||
|
|
||||||
|
const NodeData &nd = ss->nodes[idx];
|
||||||
|
// Get instance
|
||||||
|
ERR_FAIL_COND_V(nd.instance < 0, nullptr); // Not an instance, middle of path should be an instance.
|
||||||
|
ERR_FAIL_COND_V(nd.instance & FLAG_INSTANCE_IS_PLACEHOLDER, nullptr); // Instance is somehow a placeholder?!
|
||||||
|
Ref<PackedScene> sdata = ss->variants[nd.instance & FLAG_MASK];
|
||||||
|
ERR_FAIL_COND_V(sdata.is_null(), nullptr);
|
||||||
|
Ref<SceneState> sstate = sdata->get_state();
|
||||||
|
ss = sstate.ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
NodePath recovered_path(full_path, false);
|
||||||
|
return p_base->get_node_or_null(recovered_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t SceneState::get_node_unique_id(int p_idx) const {
|
||||||
|
if (p_idx >= ids.size()) {
|
||||||
|
return Node::UNIQUE_SCENE_ID_UNASSIGNED;
|
||||||
|
}
|
||||||
|
return ids[p_idx];
|
||||||
|
}
|
||||||
|
|
||||||
NodePath SceneState::get_node_path(int p_idx, bool p_for_parent) const {
|
NodePath SceneState::get_node_path(int p_idx, bool p_for_parent) const {
|
||||||
ERR_FAIL_INDEX_V(p_idx, nodes.size(), NodePath());
|
ERR_FAIL_INDEX_V(p_idx, nodes.size(), NodePath());
|
||||||
|
|
||||||
@ -1828,6 +2024,52 @@ NodePath SceneState::get_node_path(int p_idx, bool p_for_parent) const {
|
|||||||
return NodePath(sub_path, false);
|
return NodePath(sub_path, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PackedInt32Array SceneState::get_node_id_path(int p_idx) const {
|
||||||
|
PackedInt32Array pp = get_node_parent_id_path(p_idx);
|
||||||
|
if (pp.is_empty()) {
|
||||||
|
return pp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_idx < ids.size()) {
|
||||||
|
pp.push_back(ids[p_idx]);
|
||||||
|
return pp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PackedInt32Array();
|
||||||
|
}
|
||||||
|
|
||||||
|
PackedInt32Array SceneState::get_node_parent_id_path(int p_idx) const {
|
||||||
|
if (nodes[p_idx].parent < 0 || nodes[p_idx].parent == NO_PARENT_SAVED) {
|
||||||
|
return PackedInt32Array();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodes[p_idx].parent & FLAG_ID_IS_PATH) {
|
||||||
|
int id = nodes[p_idx].parent & FLAG_MASK;
|
||||||
|
if (id >= id_paths.size()) {
|
||||||
|
return PackedInt32Array();
|
||||||
|
}
|
||||||
|
return id_paths[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
return PackedInt32Array();
|
||||||
|
}
|
||||||
|
|
||||||
|
PackedInt32Array SceneState::get_node_owner_id_path(int p_idx) const {
|
||||||
|
if (nodes[p_idx].owner < 0) {
|
||||||
|
return PackedInt32Array();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodes[p_idx].owner & FLAG_ID_IS_PATH) {
|
||||||
|
int id = nodes[p_idx].owner & FLAG_MASK;
|
||||||
|
if (id >= id_paths.size()) {
|
||||||
|
return PackedInt32Array();
|
||||||
|
}
|
||||||
|
return id_paths[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
return PackedInt32Array();
|
||||||
|
}
|
||||||
|
|
||||||
int SceneState::get_node_property_count(int p_idx) const {
|
int SceneState::get_node_property_count(int p_idx) const {
|
||||||
ERR_FAIL_INDEX_V(p_idx, nodes.size(), -1);
|
ERR_FAIL_INDEX_V(p_idx, nodes.size(), -1);
|
||||||
return nodes[p_idx].properties.size();
|
return nodes[p_idx].properties.size();
|
||||||
@ -1909,6 +2151,24 @@ NodePath SceneState::get_connection_target(int p_idx) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PackedInt32Array SceneState::get_connection_target_id_path(int p_idx) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_idx, connections.size(), PackedInt32Array());
|
||||||
|
if (connections[p_idx].to & FLAG_ID_IS_PATH && connections[p_idx].to < id_paths.size()) {
|
||||||
|
return id_paths[connections[p_idx].to];
|
||||||
|
} else {
|
||||||
|
return PackedInt32Array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PackedInt32Array SceneState::get_connection_source_id_path(int p_idx) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_idx, connections.size(), PackedInt32Array());
|
||||||
|
if (connections[p_idx].from & FLAG_ID_IS_PATH && connections[p_idx].from < id_paths.size()) {
|
||||||
|
return id_paths[connections[p_idx].from];
|
||||||
|
} else {
|
||||||
|
return PackedInt32Array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StringName SceneState::get_connection_method(int p_idx) const {
|
StringName SceneState::get_connection_method(int p_idx) const {
|
||||||
ERR_FAIL_INDEX_V(p_idx, connections.size(), StringName());
|
ERR_FAIL_INDEX_V(p_idx, connections.size(), StringName());
|
||||||
return names[connections[p_idx].method];
|
return names[connections[p_idx].method];
|
||||||
@ -2013,12 +2273,13 @@ int SceneState::add_value(const Variant &p_value) {
|
|||||||
return variants.size() - 1;
|
return variants.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SceneState::add_node_path(const NodePath &p_path) {
|
int SceneState::add_node_path(const NodePath &p_path, const PackedInt32Array &p_uid_path) {
|
||||||
node_paths.push_back(p_path);
|
node_paths.push_back(p_path);
|
||||||
|
id_paths.push_back(p_uid_path);
|
||||||
return (node_paths.size() - 1) | FLAG_ID_IS_PATH;
|
return (node_paths.size() - 1) | FLAG_ID_IS_PATH;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SceneState::add_node(int p_parent, int p_owner, int p_type, int p_name, int p_instance, int p_index) {
|
int SceneState::add_node(int p_parent, int p_owner, int p_type, int p_name, int p_instance, int p_index, int32_t p_unique_id) {
|
||||||
NodeData nd;
|
NodeData nd;
|
||||||
nd.parent = p_parent;
|
nd.parent = p_parent;
|
||||||
nd.owner = p_owner;
|
nd.owner = p_owner;
|
||||||
@ -2029,6 +2290,8 @@ int SceneState::add_node(int p_parent, int p_owner, int p_type, int p_name, int
|
|||||||
|
|
||||||
nodes.push_back(nd);
|
nodes.push_back(nd);
|
||||||
|
|
||||||
|
ids.push_back(p_unique_id);
|
||||||
|
|
||||||
return nodes.size() - 1;
|
return nodes.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -39,6 +39,8 @@ class SceneState : public RefCounted {
|
|||||||
Vector<StringName> names;
|
Vector<StringName> names;
|
||||||
Vector<Variant> variants;
|
Vector<Variant> variants;
|
||||||
Vector<NodePath> node_paths;
|
Vector<NodePath> node_paths;
|
||||||
|
Vector<PackedInt32Array> id_paths;
|
||||||
|
mutable PackedInt32Array ids;
|
||||||
Vector<NodePath> editable_instances;
|
Vector<NodePath> editable_instances;
|
||||||
mutable HashMap<NodePath, int> node_path_cache;
|
mutable HashMap<NodePath, int> node_path_cache;
|
||||||
mutable HashMap<int, int> base_scene_node_remap;
|
mutable HashMap<int, int> base_scene_node_remap;
|
||||||
@ -88,7 +90,7 @@ class SceneState : public RefCounted {
|
|||||||
|
|
||||||
Vector<ConnectionData> connections;
|
Vector<ConnectionData> connections;
|
||||||
|
|
||||||
Error _parse_node(Node *p_owner, Node *p_node, int p_parent_idx, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map);
|
Error _parse_node(Node *p_owner, Node *p_node, int p_parent_idx, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map, HashSet<int32_t> &ids_saved);
|
||||||
Error _parse_connections(Node *p_owner, Node *p_node, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map);
|
Error _parse_connections(Node *p_owner, Node *p_node, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map);
|
||||||
|
|
||||||
String path;
|
String path;
|
||||||
@ -101,6 +103,8 @@ class SceneState : public RefCounted {
|
|||||||
|
|
||||||
int _find_base_scene_node_remap_key(int p_idx) const;
|
int _find_base_scene_node_remap_key(int p_idx) const;
|
||||||
|
|
||||||
|
Node *_recover_node_path_index(Node *p_base, int p_idx) const;
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
public:
|
public:
|
||||||
typedef void (*InstantiationWarningNotify)(const String &p_warning);
|
typedef void (*InstantiationWarningNotify)(const String &p_warning);
|
||||||
@ -171,7 +175,11 @@ public:
|
|||||||
StringName get_node_type(int p_idx) const;
|
StringName get_node_type(int p_idx) const;
|
||||||
StringName get_node_name(int p_idx) const;
|
StringName get_node_name(int p_idx) const;
|
||||||
NodePath get_node_path(int p_idx, bool p_for_parent = false) const;
|
NodePath get_node_path(int p_idx, bool p_for_parent = false) const;
|
||||||
|
int32_t get_node_unique_id(int p_idx) const;
|
||||||
|
PackedInt32Array get_node_id_path(int p_idx) const;
|
||||||
|
PackedInt32Array get_node_parent_id_path(int p_idx) const;
|
||||||
NodePath get_node_owner_path(int p_idx) const;
|
NodePath get_node_owner_path(int p_idx) const;
|
||||||
|
PackedInt32Array get_node_owner_id_path(int p_idx) const;
|
||||||
Ref<PackedScene> get_node_instance(int p_idx) const;
|
Ref<PackedScene> get_node_instance(int p_idx) const;
|
||||||
String get_node_instance_placeholder(int p_idx) const;
|
String get_node_instance_placeholder(int p_idx) const;
|
||||||
bool is_node_instance_placeholder(int p_idx) const;
|
bool is_node_instance_placeholder(int p_idx) const;
|
||||||
@ -188,6 +196,10 @@ public:
|
|||||||
StringName get_connection_signal(int p_idx) const;
|
StringName get_connection_signal(int p_idx) const;
|
||||||
NodePath get_connection_target(int p_idx) const;
|
NodePath get_connection_target(int p_idx) const;
|
||||||
StringName get_connection_method(int p_idx) const;
|
StringName get_connection_method(int p_idx) const;
|
||||||
|
|
||||||
|
PackedInt32Array get_connection_source_id_path(int p_idx) const;
|
||||||
|
PackedInt32Array get_connection_target_id_path(int p_idx) const;
|
||||||
|
|
||||||
int get_connection_flags(int p_idx) const;
|
int get_connection_flags(int p_idx) const;
|
||||||
int get_connection_unbinds(int p_idx) const;
|
int get_connection_unbinds(int p_idx) const;
|
||||||
Array get_connection_binds(int p_idx) const;
|
Array get_connection_binds(int p_idx) const;
|
||||||
@ -202,8 +214,8 @@ public:
|
|||||||
|
|
||||||
int add_name(const StringName &p_name);
|
int add_name(const StringName &p_name);
|
||||||
int add_value(const Variant &p_value);
|
int add_value(const Variant &p_value);
|
||||||
int add_node_path(const NodePath &p_path);
|
int add_node_path(const NodePath &p_path, const PackedInt32Array &p_uid_path);
|
||||||
int add_node(int p_parent, int p_owner, int p_type, int p_name, int p_instance, int p_index);
|
int add_node(int p_parent, int p_owner, int p_type, int p_name, int p_instance, int p_index, int32_t p_unique_id);
|
||||||
void add_node_property(int p_node, int p_name, int p_value, bool p_deferred_node_path = false);
|
void add_node_property(int p_node, int p_name, int p_value, bool p_deferred_node_path = false);
|
||||||
void add_node_group(int p_node, int p_group);
|
void add_node_group(int p_node, int p_group);
|
||||||
void set_base_scene(int p_idx);
|
void set_base_scene(int p_idx);
|
||||||
|
|||||||
@ -194,6 +194,8 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars
|
|||||||
int name = -1;
|
int name = -1;
|
||||||
int instance = -1;
|
int instance = -1;
|
||||||
int index = -1;
|
int index = -1;
|
||||||
|
int unique_id = Node::UNIQUE_SCENE_ID_UNASSIGNED;
|
||||||
|
|
||||||
//int base_scene=-1;
|
//int base_scene=-1;
|
||||||
|
|
||||||
if (next_tag.fields.has("name")) {
|
if (next_tag.fields.has("name")) {
|
||||||
@ -202,8 +204,14 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars
|
|||||||
|
|
||||||
if (next_tag.fields.has("parent")) {
|
if (next_tag.fields.has("parent")) {
|
||||||
NodePath np = next_tag.fields["parent"];
|
NodePath np = next_tag.fields["parent"];
|
||||||
np.prepend_period(); //compatible to how it manages paths internally
|
PackedInt32Array np_id;
|
||||||
parent = packed_scene->get_state()->add_node_path(np);
|
if (next_tag.fields.has("parent_id_path")) {
|
||||||
|
np_id = next_tag.fields["parent_id_path"];
|
||||||
|
}
|
||||||
|
parent = packed_scene->get_state()->add_node_path(np, np_id);
|
||||||
|
}
|
||||||
|
if (next_tag.fields.has("unique_id")) {
|
||||||
|
unique_id = next_tag.fields["unique_id"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (next_tag.fields.has("type")) {
|
if (next_tag.fields.has("type")) {
|
||||||
@ -246,7 +254,11 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (next_tag.fields.has("owner")) {
|
if (next_tag.fields.has("owner")) {
|
||||||
owner = packed_scene->get_state()->add_node_path(next_tag.fields["owner"]);
|
PackedInt32Array np_id;
|
||||||
|
if (next_tag.fields.has("owner_uid_path")) {
|
||||||
|
np_id = next_tag.fields["owner_uid_path"];
|
||||||
|
}
|
||||||
|
owner = packed_scene->get_state()->add_node_path(next_tag.fields["owner"], np_id);
|
||||||
} else {
|
} else {
|
||||||
if (parent != -1 && !(type == SceneState::TYPE_INSTANTIATED && instance == -1)) {
|
if (parent != -1 && !(type == SceneState::TYPE_INSTANTIATED && instance == -1)) {
|
||||||
owner = 0; //if no owner, owner is root
|
owner = 0; //if no owner, owner is root
|
||||||
@ -257,7 +269,7 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars
|
|||||||
index = next_tag.fields["index"];
|
index = next_tag.fields["index"];
|
||||||
}
|
}
|
||||||
|
|
||||||
int node_id = packed_scene->get_state()->add_node(parent, owner, type, name, instance, index);
|
int node_id = packed_scene->get_state()->add_node(parent, owner, type, name, instance, index, unique_id);
|
||||||
|
|
||||||
if (next_tag.fields.has("groups")) {
|
if (next_tag.fields.has("groups")) {
|
||||||
Array groups = next_tag.fields["groups"];
|
Array groups = next_tag.fields["groups"];
|
||||||
@ -327,6 +339,16 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars
|
|||||||
int unbinds = 0;
|
int unbinds = 0;
|
||||||
Array binds;
|
Array binds;
|
||||||
|
|
||||||
|
PackedInt32Array from_id;
|
||||||
|
if (next_tag.fields.has("from_uid_path")) {
|
||||||
|
from_id = next_tag.fields["from_uid_path"];
|
||||||
|
}
|
||||||
|
|
||||||
|
PackedInt32Array to_id;
|
||||||
|
if (next_tag.fields.has("to_uid_path")) {
|
||||||
|
to_id = next_tag.fields["to_uid_path"];
|
||||||
|
}
|
||||||
|
|
||||||
if (next_tag.fields.has("flags")) {
|
if (next_tag.fields.has("flags")) {
|
||||||
flags = next_tag.fields["flags"];
|
flags = next_tag.fields["flags"];
|
||||||
}
|
}
|
||||||
@ -345,8 +367,8 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars
|
|||||||
}
|
}
|
||||||
|
|
||||||
packed_scene->get_state()->add_connection(
|
packed_scene->get_state()->add_connection(
|
||||||
packed_scene->get_state()->add_node_path(from.simplified()),
|
packed_scene->get_state()->add_node_path(from.simplified(), from_id),
|
||||||
packed_scene->get_state()->add_node_path(to.simplified()),
|
packed_scene->get_state()->add_node_path(to.simplified(), to_id),
|
||||||
packed_scene->get_state()->add_name(signal),
|
packed_scene->get_state()->add_name(signal),
|
||||||
packed_scene->get_state()->add_name(method),
|
packed_scene->get_state()->add_name(method),
|
||||||
flags,
|
flags,
|
||||||
@ -1963,7 +1985,10 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso
|
|||||||
StringName type = state->get_node_type(i);
|
StringName type = state->get_node_type(i);
|
||||||
StringName name = state->get_node_name(i);
|
StringName name = state->get_node_name(i);
|
||||||
int index = state->get_node_index(i);
|
int index = state->get_node_index(i);
|
||||||
NodePath path = state->get_node_path(i, true);
|
int unique_id = state->get_node_unique_id(i);
|
||||||
|
NodePath parent_path = state->get_node_path(i, true);
|
||||||
|
PackedInt32Array parent_id_path = state->get_node_parent_id_path(i);
|
||||||
|
PackedInt32Array owner_id_path = state->get_node_owner_id_path(i);
|
||||||
NodePath owner = state->get_node_owner_path(i);
|
NodePath owner = state->get_node_owner_path(i);
|
||||||
Ref<PackedScene> instance = state->get_node_instance(i);
|
Ref<PackedScene> instance = state->get_node_instance(i);
|
||||||
String instance_placeholder = state->get_node_instance_placeholder(i);
|
String instance_placeholder = state->get_node_instance_placeholder(i);
|
||||||
@ -1975,16 +2000,27 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso
|
|||||||
if (type != StringName()) {
|
if (type != StringName()) {
|
||||||
header += " type=\"" + String(type) + "\"";
|
header += " type=\"" + String(type) + "\"";
|
||||||
}
|
}
|
||||||
if (path != NodePath()) {
|
if (parent_path != NodePath()) {
|
||||||
header += " parent=\"" + String(path.simplified()).c_escape() + "\"";
|
header += " parent=\"" + String(parent_path.simplified()).c_escape() + "\"";
|
||||||
|
if (parent_id_path.size()) {
|
||||||
|
header += " parent_id_path=" + Variant(parent_id_path).get_construct_string();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (owner != NodePath() && owner != NodePath(".")) {
|
if (owner != NodePath() && owner != NodePath(".")) {
|
||||||
header += " owner=\"" + String(owner.simplified()).c_escape() + "\"";
|
header += " owner=\"" + String(owner.simplified()).c_escape() + "\"";
|
||||||
|
if (owner_id_path.size()) {
|
||||||
|
header += " owner_uid_path=" + Variant(owner_id_path).get_construct_string();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
header += " index=\"" + itos(index) + "\"";
|
header += " index=\"" + itos(index) + "\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (unique_id != Node::UNIQUE_SCENE_ID_UNASSIGNED) {
|
||||||
|
header += " unique_id=" + itos(unique_id) + "";
|
||||||
|
}
|
||||||
|
|
||||||
if (deferred_node_paths.size()) {
|
if (deferred_node_paths.size()) {
|
||||||
header += " node_paths=" + Variant(deferred_node_paths).get_construct_string();
|
header += " node_paths=" + Variant(deferred_node_paths).get_construct_string();
|
||||||
}
|
}
|
||||||
@ -2050,6 +2086,20 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso
|
|||||||
connstr += " flags=" + itos(flags);
|
connstr += " flags=" + itos(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
PackedInt32Array from_idp = state->get_connection_source_id_path(i);
|
||||||
|
if (from_idp.size()) {
|
||||||
|
connstr += " from_uid_path=" + Variant(from_idp).get_construct_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
PackedInt32Array to_idp = state->get_connection_target_id_path(i);
|
||||||
|
if (to_idp.size()) {
|
||||||
|
connstr += " to_uid_path=" + Variant(to_idp).get_construct_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int unbinds = state->get_connection_unbinds(i);
|
int unbinds = state->get_connection_unbinds(i);
|
||||||
if (unbinds > 0) {
|
if (unbinds > 0) {
|
||||||
connstr += " unbinds=" + itos(unbinds);
|
connstr += " unbinds=" + itos(unbinds);
|
||||||
|
|||||||
Reference in New Issue
Block a user