feat: expand progression and multiplayer systems
Add named save slots, progression import/export, and a unified play flow. Add live friend requests, presence, invitations, and relationship controls without durable discovery-server social storage. Advance the network protocol with isolated channels, movement reconciliation, late-join recovery, fishing replication, and animation synchronization. Preserve per-species catch totals, refine generated-world startup and water recovery, and complete the related input and interface improvements.
This commit is contained in:
parent
1db1a5b754
commit
3b84bfe3a0
97 changed files with 7869 additions and 982 deletions
431
player/player.gd
431
player/player.gd
|
|
@ -139,6 +139,10 @@ const NETWORK_SNAPSHOT_JITTER_WEIGHT: float = 0.15
|
|||
const NETWORK_REMOTE_SMOOTHING_RATE: float = 12.0
|
||||
const NETWORK_REMOTE_JITTER_SMOOTHING_RATE: float = 6.0
|
||||
const NETWORK_INPUT_STALE_TIMEOUT_SECONDS: float = 0.25
|
||||
const NETWORK_INPUT_STALE_TIMEOUT_MAX_SECONDS: float = 1.25
|
||||
const NETWORK_INPUT_STALE_RTT_MULTIPLIER: float = 1.5
|
||||
const NETWORK_MOVEMENT_HISTORY_SECONDS: float = 1.25
|
||||
const NETWORK_MAX_LAG_COMPENSATION_SECONDS: float = 0.75
|
||||
const LOCAL_PREDICTION_EXTRAPOLATION_LIMIT_SECONDS: float = 0.25
|
||||
const LOCAL_PREDICTION_FALLBACK_TRANSIT_RATIO: float = 0.5
|
||||
const LOCAL_PREDICTION_CORRECTION_THRESHOLD: float = 0.12
|
||||
|
|
@ -417,6 +421,7 @@ class ShowcaseCameraSnapshot:
|
|||
@export var controller_camera_speed: float = 2.5
|
||||
@export_range(0.0, 1.0, 0.01) var controller_camera_deadzone: float = 0.2
|
||||
@export var invert_camera_y: bool = false
|
||||
var _swap_hotbar_camera_scroll: bool = false
|
||||
@export_range(-89.0, 0.0, 0.5) var minimum_pitch_degrees: float = -65.0
|
||||
@export_range(0.0, 89.0, 0.5) var maximum_pitch_degrees: float = 45.0
|
||||
@export var minimum_zoom: float = 2.0
|
||||
|
|
@ -506,6 +511,9 @@ var _network_slow_walk: bool = false
|
|||
var _last_network_input_sequence: int = 0
|
||||
var _network_input_age: float = 0.0
|
||||
var _network_input_stale: bool = false
|
||||
var _network_input_stale_timeout_seconds: float = (
|
||||
NETWORK_INPUT_STALE_TIMEOUT_SECONDS
|
||||
)
|
||||
var _network_jump_intent_active: bool = false
|
||||
var _network_target_position: Vector3
|
||||
var _network_target_velocity: Vector3
|
||||
|
|
@ -519,6 +527,9 @@ var _network_target_animation_action_paused: bool = false
|
|||
var _network_snapshot_ready: bool = false
|
||||
var _network_snapshot_age: float = 0.0
|
||||
var _network_snapshot_jitter: float = 0.0
|
||||
var _network_simulation_only: bool = false
|
||||
var _local_reconciliation_visual_offset: Vector3 = Vector3.ZERO
|
||||
var _authoritative_movement_history: Array[Dictionary] = []
|
||||
var _local_network_jump_intent_pending: bool = false
|
||||
var _local_network_jump_intent_sequence: int = -1
|
||||
var _animation_action_id: StringName = &""
|
||||
|
|
@ -791,10 +802,16 @@ func _finish_successful_net_strike_pause() -> void:
|
|||
func _set_animation_action_paused(paused: bool) -> void:
|
||||
if _animation_action_id.is_empty() or _animation_action_paused == paused:
|
||||
return
|
||||
var next_sequence: int = (
|
||||
1
|
||||
if _animation_action_sequence
|
||||
>= NetworkPlayerAnimationProtocol.MAX_ACTION_SEQUENCE
|
||||
else _animation_action_sequence + 1
|
||||
)
|
||||
_apply_animation_action_state(
|
||||
NetworkPlayerAnimationProtocol.make_action_state(
|
||||
_animation_action_id,
|
||||
_animation_action_sequence,
|
||||
next_sequence,
|
||||
_animation_action_elapsed,
|
||||
paused,
|
||||
)
|
||||
|
|
@ -972,6 +989,12 @@ func _physics_process(delta: float) -> void:
|
|||
if _network_interpolation_enabled:
|
||||
_update_network_interpolation(delta)
|
||||
return
|
||||
_simulate_movement_physics(delta)
|
||||
if _network_authoritative_simulation:
|
||||
_record_authoritative_movement_state()
|
||||
|
||||
|
||||
func _simulate_movement_physics(delta: float) -> void:
|
||||
if _network_authoritative_simulation:
|
||||
_update_network_input_freshness(delta)
|
||||
if local_control_enabled and _free_camera_active:
|
||||
|
|
@ -1075,6 +1098,9 @@ func _physics_process(delta: float) -> void:
|
|||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if _network_simulation_only:
|
||||
return
|
||||
_update_local_reconciliation_visuals(delta)
|
||||
if not _animation_action_id.is_empty() and not _animation_action_paused:
|
||||
_animation_action_elapsed = minf(
|
||||
_animation_action_elapsed + delta,
|
||||
|
|
@ -1607,6 +1633,23 @@ func is_sitting() -> bool:
|
|||
return _sitting
|
||||
|
||||
|
||||
func get_network_sitting_state() -> bool:
|
||||
return _sitting or _sit_after_landing
|
||||
|
||||
|
||||
func apply_network_sitting_state(should_sit: bool) -> void:
|
||||
_set_sitting(should_sit)
|
||||
|
||||
|
||||
func apply_authoritative_network_sitting_state(should_sit: bool) -> void:
|
||||
if should_sit and not is_on_floor():
|
||||
_sit_after_landing = true
|
||||
_set_sitting(false)
|
||||
return
|
||||
_sit_after_landing = false
|
||||
_set_sitting(should_sit)
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
# Camera dragging must observe the complete mouse gesture before GUI controls
|
||||
# get a chance to consume one part of it. Do not claim the event here: the
|
||||
|
|
@ -1648,7 +1691,7 @@ func _unhandled_input(event: InputEvent) -> void:
|
|||
|
||||
var mouse_zoom_in: bool = (
|
||||
event is InputEventMouseButton
|
||||
and event.shift_pressed
|
||||
and event.shift_pressed != _swap_hotbar_camera_scroll
|
||||
and event.is_action_pressed("camera_zoom_in")
|
||||
)
|
||||
var controller_zoom_in: bool = (
|
||||
|
|
@ -1657,7 +1700,7 @@ func _unhandled_input(event: InputEvent) -> void:
|
|||
)
|
||||
var mouse_zoom_out: bool = (
|
||||
event is InputEventMouseButton
|
||||
and event.shift_pressed
|
||||
and event.shift_pressed != _swap_hotbar_camera_scroll
|
||||
and event.is_action_pressed("camera_zoom_out")
|
||||
)
|
||||
var controller_zoom_out: bool = (
|
||||
|
|
@ -1878,10 +1921,14 @@ func get_network_peer_id() -> int:
|
|||
return _network_peer_id
|
||||
|
||||
|
||||
func configure_network_remote(authoritative_simulation: bool) -> void:
|
||||
func configure_network_remote(
|
||||
authoritative_simulation: bool,
|
||||
simulation_only: bool = false,
|
||||
) -> void:
|
||||
set_local_control(false)
|
||||
_network_authoritative_simulation = authoritative_simulation
|
||||
_network_interpolation_enabled = not authoritative_simulation
|
||||
set_network_simulation_only(simulation_only)
|
||||
_network_axis = Vector2.ZERO
|
||||
_network_jump_pending = false
|
||||
_network_sprint = false
|
||||
|
|
@ -1889,6 +1936,7 @@ func configure_network_remote(authoritative_simulation: bool) -> void:
|
|||
_network_slow_walk = false
|
||||
_network_input_age = 0.0
|
||||
_network_input_stale = false
|
||||
_network_input_stale_timeout_seconds = NETWORK_INPUT_STALE_TIMEOUT_SECONDS
|
||||
_network_jump_intent_active = false
|
||||
_network_snapshot_ready = false
|
||||
_network_snapshot_age = 0.0
|
||||
|
|
@ -1900,10 +1948,43 @@ func configure_network_remote(authoritative_simulation: bool) -> void:
|
|||
_network_target_animation_action_elapsed = 0.0
|
||||
_network_target_animation_action_paused = false
|
||||
_camera.current = false
|
||||
_authoritative_movement_history.clear()
|
||||
|
||||
|
||||
func set_network_simulation_only(enabled: bool) -> void:
|
||||
_network_simulation_only = enabled
|
||||
if not is_node_ready():
|
||||
return
|
||||
set_process(not enabled)
|
||||
for presentation_root: Node in [
|
||||
_visuals,
|
||||
get_node_or_null("GroundShadow"),
|
||||
get_node_or_null("SprintDust"),
|
||||
_camera_yaw,
|
||||
]:
|
||||
if presentation_root == null:
|
||||
continue
|
||||
presentation_root.process_mode = (
|
||||
Node.PROCESS_MODE_DISABLED
|
||||
if enabled
|
||||
else Node.PROCESS_MODE_INHERIT
|
||||
)
|
||||
if presentation_root is Node3D:
|
||||
(presentation_root as Node3D).visible = not enabled
|
||||
|
||||
|
||||
func is_network_simulation_only() -> bool:
|
||||
return _network_simulation_only
|
||||
|
||||
|
||||
func reset_network_movement_state() -> void:
|
||||
_clear_local_network_jump_intent()
|
||||
if not _animation_action_id.is_empty():
|
||||
end_animation_action()
|
||||
_sit_after_landing = false
|
||||
_sitting_intent_pending = false
|
||||
_sitting_intent_sequence = -1
|
||||
_set_sitting(false)
|
||||
_network_axis = Vector2.ZERO
|
||||
_network_jump_pending = false
|
||||
_network_sprint = false
|
||||
|
|
@ -1911,6 +1992,7 @@ func reset_network_movement_state() -> void:
|
|||
_network_slow_walk = false
|
||||
_network_input_age = 0.0
|
||||
_network_input_stale = false
|
||||
_network_input_stale_timeout_seconds = NETWORK_INPUT_STALE_TIMEOUT_SECONDS
|
||||
_network_jump_intent_active = false
|
||||
_last_network_input_sequence = 0
|
||||
|
||||
|
|
@ -1939,7 +2021,7 @@ func capture_network_input(sequence: int) -> Dictionary:
|
|||
"sprint": false,
|
||||
"sneak": false,
|
||||
"slow_walk": false,
|
||||
"sitting": _sitting,
|
||||
"sitting": get_network_sitting_state(),
|
||||
"casting": (
|
||||
_fishing_visual_phase == FishingVisualPhase.CASTING
|
||||
),
|
||||
|
|
@ -1964,13 +2046,66 @@ func capture_network_input(sequence: int) -> Dictionary:
|
|||
"sprint": Input.is_action_pressed("sprint"),
|
||||
"sneak": Input.is_action_pressed("sneak"),
|
||||
"slow_walk": Input.is_action_pressed("slow_walk"),
|
||||
"sitting": _sitting,
|
||||
"sitting": get_network_sitting_state(),
|
||||
"casting": _fishing_visual_phase == FishingVisualPhase.CASTING,
|
||||
"animation_action": _make_animation_action_state(),
|
||||
}
|
||||
|
||||
|
||||
func apply_authoritative_network_input(data: Dictionary) -> void:
|
||||
func has_active_network_input() -> bool:
|
||||
if not local_control_enabled:
|
||||
return false
|
||||
if _local_network_jump_intent_pending or _sitting_intent_pending:
|
||||
return true
|
||||
if not _is_movement_input_enabled() or _free_camera_active:
|
||||
return false
|
||||
return (
|
||||
not Input.get_vector(
|
||||
"move_left",
|
||||
"move_right",
|
||||
"move_forward",
|
||||
"move_backward",
|
||||
).is_zero_approx()
|
||||
or Input.is_action_pressed("sprint")
|
||||
or Input.is_action_pressed("sneak")
|
||||
or Input.is_action_pressed("slow_walk")
|
||||
or _fishing_visual_phase == FishingVisualPhase.CASTING
|
||||
or not _animation_action_id.is_empty()
|
||||
)
|
||||
|
||||
|
||||
func get_network_input_state_hash() -> int:
|
||||
var axis: Vector2 = Vector2.ZERO
|
||||
if (
|
||||
local_control_enabled
|
||||
and _is_movement_input_enabled()
|
||||
and not _free_camera_active
|
||||
):
|
||||
axis = Input.get_vector(
|
||||
"move_left",
|
||||
"move_right",
|
||||
"move_forward",
|
||||
"move_backward",
|
||||
)
|
||||
return hash([
|
||||
axis,
|
||||
_camera_yaw.global_rotation.y,
|
||||
_local_network_jump_intent_pending,
|
||||
Input.is_action_pressed("sprint"),
|
||||
Input.is_action_pressed("sneak"),
|
||||
Input.is_action_pressed("slow_walk"),
|
||||
get_network_sitting_state(),
|
||||
_fishing_visual_phase == FishingVisualPhase.CASTING,
|
||||
_animation_action_id,
|
||||
_animation_action_sequence,
|
||||
_animation_action_paused,
|
||||
])
|
||||
|
||||
|
||||
func apply_authoritative_network_input(
|
||||
data: Dictionary,
|
||||
stale_timeout_seconds: float = NETWORK_INPUT_STALE_TIMEOUT_SECONDS,
|
||||
) -> void:
|
||||
var sequence: int = int(data.get("sequence", 0))
|
||||
if sequence <= _last_network_input_sequence:
|
||||
return
|
||||
|
|
@ -1980,6 +2115,11 @@ func apply_authoritative_network_input(data: Dictionary) -> void:
|
|||
_last_network_input_sequence = sequence
|
||||
_network_input_age = 0.0
|
||||
_network_input_stale = false
|
||||
_network_input_stale_timeout_seconds = clampf(
|
||||
stale_timeout_seconds,
|
||||
NETWORK_INPUT_STALE_TIMEOUT_SECONDS,
|
||||
NETWORK_INPUT_STALE_TIMEOUT_MAX_SECONDS,
|
||||
)
|
||||
_network_axis = Vector2(float(axis[0]), float(axis[1])).limit_length(1.0)
|
||||
_network_camera_yaw = float(data.get("camera_yaw", 0.0))
|
||||
var jump_intent_active: bool = bool(data.get("jump", false))
|
||||
|
|
@ -1989,15 +2129,13 @@ func apply_authoritative_network_input(data: Dictionary) -> void:
|
|||
_network_sprint = bool(data.get("sprint", false))
|
||||
_network_sneak = bool(data.get("sneak", false))
|
||||
_network_slow_walk = bool(data.get("slow_walk", false))
|
||||
_apply_animation_action_state(data.get("animation_action", {}))
|
||||
apply_authoritative_network_animation_action(
|
||||
data.get("animation_action", {})
|
||||
)
|
||||
_apply_network_casting(bool(data.get("casting", false)))
|
||||
var sitting_requested: bool = bool(data.get("sitting", false))
|
||||
if sitting_requested and not is_on_floor():
|
||||
_sit_after_landing = true
|
||||
_set_sitting(false)
|
||||
else:
|
||||
_sit_after_landing = false
|
||||
_set_sitting(sitting_requested)
|
||||
apply_authoritative_network_sitting_state(
|
||||
bool(data.get("sitting", false))
|
||||
)
|
||||
|
||||
|
||||
func make_network_snapshot(peer_id: int) -> Dictionary:
|
||||
|
|
@ -2007,19 +2145,49 @@ func make_network_snapshot(peer_id: int) -> Dictionary:
|
|||
"position": [global_position.x, global_position.y, global_position.z],
|
||||
"velocity": [velocity.x, velocity.y, velocity.z],
|
||||
"visual_yaw": _visuals.rotation.y,
|
||||
"animation_state": NetworkPlayerAnimationProtocol.make_state(
|
||||
_get_authoritative_locomotion_id(),
|
||||
is_on_floor(),
|
||||
_animation_action_id,
|
||||
_animation_action_sequence,
|
||||
_animation_action_elapsed,
|
||||
_animation_action_paused,
|
||||
),
|
||||
"sitting": _sitting,
|
||||
"animation_state": make_network_animation_state(),
|
||||
"grounded": is_on_floor(),
|
||||
"sitting": get_network_sitting_state(),
|
||||
"casting": _fishing_visual_phase == FishingVisualPhase.CASTING,
|
||||
}
|
||||
|
||||
|
||||
func make_network_animation_state() -> Dictionary:
|
||||
return NetworkPlayerAnimationProtocol.make_state(
|
||||
_get_authoritative_locomotion_id(),
|
||||
is_on_floor(),
|
||||
_animation_action_id,
|
||||
_animation_action_sequence,
|
||||
_animation_action_elapsed,
|
||||
_animation_action_paused,
|
||||
)
|
||||
|
||||
|
||||
func apply_network_animation_state(state: Dictionary) -> void:
|
||||
_apply_network_target_animation_state(state)
|
||||
|
||||
|
||||
func apply_authoritative_network_animation_action(
|
||||
action_state: Dictionary,
|
||||
) -> void:
|
||||
if not NetworkPlayerAnimationProtocol.validate_action_state(action_state):
|
||||
return
|
||||
var incoming_sequence: int = int(action_state["sequence"])
|
||||
var wrapped_sequence: bool = (
|
||||
_animation_action_sequence
|
||||
== NetworkPlayerAnimationProtocol.MAX_ACTION_SEQUENCE
|
||||
and incoming_sequence == 1
|
||||
)
|
||||
if incoming_sequence < _animation_action_sequence and not wrapped_sequence:
|
||||
return
|
||||
if (
|
||||
incoming_sequence == _animation_action_sequence
|
||||
and StringName(str(action_state["id"])) != _animation_action_id
|
||||
):
|
||||
return
|
||||
_apply_animation_action_state(action_state)
|
||||
|
||||
|
||||
func push_network_snapshot(
|
||||
snapshot: Dictionary,
|
||||
estimated_transit_seconds: float = -1.0,
|
||||
|
|
@ -2053,7 +2221,8 @@ func push_network_snapshot(
|
|||
)
|
||||
_network_target_velocity = parsed["velocity"]
|
||||
_network_target_visual_yaw = parsed["visual_yaw"]
|
||||
_apply_network_target_animation_state(parsed["animation_state"])
|
||||
if not Dictionary(parsed.get("animation_state", {})).is_empty():
|
||||
_apply_network_target_animation_state(parsed["animation_state"])
|
||||
_network_snapshot_age = 0.0
|
||||
_apply_network_casting(bool(parsed["casting"]))
|
||||
_set_sitting(bool(parsed["sitting"]))
|
||||
|
|
@ -2066,9 +2235,8 @@ func push_network_snapshot(
|
|||
|
||||
func apply_local_prediction_correction(
|
||||
snapshot: Dictionary,
|
||||
latest_input_sequence: int = 0,
|
||||
pending_inputs: Array[Dictionary] = [],
|
||||
input_interval_seconds: float = 0.0,
|
||||
estimated_transit_seconds: float = -1.0,
|
||||
) -> void:
|
||||
var parsed: Dictionary = _parse_network_snapshot(snapshot)
|
||||
if parsed.is_empty():
|
||||
|
|
@ -2090,27 +2258,158 @@ func apply_local_prediction_correction(
|
|||
_clear_local_network_jump_intent()
|
||||
if not _sitting_intent_pending:
|
||||
_set_sitting(bool(parsed["sitting"]))
|
||||
var authoritative_position: Vector3 = parsed["position"]
|
||||
var transit_seconds: float = resolve_local_prediction_transit_seconds(
|
||||
acknowledged_input,
|
||||
latest_input_sequence,
|
||||
input_interval_seconds,
|
||||
estimated_transit_seconds,
|
||||
var previous_visual_position: Vector3 = _visuals.global_position
|
||||
var base_visual_local_position: Vector3 = (
|
||||
_visuals.position - _local_reconciliation_visual_offset
|
||||
)
|
||||
if transit_seconds > 0.0:
|
||||
authoritative_position += (
|
||||
(parsed["velocity"] as Vector3) * transit_seconds
|
||||
)
|
||||
var error_distance: float = global_position.distance_to(
|
||||
authoritative_position
|
||||
var previous_position: Vector3 = global_position
|
||||
global_position = parsed["position"]
|
||||
velocity = parsed["velocity"]
|
||||
if input_interval_seconds > 0.0:
|
||||
for input: Dictionary in pending_inputs:
|
||||
_replay_network_movement_input(input, input_interval_seconds)
|
||||
var correction_distance: float = previous_position.distance_to(
|
||||
global_position
|
||||
)
|
||||
if error_distance > LOCAL_PREDICTION_SNAP_DISTANCE:
|
||||
global_position = authoritative_position
|
||||
elif error_distance > LOCAL_PREDICTION_CORRECTION_THRESHOLD:
|
||||
global_position = global_position.lerp(
|
||||
authoritative_position,
|
||||
LOCAL_PREDICTION_CORRECTION_WEIGHT,
|
||||
if correction_distance <= LOCAL_PREDICTION_SNAP_DISTANCE:
|
||||
_visuals.global_position = previous_visual_position
|
||||
_local_reconciliation_visual_offset = (
|
||||
_visuals.position - base_visual_local_position
|
||||
)
|
||||
else:
|
||||
_local_reconciliation_visual_offset = Vector3.ZERO
|
||||
|
||||
|
||||
func _replay_network_movement_input(
|
||||
data: Dictionary,
|
||||
delta: float,
|
||||
) -> void:
|
||||
var axis_value: Variant = data.get("axis", [])
|
||||
if typeof(axis_value) != TYPE_ARRAY or axis_value.size() != 2:
|
||||
return
|
||||
if bool(data.get("sitting", false)) or _water_recovery_active:
|
||||
velocity = Vector3.ZERO
|
||||
return
|
||||
var input_vector := Vector2(
|
||||
float(axis_value[0]),
|
||||
float(axis_value[1]),
|
||||
).limit_length(1.0)
|
||||
var camera_basis := Basis(
|
||||
Vector3.UP,
|
||||
float(data.get("camera_yaw", 0.0)),
|
||||
)
|
||||
var move_direction: Vector3 = (
|
||||
camera_basis.x * input_vector.x
|
||||
+ camera_basis.z * input_vector.y
|
||||
)
|
||||
move_direction.y = 0.0
|
||||
move_direction = move_direction.normalized()
|
||||
_network_sprint = bool(data.get("sprint", false))
|
||||
_network_sneak = bool(data.get("sneak", false))
|
||||
_network_slow_walk = bool(data.get("slow_walk", false))
|
||||
# Replay the speed authored by this exact pending input. Consulting the
|
||||
# current InputMap here would make an older walk replay as a sprint (or the
|
||||
# reverse) whenever the local button changed while a snapshot was in flight.
|
||||
var replay_speed: float = walk_speed
|
||||
if _network_sneak:
|
||||
replay_speed = sneak_speed
|
||||
elif _network_slow_walk:
|
||||
replay_speed = slow_walk_speed
|
||||
elif _network_sprint:
|
||||
replay_speed = sprint_speed
|
||||
if item_effects != null:
|
||||
replay_speed *= item_effects.get_movement_multiplier()
|
||||
var input_strength: float = minf(input_vector.length(), 1.0)
|
||||
velocity.x = move_direction.x * replay_speed * input_strength
|
||||
velocity.z = move_direction.z * replay_speed * input_strength
|
||||
if not is_on_floor():
|
||||
var gravity_multiplier: float = (
|
||||
upward_gravity_multiplier
|
||||
if velocity.y > 0.0
|
||||
else fall_gravity_multiplier
|
||||
)
|
||||
velocity.y -= _gravity * gravity_multiplier * delta
|
||||
elif bool(data.get("jump", false)):
|
||||
velocity.y = jump_velocity
|
||||
move_and_slide()
|
||||
|
||||
|
||||
func _update_local_reconciliation_visuals(delta: float) -> void:
|
||||
if _local_reconciliation_visual_offset.is_zero_approx():
|
||||
_local_reconciliation_visual_offset = Vector3.ZERO
|
||||
return
|
||||
var retained_ratio: float = exp(-14.0 * delta)
|
||||
var retained_offset: Vector3 = (
|
||||
_local_reconciliation_visual_offset * retained_ratio
|
||||
)
|
||||
_visuals.position += retained_offset - _local_reconciliation_visual_offset
|
||||
_local_reconciliation_visual_offset = retained_offset
|
||||
|
||||
|
||||
static func resolve_network_input_stale_timeout_seconds(
|
||||
round_trip_msec: int,
|
||||
) -> float:
|
||||
if round_trip_msec < 0:
|
||||
return NETWORK_INPUT_STALE_TIMEOUT_SECONDS
|
||||
return clampf(
|
||||
NETWORK_INPUT_STALE_TIMEOUT_SECONDS
|
||||
+ float(round_trip_msec) / 1000.0
|
||||
* NETWORK_INPUT_STALE_RTT_MULTIPLIER,
|
||||
NETWORK_INPUT_STALE_TIMEOUT_SECONDS,
|
||||
NETWORK_INPUT_STALE_TIMEOUT_MAX_SECONDS,
|
||||
)
|
||||
|
||||
|
||||
func _record_authoritative_movement_state() -> void:
|
||||
var now_seconds: float = Time.get_ticks_msec() / 1000.0
|
||||
_authoritative_movement_history.append({
|
||||
"sequence": _last_network_input_sequence,
|
||||
"recorded_at": now_seconds,
|
||||
"position": global_position,
|
||||
"velocity": velocity,
|
||||
"cast_origin": get_cast_origin_position(),
|
||||
"facing": get_facing_direction(),
|
||||
"sneaking": is_sneaking(),
|
||||
})
|
||||
var oldest_allowed: float = now_seconds - NETWORK_MOVEMENT_HISTORY_SECONDS
|
||||
while (
|
||||
not _authoritative_movement_history.is_empty()
|
||||
and float(
|
||||
_authoritative_movement_history[0].get("recorded_at", 0.0)
|
||||
) < oldest_allowed
|
||||
):
|
||||
_authoritative_movement_history.pop_front()
|
||||
|
||||
|
||||
func get_lag_compensated_movement_state(
|
||||
input_sequence: int,
|
||||
max_age_seconds: float = NETWORK_MAX_LAG_COMPENSATION_SECONDS,
|
||||
) -> Dictionary:
|
||||
var now_seconds: float = Time.get_ticks_msec() / 1000.0
|
||||
for index: int in range(
|
||||
_authoritative_movement_history.size() - 1,
|
||||
-1,
|
||||
-1,
|
||||
):
|
||||
var candidate: Dictionary = _authoritative_movement_history[index]
|
||||
if now_seconds - float(candidate.get("recorded_at", 0.0)) > (
|
||||
max_age_seconds
|
||||
):
|
||||
break
|
||||
if (
|
||||
input_sequence <= 0
|
||||
or int(candidate.get("sequence", 0)) <= input_sequence
|
||||
):
|
||||
return candidate.duplicate(true)
|
||||
return {
|
||||
"sequence": _last_network_input_sequence,
|
||||
"recorded_at": now_seconds,
|
||||
"position": global_position,
|
||||
"velocity": velocity,
|
||||
"cast_origin": get_cast_origin_position(),
|
||||
"facing": get_facing_direction(),
|
||||
"sneaking": is_sneaking(),
|
||||
}
|
||||
|
||||
|
||||
static func resolve_local_prediction_transit_seconds(
|
||||
|
|
@ -2154,7 +2453,8 @@ func apply_network_teleport(snapshot: Dictionary) -> void:
|
|||
_network_target_position = global_position
|
||||
_network_target_velocity = velocity
|
||||
_network_target_visual_yaw = _visuals.rotation.y
|
||||
_apply_network_target_animation_state(parsed["animation_state"])
|
||||
if not Dictionary(parsed.get("animation_state", {})).is_empty():
|
||||
_apply_network_target_animation_state(parsed["animation_state"])
|
||||
_network_snapshot_age = 0.0
|
||||
_network_snapshot_ready = true
|
||||
|
||||
|
|
@ -2180,12 +2480,16 @@ func _parse_network_snapshot(snapshot: Dictionary) -> Dictionary:
|
|||
float(network_velocity[2])
|
||||
)
|
||||
var visual_yaw: float = float(snapshot.get("visual_yaw", 0.0))
|
||||
var animation_state_value: Variant = snapshot.get("animation_state")
|
||||
if not NetworkPlayerAnimationProtocol.validate_state(animation_state_value):
|
||||
return {}
|
||||
var animation_state: Dictionary = (
|
||||
animation_state_value as Dictionary
|
||||
).duplicate(true)
|
||||
var animation_state: Dictionary = {}
|
||||
if snapshot.has("animation_state"):
|
||||
var animation_state_value: Variant = snapshot.get("animation_state")
|
||||
if not NetworkPlayerAnimationProtocol.validate_state(
|
||||
animation_state_value
|
||||
):
|
||||
return {}
|
||||
animation_state = (
|
||||
animation_state_value as Dictionary
|
||||
).duplicate(true)
|
||||
if (
|
||||
snapshot.has("acknowledged_input")
|
||||
and (
|
||||
|
|
@ -2237,6 +2541,23 @@ func _apply_network_target_animation_state(state: Dictionary) -> void:
|
|||
StringName(str(state["locomotion_id"]))
|
||||
)
|
||||
var action: Dictionary = state["action"]
|
||||
var incoming_sequence: int = int(action["sequence"])
|
||||
var wrapped_sequence: bool = (
|
||||
_network_target_animation_action_sequence
|
||||
== NetworkPlayerAnimationProtocol.MAX_ACTION_SEQUENCE
|
||||
and incoming_sequence == 1
|
||||
)
|
||||
if (
|
||||
incoming_sequence < _network_target_animation_action_sequence
|
||||
and not wrapped_sequence
|
||||
):
|
||||
return
|
||||
if (
|
||||
incoming_sequence == _network_target_animation_action_sequence
|
||||
and StringName(str(action["id"]))
|
||||
!= _network_target_animation_action_id
|
||||
):
|
||||
return
|
||||
_network_target_animation_action_id = StringName(str(action["id"]))
|
||||
_network_target_animation_action_sequence = int(action["sequence"])
|
||||
_network_target_animation_action_elapsed = float(action["elapsed"])
|
||||
|
|
@ -2414,11 +2735,11 @@ func _update_network_interpolation(delta: float) -> void:
|
|||
func _update_network_input_freshness(delta: float) -> void:
|
||||
_network_input_age = minf(
|
||||
_network_input_age + delta,
|
||||
NETWORK_INPUT_STALE_TIMEOUT_SECONDS + 1.0,
|
||||
_network_input_stale_timeout_seconds + 1.0,
|
||||
)
|
||||
if (
|
||||
_network_input_stale
|
||||
or _network_input_age <= NETWORK_INPUT_STALE_TIMEOUT_SECONDS
|
||||
or _network_input_age <= _network_input_stale_timeout_seconds
|
||||
):
|
||||
return
|
||||
_network_input_stale = true
|
||||
|
|
@ -2523,6 +2844,7 @@ func apply_camera_settings(
|
|||
new_mouse_sensitivity: float,
|
||||
new_controller_sensitivity: float,
|
||||
invert_vertical: bool,
|
||||
swap_hotbar_camera_scroll: bool = false,
|
||||
) -> void:
|
||||
mouse_sensitivity = clampf(new_mouse_sensitivity, 0.001, 0.012)
|
||||
controller_camera_speed = clampf(
|
||||
|
|
@ -2531,6 +2853,7 @@ func apply_camera_settings(
|
|||
5.0
|
||||
)
|
||||
invert_camera_y = invert_vertical
|
||||
_swap_hotbar_camera_scroll = swap_hotbar_camera_scroll
|
||||
|
||||
|
||||
func is_camera_input_enabled() -> bool:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue