NEO-22: suppress stream snap-back while locomoting
Stream HTTP responses can lag integrated move_and_slide by roughly one RTT, so apply_as_snap was pulling the capsule backward each tick while moving. Skip small XZ corrections when wish or horizontal speed indicates locomotion, with a cap so large desync still snaps. move_rejected sets a one-shot flag so resync after move-stream 400 still applies the authoritative pose.pull/42/head
parent
878b1f4aee
commit
39a41ac62c
|
|
@ -30,11 +30,19 @@ const STREAM_IDLE_VEL_HZ_SQ: float = 0.0004
|
|||
const AUTH_SNAP_MIN_HORIZONTAL: float = 0.08
|
||||
const AUTH_SNAP_MIN_VERTICAL: float = 0.04
|
||||
|
||||
## While moving, ignore stream snaps smaller than this (XZ m): responses are often one RTT behind
|
||||
## integrated `move_and_slide`, so snapping causes rubber-band / bounciness.
|
||||
const AUTH_SNAP_LOCOMOTION_SKIP_MAX_DH: float = 0.55
|
||||
## Horizontal speed² above this (m²/s²) counts as “moving” for snap suppression (~0.35 m/s).
|
||||
const AUTH_SNAP_LOCOMOTION_VEL_HZ_SQ: float = 0.12
|
||||
|
||||
## Bump on each rejection so older one-shot timers do not clear a newer message.
|
||||
var _move_reject_msg_token: int = 0
|
||||
var _stream_physics_counter: int = 0
|
||||
## After the first `apply_as_snap` authority update, micro-snaps may be skipped (boot always applies).
|
||||
var _allow_authority_soft_snap_skip: bool = false
|
||||
## Next `apply_as_snap` must not be suppressed (e.g. `move-stream` 400 → `sync_from_server`).
|
||||
var _authority_force_snap_next: bool = false
|
||||
|
||||
## [InputMap] [code]dev_toggle_occluder_obstacle[/code] (default **Ctrl+Shift+K**):
|
||||
## Restored when the obstacle is shown again.
|
||||
|
|
@ -125,10 +133,19 @@ func _on_target_chosen(world: Vector3) -> void:
|
|||
|
||||
func _on_authoritative_position(world: Vector3, apply_as_snap: bool) -> void:
|
||||
if apply_as_snap:
|
||||
if _allow_authority_soft_snap_skip:
|
||||
var p: Vector3 = _player.global_position
|
||||
var dh: float = Vector2(world.x - p.x, world.z - p.z).length()
|
||||
var dy: float = absf(world.y - p.y)
|
||||
var force: bool = _authority_force_snap_next
|
||||
_authority_force_snap_next = false
|
||||
var p: Vector3 = _player.global_position
|
||||
var dh: float = Vector2(world.x - p.x, world.z - p.z).length()
|
||||
var dy: float = absf(world.y - p.y)
|
||||
if _allow_authority_soft_snap_skip and not force:
|
||||
var wish_len2: float = _player._locomotion_wish_world_xz.length_squared()
|
||||
var vel_hz_sq: float = Vector2(_player.velocity.x, _player.velocity.z).length_squared()
|
||||
var locomoting: bool = (
|
||||
wish_len2 > 1e-10 or vel_hz_sq > AUTH_SNAP_LOCOMOTION_VEL_HZ_SQ
|
||||
)
|
||||
if locomoting and dh < AUTH_SNAP_LOCOMOTION_SKIP_MAX_DH:
|
||||
return
|
||||
if dh < AUTH_SNAP_MIN_HORIZONTAL and dy < AUTH_SNAP_MIN_VERTICAL:
|
||||
return
|
||||
_allow_authority_soft_snap_skip = true
|
||||
|
|
@ -138,6 +155,7 @@ func _on_authoritative_position(world: Vector3, apply_as_snap: bool) -> void:
|
|||
|
||||
|
||||
func _on_move_rejected(reason_code: String) -> void:
|
||||
_authority_force_snap_next = true
|
||||
# Rejected POST never reached VERIFY_GET, so no new nav target was applied; do not clear an
|
||||
# in-flight path from a prior successful move.
|
||||
push_warning("Move rejected: %s" % reason_code)
|
||||
|
|
|
|||
Loading…
Reference in New Issue