NEO-22: stop applying move-stream HTTP body as client teleport
Successful move-stream responses echoed server position roughly one RTT behind integrated move_and_slide, so apply_as_snap still fired whenever dh exceeded the locomotion skip threshold. Treat stream 200 as ack-only: do not emit authoritative_position_received. Boot GET and click-move verify unchanged. Update README + GdUnit expectation.pull/42/head
parent
39a41ac62c
commit
6cc2ac60d4
|
|
@ -53,7 +53,7 @@ Elevated walkables use **distinct albedo tints** in `scenes/main.tscn` so screen
|
|||
|
||||
## Authoritative movement (NEO-7, NEO-11, NEO-22)
|
||||
|
||||
With the game server running ([`server/README.md`](../server/README.md)), **WASD** (input actions **`move_*`** in `project.godot`) drives **camera-relative XZ** wish direction on the **`Player`**, and **`main.gd`** periodically **`POST`**s **`/game/players/{id}/move-stream`** with the **latest** body-centre sample while you are moving (no stream while standing still, so idle anchor does not fight HTTP). **`PositionAuthorityClient`** coalesces to **one target per request** so a slow round-trip cannot apply a stale multi-step chain after the capsule has moved. The server validates each leg with the same rules as a single **`MoveCommand`**, applies **all targets in order** (reject = **no** partial apply), and returns **`PositionStateResponse`**; the client **snaps** when the correction exceeds a small threshold (boot snap always applies once). **`POST /game/players/{id}/move`** remains for **debug** click-to-move when running a **debug** Godot build (`ground_pick.gd` only listens in **`OS.is_debug_build()`**): it still does **POST** + **`GET`** verify and sets a **nav walk goal** on the player.
|
||||
With the game server running ([`server/README.md`](../server/README.md)), **WASD** (input actions **`move_*`** in `project.godot`) drives **camera-relative XZ** wish direction on the **`Player`**, and **`main.gd`** periodically **`POST`**s **`/game/players/{id}/move-stream`** with the **latest** body-centre sample while you are moving (no stream while standing still, so idle anchor does not fight HTTP). **`PositionAuthorityClient`** coalesces to **one target per request** so a slow round-trip cannot apply a stale multi-step chain after the capsule has moved. The server validates each leg with the same rules as a single **`MoveCommand`**, applies **all targets in order** (reject = **no** partial apply), and returns **`PositionStateResponse`**. A successful **`move-stream`** response is treated as an **ack only** (no client teleport): the echoed position typically trails **`move_and_slide`** by roughly one RTT, so snapping it caused rubber-banding. **Boot** `GET` and **debug** click-move **verify** `GET` still emit **`authoritative_position_received`** so **`main.gd`** can **`snap_to_server`** once or set a **nav walk goal**. **`POST /game/players/{id}/move`** remains for **debug** click-to-move when running a **debug** Godot build (`ground_pick.gd` only listens in **`OS.is_debug_build()`**): it still does **POST** + **`GET`** verify and sets a **nav walk goal** on the player.
|
||||
|
||||
**Boot** `sync_from_server()` **snaps** once so spawn matches the server.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ extends Node
|
|||
## NS-16: POST MoveCommand then GET PositionState; emits authoritative world position for the
|
||||
## avatar. NS-19: failed validation (HTTP 400 + rejection body) emits `move_rejected`.
|
||||
## NS-23: second signal arg — `true` = boot snap, `false` = post-move (nav goal, no teleport).
|
||||
## NEO-22: `submit_stream_targets` POSTs `move-stream` (batched small moves); no VERIFY GET.
|
||||
## NEO-22: `submit_stream_targets` POSTs `move-stream` (batched small moves); no VERIFY GET. A 200
|
||||
## response does not emit `authoritative_position_received` (ack-only; avoids RTT snap-back).
|
||||
## (No `class_name` so headless/CI can load the project before `.godot` import exists.)
|
||||
|
||||
signal authoritative_position_received(world: Vector3, apply_as_snap: bool)
|
||||
|
|
@ -207,7 +208,11 @@ func _on_request_completed(
|
|||
_stream_in_flight_count = 0
|
||||
_busy = false
|
||||
if response_code == 200:
|
||||
_emit_position_from_response(text, true)
|
||||
# Do not emit `authoritative_position_received` here. The body echoes server position
|
||||
# after applying the batch, which typically lags the client's integrated capsule by
|
||||
# roughly one RTT — snapping caused visible rubber-banding during WASD. Boot GET and
|
||||
# click-move VERIFY still drive snaps / nav goals.
|
||||
pass
|
||||
_try_flush_pending()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ func test_second_sync_while_busy_is_ignored() -> void:
|
|||
assert_that(mock.request_count).is_equal(1)
|
||||
|
||||
|
||||
func test_move_stream_post_200_emits_snap_from_body() -> void:
|
||||
func test_move_stream_post_200_does_not_emit_position() -> void:
|
||||
var mock := MockHttpTransport.new()
|
||||
mock.enqueue(
|
||||
HTTPRequest.RESULT_SUCCESS,
|
||||
|
|
@ -126,6 +126,4 @@ func test_move_stream_post_200_emits_snap_from_body() -> void:
|
|||
var c := _make_client(mock)
|
||||
monitor_signals(c)
|
||||
c.submit_stream_targets([Vector3(-4.9, 0.9, -5.0)])
|
||||
await assert_signal(c).is_emitted(
|
||||
"authoritative_position_received", Vector3(-4.9, 0.9, -5.0), true
|
||||
)
|
||||
await assert_signal(c).wait_until(400).is_not_emitted("authoritative_position_received")
|
||||
|
|
|
|||
Loading…
Reference in New Issue