~starkingdoms/starkingdoms

6c4380ea41ef2824f25fcd3b63c2cbda9dddba77 — core 21 days ago 7276800
fix: broken join routine
M crates/unified/assets/config/world.wc.toml => crates/unified/assets/config/world.wc.toml +4 -4
@@ 7,11 7,11 @@ spawn_parts_interval_secs = 1
default_height = 50
default_width = 50
default_mass = 100
joint_align_compliance = 0.000000000000000002
joint_align_compliance = 0.0002
joint_angle_compliance = 0.00000002
joint_limit_compliance = 0.0000006
joint_linear_damping = 40.0
joint_angular_damping = 20.0
joint_limit_compliance = 0.006
joint_linear_damping = 4.0
joint_angular_damping = 2.0

[hearty]
thrust = 500000

M crates/unified/src/attachment.rs => crates/unified/src/attachment.rs +10 -0
@@ 4,24 4,29 @@ use serde::{Deserialize, Serialize};
use std::ops::Deref;

#[derive(Component, Serialize, Deserialize)]
#[require(Replicated)]
/// The primary component for a ship structure
pub struct Ship;

#[derive(Component, Serialize, Deserialize, MapEntities, Deref)]
#[relationship_target(relationship = PartInShip, linked_spawn)]
#[require(Replicated)]
pub struct Parts(#[entities] Vec<Entity>);

#[derive(Component, Serialize, Deserialize, MapEntities)]
#[relationship(relationship_target = Parts)]
#[require(Replicated)]
pub struct PartInShip(#[entities] pub Entity);

#[derive(Component, Serialize, Deserialize)]
#[require(Transform)]
#[require(Replicated)]
pub struct Joint {
    pub id: JointId,
    pub transform: Transform,
}
#[derive(Component, Serialize, Deserialize, MapEntities)]
#[require(Replicated)]
pub struct Peer {
    #[entities]
    pub peer_joint_entity_id: Entity,


@@ 32,9 37,11 @@ pub struct Peer {

#[derive(Component, Serialize, Deserialize, MapEntities)]
#[relationship(relationship_target = Joints)]
#[require(Replicated)]
pub struct JointOf(#[entities] pub Entity);
#[derive(Component, Serialize, Deserialize, MapEntities, Debug, Clone)]
#[relationship_target(relationship = JointOf)]
#[require(Replicated)]
pub struct Joints(#[entities] Vec<Entity>);
impl Deref for Joints {
    type Target = Vec<Entity>;


@@ 44,9 51,11 @@ impl Deref for Joints {
}
#[derive(Component, Serialize, Deserialize, MapEntities)]
#[relationship(relationship_target = Snaps)]
#[require(Replicated)]
pub struct SnapOf(#[entities] pub Entity);
#[derive(Component, Serialize, Deserialize, MapEntities)]
#[relationship_target(relationship = SnapOf)]
#[require(Replicated)]
pub struct Snaps(#[entities] Vec<Entity>);
impl Deref for Snaps {
    type Target = Vec<Entity>;


@@ 66,4 75,5 @@ impl JointId {
}

#[derive(Serialize, Deserialize, Component, MapEntities)]
#[require(Replicated)]
pub struct SnapOfJoint(#[entities] pub Entity);

M crates/unified/src/client/mod.rs => crates/unified/src/client/mod.rs +22 -18
@@ 4,7 4,7 @@ use crate::client::planet::indicators::indicators_plugin;
use crate::client::starfield::starfield_plugin;
use crate::client::ui::ui_plugin;
use crate::client::zoom::zoom_plugin;
use crate::ecs::{Part, Player};
use crate::ecs::{Hi, Part, Player};
use aeronet_websocket::client::WebSocketClient;
use bevy::dev_tools::picking_debug::DebugPickingMode;
use crate::prelude::*;


@@ 68,26 68,30 @@ impl Plugin for ClientPlugin {

fn find_me(
    mut commands: Commands,
    q_clients: Query<(Entity, &Player, &Part), Added<Player>>,
    mut reader: MessageReader<Hi>,
    asset_server: Res<AssetServer>,
) {
    for (entity, player, part) in q_clients.iter() {
        if player.client == entity {
            commands.entity(entity).insert(Me);
            let mut heart_sprite =
                Sprite::from_image(asset_server.load("sprites/hearty_heart.png"));
            heart_sprite.custom_size = Some(Vec2::new(
                part.strong_config.physics.width,
                part.strong_config.physics.height,
            ));
            heart_sprite.color = Color::srgb(20.0, 0.0, 0.0);

            commands.spawn((
                ChildOf(entity),
                heart_sprite,
                Transform::from_xyz(0.0, 0.0, 10.0),
            ));
        }
    for msg in reader.read() {
        info!("^^^^^^^^^^^ IGNORE THESE WARNINGS ^^^^^^^^^^^^^^");
        info!("they are normal! and are from the world state being replicated as it is sent over the network");
        info!(?msg, "finding me: got hello from server");
        commands.entity(msg.you_are).insert(Me);

        /*let mut heart_sprite =
            Sprite::from_image(asset_server.load("sprites/hearty_heart.png"));
        heart_sprite.custom_size = Some(Vec2::new(
            part.strong_config.physics.width,
            part.strong_config.physics.height,
        ));
        heart_sprite.color = Color::srgb(20.0, 0.0, 0.0);

        commands.spawn((
            ChildOf(entity),
            heart_sprite,
            Transform::from_xyz(0.0, 0.0, 10.0),
        ));
        */
    }
}


M crates/unified/src/ecs.rs => crates/unified/src/ecs.rs +10 -9
@@ 35,24 35,18 @@ pub struct Part {
    pub strong_config: PartConfig,
}
#[derive(Component, Debug)]
#[require(Replicated)]
pub struct PartHandle(pub Handle<PartConfig>);

#[derive(Component, Serialize, Deserialize, Debug)]
#[require(Replicated)]
pub struct Player {
    #[entities]
    pub client: Entity,
}

#[derive(Component, Default, Serialize, Deserialize, Debug)]
#[allow(clippy::struct_excessive_bools, reason = "It's not a state machine")]
pub struct PlayerThrust {
    pub up: bool,
    pub down: bool,
    pub left: bool,
    pub right: bool,
}

#[derive(Component, Serialize, Deserialize, Debug)]
#[require(Replicated)]
pub struct Particles {
    pub effect: String,
    pub active: bool,


@@ 71,6 65,7 @@ pub struct DragRequestEvent {
}

#[derive(Component, Serialize, Deserialize, Debug)]
#[require(Replicated)]
pub struct PlayerStorage {
    pub fuel_capacity: f32,
    pub fuel: f32,


@@ 80,3 75,9 @@ pub struct PlayerStorage {

#[derive(Component)]
pub struct Me;

#[derive(Message, Serialize, Deserialize, Debug, MapEntities)]
pub struct Hi {
    #[entities]
    pub you_are: Entity
}
\ No newline at end of file

M crates/unified/src/ecs/thruster.rs => crates/unified/src/ecs/thruster.rs +2 -0
@@ 4,6 4,7 @@ use bevy::math::Vec2;
use bevy::prelude::Bundle;
use serde::{Deserialize, Serialize};
use crate::prelude::{ChildOf, Component, Entity, Transform};
use bevy_replicon::prelude::Replicated;

#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
pub struct ThrusterId(pub String);


@@ 30,6 31,7 @@ impl Deref for PartThrusters {
pub struct ThrusterOfPart(#[entities] pub Entity);

#[derive(Component, Serialize, Deserialize)]
#[require(Replicated)]
pub struct Thruster {
    pub id: ThrusterId,
    pub thrust_vector: Vec2

M crates/unified/src/server/mod.rs => crates/unified/src/server/mod.rs +3 -1
@@ 63,11 63,13 @@ impl ServerPlugin {
    }
}

#[derive(Component)]
#[derive(Component, Debug)]
#[require(Replicated)]
pub struct ConnectedGameEntity {
    pub network_entity: Entity,
}
#[derive(Component)]
#[require(Replicated)]
pub struct ConnectedNetworkEntity {
    pub game_entity: Entity,
}

M crates/unified/src/server/part.rs => crates/unified/src/server/part.rs +1 -0
@@ 16,6 16,7 @@ pub struct SpawnPartBundle {
}

#[derive(Component)]
#[require(Replicated)]
pub struct SpawnPartRequest(pub Handle<PartConfig>);

// wait for parts assets to be ready, then spawn the full part

M crates/unified/src/server/player.rs => crates/unified/src/server/player.rs +1 -0
@@ 25,6 25,7 @@ pub fn player_management_plugin(app: &mut App) {
}

#[derive(Component)]
#[require(Replicated)]
struct PartiallyDisconnected;

/// Partial Disconnects are created when a part is disconnected to indicate that a disconnect has started.

M crates/unified/src/server/player/join.rs => crates/unified/src/server/player/join.rs +15 -5
@@ 1,6 1,6 @@
use crate::config::planet::Planet;
use crate::config::world::GlobalWorldConfig;
use crate::ecs::{Player, PlayerStorage, PlayerThrust};
use crate::ecs::{Hi, Player, PlayerStorage};
use crate::prelude::*;
use crate::server::ConnectedGameEntity;
use crate::server::part::SpawnPartRequest;


@@ 38,7 38,6 @@ fn join_player(joined_player: Entity, mut commands: Commands, wc: &GlobalWorldCo
        .insert(SpawnPartRequest(
            asset_server.load("config/parts/hearty.part.toml"),
        ))
        .insert(PlayerThrust::default())
        .insert(PlayerStorage {
            fuel_capacity: 25.0,
            fuel: 25.0,


@@ 52,24 51,35 @@ fn join_player(joined_player: Entity, mut commands: Commands, wc: &GlobalWorldCo
}

#[derive(Component)]
#[require(Replicated)]
pub struct PendingPlayer;

pub fn handle_new_players(
    mut commands: Commands,
    q_new_clients: Query<Entity, Added<ConnectedGameEntity>>,
    q_new_clients: Query<(Entity, &ConnectedGameEntity), Added<ConnectedGameEntity>>,
    world_config: Res<WorldConfigResource>,
    planets: Query<(&Transform, &Planet)>,
    mut hi_writer: MessageWriter<ToClients<Hi>>,
    asset_server: Res<AssetServer>,
) {
    for joined_player in &q_new_clients {
        hi_writer.write(ToClients {
            mode: SendMode::Direct(ClientId::Client(joined_player.1.network_entity)),
            message: Hi {
                you_are: joined_player.0
            }
        });
    }
    let Some(wc) = &world_config.config else {
        warn!("got a joined player, but world config is not loaded! waiting until it is...");
        for joined_player in &q_new_clients {
            commands.entity(joined_player).insert(PendingPlayer);
            commands.entity(joined_player.0).insert(PendingPlayer);
        }
        return;
    };
    for joined_player in &q_new_clients {
        join_player(joined_player, commands.reborrow(), wc, planets, &asset_server);
        debug!(?joined_player, "new player!");
        join_player(joined_player.0, commands.reborrow(), wc, planets, &asset_server);
    }
}
pub fn handle_pending_players(

M crates/unified/src/shared_plugins.rs => crates/unified/src/shared_plugins.rs +7 -3
@@ 1,12 1,13 @@
use crate::attachment::{Joint, JointOf, PartInShip, Peer, Ship, SnapOf, SnapOfJoint};
use crate::config::planet::{Planet, PlanetConfigCollection};
use crate::ecs::{DragRequestEvent, Part, Particles, Player, PlayerStorage};
use crate::ecs::{DragRequestEvent, Hi, Part, Particles, Player, PlayerStorage};
use bevy::app::{App, PluginGroup, PluginGroupBuilder};
use bevy_common_assets::toml::TomlAssetPlugin;
use crate::prelude::*;
use bevy_replicon::prelude::{AppRuleExt, Channel, ClientMessageAppExt};
use crate::config::part::PartConfig;
use crate::config::world::GlobalWorldConfig;
use crate::ecs::thruster::{Thruster, ThrusterOfPart};
use crate::physics::register_physics_components_for_replication;
use crate::thrust::ThrustSolution;



@@ 31,8 32,9 @@ impl PluginGroup for SharedPluginGroup {
pub fn register_everything(app: &mut App) {
    app.add_mapped_client_message::<ThrustSolution>(Channel::Ordered)
        .add_mapped_client_message::<DragRequestEvent>(Channel::Ordered)
        .add_mapped_server_message::<Hi>(Channel::Ordered)
        .replicate::<Transform>()
        .replicate::<GlobalTransform>()
        //.replicate::<GlobalTransform>()
        //.replicate::<Collider>()
        //.replicate::<RigidBody>()
        .replicate::<Planet>()


@@ 47,7 49,9 @@ pub fn register_everything(app: &mut App) {
        .replicate::<JointOf>()
        .replicate::<SnapOfJoint>()
        .replicate::<SnapOf>()
        .replicate::<PlayerStorage>();
        .replicate::<PlayerStorage>()
        .replicate::<Thruster>()
        .replicate::<ThrusterOfPart>();
}

fn physics_setup_plugin(app: &mut App) {