Files
whale-town-front-v2/_Core/managers/PlayerStateManager.gd

228 lines
8.3 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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