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