diff --git a/client/README.md b/client/README.md index 3f8325a..333b82e 100644 --- a/client/README.md +++ b/client/README.md @@ -82,7 +82,7 @@ If the server is **down**, boot **`GET`** fails silently (check Output for warni On boot, **`InteractablesCatalogClient`** **`GET`s** **`/game/world/interactables`**; **`interactable_world_builder.gd`** spawns **walkable** prototype props plus **two** small glow markers per row under **`World/InteractionMarkers`**. **`interaction_radius_indicators.gd`** compares the snapped **`CharacterBody3D`** **`global_position`** to each descriptor’s **`anchor`** on **X/Z** with the same **`interactionRadius`** and inclusive **`<=`** rule as the server — **preview only**; **`POST /game/players/{id}/interact`** remains authoritative. -- **`InteractionRequestClient`**: **`interact`** (**E**) → **`prototype_terminal`**; **`interact_secondary`** (**R**) → **`prototype_resource_node_alpha`** (stable ids; must match `PrototypeInteractableRegistry.cs`). **Output** prints `allowed` / `reasonCode`. In-flight requests are ignored (same busy pattern as **`PositionAuthorityClient`**). +- **`InteractionRequestClient`**: **`interact`** (**E**) → **`prototype_terminal`**; **`interact_secondary`** (**R**) → **`prototype_resource_node_alpha`** (stable ids; must match `PrototypeInteractableRegistry.cs`). **Output** prints `allowed` / `reasonCode` per response (lines prefixed with `Interaction []: …`). Only one HTTP POST runs at a time; if you press **E** then **R** while the first request is still in flight, the **latest** id is remembered and sent **immediately after** the first completes (last-wins coalescing — not a silent drop). - **Inspector:** match **`base_url`** on **`InteractablesCatalogClient`**, **`InteractionRequestClient`**, and **`PositionAuthorityClient`** to the running server. - **No server:** catalog **GET** fails → **Output** errors; props and glow markers do not spawn (expected prototype degradation). diff --git a/client/scripts/interaction_request_client.gd b/client/scripts/interaction_request_client.gd index 60f9f8d..85aebec 100644 --- a/client/scripts/interaction_request_client.gd +++ b/client/scripts/interaction_request_client.gd @@ -16,6 +16,10 @@ const ID_RESOURCE_NODE := "prototype_resource_node_alpha" var _http: Node var _busy: bool = false +## While an HTTP POST is in flight, the latest [code]interactableId[/code] from a new press overwrites +## this slot (last wins); [method _try_flush_pending] runs the next POST after [signal HTTPRequest.request_completed]. +var _pending_interactable_id: String = "" +var _current_interactable_id: String = "" func _ready() -> void: @@ -44,7 +48,13 @@ func _base_root() -> String: func _post_interact(interactable_id: String) -> void: if _busy: + _pending_interactable_id = interactable_id return + _start_post(interactable_id) + + +func _start_post(interactable_id: String) -> void: + _current_interactable_id = interactable_id _busy = true var url := "%s/game/players/%s/interact" % [_base_root(), dev_player_id.strip_edges()] var payload := {"schemaVersion": 1, "interactableId": interactable_id} @@ -54,6 +64,14 @@ func _post_interact(interactable_id: String) -> void: if err != OK: push_warning("InteractionRequestClient: POST failed to start (%s)" % err) _busy = false + _try_flush_pending() + + +func _try_flush_pending() -> void: + var next: String = _pending_interactable_id + _pending_interactable_id = "" + if not next.is_empty() and not _busy: + _start_post(next) func _on_request_completed( @@ -61,6 +79,7 @@ func _on_request_completed( ) -> void: _busy = false var text := body.get_string_from_utf8() + var id_echo: String = _current_interactable_id if response_code == 200: var parsed: Variant = JSON.parse_string(text) if parsed is Dictionary: @@ -68,8 +87,14 @@ func _on_request_completed( var allowed: bool = bool(d.get("allowed", false)) var reason: Variant = d.get("reasonCode", null) if allowed: - print("Interaction: allowed=true (reasonCode omitted on success)") + print( + "Interaction [%s]: allowed=true (reasonCode omitted on success)" % id_echo + ) else: - print("Interaction: allowed=false reasonCode=%s" % str(reason)) + print( + "Interaction [%s]: allowed=false reasonCode=%s" % [id_echo, str(reason)] + ) + _try_flush_pending() return - print("Interaction: HTTP %s body=%s" % [response_code, text]) + print("Interaction [%s]: HTTP %s body=%s" % [id_echo, response_code, text]) + _try_flush_pending()