From b072b09c00e6892310c6ee9187f441e95ae89ccf Mon Sep 17 00:00:00 2001 From: core Date: Mon, 7 Jul 2025 23:56:56 -0400 Subject: [PATCH] feat: part & attachment beginnings --- Cargo.lock | 13 --- crates/unified/Cargo.toml | 5 +- .../assets/config/parts/hearty.part.toml | 14 +++ crates/unified/src/attachment.rs | 21 +++++ .../unified/src/client/incoming_particles.rs | 46 ---------- crates/unified/src/client/mod.rs | 3 - crates/unified/src/client_plugins.rs | 2 - crates/unified/src/config/mod.rs | 1 + crates/unified/src/config/part.rs | 28 ++++++ crates/unified/src/config/world.rs | 2 +- crates/unified/src/ecs.rs | 9 +- crates/unified/src/lib.rs | 1 + crates/unified/src/server/mod.rs | 5 +- crates/unified/src/server/part.rs | 89 +++++++++++++++++++ crates/unified/src/server/player.rs | 13 ++- crates/unified/src/server_plugins.rs | 4 +- crates/unified/src/shared_plugins.rs | 12 ++- 17 files changed, 188 insertions(+), 80 deletions(-) create mode 100644 crates/unified/assets/config/parts/hearty.part.toml create mode 100644 crates/unified/src/attachment.rs delete mode 100644 crates/unified/src/client/incoming_particles.rs create mode 100644 crates/unified/src/config/part.rs create mode 100644 crates/unified/src/server/part.rs diff --git a/Cargo.lock b/Cargo.lock index e1081e95c2f7bcfacb7358d18175c3839d182981..4c8774b2714f579bd37054db367cc6cf33fc3ee8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1233,18 +1233,6 @@ dependencies = [ "encase_derive_impl 0.10.0", ] -[[package]] -name = "bevy_enoki" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740289c63cb752adbad3823c49c01d479d853a10d76ccfe5ce3d1f3ea8e14ac2" -dependencies = [ - "bevy 0.16.1", - "rand 0.8.5", - "ron 0.8.1", - "serde", -] - [[package]] name = "bevy_gizmos" version = "0.13.2" @@ -7797,7 +7785,6 @@ dependencies = [ "bevy 0.16.1", "bevy_common_assets", "bevy_egui", - "bevy_enoki", "bevy_rapier2d 0.30.0", "bevy_replicon", "clap", diff --git a/crates/unified/Cargo.toml b/crates/unified/Cargo.toml index d58cf11c9695650e83caf382c1b6a4c812b7a365..f4db34931fa979e42e3676cbe9497caca27c1c79 100644 --- a/crates/unified/Cargo.toml +++ b/crates/unified/Cargo.toml @@ -26,7 +26,8 @@ bevy = { version = "0.16", default-features = false, features = [ "wayland", "multi_threaded", "bevy_dev_tools", - "bevy_sprite_picking_backend" + "bevy_sprite_picking_backend", + "png" ] } bevy_rapier2d = { version = "0.30", features = ["serde-serialize", "simd-stable"] } bevy_common_assets = { version = "0.13", features = ["toml"] } @@ -53,8 +54,6 @@ bevy_egui = "0.35" ordered-float = { version = "5", features = ["serde"] } ron = "0.10" -bevy_enoki = "0.4" - [target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/crates/unified/assets/config/parts/hearty.part.toml b/crates/unified/assets/config/parts/hearty.part.toml new file mode 100644 index 0000000000000000000000000000000000000000..268a9827558f2cb1ce26f47c233114890ba31816 --- /dev/null +++ b/crates/unified/assets/config/parts/hearty.part.toml @@ -0,0 +1,14 @@ +[part] +name = "Hearty" +sprite_connected = "textures/hearty.png" +sprite_disconnected = "textures/hearty.png" + +[physics] +width = 50 +height = 50 +mass = 100 + +# North +[[joints]] +translation = [ 0.0, 50.0, 0.0 ] +rotation = 0.0 \ No newline at end of file diff --git a/crates/unified/src/attachment.rs b/crates/unified/src/attachment.rs new file mode 100644 index 0000000000000000000000000000000000000000..a46953dace7a467d2063df58337422b69ad0b0f3 --- /dev/null +++ b/crates/unified/src/attachment.rs @@ -0,0 +1,21 @@ +use bevy::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Component, Serialize, Deserialize)] +/// The primary component for a ship structure +pub struct Ship; + +#[derive(Component, Serialize, Deserialize)] +#[relationship_target(relationship = PartInShip, linked_spawn)] +pub struct Parts(Vec); + +#[derive(Component, Serialize, Deserialize)] +#[relationship(relationship_target = Parts)] +pub struct PartInShip(Entity); + +#[derive(Component, Serialize, Deserialize)] +pub struct Joint { + pub transform: Transform +} +#[derive(Component, Serialize, Deserialize)] +pub struct Peer(Entity); \ No newline at end of file diff --git a/crates/unified/src/client/incoming_particles.rs b/crates/unified/src/client/incoming_particles.rs deleted file mode 100644 index 2a849bd3ee6ce86bab9c74466cc043f89b6f8663..0000000000000000000000000000000000000000 --- a/crates/unified/src/client/incoming_particles.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::ecs::Particles; -use bevy::prelude::*; -use bevy_enoki::prelude::ParticleSpawnerState; -use bevy_enoki::{ParticleEffectHandle, ParticleSpawner}; - -pub fn replicated_particles_plugin(app: &mut App) { - app.add_systems( - PreUpdate, - (replicate_new_particles, replicate_updated_particles), - ); -} - -fn replicate_new_particles( - q: Query<(Entity, &Particles), Added>, - assets: Res, - mut commands: Commands, -) { - for (entity, p) in q.iter() { - commands - .entity(entity) - .insert(ParticleSpawner::default()) - .insert(ParticleEffectHandle(assets.load(&p.effect))) - .insert(ParticleSpawnerState { - active: p.active, - ..default() - }); - info!("replicate_new_particles {:?}", p); - } -} -fn replicate_updated_particles( - mut q: Query< - ( - Entity, - &mut ParticleEffectHandle, - &mut ParticleSpawnerState, - &Particles, - ), - Changed, - >, - assets: Res, -) { - for (_entity, mut handle, mut state, p) in &mut q { - *handle = ParticleEffectHandle(assets.load(&p.effect)); - state.active = p.active; - } -} diff --git a/crates/unified/src/client/mod.rs b/crates/unified/src/client/mod.rs index 0ad5df76c9cf6931a0ac7b13646a4912c6a606fd..adb04069cc5a38ab24fec21f414dee2acc4f428c 100644 --- a/crates/unified/src/client/mod.rs +++ b/crates/unified/src/client/mod.rs @@ -1,5 +1,4 @@ mod colors; -mod incoming_particles; mod parts; mod key_input; mod net; @@ -9,7 +8,6 @@ mod planet; mod zoom; mod particles; -use crate::client::incoming_particles::replicated_particles_plugin; use crate::client::parts::parts_plugin; use planet::incoming_planets::incoming_planets_plugin; use crate::client::key_input::key_input_plugin; @@ -55,7 +53,6 @@ impl Plugin for ClientPlugin { .add_plugins(key_input_plugin) .add_plugins(starfield_plugin) .add_plugins(ui_plugin) - .add_plugins(replicated_particles_plugin) .add_plugins(zoom_plugin) .insert_resource(DebugPickingMode::Disabled); } diff --git a/crates/unified/src/client_plugins.rs b/crates/unified/src/client_plugins.rs index fb7851f13012bb970d2d244e825d633121f81da9..644ba1636b267716c3f69ba08aa888b07f32c446 100644 --- a/crates/unified/src/client_plugins.rs +++ b/crates/unified/src/client_plugins.rs @@ -7,7 +7,6 @@ use bevy::dev_tools::picking_debug::DebugPickingPlugin; use bevy::log::LogPlugin; use bevy::prelude::MeshPickingPlugin; use bevy::sprite::prelude::SpritePickingPlugin; -use bevy_enoki::EnokiPlugin; use bevy_rapier2d::prelude::RapierDebugRenderPlugin; use bevy_replicon::RepliconPlugins; @@ -21,7 +20,6 @@ impl PluginGroup for ClientPluginGroup { .add_group(RepliconPlugins) .add(WebSocketClientPlugin) .add(AeronetRepliconClientPlugin) - .add(EnokiPlugin) .add(MeshPickingPlugin) .add(DebugPickingPlugin) .add(ClientPlugin { diff --git a/crates/unified/src/config/mod.rs b/crates/unified/src/config/mod.rs index 5ca33e5ff42d7f9bbdbbf779ce937252a92323d8..7a72fe4956d4cb1056ae35d874bfc1b856602c6c 100644 --- a/crates/unified/src/config/mod.rs +++ b/crates/unified/src/config/mod.rs @@ -1,2 +1,3 @@ pub mod planet; pub mod world; +pub mod part; diff --git a/crates/unified/src/config/part.rs b/crates/unified/src/config/part.rs new file mode 100644 index 0000000000000000000000000000000000000000..72a6d45d44b821d954e165143157fb9c77d112c1 --- /dev/null +++ b/crates/unified/src/config/part.rs @@ -0,0 +1,28 @@ +use bevy::asset::Asset; +use bevy::math::Vec3; +use bevy::prelude::TypePath; +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, TypePath, Serialize, Clone, Debug, PartialEq, Asset)] +pub struct PartConfig { + pub part: PartPartConfig, + pub physics: PartPhysicsConfig, + pub joints: Vec +} +#[derive(Deserialize, TypePath, Serialize, Clone, Debug, PartialEq, Asset)] +pub struct PartPartConfig { + pub name: String, + pub sprite_connected: String, + pub sprite_disconnected: String, +} +#[derive(Deserialize, TypePath, Serialize, Clone, Debug, PartialEq, Asset)] +pub struct PartPhysicsConfig { + pub width: f32, + pub height: f32, + pub mass: f32 +} +#[derive(Deserialize, TypePath, Serialize, Clone, Debug, PartialEq, Asset)] +pub struct JointConfig { + pub translation: Vec3, + pub rotation: f32 +} \ No newline at end of file diff --git a/crates/unified/src/config/world.rs b/crates/unified/src/config/world.rs index fa9a7034077f90eb30ceccf490f1478c1655d223..7e8d7f1fcee13f7790176a8ea94b1db6d5d04501 100644 --- a/crates/unified/src/config/world.rs +++ b/crates/unified/src/config/world.rs @@ -20,7 +20,7 @@ pub struct WorldConfig { pub struct PartConfig { pub default_width: f32, pub default_height: f32, - pub default_mass: f32, + pub default_mass: f32 } #[derive(Deserialize, Asset, TypePath, Clone)] diff --git a/crates/unified/src/ecs.rs b/crates/unified/src/ecs.rs index 3d1f173e4d45c13f079c35683fe12c9b7a485954..58de158e42e280633a67ea88519240b5f62f8b5c 100644 --- a/crates/unified/src/ecs.rs +++ b/crates/unified/src/ecs.rs @@ -4,20 +4,19 @@ use bevy_rapier2d::dynamics::AdditionalMassProperties; use bevy_rapier2d::dynamics::RigidBody; use bevy_rapier2d::geometry::Collider; use bevy_rapier2d::prelude::*; +use bevy_replicon::prelude::Replicated; use serde::{Deserialize, Serialize}; -#[derive(Component, Serialize, Deserialize)] -pub struct Ball; -#[derive(Component, Serialize, Deserialize)] -pub struct Ground; #[derive(Component)] pub struct MainCamera; + #[derive(Component)] pub struct StarfieldFront; #[derive(Component)] pub struct StarfieldMid; #[derive(Component)] pub struct StarfieldBack; + #[derive(Resource, Default)] pub struct CursorWorldCoordinates(pub Option); @@ -31,7 +30,7 @@ pub enum ThrustEvent { } #[derive(Component, Serialize, Deserialize, Debug)] -#[require(ReadMassProperties, RigidBody::Dynamic, ExternalForce, ExternalImpulse)] +#[require(ReadMassProperties, RigidBody::Dynamic, ExternalForce, ExternalImpulse, Replicated)] pub struct Part { pub sprite: String, pub width: f32, diff --git a/crates/unified/src/lib.rs b/crates/unified/src/lib.rs index 11a7f0b8eaaf916559f7cd9705dc1afadb8ba63a..13f8b9cfe6e61fc76de68c65e5e23f80cad70ebd 100644 --- a/crates/unified/src/lib.rs +++ b/crates/unified/src/lib.rs @@ -28,3 +28,4 @@ pub mod shared_plugins; #[cfg(all(not(target_arch = "wasm32"), feature = "particle_editor"))] pub mod particle_editor; pub mod clientevent; +pub mod attachment; \ No newline at end of file diff --git a/crates/unified/src/server/mod.rs b/crates/unified/src/server/mod.rs index a1ad5ae7ddfe0120a444bd49715df106b267f3bd..9c299e3391610914876b864cef8800eeefca41eb 100644 --- a/crates/unified/src/server/mod.rs +++ b/crates/unified/src/server/mod.rs @@ -4,6 +4,7 @@ pub mod player; mod world_config; mod earth_parts; mod part_dragging; +mod part; use crate::server::gravity::newtonian_gravity_plugin; use crate::server::planets::planets_plugin; @@ -18,6 +19,7 @@ use bevy::prelude::*; use bevy_replicon::prelude::Replicated; use std::net::SocketAddr; use crate::server::earth_parts::spawn_parts_plugin; +use crate::server::part::part_config_plugin; use crate::server::part_dragging::part_dragging_plugin; pub struct ServerPlugin { @@ -47,7 +49,8 @@ impl Plugin for ServerPlugin { .add_plugins(newtonian_gravity_plugin) .add_plugins(player_management_plugin) .add_plugins(part_dragging_plugin) - .add_plugins(spawn_parts_plugin); + .add_plugins(spawn_parts_plugin) + .add_plugins(part_config_plugin); } } impl ServerPlugin { diff --git a/crates/unified/src/server/part.rs b/crates/unified/src/server/part.rs new file mode 100644 index 0000000000000000000000000000000000000000..ad753e020b6ba9e94fa5329f5bc77be3d698739d --- /dev/null +++ b/crates/unified/src/server/part.rs @@ -0,0 +1,89 @@ +use bevy::asset::Handle; +use bevy::prelude::*; +use bevy_rapier2d::prelude::{AdditionalMassProperties, Collider}; +use crate::config::part::PartConfig; +use crate::ecs::Part; + +pub fn part_config_plugin(app: &mut App) { + app.add_systems(PreUpdate, handle_spawn_part_requests) + .add_systems(Update, (update_part_requests, hotreload)); +} + +#[derive(Component, Debug)] +pub struct SpawnPart(pub String); +#[derive(Component)] +struct LoadingPart(Handle); +#[derive(Component, Debug)] +struct PartType(AssetId); + +// watch for new SpawnPart components and start loading their config files +fn handle_spawn_part_requests(new_parts: Query<(Entity, &SpawnPart), Added>, mut commands: Commands, asset_server: Res) { + for (new_part, request) in &new_parts { + info!(?new_part, ?request, "answering part request"); + commands.entity(new_part) + .remove::() + .insert(LoadingPart(asset_server.load(request.0.clone()))); + } +} +fn update_part_requests( + mut ev_config: EventReader>, + loading_parts: Query<(Entity, &LoadingPart)>, + assets: ResMut>, + mut commands: Commands +) { + for ev in ev_config.read() { + match ev { + AssetEvent::Added { id } => { + info!(?id, "asset added"); + for (loading_part, req) in &loading_parts { + if req.0.id() == *id { + let Some(asset) = assets.get(*id) else { continue; }; + spawn_part(commands.reborrow(), loading_part, asset, id); + } + } + }, + AssetEvent::Modified { id } => { + warn!("asset modification missed!"); + } + _ => {} + } + } +} +fn hotreload( + mut ev_config: EventReader>, + existing_parts: Query<(Entity, &PartType)>, + assets: ResMut>, + mut commands: Commands +) { + for ev in ev_config.read() { + match ev { + AssetEvent::Modified { id } => { + info!(?id, "updating part"); + for (existing_part, ptype) in &existing_parts { + if ptype.0 == *id { + let Some(asset) = assets.get(ptype.0) else { continue; }; + spawn_part(commands.reborrow(), existing_part, asset, id); + } + } + } + AssetEvent::Added { id } => { + warn!("asset addition missed!"); + } + _ => {} + } + } +} + +fn spawn_part(mut commands: Commands, entity: Entity, part: &PartConfig, id: &AssetId) { + commands.entity(entity) + .remove::() + .insert(Part { + sprite: part.part.sprite_disconnected.clone(), + width: part.physics.width, + height: part.physics.height, + mass: part.physics.mass, + }) + .insert(Collider::cuboid(part.physics.width / 2.0, part.physics.height / 2.0)) + .insert(AdditionalMassProperties::Mass(part.physics.mass)) + .insert(PartType(*id)); +} \ No newline at end of file diff --git a/crates/unified/src/server/player.rs b/crates/unified/src/server/player.rs index 6f4fa1c4dc5b7677d5232bd34ff30c823e6e5025..34f0da3628826020afd554e6252f663c4cbf8cf3 100644 --- a/crates/unified/src/server/player.rs +++ b/crates/unified/src/server/player.rs @@ -9,6 +9,7 @@ use bevy_rapier2d::prelude::{ AdditionalMassProperties, Collider, ExternalForce, ExternalImpulse, MassProperties, }; use bevy_replicon::prelude::{FromClient, Replicated}; +use crate::server::part::{SpawnPart}; pub fn player_management_plugin(app: &mut App) { app.add_systems(PreUpdate, reset_movement) @@ -28,6 +29,7 @@ fn handle_new_players( q_new_clients: Query>, world_config: Res, planets: Query<(&Transform, &Planet)>, + asset_server: Res ) { let Some(wc) = &world_config.config else { return; @@ -48,6 +50,15 @@ fn handle_new_players( info!(?new_transform, "set player's position!"); + commands.entity(joined_player) + .insert(SpawnPart("config/parts/hearty.part.toml".to_string())) + .insert(new_transform) + .insert(PlayerThrust::default()) + .insert(Player { + client: joined_player + }); + + /* commands .entity(joined_player) .insert(PartBundle { @@ -131,7 +142,7 @@ fn handle_new_players( ), Replicated ), - ]); + ]);*/ } } diff --git a/crates/unified/src/server_plugins.rs b/crates/unified/src/server_plugins.rs index 60f6f4b53df3b140220c7ce86c7f3ca6ba40d03a..d48bbd60f035e7e7231c2f8345ab98c672b97fc6 100644 --- a/crates/unified/src/server_plugins.rs +++ b/crates/unified/src/server_plugins.rs @@ -1,5 +1,5 @@ use crate::config::planet::PlanetConfigCollection; -use crate::config::world::GlobalWorldConfig; +use crate::config::world::{GlobalWorldConfig}; use aeronet_replicon::server::AeronetRepliconServerPlugin; use aeronet_websocket::server::WebSocketServerPlugin; use bevy::app::{PluginGroup, PluginGroupBuilder, ScheduleRunnerPlugin, TaskPoolPlugin}; @@ -10,6 +10,7 @@ use bevy_common_assets::toml::TomlAssetPlugin; use bevy_replicon::RepliconPlugins; use std::net::SocketAddr; use std::time::Duration; +use crate::config::part::PartConfig; pub struct ServerPluginGroup { pub bind: SocketAddr, @@ -32,6 +33,7 @@ impl PluginGroup for ServerPluginGroup { .add(AssetPlugin::default()) .add(TomlAssetPlugin::::new(&["wc.toml"])) .add(TomlAssetPlugin::::new(&["pc.toml"])) + .add(TomlAssetPlugin::::new(&["part.toml"])) .add(crate::server::ServerPlugin { bind: self.bind, max_clients: self.max_clients, diff --git a/crates/unified/src/shared_plugins.rs b/crates/unified/src/shared_plugins.rs index 017fa2ef781622efa6bae0789a1f2f6d008ccfec..bc1cd37798d8105f8414a4dbb394648bfa0e86d0 100644 --- a/crates/unified/src/shared_plugins.rs +++ b/crates/unified/src/shared_plugins.rs @@ -1,9 +1,10 @@ use crate::config::planet::Planet; -use crate::ecs::{Ball, Ground, Part, Particles, Player, ThrustEvent}; +use crate::ecs::{Part, Particles, Player, ThrustEvent}; use bevy::app::{App, PluginGroup, PluginGroupBuilder}; use bevy::prelude::*; use bevy_rapier2d::prelude::*; use bevy_replicon::prelude::{AppRuleExt, Channel, ClientEventAppExt}; +use crate::attachment::{Joint, PartInShip, Peer, Ship}; use crate::clientevent::{PartDragControlEvent, PartDragEvent}; pub struct SharedPluginGroup; @@ -23,15 +24,18 @@ pub fn register_everything(app: &mut App) { .add_mapped_client_event::(Channel::Unreliable) .add_mapped_client_event::(Channel::Ordered) .replicate::() - .replicate::() - .replicate::() .replicate::() .replicate::() .replicate::() .replicate::() .replicate::() .replicate::() - .replicate::(); + .replicate::() + + .replicate::() + .replicate::() + .replicate::() + .replicate::(); } fn physics_setup_plugin(app: &mut App) {