~starkingdoms/starkingdoms

8d2bec1c927f2adeffd7fdf0b14c7d29db41df23 — ghostly_zsh 4 days ago 90fa20b
netcode: rotation interpolation
M crates/unified/assets/config/world.wc.toml => crates/unified/assets/config/world.wc.toml +1 -1
@@ 3,7 3,7 @@
gravity = 500.0
gravity_iterations = 8
spawn_parts_at = "Earth"
spawn_parts_interval_secs = 1000
spawn_parts_interval_secs = 1
orbit_scale_factor = 4.0

[part]

M crates/unified/src/client/interpolation.rs => crates/unified/src/client/interpolation.rs +43 -32
@@ 1,5 1,5 @@
use std::collections::VecDeque;
use std::f32::consts::PI;
use std::f64::consts::PI;
use std::time::{Duration};
use avian2d::parry::transformation::utils::transform;
use bevy_replicon::client::confirm_history::ConfirmHistory;


@@ 23,23 23,19 @@ pub struct TranslationInterpolationInfo {
}
#[derive(Component, Debug)]
pub struct RotationInterpolationInfo {
    pub last_dt: Duration,
    pub this_tick_start: bevy::platform::time::Instant,
    pub last_angular_velocity: f32,
    pub latest_rotation: f32,
    pub last_rotation: f32,
    pub rotations: VecDeque<(f64, f64)> // time and rotation
}

fn update_interpolation_info(
    mut interpolation_pos_query: Query<(Entity, &Position, &mut TranslationInterpolationInfo, &ConfirmHistory), Changed<Position>>,
    mut interpolation_rot_query: Query<(&Rotation, &AngularVelocity, &mut RotationInterpolationInfo), Changed<Rotation>>,
    mut interpolation_rot_query: Query<(&Rotation, &AngularVelocity, &mut RotationInterpolationInfo, &ConfirmHistory), Changed<Rotation>>,
    server_clock: Res<ServerClock>,
    server_time_offset: Res<ServerTimeOffset>,
) {
    for (entity, position, mut info, confirm_history) in interpolation_pos_query.iter_mut() {
        let now = confirm_history.last_tick().get() as f64 / TICK_RATE - **server_time_offset/* - server_clock.rtt/2.0 - server_clock.time_offset*/;
        //let now = confirm_history.last_tick().get() as f32 / TICK_RATE as f32;
        let now = confirm_history.last_tick().get() as f64 / TICK_RATE - **server_time_offset;
        info.positions.push_back((now, position.as_vec2()));

        let mut last_over_time = 0;
        for (i, (time, _)) in info.positions.iter().enumerate() {
            if *time < now - INTERP {


@@ 47,31 43,33 @@ fn update_interpolation_info(
            }
        }
        if last_over_time > 0 {
            //debug!("pop");
            info.positions.drain(..last_over_time);
        }
        //debug!("{:?}", info.positions)
        /*if let Some((time, _)) = info.positions.get(0) && now.duration_since(*time) > INTERP {
            debug!("pop");
            pos_info.positions.pop_front();
        }*/
    }
    for (rotation, angular_velocity, mut info) in interpolation_rot_query.iter_mut() {
        info.last_dt = info.this_tick_start.elapsed();
        info.this_tick_start = bevy::platform::time::Instant::now();
        let avg_angular_velocity = (angular_velocity.0.val_num_f32() + info.last_angular_velocity) / 2.0;
        let delta_rotation = avg_angular_velocity * info.last_dt.as_secs_f32();
    for (rotation, angular_velocity, mut info, confirm_history) in interpolation_rot_query.iter_mut() {
        let now = confirm_history.last_tick().get() as f64 / TICK_RATE - **server_time_offset;
        let delta_rotation = angular_velocity.0.to_radians() / TICK_RATE;
        let num_revolutions = (delta_rotation / (2.0*PI)).round();
        info.last_rotation = info.latest_rotation;
        let mut rotation_offset = 0.0;
        let rotation = rotation.as_radians().val_num_f32();
        if delta_rotation + info.last_rotation >= PI {
            rotation_offset = 2.0*PI;
        } else if delta_rotation + info.last_rotation < -PI {
            rotation_offset = -2.0*PI;
        let rotation = rotation.as_radians() as f64;
        if let Some((_, rotation)) = info.rotations.back() {
            if delta_rotation + rotation >= PI {
                rotation_offset = 2.0 * PI;
            } else if delta_rotation + rotation < -PI {
                rotation_offset = -2.0 * PI;
            }
        }
        info.rotations.push_back((now, rotation + rotation_offset + num_revolutions*2.0*PI));

        let mut last_over_time = 0;
        for (i, (time, _)) in info.rotations.iter().enumerate() {
            if *time < now - INTERP {
                last_over_time = i;
            }
        }
        if last_over_time > 0 {
            info.rotations.drain(..last_over_time);
        }
        info.latest_rotation = rotation + rotation_offset + num_revolutions*2.0*PI;
        info.last_angular_velocity = angular_velocity.0.val_num_f32();
    }
}
fn sync_non_interpolated_transforms(


@@ 104,11 102,24 @@ fn do_interpolation(
        let dt = second_time.0 - first_time.0;
        let progress = elapsed / dt; // should be between 0.0 and 1.0
        if progress < 0.0 || progress > 1.0 { continue }
        //debug!("{:?} {:?} {:?} {}", entity, first_time.1, second_time.1, progress);
        transform.translation = (first_time.1 + progress as f32 * (second_time.1 - first_time.1)).extend(0.0);

        let dt = bevy::platform::time::Instant::now() - rot_info.this_tick_start;
        let progress = dt.as_secs_f32() / rot_info.last_dt.as_secs_f32(); // should be between 0.0 and 1.0
        transform.rotation = Quat::from_rotation_z(rot_info.last_rotation + progress * (rot_info.latest_rotation - rot_info.last_rotation));
        let mut time_after_now = 0;
        for (i, (time, _)) in rot_info.rotations.iter().enumerate() {
            if *time > now - INTERP {
                time_after_now = i;
                break;
            }
        }
        // this should not happen, but is necessary to prevent a panic
        if time_after_now == 0 { continue }
        // we need to have 2 packets to reference, so continuing if we don't have that is a-ok
        let Some(first_time) = rot_info.rotations.get(time_after_now-1) else { continue };
        let Some(second_time) = rot_info.rotations.get(time_after_now) else { continue };
        let elapsed = now - INTERP - first_time.0;
        let dt = second_time.0 - first_time.0;
        let progress = elapsed / dt; // should be between 0.0 and 1.0
        if progress < 0.0 || progress > 1.0 { continue }
        transform.rotation = Quat::from_rotation_z((first_time.1 + progress * (second_time.1 - first_time.1)) as f32);
    }
}
\ No newline at end of file

M crates/unified/src/client/parts.rs => crates/unified/src/client/parts.rs +1 -5
@@ 65,11 65,7 @@ fn handle_incoming_parts(
                positions: VecDeque::new(),
            })
            .insert(RotationInterpolationInfo {
                last_dt: Duration::from_millis(50), // assume it was 20tps because we don't know
                this_tick_start: bevy::platform::time::Instant::now(),
                last_angular_velocity: 0.0,
                latest_rotation: transform.rotation.to_euler(EulerRot::XYZ).2,
                last_rotation: transform.rotation.to_euler(EulerRot::XYZ).2,
                rotations: VecDeque::new(),
            })
            .observe(on_part_click)
            .observe(open_crafting_ui);

M crates/unified/src/client/planet/incoming_planets.rs => crates/unified/src/client/planet/incoming_planets.rs +1 -5
@@ 32,11 32,7 @@ fn handle_incoming_planets(
                positions: VecDeque::new(),
            })
            .insert(RotationInterpolationInfo {
                last_dt: Duration::from_millis(50), // assume it was 20tps because we don't know
                this_tick_start: bevy::platform::time::Instant::now(),
                last_angular_velocity: 0.0,
                latest_rotation: transform.rotation.to_euler(EulerRot::XYZ).2,
                last_rotation: transform.rotation.to_euler(EulerRot::XYZ).2,
                rotations: VecDeque::new(),
            });
        trace!(?new_planet, "prepared new planet");
    }