Lightyear setup (#55)

This commit is contained in:
extrawurst
2025-07-10 23:21:11 +02:00
committed by GitHub
parent 691b9eed33
commit 78b09b33d6
26 changed files with 2515 additions and 242 deletions

2280
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,3 @@
[workspace] [workspace]
resolver = "3" resolver = "3"
members = ["crates/*"] members = ["crates/*"]
@@ -12,6 +11,7 @@ avian3d = { version = "0.3", default-features = false, features = [
"bevy_scene", "bevy_scene",
"bevy_picking", # todo: Consider if this one is necessary "bevy_picking", # todo: Consider if this one is necessary
"parallel", "parallel",
"serialize",
] } ] }
bevy = { version = "0.16.0", features = ["track_location"] } bevy = { version = "0.16.0", features = ["track_location"] }
bevy-inspector-egui = { version = "0.31" } bevy-inspector-egui = { version = "0.31" }
@@ -23,13 +23,23 @@ bevy_common_assets = { version = "0.13.0", features = ["ron"] }
bevy_debug_log = "0.6.0" bevy_debug_log = "0.6.0"
bevy_sprite3d = "5.0.0" bevy_sprite3d = "5.0.0"
bevy_trenchbroom = { version = "0.8.1", features = ["avian"] } bevy_trenchbroom = { version = "0.8.1", features = ["avian"] }
happy_feet = { git = "https://github.com/rustunit/happy_feet.git", rev = "ecfecc6243862bc2bc64dcadfd0efd21c766ab5b" } happy_feet = { git = "https://github.com/rustunit/happy_feet.git", rev = "e4e57c50ba5b5e0be5166e9e4eb629dc1d616a0d" }
lightyear = { git = "https://github.com/cBournhonesque/lightyear.git", rev = "03cbf419a2c0595261b64420bc0332fc3fe1cc3f", default-features = false, features = [
"interpolation",
"netcode",
"replication",
"std",
"steam",
"udp",
] }
lightyear_avian3d = { git = "https://github.com/cBournhonesque/lightyear.git", rev = "03cbf419a2c0595261b64420bc0332fc3fe1cc3f" }
nil = "0.14.0" nil = "0.14.0"
rand = "=0.8.5" rand = "=0.8.5"
ron = "0.8" ron = "0.8"
serde = { version = "1.0.219", features = ["derive"] } serde = { version = "1.0.219", features = ["derive"] }
shared = { path = "crates/shared" } shared = { path = "crates/shared" }
steamworks = "0.11" steamworks = "0.11"
[profile.dev.package."*"] [profile.dev.package."*"]
opt-level = 3 opt-level = 3

View File

@@ -5,6 +5,7 @@ edition = "2024"
build = "build.rs" build = "build.rs"
[features] [features]
default = ["lightyear/client"]
dbg = ["avian3d/debug-plugin", "dep:bevy-inspector-egui", "shared/dbg"] dbg = ["avian3d/debug-plugin", "dep:bevy-inspector-egui", "shared/dbg"]
[dependencies] [dependencies]
@@ -20,6 +21,7 @@ bevy_debug_log = { workspace = true }
bevy_sprite3d = { workspace = true } bevy_sprite3d = { workspace = true }
bevy_trenchbroom = { workspace = true } bevy_trenchbroom = { workspace = true }
happy_feet = { workspace = true } happy_feet = { workspace = true }
lightyear = { workspace = true }
nil = { workspace = true } nil = { workspace = true }
rand = { workspace = true } rand = { workspace = true }
ron = { workspace = true } ron = { workspace = true }

View File

@@ -0,0 +1,35 @@
use bevy::prelude::*;
use lightyear::{
netcode::Key,
prelude::{client::NetcodeConfig, *},
};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
pub fn plugin(app: &mut App) {
app.add_systems(Startup, temp_connect_on_startup);
}
fn temp_connect_on_startup(mut commands: Commands) -> Result {
let client_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 25564);
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 25565);
let auth = Authentication::Manual {
server_addr,
client_id: 0,
private_key: Key::default(),
protocol_id: 0,
};
commands
.spawn((
Name::from("Client"),
Client::default(),
LocalAddr(client_addr),
PeerAddr(server_addr),
Link::new(None),
ReplicationReceiver::default(),
client::NetcodeClient::new(auth, NetcodeConfig::default())?,
UdpIo::default(),
))
.trigger(Connect);
Ok(())
}

View File

@@ -1,3 +1,4 @@
mod client;
mod debug; mod debug;
mod steam; mod steam;
mod ui; mod ui;
@@ -16,8 +17,10 @@ use bevy_trenchbroom::prelude::*;
use bevy_ui_gradients::UiGradientsPlugin; use bevy_ui_gradients::UiGradientsPlugin;
use camera::MainCamera; use camera::MainCamera;
use heads_database::HeadDatabaseAsset; use heads_database::HeadDatabaseAsset;
use lightyear::prelude::client::ClientPlugins;
use loading_assets::AudioAssets; use loading_assets::AudioAssets;
use shared::*; use shared::*;
use std::time::Duration;
use utils::{billboards, sprite_3d_animation, squish_animation, trail}; use utils::{billboards, sprite_3d_animation, squish_animation, trail};
fn main() { fn main() {
@@ -58,6 +61,9 @@ fn main() {
.auto_open_threshold(bevy::log::tracing::level_filters::LevelFilter::OFF), .auto_open_threshold(bevy::log::tracing::level_filters::LevelFilter::OFF),
); );
app.add_plugins(PhysicsPlugins::default()); app.add_plugins(PhysicsPlugins::default());
app.add_plugins(ClientPlugins {
tick_duration: Duration::from_secs_f64(1.0 / 60.0),
});
app.add_plugins(Sprite3dPlugin); app.add_plugins(Sprite3dPlugin);
app.add_plugins(TrenchBroomPlugins( app.add_plugins(TrenchBroomPlugins(
TrenchBroomConfig::new("hedz").icon(None), TrenchBroomConfig::new("hedz").icon(None),
@@ -87,9 +93,11 @@ fn main() {
app.add_plugins(player::plugin); app.add_plugins(player::plugin);
app.add_plugins(gates::plugin); app.add_plugins(gates::plugin);
app.add_plugins(platforms::plugin); app.add_plugins(platforms::plugin);
app.add_plugins(protocol::plugin);
app.add_plugins(movables::plugin); app.add_plugins(movables::plugin);
app.add_plugins(billboards::plugin); app.add_plugins(billboards::plugin);
app.add_plugins(aim::plugin); app.add_plugins(aim::plugin);
app.add_plugins(client::plugin);
app.add_plugins(npc::plugin); app.add_plugins(npc::plugin);
app.add_plugins(keys::plugin); app.add_plugins(keys::plugin);
app.add_plugins(squish_animation::plugin); app.add_plugins(squish_animation::plugin);
@@ -106,7 +114,7 @@ fn main() {
app.add_plugins(hitpoints::plugin); app.add_plugins(hitpoints::plugin);
app.add_plugins(cash_heal::plugin); app.add_plugins(cash_heal::plugin);
app.add_plugins(debug::plugin); app.add_plugins(debug::plugin);
app.add_plugins(utils::observers::plugin); app.add_plugins(utils::plugin);
app.add_plugins(water::plugin); app.add_plugins(water::plugin);
app.add_plugins(head_drop::plugin); app.add_plugins(head_drop::plugin);
app.add_plugins(trail::plugin); app.add_plugins(trail::plugin);

View File

@@ -1,8 +1,7 @@
use crate::GameState;
use bevy::{color::palettes::css::BLACK, prelude::*}; use bevy::{color::palettes::css::BLACK, prelude::*};
use shared::{HEDZ_GREEN, HEDZ_PURPLE, control::CharacterInputEnabled, loading_assets::UIAssets}; use shared::{HEDZ_GREEN, HEDZ_PURPLE, control::CharacterInputEnabled, loading_assets::UIAssets};
use crate::GameState;
#[derive(States, Default, Clone, Eq, PartialEq, Debug, Hash)] #[derive(States, Default, Clone, Eq, PartialEq, Debug, Hash)]
#[states(scoped_entities)] #[states(scoped_entities)]
enum PauseMenuState { enum PauseMenuState {

View File

@@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[features] [features]
default = ["lightyear/server"]
dbg = ["avian3d/debug-plugin", "dep:bevy-inspector-egui", "shared/dbg"] dbg = ["avian3d/debug-plugin", "dep:bevy-inspector-egui", "shared/dbg"]
[dependencies] [dependencies]
@@ -19,6 +20,8 @@ bevy_debug_log = { workspace = true }
bevy_sprite3d = { workspace = true } bevy_sprite3d = { workspace = true }
bevy_trenchbroom = { workspace = true } bevy_trenchbroom = { workspace = true }
happy_feet = { workspace = true } happy_feet = { workspace = true }
lightyear = { workspace = true }
lightyear_avian3d = { workspace = true }
nil = { workspace = true } nil = { workspace = true }
rand = { workspace = true } rand = { workspace = true }
ron = { workspace = true } ron = { workspace = true }

View File

@@ -6,9 +6,13 @@ use bevy_sprite3d::Sprite3dPlugin;
use bevy_trenchbroom::prelude::*; use bevy_trenchbroom::prelude::*;
use bevy_ui_gradients::UiGradientsPlugin; use bevy_ui_gradients::UiGradientsPlugin;
use heads_database::HeadDatabaseAsset; use heads_database::HeadDatabaseAsset;
use lightyear::prelude::server::ServerPlugins;
use shared::*; use shared::*;
use std::time::Duration;
use utils::{billboards, sprite_3d_animation, squish_animation, trail}; use utils::{billboards, sprite_3d_animation, squish_animation, trail};
mod server;
fn main() { fn main() {
let mut app = App::new(); let mut app = App::new();
@@ -44,6 +48,9 @@ fn main() {
bevy_debug_log::LogViewerPlugin::default() bevy_debug_log::LogViewerPlugin::default()
.auto_open_threshold(bevy::log::tracing::level_filters::LevelFilter::OFF), .auto_open_threshold(bevy::log::tracing::level_filters::LevelFilter::OFF),
); );
app.add_plugins(ServerPlugins {
tick_duration: Duration::from_secs_f32(1.0 / 60.0),
});
app.add_plugins(PhysicsPlugins::default()); app.add_plugins(PhysicsPlugins::default());
app.add_plugins(Sprite3dPlugin); app.add_plugins(Sprite3dPlugin);
app.add_plugins(TrenchBroomPlugins( app.add_plugins(TrenchBroomPlugins(
@@ -77,6 +84,8 @@ fn main() {
app.add_plugins(movables::plugin); app.add_plugins(movables::plugin);
app.add_plugins(billboards::plugin); app.add_plugins(billboards::plugin);
app.add_plugins(aim::plugin); app.add_plugins(aim::plugin);
app.add_plugins(protocol::plugin);
app.add_plugins(server::plugin);
app.add_plugins(npc::plugin); app.add_plugins(npc::plugin);
app.add_plugins(keys::plugin); app.add_plugins(keys::plugin);
app.add_plugins(squish_animation::plugin); app.add_plugins(squish_animation::plugin);
@@ -92,7 +101,7 @@ fn main() {
app.add_plugins(heads::plugin); app.add_plugins(heads::plugin);
app.add_plugins(hitpoints::plugin); app.add_plugins(hitpoints::plugin);
app.add_plugins(cash_heal::plugin); app.add_plugins(cash_heal::plugin);
app.add_plugins(utils::observers::plugin); app.add_plugins(utils::plugin);
app.add_plugins(water::plugin); app.add_plugins(water::plugin);
app.add_plugins(head_drop::plugin); app.add_plugins(head_drop::plugin);
app.add_plugins(trail::plugin); app.add_plugins(trail::plugin);

View File

@@ -0,0 +1,45 @@
use bevy::prelude::*;
use lightyear::prelude::{
server::{NetcodeConfig, NetcodeServer, ServerUdpIo},
*,
};
use shared::utils::commands::IsServer;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
pub fn plugin(app: &mut App) {
app.init_resource::<IsServer>();
app.add_systems(Startup, start_server);
app.add_observer(handle_new_client);
}
fn handle_new_client(
trigger: Trigger<OnAdd, Connected>,
mut commands: Commands,
id: Query<&PeerAddr>,
) -> Result {
let id = id.get(trigger.target())?;
info!("Client connected on IP: {}", id.ip());
commands
.entity(trigger.target())
.insert(ReplicationSender::default());
Ok(())
}
fn start_server(mut commands: Commands) -> Result {
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 25565);
commands
.spawn((
Name::from("Server"),
LocalAddr(server_addr),
ServerUdpIo::default(),
NetcodeServer::new(NetcodeConfig::default()),
))
.trigger(server::Start);
Ok(())
}

View File

@@ -19,6 +19,7 @@ bevy_debug_log = { workspace = true }
bevy_sprite3d = { workspace = true } bevy_sprite3d = { workspace = true }
bevy_trenchbroom = { workspace = true } bevy_trenchbroom = { workspace = true }
happy_feet = { workspace = true } happy_feet = { workspace = true }
lightyear = { workspace = true }
nil = { workspace = true } nil = { workspace = true }
rand = { workspace = true } rand = { workspace = true }
ron = { workspace = true } ron = { workspace = true }

View File

@@ -58,11 +58,15 @@ fn on_trigger_arrow(
state.rot.mul_quat(Quat::from_rotation_y(PI)) state.rot.mul_quat(Quat::from_rotation_y(PI))
}; };
let mut t = Transform::from_translation(state.pos).with_rotation(rotation); let mut transform = Transform::from_translation(state.pos).with_rotation(rotation);
t.translation += t.forward().as_vec3() * 2.; transform.translation += transform.forward().as_vec3() * 2.;
let damage = heads_db.head_stats(state.head).damage; let damage = heads_db.head_stats(state.head).damage;
commands.spawn((Name::new("projectile-arrow"), ArrowProjectile { damage }, t)); commands.spawn((
Name::new("projectile-arrow"),
ArrowProjectile { damage },
transform,
));
} }
fn update( fn update(

View File

@@ -6,12 +6,17 @@ use crate::{
hitpoints::Hit, hitpoints::Hit,
loading_assets::GameAssets, loading_assets::GameAssets,
physics_layers::GameLayer, physics_layers::GameLayer,
protocol::GltfSceneRoot,
tb_entities::EnemySpawn, tb_entities::EnemySpawn,
utils::{auto_rotate::AutoRotation, global_observer, sprite_3d_animation::AnimationTimer}, utils::{
auto_rotate::AutoRotation, commands::CommandExt, global_observer,
sprite_3d_animation::AnimationTimer,
},
}; };
use avian3d::prelude::*; use avian3d::prelude::*;
use bevy::{pbr::NotShadowCaster, prelude::*}; use bevy::{pbr::NotShadowCaster, prelude::*};
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams}; use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
use lightyear::prelude::{NetworkTarget, Replicate};
use std::f32::consts::PI; use std::f32::consts::PI;
const MAX_SHOT_AGES: f32 = 15.; const MAX_SHOT_AGES: f32 = 15.;
@@ -58,8 +63,6 @@ fn on_trigger_missile(
query_transform: Query<&Transform>, query_transform: Query<&Transform>,
time: Res<Time>, time: Res<Time>,
heads_db: Res<HeadsDatabase>, heads_db: Res<HeadsDatabase>,
assets: Res<GameAssets>,
gltf_assets: Res<Assets<Gltf>>,
) { ) {
let state = trigger.event().0; let state = trigger.event().0;
@@ -76,33 +79,32 @@ fn on_trigger_missile(
let head = heads_db.head_stats(state.head); let head = heads_db.head_stats(state.head);
let mut t = Transform::from_translation(state.pos).with_rotation(rotation); let mut transform = Transform::from_translation(state.pos).with_rotation(rotation);
t.translation += t.forward().as_vec3() * 2.0; transform.translation += transform.forward().as_vec3() * 2.0;
let mesh = assets.projectiles[format!("{}.glb", head.projectile).as_str()].clone(); commands
let asset = gltf_assets.get(&mesh).unwrap(); .spawn((
Name::new("projectile-missile"),
commands.spawn(( CurverProjectile {
Name::new("projectile-missile"), time: time.elapsed_secs(),
CurverProjectile { damage: head.damage,
time: time.elapsed_secs(), },
damage: head.damage, Collider::capsule_endpoints(0.4, Vec3::new(0., 0., 2.), Vec3::new(0., 0., -2.)),
}, CollisionLayers::new(
Collider::capsule_endpoints(0.4, Vec3::new(0., 0., 2.), Vec3::new(0., 0., -2.)), LayerMask(GameLayer::Projectile.to_bits()),
CollisionLayers::new( LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
LayerMask(GameLayer::Projectile.to_bits()), ),
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()), Sensor,
), CollisionEventsEnabled,
Sensor, Visibility::default(),
CollisionEventsEnabled, transform,
Visibility::default(), children![(
t, Transform::from_rotation(Quat::from_rotation_x(PI / 2.).inverse()),
children![( AutoRotation(Quat::from_rotation_x(0.4) * Quat::from_rotation_z(0.3)),
Transform::from_rotation(Quat::from_rotation_x(PI / 2.).inverse()), GltfSceneRoot::Projectile(head.projectile.clone()),
AutoRotation(Quat::from_rotation_x(0.4) * Quat::from_rotation_z(0.3)), ),],
SceneRoot(asset.scenes[0].clone()), ))
),], .insert_server(Replicate::to_clients(NetworkTarget::All));
));
} }
fn enemy_hit( fn enemy_hit(

View File

@@ -97,8 +97,8 @@ fn on_trigger_gun(
state.rot.mul_quat(Quat::from_rotation_y(PI)) state.rot.mul_quat(Quat::from_rotation_y(PI))
}; };
let mut t = Transform::from_translation(state.pos).with_rotation(rotation); let mut transform = Transform::from_translation(state.pos).with_rotation(rotation);
t.translation += t.forward().as_vec3() * 2.0; transform.translation += transform.forward().as_vec3() * 2.0;
commands.spawn(( commands.spawn((
Name::new("projectile-gun"), Name::new("projectile-gun"),
@@ -114,7 +114,7 @@ fn on_trigger_gun(
Sensor, Sensor,
CollisionEventsEnabled, CollisionEventsEnabled,
Visibility::default(), Visibility::default(),
t, transform,
Children::spawn(Spawn(Gizmo { Children::spawn(Spawn(Gizmo {
handle: gizmo_assets.add({ handle: gizmo_assets.add({
let mut g = GizmoAsset::default(); let mut g = GizmoAsset::default();

View File

@@ -5,14 +5,17 @@ use crate::{
heads_database::HeadsDatabase, heads_database::HeadsDatabase,
loading_assets::GameAssets, loading_assets::GameAssets,
physics_layers::GameLayer, physics_layers::GameLayer,
protocol::GltfSceneRoot,
sounds::PlaySound, sounds::PlaySound,
utils::{ utils::{
explosions::Explosion, global_observer, sprite_3d_animation::AnimationTimer, trail::Trail, commands::CommandExt, explosions::Explosion, global_observer,
sprite_3d_animation::AnimationTimer, trail::Trail,
}, },
}; };
use avian3d::prelude::*; use avian3d::prelude::*;
use bevy::{pbr::NotShadowCaster, prelude::*}; use bevy::{pbr::NotShadowCaster, prelude::*};
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams}; use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
use lightyear::prelude::{NetworkTarget, Replicate};
use std::f32::consts::PI; use std::f32::consts::PI;
const MAX_SHOT_AGES: f32 = 15.; const MAX_SHOT_AGES: f32 = 15.;
@@ -56,8 +59,6 @@ fn on_trigger_missile(
query_transform: Query<&Transform>, query_transform: Query<&Transform>,
time: Res<Time>, time: Res<Time>,
heads_db: Res<HeadsDatabase>, heads_db: Res<HeadsDatabase>,
assets: Res<GameAssets>,
gltf_assets: Res<Assets<Gltf>>,
mut gizmo_assets: ResMut<Assets<GizmoAsset>>, mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
) { ) {
let state = trigger.event().0; let state = trigger.event().0;
@@ -75,51 +76,50 @@ fn on_trigger_missile(
let head = heads_db.head_stats(state.head); let head = heads_db.head_stats(state.head);
let mut t = Transform::from_translation(state.pos).with_rotation(rotation); let mut transform = Transform::from_translation(state.pos).with_rotation(rotation);
t.translation += t.forward().as_vec3() * 2.0; transform.translation += transform.forward().as_vec3() * 2.0;
let mesh = assets.projectiles["missile.glb"].clone(); commands
let asset = gltf_assets.get(&mesh).unwrap(); .spawn((
Name::new("projectile-missile"),
commands.spawn(( MissileProjectile {
Name::new("projectile-missile"), time: time.elapsed_secs(),
MissileProjectile { damage: head.damage,
time: time.elapsed_secs(), },
damage: head.damage, Collider::capsule_endpoints(0.4, Vec3::new(0., 0., 2.), Vec3::new(0., 0., -2.)),
}, CollisionLayers::new(
Collider::capsule_endpoints(0.4, Vec3::new(0., 0., 2.), Vec3::new(0., 0., -2.)), LayerMask(GameLayer::Projectile.to_bits()),
CollisionLayers::new( LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
LayerMask(GameLayer::Projectile.to_bits()),
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
),
Sensor,
CollisionEventsEnabled,
Visibility::default(),
t,
children![
(
Transform::from_rotation(Quat::from_rotation_x(PI / 2.).inverse())
.with_scale(Vec3::splat(0.04)),
SceneRoot(asset.scenes[0].clone()),
), ),
( Sensor,
Trail::new( CollisionEventsEnabled,
12, Visibility::default(),
LinearRgba::rgb(1., 0.0, 0.), transform,
LinearRgba::rgb(0.9, 0.9, 0.) children![
) (
.with_pos(t.translation), Transform::from_rotation(Quat::from_rotation_x(PI / 2.).inverse())
Gizmo { .with_scale(Vec3::splat(0.04)),
handle: gizmo_assets.add(GizmoAsset::default()), GltfSceneRoot::Projectile("missile".to_string()),
line_config: GizmoLineConfig { ),
width: 10., (
Trail::new(
12,
LinearRgba::rgb(1., 0.0, 0.),
LinearRgba::rgb(0.9, 0.9, 0.)
)
.with_pos(transform.translation),
Gizmo {
handle: gizmo_assets.add(GizmoAsset::default()),
line_config: GizmoLineConfig {
width: 10.,
..default()
},
..default() ..default()
}, },
..default() )
}, ],
) ))
], .insert_server(Replicate::to_clients(NetworkTarget::All));
));
} }
fn update(mut query: Query<&mut Transform, With<MissileProjectile>>) { fn update(mut query: Query<&mut Transform, With<MissileProjectile>>) {

View File

@@ -5,9 +5,10 @@ use crate::{
heads_database::HeadsDatabase, heads_database::HeadsDatabase,
loading_assets::GameAssets, loading_assets::GameAssets,
physics_layers::GameLayer, physics_layers::GameLayer,
protocol::GltfSceneRoot,
sounds::PlaySound, sounds::PlaySound,
utils::{ utils::{
auto_rotate::AutoRotation, explosions::Explosion, global_observer, auto_rotate::AutoRotation, commands::CommandExt, explosions::Explosion, global_observer,
sprite_3d_animation::AnimationTimer, sprite_3d_animation::AnimationTimer,
}, },
}; };
@@ -15,10 +16,12 @@ use avian3d::prelude::*;
use bevy::{pbr::NotShadowCaster, prelude::*}; use bevy::{pbr::NotShadowCaster, prelude::*};
use bevy_ballistic::launch_velocity; use bevy_ballistic::launch_velocity;
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams}; use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
use lightyear::prelude::{NetworkTarget, Replicate};
use serde::{Deserialize, Serialize};
use std::f32::consts::PI; use std::f32::consts::PI;
#[derive(Component)] #[derive(Component, Serialize, Deserialize, PartialEq)]
struct ThrownProjectile { pub struct ThrownProjectile {
impact_animation: bool, impact_animation: bool,
damage: u32, damage: u32,
} }
@@ -50,8 +53,6 @@ fn on_trigger_thrown(
trigger: Trigger<TriggerThrow>, trigger: Trigger<TriggerThrow>,
mut commands: Commands, mut commands: Commands,
query_transform: Query<&Transform>, query_transform: Query<&Transform>,
assets: Res<GameAssets>,
gltf_assets: Res<Assets<Gltf>>,
heads_db: Res<HeadsDatabase>, heads_db: Res<HeadsDatabase>,
) { ) {
let state = trigger.event().0; let state = trigger.event().0;
@@ -76,8 +77,6 @@ fn on_trigger_thrown(
}; };
let head = heads_db.head_stats(state.head); let head = heads_db.head_stats(state.head);
let mesh = assets.projectiles[format!("{}.glb", head.projectile).as_str()].clone();
let asset = gltf_assets.get(&mesh).unwrap();
//TODO: projectile db? //TODO: projectile db?
let explosion_animation = !matches!(state.head, 8 | 16); let explosion_animation = !matches!(state.head, 8 | 16);
@@ -102,9 +101,10 @@ fn on_trigger_thrown(
Visibility::default(), Visibility::default(),
Sensor, Sensor,
)) ))
.insert_server(Replicate::to_clients(NetworkTarget::All))
.with_child(( .with_child((
AutoRotation(Quat::from_rotation_x(0.4) * Quat::from_rotation_z(0.3)), AutoRotation(Quat::from_rotation_x(0.4) * Quat::from_rotation_z(0.3)),
SceneRoot(asset.scenes[0].clone()), GltfSceneRoot::Projectile(head.projectile.clone()),
)); ));
} }

View File

@@ -114,11 +114,11 @@ fn update_player_aim(
} }
} }
if let Some(e) = &aim_target.0 { if let Some(e) = &aim_target.0
if commands.get_entity(*e).is_err() { && commands.get_entity(*e).is_err()
aim_target.0 = None; {
return; aim_target.0 = None;
} return;
} }
if new_target != aim_target.0 { if new_target != aim_target.0 {
@@ -166,11 +166,11 @@ fn update_npc_aim(
} }
} }
if let Some(e) = &aim_target.0 { if let Some(e) = &aim_target.0
if commands.get_entity(*e).is_err() { && commands.get_entity(*e).is_err()
aim_target.0 = None; {
return; aim_target.0 = None;
} return;
} }
if new_target != aim_target.0 { if new_target != aim_target.0 {

View File

@@ -140,16 +140,16 @@ fn sync(
target_data: Query<(&Hitpoints, &ActiveHeads), With<Npc>>, target_data: Query<(&Hitpoints, &ActiveHeads), With<Npc>>,
) { ) {
let mut new_state = None; let mut new_state = None;
if let Some(e) = player_target.iter().next().and_then(|target| target.0) { if let Some(e) = player_target.iter().next().and_then(|target| target.0)
if let Ok((hp, heads)) = target_data.get(e) { && let Ok((hp, heads)) = target_data.get(e)
let head = heads.current().expect("target must have a head on"); {
new_state = Some(UiHeadState { let head = heads.current().expect("target must have a head on");
head: head.head, new_state = Some(UiHeadState {
health: hp.health(), head: head.head,
ammo: 1., health: hp.health(),
reloading: None, ammo: 1.,
}); reloading: None,
} });
} }
if new_state != target.head { if new_state != target.head {

View File

@@ -10,17 +10,14 @@ use crate::{
}; };
use avian3d::{math::*, prelude::*}; use avian3d::{math::*, prelude::*};
use bevy::prelude::*; use bevy::prelude::*;
use happy_feet::{ use happy_feet::prelude::{
KinematicVelocity, Character, CharacterDrag, CharacterGravity, CharacterMovement, CharacterPlugins,
ground::{Grounding, GroundingConfig}, GroundFriction, Grounding, GroundingConfig, KinematicVelocity, MoveInput, SteppingBehaviour,
prelude::{ SteppingConfig,
Character, CharacterDrag, CharacterFriction, CharacterGravity, CharacterMovement,
CharacterPlugin, MoveInput, SteppingBehaviour, SteppingConfig,
},
}; };
pub fn plugin(app: &mut App) { pub fn plugin(app: &mut App) {
app.add_plugins(CharacterPlugin::default()); app.add_plugins(CharacterPlugins::default());
app.register_type::<MovementSpeedFactor>(); app.register_type::<MovementSpeedFactor>();
@@ -180,7 +177,7 @@ impl CharacterControllerBundle {
}; };
Self { Self {
character_controller: Character { up: Dir3::Y }, character_controller: Character,
collider, collider,
move_input: MoveInput::default(), move_input: MoveInput::default(),
movement_factor: MovementSpeedFactor(1.0), movement_factor: MovementSpeedFactor(1.0),
@@ -197,7 +194,7 @@ struct MovementConfig {
step: SteppingConfig, step: SteppingConfig,
ground: GroundingConfig, ground: GroundingConfig,
gravity: CharacterGravity, gravity: CharacterGravity,
friction: CharacterFriction, friction: GroundFriction,
drag: CharacterDrag, drag: CharacterDrag,
settings: ControllerSettings, settings: ControllerSettings,
} }
@@ -208,16 +205,19 @@ const RUNNING_MOVEMENT_CONFIG: MovementConfig = MovementConfig {
acceleration: 40.0, acceleration: 40.0,
}, },
step: SteppingConfig { step: SteppingConfig {
max_height: 0.25, max_vertical: 0.25,
max_horizontal: 0.4,
behaviour: SteppingBehaviour::Grounded, behaviour: SteppingBehaviour::Grounded,
max_substeps: 8,
}, },
ground: GroundingConfig { ground: GroundingConfig {
max_angle: PI / 4.0, max_angle: PI / 4.0,
max_distance: 0.2, max_distance: 0.2,
snap_to_surface: true, snap_to_surface: true,
up_direction: Dir3::Y,
}, },
gravity: CharacterGravity(vec3(0.0, -60.0, 0.0)), gravity: CharacterGravity(Some(vec3(0.0, -60.0, 0.0))),
friction: CharacterFriction(10.0), friction: GroundFriction(10.0),
drag: CharacterDrag(0.0), drag: CharacterDrag(0.0),
settings: ControllerSettings { settings: ControllerSettings {
jump_force: 25.0, jump_force: 25.0,
@@ -231,16 +231,19 @@ const FLYING_MOVEMENT_CONFIG: MovementConfig = MovementConfig {
acceleration: 300.0, acceleration: 300.0,
}, },
step: SteppingConfig { step: SteppingConfig {
max_height: 0.25, max_vertical: 0.25,
max_horizontal: 0.4,
behaviour: SteppingBehaviour::Never, behaviour: SteppingBehaviour::Never,
max_substeps: 8,
}, },
ground: GroundingConfig { ground: GroundingConfig {
max_angle: 0.0, max_angle: 0.0,
max_distance: -1.0, max_distance: -1.0,
snap_to_surface: false, snap_to_surface: false,
up_direction: Dir3::Y,
}, },
gravity: CharacterGravity(Vec3::ZERO), gravity: CharacterGravity(Some(Vec3::ZERO)),
friction: CharacterFriction(0.0), friction: GroundFriction(0.0),
drag: CharacterDrag(10.0), drag: CharacterDrag(10.0),
settings: ControllerSettings { settings: ControllerSettings {
jump_force: 0.0, jump_force: 0.0,

View File

@@ -89,14 +89,14 @@ impl ActiveHeads {
continue; continue;
} }
if let Some(head) = head { if let Some(head) = head
if head.health < head.health_max { && head.health < head.health_max
head.health = head {
.health head.health = head
.saturating_add(heal_amount) .health
.clamp(0, head.health_max); .saturating_add(heal_amount)
healed = true; .clamp(0, head.health_max);
} healed = true;
} }
} }

View File

@@ -24,6 +24,7 @@ pub mod npc;
pub mod physics_layers; pub mod physics_layers;
pub mod platforms; pub mod platforms;
pub mod player; pub mod player;
pub mod protocol;
pub mod sounds; pub mod sounds;
pub mod steam; pub mod steam;
pub mod tb_entities; pub mod tb_entities;

View File

@@ -79,7 +79,8 @@ fn move_active(
let t = (elapsed - active.start_time) / active.duration; let t = (elapsed - active.start_time) / active.duration;
transform.rotation = active.start.rotation.lerp(active.target.rotation, t); transform.rotation = active.start.rotation.lerp(active.target.rotation, t);
} else { } else {
*transform = active.target; transform.translation = active.target.translation;
transform.rotation = active.target.rotation;
commands.entity(e).remove::<(ActiveMovable, Movable)>(); commands.entity(e).remove::<(ActiveMovable, Movable)>();
} }
} }

View File

@@ -65,10 +65,10 @@ fn on_spawn_check(
} }
for (e, spawn, transform) in query.iter() { for (e, spawn, transform) in query.iter() {
if let Some(order) = spawn.spawn_order { if let Some(order) = spawn.spawn_order
if order > spawning.spawn_index { && order > spawning.spawn_index
continue; {
} continue;
} }
let id = names[&spawn.head]; let id = names[&spawn.head];

View File

@@ -0,0 +1,41 @@
use crate::{global_observer, loading_assets::GameAssets};
use bevy::prelude::*;
use lightyear::prelude::AppComponentExt;
use serde::{Deserialize, Serialize};
pub fn plugin(app: &mut App) {
app.register_component::<Transform>();
app.register_component::<GltfSceneRoot>();
global_observer!(app, spawn_gltf_scene_roots);
}
#[derive(Component, Reflect, Serialize, Deserialize, PartialEq)]
#[reflect(Component)]
pub enum GltfSceneRoot {
Projectile(String),
}
fn spawn_gltf_scene_roots(
trigger: Trigger<OnAdd, GltfSceneRoot>,
mut commands: Commands,
gltf_roots: Query<&GltfSceneRoot>,
assets: Res<GameAssets>,
gltfs: Res<Assets<Gltf>>,
) -> Result {
let root = gltf_roots.get(trigger.target())?;
let (gltf, index) = match root {
GltfSceneRoot::Projectile(addr) => (
assets.projectiles[format!("{addr}.glb").as_str()].clone(),
0,
),
};
let gltf = gltfs.get(&gltf).unwrap();
let scene = gltf.scenes[index].clone();
commands.entity(trigger.target()).insert(SceneRoot(scene));
Ok(())
}

View File

@@ -0,0 +1,21 @@
use bevy::ecs::{
bundle::Bundle, resource::Resource, system::EntityCommands, world::EntityWorldMut,
};
#[derive(Default, Resource)]
pub struct IsServer;
pub trait CommandExt {
fn insert_server(&mut self, bundle: impl Bundle) -> &mut Self;
}
impl<'w> CommandExt for EntityCommands<'w> {
fn insert_server(&mut self, bundle: impl Bundle) -> &mut Self {
self.queue(|mut entity: EntityWorldMut| {
if entity.world().contains_resource::<IsServer>() {
entity.insert(bundle);
}
});
self
}
}

View File

@@ -1,9 +1,15 @@
pub mod auto_rotate; pub mod auto_rotate;
pub mod billboards; pub mod billboards;
pub mod commands;
pub mod explosions; pub mod explosions;
pub mod observers; pub mod observers;
pub mod sprite_3d_animation; pub mod sprite_3d_animation;
pub mod squish_animation; pub mod squish_animation;
pub mod trail; pub mod trail;
use bevy::prelude::*;
pub(crate) use observers::global_observer; pub(crate) use observers::global_observer;
pub fn plugin(app: &mut App) {
app.add_plugins(observers::plugin);
}

View File

@@ -18,7 +18,7 @@ dbg-server:
RUST_BACKTRACE=1 cargo r --bin server --features dbg RUST_BACKTRACE=1 cargo r --bin server --features dbg
sort: sort:
cargo sort cargo sort --workspace
check: check:
cargo sort --check --workspace cargo sort --check --workspace