refactor: harden client runtime and exports

This commit is contained in:
ANG-Server
2026-07-23 00:59:00 +08:00
parent 5d2339a29b
commit fc872fe8af
27 changed files with 902 additions and 856 deletions

View File

@@ -8,6 +8,11 @@ signal reward_claimed(task_id: String, wallet: Dictionary)
var _board: Dictionary = {}
var _loading_board: bool = false
var _account_generation: int = -1
var _recent_activity_at: Dictionary = {}
const ACTIVITY_DEBOUNCE_MSEC: int = 500
const MAX_ACTIVITY_RETRIES: int = 3
const ACTIVITY_RETRY_BASE_SECONDS: float = 0.5
func _ready() -> void:
var auth_manager := get_node_or_null("/root/AuthManager")
@@ -37,14 +42,34 @@ func refresh_board() -> void:
func report_activity(activity: String, target_id: String = "") -> void:
if not _is_authenticated():
return
var normalized_target_id := target_id.strip_edges()
var activity_key := "%s:%s" % [activity, normalized_target_id]
var now_msec := Time.get_ticks_msec()
if now_msec - int(_recent_activity_at.get(activity_key, 0)) < ACTIVITY_DEBOUNCE_MSEC:
return
_recent_activity_at[activity_key] = now_msec
_send_activity(activity, normalized_target_id, 0, _current_account_generation())
func _send_activity(activity: String, target_id: String, retry_count: int, request_generation: int) -> void:
if request_generation != _current_account_generation() or not _is_authenticated():
return
var api := _api_client()
if api == null:
return
var payload := {"activity": activity}
if not target_id.strip_edges().is_empty():
payload["target_id"] = target_id.strip_edges()
var request_generation := _current_account_generation()
api.call("post_json", "/tasks/activities", payload, Callable(self, "_on_activity_response").bind(request_generation), true)
var payload := {
"activity": activity,
"nonce": _create_activity_nonce(),
"occurred_at": Time.get_datetime_string_from_system(true, false),
}
if not target_id.is_empty():
payload["target_id"] = target_id
api.call(
"post_json",
"/tasks/activities",
payload,
Callable(self, "_on_activity_response").bind(activity, target_id, retry_count, request_generation),
true
)
func claim_task(task_id: String) -> void:
if task_id.strip_edges().is_empty() or not _is_authenticated():
@@ -63,6 +88,7 @@ func _refresh_after_ready() -> void:
func _on_auth_state_changed(is_authenticated: bool, _user: Dictionary) -> void:
if not is_authenticated:
_board.clear()
_recent_activity_at.clear()
_loading_board = false
_account_generation = -1
board_changed.emit({})
@@ -78,10 +104,33 @@ func _on_board_response(success: bool, response: Dictionary, error_info: Diction
return
_apply_board_response(response)
func _on_activity_response(success: bool, response: Dictionary, _error_info: Dictionary, request_generation: int) -> void:
if not success or request_generation != _current_account_generation():
func _on_activity_response(
success: bool,
response: Dictionary,
error_info: Dictionary,
activity: String,
target_id: String,
retry_count: int,
request_generation: int
) -> void:
if request_generation != _current_account_generation():
return
_apply_board_response(response)
if success:
_apply_board_response(response)
return
if retry_count >= MAX_ACTIVITY_RETRIES or not _is_retriable_session_conflict(error_info):
return
var delay := ACTIVITY_RETRY_BASE_SECONDS * pow(2.0, retry_count)
get_tree().create_timer(delay).timeout.connect(
Callable(self, "_send_activity").bind(activity, target_id, retry_count + 1, request_generation),
CONNECT_ONE_SHOT
)
func _is_retriable_session_conflict(error_info: Dictionary) -> bool:
if int(error_info.get("response_code", 0)) != 409:
return false
var message := str(error_info.get("message", ""))
return message.contains("会话") or message.contains("位置") or message.contains("交互范围")
func _on_claim_response(success: bool, response: Dictionary, error_info: Dictionary, task_id: String, request_generation: int) -> void:
if request_generation != _current_account_generation():
@@ -126,3 +175,7 @@ func _is_authenticated() -> bool:
func _current_account_generation() -> int:
var auth_manager := get_node_or_null("/root/AuthManager")
return int(auth_manager.call("get_account_generation")) if auth_manager != null and auth_manager.has_method("get_account_generation") else 0
func _create_activity_nonce() -> String:
var random_bytes := Crypto.new().generate_random_bytes(16)
return random_bytes.hex_encode()