~starkingdoms/starkingdoms

0494c19c47fcf26382f055c658e7c2c9d737b55a — ghostly_zsh 5 days ago a1e817f
netcode fix: dumb stupid interpolation works when there's no inconsistent delay or loss or reordering of packets
M crates/unified/src/client/interpolation.rs => crates/unified/src/client/interpolation.rs +51 -13
@@ 1,3 1,4 @@
use std::f32::consts::PI;
use std::time::{Duration};
use avian2d::parry::transformation::utils::transform;
use crate::prelude::*;


@@ 5,34 6,71 @@ use crate::prelude::*;
pub fn interpolation_plugin(app: &mut App) {
    app
        .add_systems(Update, update_interpolation_info)
        .add_systems(Update, sync_non_interpolated_transforms)
        .add_systems(Update, do_interpolation);
}

#[derive(Component, Debug)]
pub struct TransformInterpolationInfo {
pub struct TranslationInterpolationInfo {
    pub last_dt: Duration,
    pub this_tick_start: bevy::platform::time::Instant,
    pub latest_transform: Transform,
    pub last_transform: Transform,
    pub latest_position: Vec2,
    pub last_position: Vec2,
}
#[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,
}

fn update_interpolation_info(
    mut interpolation_query: Query<(&Transform, &mut TransformInterpolationInfo), Changed<Transform>>,
    time: Res<Time>,
    mut interpolation_pos_query: Query<(Entity, &Position, &mut TranslationInterpolationInfo), Changed<Position>>,
    mut interpolation_rot_query: Query<(&Rotation, &AngularVelocity, &mut RotationInterpolationInfo), Changed<Rotation>>,
) {
    for (transform, mut info) in interpolation_query.iter_mut() {
    for (entity, position, mut info) in interpolation_pos_query.iter_mut() {
        info.last_dt = info.this_tick_start.elapsed();
        info.this_tick_start = bevy::platform::time::Instant::now();
        info.last_transform = info.latest_transform;
        info.latest_transform = transform.clone();
        info.last_position = info.latest_position;
        info.latest_position = position.as_vec2();
    }
    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();
        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;
        }
        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(
    mut query: Query<(&mut Transform, &Position), (Changed<Position>, Without<TranslationInterpolationInfo>)>,
) {
    for (mut transform, position) in &mut query {
        transform.translation = position.as_vec2().extend(0.0);
    }
}
fn do_interpolation(
    mut interpolation_query: Query<(&mut Transform, &TransformInterpolationInfo)>,
    mut interpolation_query: Query<(Entity, &mut Transform, &TranslationInterpolationInfo, &RotationInterpolationInfo)>,
) {
    for (mut transform, info) in &mut interpolation_query {
        let dt = bevy::platform::time::Instant::now() - info.this_tick_start;
        let progress = dt.as_secs_f32() / info.last_dt.as_secs_f32(); // should be between 0.0 and 1.0
        transform.bypass_change_detection().translation = info.last_transform.translation + progress * (info.latest_transform.translation - info.last_transform.translation);
    for (entity, mut transform, pos_info, rot_info) in &mut interpolation_query {
        let dt = bevy::platform::time::Instant::now() - pos_info.this_tick_start;
        let progress = dt.as_secs_f32() / pos_info.last_dt.as_secs_f32(); // should be between 0.0 and 1.0
        transform.translation = (pos_info.last_position + progress * (pos_info.latest_position - pos_info.last_position)).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));
    }
}
\ No newline at end of file

M crates/unified/src/client/mod.rs => crates/unified/src/client/mod.rs +1 -1
@@ 59,7 59,7 @@ pub mod starguide;
pub mod crafting;
pub mod components;
pub mod plugins;
mod interpolation;
pub mod interpolation;

pub struct ClientPlugin {
    pub server: Option<String>

M crates/unified/src/client/parts.rs => crates/unified/src/client/parts.rs +11 -4
@@ 11,7 11,7 @@ use crate::client::components::Me;
use crate::client::ship::attachment::AttachmentDebugRes;
use crate::prelude::*;
use bevy_replicon::client::confirm_history::ConfirmHistory;
use crate::client::interpolation::TransformInterpolationInfo;
use crate::client::interpolation::{RotationInterpolationInfo, TranslationInterpolationInfo};

pub fn parts_plugin(app: &mut App) {
    app.insert_resource(DragResource { dragged: None });


@@ 60,11 60,18 @@ fn handle_incoming_parts(
            .insert(MAIN_LAYER)
            .insert(sprite)
            .insert(Pickable::default())
            .insert(TransformInterpolationInfo {
            .insert(TranslationInterpolationInfo {
                last_dt: Duration::from_millis(50), // assume it was 20tps because we don't know
                this_tick_start: bevy::platform::time::Instant::now(),
                latest_transform: transform.clone(),
                last_transform: transform.clone(),
                latest_position: transform.translation.truncate(),
                last_position: transform.translation.truncate(),
            })
            .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,
            })
            .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 +16 -1
@@ 1,7 1,9 @@
use std::time::Duration;
use crate::shared::config::planet::{Planet, SpecialSpriteProperties};
use crate::prelude::*;
use crate::shared::ecs::{MAIN_STAR_LAYERS};
use bevy_replicon::client::confirm_history::ConfirmHistory;
use crate::client::interpolation::{RotationInterpolationInfo, TranslationInterpolationInfo};

pub fn incoming_planets_plugin(app: &mut App) {
    app.add_systems(Update, (handle_incoming_planets, handle_updated_planets));


@@ 24,7 26,20 @@ fn handle_incoming_planets(
    for (new_entity, new_planet, transform, confirm_history) in new_planets.iter() {
        commands.entity(new_entity)
            .insert(MAIN_STAR_LAYERS.clone())
            .insert(build_planet_sprite(new_planet, &asset_server));
            .insert(build_planet_sprite(new_planet, &asset_server))
            .insert(TranslationInterpolationInfo {
                last_dt: Duration::from_millis(50), // assume it was 20tps because we don't know
                this_tick_start: bevy::platform::time::Instant::now(),
                latest_position: transform.translation.truncate(),
                last_position: transform.translation.truncate(),
            })
            .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,
            });
        trace!(?new_planet, "prepared new planet");
    }
}

M crates/unified/src/shared/net.rs => crates/unified/src/shared/net.rs +2 -2
@@ 17,8 17,8 @@ pub fn register_replication(app: &mut App) {
    app
        .add_mapped_server_message::<Hi>(Channel::Ordered)

        .replicate::<Transform>()
        .replicate::<GlobalTransform>()
        .replicate_once::<Transform>()
        .replicate_once::<GlobalTransform>()

        .replicate::<Position>()
        .replicate::<Rotation>()