113 lines
3.4 KiB
GDScript
113 lines
3.4 KiB
GDScript
extends Node
|
|
|
|
## NEO-29: client bridge for server-owned `HotbarLoadout` v1 fetch/update.
|
|
signal loadout_changed(loadout: Dictionary)
|
|
signal loadout_update_denied(reason_code: String, loadout: Dictionary)
|
|
|
|
@export var base_url: String = "http://127.0.0.1:5253"
|
|
@export var dev_player_id: String = "dev-local-1"
|
|
@export var injected_http: Node = null
|
|
|
|
var _http: Node
|
|
var _busy: bool = false
|
|
var _last_loadout: Dictionary = {}
|
|
var _hotbar_state: Node = null
|
|
|
|
|
|
func _ready() -> void:
|
|
if injected_http != null:
|
|
_http = injected_http
|
|
else:
|
|
_http = HTTPRequest.new()
|
|
add_child(_http)
|
|
if _http is HTTPRequest:
|
|
(_http as HTTPRequest).timeout = 30.0
|
|
@warning_ignore("unsafe_method_access")
|
|
_http.request_completed.connect(_on_request_completed)
|
|
|
|
|
|
func set_hotbar_state(state_node: Node) -> void:
|
|
_hotbar_state = state_node
|
|
|
|
|
|
func request_sync_from_server() -> void:
|
|
if _busy:
|
|
return
|
|
_busy = true
|
|
var url := "%s/game/players/%s/hotbar-loadout" % [_base_root(), _player_path_segment()]
|
|
var err: Error = _http.request(url)
|
|
if err != OK:
|
|
push_warning("HotbarLoadoutClient: GET failed to start (%s)" % err)
|
|
_busy = false
|
|
|
|
|
|
func request_bind_slot(slot_index: int, ability_id: String) -> void:
|
|
if _busy:
|
|
return
|
|
var payload: Dictionary = {
|
|
"schemaVersion": 1,
|
|
"slots": [{"slotIndex": slot_index, "abilityId": ability_id.strip_edges()}],
|
|
}
|
|
_post_update(payload)
|
|
|
|
|
|
func cached_loadout() -> Dictionary:
|
|
return _last_loadout.duplicate(true)
|
|
|
|
|
|
func _post_update(payload: Dictionary) -> void:
|
|
_busy = true
|
|
var url := "%s/game/players/%s/hotbar-loadout" % [_base_root(), _player_path_segment()]
|
|
var headers := PackedStringArray(["Content-Type: application/json"])
|
|
var err: Error = _http.request(url, headers, HTTPClient.METHOD_POST, JSON.stringify(payload))
|
|
if err != OK:
|
|
push_warning("HotbarLoadoutClient: POST failed to start (%s)" % err)
|
|
_busy = false
|
|
|
|
|
|
func _base_root() -> String:
|
|
return base_url.strip_edges().rstrip("/")
|
|
|
|
|
|
func _player_path_segment() -> String:
|
|
return dev_player_id.strip_edges()
|
|
|
|
|
|
func _on_request_completed(
|
|
result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray
|
|
) -> void:
|
|
_busy = false
|
|
if result != HTTPRequest.RESULT_SUCCESS:
|
|
push_warning("HotbarLoadoutClient: HTTP failed (result=%s)" % result)
|
|
return
|
|
var text := body.get_string_from_utf8()
|
|
var parsed: Variant = JSON.parse_string(text)
|
|
if not parsed is Dictionary:
|
|
if response_code != 200:
|
|
push_warning("HotbarLoadoutClient: HTTP %s non-JSON body" % response_code)
|
|
return
|
|
var data: Dictionary = parsed
|
|
var loadout: Dictionary = data
|
|
if data.has("loadout") and data["loadout"] is Dictionary:
|
|
loadout = data["loadout"]
|
|
if loadout.is_empty() or not loadout.has("slots"):
|
|
return
|
|
_last_loadout = loadout.duplicate(true)
|
|
_apply_state(loadout)
|
|
loadout_changed.emit(_last_loadout.duplicate(true))
|
|
if data.has("updated") and not bool(data.get("updated", false)):
|
|
var reason_variant: Variant = data.get("reasonCode", "")
|
|
var reason: String = reason_variant as String if reason_variant is String else ""
|
|
if not reason.is_empty():
|
|
loadout_update_denied.emit(reason, _last_loadout.duplicate(true))
|
|
|
|
|
|
func _apply_state(loadout: Dictionary) -> void:
|
|
if _hotbar_state == null or not is_instance_valid(_hotbar_state):
|
|
return
|
|
if not _hotbar_state.has_method("apply_slots"):
|
|
return
|
|
var slots_variant: Variant = loadout.get("slots", [])
|
|
if slots_variant is Array:
|
|
_hotbar_state.call("apply_slots", slots_variant as Array)
|