refactor: harden client runtime and exports
This commit is contained in:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user