fix: stabilize web registration and asset loading
This commit is contained in:
@@ -17,11 +17,14 @@ signal email_verification_failed(message: String)
|
||||
signal profile_update_succeeded(profile: Dictionary)
|
||||
signal profile_update_failed(message: String)
|
||||
signal logout_completed()
|
||||
signal browser_bootstrap_received(kind: String)
|
||||
|
||||
const NetworkConfig = preload("res://_Core/utils/NetworkConfig.gd")
|
||||
|
||||
const DEFAULT_AUTH_CONFIG_PATH: String = "user://auth.cfg"
|
||||
const REQUEST_TIMEOUT: float = 12.0
|
||||
const BROWSER_BOOTSTRAP_STORAGE_KEY: String = "whaletown.auth.bootstrap"
|
||||
const BROWSER_BOOTSTRAP_POLL_INTERVAL: float = 0.25
|
||||
|
||||
var _access_token: String = ""
|
||||
var _refresh_token: String = ""
|
||||
@@ -32,9 +35,25 @@ var _session_generation: int = 0
|
||||
var _account_generation: int = 0
|
||||
var _refresh_in_flight: bool = false
|
||||
var _auth_config_path: String = DEFAULT_AUTH_CONFIG_PATH
|
||||
var _pending_registration_username: String = ""
|
||||
var _pending_registration_password: String = ""
|
||||
var _registration_recovery_in_flight: bool = false
|
||||
var _registration_recovery_error: String = ""
|
||||
var _browser_bootstrap_kind: String = ""
|
||||
var _browser_bootstrap_poll_elapsed: float = 0.0
|
||||
|
||||
func _ready() -> void:
|
||||
_load_cached_session()
|
||||
_try_import_browser_bootstrap(false)
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if OS.get_name() != "Web" or not _browser_bootstrap_kind.is_empty():
|
||||
return
|
||||
_browser_bootstrap_poll_elapsed += delta
|
||||
if _browser_bootstrap_poll_elapsed < BROWSER_BOOTSTRAP_POLL_INTERVAL:
|
||||
return
|
||||
_browser_bootstrap_poll_elapsed = 0.0
|
||||
_try_import_browser_bootstrap(true)
|
||||
|
||||
func _exit_tree() -> void:
|
||||
for request in _active_requests:
|
||||
@@ -70,6 +89,14 @@ func get_account_generation() -> int:
|
||||
func get_auth_config_path() -> String:
|
||||
return _auth_config_path
|
||||
|
||||
func consume_browser_bootstrap_kind() -> String:
|
||||
var kind := _browser_bootstrap_kind
|
||||
_browser_bootstrap_kind = ""
|
||||
return kind
|
||||
|
||||
func get_browser_bootstrap_kind() -> String:
|
||||
return _browser_bootstrap_kind
|
||||
|
||||
func login(identifier: String, password: String) -> void:
|
||||
var normalized_identifier := identifier.strip_edges()
|
||||
if normalized_identifier.is_empty():
|
||||
@@ -144,6 +171,10 @@ func register(username: String, password: String, nickname: String = "", email:
|
||||
if not normalized_skin_id.is_empty():
|
||||
payload["skin_id"] = normalized_skin_id
|
||||
|
||||
_pending_registration_username = normalized_username
|
||||
_pending_registration_password = password
|
||||
_registration_recovery_in_flight = false
|
||||
_registration_recovery_error = ""
|
||||
_advance_account_generation()
|
||||
_request_json("/auth/register", payload, _on_register_response, HTTPClient.METHOD_POST, false, true)
|
||||
|
||||
@@ -289,14 +320,21 @@ func _on_login_response(success: bool, data: Dictionary, error_info: Dictionary)
|
||||
|
||||
func _on_register_response(success: bool, data: Dictionary, error_info: Dictionary) -> void:
|
||||
if not success:
|
||||
register_failed.emit(str(error_info.get("message", "注册失败")))
|
||||
var message := str(error_info.get("message", "注册失败"))
|
||||
if _should_recover_registration(message):
|
||||
_recover_registration_session(message)
|
||||
return
|
||||
_clear_pending_registration()
|
||||
register_failed.emit(message)
|
||||
return
|
||||
|
||||
_apply_auth_payload(data)
|
||||
if not is_authenticated():
|
||||
_clear_pending_registration()
|
||||
register_failed.emit("注册响应缺少 access_token")
|
||||
return
|
||||
|
||||
_clear_pending_registration()
|
||||
_emit_event(EventNames.AUTH_REGISTER_SUCCESS, {
|
||||
"user": get_current_user()
|
||||
})
|
||||
@@ -304,6 +342,49 @@ func _on_register_response(success: bool, data: Dictionary, error_info: Dictiona
|
||||
auth_state_changed.emit(true, get_current_user())
|
||||
_refresh_player_snapshot()
|
||||
|
||||
func _should_recover_registration(message: String) -> bool:
|
||||
if _registration_recovery_in_flight or _pending_registration_username.is_empty() or _pending_registration_password.is_empty():
|
||||
return false
|
||||
return message.begins_with("网络请求失败") \
|
||||
or message.begins_with("网络请求发送失败") \
|
||||
or message.contains("用户名已存在") \
|
||||
or message.contains("用户名已被注册")
|
||||
|
||||
func _recover_registration_session(original_error: String) -> void:
|
||||
_registration_recovery_in_flight = true
|
||||
_registration_recovery_error = original_error
|
||||
_request_json("/auth/login", {
|
||||
"identifier": _pending_registration_username,
|
||||
"password": _pending_registration_password,
|
||||
}, _on_registration_recovery_response, HTTPClient.METHOD_POST, false, true)
|
||||
|
||||
func _on_registration_recovery_response(success: bool, data: Dictionary, error_info: Dictionary) -> void:
|
||||
if not success:
|
||||
var original_error := _registration_recovery_error
|
||||
_clear_pending_registration()
|
||||
register_failed.emit(str(error_info.get("message", original_error if not original_error.is_empty() else "注册状态确认失败")))
|
||||
return
|
||||
|
||||
_apply_auth_payload(data)
|
||||
if not is_authenticated():
|
||||
_clear_pending_registration()
|
||||
register_failed.emit("账号已创建,但登录确认失败")
|
||||
return
|
||||
|
||||
_clear_pending_registration()
|
||||
_emit_event(EventNames.AUTH_REGISTER_SUCCESS, {
|
||||
"user": get_current_user()
|
||||
})
|
||||
register_succeeded.emit(get_current_user())
|
||||
auth_state_changed.emit(true, get_current_user())
|
||||
_refresh_player_snapshot()
|
||||
|
||||
func _clear_pending_registration() -> void:
|
||||
_pending_registration_username = ""
|
||||
_pending_registration_password = ""
|
||||
_registration_recovery_in_flight = false
|
||||
_registration_recovery_error = ""
|
||||
|
||||
func _on_refresh_response(success: bool, data: Dictionary, _error_info: Dictionary) -> void:
|
||||
_refresh_in_flight = false
|
||||
if not success:
|
||||
@@ -334,6 +415,38 @@ func _apply_auth_payload(payload: Dictionary) -> void:
|
||||
_session_generation += 1
|
||||
_save_cached_session()
|
||||
|
||||
func _try_import_browser_bootstrap(emit_signal: bool) -> bool:
|
||||
if OS.get_name() != "Web" or not Engine.has_singleton("JavaScriptBridge"):
|
||||
return false
|
||||
var js_bridge: Object = Engine.get_singleton("JavaScriptBridge")
|
||||
if js_bridge == null:
|
||||
return false
|
||||
var raw: String = str(js_bridge.eval(
|
||||
"window.sessionStorage.getItem('%s') || ''" % BROWSER_BOOTSTRAP_STORAGE_KEY,
|
||||
true
|
||||
)).strip_edges()
|
||||
if raw.is_empty():
|
||||
return false
|
||||
var json := JSON.new()
|
||||
if json.parse(raw) != OK or not (json.data is Dictionary):
|
||||
js_bridge.eval("window.sessionStorage.removeItem('%s')" % BROWSER_BOOTSTRAP_STORAGE_KEY)
|
||||
return false
|
||||
var bootstrap := json.data as Dictionary
|
||||
var kind := str(bootstrap.get("kind", "")).strip_edges()
|
||||
var payload_variant: Variant = bootstrap.get("payload", {})
|
||||
if not ["login", "register"].has(kind) or not (payload_variant is Dictionary):
|
||||
js_bridge.eval("window.sessionStorage.removeItem('%s')" % BROWSER_BOOTSTRAP_STORAGE_KEY)
|
||||
return false
|
||||
_apply_auth_payload(payload_variant as Dictionary)
|
||||
if not is_authenticated():
|
||||
return false
|
||||
_browser_bootstrap_kind = kind
|
||||
js_bridge.eval("window.sessionStorage.removeItem('%s')" % BROWSER_BOOTSTRAP_STORAGE_KEY)
|
||||
if emit_signal:
|
||||
browser_bootstrap_received.emit(kind)
|
||||
auth_state_changed.emit(true, get_current_user())
|
||||
return true
|
||||
|
||||
func _on_profile_response(success: bool, data: Dictionary, _error_info: Dictionary) -> void:
|
||||
if not success:
|
||||
profile_update_failed.emit(str(_error_info.get("message", "玩家资料保存失败")))
|
||||
|
||||
Reference in New Issue
Block a user