forked from xiangwang25/whale-town-front-v2
fix: stabilize web registration and asset loading
This commit is contained in:
@@ -26,12 +26,14 @@ const REGISTRATION_CHOICE_REFERENCE_UPLOAD_BOX_PATH: String = REGISTRATION_CHOIC
|
||||
const REGISTRATION_CHOICE_IMAGE_PLACEHOLDER_BOX_PATH: String = REGISTRATION_CHOICE_ASSET_DIR + "/image_placeholder_box.png"
|
||||
const UI_FONT_PATH: String = "res://assets/fonts/msyh.ttc"
|
||||
const NetworkConfig = preload("res://_Core/utils/NetworkConfig.gd")
|
||||
const WebFilePicker = preload("res://_Core/utils/WebFilePicker.gd")
|
||||
const SKIN_GENERATION_CREATE_ENDPOINT: String = "/api/skin-generation/jobs"
|
||||
const SKIN_GENERATION_POLL_ENDPOINT_TEMPLATE: String = "/api/skin-generation/jobs/%s"
|
||||
const SKIN_GENERATION_POLL_INTERVAL: float = 2.0
|
||||
const SKIN_GENERATION_REQUEST_TIMEOUT: float = 24.0
|
||||
const AVATAR_NATIVE_FILE_FILTERS: Array[String] = ["*.png,*.jpg,*.jpeg,*.webp"]
|
||||
const SKIN_NATIVE_FILE_FILTERS: Array[String] = ["*.png,*.jpg,*.jpeg,*.webp"]
|
||||
const SKIN_NATIVE_FILE_FILTERS: Array[String] = ["*.png"]
|
||||
const WEB_FILE_MAX_BYTES: int = 8 * 1024 * 1024
|
||||
const TEXT_COLOR: Color = Color(0.03, 0.12, 0.23, 1.0)
|
||||
const MUTED_COLOR: Color = Color(0.26, 0.34, 0.44, 1.0)
|
||||
const ACCENT_COLOR: Color = Color(0.10, 0.38, 0.68, 1.0)
|
||||
@@ -91,7 +93,7 @@ var _scene_manager: Node
|
||||
var _appearance_manager: Node
|
||||
var _ui_font: FontFile
|
||||
var _brand_font: SystemFont
|
||||
var _choice_font: SystemFont
|
||||
var _choice_font: Font
|
||||
var _resuming_cached_session: bool = false
|
||||
var _cached_resume_start_generation: int = 0
|
||||
var _skin_generation_create_request: HTTPRequest
|
||||
@@ -114,13 +116,18 @@ var _awaiting_registration_skin_choice: bool = false
|
||||
var _pending_profile_sync_after_auth: bool = false
|
||||
var _deferred_enter_square_after_profile_sync: bool = false
|
||||
var _is_sending_register_code: bool = false
|
||||
var _web_file_picker: RefCounted = WebFilePicker.new()
|
||||
|
||||
func _enter_tree() -> void:
|
||||
_ui_font = load(UI_FONT_PATH) as FontFile
|
||||
_brand_font = SystemFont.new()
|
||||
_brand_font.font_names = PackedStringArray(["Arial Rounded MT Bold", "Avenir Next", "Trebuchet MS", "Arial"])
|
||||
_choice_font = SystemFont.new()
|
||||
_choice_font.font_names = PackedStringArray(["STHeiti Medium", "Hiragino Sans GB", "PingFang SC", "Microsoft YaHei"])
|
||||
if OS.get_name() == "Web":
|
||||
_choice_font = _ui_font
|
||||
else:
|
||||
var systemFont := SystemFont.new()
|
||||
systemFont.font_names = PackedStringArray(["STHeiti Medium", "Hiragino Sans GB", "PingFang SC", "Microsoft YaHei"])
|
||||
_choice_font = systemFont
|
||||
_build_v1_auth_ui()
|
||||
|
||||
func _build_v1_auth_ui() -> void:
|
||||
@@ -692,6 +699,12 @@ func _ready() -> void:
|
||||
|
||||
if _auth_manager != null and bool(_auth_manager.call("is_authenticated")) and _should_auto_resume_cached_session():
|
||||
_resume_cached_session()
|
||||
elif _auth_manager != null and _auth_manager.has_method("consume_browser_bootstrap_kind"):
|
||||
var bootstrapKind := str(_auth_manager.call("consume_browser_bootstrap_kind"))
|
||||
if not bootstrapKind.is_empty():
|
||||
call_deferred("_complete_browser_bootstrap", bootstrapKind)
|
||||
else:
|
||||
login_identifier_input.grab_focus()
|
||||
else:
|
||||
login_identifier_input.grab_focus()
|
||||
|
||||
@@ -725,6 +738,17 @@ func _connect_signals() -> void:
|
||||
_auth_manager.connect("profile_update_succeeded", _on_profile_update_succeeded)
|
||||
_auth_manager.connect("profile_update_failed", _on_profile_update_failed)
|
||||
_auth_manager.connect("auth_state_changed", _on_auth_state_changed)
|
||||
if _auth_manager.has_signal("browser_bootstrap_received"):
|
||||
_auth_manager.connect("browser_bootstrap_received", _complete_browser_bootstrap)
|
||||
|
||||
func _complete_browser_bootstrap(kind: String) -> void:
|
||||
if _is_submitting:
|
||||
return
|
||||
_set_submitting(true, "正在准备小镇...")
|
||||
if kind == "register":
|
||||
_on_register_succeeded(_auth_manager.call("get_current_user"))
|
||||
return
|
||||
_on_login_succeeded(_auth_manager.call("get_current_user"))
|
||||
|
||||
func _setup_skin_generation_requests() -> void:
|
||||
_skin_generation_create_request = HTTPRequest.new()
|
||||
@@ -1626,6 +1650,9 @@ func _hide_skin_workshop() -> void:
|
||||
skin_workshop_overlay.hide()
|
||||
|
||||
func _on_workshop_source_pressed() -> void:
|
||||
if _open_web_file_picker("workshop"):
|
||||
_set_workshop_generation_status("请选择角色参考图片")
|
||||
return
|
||||
var err := DisplayServer.file_dialog_show(
|
||||
"选择角色参考图片",
|
||||
_default_picker_dir(),
|
||||
@@ -1680,6 +1707,9 @@ func _update_skin_library_buttons() -> void:
|
||||
_apply_library_arrow_style(skin_library_next_button, true)
|
||||
|
||||
func _on_upload_avatar_pressed() -> void:
|
||||
if _open_web_file_picker("avatar"):
|
||||
status_label.text = "请选择头像图片"
|
||||
return
|
||||
var err := DisplayServer.file_dialog_show(
|
||||
"选择头像图片",
|
||||
_default_picker_dir(),
|
||||
@@ -1698,6 +1728,11 @@ func _on_upload_avatar_pressed() -> void:
|
||||
status_label.text = "系统文件选择器未打开:%s / %s" % [DisplayServer.get_name(), error_string(err)]
|
||||
|
||||
func _on_upload_skin_pressed() -> void:
|
||||
if _open_web_file_picker("skin"):
|
||||
status_label.text = "请选择8x4角色皮肤PNG"
|
||||
if is_instance_valid(workshop_status_label):
|
||||
workshop_status_label.text = "等待选择8x4透明PNG"
|
||||
return
|
||||
var err := DisplayServer.file_dialog_show(
|
||||
"选择8x4角色皮肤PNG",
|
||||
_default_picker_dir(),
|
||||
@@ -1719,6 +1754,46 @@ func _on_upload_skin_pressed() -> void:
|
||||
return
|
||||
status_label.text = "系统文件选择器未打开:%s / %s" % [DisplayServer.get_name(), error_string(err)]
|
||||
|
||||
func _open_web_file_picker(kind: String) -> bool:
|
||||
if OS.get_name() != "Web" or _web_file_picker == null:
|
||||
return false
|
||||
var accept := ".png" if kind == "skin" else ".png,.jpg,.jpeg,.webp"
|
||||
return bool(_web_file_picker.call("open", accept, WEB_FILE_MAX_BYTES, _on_web_file_selected.bind(kind)))
|
||||
|
||||
func _on_web_file_selected(status: String, fileName: String, _mimeType: String, base64Data: String, kind: String) -> void:
|
||||
if status == "cancel":
|
||||
if kind == "workshop":
|
||||
_set_workshop_generation_status("已取消角色参考图选择")
|
||||
else:
|
||||
status_label.text = "已取消图片选择"
|
||||
return
|
||||
if status == "too_large":
|
||||
var message := "图片不能超过8MB"
|
||||
if kind == "workshop":
|
||||
_set_workshop_generation_status(message)
|
||||
else:
|
||||
status_label.text = message
|
||||
return
|
||||
if status != "ok":
|
||||
var message := "浏览器读取图片失败"
|
||||
if kind == "workshop":
|
||||
_set_workshop_generation_status(message)
|
||||
else:
|
||||
status_label.text = message
|
||||
return
|
||||
|
||||
var path := WebFilePicker.save_base64_file(base64Data, fileName, kind)
|
||||
if path.is_empty():
|
||||
status_label.text = "图片暂存失败"
|
||||
return
|
||||
match kind:
|
||||
"avatar":
|
||||
_on_native_avatar_file_selected(true, PackedStringArray([path]), 0)
|
||||
"skin":
|
||||
_on_native_skin_file_selected(true, PackedStringArray([path]), 0)
|
||||
"workshop":
|
||||
_on_native_workshop_source_file_selected(true, PackedStringArray([path]), 0)
|
||||
|
||||
func _open_macos_file_dialog(title: String, callbackMethod: String) -> bool:
|
||||
if OS.get_name() != "macOS":
|
||||
return false
|
||||
|
||||
@@ -14,6 +14,7 @@ const ICON_SCRIPT: Script = preload("res://scenes/ui/SettingsPanelIcon.gd")
|
||||
const TOGGLE_SCRIPT: Script = preload("res://scenes/ui/SettingsToggle.gd")
|
||||
const SLIDER_SCRIPT: Script = preload("res://scenes/ui/SettingsSlider.gd")
|
||||
const BADGE_TEXTURE_PATH: String = "res://assets/ui/settings/settings_whale_badge_asset_v1.png"
|
||||
const WebFilePicker = preload("res://_Core/utils/WebFilePicker.gd")
|
||||
|
||||
const TEXT_COLOR: Color = Color(0.188, 0.294, 0.424)
|
||||
const MUTED_COLOR: Color = Color(0.560, 0.639, 0.733)
|
||||
@@ -22,6 +23,7 @@ const SOFT_BLUE: Color = Color(0.918, 0.961, 0.992, 0.92)
|
||||
const DANGER_COLOR: Color = Color(0.894, 0.392, 0.357)
|
||||
const MOVEMENT_ACTIONS: Array[String] = ["move_left", "move_right", "move_up", "move_down"]
|
||||
const AVATAR_NATIVE_FILE_FILTERS: Array[String] = ["*.png,*.jpg,*.jpeg,*.webp"]
|
||||
const WEB_FILE_MAX_BYTES: int = 8 * 1024 * 1024
|
||||
|
||||
const CATEGORIES: Array[Dictionary] = [
|
||||
{"id": "basic", "label": "基础", "icon": "basic"},
|
||||
@@ -71,6 +73,7 @@ var _savedSettings: Dictionary = DEFAULT_SETTINGS.duplicate(true)
|
||||
var _isOpen: bool = false
|
||||
var _transitionTween: Tween
|
||||
var _avatarUploadPending: bool = false
|
||||
var _webFilePicker: RefCounted = WebFilePicker.new()
|
||||
|
||||
func _ready() -> void:
|
||||
visible = false
|
||||
@@ -976,6 +979,9 @@ func _on_reconnect_pressed() -> void:
|
||||
_statusLabel.text = "已请求重新连接聊天服务"
|
||||
|
||||
func _on_upload_avatar_pressed() -> void:
|
||||
if _try_open_web_avatar_file_picker():
|
||||
_set_account_status("请选择头像图片")
|
||||
return
|
||||
if _open_macos_avatar_file_dialog():
|
||||
return
|
||||
if _try_open_native_avatar_file_picker():
|
||||
@@ -983,6 +989,27 @@ func _on_upload_avatar_pressed() -> void:
|
||||
return
|
||||
_set_account_status("系统文件选择器暂时不可用")
|
||||
|
||||
func _try_open_web_avatar_file_picker() -> bool:
|
||||
if OS.get_name() != "Web" or _webFilePicker == null:
|
||||
return false
|
||||
return bool(_webFilePicker.call("open", ".png,.jpg,.jpeg,.webp", WEB_FILE_MAX_BYTES, _on_web_avatar_file_selected))
|
||||
|
||||
func _on_web_avatar_file_selected(status: String, fileName: String, _mimeType: String, base64Data: String) -> void:
|
||||
if status == "cancel":
|
||||
_set_account_status("已取消头像选择")
|
||||
return
|
||||
if status == "too_large":
|
||||
_set_account_status("头像图片不能超过8MB")
|
||||
return
|
||||
if status != "ok":
|
||||
_set_account_status("浏览器读取头像失败")
|
||||
return
|
||||
var path := WebFilePicker.save_base64_file(base64Data, fileName, "avatar")
|
||||
if path.is_empty():
|
||||
_set_account_status("头像图片暂存失败")
|
||||
return
|
||||
_on_avatar_file_selected(path)
|
||||
|
||||
func _try_open_native_avatar_file_picker() -> bool:
|
||||
if DisplayServer.get_name() == "headless":
|
||||
return false
|
||||
|
||||
40
scenes/ui/WebBootstrap.gd
Normal file
40
scenes/ui/WebBootstrap.gd
Normal file
@@ -0,0 +1,40 @@
|
||||
extends Control
|
||||
|
||||
var _auth_manager: Node
|
||||
var _scene_manager: Node
|
||||
var _handled: bool = false
|
||||
|
||||
func _ready() -> void:
|
||||
_auth_manager = get_node_or_null("/root/AuthManager")
|
||||
_scene_manager = get_node_or_null("/root/SceneManager")
|
||||
if OS.get_name() != "Web":
|
||||
_scene_manager.call_deferred("change_scene", "auth", false)
|
||||
return
|
||||
if _auth_manager == null:
|
||||
return
|
||||
if _auth_manager.has_signal("browser_bootstrap_received"):
|
||||
_auth_manager.connect("browser_bootstrap_received", _on_browser_bootstrap_received)
|
||||
var kind := str(_auth_manager.call("get_browser_bootstrap_kind")) if _auth_manager.has_method("get_browser_bootstrap_kind") else ""
|
||||
if not kind.is_empty():
|
||||
call_deferred("_on_browser_bootstrap_received", kind)
|
||||
|
||||
func _on_browser_bootstrap_received(kind: String) -> void:
|
||||
if _handled or _scene_manager == null:
|
||||
return
|
||||
_handled = true
|
||||
if kind == "register":
|
||||
_scene_manager.call("change_scene", "auth", false)
|
||||
return
|
||||
if _auth_manager.has_method("consume_browser_bootstrap_kind"):
|
||||
_auth_manager.call("consume_browser_bootstrap_kind")
|
||||
var appearanceManager := get_node_or_null("/root/AppearanceManager")
|
||||
if appearanceManager != null and appearanceManager.has_method("apply_account_profile"):
|
||||
appearanceManager.call("apply_account_profile", _auth_manager.call("get_current_profile"))
|
||||
var chatManager := get_node_or_null("/root/ChatManager")
|
||||
var token := str(_auth_manager.call("get_access_token"))
|
||||
if chatManager != null and not token.is_empty():
|
||||
chatManager.call("set_game_token", token)
|
||||
chatManager.call("connect_to_chat_server")
|
||||
if _auth_manager.has_method("fetch_profile"):
|
||||
_auth_manager.call("fetch_profile")
|
||||
_scene_manager.call("change_scene", "square", false)
|
||||
1
scenes/ui/WebBootstrap.gd.uid
Normal file
1
scenes/ui/WebBootstrap.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://22fxlhvp012i
|
||||
12
scenes/ui/WebBootstrap.tscn
Normal file
12
scenes/ui/WebBootstrap.tscn
Normal file
@@ -0,0 +1,12 @@
|
||||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://scenes/ui/WebBootstrap.gd" id="1_bootstrap"]
|
||||
|
||||
[node name="WebBootstrap" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_bootstrap")
|
||||
Reference in New Issue
Block a user