Improve public room connection resilience
This commit is contained in:
parent
15551030f5
commit
2ffcbf51ac
2 changed files with 184 additions and 9 deletions
|
|
@ -5,8 +5,10 @@ signal rooms_updated(rooms: Array[Dictionary])
|
||||||
signal browse_status_changed(message: String, is_error: bool)
|
signal browse_status_changed(message: String, is_error: bool)
|
||||||
signal host_settings_changed(room_name: String, discoverable: bool)
|
signal host_settings_changed(room_name: String, discoverable: bool)
|
||||||
signal host_status_changed(message: String, is_error: bool)
|
signal host_status_changed(message: String, is_error: bool)
|
||||||
|
signal host_state_changed(state: int)
|
||||||
signal public_join_prepared(endpoint: String)
|
signal public_join_prepared(endpoint: String)
|
||||||
signal public_join_status_changed(message: String, is_error: bool)
|
signal public_join_status_changed(message: String, is_error: bool)
|
||||||
|
signal public_join_state_changed(state: int)
|
||||||
|
|
||||||
const BASE_URL_SETTING: String = "network/discovery/base_url"
|
const BASE_URL_SETTING: String = "network/discovery/base_url"
|
||||||
const BASE_URL_ENVIRONMENT: String = "NETFISHING_DISCOVERY_URL"
|
const BASE_URL_ENVIRONMENT: String = "NETFISHING_DISCOVERY_URL"
|
||||||
|
|
@ -19,6 +21,26 @@ const TRAVERSAL_POLL_INTERVAL_SECONDS: float = 1.0
|
||||||
const JOIN_PROBE_INTERVAL_SECONDS: float = 0.35
|
const JOIN_PROBE_INTERVAL_SECONDS: float = 0.35
|
||||||
const TRAVERSAL_PACKET_PREFIX: String = "NETFISHING_TRAVERSAL_V1 "
|
const TRAVERSAL_PACKET_PREFIX: String = "NETFISHING_TRAVERSAL_V1 "
|
||||||
const UPNP_MAPPING_DURATION_SECONDS: int = 3600
|
const UPNP_MAPPING_DURATION_SECONDS: int = 3600
|
||||||
|
const UPNP_RENEW_INTERVAL_SECONDS: float = 2700.0
|
||||||
|
const UPNP_RETRY_INTERVAL_SECONDS: float = 300.0
|
||||||
|
|
||||||
|
enum HostState {
|
||||||
|
UNAVAILABLE,
|
||||||
|
CLOSED,
|
||||||
|
PRIVATE,
|
||||||
|
OPENING_PORT,
|
||||||
|
REGISTERING,
|
||||||
|
VERIFYING,
|
||||||
|
PUBLIC,
|
||||||
|
ERROR,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PublicJoinState {
|
||||||
|
IDLE,
|
||||||
|
REQUESTING_ROUTE,
|
||||||
|
CONNECTING,
|
||||||
|
ERROR,
|
||||||
|
}
|
||||||
|
|
||||||
enum HostRequestKind {
|
enum HostRequestKind {
|
||||||
NONE,
|
NONE,
|
||||||
|
|
@ -33,6 +55,8 @@ var _room_name: String = DEFAULT_ROOM_NAME
|
||||||
var _discoverable: bool = false
|
var _discoverable: bool = false
|
||||||
var _host_status_message: String = ""
|
var _host_status_message: String = ""
|
||||||
var _host_status_is_error: bool = false
|
var _host_status_is_error: bool = false
|
||||||
|
var _host_state: HostState = HostState.CLOSED
|
||||||
|
var _public_join_state: PublicJoinState = PublicJoinState.IDLE
|
||||||
var _lease_room_id: String = ""
|
var _lease_room_id: String = ""
|
||||||
var _lease_token: String = ""
|
var _lease_token: String = ""
|
||||||
var _host_request_kind: HostRequestKind = HostRequestKind.NONE
|
var _host_request_kind: HostRequestKind = HostRequestKind.NONE
|
||||||
|
|
@ -56,6 +80,8 @@ var _pending_join_token: String = ""
|
||||||
var _pending_join_room_id: String = ""
|
var _pending_join_room_id: String = ""
|
||||||
var _upnp_thread: Thread
|
var _upnp_thread: Thread
|
||||||
var _upnp_mapping_in_progress: bool = false
|
var _upnp_mapping_in_progress: bool = false
|
||||||
|
var _upnp_operation_is_renewal: bool = false
|
||||||
|
var _upnp_renew_timer: Timer
|
||||||
var _upnp: UPNP
|
var _upnp: UPNP
|
||||||
var _upnp_mapped_port: int = 0
|
var _upnp_mapped_port: int = 0
|
||||||
|
|
||||||
|
|
@ -92,7 +118,14 @@ func _ready() -> void:
|
||||||
_heartbeat.wait_time = HEARTBEAT_INTERVAL_SECONDS
|
_heartbeat.wait_time = HEARTBEAT_INTERVAL_SECONDS
|
||||||
_heartbeat.one_shot = false
|
_heartbeat.one_shot = false
|
||||||
add_child(_heartbeat)
|
add_child(_heartbeat)
|
||||||
_heartbeat.timeout.connect(_synchronize_host_lease)
|
_heartbeat.timeout.connect(_on_heartbeat_timeout)
|
||||||
|
|
||||||
|
_upnp_renew_timer = Timer.new()
|
||||||
|
_upnp_renew_timer.name = "HostUPnPRenewal"
|
||||||
|
_upnp_renew_timer.one_shot = true
|
||||||
|
add_child(_upnp_renew_timer)
|
||||||
|
_upnp_renew_timer.timeout.connect(_on_upnp_renew_timer_timeout)
|
||||||
|
|
||||||
_traversal_timer = Timer.new()
|
_traversal_timer = Timer.new()
|
||||||
_traversal_timer.name = "HostTraversalPoll"
|
_traversal_timer.name = "HostTraversalPoll"
|
||||||
_traversal_timer.wait_time = TRAVERSAL_POLL_INTERVAL_SECONDS
|
_traversal_timer.wait_time = TRAVERSAL_POLL_INTERVAL_SECONDS
|
||||||
|
|
@ -121,10 +154,13 @@ func setup(session: NetworkSession) -> void:
|
||||||
_session.host_openness_changed.connect(_on_host_openness_changed)
|
_session.host_openness_changed.connect(_on_host_openness_changed)
|
||||||
host_settings_changed.emit(_room_name, _discoverable)
|
host_settings_changed.emit(_room_name, _discoverable)
|
||||||
if not is_configured():
|
if not is_configured():
|
||||||
|
_set_host_state(HostState.UNAVAILABLE)
|
||||||
_set_host_status("Room discovery is not configured in this build.", true)
|
_set_host_status("Room discovery is not configured in this build.", true)
|
||||||
elif _session.is_open_host():
|
elif _session.is_open_host():
|
||||||
|
_set_host_state(HostState.PRIVATE)
|
||||||
_set_host_status("Room is open but unlisted.", false)
|
_set_host_status("Room is open but unlisted.", false)
|
||||||
else:
|
else:
|
||||||
|
_set_host_state(HostState.CLOSED)
|
||||||
_set_host_status("Open the game before listing it publicly.", false)
|
_set_host_status("Open the game before listing it publicly.", false)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -165,9 +201,18 @@ func host_status_is_error() -> bool:
|
||||||
return _host_status_is_error
|
return _host_status_is_error
|
||||||
|
|
||||||
|
|
||||||
|
func get_host_state() -> HostState:
|
||||||
|
return _host_state
|
||||||
|
|
||||||
|
|
||||||
|
func get_public_join_state() -> PublicJoinState:
|
||||||
|
return _public_join_state
|
||||||
|
|
||||||
|
|
||||||
func set_room_name(value: String) -> bool:
|
func set_room_name(value: String) -> bool:
|
||||||
var cleaned: String = _sanitize_room_name(value)
|
var cleaned: String = _sanitize_room_name(value)
|
||||||
if cleaned.is_empty():
|
if cleaned.is_empty():
|
||||||
|
_set_host_state(HostState.ERROR)
|
||||||
_set_host_status("Room name cannot be empty.", true)
|
_set_host_status("Room name cannot be empty.", true)
|
||||||
return false
|
return false
|
||||||
if cleaned == _room_name:
|
if cleaned == _room_name:
|
||||||
|
|
@ -188,6 +233,7 @@ func set_discoverable(enabled: bool) -> bool:
|
||||||
or not _session.is_open_host()
|
or not _session.is_open_host()
|
||||||
or not is_configured()
|
or not is_configured()
|
||||||
):
|
):
|
||||||
|
_set_host_state(HostState.ERROR)
|
||||||
_set_host_status(
|
_set_host_status(
|
||||||
"Open the game before enabling discovery."
|
"Open the game before enabling discovery."
|
||||||
if is_configured()
|
if is_configured()
|
||||||
|
|
@ -201,6 +247,11 @@ func set_discoverable(enabled: bool) -> bool:
|
||||||
host_settings_changed.emit(_room_name, _discoverable)
|
host_settings_changed.emit(_room_name, _discoverable)
|
||||||
if enabled:
|
if enabled:
|
||||||
_heartbeat.start()
|
_heartbeat.start()
|
||||||
|
_set_host_state(
|
||||||
|
HostState.REGISTERING
|
||||||
|
if _session.is_dedicated_host()
|
||||||
|
else HostState.OPENING_PORT
|
||||||
|
)
|
||||||
_set_host_status("Opening public room…", false)
|
_set_host_status("Opening public room…", false)
|
||||||
if _session.is_dedicated_host():
|
if _session.is_dedicated_host():
|
||||||
_synchronize_host_lease()
|
_synchronize_host_lease()
|
||||||
|
|
@ -208,9 +259,11 @@ func set_discoverable(enabled: bool) -> bool:
|
||||||
_begin_upnp_mapping(_session.get_host_port())
|
_begin_upnp_mapping(_session.get_host_port())
|
||||||
else:
|
else:
|
||||||
_heartbeat.stop()
|
_heartbeat.stop()
|
||||||
|
_upnp_renew_timer.stop()
|
||||||
_traversal_timer.stop()
|
_traversal_timer.stop()
|
||||||
_remove_host_lease()
|
_remove_host_lease()
|
||||||
_begin_upnp_cleanup()
|
_begin_upnp_cleanup()
|
||||||
|
_set_host_state(HostState.PRIVATE)
|
||||||
_set_host_status("Room is open but unlisted.", false)
|
_set_host_status("Room is open but unlisted.", false)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
@ -220,11 +273,15 @@ func is_public_join_preparing() -> bool:
|
||||||
|
|
||||||
|
|
||||||
func prepare_public_join(room: Dictionary) -> bool:
|
func prepare_public_join(room: Dictionary) -> bool:
|
||||||
if _join_request_in_flight or _session == null or not is_configured():
|
if _join_request_in_flight:
|
||||||
|
return false
|
||||||
|
if _session == null or not is_configured():
|
||||||
|
_set_public_join_state(PublicJoinState.ERROR)
|
||||||
return false
|
return false
|
||||||
var endpoint: String = room_endpoint(room)
|
var endpoint: String = room_endpoint(room)
|
||||||
var room_id: String = str(room.get("room_id", "")).strip_edges()
|
var room_id: String = str(room.get("room_id", "")).strip_edges()
|
||||||
if endpoint.is_empty() or room_id.is_empty():
|
if endpoint.is_empty() or room_id.is_empty():
|
||||||
|
_set_public_join_state(PublicJoinState.ERROR)
|
||||||
return false
|
return false
|
||||||
_pending_join_endpoint = endpoint
|
_pending_join_endpoint = endpoint
|
||||||
_pending_join_room_id = room_id
|
_pending_join_room_id = room_id
|
||||||
|
|
@ -240,9 +297,11 @@ func prepare_public_join(room: Dictionary) -> bool:
|
||||||
"",
|
"",
|
||||||
)
|
)
|
||||||
if error != OK:
|
if error != OK:
|
||||||
|
_set_public_join_state(PublicJoinState.ERROR)
|
||||||
_clear_pending_join()
|
_clear_pending_join()
|
||||||
return false
|
return false
|
||||||
_join_request_in_flight = true
|
_join_request_in_flight = true
|
||||||
|
_set_public_join_state(PublicJoinState.REQUESTING_ROUTE)
|
||||||
public_join_status_changed.emit("Opening a route to the room…", false)
|
public_join_status_changed.emit("Opening a route to the room…", false)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
@ -298,6 +357,13 @@ func _configured_base_url() -> String:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
func _on_heartbeat_timeout() -> void:
|
||||||
|
_synchronize_host_lease()
|
||||||
|
# NAT bindings can change during a long host session. Re-sending the signed
|
||||||
|
# probe lets discovery refresh the authoritative public endpoint in place.
|
||||||
|
_send_host_verification_probe()
|
||||||
|
|
||||||
|
|
||||||
func _synchronize_host_lease() -> void:
|
func _synchronize_host_lease() -> void:
|
||||||
if not _should_advertise():
|
if not _should_advertise():
|
||||||
if not _lease_room_id.is_empty():
|
if not _lease_room_id.is_empty():
|
||||||
|
|
@ -315,6 +381,8 @@ func _synchronize_host_lease() -> void:
|
||||||
url = "%s/v1/rooms/%s" % [_base_url, _lease_room_id.uri_encode()]
|
url = "%s/v1/rooms/%s" % [_base_url, _lease_room_id.uri_encode()]
|
||||||
headers.append("Authorization: Bearer %s" % _lease_token)
|
headers.append("Authorization: Bearer %s" % _lease_token)
|
||||||
_host_request_kind = HostRequestKind.UPDATE
|
_host_request_kind = HostRequestKind.UPDATE
|
||||||
|
elif _host_state != HostState.REGISTERING:
|
||||||
|
_set_host_state(HostState.REGISTERING)
|
||||||
_start_host_request(url, headers, method, JSON.stringify(_host_payload()))
|
_start_host_request(url, headers, method, JSON.stringify(_host_payload()))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -343,6 +411,7 @@ func _start_host_request(
|
||||||
var error: Error = _host_request.request(url, headers, method, body)
|
var error: Error = _host_request.request(url, headers, method, body)
|
||||||
if error != OK:
|
if error != OK:
|
||||||
_host_request_kind = HostRequestKind.NONE
|
_host_request_kind = HostRequestKind.NONE
|
||||||
|
_set_host_state(HostState.ERROR)
|
||||||
_set_host_status("Could not contact room discovery.", true)
|
_set_host_status("Could not contact room discovery.", true)
|
||||||
return
|
return
|
||||||
_host_request_in_flight = true
|
_host_request_in_flight = true
|
||||||
|
|
@ -367,7 +436,10 @@ func _should_advertise() -> bool:
|
||||||
and is_configured()
|
and is_configured()
|
||||||
and _session != null
|
and _session != null
|
||||||
and _session.is_open_host()
|
and _session.is_open_host()
|
||||||
and not _upnp_mapping_in_progress
|
and (
|
||||||
|
not _upnp_mapping_in_progress
|
||||||
|
or _upnp_operation_is_renewal
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -390,19 +462,36 @@ func _on_host_request_completed(
|
||||||
_lease_token = str(response.get("lease_token", ""))
|
_lease_token = str(response.get("lease_token", ""))
|
||||||
if _lease_room_id.is_empty() or _lease_token.is_empty():
|
if _lease_room_id.is_empty() or _lease_token.is_empty():
|
||||||
_clear_lease()
|
_clear_lease()
|
||||||
|
_set_host_state(HostState.ERROR)
|
||||||
_set_host_status("Discovery returned an invalid lease.", true)
|
_set_host_status("Discovery returned an invalid lease.", true)
|
||||||
else:
|
else:
|
||||||
_apply_traversal_response(response)
|
_apply_traversal_response(response)
|
||||||
_host_verified = bool(room.get("verified", false))
|
_host_verified = bool(room.get("verified", false))
|
||||||
_traversal_timer.start()
|
_traversal_timer.start()
|
||||||
_send_host_verification_probe()
|
_send_host_verification_probe()
|
||||||
_set_host_status("Checking the public route…", false)
|
_set_host_state(
|
||||||
|
HostState.PUBLIC
|
||||||
|
if _host_verified
|
||||||
|
else HostState.VERIFYING
|
||||||
|
)
|
||||||
|
_set_host_status(
|
||||||
|
"Room is listed publicly."
|
||||||
|
if _host_verified
|
||||||
|
else "Checking the public route…",
|
||||||
|
false,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
|
_set_host_state(HostState.ERROR)
|
||||||
_set_host_status(_request_failure(response), true)
|
_set_host_status(_request_failure(response), true)
|
||||||
HostRequestKind.UPDATE:
|
HostRequestKind.UPDATE:
|
||||||
if transport_ok and response_code == HTTPClient.RESPONSE_OK:
|
if transport_ok and response_code == HTTPClient.RESPONSE_OK:
|
||||||
var room: Dictionary = response.get("room", {})
|
var room: Dictionary = response.get("room", {})
|
||||||
_host_verified = bool(room.get("verified", false))
|
_host_verified = bool(room.get("verified", false))
|
||||||
|
_set_host_state(
|
||||||
|
HostState.PUBLIC
|
||||||
|
if _host_verified
|
||||||
|
else HostState.VERIFYING
|
||||||
|
)
|
||||||
_set_host_status(
|
_set_host_status(
|
||||||
"Room is listed publicly."
|
"Room is listed publicly."
|
||||||
if _host_verified
|
if _host_verified
|
||||||
|
|
@ -416,13 +505,15 @@ func _on_host_request_completed(
|
||||||
_clear_lease()
|
_clear_lease()
|
||||||
_host_sync_queued = true
|
_host_sync_queued = true
|
||||||
else:
|
else:
|
||||||
|
_set_host_state(HostState.ERROR)
|
||||||
_set_host_status(_request_failure(response), true)
|
_set_host_status(_request_failure(response), true)
|
||||||
HostRequestKind.DELETE:
|
HostRequestKind.DELETE:
|
||||||
_clear_lease()
|
_clear_lease()
|
||||||
if response_code not in [
|
if not transport_ok or response_code not in [
|
||||||
HTTPClient.RESPONSE_NO_CONTENT,
|
HTTPClient.RESPONSE_NO_CONTENT,
|
||||||
HTTPClient.RESPONSE_NOT_FOUND,
|
HTTPClient.RESPONSE_NOT_FOUND,
|
||||||
] and transport_ok:
|
]:
|
||||||
|
_set_host_state(HostState.ERROR)
|
||||||
_set_host_status(_request_failure(response), true)
|
_set_host_status(_request_failure(response), true)
|
||||||
_:
|
_:
|
||||||
pass
|
pass
|
||||||
|
|
@ -445,6 +536,7 @@ func _on_join_request_completed(
|
||||||
result != HTTPRequest.RESULT_SUCCESS
|
result != HTTPRequest.RESULT_SUCCESS
|
||||||
or response_code != HTTPClient.RESPONSE_CREATED
|
or response_code != HTTPClient.RESPONSE_CREATED
|
||||||
):
|
):
|
||||||
|
_set_public_join_state(PublicJoinState.ERROR)
|
||||||
public_join_status_changed.emit(_request_failure(response), true)
|
public_join_status_changed.emit(_request_failure(response), true)
|
||||||
_clear_pending_join()
|
_clear_pending_join()
|
||||||
return
|
return
|
||||||
|
|
@ -455,11 +547,13 @@ func _on_join_request_completed(
|
||||||
or _traversal_host.is_empty()
|
or _traversal_host.is_empty()
|
||||||
or _traversal_port < 1
|
or _traversal_port < 1
|
||||||
):
|
):
|
||||||
|
_set_public_join_state(PublicJoinState.ERROR)
|
||||||
public_join_status_changed.emit(
|
public_join_status_changed.emit(
|
||||||
"Discovery returned an invalid traversal route.", true
|
"Discovery returned an invalid traversal route.", true
|
||||||
)
|
)
|
||||||
_clear_pending_join()
|
_clear_pending_join()
|
||||||
return
|
return
|
||||||
|
_set_public_join_state(PublicJoinState.CONNECTING)
|
||||||
public_join_status_changed.emit("Connecting…", false)
|
public_join_status_changed.emit("Connecting…", false)
|
||||||
public_join_prepared.emit(_pending_join_endpoint)
|
public_join_prepared.emit(_pending_join_endpoint)
|
||||||
|
|
||||||
|
|
@ -483,6 +577,7 @@ func _on_traversal_timer_timeout() -> void:
|
||||||
])
|
])
|
||||||
var error: Error = _traversal_request.request(url, headers)
|
var error: Error = _traversal_request.request(url, headers)
|
||||||
if error != OK:
|
if error != OK:
|
||||||
|
_set_host_state(HostState.ERROR)
|
||||||
_set_host_status("Could not check incoming public connections.", true)
|
_set_host_status("Could not check incoming public connections.", true)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -564,11 +659,13 @@ func _send_traversal_probe(payload: Dictionary) -> void:
|
||||||
_session.send_traversal_packet(_traversal_host, _traversal_port, packet)
|
_session.send_traversal_packet(_traversal_host, _traversal_port, packet)
|
||||||
|
|
||||||
|
|
||||||
func _begin_upnp_mapping(port: int) -> void:
|
func _begin_upnp_mapping(port: int, is_renewal: bool = false) -> void:
|
||||||
if _upnp_mapping_in_progress or port < 1:
|
if _upnp_mapping_in_progress or port < 1:
|
||||||
_synchronize_host_lease()
|
if not is_renewal:
|
||||||
|
_synchronize_host_lease()
|
||||||
return
|
return
|
||||||
_upnp_mapping_in_progress = true
|
_upnp_mapping_in_progress = true
|
||||||
|
_upnp_operation_is_renewal = is_renewal
|
||||||
_upnp_thread = Thread.new()
|
_upnp_thread = Thread.new()
|
||||||
var error: Error = _upnp_thread.start(
|
var error: Error = _upnp_thread.start(
|
||||||
_configure_upnp_mapping.bind(port)
|
_configure_upnp_mapping.bind(port)
|
||||||
|
|
@ -576,6 +673,9 @@ func _begin_upnp_mapping(port: int) -> void:
|
||||||
if error != OK:
|
if error != OK:
|
||||||
_upnp_thread = null
|
_upnp_thread = null
|
||||||
_upnp_mapping_in_progress = false
|
_upnp_mapping_in_progress = false
|
||||||
|
_upnp_operation_is_renewal = false
|
||||||
|
if is_renewal:
|
||||||
|
_schedule_upnp_renewal(UPNP_RETRY_INTERVAL_SECONDS)
|
||||||
_synchronize_host_lease()
|
_synchronize_host_lease()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -605,21 +705,54 @@ func _configure_upnp_mapping(port: int) -> Dictionary:
|
||||||
func _process(_delta: float) -> void:
|
func _process(_delta: float) -> void:
|
||||||
if _upnp_thread == null or _upnp_thread.is_alive():
|
if _upnp_thread == null or _upnp_thread.is_alive():
|
||||||
return
|
return
|
||||||
|
var was_renewal: bool = _upnp_operation_is_renewal
|
||||||
var result: Variant = _upnp_thread.wait_to_finish()
|
var result: Variant = _upnp_thread.wait_to_finish()
|
||||||
_upnp_thread = null
|
_upnp_thread = null
|
||||||
_upnp_mapping_in_progress = false
|
_upnp_mapping_in_progress = false
|
||||||
|
_upnp_operation_is_renewal = false
|
||||||
|
var mapping_succeeded: bool = false
|
||||||
if typeof(result) == TYPE_DICTIONARY:
|
if typeof(result) == TYPE_DICTIONARY:
|
||||||
var details: Dictionary = result
|
var details: Dictionary = result
|
||||||
if bool(details.get("success", false)):
|
if bool(details.get("success", false)):
|
||||||
_upnp = details.get("upnp") as UPNP
|
_upnp = details.get("upnp") as UPNP
|
||||||
_upnp_mapped_port = int(details.get("port", 0))
|
_upnp_mapped_port = int(details.get("port", 0))
|
||||||
|
mapping_succeeded = true
|
||||||
if not _discoverable:
|
if not _discoverable:
|
||||||
_begin_upnp_cleanup()
|
_begin_upnp_cleanup()
|
||||||
return
|
return
|
||||||
|
if mapping_succeeded:
|
||||||
|
_schedule_upnp_renewal(UPNP_RENEW_INTERVAL_SECONDS)
|
||||||
|
elif was_renewal:
|
||||||
|
_schedule_upnp_renewal(UPNP_RETRY_INTERVAL_SECONDS)
|
||||||
_synchronize_host_lease()
|
_synchronize_host_lease()
|
||||||
|
|
||||||
|
|
||||||
|
func _schedule_upnp_renewal(delay_seconds: float) -> void:
|
||||||
|
if (
|
||||||
|
_upnp_mapped_port < 1
|
||||||
|
or not _discoverable
|
||||||
|
or _session == null
|
||||||
|
or _session.is_dedicated_host()
|
||||||
|
):
|
||||||
|
_upnp_renew_timer.stop()
|
||||||
|
return
|
||||||
|
_upnp_renew_timer.start(delay_seconds)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_upnp_renew_timer_timeout() -> void:
|
||||||
|
if (
|
||||||
|
not _discoverable
|
||||||
|
or _session == null
|
||||||
|
or not _session.is_open_host()
|
||||||
|
or _session.is_dedicated_host()
|
||||||
|
or _upnp_mapped_port < 1
|
||||||
|
):
|
||||||
|
return
|
||||||
|
_begin_upnp_mapping(_upnp_mapped_port, true)
|
||||||
|
|
||||||
|
|
||||||
func _begin_upnp_cleanup() -> void:
|
func _begin_upnp_cleanup() -> void:
|
||||||
|
_upnp_renew_timer.stop()
|
||||||
if _upnp == null or _upnp_mapped_port < 1:
|
if _upnp == null or _upnp_mapped_port < 1:
|
||||||
return
|
return
|
||||||
_upnp.delete_port_mapping(_upnp_mapped_port, "UDP")
|
_upnp.delete_port_mapping(_upnp_mapped_port, "UDP")
|
||||||
|
|
@ -714,14 +847,19 @@ func _request_failure(response: Dictionary) -> String:
|
||||||
|
|
||||||
func _on_session_state_changed(state: NetworkSession.State) -> void:
|
func _on_session_state_changed(state: NetworkSession.State) -> void:
|
||||||
if state == NetworkSession.State.CONNECTING and not _pending_join_token.is_empty():
|
if state == NetworkSession.State.CONNECTING and not _pending_join_token.is_empty():
|
||||||
|
_set_public_join_state(PublicJoinState.CONNECTING)
|
||||||
_join_probe_timer.start()
|
_join_probe_timer.start()
|
||||||
call_deferred("_send_pending_join_probe")
|
call_deferred("_send_pending_join_probe")
|
||||||
elif state in [
|
elif state in [
|
||||||
NetworkSession.State.JOINED_CLIENT,
|
NetworkSession.State.JOINED_CLIENT,
|
||||||
NetworkSession.State.CONNECTION_FAILED,
|
|
||||||
NetworkSession.State.SERVER_LOST,
|
NetworkSession.State.SERVER_LOST,
|
||||||
NetworkSession.State.INACTIVE,
|
NetworkSession.State.INACTIVE,
|
||||||
]:
|
]:
|
||||||
|
_set_public_join_state(PublicJoinState.IDLE)
|
||||||
|
_join_probe_timer.stop()
|
||||||
|
_clear_pending_join()
|
||||||
|
elif state == NetworkSession.State.CONNECTION_FAILED:
|
||||||
|
_set_public_join_state(PublicJoinState.ERROR)
|
||||||
_join_probe_timer.stop()
|
_join_probe_timer.stop()
|
||||||
_clear_pending_join()
|
_clear_pending_join()
|
||||||
if state == NetworkSession.State.OPEN_HOST:
|
if state == NetworkSession.State.OPEN_HOST:
|
||||||
|
|
@ -729,6 +867,7 @@ func _on_session_state_changed(state: NetworkSession.State) -> void:
|
||||||
_heartbeat.start()
|
_heartbeat.start()
|
||||||
_synchronize_host_lease()
|
_synchronize_host_lease()
|
||||||
else:
|
else:
|
||||||
|
_set_host_state(HostState.PRIVATE)
|
||||||
_set_host_status("Room is open but unlisted.", false)
|
_set_host_status("Room is open but unlisted.", false)
|
||||||
return
|
return
|
||||||
if state in [
|
if state in [
|
||||||
|
|
@ -742,9 +881,14 @@ func _on_session_state_changed(state: NetworkSession.State) -> void:
|
||||||
_discoverable = false
|
_discoverable = false
|
||||||
host_settings_changed.emit(_room_name, false)
|
host_settings_changed.emit(_room_name, false)
|
||||||
_heartbeat.stop()
|
_heartbeat.stop()
|
||||||
|
_upnp_renew_timer.stop()
|
||||||
_remove_host_lease()
|
_remove_host_lease()
|
||||||
|
_begin_upnp_cleanup()
|
||||||
if state == NetworkSession.State.PRIVATE_HOST:
|
if state == NetworkSession.State.PRIVATE_HOST:
|
||||||
|
_set_host_state(HostState.CLOSED)
|
||||||
_set_host_status("Open the game before listing it publicly.", false)
|
_set_host_status("Open the game before listing it publicly.", false)
|
||||||
|
else:
|
||||||
|
_set_host_state(HostState.CLOSED)
|
||||||
|
|
||||||
|
|
||||||
func _on_host_openness_changed(is_open: bool) -> void:
|
func _on_host_openness_changed(is_open: bool) -> void:
|
||||||
|
|
@ -773,6 +917,7 @@ func _clear_pending_join() -> void:
|
||||||
|
|
||||||
|
|
||||||
func _exit_tree() -> void:
|
func _exit_tree() -> void:
|
||||||
|
_upnp_renew_timer.stop()
|
||||||
if _upnp_thread != null:
|
if _upnp_thread != null:
|
||||||
var result: Variant = _upnp_thread.wait_to_finish()
|
var result: Variant = _upnp_thread.wait_to_finish()
|
||||||
_upnp_thread = null
|
_upnp_thread = null
|
||||||
|
|
@ -790,6 +935,20 @@ func _set_host_status(message: String, is_error: bool) -> void:
|
||||||
host_status_changed.emit(message, is_error)
|
host_status_changed.emit(message, is_error)
|
||||||
|
|
||||||
|
|
||||||
|
func _set_host_state(state: HostState) -> void:
|
||||||
|
if _host_state == state:
|
||||||
|
return
|
||||||
|
_host_state = state
|
||||||
|
host_state_changed.emit(int(state))
|
||||||
|
|
||||||
|
|
||||||
|
func _set_public_join_state(state: PublicJoinState) -> void:
|
||||||
|
if _public_join_state == state:
|
||||||
|
return
|
||||||
|
_public_join_state = state
|
||||||
|
public_join_state_changed.emit(int(state))
|
||||||
|
|
||||||
|
|
||||||
func _sanitize_room_name(value: String) -> String:
|
func _sanitize_room_name(value: String) -> String:
|
||||||
var cleaned: String = value.strip_edges().replace("\n", " ").replace("\r", " ")
|
var cleaned: String = value.strip_edges().replace("\n", " ").replace("\r", " ")
|
||||||
cleaned = cleaned.replace("\t", " ")
|
cleaned = cleaned.replace("\t", " ")
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,22 @@ func _run() -> void:
|
||||||
assert(not invalid.is_valid())
|
assert(not invalid.is_valid())
|
||||||
|
|
||||||
var discovery := DiscoveryClient.new()
|
var discovery := DiscoveryClient.new()
|
||||||
|
assert(
|
||||||
|
DiscoveryClient.UPNP_RENEW_INTERVAL_SECONDS
|
||||||
|
< float(DiscoveryClient.UPNP_MAPPING_DURATION_SECONDS)
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
DiscoveryClient.UPNP_RETRY_INTERVAL_SECONDS
|
||||||
|
< DiscoveryClient.UPNP_RENEW_INTERVAL_SECONDS
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
discovery.get_host_state()
|
||||||
|
== DiscoveryClient.HostState.CLOSED
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
discovery.get_public_join_state()
|
||||||
|
== DiscoveryClient.PublicJoinState.IDLE
|
||||||
|
)
|
||||||
assert(bool(discovery.call("_valid_public_room", {
|
assert(bool(discovery.call("_valid_public_room", {
|
||||||
"room_id": "empty-dedicated-room",
|
"room_id": "empty-dedicated-room",
|
||||||
"room_name": "Empty Dedicated Room",
|
"room_name": "Empty Dedicated Room",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue