~starkingdoms/starkingdoms

49175fb533db030ca3d3dee8434ea3aa905b4172 — core 23 days ago a21b4f9
feat: orbits
M crates/unified/assets/config/planets.pc.toml => crates/unified/assets/config/planets.pc.toml +13 -10
@@ 1,3 1,6 @@
[orbit]
planet_spring_compliance = 0

[[planets]]
name = "Sun"
sprite = "textures/sun.png"


@@ 14,7 17,7 @@ radius = 666.66 # m
mass = 205_000_000.0 # kg
default_transform = [116_129.4, 0.0, 0.0]
planet_resource = { name = "Composite", color = { LinearRgba = { red = 0.7, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 5.0 }
orbit = { orbiting = "Sun", eccentricity = 0.2056 }
orbit = { orbiting = "Sun", eccentricity = 0.2056, period = 299.0 }

[[planets]]
name = "Venus"


@@ 23,7 26,7 @@ radius = 1899.8 # m
mass = 806_166_000.0 # kg
default_transform = [216_999.6, 0.0, 0.0]
planet_resource = { name = "Sulfur", color = { LinearRgba = { red = 0.7, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 5.0 }
orbit = { orbiting = "Sun", eccentricity = 0.0068 }
orbit = { orbiting = "Sun", eccentricity = 0.0068, period = 546.0 }

[[planets]]
name = "Earth"


@@ 33,7 36,7 @@ radius = 2000.0 # m
mass = 16_900_000_000.0 # kg
planet_resource = { name = "Carbon", color = { LinearRgba = { red = 1.0, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 2.0 }
default_transform = [300_000.0, 0.0, 0.0]
orbit = { orbiting = "Sun", eccentricity = 0.0167 }
orbit = { orbiting = "Sun", eccentricity = 0.0167, period = 900.0 }

[[planets]]
name = "Moon"


@@ 43,7 46,7 @@ radius = 545.4 # m
mass = 360_236_000.0 # kg
default_transform = [312_700.0, 0.0, 0.0]
planet_resource = { name = "Silicon", color = { LinearRgba = { red = 0.7, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 5.0 }
orbit = { orbiting = "Earth", eccentricity = 0.0549 }
orbit = { orbiting = "Earth", eccentricity = 0.0549, period = 256.0 }

[[planets]]
name = "Mars"


@@ 53,7 56,7 @@ radius = 1062.0 # m
mass = 525_857_000.0 # kg
default_transform = [430_000.0, 0.0, 0.0]
planet_resource = { name = "Iron", color = { LinearRgba = { red = 0.7, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 5.0 }
orbit = { orbiting = "Sun", eccentricity = 0.0934 }
orbit = { orbiting = "Sun", eccentricity = 0.0934, period = 1745.0 }

[[planets]]
name = "Jupiter"


@@ 62,7 65,7 @@ radius = 21946.0 # m
mass = 1_131_221_218_000.0 # kg
default_transform = [1_561_140.0, 0.0, 0.0]
planet_resource = { name = "Hydrogen", color = { LinearRgba = { red = 0.7, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 5.0 }
orbit = { orbiting = "Sun", eccentricity = 0.0484 }
orbit = { orbiting = "Sun", eccentricity = 0.0484, period = 11218.0 }

[[planets]]
name = "Saturn"


@@ 71,7 74,7 @@ radius = 18_280.4 # m
mass = 561_386_112_000.0 # kg
default_transform = [2_874_780.0, 0.0, 0.0]
planet_resource = { name = "Helium", color = { LinearRgba = { red = 0.7, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 5.0 }
orbit = { orbiting = "Sun", eccentricity = 0.0541 }
orbit = { orbiting = "Sun", eccentricity = 0.0541, period = 28297.0 }

[[planets]]
name = "Uranus"


@@ 80,7 83,7 @@ radius = 8014.0 # m
mass = 69_763_532_000.0 # kg
default_transform = [4_050_000.0, 0.0, 0.0]
planet_resource = { name = "Rubber", color = { LinearRgba = { red = 0.7, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 5.0 }
orbit = { orbiting = "Sun", eccentricity = 0.0472 }
orbit = { orbiting = "Sun", eccentricity = 0.0472, period = 46791.0 }

[[planets]]
name = "Neptune"


@@ 89,7 92,7 @@ radius = 7_766.0 # m
mass = 106_674_649_000.0 # kg
default_transform = [5_000_000.0, 0.0, 0.0]
planet_resource = { name = "Methane", color = { LinearRgba = { red = 0.7, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 5.0 }
orbit = { orbiting = "Sun", eccentricity = 0.0086 }
orbit = { orbiting = "Sun", eccentricity = 0.0086, period = 60454.0 }

[[planets]]
name = "Pluto"


@@ 98,4 101,4 @@ radius = 373.6 # m
mass = 10_817_000.0 # kg
default_transform = [5_922_300.0, 0.0, 0.0]
planet_resource = { name = "Ice", color = { LinearRgba = { red = 0.7, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 5.0 }
orbit = { orbiting = "Sun", eccentricity = 0.2488 }
orbit = { orbiting = "Sun", eccentricity = 0.2488, period = 118234.0 }

M crates/unified/src/client_plugins.rs => crates/unified/src/client_plugins.rs +1 -0
@@ 25,6 25,7 @@ impl PluginGroup for ClientPluginGroup {
            .add(UiPlugin)
            .add(InputDispatchPlugin)
            .add(InputManagerPlugin::<crate::client::input::ClientAction>::default())
            .add(PhysicsDebugPlugin)
    }
}


M crates/unified/src/config/planet.rs => crates/unified/src/config/planet.rs +25 -1
@@ 6,7 6,7 @@ use serde::{Deserialize, Serialize};

#[derive(Deserialize, Asset, TypePath, Component, Serialize, Clone, Debug)]
#[require(
    RigidBody::Static,
    RigidBody::Dynamic,
    Replicated,
)]
pub struct Planet {


@@ 21,6 21,23 @@ pub struct Planet {
    pub orbit: Option<OrbitData>
}

#[derive(Component, Serialize, Deserialize)]
#[require(
    RigidBody::Static,
    Replicated,
)]
pub struct PlanetSpring {
    pub name: String
}

#[derive(Component, Serialize, Deserialize)]
#[require(
    Replicated,
)]
pub struct PlanetSpringJoint {
    pub name: String
}

#[derive(Deserialize, TypePath, Serialize, Clone, Debug)]
pub struct PlanetResource {
    pub name: String,


@@ 32,6 49,7 @@ pub struct PlanetResource {
pub struct OrbitData {
    pub orbiting: String,
    pub eccentricity: f32,
    pub period: f32
}

#[derive(Deserialize, TypePath, Serialize, Clone, Debug)]


@@ 50,4 68,10 @@ pub struct PlanetBundle {
#[derive(Deserialize, Asset, TypePath)]
pub struct PlanetConfigCollection {
    pub planets: Vec<Planet>,
    pub orbit: OrbitConfig
}

#[derive(Deserialize, Asset, TypePath, Clone, Debug)]
pub struct OrbitConfig {
    pub planet_spring_compliance: f32
}

M crates/unified/src/config/world.rs => crates/unified/src/config/world.rs +1 -1
@@ 6,7 6,7 @@ use serde::Deserialize;
pub struct GlobalWorldConfig {
    pub world: WorldConfig,
    pub part: WPartConfig,
    pub hearty: HeartyConfig,
    pub hearty: HeartyConfig
}

#[derive(Deserialize, Asset, TypePath, Clone)]

M crates/unified/src/server/mod.rs => crates/unified/src/server/mod.rs +3 -0
@@ 8,6 8,7 @@ mod damping;
pub mod planets;
pub mod player;
mod system_sets;
pub mod orbit;

use crate::server::craft::craft_plugin;
use crate::server::damping::damping_plugin;


@@ 29,6 30,7 @@ use aeronet_websocket::server::WebSocketServer;
use crate::prelude::*;
use bevy_replicon::prelude::Replicated;
use std::net::SocketAddr;
use crate::server::orbit::OrbitPlugin;
use crate::server::player::thrust::server_thrust_plugin;

pub struct ServerPlugin {


@@ 63,6 65,7 @@ impl Plugin for ServerPlugin {
        .add_plugins(heat_conduction_plugin)*/
        .add_plugins(drill_plugin)
        .add_plugins(craft_plugin)
        .add_plugins(OrbitPlugin)
        .add_plugins(damping_plugin)
        .configure_sets(Update, WorldUpdateSet.before(PlayerInputSet));
        //.configure_sets(Update, PlayerInputSet.before(PhysicsSet::SyncBackend));

A crates/unified/src/server/orbit/mod.rs => crates/unified/src/server/orbit/mod.rs +74 -0
@@ 0,0 1,74 @@
use std::collections::HashMap;
use avian2d::math::TAU;
use avian2d::prelude::LinearVelocity;
use bevy::log::debug;
use bevy::math::ops::atan2;
use bevy::prelude::{Component, Plugin, Transform};
use bevy::time::Time;
use serde::{Deserialize, Serialize};
use crate::config::planet::{Planet, PlanetSpring, PlanetSpringJoint};
use crate::prelude::{App, Query, Res, Update, Without};

pub struct OrbitPlugin;
impl Plugin for OrbitPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(Update, update_orbits);
    }
}

fn update_orbits(
    mut planets: Query<(&Planet, &Transform, &mut LinearVelocity), Without<PlanetSpring>>,
    planets_2: Query<(&Planet, &Transform), Without<PlanetSpring>>,
    mut planet_springs: Query<(&PlanetSpring, &mut Transform), Without<Planet>>,
    time: Res<Time>
) {
    let parent_velocities = planets.iter().map(|u| (u.0.name.clone(), u.2.clone())).collect::<HashMap<String, LinearVelocity>>();
    for (planet, _, mut vel) in planets.iter_mut() {
        let Some(orbit_data) = &planet.orbit else { continue; };
        // find parent
        let Some(parent) = planets_2.iter().find(|u| u.0.name == orbit_data.orbiting) else { continue; };

        let a = (planet.default_transform[0] - parent.0.default_transform[0]) / (1.0 - orbit_data.eccentricity);
        let e = orbit_data.eccentricity;
        let t = orbit_data.period;

        let time = time.elapsed_secs();

        // calculate position of the planet
        let m = (TAU / t) * time;
        let e_k = iterative_kepler(m, e);
        let nu = 2.0 * atan2(
            (1.0 + e).sqrt() * (e_k / 2.0).sin(),
            (1.0 - e).sqrt() * (e_k / 2.0).cos(),
        );
        let r = a * (1.0 - e * e_k.cos());

        let x = r * nu.cos();
        let y = r * nu.sin();

        // find the spring
        let Some(mut planet_spring) = planet_springs.iter_mut().find(|u| u.0.name == planet.name) else { continue; };
        planet_spring.1.translation.x = x + parent.1.translation.x;
        planet_spring.1.translation.y = y + parent.1.translation.y;

        let Some(parent_velocity) = parent_velocities.get(&orbit_data.orbiting) else { continue; };
        let de_dt = (TAU / t) / (1.0 - e * e_k.cos());
        let b_factor = (1.0 - e * e).sqrt();
        let vx = -a * e_k.sin() * de_dt;
        let vy = a * b_factor * e_k.cos() * de_dt;
        vel.x = vx + parent_velocity.x;
        vel.y = vy + parent_velocity.y;
    }
}

fn iterative_kepler(m: f32, e: f32) -> f32 {
    let mut output = m;
    for _ in 0..100 {
        let d = (m - output + e * output.sin()) / (1.0 - e * output.cos());
        output += d;
        if d.abs() < 1e-10 {
            break;
        }
    }
    output
}
\ No newline at end of file

M crates/unified/src/server/planets.rs => crates/unified/src/server/planets.rs +47 -12
@@ 2,6 2,7 @@ use crate::{config::planet::{Planet, PlanetBundle, PlanetConfigCollection}, ecs:
use bevy::asset::Handle;
use crate::prelude::*;
use bevy_replicon::prelude::Replicated;
use crate::config::planet::{PlanetSpring, PlanetSpringJoint};

pub fn planets_plugin(app: &mut App) {
    app.init_resource::<PlanetConfigResource>()


@@ 29,6 30,7 @@ pub fn update_planets(
        &mut Transform,
        &mut Mass,
    )>,
    mut planet_joint_springs: Query<(&PlanetSpringJoint, &mut FixedJoint)>
) {
    let Some(handle) = planets.handle.as_ref() else {
        return;


@@ 43,7 45,7 @@ pub fn update_planets(
                    debug!("planet config loaded - creating planets");
                    let planet_config = assets.get(*id).unwrap();
                    for planet in &planet_config.planets {
                        commands
                        let mut planet_entity = commands
                            .spawn(PlanetBundle {
                                planet: planet.clone(),
                                transform: Transform::from_xyz(


@@ 53,14 55,31 @@ pub fn update_planets(
                                ),
                                collider: Collider::circle(planet.radius),
                                mass: Mass(planet.mass)
                            })
                            .insert(Replicated)
                            .with_child((
                            }).with_child((
                                Collider::circle(planet.radius+2.0),
                                Sensor,
                                PlanetSensor(planet.name.clone()),
                                CollisionEventsEnabled,
                            )).id();
                        if planet.orbit.is_some() {
                            let spring =commands.spawn((
                                PlanetSpring {
                                    name: planet.name.clone()
                                },
                                Transform::from_xyz(
                                    planet.default_transform[0],
                                    planet.default_transform[1],
                                    planet.default_transform[2],
                                )
                            )).id();
                            commands.spawn((
                                PlanetSpringJoint {
                                    name: planet.name.clone()
                                },
                                FixedJoint::new(planet_entity, spring).with_point_compliance(planet_config.orbit.planet_spring_compliance)
                            ));
                        }

                        trace!(?planet, "new planet spawned");
                    }
                }


@@ 70,6 89,9 @@ pub fn update_planets(
                    trace!("planet config modified - reloading planets");
                    let planet_config = assets.get(*id).unwrap();

                    info!("updating compliance on all planet spring joints");
                    planet_joint_springs.iter_mut().for_each(|mut u| u.1.point_compliance = planet_config.orbit.planet_spring_compliance);

                    for planet in &planet_config.planets {
                        let existing_planet = q_planets
                            .iter_mut()


@@ 83,15 105,10 @@ pub fn update_planets(
                                .remove::<Collider>()
                                .insert(Collider::circle(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 = Mass(planet.mass);
                            trace!(?planet, "planet hot-reloaded");
                        } else {
                            commands
                            let planet_entity = commands
                                .spawn(PlanetBundle {
                                    planet: planet.clone(),
                                    transform: Transform::from_xyz(


@@ 103,8 120,26 @@ pub fn update_planets(
                                    mass: Mass(
                                        planet.mass,
                                    ),
                                })
                                .insert(Replicated);
                                }).id();

                            if planet.orbit.is_some() {
                                let spring =commands.spawn((
                                    PlanetSpring {
                                        name: planet.name.clone()
                                    },
                                    Transform::from_xyz(
                                        planet.default_transform[0],
                                        planet.default_transform[1],
                                        planet.default_transform[2],
                                    )
                                )).id();
                                commands.spawn((
                                    PlanetSpringJoint {
                                        name: planet.name.clone()
                                    },
                                    FixedJoint::new(planet_entity, spring).with_point_compliance(planet_config.orbit.planet_spring_compliance)
                                ));
                            }
                            trace!(?planet, "new planet spawned");
                        }
                    }

M crates/unified/src/shared_plugins.rs => crates/unified/src/shared_plugins.rs +3 -1
@@ 1,5 1,5 @@
use crate::attachment::{Joint, JointOf, PartInShip, Peer, Ship, SnapOf, SnapOfJoint};
use crate::config::planet::{Planet, PlanetConfigCollection};
use crate::config::planet::{Planet, PlanetConfigCollection, PlanetSpring, PlanetSpringJoint};
use crate::config::recipe::RecipesConfig;
use crate::ecs::{CanCraft, CraftPartRequest, DragRequestEvent, Drill, Hi, Part, Particles, Player, PlayerStorage, SingleStorage, Temperature, ToggleDrillEvent};
use bevy::app::{App, PluginGroup, PluginGroupBuilder};


@@ 60,6 60,8 @@ pub fn register_everything(app: &mut App) {
    app.replicate::<Temperature>();
    app.replicate::<Drill>();
    app.replicate::<SingleStorage>();
    app.replicate::<PlanetSpring>();
    app.replicate::<PlanetSpringJoint>();
}

fn physics_setup_plugin(app: &mut App) {

M crates/unified/src/world_config.rs => crates/unified/src/world_config.rs +1 -0
@@ 1,5 1,6 @@
use crate::config::world::GlobalWorldConfig;
use bevy::asset::Handle;
use crate::config::planet::PlanetSpringJoint;
use crate::prelude::*;

pub fn world_config_plugin(app: &mut App) {