extends Node class_name _NetworkIdentityServer ## Tracks identities for use over the network ## ## Internally, netfox sends packets that refer to specific nodes, e.g. to ## synchronize their state. Nodes are referenced based on their path in the ## scene tree. To save on bandwidth, node paths are replaced with numeric IDs ## when possible. ## [br][br] ## This class manages these numerical IDs and their broadcast among peers. ## [br][br] ## Each node that is to be referred to over the network must be registered using ## [method register_node]. It is best to deregister freed nodes using [method ## deregister_node]. ## [br][br] ## Node registration and deregistration are done automatically by the high-level ## nodes ( like RollbackSynchronizer ), and usually doesn't need to be done ## manually. var _command_server: _NetworkCommandServer var _next_id := 0 var _identifiers := {} # object to NetworkIdentifier var _push_queue := {} # peer to (full name to id) var _identifier_by_name := {} # full name to NetworkIdentifier var _identifier_by_id := {} # peer to (id to NetworkIdentifier) var _cmd_ids: _NetworkCommandServer.Command static var _logger := NetfoxLogger._for_netfox("NetworkIdentityServer") func _init(p_command_server: _NetworkCommandServer = null): _command_server = p_command_server func _ready(): if not _command_server: _command_server = NetworkCommandServer _cmd_ids = _command_server.register_command(_handle_ids, MultiplayerPeer.TRANSFER_MODE_RELIABLE) # TODO(#561): Handle peer disconnect by clearing up data ## Register a node ## [br][br] ## Once a node is registered, it can be referred to over the network. The same ## node must be registered with the same node path on all peers. func register_node(node: Node) -> void: if not node.is_inside_tree(): _logger.error("Can't register node %s that is not inside tree!", [node]) return _register(node, node.get_path()) ## Deregister a node, freeing all identity data associated with it func deregister_node(node: Node) -> void: _deregister(node) ## Clear all identities func clear() -> void: _identifiers.clear() _identifier_by_name.clear() _identifier_by_id.clear() _next_id = 0 ## Queue sending the numeric ID of [param node] to [param peer] ## [br][br] ## This happens automatically when the [param node] is first referred to in a ## packet. This method allows frontloading that, at a more feasible moment. ## [br][br] ## This method does not send any data. To force sending identifiers, use [method ## flush_queue]. func queue_identifier_for(node: Node, peer: int) -> bool: var identifier := _get_identifier_of(node) if not identifier: return false _queue_for(identifier, peer) return true ## Broadcast all identities queued for synchronization ## [br][br] ## Flushed automatically by default func flush_queue() -> void: for peer in _push_queue: _cmd_ids.send(_serialize_ids(_push_queue[peer]), peer) _push_queue.clear() func _get_identifier_of(what: Object) -> _NetworkIdentifier: return _identifiers.get(what) func _resolve_reference(peer: int, identity_reference: _NetworkIdentityReference, allow_queue: bool = true) -> _NetworkIdentifier: if identity_reference.has_id(): return _get_identifier_by_id(peer, identity_reference.get_id()) else: var identifier := _get_identifier_by_name(identity_reference.get_full_name()) if allow_queue and identifier: _queue_for(identifier, peer) return identifier func _queue_for(identifier: _NetworkIdentifier, peer: int) -> void: if not _push_queue.has(peer): _push_queue[peer] = { identifier.get_full_name(): identifier.get_id_for(peer) } else: _push_queue[peer][identifier.get_full_name()] = identifier.get_id_for(peer) func _register(what: Object, path: String) -> void: if _identifiers.has(what): return var identifier := _NetworkIdentifier.new(what, path, _make_id(), multiplayer.get_unique_id()) _identifiers[what] = identifier _identifier_by_name[identifier.get_full_name()] = identifier identifier.on_id.connect(func(peer: int, id: int): _update_id_cache(identifier, peer, id)) _update_id_cache(identifier, multiplayer.get_unique_id(), identifier.get_local_id()) func _deregister(what: Object) -> void: if not _identifiers.has(what): return var identifier := _identifiers[what] as _NetworkIdentifier _identifiers.erase(what) _identifier_by_name.erase(identifier.get_full_name()) _erase_from_id_cache(identifier) func _get_identifier_by_name(full_name: String) -> _NetworkIdentifier: return _identifier_by_name.get(full_name) func _get_identifier_by_id(peer: int, id: int) -> _NetworkIdentifier: return _identifier_by_id.get(peer, {}).get(id, null) func _make_id() -> int: _next_id += 1 return _next_id func _update_id_cache(identifier: _NetworkIdentifier, peer: int, id: int) -> void: if not _identifier_by_id.has(peer): _identifier_by_id[peer] = { id: identifier } else: _identifier_by_id[peer][id] = identifier func _erase_from_id_cache(identifier: _NetworkIdentifier) -> void: for peer in identifier.get_known_peers(): if not _identifier_by_id.has(peer): # nani? continue var cache := _identifier_by_id.get(peer) as Dictionary cache.erase(identifier.get_id_for(peer)) if cache.is_empty(): _identifier_by_id.erase(peer) func _handle_ids(sender: int, data: PackedByteArray) -> void: var ids := _deserialize_ids(data) for full_name in ids: var id := ids[full_name] as int var identifier := _get_identifier_by_name(full_name) if not identifier: # Deleted since then _logger.debug("Received identifier for unknown object with full name %s, id #%d", [full_name, id]) continue identifier.set_id_for(sender, id) func _serialize_ids(ids: Dictionary) -> PackedByteArray: var buffer := StreamPeerBuffer.new() var varuint := NetworkSchemas.varuint() for full_name in ids.keys(): var id := ids[full_name] as int buffer.put_utf8_string(full_name) varuint.encode(ids[full_name], buffer) return buffer.data_array func _deserialize_ids(data: PackedByteArray) -> Dictionary: var ids := {} var varuint := NetworkSchemas.varuint() var buffer := StreamPeerBuffer.new() buffer.data_array = data while buffer.get_available_bytes() > 0: var full_name := buffer.get_utf8_string() var id := varuint.decode(buffer) as int ids[full_name] = id return ids