Initial WhaleTown V2 frontend

This commit is contained in:
2026-07-19 22:42:20 +08:00
commit 435c578dde
1421 changed files with 54486 additions and 0 deletions

View File

@@ -0,0 +1,227 @@
extends Node
# ============================================================================
# PlayerStateManager.gd - 当前玩家状态聚合管理器
# ============================================================================
# 缓存后端 /player/snapshot并向 UI 广播钱包、背包、外观和设置变化。
# ============================================================================
signal snapshot_changed(snapshot: Dictionary)
signal wallet_changed(wallet: Dictionary)
signal inventory_changed(inventory: Dictionary)
signal appearance_changed(appearance: Dictionary)
signal settings_changed(settings: Dictionary)
signal snapshot_failed(message: String)
var _snapshot: Dictionary = {}
var _wallet: Dictionary = {}
var _inventory: Dictionary = {}
var _appearance: Dictionary = {}
var _settings: Dictionary = {}
var _loadingSnapshot: bool = false
var _snapshotRequestGeneration: int = -1
func _ready() -> void:
var authManager := get_node_or_null("/root/AuthManager")
if authManager != null and authManager.has_signal("auth_state_changed"):
authManager.auth_state_changed.connect(_on_auth_state_changed)
func get_snapshot() -> Dictionary:
return _snapshot.duplicate(true)
func get_wallet() -> Dictionary:
return _wallet.duplicate(true)
func get_inventory() -> Dictionary:
return _inventory.duplicate(true)
func get_appearance() -> Dictionary:
return _appearance.duplicate(true)
func get_settings() -> Dictionary:
return _settings.duplicate(true)
func get_balance() -> int:
return int(_wallet.get("balance", 0))
func is_loaded() -> bool:
return not _snapshot.is_empty()
func refresh_snapshot() -> void:
if not _is_authenticated():
clear_state()
return
if _loadingSnapshot:
return
_loadingSnapshot = true
_snapshotRequestGeneration = _current_account_generation()
var api := _api_client()
if api == null:
_loadingSnapshot = false
_snapshotRequestGeneration = -1
snapshot_failed.emit("ApiClient 不可用")
return
api.call(
"get_json",
"/player/snapshot",
Callable(self, "_on_snapshot_response").bind(_snapshotRequestGeneration),
true
)
func apply_snapshot(snapshot: Dictionary) -> void:
_snapshot = snapshot.duplicate(true)
_wallet = _dictionary(snapshot.get("wallet", {}))
_inventory = _dictionary(snapshot.get("inventory", {}))
_appearance = _dictionary(snapshot.get("appearance", {}))
_settings = _dictionary(snapshot.get("settings", {}))
_apply_to_local_managers()
snapshot_changed.emit(get_snapshot())
wallet_changed.emit(get_wallet())
inventory_changed.emit(get_inventory())
appearance_changed.emit(get_appearance())
settings_changed.emit(get_settings())
func apply_wallet(wallet: Dictionary) -> void:
_wallet = wallet.duplicate(true)
if not _snapshot.is_empty():
_snapshot["wallet"] = _wallet.duplicate(true)
wallet_changed.emit(get_wallet())
func apply_inventory(inventory: Dictionary) -> void:
_inventory = inventory.duplicate(true)
if not _snapshot.is_empty():
_snapshot["inventory"] = _inventory.duplicate(true)
inventory_changed.emit(get_inventory())
_apply_inventory_to_appearance()
func apply_purchase_payload(payload: Dictionary) -> void:
var snapshotVariant: Variant = payload.get("snapshot", {})
if snapshotVariant is Dictionary:
apply_snapshot(snapshotVariant as Dictionary)
return
var walletVariant: Variant = payload.get("wallet", {})
if walletVariant is Dictionary:
apply_wallet(walletVariant as Dictionary)
var inventoryVariant: Variant = payload.get("inventory", {})
if inventoryVariant is Dictionary:
apply_inventory(inventoryVariant as Dictionary)
func update_appearance(skinId: String, completion: Callable = Callable()) -> void:
var api := _api_client()
if api == null:
var errorInfo := {"message": "ApiClient 不可用"}
snapshot_failed.emit(str(errorInfo.get("message")))
_call_update_completion(completion, false, {}, errorInfo)
return
var requestGeneration := _current_account_generation()
api.call(
"patch_json",
"/player/appearance",
{"skin_id": skinId},
Callable(self, "_on_update_response").bind(completion, requestGeneration),
true
)
func update_settings(settings: Dictionary, completion: Callable = Callable()) -> void:
var api := _api_client()
if api == null:
var errorInfo := {"message": "ApiClient 不可用"}
snapshot_failed.emit(str(errorInfo.get("message")))
_call_update_completion(completion, false, {}, errorInfo)
return
var requestGeneration := _current_account_generation()
api.call(
"patch_json",
"/player/settings",
{"settings": settings},
Callable(self, "_on_update_response").bind(completion, requestGeneration),
true
)
func clear_state() -> void:
_snapshot.clear()
_wallet.clear()
_inventory.clear()
_appearance.clear()
_settings.clear()
_loadingSnapshot = false
_snapshotRequestGeneration = -1
snapshot_changed.emit({})
wallet_changed.emit({})
inventory_changed.emit({})
appearance_changed.emit({})
settings_changed.emit({})
func _on_auth_state_changed(isAuthenticated: bool, _user: Dictionary) -> void:
if isAuthenticated:
if _snapshotRequestGeneration != _current_account_generation():
_loadingSnapshot = false
_snapshotRequestGeneration = -1
refresh_snapshot()
else:
clear_state()
func _on_snapshot_response(success: bool, response: Dictionary, errorInfo: Dictionary, requestGeneration: int) -> void:
if requestGeneration != _current_account_generation():
if requestGeneration == _snapshotRequestGeneration:
_loadingSnapshot = false
_snapshotRequestGeneration = -1
return
if requestGeneration == _snapshotRequestGeneration:
_loadingSnapshot = false
_snapshotRequestGeneration = -1
_apply_snapshot_response(success, response, errorInfo)
func _apply_snapshot_response(success: bool, response: Dictionary, errorInfo: Dictionary) -> void:
if not success:
snapshot_failed.emit(str(errorInfo.get("message", "玩家状态读取失败")))
return
var dataVariant: Variant = response.get("data", {})
if dataVariant is Dictionary:
apply_snapshot(dataVariant as Dictionary)
else:
snapshot_failed.emit("玩家状态格式错误")
func _on_update_response(success: bool, response: Dictionary, errorInfo: Dictionary, completion: Callable, requestGeneration: int) -> void:
if requestGeneration != _current_account_generation():
return
_apply_snapshot_response(success, response, errorInfo)
_call_update_completion(completion, success, response, errorInfo)
func _call_update_completion(completion: Callable, success: bool, response: Dictionary, errorInfo: Dictionary) -> void:
if completion.is_valid():
completion.call(success, response, errorInfo)
func _apply_to_local_managers() -> void:
_apply_inventory_to_appearance()
var appearanceManager := get_node_or_null("/root/AppearanceManager")
if appearanceManager != null and appearanceManager.has_method("apply_account_profile"):
var profile := _dictionary(_snapshot.get("profile", {}))
var appearance := _dictionary(_snapshot.get("appearance", {}))
profile["skin_id"] = str(appearance.get("selected_skin_id", profile.get("selected_skin_id", "")))
profile["owned_skin_ids"] = appearance.get("owned_skin_ids", [])
profile["owned_skins"] = appearance.get("owned_skins", [])
appearanceManager.call("apply_account_profile", profile)
var settingsManager := get_node_or_null("/root/SettingsManager")
if settingsManager != null and settingsManager.has_method("apply_account_settings"):
settingsManager.call("apply_account_settings", get_settings())
func _apply_inventory_to_appearance() -> void:
var skinIds: Variant = _inventory.get("skin_ids", [])
var appearanceManager := get_node_or_null("/root/AppearanceManager")
if skinIds is Array and appearanceManager != null and appearanceManager.has_method("set_owned_skin_ids"):
appearanceManager.call("set_owned_skin_ids", skinIds)
func _dictionary(value: Variant) -> Dictionary:
return (value as Dictionary).duplicate(true) if value is Dictionary else {}
func _api_client() -> Node:
return get_node_or_null("/root/ApiClient")
func _is_authenticated() -> bool:
var authManager := get_node_or_null("/root/AuthManager")
return bool(authManager.call("is_authenticated")) if authManager != null and authManager.has_method("is_authenticated") else false
func _current_account_generation() -> int:
var authManager := get_node_or_null("/root/AuthManager")
return int(authManager.call("get_account_generation")) if authManager != null and authManager.has_method("get_account_generation") else 0