~starkingdoms/starkingdoms

69730b33b8aed64f6b26e6cdcb334a7cec4e54f4 — core 5 months ago 8f2fb80
planets
M .cargo/config.toml => .cargo/config.toml +1 -1
@@ 3,4 3,4 @@ xtask = "run --release --package xtask --"

[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=/usr/bin/mold"]
\ No newline at end of file
rustflags = ["-C", "link-arg=-fuse-ld=lld"]

M Cargo.lock => Cargo.lock +90 -4
@@ 786,6 786,7 @@ dependencies = [
 "futures-io",
 "futures-lite",
 "js-sys",
 "notify-debouncer-full",
 "parking_lot",
 "ron",
 "serde",


@@ 857,6 858,20 @@ dependencies = [
]

[[package]]
name = "bevy_common_assets"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a18527525acb0a131ecad38eecbd0e578aefb55959304a37ab44b474b67734b"
dependencies = [
 "anyhow",
 "bevy 0.16.1",
 "csv",
 "serde",
 "thiserror 1.0.69",
 "toml 0.8.23",
]

[[package]]
name = "bevy_core"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1903,12 1918,12 @@ dependencies = [
[[package]]
name = "bevy_replicon"
version = "0.34.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d4ce1947bc4f59da376589e1b019def816b49ecb9d23ecf0233211d137ac9fe"
source = "git+https://github.com/projectharmonia/bevy_replicon?branch=fix-uninit-drop#b17e5645fbf0378399fd7f33ac5a91cd205e6613"
dependencies = [
 "bevy 0.16.1",
 "bitflags 2.9.1",
 "bytes",
 "fnv",
 "log",
 "petgraph 0.8.2",
 "postcard",


@@ 3420,6 3435,27 @@ dependencies = [
]

[[package]]
name = "csv"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf"
dependencies = [
 "csv-core",
 "itoa",
 "ryu",
 "serde",
]

[[package]]
name = "csv-core"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d"
dependencies = [
 "memchr",
]

[[package]]
name = "ctrlc"
version = "3.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 3950,6 3986,15 @@ dependencies = [
]

[[package]]
name = "file-id"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bc904b9bbefcadbd8e3a9fb0d464a9b979de6324c03b3c663e8994f46a5be36"
dependencies = [
 "windows-sys 0.52.0",
]

[[package]]
name = "filetime"
version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 5816,12 5861,44 @@ dependencies = [
 "libc",
 "log",
 "mio",
 "notify-types",
 "notify-types 1.0.1",
 "walkdir",
 "windows-sys 0.52.0",
]

[[package]]
name = "notify"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943"
dependencies = [
 "bitflags 2.9.1",
 "filetime",
 "fsevent-sys",
 "inotify 0.11.0",
 "kqueue",
 "libc",
 "log",
 "mio",
 "notify-types 2.0.0",
 "walkdir",
 "windows-sys 0.59.0",
]

[[package]]
name = "notify-debouncer-full"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2d88b1a7538054351c8258338df7c931a590513fb3745e8c15eb9ff4199b8d1"
dependencies = [
 "file-id",
 "log",
 "notify 8.0.0",
 "notify-types 2.0.0",
 "walkdir",
]

[[package]]
name = "notify-types"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 5831,6 5908,12 @@ dependencies = [
]

[[package]]
name = "notify-types"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d"

[[package]]
name = "ntapi"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 6964,6 7047,7 @@ dependencies = [
 "serde",
 "simba 0.9.0",
 "thiserror 2.0.12",
 "vec_map",
]

[[package]]


@@ 7768,11 7852,13 @@ name = "starkingdoms"
version = "0.1.0"
dependencies = [
 "bevy 0.16.1",
 "bevy_common_assets",
 "bevy_rapier2d 0.30.0",
 "bevy_replicon",
 "bevy_replicon_renet2",
 "clap",
 "crossbeam-channel",
 "log",
 "serde",
 "tracing-subscriber",
]


@@ 10174,7 10260,7 @@ version = "0.1.0"
dependencies = [
 "anyhow",
 "colored",
 "notify",
 "notify 7.0.0",
 "resvg",
 "tiny_http",
 "wasm-pack",

M Cargo.toml => Cargo.toml +10 -1
@@ 44,13 44,22 @@ crossbeam = "0.8"
# misc
bytemuck = "1"


[profile.dev]
opt-level = 1
[profile.dev.package."*"]
opt-level = 3

[profile.release]
debug = "full"
codegen-units = 1
lto = "thin"

[profile.wasm-release]
inherits = "release"
opt-level = "s"
strip = "debuginfo"


[patch.crates-io]
# TODO https://github.com/projectharmonia/bevy_replicon/pull/514
bevy_replicon = { git = "https://github.com/projectharmonia/bevy_replicon", branch = "fix-uninit-drop" }
\ No newline at end of file

M crates/unified/Cargo.toml => crates/unified/Cargo.toml +4 -2
@@ 5,8 5,9 @@ edition = "2024"
version = "0.1.0"

[dependencies]
bevy = { version = "0.16", features = ["serialize"] }
bevy_rapier2d = { version = "0.30", features = ["serde-serialize"] }
bevy = { version = "0.16", features = ["serialize", "file_watcher"] }
bevy_rapier2d = { version = "0.30", features = ["serde-serialize", "simd-stable"] }
bevy_common_assets = { version = "0.13", features = ["toml"] }

bevy_replicon = "0.34"
bevy_replicon_renet2 = { version = "0.10", features = ["native_transport"] }


@@ 14,6 15,7 @@ bevy_replicon_renet2 = { version = "0.10", features = ["native_transport"] }
clap = { version = "4", features = ["derive", "cargo"] }

tracing-subscriber = "0.3"
log = { version = "*", features = ["max_level_debug", "release_max_level_warn"] }

crossbeam-channel = "0.5"
serde = { version = "1", features = ["derive"] }
\ No newline at end of file

A crates/unified/assets/config/planets.pc.toml => crates/unified/assets/config/planets.pc.toml +20 -0
@@ 0,0 1,20 @@
[[planets]]
name = "Sun"
sprite = "textures/sun.png"
radius = 400.0 # m
mass = 160_000_00.0 # kg
default_transform = [0.0, 0.0, 0.0]

[[planets]]
name = "Mercury"
sprite = "textures/mercury_NEEDS_4_1_1.png"
radius = 6.666_7 # m
mass = 246.669_133_339 # kg
default_transform = [2322.588, 0.0, 0.0]

[[planets]]
name = "Venus"
sprite = "textures/venus.png"
radius = 18.998 # m
mass = 8_166.826_315 # kg
default_transform = [4339.992, 0.0, 0.0]
\ No newline at end of file

A crates/unified/assets/config/world.wc.toml => crates/unified/assets/config/world.wc.toml +2 -0
@@ 0,0 1,2 @@
[world]
gravity = 0.015
\ No newline at end of file

A crates/unified/assets/textures/mars.png => crates/unified/assets/textures/mars.png +0 -0
A crates/unified/assets/textures/mars_icon.png => crates/unified/assets/textures/mars_icon.png +0 -0
A crates/unified/assets/textures/starfield_dim.png => crates/unified/assets/textures/starfield_dim.png +0 -0
A crates/unified/assets/textures/superthruster_on.png => crates/unified/assets/textures/superthruster_on.png +0 -0
A crates/unified/assets/textures/venus.png => crates/unified/assets/textures/venus.png +0 -0
A crates/unified/src/client/incoming_planets.rs => crates/unified/src/client/incoming_planets.rs +33 -0
@@ 0,0 1,33 @@
use bevy::prelude::*;
use bevy_rapier2d::prelude::AdditionalMassProperties;
use crate::config::planet::Planet;

pub fn incoming_planets_plugin(app: &mut App) {
    app.add_systems(Update, (handle_incoming_planets, handle_updated_planets));
}


fn handle_incoming_planets(mut commands: Commands, mut new_planets: Query<(Entity, &Planet), Added<Planet>>, asset_server: Res<AssetServer>) {
    for (new_entity, new_planet) in new_planets.iter() {
        let mut sprite = Sprite::from_image(asset_server.load(&new_planet.sprite));
        sprite.custom_size = Some(Vec2::new(new_planet.radius*2.0, new_planet.radius*2.0));

        commands.entity(new_entity)
            .insert(sprite)
            .insert(AdditionalMassProperties::Mass(new_planet.mass));
        info!(?new_planet, "prepared new planet");
    }
}
fn handle_updated_planets(mut commands: Commands, mut updated_planets: Query<(Entity, &Planet), Changed<Planet>>, asset_server: Res<AssetServer>) {
    for (updated_entity, updated_planet) in updated_planets.iter() {
        let mut sprite = Sprite::from_image(asset_server.load(&updated_planet.sprite));
        sprite.custom_size = Some(Vec2::new(updated_planet.radius*2.0, updated_planet.radius*2.0));

        commands.entity(updated_entity)
            .remove::<Sprite>()
            .remove::<AdditionalMassProperties>()
            .insert(sprite)
            .insert(AdditionalMassProperties::Mass(updated_planet.mass));
        info!(?updated_planet, "updated planet");
    }
}
\ No newline at end of file

M crates/unified/src/client/mod.rs => crates/unified/src/client/mod.rs +5 -9
@@ 1,3 1,5 @@
mod incoming_planets;

use std::net::{SocketAddr, UdpSocket};
use std::time::SystemTime;
use bevy::prelude::*;


@@ 7,7 9,8 @@ use bevy_replicon::prelude::RepliconChannels;
use bevy_replicon_renet2::netcode::{ClientAuthentication, NetcodeClientTransport, NativeSocket};
use bevy_replicon_renet2::renet2::{ConnectionConfig, RenetClient};
use bevy_replicon_renet2::RenetChannelsExt;
use crate::ecs::{Ball, CursorWorldCoordinates, Ground, MainCamera, ReplicableCollider, SendBallHere};
use crate::client::incoming_planets::incoming_planets_plugin;
use crate::ecs::{Ball, CursorWorldCoordinates, Ground, MainCamera, SendBallHere};

pub struct ClientPlugin {
    pub server: SocketAddr


@@ 44,7 47,7 @@ impl Plugin for ClientPlugin {
            //.add_systems(Update, add_ground_sprite)
            .add_systems(Update, update_cursor_position)
            .add_systems(Update, teleport_cube_around)
            .add_systems(Update, replicate_colliders);
            .add_plugins(incoming_planets_plugin);
    }
}



@@ 53,13 56,6 @@ fn setup_graphics(mut commands: Commands) {
        .insert(MainCamera);
}

fn replicate_colliders(mut commands: Commands, q: Query<(Entity, &ReplicableCollider), Added<ReplicableCollider>>) {
    for (entity, replicated_collider) in &q {
        commands.entity(entity)
            .insert(replicated_collider.to_collider());
    }
}

fn add_ball_sprite(mut commands: Commands, q: Query<Entity, Added<Ball>>, asset_server: Res<AssetServer>) {
    for item in &q {
        let mut sprite = Sprite::from_image(asset_server.load("textures/earth.png"));

M crates/unified/src/client_plugins.rs => crates/unified/src/client_plugins.rs +6 -1
@@ 1,6 1,7 @@
use std::net::SocketAddr;
use bevy::app::{PluginGroup, PluginGroupBuilder};
use bevy::DefaultPlugins;
use bevy::log::LogPlugin;
use bevy_rapier2d::render::RapierDebugRenderPlugin;
use bevy_replicon::RepliconPlugins;
use bevy_replicon_renet2::RepliconRenetClientPlugin;


@@ 12,7 13,11 @@ pub struct ClientPluginGroup {
impl PluginGroup for ClientPluginGroup {
    fn build(self) -> PluginGroupBuilder {
        PluginGroupBuilder::start::<Self>()
            .add_group(DefaultPlugins)
            .add_group(
                DefaultPlugins
                    .build()
                    .disable::<LogPlugin>()
            )
            .add_group(
                RepliconPlugins
            )

A crates/unified/src/config/mod.rs => crates/unified/src/config/mod.rs +2 -0
@@ 0,0 1,2 @@
pub mod world;
pub mod planet;
\ No newline at end of file

A crates/unified/src/config/planet.rs => crates/unified/src/config/planet.rs +32 -0
@@ 0,0 1,32 @@
use bevy::asset::Asset;
use bevy::math::Vec3;
use bevy::prelude::{Bundle, Component, Transform, TypePath};
use bevy_rapier2d::prelude::{AdditionalMassProperties, Collider, ReadMassProperties, RigidBody};
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Asset, TypePath, Component, Serialize, Clone, Debug)]
#[require(
    ReadMassProperties,
    RigidBody::Fixed
)]
pub struct Planet {
    pub name: String,
    pub sprite: String,
    pub radius: f32,
    pub mass: f32,
    pub default_transform: [f32; 3]
}

#[derive(Bundle)]
pub struct PlanetBundle {
    pub planet: Planet,
    pub transform: Transform,
    pub collider: Collider,
    pub additional_mass_properties: AdditionalMassProperties,
}


#[derive(Deserialize, Asset, TypePath)]
pub struct PlanetConfigCollection {
    pub planets: Vec<Planet>,
}
\ No newline at end of file

A crates/unified/src/config/world.rs => crates/unified/src/config/world.rs +8 -0
@@ 0,0 1,8 @@
use bevy::asset::Asset;
use bevy::prelude::TypePath;
use serde::Deserialize;

#[derive(Deserialize, Asset, TypePath)]
pub struct WorldConfig {
    pub gravity: f64
}
\ No newline at end of file

M crates/unified/src/ecs.rs => crates/unified/src/ecs.rs +2 -41
@@ 1,7 1,5 @@
use bevy::math::Vec2;
use bevy::prelude::{Bundle, Component, Event, Resource};
use bevy_rapier2d::math::{Real, Vect};
use bevy_rapier2d::prelude::Collider;
use bevy::prelude::{Component, Event, Resource};
use serde::{Deserialize, Serialize};

#[derive(Component, Serialize, Deserialize)]


@@ 14,41 12,4 @@ pub struct MainCamera;
pub struct CursorWorldCoordinates(pub Option<Vec2>);

#[derive(Debug, Default, Deserialize, Event, Serialize)]
pub struct SendBallHere(pub Vec2);

#[derive(Bundle)]
pub struct ReplColliderBundle(ReplicableCollider, Collider);

#[derive(Serialize, Deserialize, Copy, Clone, Component)]
pub enum ReplicableCollider {
    Ball(Real),
    Cuboid { half_x: Real, half_y: Real },
    RoundCuboid { half_x: Real, half_y: Real, border_radius: Real },
    Capsule { start: Vect, end: Vect, radius: Real }
}
impl ReplicableCollider {
    pub fn to_collider(&self) -> Collider {
        match self {
            ReplicableCollider::Ball(radius) => Collider::ball(*radius),
            ReplicableCollider::Cuboid { half_x, half_y } => Collider::cuboid(*half_x, *half_y),
            ReplicableCollider::RoundCuboid { half_x, half_y, border_radius } => Collider::round_cuboid(*half_x, *half_y, *border_radius),
            ReplicableCollider::Capsule { start, end, radius } => Collider::capsule(*start, *end, *radius),
        }
    }
    pub fn ball(radius: Real) -> ReplColliderBundle {
        let rc = ReplicableCollider::Ball(radius);
        ReplColliderBundle(rc, rc.to_collider())
    }
    pub fn cuboid(half_x: Real, half_y: Real) -> ReplColliderBundle {
        let rc = ReplicableCollider::Cuboid { half_x, half_y };
        ReplColliderBundle(rc, rc.to_collider())
    }
    pub fn round_cuboid(half_x: Real, half_y: Real, border_radius: Real) -> ReplColliderBundle {
        let rc = ReplicableCollider::RoundCuboid { half_x, half_y, border_radius };
        ReplColliderBundle(rc, rc.to_collider())
    }
    pub fn capsule(start: Vect, end: Vect, radius: Real) -> ReplColliderBundle {
        let rc = ReplicableCollider::Capsule { start, end, radius };
        ReplColliderBundle(rc, rc.to_collider())
    }
}
\ No newline at end of file
pub struct SendBallHere(pub Vec2);
\ No newline at end of file

M crates/unified/src/main.rs => crates/unified/src/main.rs +20 -9
@@ 3,13 3,17 @@ pub mod shared_plugins;
pub mod server;
pub mod client;
pub mod ecs;
mod client_plugins;
pub mod client_plugins;
pub mod config;

use std::net::SocketAddr;
use std::process::exit;
use bevy::log::tracing_subscriber;
use bevy::log::{tracing_subscriber, Level, LogPlugin};
use clap::Parser;
use bevy::prelude::*;
use tracing_subscriber::EnvFilter;
use tracing_subscriber::filter::Directive;
use tracing_subscriber::util::SubscriberInitExt;
use crate::client_plugins::ClientPluginGroup;
use crate::server_plugins::ServerPluginGroup;
use crate::shared_plugins::SharedPluginGroup;


@@ 32,16 36,23 @@ enum Cli {
}

fn main() -> AppExit {
    tracing_subscriber::fmt()
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
        .init();

    let cli = Cli::parse();
    debug!(?cli, "parsed cli");

    let mut app = App::new();


    tracing_subscriber::fmt()
        .with_env_filter(
            EnvFilter::builder()
                .with_default_directive(Level::INFO.into())
                .from_env_lossy()
                .add_directive("bevy_app::plugin_group=error".parse().unwrap())
                .add_directive("bevy_app::app=error".parse().unwrap())
                .add_directive("bevy_replicon=warn".parse().unwrap())
                .add_directive("bevy_replicon_renet2=warn".parse().unwrap())
                .add_directive("naga=warn".parse().unwrap())
                .add_directive("wgpu_hal::vulkan=error".parse().unwrap())
        )
        .finish()
        .init();

    match cli {
        Cli::Client { server } => {

M crates/unified/src/server/mod.rs => crates/unified/src/server/mod.rs +8 -4
@@ 1,3 1,5 @@
mod planets;

use std::net::{SocketAddr, UdpSocket};
use std::time::{SystemTime, UNIX_EPOCH};
use bevy::prelude::*;


@@ 6,7 8,8 @@ use bevy_replicon::prelude::{FromClient, Replicated, RepliconChannels};
use bevy_replicon_renet2::netcode::{NativeSocket, NetcodeServerTransport, ServerAuthentication, ServerSetupConfig};
use bevy_replicon_renet2::renet2::{ConnectionConfig, RenetServer};
use bevy_replicon_renet2::RenetChannelsExt;
use crate::ecs::{Ball, Ground, ReplicableCollider, SendBallHere};
use crate::ecs::{Ball, Ground, SendBallHere};
use crate::server::planets::planets_plugin;

pub struct ServerPlugin {
    pub bind: SocketAddr,


@@ 42,19 45,20 @@ impl Plugin for ServerPlugin {
                info!("websocket server listening");
            })
            .add_systems(Startup, setup_physics)
            .add_systems(PreUpdate, receive_send_ball_here);
            .add_systems(PreUpdate, receive_send_ball_here)
            .add_plugins(planets_plugin);
    }
}

fn setup_physics(mut commands: Commands) {
    commands.spawn(ReplicableCollider::cuboid(500.0, 50.0))
    commands.spawn(Collider::cuboid(500.0, 50.0))
        .insert(Transform::from_xyz(0.0, -100.0, 0.0))
        .insert(Restitution::coefficient(1.0))
        .insert(Ground)
        .insert(Replicated);

    commands.spawn(RigidBody::Dynamic)
        .insert(ReplicableCollider::ball(50.0))
        .insert(Collider::ball(50.0))
        .insert(Restitution::coefficient(1.0))
        .insert(Transform::from_xyz(0.0, 400.0, 0.0))
        .insert(Velocity::default())

A crates/unified/src/server/planets.rs => crates/unified/src/server/planets.rs +86 -0
@@ 0,0 1,86 @@
use bevy::asset::Handle;
use bevy::prelude::*;
use bevy_rapier2d::dynamics::AdditionalMassProperties;
use bevy_rapier2d::prelude::Collider;
use bevy_replicon::prelude::Replicated;
use crate::config::planet::{Planet, PlanetBundle, PlanetConfigCollection};

pub fn planets_plugin(app: &mut App) {
    app
        .init_resource::<PlanetConfigResource>()
        .add_systems(Startup, start_loading_planets)
        .add_systems(Update, update_planets);
}

#[derive(Resource, Default)]
pub struct PlanetConfigResource {
    handle: Option<Handle<PlanetConfigCollection>>
}

fn start_loading_planets(assets: Res<AssetServer>, mut planets: ResMut<PlanetConfigResource>) {
    planets.handle = Some(assets.load("config/planets.pc.toml"));
}


pub fn update_planets(
    mut commands: Commands,
    mut ev_config: EventReader<AssetEvent<PlanetConfigCollection>>,
    mut assets: ResMut<Assets<PlanetConfigCollection>>,
    mut planets: ResMut<PlanetConfigResource>,
    mut q_planets: Query<(Entity, &mut Planet, &mut Transform, &mut AdditionalMassProperties)>
) {
    let Some(handle) = planets.handle.as_ref() else { return; };

    let waiting_for_asset_id = handle.id();

    for ev in ev_config.read() {
        match ev {
            AssetEvent::Added { id } => {
                if *id == waiting_for_asset_id {
                    info!("planet config loaded - creating planets");
                    let planet_config = assets.get(*id).unwrap();
                    for planet in &planet_config.planets {
                        commands.spawn(PlanetBundle {
                            planet: planet.clone(),
                            transform: Transform::from_xyz(planet.default_transform[0], planet.default_transform[1], planet.default_transform[2]),
                            collider: Collider::ball(planet.radius),
                            additional_mass_properties: AdditionalMassProperties::Mass(planet.mass),
                        }).insert(Replicated);
                        info!(?planet, "new planet spawned");
                    }
                }
            },
            AssetEvent::Modified { id } => {
                if *id == waiting_for_asset_id {
                    info!("planet config modified - reloading planets");
                    let planet_config = assets.get(*id).unwrap();

                    for planet in &planet_config.planets {
                        let existing_planet = q_planets.iter_mut().find(|(_, p, _, _)| p.name == planet.name);

                        if let Some((existing, mut e_planet, mut e_transform, mut e_mass)) = existing_planet {
                            commands.entity(existing)
                                .remove::<Collider>()
                                .insert(Collider::ball(planet.radius));
                            *e_planet = planet.clone();
                            e_transform.translation = Vec3::new(planet.default_transform[0], planet.default_transform[1], planet.default_transform[2]);
                            *e_mass = AdditionalMassProperties::Mass(planet.mass);
                            info!(?planet, "planet hot-reloaded");
                        } else {
                            commands.spawn(PlanetBundle {
                                planet: planet.clone(),
                                transform: Transform::from_xyz(planet.default_transform[0], planet.default_transform[1], planet.default_transform[2]),
                                collider: Collider::ball(planet.radius),
                                additional_mass_properties: AdditionalMassProperties::Mass(planet.mass),
                            }).insert(Replicated);
                            info!(?planet, "new planet spawned");
                        }


                    }
                }
            },
            _ => {}
        }
    }
}
\ No newline at end of file

M crates/unified/src/server_plugins.rs => crates/unified/src/server_plugins.rs +8 -0
@@ 1,10 1,14 @@
use std::net::SocketAddr;
use std::time::Duration;
use bevy::app::{PluginGroup, PluginGroupBuilder, ScheduleRunnerPlugin, TaskPoolPlugin};
use bevy::asset::AssetPlugin;
use bevy::diagnostic::FrameCountPlugin;
use bevy::time::TimePlugin;
use bevy_common_assets::toml::TomlAssetPlugin;
use bevy_replicon::RepliconPlugins;
use bevy_replicon_renet2::RepliconRenetServerPlugin;
use crate::config::planet::{Planet, PlanetConfigCollection};
use crate::config::world::WorldConfig;

pub struct ServerPluginGroup {
    pub bind: SocketAddr,


@@ 26,6 30,10 @@ impl PluginGroup for ServerPluginGroup {
            .add(
                RepliconRenetServerPlugin
            )
            /* Assets */
            .add(AssetPlugin::default())
            .add(TomlAssetPlugin::<WorldConfig>::new(&["wc.toml"]))
            .add(TomlAssetPlugin::<PlanetConfigCollection>::new(&["pc.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 -3
@@ 1,15 1,19 @@
use bevy::app::{App, PluginGroup, PluginGroupBuilder};
use bevy::math::vec2;
use bevy::prelude::{Sprite, Transform};
use bevy_rapier2d::prelude::*;
use bevy_replicon::prelude::{AppRuleExt, Channel, ClientEventAppExt};
use crate::ecs::{Ball, Ground, ReplicableCollider, SendBallHere};
use crate::config::planet::Planet;
use crate::ecs::{Ball, Ground, SendBallHere};

pub struct SharedPluginGroup;

impl PluginGroup for SharedPluginGroup {
    fn build(self) -> PluginGroupBuilder {
        PluginGroupBuilder::start::<Self>()
            .add(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(100.0))
            .add(
                RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(100.0)
            )
            .add(register_everything)
    }
}


@@ 20,5 24,6 @@ pub fn register_everything(app: &mut App) {
        .replicate::<Transform>()
        .replicate::<Ball>()
        .replicate::<Ground>()
        .replicate::<ReplicableCollider>();
        .replicate::<Collider>()
        .replicate::<Planet>();
}
\ No newline at end of file