Improve multiplayer movement reconciliation
This commit is contained in:
parent
348ae261e1
commit
a3aea98982
3 changed files with 240 additions and 89 deletions
|
|
@ -71,6 +71,7 @@ func _validate_compact_snapshot_encoding() -> void:
|
|||
1,
|
||||
)
|
||||
assert(NetworkSession.MOVEMENT_SNAPSHOT_BATCH_SIZE == 8)
|
||||
assert(NetworkSession.OWNER_SNAPSHOT_DIVISOR == 3)
|
||||
var encoded_snapshots: Array = []
|
||||
for _peer: int in NetworkSession.MOVEMENT_SNAPSHOT_BATCH_SIZE:
|
||||
encoded_snapshots.append(
|
||||
|
|
@ -283,16 +284,72 @@ func _validate_remote_snapshot_smoothing(avatar: Player) -> void:
|
|||
|
||||
avatar.set_local_control(true)
|
||||
avatar.global_position = Vector3(0.8, 0.0, 0.0)
|
||||
var replay_input: Dictionary = _movement_input(2, false)
|
||||
replay_input["axis"] = [1.0, 0.0]
|
||||
var pending_inputs: Array[Dictionary] = [replay_input]
|
||||
avatar.apply_local_prediction_correction(
|
||||
moving_snapshot,
|
||||
pending_inputs,
|
||||
2,
|
||||
1.0 / 30.0,
|
||||
0.1,
|
||||
0.1,
|
||||
)
|
||||
# Ordinary host/client disagreement is expected while a packet is in flight.
|
||||
# It must never tug the locally controlled body or camera around.
|
||||
assert(is_equal_approx(avatar.global_position.x, 0.8))
|
||||
assert(
|
||||
int(avatar.get("_local_prediction_soft_corrections")) == 0
|
||||
)
|
||||
assert(
|
||||
int(avatar.get("_local_prediction_hard_corrections")) == 0
|
||||
)
|
||||
|
||||
# A larger but still plausible mismatch must persist across multiple audits
|
||||
# before a small correction is allowed. Preserve both visible character and
|
||||
# camera positions while the collision body catches up.
|
||||
avatar.global_position = Vector3(3.0, 0.0, 0.0)
|
||||
var visual_position: Vector3 = avatar.get_node("Visuals").global_position
|
||||
var camera_position: Vector3 = avatar.get_node("CameraYaw").global_position
|
||||
var drift_snapshot: Dictionary = _network_snapshot(
|
||||
Vector3.ZERO,
|
||||
Vector3.ZERO,
|
||||
20,
|
||||
)
|
||||
for _audit: int in 3:
|
||||
avatar.apply_local_prediction_correction(
|
||||
drift_snapshot,
|
||||
20,
|
||||
1.0 / 30.0,
|
||||
0.0,
|
||||
0.1,
|
||||
)
|
||||
assert(is_equal_approx(avatar.global_position.x, 3.0))
|
||||
avatar.apply_local_prediction_correction(
|
||||
drift_snapshot,
|
||||
20,
|
||||
1.0 / 30.0,
|
||||
0.0,
|
||||
0.1,
|
||||
)
|
||||
assert(avatar.global_position.x < 3.0)
|
||||
assert(avatar.global_position.x >= 2.85 - 0.001)
|
||||
assert(avatar.get_node("Visuals").global_position == visual_position)
|
||||
assert(avatar.get_node("CameraYaw").global_position == camera_position)
|
||||
assert(
|
||||
int(avatar.get("_local_prediction_soft_corrections")) == 1
|
||||
)
|
||||
|
||||
# Genuine divergence still recovers immediately, as do the separate reliable
|
||||
# teleport and water-recovery paths used by gameplay transitions.
|
||||
avatar.global_position = Vector3(10.0, 0.0, 0.0)
|
||||
avatar.apply_local_prediction_correction(
|
||||
drift_snapshot,
|
||||
20,
|
||||
1.0 / 30.0,
|
||||
0.0,
|
||||
0.1,
|
||||
)
|
||||
assert(avatar.global_position == Vector3.ZERO)
|
||||
assert(
|
||||
int(avatar.get("_local_prediction_hard_corrections")) == 1
|
||||
)
|
||||
assert(avatar.global_position.x < 0.8)
|
||||
assert(avatar.global_position.x > 0.0)
|
||||
|
||||
|
||||
func _validate_reliable_jump_intent(avatar: Player) -> void:
|
||||
|
|
@ -305,8 +362,10 @@ func _validate_reliable_jump_intent(avatar: Player) -> void:
|
|||
assert(int(avatar.get("_local_network_jump_intent_sequence")) == 20)
|
||||
avatar.apply_local_prediction_correction(
|
||||
_network_snapshot(avatar.global_position, Vector3.ZERO, 20),
|
||||
[],
|
||||
20,
|
||||
1.0 / 30.0,
|
||||
0.0,
|
||||
0.1,
|
||||
)
|
||||
assert(not bool(avatar.capture_network_input(22)["jump"]))
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue