M Cargo.lock => Cargo.lock +0 -13
@@ 1234,18 1234,6 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 7797,7 7785,6 @@ dependencies = [
"bevy 0.16.1",
"bevy_common_assets",
"bevy_egui",
- "bevy_enoki",
"bevy_rapier2d 0.30.0",
"bevy_replicon",
"clap",
M crates/unified/Cargo.toml => crates/unified/Cargo.toml +2 -3
@@ 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]
A crates/unified/assets/config/parts/hearty.part.toml => crates/unified/assets/config/parts/hearty.part.toml +14 -0
@@ 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
A crates/unified/src/attachment.rs => crates/unified/src/attachment.rs +21 -0
@@ 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<Entity>);
+
+#[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
D crates/unified/src/client/incoming_particles.rs => crates/unified/src/client/incoming_particles.rs +0 -46
@@ 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<Particles>>,
- assets: Res<AssetServer>,
- 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<Particles>,
- >,
- assets: Res<AssetServer>,
-) {
- for (_entity, mut handle, mut state, p) in &mut q {
- *handle = ParticleEffectHandle(assets.load(&p.effect));
- state.active = p.active;
- }
-}
M crates/unified/src/client/mod.rs => crates/unified/src/client/mod.rs +0 -3
@@ 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);
}
M crates/unified/src/client_plugins.rs => crates/unified/src/client_plugins.rs +0 -2
@@ 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 {
M crates/unified/src/config/mod.rs => crates/unified/src/config/mod.rs +1 -0
@@ 1,2 1,3 @@
pub mod planet;
pub mod world;
+pub mod part;
A crates/unified/src/config/part.rs => crates/unified/src/config/part.rs +28 -0
@@ 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<JointConfig>
+}
+#[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
M crates/unified/src/config/world.rs => crates/unified/src/config/world.rs +1 -1
@@ 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)]
M crates/unified/src/ecs.rs => crates/unified/src/ecs.rs +4 -5
@@ 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<Vec2>);
@@ 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,
M crates/unified/src/lib.rs => crates/unified/src/lib.rs +1 -0
@@ 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
M crates/unified/src/server/mod.rs => crates/unified/src/server/mod.rs +4 -1
@@ 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 {
A crates/unified/src/server/part.rs => crates/unified/src/server/part.rs +89 -0
@@ 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<PartConfig>);
+#[derive(Component, Debug)]
+struct PartType(AssetId<PartConfig>);
+
+// watch for new SpawnPart components and start loading their config files
+fn handle_spawn_part_requests(new_parts: Query<(Entity, &SpawnPart), Added<SpawnPart>>, mut commands: Commands, asset_server: Res<AssetServer>) {
+ for (new_part, request) in &new_parts {
+ info!(?new_part, ?request, "answering part request");
+ commands.entity(new_part)
+ .remove::<SpawnPart>()
+ .insert(LoadingPart(asset_server.load(request.0.clone())));
+ }
+}
+fn update_part_requests(
+ mut ev_config: EventReader<AssetEvent<PartConfig>>,
+ loading_parts: Query<(Entity, &LoadingPart)>,
+ assets: ResMut<Assets<PartConfig>>,
+ 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<AssetEvent<PartConfig>>,
+ existing_parts: Query<(Entity, &PartType)>,
+ assets: ResMut<Assets<PartConfig>>,
+ 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<PartConfig>) {
+ commands.entity(entity)
+ .remove::<LoadingPart>()
+ .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
M crates/unified/src/server/player.rs => crates/unified/src/server/player.rs +12 -1
@@ 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<Entity, Added<ConnectedGameEntity>>,
world_config: Res<WorldConfigResource>,
planets: Query<(&Transform, &Planet)>,
+ asset_server: Res<AssetServer>
) {
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
),
- ]);
+ ]);*/
}
}
M crates/unified/src/server_plugins.rs => crates/unified/src/server_plugins.rs +3 -1
@@ 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::<GlobalWorldConfig>::new(&["wc.toml"]))
.add(TomlAssetPlugin::<PlanetConfigCollection>::new(&["pc.toml"]))
+ .add(TomlAssetPlugin::<PartConfig>::new(&["part.toml"]))
.add(crate::server::ServerPlugin {
bind: self.bind,
max_clients: self.max_clients,
M crates/unified/src/shared_plugins.rs => crates/unified/src/shared_plugins.rs +8 -4
@@ 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::<PartDragEvent>(Channel::Unreliable)
.add_mapped_client_event::<PartDragControlEvent>(Channel::Ordered)
.replicate::<Transform>()
- .replicate::<Ball>()
- .replicate::<Ground>()
.replicate::<Collider>()
.replicate::<RigidBody>()
.replicate::<Planet>()
.replicate::<Part>()
.replicate::<Player>()
.replicate::<Particles>()
- .replicate::<ChildOf>();
+ .replicate::<ChildOf>()
+
+ .replicate::<Ship>()
+ .replicate::<PartInShip>()
+ .replicate::<Joint>()
+ .replicate::<Peer>();
}
fn physics_setup_plugin(app: &mut App) {