feat(network): add selectable privacy-preserving discovery

This commit is contained in:
Alexander Sellite 2026-09-01 10:21:26 -04:00
parent 9cf0c8fc9c
commit 4d31fd1517
26 changed files with 959 additions and 153 deletions

View file

@ -178,16 +178,39 @@ func _run() -> void:
)
assert(not FileAccess.file_exists(discovery_settings_path))
assert(discovery.set_room_name("Client Room"))
assert(
discovery.get_host_discovery_mode()
== DiscoveryClient.HOST_DISCOVERY_PRIVATE
)
assert(discovery.set_host_connection_mode(
DiscoveryClient.HOST_CONNECTION_RELAY
))
assert(
discovery.get_host_connection_mode()
== DiscoveryClient.HOST_CONNECTION_RELAY
)
assert(
discovery.get_host_discovery_mode()
== DiscoveryClient.HOST_DISCOVERY_PRIVATE
)
var client_settings: PackedByteArray = FileAccess.get_file_as_bytes(
discovery_settings_path
)
assert(not client_settings.is_empty())
var dedicated_discovery := DiscoveryClient.new()
dedicated_discovery.call("_load_settings")
dedicated_discovery.set(
"_host_connection_preference",
DiscoveryClient.HOST_CONNECTION_RELAY,
)
assert(
dedicated_discovery.configure_dedicated_runtime("Headless Room")
)
assert(dedicated_discovery.get_room_name() == "Headless Room")
assert(
dedicated_discovery.get_host_connection_mode()
== DiscoveryClient.HOST_CONNECTION_DIRECT
)
assert(
FileAccess.get_file_as_bytes(discovery_settings_path)
== client_settings
@ -210,16 +233,26 @@ func _run() -> void:
var listed_room := {
"room_id": "empty-dedicated-room",
"room_name": "Empty Dedicated Room",
"address": "203.0.113.10",
"port": 7777,
"current_players": 0,
"max_players": 8,
"game_version": str(ProjectSettings.get_setting(
"application/config/version", "unknown"
)),
"protocol_version": NetworkProtocol.PROTOCOL_VERSION,
"connection_mode": "direct",
"ip_privacy": "peer_visible",
"host_kind": "dedicated",
}
assert(bool(discovery.call("_valid_public_room", listed_room)))
assert(not listed_room.has("address"))
assert(not listed_room.has("port"))
var relayed_room: Dictionary = listed_room.duplicate(true)
relayed_room["connection_mode"] = "relay"
relayed_room["ip_privacy"] = "relayed"
assert(not bool(discovery.call("_valid_public_room", relayed_room)))
var player_relayed_room: Dictionary = relayed_room.duplicate(true)
player_relayed_room["host_kind"] = "player"
assert(bool(discovery.call("_valid_public_room", player_relayed_room)))
var incompatible_room: Dictionary = listed_room.duplicate(true)
incompatible_room["game_version"] = "0.0.0-alpha"
assert(not bool(discovery.call("_valid_public_room", incompatible_room)))

View file

@ -0,0 +1,98 @@
extends SceneTree
const FINGERPRINT: String = (
"abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
)
const JoinGamePageScene: PackedScene = preload(
"res://ui/network/join_game_page.tscn"
)
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
var endpoint := EndpointParser.parse("203.0.113.20:49152")
assert(endpoint.is_valid())
var direct_route := ConnectionRoute.direct(endpoint)
assert(direct_route.is_valid())
assert("203.0.113.20" not in direct_route.display_description)
var route := ConnectionRoute.discovery_direct(
endpoint,
"opaque-room-id",
"Pond Friends",
)
assert(route.is_valid())
assert(route.is_discovery_join())
assert(route.display_description == "Pond Friends")
assert("203.0.113.20" not in route.display_description)
var relay_route := ConnectionRoute.discovery_relay(
endpoint,
"opaque-room-id",
"Pond Friends",
)
assert(relay_route.is_valid())
assert(relay_route.is_discovery_join())
assert(relay_route.is_relay())
assert("203.0.113.20" not in relay_route.display_description)
var join_page := JoinGamePageScene.instantiate() as JoinGamePage
root.add_child(join_page)
assert((join_page.get_node("%Address") as LineEdit).secret)
assert(str(join_page.call("_display_endpoint", "203.0.113.20:49152")) == "*****")
join_page.set("_addresses_visible", true)
join_page.call("_refresh_address_privacy_controls")
assert(not (join_page.get_node("%Address") as LineEdit).secret)
assert(
str(join_page.call("_display_endpoint", "203.0.113.20:49152"))
== "203.0.113.20:49152"
)
var saved_entry := SavedServerEntry.new()
saved_entry.kind = SavedServerEntry.Kind.SAVED
saved_entry.display_name = "203.0.113.20"
saved_entry.host = "203.0.113.20"
saved_entry.normalized_host = "203.0.113.20"
saved_entry.normalized_endpoint = "203.0.113.20:49152"
join_page.set("_addresses_visible", false)
assert(str(join_page.call("_display_entry_name", saved_entry)) == "saved server")
join_page.set("_addresses_visible", true)
assert(str(join_page.call("_display_entry_name", saved_entry)) == "203.0.113.20")
var data_root := PlayerDataRoot.new()
root.add_child(data_root)
var portable_path := ProjectSettings.globalize_path(
"user://discovery-privacy-validation"
)
var created := data_root.create_unbound_root(portable_path)
assert(bool(created.get("ok", false)))
assert(data_root.activate_process_root(portable_path))
var store := ServerTrustStore.new()
root.add_child(store)
var trust_path := data_root.path_for(&"server_trust")
store.configure_storage(trust_path, data_root)
assert(store.verify_identity(FINGERPRINT) == ServerTrustStore.Verification.FIRST_SEEN)
assert(store.trust_identity(FINGERPRINT, "Pond Friends"))
assert(store.verify_identity(FINGERPRINT) == ServerTrustStore.Verification.MATCH)
var saved_text := FileAccess.get_file_as_string(trust_path)
assert("203.0.113.20" not in saved_text)
assert("normalized_endpoint" not in saved_text)
assert("normalized_host" not in saved_text)
assert("\"port\"" not in saved_text)
var reloaded := ServerTrustStore.new()
root.add_child(reloaded)
reloaded.configure_storage(trust_path, data_root)
assert(
reloaded.verify_identity(FINGERPRINT)
== ServerTrustStore.Verification.MATCH
)
var record := reloaded.get_identity_record(FINGERPRINT)
assert(record.get("route_kind") == "discovery")
assert(not record.has("normalized_endpoint"))
assert(not record.has("normalized_host"))
assert(not record.has("port"))
print("Discovery privacy validation: PASS")
quit()

View file

@ -0,0 +1 @@
uid://dmtlh1kpm7bl1