fix: synchronize late-joining players

This commit is contained in:
Alexander Sellite 2026-08-23 01:23:00 -04:00
parent 5c3eba1ad2
commit 2aeaf1d44d
6 changed files with 371 additions and 49 deletions

View file

@ -1559,8 +1559,9 @@ func _apply_spawn_entry(entry: Dictionary) -> void:
own_snapshot["visual_yaw"] = float(entry["yaw"])
own_avatar.apply_network_teleport(own_snapshot)
return
var peer_was_added: bool = false
if not _registry.has_peer(peer_id):
_registry.add_peer(
if not _registry.add_peer(
peer_id,
entry["profile_id"],
entry["display_name"],
@ -1568,8 +1569,14 @@ func _apply_spawn_entry(entry: Dictionary) -> void:
str(entry.get("identity_fingerprint", "")),
str(entry.get("identity_public_key", "")),
_sanitized_capabilities(entry.get("capability_flags", [])),
)
):
return
peer_was_added = true
var added_record := _registry.get_peer(peer_id)
if added_record != null:
added_record.profile_authorization = Dictionary(
entry.get("profile_authorization", {})
).duplicate(true)
if added_record != null and added_record.identity_authenticated:
_archive_authenticated_identity(added_record)
var status := _known_players.observe(
@ -1596,6 +1603,8 @@ func _apply_spawn_entry(entry: Dictionary) -> void:
var record := _registry.get_peer(peer_id)
if avatar != null and record != null:
avatar.apply_appearance_snapshot(record.appearance_snapshot)
if peer_was_added and record != null:
peer_authenticated.emit(peer_id, record.display_name)
func _build_spawn_list() -> Array[Dictionary]:
@ -1695,25 +1704,62 @@ func _update_local_operator() -> void:
_local_operator = bool(_operator_peer_ids.get(local_peer_id, false))
func _verify_spawn_identity(entry: Dictionary) -> bool:
var fingerprint := str(entry.get("identity_fingerprint", ""))
var public_pem := NetworkIdentityCrypto.normalize_public_pem(
str(entry.get("identity_public_key", ""))
)
if (
NetworkIdentityCrypto.fingerprint_public_pem(public_pem) != fingerprint
or typeof(entry.get("profile_authorization")) != TYPE_DICTIONARY
):
func verify_profile_authorization(
peer_id: int,
display_name: String,
appearance: Dictionary,
authorization: Dictionary,
) -> bool:
var record := _registry.get_peer(peer_id)
if record == null:
return false
return _verify_profile_authorization_fields(
peer_id,
record.profile_id,
display_name,
appearance,
record.identity_fingerprint,
record.identity_public_key,
authorization,
)
func _verify_spawn_identity(entry: Dictionary) -> bool:
if typeof(entry.get("profile_authorization")) != TYPE_DICTIONARY:
return false
return _verify_profile_authorization_fields(
int(entry.get("peer_id", 0)),
str(entry.get("profile_id", "")),
str(entry.get("display_name", "")),
Dictionary(entry.get("appearance", {})),
str(entry.get("identity_fingerprint", "")),
str(entry.get("identity_public_key", "")),
Dictionary(entry.get("profile_authorization", {})),
)
func _verify_profile_authorization_fields(
peer_id: int,
profile_id: String,
display_name: String,
appearance: Dictionary,
fingerprint: String,
identity_public_key: String,
authorization: Dictionary,
) -> bool:
var public_pem := NetworkIdentityCrypto.normalize_public_pem(
identity_public_key
)
if NetworkIdentityCrypto.fingerprint_public_pem(public_pem) != fingerprint:
return false
var authorization: Dictionary = entry["profile_authorization"]
if authorization.is_empty():
return int(entry.get("peer_id", 0)) == 1
return peer_id == 1
if authorization.get("domain") == "handshake_client_profile":
var hello := NetworkProtocol.make_client_hello(
str(entry.get("profile_id", "")),
str(entry.get("display_name", "")),
profile_id,
display_name,
str(authorization.get("client_nonce", "")),
Dictionary(entry.get("appearance", {})),
appearance,
fingerprint,
authorization.get("signature", PackedByteArray()),
)
@ -1723,10 +1769,23 @@ func _verify_spawn_identity(entry: Dictionary) -> bool:
NetworkProtocol.client_profile_fields(hello),
hello["identity_signature"],
)
if authorization.get("domain") == "appearance_preview":
if str(authorization.get("sender_fingerprint", "")) != fingerprint:
return false
var preview := authorization.duplicate(true)
preview["appearance"] = appearance
return NetworkIdentityCrypto.verify_fields(
NetworkIdentityCrypto.load_public_key(public_pem),
"appearance_preview",
NetworkProfileProtocol.appearance_preview_signature_fields(preview),
preview.get("sender_signature", PackedByteArray()),
)
if authorization.has("sender_signature"):
if str(authorization.get("sender_fingerprint", "")) != fingerprint:
return false
var signed := authorization.duplicate(true)
signed["display_name"] = entry.get("display_name", "")
signed["appearance"] = entry.get("appearance", {})
signed["display_name"] = display_name
signed["appearance"] = appearance
return NetworkIdentityCrypto.verify_fields(
NetworkIdentityCrypto.load_public_key(public_pem),
"profile_update",