feat: expand multiplayer, chat, and release support

- add network NPC synchronization and dialogue interactions
- add world bulletin publishing and display
- improve authentication, session refresh, and appearance sync
- synchronize player direction and movement animations
- improve input focus and progressive Web loading
- add macOS/Windows builds and deployment configuration
- include required fonts, shaders, and runtime assets
This commit is contained in:
2026-09-08 21:37:43 +08:00
parent 175621f66c
commit fc6c3c1bd3
87 changed files with 4546 additions and 506 deletions

View File

@@ -13,6 +13,16 @@ const NetworkConfig = preload("res://_Core/utils/NetworkConfig.gd")
const REQUEST_TIMEOUT: float = 12.0
var _activeRequests: Array[HTTPRequest] = []
var _pendingAuthRetries: Array[Dictionary] = []
var _awaitingSessionRefresh: bool = false
func _ready() -> void:
call_deferred("_connect_auth_manager")
func _connect_auth_manager() -> void:
var authManager := get_node_or_null("/root/AuthManager")
if authManager != null and authManager.has_signal("session_refresh_completed") and not authManager.is_connected("session_refresh_completed", _on_session_refresh_completed):
authManager.connect("session_refresh_completed", _on_session_refresh_completed)
func _exit_tree() -> void:
for request in _activeRequests:
@@ -20,6 +30,7 @@ func _exit_tree() -> void:
request.cancel_request()
request.queue_free()
_activeRequests.clear()
_pendingAuthRetries.clear()
func get_json(endpoint: String, callback: Callable, authenticated: bool = true) -> void:
request_json(endpoint, {}, callback, HTTPClient.METHOD_GET, authenticated)
@@ -34,6 +45,9 @@ func put_json(endpoint: String, payload: Dictionary, callback: Callable, authent
request_json(endpoint, payload, callback, HTTPClient.METHOD_PUT, authenticated)
func request_json(endpoint: String, payload: Dictionary, callback: Callable, method: int = HTTPClient.METHOD_GET, authenticated: bool = true) -> void:
_request_json(endpoint, payload, callback, method, authenticated, 0)
func _request_json(endpoint: String, payload: Dictionary, callback: Callable, method: int, authenticated: bool, authRetryCount: int) -> void:
var request := HTTPRequest.new()
request.timeout = REQUEST_TIMEOUT
add_child(request)
@@ -42,7 +56,7 @@ func request_json(endpoint: String, payload: Dictionary, callback: Callable, met
request.request_completed.connect(func(result: int, responseCode: int, _headers: PackedStringArray, body: PackedByteArray) -> void:
_activeRequests.erase(request)
request.queue_free()
_handle_response(endpoint, result, responseCode, body, callback)
_handle_response(endpoint, result, responseCode, body, callback, payload, method, authenticated, authRetryCount)
)
var url := "%s%s" % [NetworkConfig.get_api_base_url(), endpoint]
@@ -64,13 +78,27 @@ func _headers(authenticated: bool) -> PackedStringArray:
headers.append("Authorization: Bearer %s" % accessToken)
return headers
func _handle_response(endpoint: String, result: int, responseCode: int, body: PackedByteArray, callback: Callable) -> void:
func _handle_response(
endpoint: String,
result: int,
responseCode: int,
body: PackedByteArray,
callback: Callable,
payload: Dictionary,
method: int,
authenticated: bool,
authRetryCount: int
) -> void:
if result != HTTPRequest.RESULT_SUCCESS:
var message := "网络请求失败: %s" % _http_result_to_string(result)
request_failed.emit(endpoint, message)
callback.call(false, {}, {"message": message})
return
if responseCode == 401 and authenticated and authRetryCount < 1:
_queue_authenticated_retry(endpoint, payload, callback, method, authRetryCount + 1)
return
var bodyText := body.get_string_from_utf8()
var json := JSON.new()
if json.parse(bodyText) != OK:
@@ -100,6 +128,63 @@ func _handle_response(endpoint: String, result: int, responseCode: int, body: Pa
request_failed.emit(endpoint, str(errorInfo.get("message", "请求失败")))
callback.call(false, response, errorInfo)
func _queue_authenticated_retry(endpoint: String, payload: Dictionary, callback: Callable, method: int, authRetryCount: int) -> void:
_pendingAuthRetries.append({
"endpoint": endpoint,
"payload": payload.duplicate(true),
"callback": callback,
"method": method,
"auth_retry_count": authRetryCount,
"account_generation": _current_account_generation(),
})
if _awaitingSessionRefresh:
return
var authManager := get_node_or_null("/root/AuthManager")
if authManager == null or not authManager.has_method("refresh_session"):
_finish_authenticated_retries(false)
return
_awaitingSessionRefresh = true
if not bool(authManager.call("refresh_session")):
_finish_authenticated_retries(false)
func _on_session_refresh_completed(success: bool) -> void:
_finish_authenticated_retries(success)
func _finish_authenticated_retries(success: bool) -> void:
var retries := _pendingAuthRetries.duplicate()
_pendingAuthRetries.clear()
_awaitingSessionRefresh = false
var currentGeneration := _current_account_generation()
for retryVariant in retries:
if not (retryVariant is Dictionary):
continue
var retry: Dictionary = retryVariant
var callback: Callable = retry.get("callback", Callable())
if not callback.is_valid():
continue
if not success or int(retry.get("account_generation", -1)) != currentGeneration:
callback.call(false, {}, {
"message": "登录状态已过期,请重新登录",
"response_code": 401,
"error_code": "SESSION_EXPIRED",
})
continue
_request_json(
str(retry.get("endpoint", "")),
retry.get("payload", {}) as Dictionary,
callback,
int(retry.get("method", HTTPClient.METHOD_GET)),
true,
int(retry.get("auth_retry_count", 1))
)
func _current_account_generation() -> int:
var authManager := get_node_or_null("/root/AuthManager")
if authManager == null or not authManager.has_method("get_account_generation"):
return -1
return int(authManager.call("get_account_generation"))
func _http_result_to_string(result: int) -> String:
match result:
HTTPRequest.RESULT_CHUNKED_BODY_SIZE_MISMATCH: