Bevy 0.17 Migration Final PR (#76)

* Get bevy 0.17 compiling and running (#72)

* get bevy 0.17 compiling and running

* try to fix CI breaking from const assertion for client/server features

* fix `bin` -> `lib` for `shared` in CI

* typo

* fix some collider issues (#73)

* Physics/controller improvements (#74)

* trying to fix physics prediction

* fixed prediction desync

* substantial controller improvements

* Finish off main bevy 0.17 migration (#75)

* fix lookdir issues
- airplane moving backwards
- player model facing backwards
- camera was technically backwards the whole time, and player models were facing the right way; camera is now facing forwards
- firing without a target now respects lookdir

* fix aim targeting

* migrate to bevy_trenchbroom 0.10 crates release

* fixed colliders not being adjusted out of worldspace

* predict platforms to stop constant rollbacks while riding them

* fix key/head drop visuals not working

* Fix key/head drop random initial force

* fixed static head drops duplicating

* fix platform velocity inheritance

* fix thrown projectiles not autorotating

* fix inconsistent explosion animations

* update avian3d to 0.4.1

* fix controller snapping to fixed angle upon switching heads

* clean up commented code

* fix broken physics positions

* Clean comments, fix warnings (#77)

* clean comments, fix warnings

* fix missing import

* steamworks 162 libs

* fix mouselook

---------

Co-authored-by: extrawurst <mail@rusticorn.com>
This commit is contained in:
PROMETHIA-27
2025-11-15 09:16:38 -05:00
committed by GitHub
parent ad1b7446e1
commit b83e506a4d
75 changed files with 2514 additions and 1831 deletions

View File

@@ -1,17 +1,29 @@
use bevy::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Component, Reflect)]
#[derive(Component, Reflect, PartialEq, Serialize, Deserialize)]
#[reflect(Component)]
pub struct AutoRotation(pub Quat);
pub fn plugin(app: &mut App) {
app.register_type::<AutoRotation>();
app.add_systems(FixedUpdate, update_auto_rotation);
#[cfg(feature = "client")]
app.add_systems(Update, update_auto_rotation);
}
fn update_auto_rotation(mut query: Query<(&AutoRotation, &mut Transform)>) {
for (auto_rotation, mut transform) in query.iter_mut() {
transform.rotate_local(auto_rotation.0);
#[cfg(feature = "client")]
fn update_auto_rotation(
query: Query<(&AutoRotation, &Children)>,
mut meshes: Query<&mut Transform>,
) {
for (auto_rotation, children) in query.iter() {
for &child in children {
let Ok(mut transform) = meshes.get_mut(child) else {
continue;
};
transform.rotate_local(auto_rotation.0);
}
}
}

View File

@@ -1,8 +1,9 @@
use crate::camera::MainCamera;
use bevy::prelude::*;
use bevy_sprite3d::Sprite3dPlugin;
use serde::{Deserialize, Serialize};
#[derive(Component, Reflect, Default, PartialEq, Eq)]
#[derive(Component, Reflect, Default, PartialEq, Eq, Serialize, Deserialize)]
#[reflect(Component)]
pub enum Billboard {
#[default]

View File

@@ -1,16 +1,17 @@
use bevy::ecs::{
event::Event,
entity::Entity,
event::{EntityEvent, Event},
system::{Commands, EntityCommands},
world::{EntityWorldMut, World},
};
use lightyear::prelude::Disconnected;
pub trait CommandExt {
fn trigger_server(&mut self, event: impl Event) -> &mut Self;
fn trigger_server<'a, E: Event<Trigger<'a>: Default>>(&mut self, event: E) -> &mut Self;
}
impl<'w, 's> CommandExt for Commands<'w, 's> {
fn trigger_server(&mut self, event: impl Event) -> &mut Self {
fn trigger_server<'a, E: Event<Trigger<'a>: Default>>(&mut self, event: E) -> &mut Self {
self.queue(|world: &mut World| {
let mut query_state = world.query::<&Disconnected>();
if cfg!(feature = "server") || !query_state.query(world).is_empty() {
@@ -22,11 +23,17 @@ impl<'w, 's> CommandExt for Commands<'w, 's> {
}
pub trait EntityCommandExt {
fn trigger_server(&mut self, event: impl Event) -> &mut Self;
fn trigger_server<'a, E: EntityEvent<Trigger<'a>: Default>>(
&mut self,
event: impl FnOnce(Entity) -> E + Send + Sync + 'static,
) -> &mut Self;
}
impl<'w> EntityCommandExt for EntityCommands<'w> {
fn trigger_server(&mut self, event: impl Event) -> &mut Self {
fn trigger_server<'a, E: EntityEvent<Trigger<'a>: Default>>(
&mut self,
event: impl FnOnce(Entity) -> E + Send + Sync + 'static,
) -> &mut Self {
self.queue(|mut entity: EntityWorldMut| {
let mut query_state = entity.world_scope(|world| world.query::<&Disconnected>());
if cfg!(feature = "server") || !query_state.query(entity.world()).is_empty() {

View File

@@ -13,11 +13,7 @@ pub fn plugin(app: &mut App) {
global_observer!(app, on_explosion);
}
fn on_explosion(
explosion: Trigger<Explosion>,
mut commands: Commands,
spatial_query: SpatialQuery,
) {
fn on_explosion(explosion: On<Explosion>, mut commands: Commands, spatial_query: SpatialQuery) {
let explosion = explosion.event();
let intersections = {
spatial_query.shape_intersections(
@@ -32,7 +28,8 @@ fn on_explosion(
for entity in intersections.iter() {
if let Ok(mut e) = commands.get_entity(*entity) {
e.trigger(Hit {
e.trigger(|entity| Hit {
entity,
damage: explosion.damage,
});
}

View File

@@ -3,6 +3,7 @@ pub mod billboards;
pub mod commands;
pub mod explosions;
pub mod observers;
pub mod one_shot_force;
pub mod run_conditions;
pub mod sprite_3d_animation;
pub mod squish_animation;
@@ -14,4 +15,5 @@ pub(crate) use observers::global_observer;
pub fn plugin(app: &mut App) {
app.add_plugins(observers::plugin);
app.add_plugins(one_shot_force::plugin);
}

View File

@@ -0,0 +1,19 @@
use avian3d::prelude::{Forces, RigidBodyForces};
use bevy::prelude::*;
pub fn plugin(app: &mut App) {
app.add_systems(FixedUpdate, apply_one_shot_forces);
}
#[derive(Component)]
pub struct OneShotForce(pub Vec3);
pub fn apply_one_shot_forces(
mut commands: Commands,
mut query: Query<(Entity, &OneShotForce, Forces)>,
) {
for (entity, force, mut forces) in query.iter_mut() {
forces.apply_force(force.0);
commands.entity(entity).remove::<OneShotForce>();
}
}

View File

@@ -18,16 +18,19 @@ pub fn plugin(app: &mut App) {
fn animate_sprite(
mut commands: Commands,
time: Res<Time>,
mut query: Query<(Entity, &mut AnimationTimer, &mut Sprite3d)>,
mut query: Query<(Entity, &mut AnimationTimer, &Sprite3d, &mut Sprite)>,
) {
for (e, mut timer, mut sprite_3d) in query.iter_mut() {
timer.tick(time.delta());
if timer.just_finished() {
let length = sprite_3d.texture_atlas_keys.as_ref().unwrap().len();
let atlas = sprite_3d.texture_atlas.as_mut().unwrap();
for (e, mut timer, sprite_3d, mut sprite) in query.iter_mut() {
let length = sprite_3d.texture_atlas_keys.len();
let atlas = sprite.texture_atlas.as_mut().unwrap();
if atlas.index < length - 1 {
atlas.index = atlas.index.saturating_add(1) % length;
if length > 0 {
timer.tick(time.delta());
}
if timer.just_finished() {
if atlas.index + 1 < length {
atlas.index = (atlas.index + 1) % length;
} else {
commands.entity(e).despawn();
}

View File

@@ -1,7 +1,8 @@
use bevy::prelude::*;
use ops::sin;
use serde::{Deserialize, Serialize};
#[derive(Component, Reflect)]
#[derive(Component, Reflect, PartialEq, Serialize, Deserialize)]
#[reflect(Component)]
pub struct SquishAnimation(pub f32);

View File

@@ -1,54 +1,60 @@
use crate::utils::global_observer;
use bevy::{ecs::system::SystemParam, prelude::*};
use lightyear::prelude::{AppTriggerExt, Channel, NetworkDirection, RemoteTrigger, TriggerSender};
use lightyear::prelude::{AppTriggerExt, Channel, EventSender, NetworkDirection, RemoteEvent};
use serde::{Deserialize, Serialize};
#[derive(SystemParam)]
pub struct ServerMultiTriggerSender<'w, 's, M: Event + Clone> {
senders: Query<'w, 's, &'static mut TriggerSender<M>>,
senders: Query<'w, 's, &'static mut EventSender<M>>,
}
impl<'w, 's, M: Event + Clone> ServerMultiTriggerSender<'w, 's, M> {
pub fn server_trigger_targets<C: Channel>(&mut self, trigger: M, target: &[Entity]) {
pub fn server_trigger_targets<C: Channel>(&mut self, event: M) {
if cfg!(not(feature = "server")) {
return;
}
for mut sender in self.senders.iter_mut() {
sender.trigger_targets::<C>(trigger.clone(), target.iter().copied());
sender.trigger::<C>(event.clone());
}
}
}
pub trait TriggerAppExt {
fn replicate_trigger<M: Event + Clone + Serialize + for<'de> Deserialize<'de>, C: Channel>(
fn replicate_event<
'a,
M: Event<Trigger<'a>: Default> + Clone + Serialize + for<'de> Deserialize<'de>,
C: Channel,
>(
&mut self,
);
}
impl TriggerAppExt for App {
fn replicate_trigger<M: Event + Clone + Serialize + for<'de> Deserialize<'de>, C: Channel>(
fn replicate_event<
'a,
M: Event<Trigger<'a>: Default> + Clone + Serialize + for<'de> Deserialize<'de>,
C: Channel,
>(
&mut self,
) {
self.add_trigger::<M>()
self.register_event::<M>()
.add_direction(NetworkDirection::ServerToClient);
global_observer!(self, replicate_trigger_to_clients::<M, C>);
global_observer!(self, remote_to_local_trigger::<M>);
global_observer!(self, remote_to_local_event::<M>);
}
}
fn replicate_trigger_to_clients<M: Event + Clone, C: Channel>(
trigger: Trigger<M>,
on: On<M>,
mut sender: ServerMultiTriggerSender<M>,
) {
let targets: &[Entity] = if trigger.target() == Entity::PLACEHOLDER {
&[]
} else {
&[trigger.target()]
};
sender.server_trigger_targets::<C>(trigger.event().clone(), targets);
sender.server_trigger_targets::<C>(on.event().clone());
}
fn remote_to_local_trigger<M: Event + Clone>(trigger: Trigger<RemoteTrigger<M>>, mut c: Commands) {
c.trigger_targets(trigger.event().trigger.clone(), trigger.target());
fn remote_to_local_event<'a, M: Event<Trigger<'a>: Default> + Clone>(
trigger: On<RemoteEvent<M>>,
mut c: Commands,
) {
c.trigger(trigger.event().trigger.clone());
}