From c650924d68d1b0ae092935334e26f88f3e059294 Mon Sep 17 00:00:00 2001 From: PROMETHIA-27 <42193387+PROMETHIA-27@users.noreply.github.com> Date: Tue, 29 Jul 2025 07:32:54 -0400 Subject: [PATCH] Animation sync (#60) --- Cargo.lock | 25 ++++--- Cargo.toml | 1 + crates/client/src/client.rs | 2 +- crates/client/src/main.rs | 2 +- crates/server/src/server.rs | 2 +- crates/shared/Cargo.toml | 1 + crates/shared/src/animation.rs | 28 ++++--- crates/shared/src/character.rs | 15 ++-- .../shared/src/control/controller_common.rs | 8 +- .../shared/src/control/controller_flying.rs | 6 +- .../shared/src/control/controller_running.rs | 16 ++-- crates/shared/src/heads/mod.rs | 5 +- crates/shared/src/hitpoints.rs | 20 ++--- crates/shared/src/npc.rs | 18 ++++- crates/shared/src/player.rs | 12 ++- crates/shared/src/protocol.rs | 73 ++++++++++++------- 16 files changed, 136 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa6724b..e5d6f0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4283,7 +4283,7 @@ dependencies = [ "lightyear_netcode", "lightyear_prediction 0.22.4", "lightyear_replication 0.22.4", - "lightyear_serde 0.22.4", + "lightyear_serde 0.22.5", "lightyear_steam", "lightyear_sync 0.22.4", "lightyear_transport 0.22.4", @@ -4404,7 +4404,7 @@ dependencies = [ "bytes", "lightyear_core 0.22.4", "lightyear_link 0.22.4", - "lightyear_serde 0.22.4", + "lightyear_serde 0.22.5", "serde", "smallvec", "thiserror 2.0.12", @@ -4442,7 +4442,7 @@ dependencies = [ "bevy_reflect", "bevy_time", "chrono", - "lightyear_serde 0.22.4", + "lightyear_serde 0.22.5", "lightyear_utils 0.22.4", "serde", "tracing", @@ -4601,7 +4601,7 @@ dependencies = [ "lightyear_core 0.22.4", "lightyear_messages 0.22.4", "lightyear_replication 0.22.4", - "lightyear_serde 0.22.4", + "lightyear_serde 0.22.5", "lightyear_sync 0.22.4", "lightyear_utils 0.22.4", "serde", @@ -4671,7 +4671,7 @@ dependencies = [ "lightyear_connection 0.22.4", "lightyear_core 0.22.4", "lightyear_link 0.22.4", - "lightyear_serde 0.22.4", + "lightyear_serde 0.22.5", "lightyear_transport 0.22.4", "lightyear_utils 0.22.4", "serde", @@ -4695,7 +4695,7 @@ dependencies = [ "lightyear_connection 0.22.4", "lightyear_core 0.22.4", "lightyear_link 0.22.4", - "lightyear_serde 0.22.4", + "lightyear_serde 0.22.5", "lightyear_transport 0.22.4", "lightyear_utils 0.22.4", "no_std_io2", @@ -4754,7 +4754,7 @@ dependencies = [ "lightyear_link 0.22.4", "lightyear_messages 0.22.4", "lightyear_replication 0.22.4", - "lightyear_serde 0.22.4", + "lightyear_serde 0.22.5", "lightyear_sync 0.22.4", "lightyear_utils 0.22.4", "parking_lot", @@ -4812,7 +4812,7 @@ dependencies = [ "lightyear_core 0.22.4", "lightyear_link 0.22.4", "lightyear_messages 0.22.4", - "lightyear_serde 0.22.4", + "lightyear_serde 0.22.5", "lightyear_transport 0.22.4", "lightyear_utils 0.22.4", "serde", @@ -4842,9 +4842,9 @@ dependencies = [ [[package]] name = "lightyear_serde" -version = "0.22.4" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8abacb721279778366728344102240d279e3f72693c2c8ed8a9feb527bf8b4da" +checksum = "0a45bd23d5f45b25ff53361f148dac09872c38ee184baf2ef9615250b4a9e09a" dependencies = [ "bevy_derive", "bevy_ecs", @@ -4918,7 +4918,7 @@ dependencies = [ "lightyear_core 0.22.4", "lightyear_link 0.22.4", "lightyear_messages 0.22.4", - "lightyear_serde 0.22.4", + "lightyear_serde 0.22.5", "lightyear_transport 0.22.4", "lightyear_utils 0.22.4", "serde", @@ -4973,7 +4973,7 @@ dependencies = [ "lightyear_connection 0.22.4", "lightyear_core 0.22.4", "lightyear_link 0.22.4", - "lightyear_serde 0.22.4", + "lightyear_serde 0.22.5", "lightyear_utils 0.22.4", "nonzero_ext", "ringbuffer 0.16.0", @@ -7145,6 +7145,7 @@ dependencies = [ "bevy_trenchbroom", "happy_feet", "lightyear", + "lightyear_serde 0.22.5", "nil 0.14.0", "rand 0.8.5", "ron", diff --git a/Cargo.toml b/Cargo.toml index bc5890f..b7aba75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ lightyear = { version = "0.22.4", default-features = false, features = [ "udp", ] } lightyear_avian3d = { git = "https://github.com/cBournhonesque/lightyear.git", rev = "03cbf419a2c0595261b64420bc0332fc3fe1cc3f" } +lightyear_serde = "0.22.4" nil = "0.14.0" rand = "=0.8.5" ron = "0.8" diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 8e20173..b55a43f 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -65,6 +65,6 @@ fn spawn_disconnected_player( heads_db: Res, ) { if disconnected.state == ClientState::Disconnected { - shared::player::spawn(commands, query, asset_server, heads_db) + shared::player::spawn(commands, Entity::PLACEHOLDER, query, asset_server, heads_db) } } diff --git a/crates/client/src/main.rs b/crates/client/src/main.rs index ef7afbc..e9395da 100644 --- a/crates/client/src/main.rs +++ b/crates/client/src/main.rs @@ -47,7 +47,7 @@ fn main() { ..default() }) .set(bevy::log::LogPlugin { - filter: "info".into(), + filter: "info,lightyear_replication=warn".into(), level: bevy::log::Level::INFO, // provide custom log layer to receive logging events custom_layer: bevy_debug_log::log_capture_layer, diff --git a/crates/server/src/server.rs b/crates/server/src/server.rs index 2c0fbe1..2ca8e72 100644 --- a/crates/server/src/server.rs +++ b/crates/server/src/server.rs @@ -29,7 +29,7 @@ fn handle_new_client( .entity(trigger.target()) .insert(ReplicationSender::default()); - shared::player::spawn(commands, query, asset_server, heads_db); + shared::player::spawn(commands, trigger.target(), query, asset_server, heads_db); Ok(()) } diff --git a/crates/shared/Cargo.toml b/crates/shared/Cargo.toml index 7336626..bc09828 100644 --- a/crates/shared/Cargo.toml +++ b/crates/shared/Cargo.toml @@ -20,6 +20,7 @@ bevy_sprite3d = { workspace = true } bevy_trenchbroom = { workspace = true } happy_feet = { workspace = true } lightyear = { workspace = true } +lightyear_serde = { workspace = true } nil = { workspace = true } rand = { workspace = true } ron = { workspace = true } diff --git a/crates/shared/src/animation.rs b/crates/shared/src/animation.rs index b8a3f97..bc02c30 100644 --- a/crates/shared/src/animation.rs +++ b/crates/shared/src/animation.rs @@ -2,6 +2,7 @@ use crate::{ GameState, character::CharacterAnimations, head::ActiveHead, heads_database::HeadsDatabase, }; use bevy::{animation::RepeatAnimation, ecs::query::QueryData, prelude::*}; +use serde::{Deserialize, Serialize}; use std::time::Duration; pub fn plugin(app: &mut App) { @@ -13,17 +14,24 @@ pub fn plugin(app: &mut App) { ); } -#[derive(Component, Default, Reflect)] +#[derive(Component, Default, Reflect, PartialEq, Serialize, Deserialize)] #[reflect(Component)] +#[require(AnimationFlagCache)] pub struct AnimationFlags { pub any_direction: bool, pub jumping: bool, - pub just_jumped: bool, + pub jump_count: u8, pub shooting: bool, pub restart_shooting: bool, pub hit: bool, } +#[derive(Component, Default, Reflect)] +#[reflect(Component)] +pub struct AnimationFlagCache { + pub jump_count: u8, +} + #[derive(QueryData)] #[query_data(mutable)] pub struct AnimationController { @@ -69,16 +77,12 @@ impl AnimationControllerItem<'_> { const DEFAULT_TRANSITION_DURATION: Duration = Duration::ZERO; fn update_animation( - mut animated: Query<( - AnimationController, - &CharacterAnimations, - &mut AnimationFlags, - )>, - character: Query<&ActiveHead>, + mut animated: Query<(AnimationController, &CharacterAnimations)>, + mut character: Query<(&ActiveHead, &mut AnimationFlags, &mut AnimationFlagCache)>, headdb: Res, ) { - for (mut controller, anims, mut flags) in animated.iter_mut() { - let head = character.get(anims.of_character).unwrap(); + for (mut controller, anims) in animated.iter_mut() { + let (head, mut flags, mut cache) = character.get_mut(anims.of_character).unwrap(); let head = headdb.head_stats(head.0); let is_playing_shoot = anims.shoot.is_some() @@ -153,13 +157,13 @@ fn update_animation( ); } } else if flags.jumping { - if !controller.is_playing(anims.jump) || flags.just_jumped { + if !controller.is_playing(anims.jump) || flags.jump_count != cache.jump_count { controller.play( anims.jump, DEFAULT_TRANSITION_DURATION, RepeatAnimation::Never, ); - flags.just_jumped = false; + cache.jump_count = flags.jump_count; } } else if flags.any_direction { if !controller.player.is_playing_animation(anims.run) { diff --git a/crates/shared/src/character.rs b/crates/shared/src/character.rs index 85c4ecb..4183c23 100644 --- a/crates/shared/src/character.rs +++ b/crates/shared/src/character.rs @@ -2,7 +2,6 @@ use crate::{ GameState, animation::{AnimationController, AnimationFlags}, heads_database::HeadsDatabase, - hitpoints::Hitpoints, loading_assets::GameAssets, utils::trail::Trail, }; @@ -47,7 +46,6 @@ impl CharacterHierarchy<'_, '_> { #[derive(Component, Debug, Reflect)] #[reflect(Component)] #[relationship(relationship_target = HasCharacterAnimations)] -#[require(AnimationFlags)] pub struct CharacterAnimations { #[relationship] pub of_character: Entity, @@ -69,6 +67,7 @@ const ANIM_HIT: &str = "hit"; #[derive(Component)] #[relationship_target(relationship = CharacterAnimations)] +#[require(AnimationFlags)] pub struct HasCharacterAnimations(Entity); pub fn plugin(app: &mut App) { @@ -103,15 +102,15 @@ fn spawn( }); let asset = gltf_assets.get(handle).unwrap(); - let mut t = + let mut transform = Transform::from_translation(Vec3::new(0., -1.45, 0.)).with_scale(Vec3::splat(1.2)); - t.rotate_y(PI); + transform.rotate_y(PI); commands .entity(entity) .insert(( - t, + transform, SceneRoot(asset.scenes[0].clone()), AnimatedCharacterAsset(handle.clone()), )) @@ -161,12 +160,16 @@ fn find_marker_bones( } } +#[derive(Component, Default, Reflect, PartialEq, Serialize, Deserialize)] +#[reflect(Component)] +pub struct Character; + fn setup_once_loaded( mut commands: Commands, mut query: Query<(Entity, &mut AnimationPlayer), Added>, parent: Query<&ChildOf>, animated_character: Query<(&AnimatedCharacter, &AnimatedCharacterAsset)>, - characters: Query>, + characters: Query>, gltf_assets: Res>, mut graphs: ResMut>, ) { diff --git a/crates/shared/src/control/controller_common.rs b/crates/shared/src/control/controller_common.rs index ec36f00..ff4a0a2 100644 --- a/crates/shared/src/control/controller_common.rs +++ b/crates/shared/src/control/controller_common.rs @@ -3,7 +3,6 @@ use crate::{ GameState, abilities::TriggerStateRes, animation::AnimationFlags, - character::HasCharacterAnimations, control::{Controls, SelectedController, controls::ControllerSettings}, head::ActiveHead, heads_database::{HeadControls, HeadsDatabase}, @@ -17,6 +16,7 @@ use happy_feet::prelude::{ GroundFriction, Grounding, GroundingConfig, KinematicVelocity, MoveInput, SteppingBehaviour, SteppingConfig, }; +use lightyear::prelude::Replicated; use serde::{Deserialize, Serialize}; pub fn plugin(app: &mut App) { @@ -49,16 +49,14 @@ pub fn plugin(app: &mut App) { } fn set_animation_flags( - mut flags: Query<&mut AnimationFlags>, controls: Res, trigger: Res, - player: Single<(&Grounding, &HasCharacterAnimations), With>, + player: Single<(&Grounding, &mut AnimationFlags), (With, Without)>, ) { let mut direction = controls.keyboard_state.move_dir; let deadzone = 0.2; - let (grounding, has_anims) = *player; - let mut flags = flags.get_mut(*has_anims.collection()).unwrap(); + let (grounding, mut flags) = player.into_inner(); if let Some(gamepad) = controls.gamepad_state { direction += gamepad.move_dir; diff --git a/crates/shared/src/control/controller_flying.rs b/crates/shared/src/control/controller_flying.rs index 3faeccc..b73adb2 100644 --- a/crates/shared/src/control/controller_flying.rs +++ b/crates/shared/src/control/controller_flying.rs @@ -54,10 +54,12 @@ fn rotate_rig( fn apply_controls( mut character: Query<(&mut MoveInput, &MovementSpeedFactor)>, rig_transform_q: Option>>, -) { - let (mut char_input, factor) = character.single_mut().unwrap(); +) -> Result { + let (mut char_input, factor) = character.single_mut()?; if let Some(ref rig_transform) = rig_transform_q { char_input.set(-*rig_transform.forward() * factor.0); } + + Ok(()) } diff --git a/crates/shared/src/control/controller_running.rs b/crates/shared/src/control/controller_running.rs index dcc509b..cc60046 100644 --- a/crates/shared/src/control/controller_running.rs +++ b/crates/shared/src/control/controller_running.rs @@ -2,9 +2,9 @@ use super::{ControlState, ControllerSet}; use crate::{ GameState, animation::AnimationFlags, - character::HasCharacterAnimations, control::{controller_common::MovementSpeedFactor, controls::ControllerSettings}, player::PlayerBodyMesh, + utils::commands::IsServer, }; use bevy::prelude::*; use happy_feet::prelude::{Grounding, KinematicVelocity, MoveInput}; @@ -42,21 +42,19 @@ fn apply_controls( &mut MoveInput, &mut Grounding, &mut KinematicVelocity, + &mut AnimationFlags, &ControllerSettings, &MovementSpeedFactor, - &HasCharacterAnimations, )>, rig_transform_q: Option>>, - mut anim_flags: Query<&mut AnimationFlags>, + is_server: Option>, ) { - let Ok((mut move_input, mut grounding, mut velocity, settings, move_factor, has_anims)) = + let Ok((mut move_input, mut grounding, mut velocity, mut flags, settings, move_factor)) = character.single_mut() else { return; }; - let mut flags = anim_flags.get_mut(*has_anims.collection()).unwrap(); - let mut direction = -controls.move_dir.extend(0.0).xzy(); if let Some(ref rig_transform) = rig_transform_q { @@ -72,8 +70,10 @@ fn apply_controls( move_input.set(direction * move_factor.0); if controls.jump && grounding.is_grounded() { - flags.jumping = true; - flags.just_jumped = true; + if is_server.is_some() { + flags.jumping = true; + flags.jump_count += 1; + } happy_feet::movement::jump(settings.jump_force, &mut velocity, &mut grounding, Dir3::Y) } } diff --git a/crates/shared/src/heads/mod.rs b/crates/shared/src/heads/mod.rs index 417c236..f3cfcd2 100644 --- a/crates/shared/src/heads/mod.rs +++ b/crates/shared/src/heads/mod.rs @@ -4,7 +4,6 @@ use crate::{ GameState, animation::AnimationFlags, backpack::{BackbackSwapEvent, Backpack}, - character::HasCharacterAnimations, global_observer, heads_database::HeadsDatabase, hitpoints::Hitpoints, @@ -215,8 +214,7 @@ fn reload( mut commands: Commands, mut active: Query<&mut ActiveHeads>, time: Res