#![feature(generic_associated_types)]
use crystalorb::{client::stage::Stage, Config, TweeningMethod};
use test_log::test;
mod common;
use common::MockClientServer;
#[test]
fn when_server_and_client_clocks_desync_then_client_should_resync_quickly() {
const UPDATE_COUNT: usize = 200;
const TIMESTEP_SECONDS: f64 = 1.0 / 64.0;
for desync_seconds in &[
0.0f64,
0.5f64,
-0.5f64,
-1.0f64,
-100.0f64,
-1000.0f64,
-10000.0f64,
] {
let mut mock_client_server = MockClientServer::new(Config {
lag_compensation_latency: TIMESTEP_SECONDS * 16.0,
blend_latency: 0.2,
timestep_seconds: TIMESTEP_SECONDS,
clock_sync_needed_sample_count: 8,
clock_sync_request_period: 0.0,
clock_sync_assumed_outlier_rate: 0.2,
max_tolerable_clock_deviation: 0.1,
snapshot_send_period: 0.1,
update_delta_seconds_max: 0.5,
timestamp_skip_threshold_seconds: 1.0,
fastforward_max_per_step: 10,
tweening_method: TweeningMethod::MostRecentlyPassed,
});
mock_client_server.client_1_net.connect();
mock_client_server.client_2_net.connect();
mock_client_server.update_until_clients_ready(TIMESTEP_SECONDS);
match mock_client_server.client_1.stage() {
Stage::Ready(client) => {
assert_eq!(
client.last_completed_timestamp(),
mock_client_server
.server
.estimated_client_last_completed_timestamp()
+ 1,
"Precondition: clocks are initially synced up"
);
}
_ => unreachable!(),
}
mock_client_server.client_1_clock_offset = *desync_seconds;
for _ in 0..UPDATE_COUNT {
mock_client_server.update(TIMESTEP_SECONDS);
}
match mock_client_server.client_1.stage() {
Stage::Ready(client) => {
assert_eq!(
client.last_completed_timestamp(),
mock_client_server
.server
.estimated_client_last_completed_timestamp()
+ 1,
"Condition: Client synced up after {} updates (desync by {})",
UPDATE_COUNT,
desync_seconds
);
}
_ => unreachable!(),
}
}
}
#[test]
fn when_client_connects_then_client_calculates_correct_initial_clock_offset() {
const TIMESTEP_SECONDS: f64 = 1.0 / 64.0;
for desync_seconds in &[
0.0f64,
0.5f64,
1.0f64,
100.0f64,
1000.0f64,
10000.0f64,
-0.5f64,
-1.0f64,
-100.0f64,
-1000.0f64,
-10000.0f64,
] {
let mut mock_client_server = MockClientServer::new(Config {
lag_compensation_latency: TIMESTEP_SECONDS * 16.0,
blend_latency: 0.2,
timestep_seconds: TIMESTEP_SECONDS,
clock_sync_needed_sample_count: 8,
clock_sync_request_period: 0.0,
clock_sync_assumed_outlier_rate: 0.2,
max_tolerable_clock_deviation: 0.1,
snapshot_send_period: 0.1,
update_delta_seconds_max: 0.5,
timestamp_skip_threshold_seconds: 1.0,
fastforward_max_per_step: 10,
tweening_method: TweeningMethod::MostRecentlyPassed,
});
mock_client_server.client_1_net.connect();
mock_client_server.client_2_net.connect();
mock_client_server.client_1_clock_offset = *desync_seconds;
mock_client_server.update_until_clients_ready(TIMESTEP_SECONDS);
match mock_client_server.client_1.stage() {
Stage::Ready(client) => {
assert_eq!(
client.last_completed_timestamp(),
mock_client_server
.server
.estimated_client_last_completed_timestamp()
+ 1,
"Precondition: clocks are initially synced up"
);
}
_ => unreachable!(),
}
}
}