mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
sync - experiment with woobling over multiple ticks, to adjust to small imperfections, later discarded
This commit is contained in:
parent
820b3f06b0
commit
819661d1bb
@ -30,7 +30,7 @@ use common::{
|
|||||||
slot::{EquipSlot, InvSlotId, Slot},
|
slot::{EquipSlot, InvSlotId, Slot},
|
||||||
CharacterState, ChatMode, CommandGenerator, ControlAction, ControlEvent, Controller,
|
CharacterState, ChatMode, CommandGenerator, ControlAction, ControlEvent, Controller,
|
||||||
ControllerInputs, GroupManip, InputKind, InventoryAction, InventoryEvent,
|
ControllerInputs, GroupManip, InputKind, InventoryAction, InventoryEvent,
|
||||||
InventoryUpdateEvent, MapMarkerChange, RemoteController, UtteranceKind,
|
InventoryUpdateEvent, MapMarkerChange, RemoteController, UtteranceKind, Vel,
|
||||||
},
|
},
|
||||||
event::{EventBus, LocalEvent, UpdateCharacterMetadata},
|
event::{EventBus, LocalEvent, UpdateCharacterMetadata},
|
||||||
grid::Grid,
|
grid::Grid,
|
||||||
@ -238,6 +238,7 @@ pub struct Client {
|
|||||||
local_command_gen: CommandGenerator,
|
local_command_gen: CommandGenerator,
|
||||||
next_control: Controller,
|
next_control: Controller,
|
||||||
inter_tick_rewind_time: Option<Duration>,
|
inter_tick_rewind_time: Option<Duration>,
|
||||||
|
_rewind_fluctuation_budget: f64,
|
||||||
|
|
||||||
network: Option<Network>,
|
network: Option<Network>,
|
||||||
participant: Option<Participant>,
|
participant: Option<Participant>,
|
||||||
@ -723,6 +724,7 @@ impl Client {
|
|||||||
local_command_gen: CommandGenerator::default(),
|
local_command_gen: CommandGenerator::default(),
|
||||||
next_control: Controller::default(),
|
next_control: Controller::default(),
|
||||||
inter_tick_rewind_time: None,
|
inter_tick_rewind_time: None,
|
||||||
|
_rewind_fluctuation_budget: 0.0,
|
||||||
|
|
||||||
network: Some(network),
|
network: Some(network),
|
||||||
participant: Some(participant),
|
participant: Some(participant),
|
||||||
@ -1732,7 +1734,9 @@ impl Client {
|
|||||||
frontend_events.append(&mut self.handle_new_messages()?);
|
frontend_events.append(&mut self.handle_new_messages()?);
|
||||||
|
|
||||||
// Simulate Ahead
|
// Simulate Ahead
|
||||||
if let Some(_rewind_time) = self.inter_tick_rewind_time {
|
common_base::plot!("recived_time_sync", 0.0);
|
||||||
|
if let Some(rewind_time) = self.inter_tick_rewind_time {
|
||||||
|
common_base::plot!("recived_time_sync", 1.0);
|
||||||
let _time = self.state.ecs().read_resource::<Time>().0 as f64;
|
let _time = self.state.ecs().read_resource::<Time>().0 as f64;
|
||||||
let simulate_ahead = self
|
let simulate_ahead = self
|
||||||
.state
|
.state
|
||||||
@ -1741,10 +1745,49 @@ impl Client {
|
|||||||
.get(self.entity())
|
.get(self.entity())
|
||||||
.map(|rc| rc.simulate_ahead())
|
.map(|rc| rc.simulate_ahead())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
// We substract `dt` here, as otherwise we
|
||||||
|
// Tick1: server_time=100ms dt=60ms ping=30 simulate_ahead=130ms,
|
||||||
|
// rewind_tick=130ms end_tick=100+130+60=290
|
||||||
|
// Tick2: server_time=130ms dt=30ms ping=30 simulate_ahead=130ms,
|
||||||
|
// rewind_tick=130ms end_tick=130+130+30=290
|
||||||
|
// Tick3: server_time=160ms dt=60ms ping=30 simulate_ahead=130ms,
|
||||||
|
// rewind_tick=130ms end_tick=160+130+60=350 with dt substraction
|
||||||
|
// Tick1: server_time=100ms dt=60ms ping=30 simulate_ahead=130ms,
|
||||||
|
// rewind_tick=70ms end_tick=100+70+60=230 Tick2: server_time=130ms
|
||||||
|
// dt=30ms ping=30 simulate_ahead=130ms, rewind_tick=100ms
|
||||||
|
// end_tick=130+100+30=260 Tick3: server_time=160ms dt=60ms ping=30
|
||||||
|
// simulate_ahead=130ms, rewind_tick=70ms end_tick=160+70+60=290
|
||||||
let simulate_ahead = simulate_ahead.max(dt) - dt;
|
let simulate_ahead = simulate_ahead.max(dt) - dt;
|
||||||
|
// measurements lead to the effect that smooth_diff is == 0.0 when we add 2
|
||||||
|
// server ticks here.
|
||||||
|
let simulate_ahead = simulate_ahead + Duration::from_secs_f64(1.0 / 30.0);
|
||||||
|
|
||||||
|
let _strict_end_tick_time =
|
||||||
|
simulate_ahead.as_secs_f64() + /*simulated dt of this tick*/dt.as_secs_f64();
|
||||||
|
|
||||||
|
// Simulate_ahead still fluctionates because Server Tick != Client Tick, and we
|
||||||
|
// cant control the phase in which the sync happens.
|
||||||
|
// In order to dampen it, we calculate the smooth_time and make sure to not
|
||||||
|
// derive to much from it
|
||||||
|
let smooth_diff = simulate_ahead.as_secs_f64() - rewind_time.as_secs_f64();
|
||||||
|
|
||||||
|
//const WARP_PERCENT: f64 = 0.05; // make sure we end up not further than 5%
|
||||||
|
// from the estimated tick let warp_budget
|
||||||
|
let simulate_ahead = if smooth_diff / dt.as_secs_f64() > 0.05 {
|
||||||
|
// use
|
||||||
|
simulate_ahead
|
||||||
|
} else {
|
||||||
|
simulate_ahead
|
||||||
|
};
|
||||||
|
|
||||||
|
common_base::plot!("smooth_diff", smooth_diff);
|
||||||
|
|
||||||
|
//let simulate_ahead = simulate_ahead.max(dt)/* - dt*/;
|
||||||
|
//let simulate_ahead = rewind_time.min(simulate_ahead);
|
||||||
tracing::warn!(?simulate_ahead, ?dt, "simulating ahead again");
|
tracing::warn!(?simulate_ahead, ?dt, "simulating ahead again");
|
||||||
|
common_base::plot!("rewind_time", rewind_time.as_secs_f64());
|
||||||
self.state.rewind_tick(
|
self.state.rewind_tick(
|
||||||
simulate_ahead.max(dt) - dt,
|
simulate_ahead,
|
||||||
|dispatch_builder| {
|
|dispatch_builder| {
|
||||||
add_rewind_systems(dispatch_builder);
|
add_rewind_systems(dispatch_builder);
|
||||||
},
|
},
|
||||||
@ -1752,6 +1795,7 @@ impl Client {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
common_base::plot!("dt", dt.as_secs_f64());
|
||||||
self.state.tick(
|
self.state.tick(
|
||||||
dt,
|
dt,
|
||||||
|dispatch_builder| {
|
|dispatch_builder| {
|
||||||
@ -1760,6 +1804,29 @@ impl Client {
|
|||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
let time = self.state.ecs().read_resource::<Time>().0;
|
||||||
|
common_base::plot!("tick_afterwards", time);
|
||||||
|
let vel = self
|
||||||
|
.state
|
||||||
|
.ecs()
|
||||||
|
.read_storage::<Vel>()
|
||||||
|
.get(self.entity())
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or(Vel(Vec3::zero()));
|
||||||
|
|
||||||
|
common_base::plot!("vel_x_after", vel.0.x as f64);
|
||||||
|
common_base::plot!("vel_y_after", vel.0.y as f64);
|
||||||
|
let pos = self
|
||||||
|
.state
|
||||||
|
.ecs()
|
||||||
|
.read_storage::<common::comp::Pos>()
|
||||||
|
.get(self.entity())
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or(common::comp::Pos(Vec3::zero()));
|
||||||
|
|
||||||
|
common_base::plot!("pos_x_after", pos.0.x as f64);
|
||||||
|
common_base::plot!("pos_y_after", pos.0.y as f64);
|
||||||
|
common_base::plot!("pos_z_after", pos.0.z as f64);
|
||||||
|
|
||||||
// 2) Handle input from frontend.
|
// 2) Handle input from frontend.
|
||||||
// Pass character actions from frontend input to the player's entity.
|
// Pass character actions from frontend input to the player's entity.
|
||||||
@ -2191,6 +2258,8 @@ impl Client {
|
|||||||
prof_span!("handle_server_in_game_msg");
|
prof_span!("handle_server_in_game_msg");
|
||||||
match msg {
|
match msg {
|
||||||
ServerGeneral::TimeSync(time) => {
|
ServerGeneral::TimeSync(time) => {
|
||||||
|
// Even with a stable network, expect time to oscillate around the actual time
|
||||||
|
// by SERVER_TICK (33.3ms)
|
||||||
let old_time = self.state.ecs().read_resource::<Time>().0;
|
let old_time = self.state.ecs().read_resource::<Time>().0;
|
||||||
let diff = old_time - time.0;
|
let diff = old_time - time.0;
|
||||||
self.state.ecs().write_resource::<Time>().0 = time.0;
|
self.state.ecs().write_resource::<Time>().0 = time.0;
|
||||||
@ -2203,20 +2272,28 @@ impl Client {
|
|||||||
tracing::warn!(?old_time, ?diff, "Time was advanced by server");
|
tracing::warn!(?old_time, ?diff, "Time was advanced by server");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ServerGeneral::AckControl(acked_ids, _time) => {
|
|
||||||
|
ServerGeneral::AckControl {
|
||||||
|
acked_ids,
|
||||||
|
highest_ahead_command,
|
||||||
|
predict_available,
|
||||||
|
} => {
|
||||||
if let Some(remote_controller) = self
|
if let Some(remote_controller) = self
|
||||||
.state
|
.state
|
||||||
.ecs()
|
.ecs()
|
||||||
.write_storage::<RemoteController>()
|
.write_storage::<RemoteController>()
|
||||||
.get_mut(self.entity())
|
.get_mut(self.entity())
|
||||||
{
|
{
|
||||||
|
common_base::plot!("server_predict_available", predict_available as f64);
|
||||||
|
common_base::plot!("highest_ahead_command2", highest_ahead_command);
|
||||||
|
|
||||||
// for now ignore the time send by the server as its based on TIME and just use
|
// for now ignore the time send by the server as its based on TIME and just use
|
||||||
// MonotonicTime
|
// MonotonicTime
|
||||||
let monotonic_time = Duration::from_secs_f64(
|
let monotonic_time = Duration::from_secs_f64(
|
||||||
self.state.ecs().read_resource::<MonotonicTime>().0,
|
self.state.ecs().read_resource::<MonotonicTime>().0,
|
||||||
);
|
);
|
||||||
//let time = Duration::from_secs_f64(time.0);
|
//let time = Duration::from_secs_f64(time.0);
|
||||||
remote_controller.acked(acked_ids, monotonic_time);
|
remote_controller.acked(acked_ids, monotonic_time, highest_ahead_command);
|
||||||
remote_controller.maintain(None);
|
remote_controller.maintain(None);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -49,13 +49,13 @@ macro_rules! comp_packet {
|
|||||||
sync::handle_insert(comp, entity, world);
|
sync::handle_insert(comp, entity, world);
|
||||||
},)*
|
},)*
|
||||||
Self::Pos(comp) => {
|
Self::Pos(comp) => {
|
||||||
sync::handle_interp_insert(comp, entity, world, force_update)
|
sync::handle_interp_insert(comp, entity, world, true)
|
||||||
},
|
},
|
||||||
Self::Vel(comp) => {
|
Self::Vel(comp) => {
|
||||||
sync::handle_interp_insert(comp, entity, world, force_update)
|
sync::handle_interp_insert(comp, entity, world, true)
|
||||||
},
|
},
|
||||||
Self::Ori(comp) => {
|
Self::Ori(comp) => {
|
||||||
sync::handle_interp_insert(comp, entity, world, force_update)
|
sync::handle_interp_insert(comp, entity, world, true)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,13 +67,13 @@ macro_rules! comp_packet {
|
|||||||
sync::handle_modify(comp, entity, world);
|
sync::handle_modify(comp, entity, world);
|
||||||
},)*
|
},)*
|
||||||
Self::Pos(comp) => {
|
Self::Pos(comp) => {
|
||||||
sync::handle_interp_modify(comp, entity, world, force_update)
|
sync::handle_interp_modify(comp, entity, world, true)
|
||||||
},
|
},
|
||||||
Self::Vel(comp) => {
|
Self::Vel(comp) => {
|
||||||
sync::handle_interp_modify(comp, entity, world, force_update)
|
sync::handle_interp_modify(comp, entity, world, true)
|
||||||
},
|
},
|
||||||
Self::Ori(comp) => {
|
Self::Ori(comp) => {
|
||||||
sync::handle_interp_modify(comp, entity, world, force_update)
|
sync::handle_interp_modify(comp, entity, world, true)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,7 +143,14 @@ pub enum ServerGeneral {
|
|||||||
SpectatorSuccess(Vec3<f32>),
|
SpectatorSuccess(Vec3<f32>),
|
||||||
//Ingame related
|
//Ingame related
|
||||||
TimeSync(Time),
|
TimeSync(Time),
|
||||||
AckControl(HashSet<u64>, Time),
|
AckControl {
|
||||||
|
acked_ids: HashSet<u64>,
|
||||||
|
/// measured by the time the furthest command to become active <>
|
||||||
|
/// current server time
|
||||||
|
highest_ahead_command: f64,
|
||||||
|
/// number of predicts available at the server
|
||||||
|
predict_available: usize,
|
||||||
|
},
|
||||||
GroupUpdate(comp::group::ChangeNotification<Uid>),
|
GroupUpdate(comp::group::ChangeNotification<Uid>),
|
||||||
/// Indicate to the client that they are invited to join a group
|
/// Indicate to the client that they are invited to join a group
|
||||||
Invite {
|
Invite {
|
||||||
@ -318,7 +325,7 @@ impl ServerMsg {
|
|||||||
//Ingame related
|
//Ingame related
|
||||||
ServerGeneral::GroupUpdate(_)
|
ServerGeneral::GroupUpdate(_)
|
||||||
| ServerGeneral::TimeSync(_)
|
| ServerGeneral::TimeSync(_)
|
||||||
| ServerGeneral::AckControl(_, _)
|
| ServerGeneral::AckControl { .. }
|
||||||
| ServerGeneral::Invite { .. }
|
| ServerGeneral::Invite { .. }
|
||||||
| ServerGeneral::InvitePending(_)
|
| ServerGeneral::InvitePending(_)
|
||||||
| ServerGeneral::InviteComplete { .. }
|
| ServerGeneral::InviteComplete { .. }
|
||||||
|
@ -56,7 +56,7 @@ impl<T: 'static + Send + Sync> Component for InterpBuffer<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 0 is pure physics, 1 is pure extrapolation
|
// 0 is pure physics, 1 is pure extrapolation
|
||||||
const PHYSICS_VS_EXTRAPOLATION_FACTOR: f32 = 0.1;
|
const PHYSICS_VS_EXTRAPOLATION_FACTOR: f32 = 0.0;
|
||||||
const POSITION_INTERP_SANITY: Option<f32> = None;
|
const POSITION_INTERP_SANITY: Option<f32> = None;
|
||||||
const VELOCITY_INTERP_SANITY: Option<f32> = None;
|
const VELOCITY_INTERP_SANITY: Option<f32> = None;
|
||||||
const ENABLE_POSITION_HERMITE: bool = false;
|
const ENABLE_POSITION_HERMITE: bool = false;
|
||||||
|
@ -3,7 +3,6 @@ use hashbrown::HashSet;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specs::{Component, DenseVecStorage};
|
use specs::{Component, DenseVecStorage};
|
||||||
use std::{collections::VecDeque, time::Duration};
|
use std::{collections::VecDeque, time::Duration};
|
||||||
use tracing::warn;
|
|
||||||
|
|
||||||
pub type ControlCommands = VecDeque<ControlCommand>;
|
pub type ControlCommands = VecDeque<ControlCommand>;
|
||||||
|
|
||||||
@ -29,6 +28,10 @@ pub struct ControlCommand {
|
|||||||
action_time: Duration,
|
action_time: Duration,
|
||||||
/// *ContinuousMonotonicTime* when this msg was first send to remote
|
/// *ContinuousMonotonicTime* when this msg was first send to remote
|
||||||
first_send_monotonic_time: Option<Duration>,
|
first_send_monotonic_time: Option<Duration>,
|
||||||
|
/// action_time - *Time* when this msg was first send to remote
|
||||||
|
first_send_simulate_ahead_time: Option<f64>,
|
||||||
|
/// *ContinuousMonotonicTime* when this msg was first acked by remote
|
||||||
|
first_acked_monotonic_time: Option<Duration>,
|
||||||
msg: Controller,
|
msg: Controller,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,23 +95,88 @@ impl RemoteController {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// arrived at remote and no longer need to be hold locally
|
/// remote confirmed arrival of commands. (not that they actually were used)
|
||||||
pub fn acked(&mut self, ids: HashSet<u64>, monotonic_time: Duration) {
|
/// we need to hold them, (especially with high lag) as we might revert to
|
||||||
// To get the latency we use the LOWEST `first_send_monotonic_time` in this Set.
|
/// server-time.
|
||||||
// Why lowest ? Because we want to be STABLE, and if we just use the highest it
|
pub fn acked(
|
||||||
// wouldn't be enough latency for all (e.g. retransmittion)
|
&mut self,
|
||||||
let mut lowest_monotonic_time = None;
|
ids: HashSet<u64>,
|
||||||
self.commands.retain(|c| {
|
monotonic_time: Duration,
|
||||||
let retain = !ids.contains(&c.id);
|
highest_ahead_command: f64,
|
||||||
if !retain
|
) {
|
||||||
&& c.first_send_monotonic_time.map_or(false, |mt| {
|
// calculating avg_latency.
|
||||||
mt < lowest_monotonic_time.unwrap_or(monotonic_time)
|
// - server returns the time the furthest away command has left till it becomes
|
||||||
})
|
// active. As the server time is constant and we stored this value locally,
|
||||||
{
|
// we use it to calculate the first ahead_command for the first command in
|
||||||
lowest_monotonic_time = c.first_send_monotonic_time;
|
// this batch.
|
||||||
|
// - we try to keep the remote_ahead in a specific window to allow for
|
||||||
|
// retransmittion and remote tick time
|
||||||
|
|
||||||
|
// find time it took from client -> server
|
||||||
|
let high_filter = self
|
||||||
|
.commands
|
||||||
|
.iter()
|
||||||
|
.filter(|c| ids.contains(&c.id) && c.first_acked_monotonic_time.is_none());
|
||||||
|
if let Some(highest) = high_filter.max_by_key(|c| c.action_time) {
|
||||||
|
let remote_time = highest.action_time.as_secs_f64() - highest_ahead_command;
|
||||||
|
//let latency = highest.first_send_simulate_ahead_time.unwrap_or_default() -
|
||||||
|
// remote_time;
|
||||||
|
common_base::plot!("prob_remote_time", remote_time);
|
||||||
|
//common_base::plot!("prob_avg_latency", latency);
|
||||||
|
//let low_filter = self.commands.iter().filter(|c| ids.contains(&c.id) &&
|
||||||
|
// c.first_send_monotonic_time == highest.first_send_monotonic_time);
|
||||||
|
let low_filter = self
|
||||||
|
.commands
|
||||||
|
.iter()
|
||||||
|
.filter(|c| ids.contains(&c.id) && c.first_acked_monotonic_time.is_none());
|
||||||
|
if let Some(lowest) = low_filter.min_by_key(|c| c.action_time) {
|
||||||
|
let low_high_diff = highest.action_time - lowest.action_time;
|
||||||
|
// if this is 50ms, it means the lowest command arrived at server 50 before it
|
||||||
|
// was used
|
||||||
|
common_base::plot!("highest_ahead_command", highest_ahead_command);
|
||||||
|
let ahead_time = highest_ahead_command - low_high_diff.as_secs_f64();
|
||||||
|
common_base::plot!("low_high_diff", low_high_diff.as_secs_f64());
|
||||||
|
common_base::plot!("ahead_time", ahead_time);
|
||||||
|
const LOWER_END: f64 = 0.18;
|
||||||
|
const UPPER_END: f64 = 0.22;
|
||||||
|
//if ahead_time > 2.0 {
|
||||||
|
let len = self.commands.len();
|
||||||
|
tracing::error!(
|
||||||
|
?ahead_time,
|
||||||
|
?ids,
|
||||||
|
?highest_ahead_command,
|
||||||
|
?highest,
|
||||||
|
?lowest,
|
||||||
|
?len,
|
||||||
|
"bigger2"
|
||||||
|
);
|
||||||
|
//}
|
||||||
|
if ahead_time < LOWER_END {
|
||||||
|
self.avg_latency += Duration::from_millis(10);
|
||||||
|
}
|
||||||
|
if ahead_time > UPPER_END && self.avg_latency > Duration::from_millis(3) {
|
||||||
|
self.avg_latency -= Duration::from_millis(3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
retain
|
}
|
||||||
});
|
|
||||||
|
for c in &mut self.commands {
|
||||||
|
c.first_acked_monotonic_time = Some(monotonic_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
let mut lowest_monotonic_time = None;
|
||||||
|
for c in &mut self.commands {
|
||||||
|
let new_acked = c.first_acked_monotonic_time.is_none() && ids.contains(&c.id);
|
||||||
|
if new_acked {
|
||||||
|
c.first_acked_monotonic_time = Some(monotonic_time);
|
||||||
|
if c.first_send_monotonic_time.map_or(false, |mt| {
|
||||||
|
mt < lowest_monotonic_time.unwrap_or(monotonic_time)
|
||||||
|
}) {
|
||||||
|
lowest_monotonic_time = c.first_send_monotonic_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if let Some(lowest_monotonic_time) = lowest_monotonic_time {
|
if let Some(lowest_monotonic_time) = lowest_monotonic_time {
|
||||||
if let Some(latency) = monotonic_time.checked_sub(lowest_monotonic_time) {
|
if let Some(latency) = monotonic_time.checked_sub(lowest_monotonic_time) {
|
||||||
common_base::plot!("latency", latency.as_secs_f64());
|
common_base::plot!("latency", latency.as_secs_f64());
|
||||||
@ -119,16 +187,25 @@ impl RemoteController {
|
|||||||
synced and monotonic!"
|
synced and monotonic!"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
common_base::plot!("avg_latency", self.avg_latency.as_secs_f64());
|
common_base::plot!("avg_latency", self.avg_latency.as_secs_f64());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// prepare commands for sending
|
/// prepare commands for sending
|
||||||
|
/// only include commands, that were not yet acked!
|
||||||
pub fn prepare_commands(&mut self, monotonic_time: Duration) -> ControlCommands {
|
pub fn prepare_commands(&mut self, monotonic_time: Duration) -> ControlCommands {
|
||||||
for c in &mut self.commands {
|
let simulate_ahead = self.simulate_ahead().as_secs_f64();
|
||||||
c.first_send_monotonic_time.get_or_insert(monotonic_time);
|
self.commands
|
||||||
}
|
.iter_mut()
|
||||||
self.commands.clone()
|
.filter(|c| c.first_acked_monotonic_time.is_none())
|
||||||
|
.map(|c| {
|
||||||
|
c.first_send_monotonic_time.get_or_insert(monotonic_time);
|
||||||
|
c.first_send_simulate_ahead_time
|
||||||
|
.get_or_insert(simulate_ahead);
|
||||||
|
&*c
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn commands(&self) -> &ControlCommands { &self.commands }
|
pub fn commands(&self) -> &ControlCommands { &self.commands }
|
||||||
@ -230,7 +307,7 @@ impl RemoteController {
|
|||||||
pub fn avg_latency(&self) -> Duration { self.avg_latency }
|
pub fn avg_latency(&self) -> Duration { self.avg_latency }
|
||||||
|
|
||||||
pub fn simulate_ahead(&self) -> Duration {
|
pub fn simulate_ahead(&self) -> Duration {
|
||||||
const FIXED_OFFSET: Duration = Duration::from_millis(50);
|
const FIXED_OFFSET: Duration = Duration::from_millis(0);
|
||||||
self.avg_latency() + FIXED_OFFSET
|
self.avg_latency() + FIXED_OFFSET
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -240,7 +317,7 @@ impl Default for RemoteController {
|
|||||||
Self {
|
Self {
|
||||||
commands: VecDeque::new(),
|
commands: VecDeque::new(),
|
||||||
existing_commands: HashSet::new(),
|
existing_commands: HashSet::new(),
|
||||||
max_hold: Duration::from_secs(1),
|
max_hold: Duration::from_secs(5),
|
||||||
avg_latency: Duration::from_millis(50),
|
avg_latency: Duration::from_millis(50),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -261,6 +338,8 @@ impl CommandGenerator {
|
|||||||
ControlCommand {
|
ControlCommand {
|
||||||
action_time,
|
action_time,
|
||||||
first_send_monotonic_time: None,
|
first_send_monotonic_time: None,
|
||||||
|
first_send_simulate_ahead_time: None,
|
||||||
|
first_acked_monotonic_time: None,
|
||||||
id: self.id,
|
id: self.id,
|
||||||
msg,
|
msg,
|
||||||
}
|
}
|
||||||
|
@ -594,15 +594,24 @@ impl State {
|
|||||||
pub fn rewind_tick(
|
pub fn rewind_tick(
|
||||||
&mut self,
|
&mut self,
|
||||||
simulate_ahead: Duration,
|
simulate_ahead: Duration,
|
||||||
add_systems: impl Fn(&mut DispatcherBuilder),
|
add_systems: impl Fn(&mut DispatcherBuilder) + Clone,
|
||||||
update_terrain_and_regions: bool,
|
update_terrain_and_regions: bool,
|
||||||
) {
|
) {
|
||||||
|
common_base::plot!("simulate_ahead", simulate_ahead.as_secs_f64());
|
||||||
let time_of_day = self.ecs.read_resource::<TimeOfDay>().0;
|
let time_of_day = self.ecs.read_resource::<TimeOfDay>().0;
|
||||||
let _time = self.ecs.read_resource::<Time>().0;
|
let _time = self.ecs.read_resource::<Time>().0;
|
||||||
let monotonic_time = self.ecs.read_resource::<MonotonicTime>().0;
|
let monotonic_time = self.ecs.read_resource::<MonotonicTime>().0;
|
||||||
let delta_time = self.ecs.read_resource::<DeltaTime>().0;
|
let delta_time = self.ecs.read_resource::<DeltaTime>().0;
|
||||||
|
|
||||||
self.tick(simulate_ahead, add_systems, update_terrain_and_regions);
|
const MAX_INCREMENTS: usize = 100; // The maximum number of collision tests per tick
|
||||||
|
const STEP_SEC: f64 = 0.1;
|
||||||
|
let increments =
|
||||||
|
((simulate_ahead.as_secs_f64() / STEP_SEC).ceil() as usize).clamped(1, MAX_INCREMENTS);
|
||||||
|
for _i in 0..increments {
|
||||||
|
//tracing::trace!(?i, ?increments, "subtick");
|
||||||
|
let partial = simulate_ahead / (increments as u32);
|
||||||
|
self.tick(partial, add_systems.clone(), update_terrain_and_regions);
|
||||||
|
}
|
||||||
|
|
||||||
// rewind changes
|
// rewind changes
|
||||||
|
|
||||||
@ -639,7 +648,7 @@ impl State {
|
|||||||
// Update delta time.
|
// Update delta time.
|
||||||
// Beyond a delta time of MAX_DELTA_TIME, start lagging to avoid skipping
|
// Beyond a delta time of MAX_DELTA_TIME, start lagging to avoid skipping
|
||||||
// important physics events.
|
// important physics events.
|
||||||
self.ecs.write_resource::<DeltaTime>().0 = dt.as_secs_f32().min(MAX_DELTA_TIME);
|
self.ecs.write_resource::<DeltaTime>().0 = dt.as_secs_f32(); //.min(MAX_DELTA_TIME);
|
||||||
|
|
||||||
if update_terrain_and_regions {
|
if update_terrain_and_regions {
|
||||||
self.update_region_map();
|
self.update_region_map();
|
||||||
|
@ -22,7 +22,7 @@ use specs::DispatcherBuilder;
|
|||||||
pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
|
pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
|
||||||
dispatch::<predict_controller::Sys>(dispatch_builder, &[]);
|
dispatch::<predict_controller::Sys>(dispatch_builder, &[]);
|
||||||
//TODO: don't run interpolation on server
|
//TODO: don't run interpolation on server
|
||||||
dispatch::<interpolation::Sys>(dispatch_builder, &[]);
|
dispatch::<interpolation::Sys>(dispatch_builder, &[&predict_controller::Sys::sys_name()]);
|
||||||
dispatch::<mount::Sys>(dispatch_builder, &[]);
|
dispatch::<mount::Sys>(dispatch_builder, &[]);
|
||||||
dispatch::<controller::Sys>(dispatch_builder, &[&mount::Sys::sys_name()]);
|
dispatch::<controller::Sys>(dispatch_builder, &[&mount::Sys::sys_name()]);
|
||||||
dispatch::<character_behavior::Sys>(dispatch_builder, &[&controller::Sys::sys_name()]);
|
dispatch::<character_behavior::Sys>(dispatch_builder, &[&controller::Sys::sys_name()]);
|
||||||
@ -41,8 +41,9 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_rewind_systems(dispatch_builder: &mut DispatcherBuilder) {
|
pub fn add_rewind_systems(dispatch_builder: &mut DispatcherBuilder) {
|
||||||
|
dispatch::<predict_controller::Sys>(dispatch_builder, &[]);
|
||||||
//TODO: don't run interpolation on server
|
//TODO: don't run interpolation on server
|
||||||
dispatch::<interpolation::Sys>(dispatch_builder, &[]);
|
dispatch::<interpolation::Sys>(dispatch_builder, &[&predict_controller::Sys::sys_name()]);
|
||||||
dispatch::<mount::Sys>(dispatch_builder, &[]);
|
dispatch::<mount::Sys>(dispatch_builder, &[]);
|
||||||
dispatch::<controller::Sys>(dispatch_builder, &[&mount::Sys::sys_name()]);
|
dispatch::<controller::Sys>(dispatch_builder, &[&mount::Sys::sys_name()]);
|
||||||
dispatch::<character_behavior::Sys>(dispatch_builder, &[&controller::Sys::sys_name()]);
|
dispatch::<character_behavior::Sys>(dispatch_builder, &[&controller::Sys::sys_name()]);
|
||||||
|
@ -216,6 +216,9 @@ impl<'a> PhysicsData<'a> {
|
|||||||
let entity_center = position.0 + Vec3::new(0.0, 0.0, z_min + half_height);
|
let entity_center = position.0 + Vec3::new(0.0, 0.0, z_min + half_height);
|
||||||
let flat_radius = collider.bounding_radius() * scale;
|
let flat_radius = collider.bounding_radius() * scale;
|
||||||
let radius = (flat_radius.powi(2) + half_height.powi(2)).sqrt();
|
let radius = (flat_radius.powi(2) + half_height.powi(2)).sqrt();
|
||||||
|
//tracing::info!(?i, ?vel, "vel");
|
||||||
|
common_base::plot!("vel_x", vel.0.x as f64);
|
||||||
|
common_base::plot!("vel_y", vel.0.y as f64);
|
||||||
|
|
||||||
// Move center to the middle between OLD and OLD+VEL_DT
|
// Move center to the middle between OLD and OLD+VEL_DT
|
||||||
// so that we can reduce the collision_boundary.
|
// so that we can reduce the collision_boundary.
|
||||||
|
@ -178,7 +178,7 @@ impl Client {
|
|||||||
//In-game related
|
//In-game related
|
||||||
ServerGeneral::GroupUpdate(_)
|
ServerGeneral::GroupUpdate(_)
|
||||||
| ServerGeneral::TimeSync(_)
|
| ServerGeneral::TimeSync(_)
|
||||||
| ServerGeneral::AckControl(_, _)
|
| ServerGeneral::AckControl { .. }
|
||||||
| ServerGeneral::Invite { .. }
|
| ServerGeneral::Invite { .. }
|
||||||
| ServerGeneral::InvitePending(_)
|
| ServerGeneral::InvitePending(_)
|
||||||
| ServerGeneral::InviteComplete { .. }
|
| ServerGeneral::InviteComplete { .. }
|
||||||
|
@ -87,11 +87,20 @@ impl Sys {
|
|||||||
},
|
},
|
||||||
ClientGeneral::Control(rc) => {
|
ClientGeneral::Control(rc) => {
|
||||||
if presence.kind.controlling_char() {
|
if presence.kind.controlling_char() {
|
||||||
let ids = remote_controller.append(rc);
|
let highest_action_time = rc.iter().map(|rc| rc.source_time()).max().unwrap_or_default();
|
||||||
remote_controller.maintain(Some(Duration::from_secs_f64(time.0)));
|
if rc.is_empty() {
|
||||||
|
tracing::warn!("client send a empty ClientGeneral::Control msg");
|
||||||
// confirm controls
|
} else {
|
||||||
client.send(ServerGeneral::AckControl(ids, *time))?;
|
let acked_ids = remote_controller.append(rc);
|
||||||
|
remote_controller.maintain(Some(Duration::from_secs_f64(time.0)));
|
||||||
|
let highest_ahead_command = highest_action_time.as_secs_f64()-time.0;
|
||||||
|
let predict_available = remote_controller.commands().len();
|
||||||
|
client.send(ServerGeneral::AckControl{
|
||||||
|
acked_ids,
|
||||||
|
highest_ahead_command,
|
||||||
|
predict_available
|
||||||
|
})?;
|
||||||
|
}
|
||||||
//Todo: FIXME!!!
|
//Todo: FIXME!!!
|
||||||
/*
|
/*
|
||||||
// Skip respawn if client entity is alive
|
// Skip respawn if client entity is alive
|
||||||
|
Loading…
Reference in New Issue
Block a user